Repository navigation
SONARJAVA-7242: Improve S9149 to not raise when the parent class design requires redeclaring static methods - #6400
Conversation
…gn requires redeclaring static methods Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Ruling Diff SummaryDetected changes in 2 rule files: 39 issues removed, 0 issues added. S9149 (
|
|
/pvf S9149 |
|
📊 Performance Validation complete |
nathsou
left a comment
There was a problem hiding this comment.
Approving: the Guava and suite() false positives are handled and the ruling looks right. Two points on the exemption's boundaries, both reproduced locally with CheckVerifier probes against this branch.
| private static boolean isRequiredByDesign(Symbol.MethodSymbol methodSymbol, Symbol.MethodSymbol hiddenMethod) { | ||
| Type returnType = methodSymbol.returnType().type().erasure(); | ||
| Type hiddenReturnType = hiddenMethod.returnType().type().erasure(); | ||
| boolean isCovariantFactory = !returnType.equals(hiddenReturnType) && returnType.isSubtypeOf(hiddenReturnType); |
There was a problem hiding this comment.
This exemption has no factory condition. Since JLS 8.4.8.3 requires a hiding method's return type to be return-type-substitutable for the hidden one, any difference in return types in compiling code means "exempt", so the rule now only fires when the return types are identical. For example, static String helper() hiding static Object helper() is no longer reported.
Could we restrict it to factory-like return types, e.g. the declaring class, a subtype of it, or a type nested in it? That should still cover the Guava cases (ImmutableBiMap, ImmutableBiMap.Builder), to be confirmed against the ruling.
There was a problem hiding this comment.
Done: the exemption now only applies when the covariant return type is the declaring class, a subtype of it, or a type nested in it. static String helper() hiding static Object helper() is reported again, and so is List<String> copyOf(...) hiding Collection<String> copyOf(...). A nested Builder case was added. The CI ruling will confirm the Guava baseline.
| private static boolean isJUnit3Suite(Symbol.MethodSymbol methodSymbol) { | ||
| return "suite".equals(methodSymbol.name()) | ||
| && methodSymbol.parameterTypes().isEmpty() | ||
| && methodSymbol.returnType().type().is("junit.framework.Test"); |
There was a problem hiding this comment.
The JUnit 3 runner (BaseTestRunner.getTest) accepts any suite() whose return type implements Test, and public static TestSuite suite() is common. With .is(...), a parent and child both declaring TestSuite suite() are still reported. isSubtypeOf("junit.framework.Test") would cover it.
There was a problem hiding this comment.
Done: switched to isSubtypeOf("junit.framework.Test") and added a TestSuite suite() test case.
…ubtypes for suite() Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| public static class ParentTestSuiteCase extends TestCase { | ||
| public static TestSuite suite() { | ||
| return new TestSuite(ParentTestSuiteCase.class); | ||
| } | ||
| } | ||
|
|
||
| public static class ChildTestSuiteCase extends ParentTestSuiteCase { | ||
| public static TestSuite suite() { // Compliant - JUnit 3 suite | ||
| return new TestSuite(ChildTestSuiteCase.class); | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Edge Case: New TestSuite fixture adds an autoscan FP; diff_S9149.json still says 1
This commit adds ChildTestSuiteCase.suite() (marked // Compliant - JUnit 3 suite) to the default sample. Autoscan runs without bytecode, so junit.framework.TestSuite and junit.framework.Test don't resolve. Both suite() return types then look identical, so isCovariantFactory returns false, and isSubtypeOf("junit.framework.Test") against an unknown class type also returns false. The rule therefore reports this method too. That is the same mechanism the PR description gives for the existing ChildTestCase.suite() false positive, so autoscan should now see 2 false positives. its/autoscan/src/test/resources/autoscan/diffs/diff_S9149.json was not updated and still has "falsePositives": 1. The PR description's "1 more false positive" claim is now out of date as well.
Bump the expected autoscan false-positive count to account for the new unresolvable suite() fixture:
{
"ruleKey": "S9149",
"hasTruePositives": true,
"falseNegatives": 0,
"falsePositives": 2
}
Was this helpful? React with 👍 / 👎
Code Review 👍 Approved with suggestions 1 closed / 2 findings🟡 Medium risk · Broadened S9149 exemptions for covariant factories and JUnit 3 suite() methods could suppress actionable static-method-hiding findings in edge cases. Restricts the S9149 exemption to covariant static factories and JUnit 3 💡 Edge Case: New TestSuite fixture adds an autoscan FP; diff_S9149.json still says 1📄 java-checks-test-sources/default/src/main/java/checks/StaticMethodHidingCheckSample.java:426-436 📄 java-checks/src/main/java/org/sonar/java/checks/StaticMethodHidingCheck.java:105-119 📄 its/autoscan/src/test/resources/autoscan/diffs/diff_S9149.json This commit adds Bump the expected autoscan false-positive count to account for the new unresolvable suite() fixture✅ 1 closed✅ Quality: "Unrelated return type" test actually uses identical return types
🤖 Prompt for agentsVerification Rules 2/2 met Repos 4 inspected · PRs 1 inspectedFunctional validation No results Rules 2 passed 1 not applicable
Broader impact Repos 4 inspected PRs 1 inspectedCross-repo 4 repositories inspected 0 open impacts
Cross-PR 1 PR inspected 0 open impacts
Auto-approval Not enabledFor more verification details, comment OptionsAuto-apply is off → Gitar will not commit updates to this branch. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Gitar |
|




Summary
S9149 (static methods should not hide methods from superclasses) raised on hiding that is required by the design of the parent class. The rule no longer raises in two such cases:
ImmutableBiMap.of()/copyOf()/builder()hidingImmutableMap's).public static Test suite(): the JUnit 3 runner looks this method up on each test class, so every subclass has to redeclare it.Hiding with the same or an unrelated return type, including unknown types, is still reported.
Ruling / autoscan
suite()issues in test classes removed; theLocaleBeanUtilsissues remain.junit.framework.Testcannot be resolved without bytecode. This is a prediction and not verified locally.Not addressed
The ticket also suggests not raising when the hidden method comes from an external library. This was not done: the semantic model has no reliable way to tell library classes apart from project classes compiled in other files. Those issues are still reported (e.g. hiding
Thread.sleep).🤖 Generated with Claude Code
Agent workflow
Tool link: https://github.com/SonarSource/languages-experimental-tooling/tree/romain/my-tickets/personal/romain-brenguier
PR created using
uv run create_with_claude.py SONARJAVA-7242.txtAddressed review comments in pr_report_6400.md using
uv run address_reviews.py pr_report_6400.md