Skip to content

SONARJAVA-6951: Implemented rule S9390 - Manual null checks should be replaced with Objects.equals() - #6123

Merged
romainbrenguier merged 5 commits into
masterfrom
romain/new-rule-s9390-sonarjava-6951
Sep 14, 2026
Merged

SONARJAVA-6951: Implemented rule S9390 - Manual null checks should be replaced with Objects.equals()#6123
romainbrenguier merged 5 commits into
masterfrom
romain/new-rule-s9390-sonarjava-6951

Conversation

@romainbrenguier

Copy link
Copy Markdown
Contributor

Implements rule S9390 which detects ternary expressions performing redundant null-safe equality checks.

The pattern is semantically equivalent to but more verbose and error-prone.

Changes:

  • Added - the rule implementation using
  • Added - test class with semantic and non-semantic tests
  • Added - comprehensive test cases with semantic analysis
  • Added - test cases for non-semantic analysis

The rule detects:

  • → suggests
  • → suggests
  • Qualified identifiers:

Accepted false negatives:

  • Method calls:
  • Complex expressions with or in the false branch

@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

SONARJAVA-6951

@datadog-sonarsource

datadog-sonarsource Bot commented Sep 11, 2026

Copy link
Copy Markdown

Pipelines

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 7f1356b | Docs | View more details | Give us feedback!

gitar-bot[bot]

This comment was marked as resolved.

- Add missing field `d` in test sample to fix compilation
- Add rule metadata files (S9390.json, S9390.html) and Sonar way profile
- Use syntactic method name lookup to fix rule not firing without semantics
- Guard against overloaded equals(SpecificType) methods to prevent false positives
- Remove duplicated null-comparison logic in matchesPattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gitar-bot
gitar-bot Bot dismissed their stale review September 14, 2026 08:36

✅ Code review updated (blocking issues remain unresolved).

Configure merge blocking

@github-actions

Copy link
Copy Markdown
Contributor

Ruling needs updating. A fix PR has been created: #6132

Please review and merge it into your branch.

Simplify boolean return in matchesPattern to fix SonarQube code smell,
add test cases to improve coverage above 90%, and add ruling baseline
for sonar-server findings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Ruling Diff Summary

Detected changes in 1 rule files: 0 issues removed, 2 issues added.

S9390 (java) on sonar-server - 0 issues removed, 2 issues added - new ruling file

Added src/main/java/org/sonar/server/qualityprofile/ws/QProfileReference.java (line 139)

(source file not found at this revision: src/main/java/org/sonar/server/qualityprofile/ws/QProfileReference.java)

Added src/main/java/org/sonar/server/usergroups/ws/GroupWsRef.java (line 143)

(source file not found at this revision: src/main/java/org/sonar/server/usergroups/ws/GroupWsRef.java)

@romainbrenguier
romainbrenguier marked this pull request as ready for review September 14, 2026 09:33
romainbrenguier and others added 2 commits September 14, 2026 11:50
Use // prefix for caret lines, remove [[sc;ec]] annotations, and
correct marker alignment to match actual reported issue locations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@nathsou nathsou left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good!

@gitar-bot

gitar-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 5 resolved / 5 findings

Implements rule S9390 to detect manual null checks that should use Objects.equals() instead. All findings have been resolved: the rule now fires correctly with semantic analysis, test files compile, false positives on overloaded equals methods are eliminated, null-comparison parsing is deduplicated, and S9390 metadata and profile registration are complete.

✅ 5 resolved
Bug: Rule never fires without semantics: methodSymbol().name() is unknown

