SONARJAVA-6824: Implemented rule S9358 - Conditional expressions should not duplicate operations in both branches - #6001
Conversation
…ld not duplicate operations in both branches This rule detects when a ternary operator applies the same operation (method invocation, object creation, or array access) to different arguments in both branches. Such patterns can be refactored by moving the condition inside the operation for better readability.
This comment has been minimized.
This comment has been minimized.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move test cases into methods with properly typed local variables to fix compilation errors in TernaryOperatorSameOperationCheckSample - Use ExpressionUtils.skipParentheses() to handle parenthesized expressions in ternary branches - Fix argument comparison logic to flag cases where at least one argument differs (not only when all arguments differ) - Add test cases for multi-argument scenarios Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with GitHub Actions
|
❌ Ruling needs updating. A fix PR has been created: #6012 Please review and merge it into your branch. |
- Change type from String to Object for ternary with Foo/Bar constructors - Merge ruling expectation updates from PR #6012 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ruling Diff SummaryDetected changes in 4 rule files: 0 issues removed, 16 issues added. S9358 (
|
|
❌ Ruling needs updating. A fix PR has been created: #6014 Please review and merge it into your branch. |
- Remove unused imports (ArrayDimensionTree, TypeTree) - Remove dead code: null checks that always evaluate to false - Reduce duplication by extracting hasExactlyOneArgumentDifference() and consolidating sameExpression/sameTree into a single method - Remove unused typeArguments handling in sameNewClass - Add more test cases for edge cases (no-arg methods, different receivers, mixed expression kinds, member select edge cases) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with GitHub Actions
|
❌ Ruling needs updating. A fix PR has been created: #6014 Please review and merge it into your branch. |
…ces and add ruling expectations The method was using a boolean flag that returned true when any arguments differed, causing false positives for cases with multiple differing arguments. Now uses an integer counter to ensure exactly one argument differs. Also adds eclipse-jetty ruling expectations and test cases for multiple argument differences. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
' of github.com:SonarSource/sonar-java into romain/new-rule-s9358-sonarjava-6824
🤖 Generated with GitHub Actions
|
❌ Ruling needs updating. A fix PR has been created: #6016 Please review and merge it into your branch. |
|
❌ Ruling needs updating. A fix PR has been created: #6018 Please review and merge it into your branch. |
…ar-to-main Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| } | ||
|
|
||
| private static boolean sameMethodInvocation(MethodInvocationTree left, MethodInvocationTree right) { | ||
| return sameMethodSelect(left.methodSelect(), right.methodSelect()) |
There was a problem hiding this comment.
Method names do not identify the invoked operation. For example, condition ? overloaded(1) : overloaded("x") invokes distinct overloads but is reported, while condition ? foo(a) : this.foo(b) invokes the same method but is missed. The suggested refactoring for the overload case may not compile. Please compare resolved method symbols when semantic information is available, and avoid reporting ambiguous syntax-only matches.
There was a problem hiding this comment.
Addressed in commit 0e2ef4b: the check now compares resolved method symbols via sameMethodSymbol. When semantic information is available, overloaded methods with different signatures (e.g. overloaded(int) vs overloaded(String)) are correctly identified as different operations and not reported. When symbols are unknown (no semantics), the check falls back to treating them as the same method to avoid false negatives. Additionally, sameTree now uses SyntacticEquivalence.areEquivalent for proper structural comparison, and test cases have been added for these scenarios.
| } | ||
|
|
||
| private static boolean sameNewClass(NewClassTree left, NewClassTree right) { | ||
| if (!sameTree(left.identifier(), right.identifier())) { |
There was a problem hiding this comment.
This comparison ignores constructor identity and the rest of the creation context. condition ? new Overloaded(1) : new Overloaded("x") is reported even when those arguments select different constructors; enclosing instances and anonymous class bodies are ignored as well. Please compare resolved constructor symbols and relevant NewClassTree context before treating these branches as the same operation.
There was a problem hiding this comment.
Addressed in commit 0e2ef4b: sameNewClass now compares resolved constructor symbols via sameMethodSymbol, checks for anonymous class bodies (returns false if either has a class body), and compares enclosing expressions via sameNullableTree. Test cases have been added for overloaded constructors, anonymous class bodies, and qualified instantiations (obj.new Inner(a) vs obj.new Inner(b)).
There was a problem hiding this comment.
Addressed in commit 0e2ef4b: sameNewClass now compares resolved constructor symbols via sameMethodSymbol, checks for anonymous class bodies (returns false if either has a class body), and compares enclosing expressions via sameNullableTree. Test cases have been added for overloaded constructors, anonymous class bodies, and qualified instantiations (obj.new Inner(a) vs obj.new Inner(b)).
…ves on overloaded methods Use resolved method/constructor symbols instead of syntactic name comparison to correctly distinguish overloaded methods and constructors. When semantic info is unavailable, fall back to syntactic matching. Also exclude new expressions with anonymous class bodies or differing enclosing expressions. Update ruling expectations to remove identified false positives. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…test coverage Replace toString()-based tree comparison with SyntacticEquivalence.areEquivalent() to correctly handle non-identifier receivers (e.g. getObj().foo(a) vs getObj().foo(b)). Add test cases for qualified instantiations, mixed enclosing expressions, non-identifier receivers, and anonymous class bodies in without-semantic mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with GitHub Actions
|
❌ Ruling needs updating. A fix PR has been created: #6029 Please review and merge it into your branch. |
… condition findings Inline the null checks for enclosingExpression() directly in sameNewClass so SonarQube can properly track nullability from the @nullable return type. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code Review ✅ Approved 6 resolved / 6 findingsImplements rule S9358 to detect duplicated operations in conditional expression branches with comprehensive test coverage. Addressed missing resource files, argument difference counting, and edge cases with parentheses and nested ternaries. ✅ 6 resolved✅ Bug: Missing S9358 rule metadata (JSON/HTML) resource files
✅ Bug: Parentheses not unwrapped; nested-ternary test case won't be detected
✅ Edge Case: Only flags calls where every argument differs
✅ Bug: hasExactlyOneArgumentDifference flags any number of differing args
✅ Quality: New sameNullableTree/enclosingExpression branches are untested
...and 1 more resolved from earlier reviews Implementation Status ◻️ 0 of 1 objectives covered◻️ SONARJAVA-6824 - 0 of 1 objectives coveredThis PR does not cover the implementation of rule S9358 as the diff is empty or unrelated. Other objectives on this issue, possibly covered elsewhere:
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 |
|




This PR implements rule S9358 which detects when a ternary operator applies the same operation (method invocation, object creation, or array access) to different arguments in both branches.
Such patterns can be refactored by moving the condition inside the operation for better readability:
The rule handles:
Test cases cover both compliant and non-compliant scenarios including edge cases like nested ternaries, chained method calls, and generic type arguments.