20
20
import static com .google .common .collect .ImmutableSet .toImmutableSet ;
21
21
import static com .google .errorprone .util .ASTHelpers .isConsideredFinal ;
22
22
import static com .google .errorprone .util .ASTHelpers .isStatic ;
23
+ import static com .google .errorprone .util .Reachability .canCompleteNormally ;
23
24
24
25
import com .google .common .collect .ImmutableList ;
25
26
import com .google .common .collect .ImmutableSet ;
26
27
import com .google .common .collect .Sets ;
27
28
import com .google .errorprone .VisitorState ;
29
+ import com .sun .source .tree .BinaryTree ;
30
+ import com .sun .source .tree .BindingPatternTree ;
28
31
import com .sun .source .tree .BlockTree ;
29
32
import com .sun .source .tree .CatchTree ;
30
33
import com .sun .source .tree .ClassTree ;
31
34
import com .sun .source .tree .CompilationUnitTree ;
35
+ import com .sun .source .tree .ConditionalExpressionTree ;
32
36
import com .sun .source .tree .EnhancedForLoopTree ;
37
+ import com .sun .source .tree .ExpressionTree ;
33
38
import com .sun .source .tree .ForLoopTree ;
34
39
import com .sun .source .tree .IdentifierTree ;
40
+ import com .sun .source .tree .IfTree ;
35
41
import com .sun .source .tree .ImportTree ;
42
+ import com .sun .source .tree .InstanceOfTree ;
36
43
import com .sun .source .tree .LambdaExpressionTree ;
37
44
import com .sun .source .tree .MemberSelectTree ;
38
45
import com .sun .source .tree .MethodInvocationTree ;
39
46
import com .sun .source .tree .MethodTree ;
40
47
import com .sun .source .tree .NewClassTree ;
48
+ import com .sun .source .tree .ParenthesizedTree ;
41
49
import com .sun .source .tree .StatementTree ;
42
50
import com .sun .source .tree .Tree ;
43
51
import com .sun .source .tree .Tree .Kind ;
44
52
import com .sun .source .tree .TryTree ;
53
+ import com .sun .source .tree .UnaryTree ;
45
54
import com .sun .source .tree .VariableTree ;
55
+ import com .sun .source .util .SimpleTreeVisitor ;
46
56
import com .sun .source .util .TreePath ;
47
57
import com .sun .source .util .TreeScanner ;
48
58
import com .sun .tools .javac .code .Kinds .KindSelector ;
@@ -157,7 +167,35 @@ private static Symbol findIdent(
157
167
*/
158
168
public static ImmutableSet <VarSymbol > findAllIdents (VisitorState state ) {
159
169
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.
160
172
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 ();
161
199
for (Tree curr : state .getPath ().getParentPath ()) {
162
200
switch (curr .getKind ()) {
163
201
case BLOCK -> {
@@ -166,6 +204,9 @@ public static ImmutableSet<VarSymbol> findAllIdents(VisitorState state) {
166
204
break ;
167
205
}
168
206
addIfVariable (stmt , result );
207
+ if (stmt instanceof IfTree ifTree && !canCompleteNormally (ifTree .getThenStatement ())) {
208
+ findBindingVariables (ifTree .getCondition (), result , /* startNegated= */ true );
209
+ }
169
210
}
170
211
}
171
212
case LAMBDA_EXPRESSION -> {
@@ -231,6 +272,26 @@ public static ImmutableSet<VarSymbol> findAllIdents(VisitorState state) {
231
272
addAllIfVariable (tryTree .getResources (), result );
232
273
}
233
274
}
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
+ }
234
295
case COMPILATION_UNIT -> {
235
296
for (ImportTree importTree : ((CompilationUnitTree ) curr ).getImports ()) {
236
297
if (importTree .isStatic ()
@@ -266,6 +327,48 @@ public static ImmutableSet<VarSymbol> findAllIdents(VisitorState state) {
266
327
.collect (toImmutableSet ());
267
328
}
268
329
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
+
269
372
/**
270
373
* Finds all variable declarations which are unused at this point in the AST (i.e. they might be
271
374
* used further on).
@@ -419,7 +522,7 @@ private static boolean isVisible(VarSymbol var, TreePath path) {
419
522
// in the enclosing class or a superclass).
420
523
return modifiers .contains (Modifier .PUBLIC ) || modifiers .contains (Modifier .PROTECTED );
421
524
}
422
- case PARAMETER , LOCAL_VARIABLE -> {
525
+ case PARAMETER , LOCAL_VARIABLE , BINDING_VARIABLE -> {
423
526
// If we are in an anonymous inner class, lambda, or local class, any local variable or
424
527
// method parameter we access that is defined outside the anonymous class/lambda must be
425
528
// final or effectively final (JLS 8.1.3).
0 commit comments