Skip to content

fix(#5845): strip parentheses before matching in 6 bug checks to eliminate false positives/negatives#5889

Open
HarshMehta112 wants to merge 1 commit into
google:masterfrom
HarshMehta112:master
Open

fix(#5845): strip parentheses before matching in 6 bug checks to eliminate false positives/negatives#5889
HarshMehta112 wants to merge 1 commit into
google:masterfrom
HarshMehta112:master

Conversation

@HarshMehta112

Copy link
Copy Markdown
Contributor

Fixes: #5845

Problem

Six unrelated checks share a root cause: their AST matchers inspect raw ExpressionTree
operands without unwrapping enclosing ParenthesizedTree nodes. Per JLS §15.8.5, a
parenthesized expression denotes the same variable as its contained expression —
parentheses are semantically inert. Because ASTHelpers.getSymbol(ParenthesizedTree)
returns null (no case for PARENTHESIZED in the dispatch), wrapping or unwrapping one
pair of redundant parentheses around an operand silently flips the verdict.

Check Reproducer Direction
LoopConditionChecker for (int i = 0; i < 10; (i)++) {} false positive
NonAtomicVolatileUpdate (x)++ on a volatile field false negative
PatternMatchingInstanceof String s = (String)(o) false negative
DuplicateBranches true ? 1 * 5 : (1 * 5) false negative
InlineTrivialConstant private static final String EMPTY = ("") false negative
AssignmentExpression a = b = 0 vs a = (b = 0) verdict drift both directions

Fix

Apply ASTHelpers.stripParentheses() at the point where each check extracts the operand
it will inspect.

LoopConditionChecker

UpdateScanner.check(): strip before getSymbol. One change covers all four call sites
(visitUnary, visitAssignment, visitCompoundAssignment, visitMethodInvocation).

NonAtomicVolatileUpdate

Strip in all three extractor lambdas (expressionFromUnaryTree,
variableFromCompoundAssignmentTree, variableFromAssignmentTree) before the
hasModifier(VOLATILE) check.

PatternMatchingInstanceof

Strip node.getExpression() in findAllCasts.visitTypeCast before the
constantExpression symbol lookup.

DuplicateBranches

Strip both arms before toString() comparison. Source positions for the suggested fix
still use the original (possibly-parenthesized) tree, so the replacement is always
syntactically valid.

InlineTrivialConstant

Strip tree.getInitializer() before the STRING_LITERAL kind check and value
extraction.

AssignmentExpression

Walk getParentPath() in a loop until the leaf is no longer ParenthesizedTree, then
use the resulting effectiveParent for all three downstream decisions:

  • binary-context exemption ((foo = bar) != null)
  • duplicate-assignment detection (a = a = foo())
  • chained-assignment exemption (x = y = 0)

Tests

32 new test cases across all six checkers:

  • Single and multiple layers of redundant parentheses
  • All operator forms: postfix/prefix increment/decrement, compound assignment (+=, -=),
    simple assignment
  • Negative cases confirming legitimate no-find verdicts are unaffected (e.g. non-volatile
    fields, synchronized blocks, different branch content after stripping)
  • Realistic patterns: negated if/else, ternary arms, do-while/while loop bodies, chained
    assignments, inline cast use, duplicate assignment through parentheses

Signed-off-by: Harsh Mehta <harshmehta010102@gmail.com>
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.

Multiple checks are not parenthesis-invariant: matchers don't look through ParenthesizedTree

1 participant