📄 java-checks/src/main/java/org/sonar/java/checks/ObjectsEqualsCheck.java:128-135 📄 java-checks-test-sources/default/src/main/java/checks/ObjectsEqualsCheckNoSemanticSample.java:12-19 📄 java-checks/src/test/java/org/sonar/java/checks/ObjectsEqualsCheckTest.java:34-41
extractEqualsCall matches the method name via mit.methodSymbol().name(), but with no semantic information (which is exactly what test_without_semantic sets up via withoutSemantic()) methodSymbol() returns Symbol.MethodSymbol.UNKNOWN_METHOD whose name() is "!unknownMethod!" (java-frontend/src/main/java/org/sonar/java/model/expression/MethodInvocationTreeImpl.java, org/sonar/java/model/Symbols.java:305). So for a != null ? a.equals(b) : b == null in ObjectsEqualsCheckNoSemanticSample.java no issue is raised and every // Noncompliant expectation in that file (lines 12, 15, 18, 19, 63, 66, 72, 79, 85, 91, 97) fails. Match the name syntactically with ExpressionUtils.methodName(mit).name().

Bug: Sample file does not compile: undeclared identifier d

📄 java-checks-test-sources/default/src/main/java/checks/ObjectsEqualsCheckSample.java:6-9 📄 java-checks-test-sources/default/src/main/java/checks/ObjectsEqualsCheckSample.java:115 📄 java-checks-test-sources/default/src/main/java/checks/ObjectsEqualsCheckSample.java:132
ObjectsEqualsCheckSample declares only the fields a, b, c and other, but lines 115 and 132 reference d (c.equals(d) : d == null). java-checks-test-sources/default compiles src/main/java with the standard compiler plugin (no failOnError=false; non-compiling samples live under src/main/java/files/non-compiling/), so this breaks the module build and therefore the rule's own test run. Declare an Object d; field (or reuse an existing one).

Edge Case: False positive on overloaded equals(SpecificType) methods

📄 java-checks/src/main/java/org/sonar/java/checks/ObjectsEqualsCheck.java:128-142
extractEqualsCall accepts any single-argument method named equals, without checking that it resolves to equals(Object). For a class declaring an overload such as boolean equals(MyType other), a != null ? a.equals(b) : b == null (with b of type MyType) binds to the overload, whereas the suggested Objects.equals(a, b) dispatches to equals(Object) — so applying the rule's advice changes behaviour. When the method symbol is known, verify the declared parameter type is java.lang.Object (e.g. via MethodMatchers / methodSymbol().parameterTypes()), and fall back to the syntactic name only when semantics are unavailable.

Quality: Duplicated null-comparison parsing in matchesPattern

📄 java-checks/src/main/java/org/sonar/java/checks/ObjectsEqualsCheck.java:51-59 📄 java-checks/src/main/java/org/sonar/java/checks/ObjectsEqualsCheck.java:84-98
matchesPattern calls isNotEqualToNull(condition) and then getIdentifierFromNotEqualToNull(condition), which re-runs the same kind check and re-parses both operands; the first call is fully redundant because the extractor already returns null for non-matching trees. Drop the isNotEqualToNull guard in matchesPattern (and its now single-use duplicate logic) so the operand parsing exists in one place.

Bug: Missing S9390 metadata (json/html) and profile registration

📄 java-checks/src/main/java/org/sonar/java/checks/ObjectsEqualsCheck.java:31
No S9390.json / S9390.html exists under sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/, and there is no profiles/Sonar_way/S9390 (nor Sonar_agentic_AI/S9390) entry. GeneratedCheckListTest asserts that every check discovered by the generated check list has both metadata files, so the plugin test suite fails and the rule cannot be loaded by RuleMetadataLoader; without the profile file the rule is also absent from Sonar way. Add the metadata files and the profile marker file per sonar-java-plugin/src/main/resources/profiles/README.md.

Implementation Status ✅ 1 of 1 objectives covered
SONARJAVA-6951 - 1 of 1 objectives covered

This PR implements rule S9390 to replace manual null checks with Objects.equals().

✅ 1 covered here
  • ✅ Implement rule S9390 to replace manual null checks with Objects.equals()
Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Counting what did not apply, without listing it.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqube-next

Copy link
Copy Markdown
Contributor

@romainbrenguier
romainbrenguier merged commit f29eeab into master Sep 14, 2026
28 of 29 checks passed
@romainbrenguier
romainbrenguier deleted the romain/new-rule-s9390-sonarjava-6951 branch September 14, 2026 14:39
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