Skip to content

Fix "unneeded.suppression" warning#7747

Merged
smillst merged 13 commits into
typetools:masterfrom
mernst:warn-unneeded-efficiency
May 29, 2026
Merged

Fix "unneeded.suppression" warning#7747
smillst merged 13 commits into
typetools:masterfrom
mernst:warn-unneeded-efficiency

Conversation

@mernst
Copy link
Copy Markdown
Member

@mernst mernst commented May 28, 2026

No description provided.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 23eb33cc-9d76-4f12-8185-87a72cd17b72

📥 Commits

Reviewing files that changed from the base of the PR and between 392d779 and 17a0681.

📒 Files selected for processing (3)
  • docs/manual/introduction.tex
  • docs/manual/warnings.tex
  • framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java

📝 Walkthrough

Walkthrough

SourceChecker drops exported suppression constants and rewrites unneeded-suppression handling: warnUnneededSuppressions now takes checkerPrefixes and inspects @SuppressWarnings forms directly; reportUnneededSuppression and shouldSuppress were updated to use literal message-key strings and to compute the in-effect message key per suppression entry. Several local @SuppressWarnings("nullness") annotations were removed or narrowed, a PuritySubstring test was added, and documentation (warnings and intro) plus a docs typo were updated.

Possibly related PRs

  • typetools/checker-framework#7650: Changes to suppression-prefix handling for rlccalledmethods/calledmethods that rely on the same SourceChecker suppression parsing/matching logic.

Suggested reviewers

  • smillst
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java (1)

2825-2835: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Compare against messageKey, not the suppression token.

At Line 2825 and Line 2834, the check now uses currentSuppressWarningsInEffect. In these branches, that value is a prefix (or "all"), so the condition is effectively always true and can incorrectly suppress the unneeded.suppression warning.

🔧 Proposed fix
-          boolean result = !currentSuppressWarningsInEffect.equals("unneeded.suppression");
+          boolean result = !messageKey.equals("unneeded.suppression");
           return result;
...
-          boolean result = !currentSuppressWarningsInEffect.equals("unneeded.suppression");
+          boolean result = !messageKey.equals("unneeded.suppression");
           return result;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java`
around lines 2825 - 2835, The code is comparing the suppression token
(currentSuppressWarningsInEffect) against "unneeded.suppression" and therefore
always succeeds for prefixes/"all"; change those checks to compare the actual
message key instead. In the SourceChecker logic where you currently compute
boolean result = !currentSuppressWarningsInEffect.equals("unneeded.suppression")
(both occurrences guarded by requirePrefixInWarningSuppressions and
SUPPRESS_ALL_MESSAGE_KEY), replace the comparison to use messageKey (e.g.,
boolean result = !messageKey.equals("unneeded.suppression")) so suppression
decisions check the real warning message key rather than the suppression token.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java`:
- Around line 2825-2835: The code is comparing the suppression token
(currentSuppressWarningsInEffect) against "unneeded.suppression" and therefore
always succeeds for prefixes/"all"; change those checks to compare the actual
message key instead. In the SourceChecker logic where you currently compute
boolean result = !currentSuppressWarningsInEffect.equals("unneeded.suppression")
(both occurrences guarded by requirePrefixInWarningSuppressions and
SUPPRESS_ALL_MESSAGE_KEY), replace the comparison to use messageKey (e.g.,
boolean result = !messageKey.equals("unneeded.suppression")) so suppression
decisions check the real warning message key rather than the suppression token.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b702b2a8-b4cf-4e96-bfac-5bafd2a70cd5

📥 Commits

Reviewing files that changed from the base of the PR and between 92cb879 and 73de83d.

📒 Files selected for processing (1)
  • framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java (3)

2472-2480: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Remove the dead allErrorKeys plumbing from the warning pass.

warnUnneededSuppressions(...) no longer consults allErrorKeys, but the caller still rebuilds that set for every compilation unit. That extra scan is now pure overhead in the hot path this PR is optimizing.

♻️ Proposed cleanup
-    Set<String> errorKeys = new HashSet<>(messagesProperties.stringPropertyNames());
     for (SourceChecker subChecker : subcheckers) {
       allElementsWithSuppressedWarnings.addAll(subChecker.elementsWithSuppressedWarnings);
       subChecker.elementsWithSuppressedWarnings.clear();
       prefixes.addAll(subChecker.getSuppressWarningsPrefixes());
-      errorKeys.addAll(subChecker.messagesProperties.stringPropertyNames());
       subChecker.getVisitor().treesWithSuppressWarnings.clear();
     }
