You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Added GeneratedStringLiteralRecognizer to suppress string literals likely generated in Kotlin annotations
Check updates:
Updated ControlCharacterInLiteralCheck to ignore recognized generated string literals
This will update automatically on new commits.
hashicorp-vault-sonar-prodBot
changed the title
S2479 Reduce noise by suppressing on strings that are most likely generated
SONARJAVA-6876 S2479 Reduce noise by suppressing on strings that are most likely generated
Aug 28, 2026
Adds GeneratedStringLiteralRecognizer to reduce noise in ControlCharacterInLiteralCheck by suppressing string literals likely generated in Kotlin annotations. The helper now checks for generated strings before the cheaper regex validation, and the semantic analysis path is covered by existing tests. No issues found.
✅ 3 resolved✅ Performance: isGenerated runs for every literal before the cheap regex check
📄 java-checks/src/main/java/org/sonar/java/checks/ControlCharacterInLiteralCheck.java:68-82📄 java-checks/src/main/java/org/sonar/java/checks/helpers/GeneratedStringLiteralRecognizer.java:52-66 isGenerated is invoked as the first statement of visitNode, so for every string/char literal/text block in every analysed file it walks the parent chain up to the compilation-unit root (the while loop only stops on an ARGUMENTS parent, which the vast majority of literals never have), and in the unresolved-type case it additionally runs concatenate plus a full import scan up to three times. The suppression is only needed for the tiny fraction of literals that actually contain a control character, so moving the call behind matcher.find() yields identical behaviour with strictly less work on the hot path of a rule that subscribes to all literals.
✅ Quality: No withoutSemantic() test; shadowing case only covers semantic path
📄 java-checks/src/test/java/org/sonar/java/checks/helpers/GeneratedStringLiteralRecognizerTest.java:32-46📄 java-checks-test-sources/default/src/main/java/checks/helpers/GeneratedStringLiteralRecognizerSample.java:3📄 java-checks-test-sources/default/src/main/java/checks/helpers/GeneratedStringLiteralRecognizerSample.java:32-41
The new test class only runs with the full classpath and with withClassPath(List.of()), not with the repo-mandated withoutSemantic(). InternalCheckVerifier.scanFiles skips enableSemanticWithProjectClasspath when withoutSemantic is set, so in that mode even the source-declared nested @interface Metadata has an unknown symbolType; isAnnotationType then falls back to simple-name matching and finds import kotlin.Metadata in the same file, so ShadowedMetadataSample.Annotated (line 38, marked // Compliant) would be classified as generated. The shadowing scenario the sample claims to cover is therefore only validated on the resolved-type path. Add the withoutSemantic() test and put the shadowing case in a file that does not import kotlin.Metadata so it is meaningful in both modes; also add a text-block case, since isGenerated explicitly accepts Tree.Kind.TEXT_BLOCK but no text block appears in the sample.
✅ Bug: Unknown-type fallback now needs bindings; breaks without semantics
📄 java-checks/src/main/java/org/sonar/java/checks/helpers/GeneratedStringLiteralRecognizer.java:75-80📄 java-checks/src/main/java/org/sonar/java/checks/ControlCharacterInLiteralCheck.java:81-83📄 java-checks/src/main/java/org/sonar/java/checks/helpers/GeneratedStringLiteralRecognizer.java:26-35📄 java-checks/src/test/java/org/sonar/java/checks/helpers/GeneratedStringLiteralRecognizerTest.java:79-93
The fallback for unresolved annotations changed from a purely syntactic check (ExpressionsHelper.concatenate(annotation.annotationType()) plus an explicit-import check) to annotation.symbolType().name(). When no ECJ bindings exist at all (semantic disabled — AbstractTypedTree.symbolType() returns Type.UNKNOWN and Symbols.UnknownType.name() is "!Unknown!"), name() can never equal Metadata/SourceDebugExtension/DebugMetadata, so suppression silently stops working; the old syntactic path still worked there. Concretely, checks/ControlCharacterInLiteralCheck.java:5 (@Metadata(d1 = {"U+200B …"}) // Compliant, generated string) is scanned by ControlCharacterInLiteralCheckTest.test_without_semantic(), which will now get an unexpected issue on that line. Keep a syntax-based fallback (annotation type tree name) for the unresolved case instead of relying on symbolType().name().
Options
Auto-apply is off → Gitar will not commit updates to this branch. Display: compact → Showing less information.
Comment with these commands to change the behavior for this request:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of AT-82
Summary by Gitar
GeneratedStringLiteralRecognizerto suppress string literals likely generated in Kotlin annotationsControlCharacterInLiteralCheckto ignore recognized generated string literalsThis will update automatically on new commits.