-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[core] Fix #5039: Refactor RuleContext to use builder pattern #6260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This will replace the many `addViolation*` methods with a simpler set of orthogonal methods. - First specify the location. - Then specify the message/format arguments. Fix pmd#5039
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
|
Compared to main: (comment created at 2025-11-29 16:37:01+00:00 for c343925) |
Header comments are not considered part of the root node...
adangel
left a comment
There was a problem hiding this 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
...apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/ExcessiveParameterListRule.java
Outdated
Show resolved
Hide resolved
...apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/internal/AbstractCounterCheckRule.java
Outdated
Show resolved
Hide resolved
pmd-core/src/main/java/net/sourceforge/pmd/reporting/RuleContext.java
Outdated
Show resolved
Hide resolved
| */ | ||
| @Experimental | ||
| default Reportable atLocation(FileLocation loc) { | ||
| assert NodeFindingUtil.nodeCoversLocation(this, loc) : "Node does not contain location " + loc; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
pmd-core/src/main/java/net/sourceforge/pmd/reporting/RuleContext.java
Outdated
Show resolved
Hide resolved
| private final RootNode rootNode; | ||
|
|
||
| // Default message, parsed only once to avoid overhead of parsing. | ||
| private MessageFormat defaultMessageFormat; |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
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. |
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
Removed Experimental APIs
Deprecated APIs
Related issues
Ready?
./mvnw clean verifypasses (checked automatically by github actions)