2020import static com .google .common .collect .ImmutableSet .toImmutableSet ;
2121import static com .google .errorprone .util .ASTHelpers .isConsideredFinal ;
2222import static com .google .errorprone .util .ASTHelpers .isStatic ;
23+ import static com .google .errorprone .util .Reachability .canCompleteNormally ;
2324
2425import com .google .common .collect .ImmutableList ;
2526import com .google .common .collect .ImmutableSet ;
2627import com .google .common .collect .Sets ;
2728import com .google .errorprone .VisitorState ;
29+ import com .sun .source .tree .BinaryTree ;
30+ import com .sun .source .tree .BindingPatternTree ;
2831import com .sun .source .tree .BlockTree ;
2932import com .sun .source .tree .CatchTree ;
3033import com .sun .source .tree .ClassTree ;
3134import com .sun .source .tree .CompilationUnitTree ;
35+ import com .sun .source .tree .ConditionalExpressionTree ;
3236import com .sun .source .tree .EnhancedForLoopTree ;
37+ import com .sun .source .tree .ExpressionTree ;
3338import com .sun .source .tree .ForLoopTree ;
3439import com .sun .source .tree .IdentifierTree ;
40+ import com .sun .source .tree .IfTree ;
3541import com .sun .source .tree .ImportTree ;
42+ import com .sun .source .tree .InstanceOfTree ;
3643import com .sun .source .tree .LambdaExpressionTree ;
3744import com .sun .source .tree .MemberSelectTree ;
3845import com .sun .source .tree .MethodInvocationTree ;
3946import com .sun .source .tree .MethodTree ;
4047import com .sun .source .tree .NewClassTree ;
48+ import com .sun .source .tree .ParenthesizedTree ;
4149import com .sun .source .tree .StatementTree ;
4250import com .sun .source .tree .Tree ;
4351import com .sun .source .tree .Tree .Kind ;
4452import com .sun .source .tree .TryTree ;
53+ import com .sun .source .tree .UnaryTree ;
4554import com .sun .source .tree .VariableTree ;
55+ import com .sun .source .util .SimpleTreeVisitor ;
4656import com .sun .source .util .TreePath ;
4757import com .sun .source .util .TreeScanner ;
4858import com .sun .tools .javac .code .Kinds .KindSelector ;
@@ -157,7 +167,35 @@ private static Symbol findIdent(
157167 */
158168 public static ImmutableSet <VarSymbol > findAllIdents (VisitorState state ) {
159169 ImmutableSet .Builder <VarSymbol > result = new ImmutableSet .Builder <>();
170+
171+ // If we're in a binary tree, scan up separately to find anything to the left that implies us.
160172 Tree prev = state .getPath ().getLeaf ();
173+ loop :
174+ for (Tree curr : state .getPath ().getParentPath ()) {
175+ switch (curr .getKind ()) {
176+ case CONDITIONAL_AND -> {
177+ BinaryTree binaryTree = (BinaryTree ) curr ;
178+ if (prev == binaryTree .getRightOperand ()) {
179+ findBindingVariables (binaryTree .getLeftOperand (), result , /* startNegated= */ false );
180+ }
181+ }
182+ case CONDITIONAL_OR -> {
183+ BinaryTree binaryTree = (BinaryTree ) curr ;
184+ if (prev == binaryTree .getRightOperand ()) {
185+ findBindingVariables (binaryTree .getLeftOperand (), result , /* startNegated= */ true );
186+ }
187+ }
188+ default -> {
189+ if (!(curr instanceof ExpressionTree )) {
190+ break loop ;
191+ }
192+ }
193+ }
194+
195+ prev = curr ;
196+ }
197+
198+ prev = state .getPath ().getLeaf ();
161199 for (Tree curr : state .getPath ().getParentPath ()) {
162200 switch (curr .getKind ()) {
163201 case BLOCK -> {
@@ -166,6 +204,9 @@ public static ImmutableSet<VarSymbol> findAllIdents(VisitorState state) {
166204 break ;
167205 }
168206 addIfVariable (stmt , result );
207+ if (stmt instanceof IfTree ifTree && !canCompleteNormally (ifTree .getThenStatement ())) {
208+ findBindingVariables (ifTree .getCondition (), result , /* startNegated= */ true );
209+ }
169210 }
170211 }
171212 case LAMBDA_EXPRESSION -> {
@@ -231,6 +272,26 @@ public static ImmutableSet<VarSymbol> findAllIdents(VisitorState state) {
231272 addAllIfVariable (tryTree .getResources (), result );
232273 }
233274 }
275+ case IF -> {
276+ var ifTree = (IfTree ) curr ;
277+ if (prev == ifTree .getThenStatement ()) {
278+ findBindingVariables (ifTree .getCondition (), result , /* startNegated= */ false );
279+ }
280+ if (prev == ifTree .getElseStatement ()) {
281+ findBindingVariables (ifTree .getCondition (), result , /* startNegated= */ true );
282+ }
283+ }
284+ case CONDITIONAL_EXPRESSION -> {
285+ ConditionalExpressionTree conditionalExpressionTree = (ConditionalExpressionTree ) curr ;
286+ if (prev == conditionalExpressionTree .getTrueExpression ()) {
287+ findBindingVariables (
288+ conditionalExpressionTree .getCondition (), result , /* startNegated= */ false );
289+ }
290+ if (prev == conditionalExpressionTree .getFalseExpression ()) {
291+ findBindingVariables (
292+ conditionalExpressionTree .getCondition (), result , /* startNegated= */ true );
293+ }
294+ }
234295 case COMPILATION_UNIT -> {
235296 for (ImportTree importTree : ((CompilationUnitTree ) curr ).getImports ()) {
236297 if (importTree .isStatic ()
@@ -266,6 +327,48 @@ public static ImmutableSet<VarSymbol> findAllIdents(VisitorState state) {
266327 .collect (toImmutableSet ());
267328 }
268329
330+ private static void findBindingVariables (
331+ Tree tree , ImmutableSet .Builder <VarSymbol > result , boolean startNegated ) {
332+ new SimpleTreeVisitor <Void , Void >() {
333+ boolean negated = startNegated ;
334+
335+ @ Override
336+ public Void visitInstanceOf (InstanceOfTree node , Void unused ) {
337+ if (!negated && node .getPattern () instanceof BindingPatternTree bpt ) {
338+ addIfVariable (bpt .getVariable (), result );
339+ }
340+ return null ;
341+ }
342+
343+ @ Override
344+ public Void visitParenthesized (ParenthesizedTree node , Void unused ) {
345+ return visit (node .getExpression (), null );
346+ }
347+
348+ @ Override
349+ public Void visitUnary (UnaryTree node , Void unused ) {
350+ if (node .getKind ().equals (Kind .LOGICAL_COMPLEMENT )) {
351+ negated = !negated ;
352+ return visit (node .getExpression (), null );
353+ }
354+ return null ;
355+ }
356+
357+ @ Override
358+ public Void visitBinary (BinaryTree node , Void unused ) {
359+ if (node .getKind ().equals (Kind .CONDITIONAL_AND ) && !negated ) {
360+ visit (node .getLeftOperand (), null );
361+ visit (node .getRightOperand (), null );
362+ }
363+ if (node .getKind ().equals (Kind .CONDITIONAL_OR ) && negated ) {
364+ visit (node .getLeftOperand (), null );
365+ visit (node .getRightOperand (), null );
366+ }
367+ return null ;
368+ }
369+ }.visit (tree , null );
370+ }
371+
269372 /**
270373 * Finds all variable declarations which are unused at this point in the AST (i.e. they might be
271374 * used further on).
@@ -419,7 +522,7 @@ private static boolean isVisible(VarSymbol var, TreePath path) {
419522 // in the enclosing class or a superclass).
420523 return modifiers .contains (Modifier .PUBLIC ) || modifiers .contains (Modifier .PROTECTED );
421524 }
422- case PARAMETER , LOCAL_VARIABLE -> {
525+ case PARAMETER , LOCAL_VARIABLE , BINDING_VARIABLE -> {
423526 // If we are in an anonymous inner class, lambda, or local class, any local variable or
424527 // method parameter we access that is defined outside the anonymous class/lambda must be
425528 // final or effectively final (JLS 8.1.3).
0 commit comments