SONARJAVA-6790: Implement S9350: equals() implementations should not compare mismatched members - #5946
SONARJAVA-6790: Implement S9350: equals() implementations should not compare mismatched members#5946nathsou wants to merge 9 commits into
Conversation
| Map<Tree, Set<MemberPair>> pairsByStatement = new HashMap<>(); | ||
| for (ComparisonSite comparison : collector.comparisons) { | ||
| pairsByStatement.computeIfAbsent(comparison.statement, key -> new HashSet<>()).add(comparison.pair()); | ||
| } | ||
| for (ComparisonSite comparison : collector.comparisons) { | ||
| if (pairsByStatement.get(comparison.statement).contains(comparison.pair().reversed())) { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
💡 Edge Case: Per-statement swap exception misses order-independent equality split across statements
The swapped-pair exception is now scoped per enclosing statement (pairsByStatement.get(comparison.statement)), so an order-independent equality whose forward and reversed comparisons live in different statements will no longer be recognized as symmetric and will be reported as a false positive. Example: boolean f = this.a == that.b; on one line and boolean b = this.b == that.a; on another produces two issues even though together they express symmetric equality. This is uncommon, but if you want to preserve suppression for such cases consider also matching reversed pairs found anywhere in the same method while keeping the per-statement grouping for the DistinctStatements case.
Was this helpful? React with 👍 / 👎
|
❌ Ruling needs updating. A fix PR has been created: #5948 Please review and merge it into your branch. |
Ruling flagged this.foo.equals(that.foo) on generic types because JSymbol equality includes type arguments, so the same field looked mismatched.
romainbrenguier
left a comment
There was a problem hiding this comment.
It needs a test without semantic, otherwise it looks ok
| CheckVerifier.newVerifier() | ||
| .onFile(mainCodeSourcesPath("checks/EqualsMismatchedMembersCheckSample.java")) | ||
| .withCheck(new EqualsMismatchedMembersCheck()) | ||
| .verifyIssues(); |
There was a problem hiding this comment.
We should have a similar test using .withoutSemantic
There was a problem hiding this comment.
Added test_without_semantic() on the compiling sample. The check still reports the same issues from intra-file symbols when no bytecode is provided, so this uses .withoutSemantic().verifyIssues() like SillyEqualsCheckTest.
Code Review 👍 Approved with suggestions 3 resolved / 4 findingsImplements rule S9350 to flag 💡 Edge Case: Per-statement swap exception misses order-independent equality split across statements📄 java-checks/src/main/java/org/sonar/java/checks/EqualsMismatchedMembersCheck.java:94-101 📄 java-checks/src/main/java/org/sonar/java/checks/EqualsMismatchedMembersCheck.java:269-275 The swapped-pair exception is now scoped per enclosing statement ( ✅ 3 resolved✅ Edge Case: Mismatched-member check ignores which instance operands belong to
✅ Quality: MemberRef.symbol component is now unused after name-based switch
✅ Edge Case: Name-based
|
| Auto-apply | Compact |
|
|
Was this helpful? React with 👍 / 👎 | Gitar
|




Summary
equals()implementations comparing unmatched instance fields or getters of the enclosing type.==/!=,Objects.equals, GuavaObjects.equal,Arrays.equals, instanceequals, and 0-arg getters/record accessors, including the swapped-pair exception for order-independent equality.Links
AI disclosure