SONARJAVA-6844: Implement S9394: "contentEquals()" should be used instead of "equals()" for "CharSequence" comparisons - #6140
SONARJAVA-6844: Implement S9394: "contentEquals()" should be used instead of "equals()" for "CharSequence" comparisons#6140nathsou wants to merge 2 commits into
Conversation
…tead of "equals()" for "CharSequence" comparisons
|
|
…ndle String-bounded generics
| } else if (ownerType.is("java.lang.String") && argumentType.isSubtypeOf("java.lang.CharSequence")) { | ||
| return; |
There was a problem hiding this comment.
⚠️ Bug: S2159 deferral loses issues S9394 cannot report (String-bounded receivers)
The new early return in checkWhenOwnerIsNotArray uses the erased receiver type, so it also silences receivers whose static type is a String-bounded type variable or capture (e.g. <T extends String> boolean f(T t, StringBuilder sb) { return t.equals(sb); }, or stringList.get(0).equals(sb) with List<? extends String>): erasure(T) is java.lang.String, so S2159 returns. S9394 cannot pick these up, because MethodMatchers.ofTypes("java.lang.String") tests the call-site type with exact Type.is() (MethodMatchersBuilder#getCallSiteType/ofTypes), and a type variable/capture is not is("java.lang.String"). Result: a comparison that always returns false at runtime was reported before this PR and is now reported by no rule; switching the matcher to ofSubTypes("java.lang.String") closes the gap (and the receiver-side generic case deserves a sample line, since the new samples only exercise the argument side).
Match String-bounded type variables/captures as receivers in StringEqualsCharSequenceCheck so the cases S2159 now defers are still reported (add a <T extends String> ... t.equals(sb) Noncompliant line to StringEqualsCharSequenceCheckSample).:
private static final MethodMatchers EQUALS_MATCHER = MethodMatchers.create()
.ofSubTypes("java.lang.String")
.names("equals")
.addParametersMatcher("java.lang.Object")
.build();
- Apply fix
Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎
| object.equals(1); // Compliant | ||
| integer.equals(1); // Compliant | ||
| string.equals(1); // Noncompliant {{Remove this call to "equals"; comparisons between unrelated types always return false.}} | ||
| string.equals(stringBuilder); |
There was a problem hiding this comment.
💡 Quality: New compliant sample lines lack "// Compliant" annotation
string.equals(stringBuilder); is added with no marker, while every other non-reporting line in SillyEqualsCheckSample carries // Compliant plus a reason; the same applies to the new compliant lines in StringEqualsCharSequenceCheckSample (str.equals(genericStr), str.equals(stringList.get(0))). Without the marker a reader cannot tell whether the absence of an expectation is intentional or a forgotten // Noncompliant, which matters here because the S2159 line encodes the deliberate deferral to S9394.
Annotate the intentionally compliant line in SillyEqualsCheckSample (and add // Compliant to the two new compliant lines in StringEqualsCharSequenceCheckSample).:
string.equals(stringBuilder); // Compliant, reported by S9394 instead
- Apply fix
Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎
CI failed: Maven build failed due to a server-side 503 error while downloading the SonarQube security plugin during the scanner analysis phase.Overview1 CI job failed due to a temporary infrastructure/server issue preventing the SonarScanner from downloading a required plugin (HTTP 503). FailuresSonarScanner Plugin Download Failure (confidence: high)
Summary
Code Review
|
| Auto-apply | Compact | Unblock |
|
|
|
Was this helpful? React with 👍 / 👎 | Gitar




Summary
This PR implements rule
S9394(StringEqualsCharSequenceCheck):"contentEquals()" should be used instead of "equals()" for "CharSequence" comparisons.String.equals(Object)requires an argument of typeStringand always returnsfalseat runtime when invoked with non-StringCharSequenceimplementations (e.g.StringBuilder,StringBuffer,CharBuffer, or interfaceCharSequence), even when representing the identical character sequence. To compare textual contents,contentEquals()should be used instead.equals(Object)calls where the receiver is aStringand the argument is a non-null, non-Stringsubtype ofCharSequence.JavaQuickFixreplacingequalswithcontentEquals.StringEqualsCharSequenceCheckTestandStringEqualsCharSequenceCheckSample).Sonar wayquality profile.References
AI disclosure
LLM model used for implementation: gemini-3.8-flash-high