SONARJAVA-6768 Implement new rule S7438: Incompatible bit masks should not be used in comparisons - #5939
Conversation
Detect incompatible bit masks in equality comparisons where bitwise AND or OR operations are compared against values that are impossible given the mask, making the comparison always true or always false.
Fix test marker alignment and add int-type-aware normalization to avoid false positives when high-bit int hex masks (e.g., 0xFFFFFFFF) are compared with negative int values. In no-semantic mode, falls back to checking literal types (int vs long) to determine operand width. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Simplify isIntOperation to rely solely on symbolType() since ECJ always resolves primitive types even in no-semantic mode, making the long-literal heuristic fallback unreachable dead code. Add additional test cases for OR+!=, long literal mask on left, and comparison value on left with long. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Sign-extend int hex literals (e.g. 0xFFFFFFFF) through int cast to match Java's actual sign-extension behavior when used in long context. Fixes false positives on patterns like (longX & 0xFFFFFFFF) == -1L. - Improve isIntOperation to use fullyQualifiedName() and fall back to a long-literal heuristic when semantic info is unavailable, preventing silent truncation of long operations to int width. - Add test cases for int mask sign extension and long literal edge cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace maskOperandValue calls in extractMask with signExtendedLongValue, which has an identical implementation. This removes the duplicate code flagged in review and fixes uncovered branches that prevented the Quality Gate from passing (84.5% < 90% threshold). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add non-compiling test file with unresolvable types to exercise the literal-based heuristic in isIntOperation and all branches of hasLongLiteral. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| } | ||
| if (isIntOperation(bitwiseOp, possibleConstant)) { | ||
| mask = (long) mask.intValue(); | ||
| value = (long) value.intValue(); |
There was a problem hiding this comment.
[P1] Do not truncate a long comparison operand
An int bitwise result is promoted to long for comparison; Java does not truncate the RHS. For if ((x & 0xFFFFFFFF) == 0xFFFFFFFFL), the condition is always false, but both values become -1 here and no issue is raised.
isIntOperation() returns true from the known-type branch whenever bitwiseOp.symbolType() is int, without checking whether the comparison operand is itself a long (unlike the heuristic fallback branch, which does call hasLongLiteral(comparisonValue)). Please preserve the comparison operand's width / account for the sign-extended int result in the known-type branches too.
|
|
||
| @Nullable | ||
| private static Long signExtendedLongValue(ExpressionTree operand) { | ||
| Long value = LiteralUtils.longLiteralValue(operand); |
There was a problem hiding this comment.
[P2] Unwrap parentheses before parsing literals
LiteralUtils.longLiteralValue only unwraps UNARY_MINUS/UNARY_PLUS before requiring INT_LITERAL/LONG_LITERAL; it never calls ExpressionUtils.skipParentheses. So if ((x & (1)) == 2) and if ((x & 1) == (2)) are silently skipped despite being always false. isIntLiteral/hasLongLiteral elsewhere in this file already call skipParentheses, so this looks like an oversight rather than intentional.
|
|
||
| @Nullable | ||
| private static Long signExtendedLongValue(ExpressionTree operand) { | ||
| Long value = LiteralUtils.longLiteralValue(operand); |
There was a problem hiding this comment.
[P2] Support unsigned high-bit long masks
Long.decode throws NumberFormatException for hex literals above Long.MAX_VALUE, such as 0xFFFF_FFFF_FFFF_FFFEL (this is even called out in a comment in LiteralUtils.java). Consequently if ((flags & 0xFFFF_FFFF_FFFF_FFFEL) == -1L) is missed, since longLiteralValue returns null and the check bails out via the mask == null || value == null guard.
… unsigned long masks - Unwrap parentheses in signExtendedLongValue via parseLongLiteral so expressions like (x & (1)) == 2 are correctly detected - Detect incompatibility when int bitwise result is compared to a long literal outside int range (e.g. (intVar & 0xFFFFFFFF) == 0xFFFFFFFFL) - Add parseLongLiteral helper using Long.parseUnsignedLong to handle hex long literals above Long.MAX_VALUE (e.g. 0xFFFF_FFFF_FFFF_FFFEL) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge nested if statements (S1066) and reword comment to not look like commented-out code (S125). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Code Review ✅ Approved 4 resolved / 4 findingsImplements new rule S7438 to detect incompatible bit masks in comparisons, addressing sign mismatches on int masks, missing sign extension in long comparisons, and truncation issues in no-semantic mode. ✅ 4 resolved✅ Edge Case: Sign mismatch on int masks can cause false positives
✅ Edge Case: int hex mask not sign-extended in long comparisons → false positive
✅ Edge Case: Long bitwise ops truncated to int in no-semantic mode
✅ Quality: signExtendedLongValue and maskOperandValue are identical
Implementation Status ✅ 1 / 1 issues implemented✅ SONARJAVA-6768 — 1 / 1 objectivesThe PR implements the new rule S7438 checking for incompatible bit masks in comparisons. ✅ 1 complete
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 |




Detect incompatible bit masks in equality comparisons where bitwise AND or OR operations are compared against values that are impossible given the mask, making the comparison always true or always false.