JAVASE-241 FPs when Jakarta / Javax NotNull annotation is used - #5990
JAVASE-241 FPs when Jakarta / Javax NotNull annotation is used#5990asya-vorobeva wants to merge 5 commits into
Conversation
Delegate path/naming heuristics to TestFileClassifier from sonar-analyzer-commons and extend it with Java-specific signals (IT paths, filename suffixes/prefix). - Remove hasTestNamingConvention() and hasTestPathSegment(); replaced by TestFileClassifier.of() with JAVA_TEST_PATTERNS covering test/tests/testing directory segments, src/it/java, src/its/java, *Test/*Spec/*IT suffixes, and the new Test* prefix convention. - Cache the TestFileClassifier per Configuration via AtomicReference so WildcardPatterns are compiled once per analysis run, not once per file. - Keep isPlatformTestFile() and hasTestFrameworkAnnotation() unchanged; public API isTestFile(context) is backward-compatible. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The **/Test*.java pattern is too broad and generates too many false positives (e.g. TestUtils, TestHelper production utilities). Suffix-based patterns (*Test, *Tests, *TestCase, *IT, *ITCase, *Spec, *Specs) provide sufficient recall without the noise. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s unaffected When sonar.tests is configured, TestFileClassifier suppresses its path/naming heuristic because the platform already classifies test files as TEST. Add three tests to lock in this boundary: - naming and path signals are ignored when sonar.tests is set - InputFile.Type.TEST remains authoritative regardless - annotation signal (hasTestFrameworkAnnotation) is config-independent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add **/IT/java/** and **/ITS/java/** to JAVA_TEST_PATTERNS to cover projects that use uppercase directory names for integration tests. Update Javadoc and consolidate the four separate IT/ITS path tests into one. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
javax.validation.constraints.NotNull and jakarta.validation.constraints.NotNull are runtime constraints, not static nullability guarantees. Their previous NON_NULL classification also caused an inconsistency (SONARJAVA-3803): @NotNull without arguments resolved to NON_NULL while @NotNull(groups=...) resolved to UNKNOWN. Moving both to WEAK_NULLABLE gives consistent, conservative treatment. Rules fixed as a direct consequence: - S4454: @NotNull on equals() parameter no longer fires (WEAK_NULLABLE is not isNonNull()) - S6539: @NotNull inside @NullMarked no longer flagged as redundant Rules requiring explicit guards after reclassification: - S4682: added FQN-based exclusion — BV @NotNull on a primitive return type is a validation constraint, not a nullable annotation - S2638: added isBeanValidationAnnotation() guard in compareNullability() — when the upper-bound annotation is a BV annotation, the comparison is skipped so that BV @NotNull on a parent param or child return does not incorrectly fire Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| // Bean Validation @NotNull is a runtime constraint, not a static nullability guarantee. | ||
| // It is placed here rather than NONNULL_ANNOTATIONS because it cannot serve as a reliable | ||
| // static analysis signal (especially when groups= is used), so it is treated conservatively. | ||
| "javax.validation.constraints.NotNull", | ||
| "jakarta.validation.constraints.NotNull", |
There was a problem hiding this comment.
⚠️ Bug: @NotNull reclassified globally causes new FP in Spring S6816
Moving javax/jakarta.validation.constraints.NotNull from NONNULL_ANNOTATIONS to WEAK_NULLABLE_ANNOTATIONS is a global change that affects every rule reading nullabilityData(), not just the two rules patched with explicit exclusions. In S6816 (NullableInjectedFieldsHaveDefaultValueCheck), getNullableAnnotation() returns present whenever an annotation exists and isNonNull() is false; a Spring @Value("${x}") @NotNull field (a very common combination) now satisfies that condition and gets flagged "Provide a default null value for this field" — contradictory advice for a field explicitly required to be non-null. This is a new false positive introduced by the reclassification and is not covered by any test in this PR. Consider either keeping @NotNull out of both the NONNULL and NULLABLE sets (so it resolves to NO_ANNOTATION/UNKNOWN and only the intended rules change), or adding a bean-validation guard to S6816 like the ones added to S2638/S4682.
Guard S6816 against Bean Validation @NotNull (mirroring the exclusion sets added to S2638/S4682).:
private static Optional<AnnotationTree> getNullableAnnotation(VariableTree field) {
SymbolMetadata.NullabilityData nullabilityData = field.symbol().metadata().nullabilityData(SymbolMetadata.NullabilityTarget.FIELD);
SymbolMetadata.AnnotationInstance instance = nullabilityData.annotation();
if (instance == null
|| nullabilityData.isNonNull(SymbolMetadata.NullabilityLevel.CLASS, false, false)
|| isBeanValidationConstraint(instance)) {
return Optional.empty();
}
return Optional.ofNullable(field.symbol().metadata().findAnnotationTree(instance));
}
Was this helpful? React with 👍 / 👎
| // Bean Validation @NotNull is a runtime constraint, not a static nullability guarantee. | ||
| // It is placed here rather than NONNULL_ANNOTATIONS because it cannot serve as a reliable | ||
| // static analysis signal (especially when groups= is used), so it is treated conservatively. | ||
| "javax.validation.constraints.NotNull", | ||
| "jakarta.validation.constraints.NotNull", |
There was a problem hiding this comment.
💡 Bug: Global @NotNull move silently changes S2789/S2447/S4454 behavior
Beyond the two patched rules, the reclassification changes other rules that read nullability with no accompanying tests: S2789 (NullShouldNotBeUsedWithOptionalCheck) will now report an @NotNull Optional<...> method as "should not be @NotNull" (new false positive via isNullable at line 175); S2447 (BooleanMethodReturnCheck) will stop checking @NotNull Boolean methods that return null literals, since !isNullable(...) is now false (new false negative); and S4454 (EqualsParametersMarkedNonNullCheck) stops flagging @NotNull equals parameters (the PR's test change to EqualsParametersMarkedNonNullCheckSample marks this Compliant, so likely intended, but S2789/S2447 appear unintended). Add ruling/test coverage for these rules to confirm the behavior changes are acceptable, or gate the reclassification the same way the two patched rules do.
Avoid globally tagging @NotNull as nullable; scope the special-casing to the intended rules.:
// Prefer not classifying a runtime constraint as a static nullability signal at all:
// keep javax/jakarta validation @NotNull OUT of both WEAK_NULLABLE_ANNOTATIONS and
// NONNULL_ANNOTATIONS so it resolves to NO_ANNOTATION, then only S2638/S4682 need the
// explicit bean-validation handling. This avoids the S2789/S2447/S4454 side effects.
Was this helpful? React with 👍 / 👎
Code Review
|
| Auto-apply | Compact | Unblock |
|
|
|
Was this helpful? React with 👍 / 👎 | Gitar
|




Treat Javax / Jakarta Bean Validation @NotNull annotations as WEAK_NULLABLE.