Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@oowekyala
Copy link
Member

@oowekyala oowekyala commented Nov 25, 2025

Describe the PR

Add new API to RuleContext to report violations. The goal is to replace the set of addViolation* methods with a more flexible 2-stage builder pattern. This allows specifying the violation location and the message parameters independently and increases the readability and usability of this API.

I removed the more complex overloads already in this PR (those that were marked @Experimental) and replace their usages. Should we deprecate the simpler methods now, later, never? I think we should do so, but maybe in a separate PR.

API Changes

New Experimental APIs

  • core
    • Node#atLocation
    • Node#atToken
    • CannotBeSuppressed

Removed Experimental APIs

  • core
    • RuleContext#addViolationWithPosition(Node node, JavaccToken token, String message, Object... formatArgs) (introduced in 7.17.0)
    • RuleContext#addViolationWithPosition(Reportable reportable, AstInfo<?> astInfo, FileLocation location, String message, Object... formatArgs) (introduced in 7.9.0)
    • RuleContext#addViolationNoSuppress(Reportable reportable, AstInfo<?> astInfo, String message, Object... formatArgs) (introduced in 7.14.0)

Deprecated APIs

  • core
    • RuleContext#addViolationWithPosition(Node node, int beginLine, int endLine, String message, Object... formatArgs)

Related issues

Ready?

  • Added unit tests for fixed bug/feature
  • Passing all unit tests
  • Complete build ./mvnw clean verify passes (checked automatically by github actions)
  • Added (in-code) documentation (if needed)
  • Update release notes with deprecations

@oowekyala oowekyala added this to the 7.20.0 milestone Nov 25, 2025
This has been a TODO for a long time. Compared to
automatically finding the suppression node, this
saves time, as we don't have to explore the whole
tree when reporting something.
parse default message ahead of time
@pmd-actions-helper
Copy link
Contributor

pmd-actions-helper bot commented Nov 26, 2025

Documentation Preview

Compared to main:
This changeset changes 18 violations,
introduces 3 new violations, 0 new errors and 0 new configuration errors,
removes 3 violations, 0 errors and 0 configuration errors.

Regression Tester Report

(comment created at 2025-11-29 16:37:01+00:00 for c343925)

@oowekyala oowekyala marked this pull request as ready for review November 26, 2025 22:57
@oowekyala oowekyala added the an:enhancement An improvement on existing features / rules label Nov 27, 2025
@adangel adangel modified the milestones: 7.20.0, 7.21.0 Dec 30, 2025
@oowekyala oowekyala requested a review from jsotuyod January 5, 2026 11:44
Copy link
Member

@adangel adangel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I like the new API (almost 😄 )

I would really name the verb report() instead of warn().

If we introduce the new API with the violation builder without marking it Experimental (like it is right now in this PR), then I'd deprecate all the old methods addViolation... right now. No need to wait, because we have then already a stable alternative API.

I've created an "API Changes" section in the description of this PR to keep track of the changes for the release notes so far.

We also need to update the documentation then on how to report a violation: https://docs.pmd-code.org/latest/pmd_userdocs_extending_writing_java_rules.html#reporting-violations

*/
@Experimental
default Reportable atLocation(FileLocation loc) {
assert NodeFindingUtil.nodeCoversLocation(this, loc) : "Node does not contain location " + loc;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we always check this condition? assert would only be enabled during unit tests...

I think, it would be better to always check this and throw this as a IllegalArgumentException. If we don't check this, we allow to create confusing violations - maybe better to prevent that at the first place?
Performance wise I think, it should not be too bad: This method would only be called, when a rule is about to report a violation, wouldn't it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the kind of assertion that should only be enabled during unit tests. It is relatively expensive to check and will likely be caught by rule unit tests immediately if you used it wrong. And also, nothing bad will happen if this condition is not checked at runtime: no code relies on this assumption being true. Maybe it is completely useless to check this tbh... I just wanted to prevent obvious mistakes by a rule implementor.

It's true it would be only called when reporting a violation but there are already sort of "generic" rules like AbstractCounterCheckRule that use it every time they report something. Those can be used to report metrics (with a very low report level) and therefore report may produce many violations...

* @since 7.20.0
*/
@CheckReturnValue
public ViolationBuilder atLine(int lineNumber) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we really provide this method? It sounds to me a bit lower level. Do you use this method already in some rules?
Used in

CommentContentRule (java)
CommentSizeRule (java)
AvoidTabCharacterRule (plsql)
LineLengthRule (plsql)

Only 4 rules need this. I'd prefer not to provide this as an API, so that there is only one way to create a violation from a rule context. This keeps the API surface smaller.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a case of "only 4 rules need this for now"... These 4 rules could actually apply to all languages and so we could add rules that use it in the future. It's also a method that is not obvious to implement and which therefore, I think, belongs in a shared API (even if "only" 4 rules need it).

If you want we can make it @Experimental, but this is also an easy to specify method so I don't think there is anything we would need to change about it.

private final RootNode rootNode;

// Default message, parsed only once to avoid overhead of parsing.
private MessageFormat defaultMessageFormat;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth it? A new RuleContext is created for every file and rule. The message would only be reused, if the same rule finds more than one violation in a single file...


/** Marker annotation for Intellij inspection to warn on unused return value. */
@Documented
@interface CheckReturnValue {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably should move this annotation to net.sourceforge.pmd.annotation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or did you mean to use com.google.errorprone.annotations.CheckReturnValue? We get it currently transitively via com.google.code.gson:gson, but if we use it ourselves, we should at a explicit dependency.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intellij inspection handles any annotation called CheckReturnValue. It doesn't even need to be public (which is why I kept this annotation package private). If we really want to use a well-known annotation then I would suggest using jetbrains annotations instead, because that package is small and would give us @NotNull/@Nullable (#6134).

@adangel adangel changed the title [core] Refactor RuleContext to use builder pattern [core] Fix #5039: Refactor RuleContext to use builder pattern Jan 8, 2026
@oowekyala
Copy link
Member Author

If we introduce the new API with the violation builder without marking it Experimental (like it is right now in this PR), then I'd deprecate all the old methods addViolation... right now. No need to wait, because we have then already a stable alternative API.

Are you ok with doing this in a companion PR? I would prefer this PR to be only about the new API and the other being just mechanical translation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

an:enhancement An improvement on existing features / rules

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[core] Add fluent API to report violations

2 participants