-    warnUnneededSuppressions(allElementsWithSuppressedWarnings, prefixes, errorKeys);
+    warnUnneededSuppressions(allElementsWithSuppressedWarnings, prefixes);
@@
-   * `@param` allErrorKeys all error keys that can be issued by this checker
    */
   protected void warnUnneededSuppressions(
-      Set<Element> elementsSuppress, Set<String> checkerPrefixes, Set<String> allErrorKeys) {
+      Set<Element> elementsSuppress, Set<String> checkerPrefixes) {

Also applies to: 2492-2496

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java`
around lines 2472 - 2480, The code builds a local Set<String> errorKeys from
messagesProperties.stringPropertyNames() and merges subcheckers'
messagesProperties into it only to pass to warnUnneededSuppressions, but
warnUnneededSuppressions no longer uses that parameter; remove this dead
"allErrorKeys" plumbing by deleting the creation and incremental population of
errorKeys (the HashSet based on messagesProperties.stringPropertyNames() and the
errorKeys.addAll(...) calls inside the subcheckers loop) and simply call
warnUnneededSuppressions(allElementsWithSuppressedWarnings, prefixes) (or the
current two-arg form); also remove the analogous redundant code in the other
similar block that merges subcheckers (the block using
elementsWithSuppressedWarnings, getSuppressWarningsPrefixes(),
messagesProperties, and getVisitor().treesWithSuppressWarnings).

2822-2835: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Check the emitted messageKey, not the suppression token.

These branches are supposed to block prefix-only suppressions from hiding the unneeded.suppression meta-warning, but currentSuppressWarningsInEffect is the prefix (checkername / all), so the guard is always true. As written, @SuppressWarnings("checkername") and @SuppressWarnings("all") still suppress the meta-warning, which violates the documented contract.

🐛 Proposed fix
         if (prefixes.contains(currentSuppressWarningsInEffect)) {
           // The value in the `@SuppressWarnings` is exactly a prefix.
           // Suppress the warning unless its message key is "unneeded.suppression".
-          boolean result = !currentSuppressWarningsInEffect.equals("unneeded.suppression");
-          return result;
+          return !messageKey.equals("unneeded.suppression");
         } else if (requirePrefixInWarningSuppressions) {
@@
         } else if (currentSuppressWarningsInEffect.equals(SUPPRESS_ALL_MESSAGE_KEY)) {
           // Prefixes aren't required and the SuppressWarnings string is "all".
           // Suppress the warning unless its message key is "unneeded.suppression".
-          boolean result = !currentSuppressWarningsInEffect.equals("unneeded.suppression");
-          return result;
+          return !messageKey.equals("unneeded.suppression");
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java`
around lines 2822 - 2835, The code is comparing the suppression token
(currentSuppressWarningsInEffect / SUPPRESS_ALL_MESSAGE_KEY) to
"unneeded.suppression" instead of checking the emitted diagnostic's message key,
so prefix-only suppressions still hide the meta-warning; update the three places
that compute boolean result to check the diagnostic message key instead (e.g.,
use messageKey.equals("unneeded.suppression") or message.getKey() if messageKey
isn't in scope) and return !messageKey.equals("unneeded.suppression") (or
!message.getKey().equals(...)) so that only a suppression that targets the
actual message key suppresses the meta-warning; keep the existing branches
(prefixes.contains(...), requirePrefixInWarningSuppressions,
SUPPRESS_ALL_MESSAGE_KEY) but change the equality check to the messageKey.

2518-2520: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Require an exact :unneeded.suppression suffix here.

regionMatches only checks the prefix, so strings like checker:unneeded.suppression.foo are treated as exempt and never reported, even though shouldSuppress(...) will not let them suppress the emitted meta-warning. Compare the suffix exactly instead of prefix-matching it.

🐛 Proposed fix
           if (colonPos != -1) {
             String warningPrefix = suppressWarningsString.substring(0, colonPos);
+            String suppressedKey = suppressWarningsString.substring(colonPos + 1);
             if (checkerPrefixes.contains(warningPrefix)
-                && !suppressWarningsString.regionMatches(
-                    colonPos, ":unneeded.suppression", 0, ":unneeded.suppression".length())) {
+                && !suppressedKey.equals("unneeded.suppression")) {
               reportUnneededSuppression(tree, suppressWarningsString);
             }
           }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java`
around lines 2518 - 2520, The current check uses regionMatches which only
matches a prefix at colonPos, allowing strings like
"checker:unneeded.suppression.foo" to slip through; change it to require an
exact suffix match by comparing the substring starting at colonPos to the
literal ":unneeded.suppression" (e.g., use
suppressWarningsString.substring(colonPos).equals(":unneeded.suppression") or
ensure colonPos + literal.length() == suppressWarningsString.length() after
regionMatches). Update the conditional that references checkerPrefixes,
warningPrefix, suppressWarningsString, and colonPos so it only treats the string
as exempt when the substring equals the exact literal (this keeps behavior
consistent with shouldSuppress(...)).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java`:
- Around line 2472-2480: The code builds a local Set<String> errorKeys from
messagesProperties.stringPropertyNames() and merges subcheckers'
messagesProperties into it only to pass to warnUnneededSuppressions, but
warnUnneededSuppressions no longer uses that parameter; remove this dead
"allErrorKeys" plumbing by deleting the creation and incremental population of
errorKeys (the HashSet based on messagesProperties.stringPropertyNames() and the
errorKeys.addAll(...) calls inside the subcheckers loop) and simply call
warnUnneededSuppressions(allElementsWithSuppressedWarnings, prefixes) (or the
current two-arg form); also remove the analogous redundant code in the other
similar block that merges subcheckers (the block using
elementsWithSuppressedWarnings, getSuppressWarningsPrefixes(),
messagesProperties, and getVisitor().treesWithSuppressWarnings).
- Around line 2822-2835: The code is comparing the suppression token
(currentSuppressWarningsInEffect / SUPPRESS_ALL_MESSAGE_KEY) to
"unneeded.suppression" instead of checking the emitted diagnostic's message key,
so prefix-only suppressions still hide the meta-warning; update the three places
that compute boolean result to check the diagnostic message key instead (e.g.,
use messageKey.equals("unneeded.suppression") or message.getKey() if messageKey
isn't in scope) and return !messageKey.equals("unneeded.suppression") (or
!message.getKey().equals(...)) so that only a suppression that targets the
actual message key suppresses the meta-warning; keep the existing branches
(prefixes.contains(...), requirePrefixInWarningSuppressions,
SUPPRESS_ALL_MESSAGE_KEY) but change the equality check to the messageKey.
- Around line 2518-2520: The current check uses regionMatches which only matches
a prefix at colonPos, allowing strings like "checker:unneeded.suppression.foo"
to slip through; change it to require an exact suffix match by comparing the
substring starting at colonPos to the literal ":unneeded.suppression" (e.g., use
suppressWarningsString.substring(colonPos).equals(":unneeded.suppression") or
ensure colonPos + literal.length() == suppressWarningsString.length() after
regionMatches). Update the conditional that references checkerPrefixes,
warningPrefix, suppressWarningsString, and colonPos so it only treats the string
as exempt when the substring equals the exact literal (this keeps behavior
consistent with shouldSuppress(...)).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a48038e8-7995-49a3-8fab-35436769854e

📥 Commits

Reviewing files that changed from the base of the PR and between 73de83d and 990b6a6.

📒 Files selected for processing (1)
  • framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java

@mernst mernst changed the title Improve efficiency of "unneeded.suppression" warning Fix "unneeded.suppression" warning May 28, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java`:
- Around line 2843-2847: The branch in SourceChecker that currently checks
currentSuppressWarningsPrefix.equals("allcheckers") ||
!prefixes.contains(currentSuppressWarningsPrefix) wrongly skips
"allcheckers:<key>" forms; update the logic used in the loop that evaluates
suppressWarningsString() so that if currentSuppressWarningsPrefix
startsWith("allcheckers") (or matches the exact "allcheckers" token with a colon
suffix) you treat it as a valid global prefix rather than skipping it — i.e.,
detect the "allcheckers:" variant from currentSuppressWarningsPrefix and allow
it to be handled like other supported prefixes (so that
warnUnneededSuppressions(...) will recognize and not report those suppressions
as unused).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 25d65127-1aba-4e9e-a64b-fc205535ebbe

📥 Commits

Reviewing files that changed from the base of the PR and between ee084d7 and c6bc342.

📒 Files selected for processing (1)
  • framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java

Comment thread framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java Outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@smillst smillst merged commit 264c73d into typetools:master May 29, 2026
35 checks passed
@smillst smillst deleted the warn-unneeded-efficiency branch May 29, 2026 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants