Skip to content

Commit 450126a

Browse files
graememorganError Prone Team
authored andcommitted
Open-source Java21 matchers.
PiperOrigin-RevId: 832332360
1 parent f101473 commit 450126a

File tree

5 files changed

+166
-5
lines changed

5 files changed

+166
-5
lines changed

check_api/src/main/java/com/google/errorprone/bugpatterns/BugChecker.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@
5151
import com.sun.source.tree.CompilationUnitTree;
5252
import com.sun.source.tree.CompoundAssignmentTree;
5353
import com.sun.source.tree.ConditionalExpressionTree;
54+
import com.sun.source.tree.ConstantCaseLabelTree;
5455
import com.sun.source.tree.ContinueTree;
56+
import com.sun.source.tree.DeconstructionPatternTree;
57+
import com.sun.source.tree.DefaultCaseLabelTree;
5558
import com.sun.source.tree.DoWhileLoopTree;
5659
import com.sun.source.tree.EmptyStatementTree;
5760
import com.sun.source.tree.EnhancedForLoopTree;
@@ -78,6 +81,7 @@
7881
import com.sun.source.tree.PackageTree;
7982
import com.sun.source.tree.ParameterizedTypeTree;
8083
import com.sun.source.tree.ParenthesizedTree;
84+
import com.sun.source.tree.PatternCaseLabelTree;
8185
import com.sun.source.tree.PrimitiveTypeTree;
8286
import com.sun.source.tree.ProvidesTree;
8387
import com.sun.source.tree.RequiresTree;
@@ -385,10 +389,22 @@ public interface ConditionalExpressionTreeMatcher extends Suppressible {
385389
Description matchConditionalExpression(ConditionalExpressionTree tree, VisitorState state);
386390
}
387391

392+
public interface ConstantCaseLabelTreeMatcher extends Suppressible {
393+
Description matchConstantCaseLabel(ConstantCaseLabelTree tree, VisitorState state);
394+
}
395+
388396
public interface ContinueTreeMatcher extends Suppressible {
389397
Description matchContinue(ContinueTree tree, VisitorState state);
390398
}
391399

400+
public interface DeconstructionPatternTreeMatcher extends Suppressible {
401+
Description matchDeconstructionPattern(DeconstructionPatternTree tree, VisitorState state);
402+
}
403+
404+
public interface DefaultCaseLabelTreeMatcher extends Suppressible {
405+
Description matchDefaultCaseLabel(DefaultCaseLabelTree tree, VisitorState state);
406+
}
407+
392408
public interface DoWhileLoopTreeMatcher extends Suppressible {
393409
Description matchDoWhileLoop(DoWhileLoopTree tree, VisitorState state);
394410
}
@@ -498,6 +514,10 @@ public interface ParenthesizedTreeMatcher extends Suppressible {
498514
Description matchParenthesized(ParenthesizedTree tree, VisitorState state);
499515
}
500516

517+
public interface PatternCaseLabelTreeMatcher extends Suppressible {
518+
Description matchPatternCaseLabel(PatternCaseLabelTree tree, VisitorState state);
519+
}
520+
501521
public interface PrimitiveTypeTreeMatcher extends Suppressible {
502522
Description matchPrimitiveType(PrimitiveTypeTree tree, VisitorState state);
503523
}

check_api/src/main/java/com/google/errorprone/scanner/ErrorProneScanner.java

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@
4141
import com.google.errorprone.bugpatterns.BugChecker.CompilationUnitTreeMatcher;
4242
import com.google.errorprone.bugpatterns.BugChecker.CompoundAssignmentTreeMatcher;
4343
import com.google.errorprone.bugpatterns.BugChecker.ConditionalExpressionTreeMatcher;
44+
import com.google.errorprone.bugpatterns.BugChecker.ConstantCaseLabelTreeMatcher;
4445
import com.google.errorprone.bugpatterns.BugChecker.ContinueTreeMatcher;
46+
import com.google.errorprone.bugpatterns.BugChecker.DeconstructionPatternTreeMatcher;
47+
import com.google.errorprone.bugpatterns.BugChecker.DefaultCaseLabelTreeMatcher;
4548
import com.google.errorprone.bugpatterns.BugChecker.DoWhileLoopTreeMatcher;
4649
import com.google.errorprone.bugpatterns.BugChecker.EmptyStatementTreeMatcher;
4750
import com.google.errorprone.bugpatterns.BugChecker.EnhancedForLoopTreeMatcher;
@@ -68,6 +71,7 @@
6871
import com.google.errorprone.bugpatterns.BugChecker.PackageTreeMatcher;
6972
import com.google.errorprone.bugpatterns.BugChecker.ParameterizedTypeTreeMatcher;
7073
import com.google.errorprone.bugpatterns.BugChecker.ParenthesizedTreeMatcher;
74+
import com.google.errorprone.bugpatterns.BugChecker.PatternCaseLabelTreeMatcher;
7175
import com.google.errorprone.bugpatterns.BugChecker.PrimitiveTypeTreeMatcher;
7276
import com.google.errorprone.bugpatterns.BugChecker.ProvidesTreeMatcher;
7377
import com.google.errorprone.bugpatterns.BugChecker.RequiresTreeMatcher;
@@ -105,7 +109,10 @@
105109
import com.sun.source.tree.CompilationUnitTree;
106110
import com.sun.source.tree.CompoundAssignmentTree;
107111
import com.sun.source.tree.ConditionalExpressionTree;
112+
import com.sun.source.tree.ConstantCaseLabelTree;
108113
import com.sun.source.tree.ContinueTree;
114+
import com.sun.source.tree.DeconstructionPatternTree;
115+
import com.sun.source.tree.DefaultCaseLabelTree;
109116
import com.sun.source.tree.DoWhileLoopTree;
110117
import com.sun.source.tree.EmptyStatementTree;
111118
import com.sun.source.tree.EnhancedForLoopTree;
@@ -132,6 +139,7 @@
132139
import com.sun.source.tree.PackageTree;
133140
import com.sun.source.tree.ParameterizedTypeTree;
134141
import com.sun.source.tree.ParenthesizedTree;
142+
import com.sun.source.tree.PatternCaseLabelTree;
135143
import com.sun.source.tree.PrimitiveTypeTree;
136144
import com.sun.source.tree.ProvidesTree;
137145
import com.sun.source.tree.RequiresTree;
@@ -233,6 +241,7 @@ protected Set<? extends Name> getCustomSuppressionAnnotations(VisitorState state
233241
return customSuppressionAnnotations.get(state);
234242
}
235243

244+
// keep-sorted start
236245
private final List<AnnotatedTypeTreeMatcher> annotatedTypeMatchers = new ArrayList<>();
237246
private final List<AnnotationTreeMatcher> annotationMatchers = new ArrayList<>();
238247
private final List<ArrayAccessTreeMatcher> arrayAccessMatchers = new ArrayList<>();
@@ -250,7 +259,11 @@ protected Set<? extends Name> getCustomSuppressionAnnotations(VisitorState state
250259
private final List<CompoundAssignmentTreeMatcher> compoundAssignmentMatchers = new ArrayList<>();
251260
private final List<ConditionalExpressionTreeMatcher> conditionalExpressionMatchers =
252261
new ArrayList<>();
262+
private final List<ConstantCaseLabelTreeMatcher> constantCaseLabelMatchers = new ArrayList<>();
253263
private final List<ContinueTreeMatcher> continueMatchers = new ArrayList<>();
264+
private final List<DeconstructionPatternTreeMatcher> deconstructionPatternMatchers =
265+
new ArrayList<>();
266+
private final List<DefaultCaseLabelTreeMatcher> defaultCaseLabelMatchers = new ArrayList<>();
254267
private final List<DoWhileLoopTreeMatcher> doWhileLoopMatchers = new ArrayList<>();
255268
private final List<EmptyStatementTreeMatcher> emptyStatementMatchers = new ArrayList<>();
256269
private final List<EnhancedForLoopTreeMatcher> enhancedForLoopMatchers = new ArrayList<>();
@@ -268,8 +281,8 @@ protected Set<? extends Name> getCustomSuppressionAnnotations(VisitorState state
268281
private final List<LiteralTreeMatcher> literalMatchers = new ArrayList<>();
269282
private final List<MemberReferenceTreeMatcher> memberReferenceMatchers = new ArrayList<>();
270283
private final List<MemberSelectTreeMatcher> memberSelectMatchers = new ArrayList<>();
271-
private final List<MethodTreeMatcher> methodMatchers = new ArrayList<>();
272284
private final List<MethodInvocationTreeMatcher> methodInvocationMatchers = new ArrayList<>();
285+
private final List<MethodTreeMatcher> methodMatchers = new ArrayList<>();
273286
private final List<ModifiersTreeMatcher> modifiersMatchers = new ArrayList<>();
274287
private final List<ModuleTreeMatcher> moduleMatchers = new ArrayList<>();
275288
private final List<NewArrayTreeMatcher> newArrayMatchers = new ArrayList<>();
@@ -278,6 +291,7 @@ protected Set<? extends Name> getCustomSuppressionAnnotations(VisitorState state
278291
private final List<PackageTreeMatcher> packageMatchers = new ArrayList<>();
279292
private final List<ParameterizedTypeTreeMatcher> parameterizedTypeMatchers = new ArrayList<>();
280293
private final List<ParenthesizedTreeMatcher> parenthesizedMatchers = new ArrayList<>();
294+
private final List<PatternCaseLabelTreeMatcher> patternCaseLabelMatchers = new ArrayList<>();
281295
private final List<PrimitiveTypeTreeMatcher> primitiveTypeMatchers = new ArrayList<>();
282296
private final List<ProvidesTreeMatcher> providesMatchers = new ArrayList<>();
283297
private final List<RequiresTreeMatcher> requiresMatchers = new ArrayList<>();
@@ -297,11 +311,14 @@ protected Set<? extends Name> getCustomSuppressionAnnotations(VisitorState state
297311
private final List<WildcardTreeMatcher> wildcardMatchers = new ArrayList<>();
298312
private final List<YieldTreeMatcher> yieldMatchers = new ArrayList<>();
299313

314+
// keep-sorted end
315+
300316
private void registerNodeTypes(
301317
BugChecker checker,
302318
ImmutableSet.Builder<Class<? extends Annotation>> customSuppressionAnnotationClasses) {
303319
customSuppressionAnnotationClasses.addAll(checker.customSuppressionAnnotations());
304320

321+
// keep-sorted start
305322
if (checker instanceof AnnotatedTypeTreeMatcher annotatedTypeTreeMatcher) {
306323
annotatedTypeMatchers.add(annotatedTypeTreeMatcher);
307324
}
@@ -350,9 +367,18 @@ private void registerNodeTypes(
350367
if (checker instanceof ConditionalExpressionTreeMatcher conditionalExpressionTreeMatcher) {
351368
conditionalExpressionMatchers.add(conditionalExpressionTreeMatcher);
352369
}
370+
if (checker instanceof ConstantCaseLabelTreeMatcher constantCaseLabelTreeMatcher) {
371+
constantCaseLabelMatchers.add(constantCaseLabelTreeMatcher);
372+
}
353373
if (checker instanceof ContinueTreeMatcher continueTreeMatcher) {
354374
continueMatchers.add(continueTreeMatcher);
355375
}
376+
if (checker instanceof DeconstructionPatternTreeMatcher deconstructionPatternTreeMatcher) {
377+
deconstructionPatternMatchers.add(deconstructionPatternTreeMatcher);
378+
}
379+
if (checker instanceof DefaultCaseLabelTreeMatcher defaultCaseLabelTreeMatcher) {
380+
defaultCaseLabelMatchers.add(defaultCaseLabelTreeMatcher);
381+
}
356382
if (checker instanceof DoWhileLoopTreeMatcher doWhileLoopTreeMatcher) {
357383
doWhileLoopMatchers.add(doWhileLoopTreeMatcher);
358384
}
@@ -401,12 +427,12 @@ private void registerNodeTypes(
401427
if (checker instanceof MemberSelectTreeMatcher memberSelectTreeMatcher) {
402428
memberSelectMatchers.add(memberSelectTreeMatcher);
403429
}
404-
if (checker instanceof MethodTreeMatcher methodTreeMatcher) {
405-
methodMatchers.add(methodTreeMatcher);
406-
}
407430
if (checker instanceof MethodInvocationTreeMatcher methodInvocationTreeMatcher) {
408431
methodInvocationMatchers.add(methodInvocationTreeMatcher);
409432
}
433+
if (checker instanceof MethodTreeMatcher methodTreeMatcher) {
434+
methodMatchers.add(methodTreeMatcher);
435+
}
410436
if (checker instanceof ModifiersTreeMatcher modifiersTreeMatcher) {
411437
modifiersMatchers.add(modifiersTreeMatcher);
412438
}
@@ -431,6 +457,9 @@ private void registerNodeTypes(
431457
if (checker instanceof ParenthesizedTreeMatcher parenthesizedTreeMatcher) {
432458
parenthesizedMatchers.add(parenthesizedTreeMatcher);
433459
}
460+
if (checker instanceof PatternCaseLabelTreeMatcher patternCaseLabelTreeMatcher) {
461+
patternCaseLabelMatchers.add(patternCaseLabelTreeMatcher);
462+
}
434463
if (checker instanceof PrimitiveTypeTreeMatcher primitiveTypeTreeMatcher) {
435464
primitiveTypeMatchers.add(primitiveTypeTreeMatcher);
436465
}
@@ -485,6 +514,7 @@ private void registerNodeTypes(
485514
if (checker instanceof YieldTreeMatcher yieldTreeMatcher) {
486515
yieldMatchers.add(yieldTreeMatcher);
487516
}
517+
// keep-sorted end
488518
}
489519

490520
@FunctionalInterface
@@ -654,13 +684,47 @@ public Void visitConditionalExpression(
654684
return super.visitConditionalExpression(tree, state);
655685
}
656686

687+
@Override
688+
public Void visitConstantCaseLabel(ConstantCaseLabelTree tree, VisitorState visitorState) {
689+
VisitorState state =
690+
processMatchers(
691+
constantCaseLabelMatchers,
692+
tree,
693+
ConstantCaseLabelTreeMatcher::matchConstantCaseLabel,
694+
visitorState);
695+
return super.visitConstantCaseLabel(tree, state);
696+
}
697+
657698
@Override
658699
public Void visitContinue(ContinueTree tree, VisitorState visitorState) {
659700
VisitorState state =
660701
processMatchers(continueMatchers, tree, ContinueTreeMatcher::matchContinue, visitorState);
661702
return super.visitContinue(tree, state);
662703
}
663704

705+
@Override
706+
public Void visitDeconstructionPattern(
707+
DeconstructionPatternTree tree, VisitorState visitorState) {
708+
VisitorState state =
709+
processMatchers(
710+
deconstructionPatternMatchers,
711+
tree,
712+
DeconstructionPatternTreeMatcher::matchDeconstructionPattern,
713+
visitorState);
714+
return super.visitDeconstructionPattern(tree, state);
715+
}
716+
717+
@Override
718+
public Void visitDefaultCaseLabel(DefaultCaseLabelTree tree, VisitorState visitorState) {
719+
VisitorState state =
720+
processMatchers(
721+
defaultCaseLabelMatchers,
722+
tree,
723+
DefaultCaseLabelTreeMatcher::matchDefaultCaseLabel,
724+
visitorState);
725+
return super.visitDefaultCaseLabel(tree, state);
726+
}
727+
664728
@Override
665729
public Void visitDoWhileLoop(DoWhileLoopTree tree, VisitorState visitorState) {
666730
VisitorState state =
@@ -874,6 +938,17 @@ public Void visitPackage(PackageTree tree, VisitorState visitorState) {
874938
return super.visitPackage(tree, state);
875939
}
876940

941+
@Override
942+
public Void visitPatternCaseLabel(PatternCaseLabelTree tree, VisitorState visitorState) {
943+
VisitorState state =
944+
processMatchers(
945+
patternCaseLabelMatchers,
946+
tree,
947+
PatternCaseLabelTreeMatcher::matchPatternCaseLabel,
948+
visitorState);
949+
return super.visitPatternCaseLabel(tree, state);
950+
}
951+
877952
// Intentionally skip visitOther. It seems to be used only for let expressions, which are
878953
// generated by javac to implement autoboxing. We are only interested in source-level constructs.
879954

core/src/main/java/com/google/errorprone/bugpatterns/argumentselectiondefects/ArgumentSelectionDefectChecker.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
import com.google.errorprone.BugPattern;
2323
import com.google.errorprone.VisitorState;
2424
import com.google.errorprone.bugpatterns.BugChecker;
25+
import com.google.errorprone.bugpatterns.BugChecker.DeconstructionPatternTreeMatcher;
2526
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
2627
import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
2728
import com.google.errorprone.matchers.Description;
2829
import com.google.errorprone.names.NamingConventions;
2930
import com.google.errorprone.names.NeedlemanWunschEditDistance;
3031
import com.google.errorprone.util.ASTHelpers;
32+
import com.sun.source.tree.DeconstructionPatternTree;
3133
import com.sun.source.tree.MethodInvocationTree;
3234
import com.sun.source.tree.NewClassTree;
3335
import com.sun.tools.javac.code.Symbol.MethodSymbol;
@@ -54,7 +56,7 @@
5456
summary = "Arguments are in the wrong order or could be commented for clarity.",
5557
severity = WARNING)
5658
public class ArgumentSelectionDefectChecker extends BugChecker
57-
implements MethodInvocationTreeMatcher, NewClassTreeMatcher {
59+
implements DeconstructionPatternTreeMatcher, MethodInvocationTreeMatcher, NewClassTreeMatcher {
5860

5961
private final ArgumentChangeFinder argumentChangeFinder;
6062

@@ -99,6 +101,13 @@ public Description matchNewClass(NewClassTree tree, VisitorState state) {
99101
return visit(InvocationInfo.createFromNewClass(tree, symbol, state));
100102
}
101103

104+
@Override
105+
public Description matchDeconstructionPattern(
106+
DeconstructionPatternTree tree, VisitorState state) {
107+
var deconstructor = InvocationInfo.createFromDeconstructionPattern(tree, state);
108+
return deconstructor == null ? Description.NO_MATCH : visit(deconstructor);
109+
}
110+
102111
private Description visit(InvocationInfo invocationInfo) {
103112
Changes changes = argumentChangeFinder.findChanges(invocationInfo);
104113

core/src/main/java/com/google/errorprone/bugpatterns/argumentselectiondefects/InvocationInfo.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,22 @@
1616

1717
package com.google.errorprone.bugpatterns.argumentselectiondefects;
1818

19+
import static com.google.errorprone.util.ASTHelpers.canonicalConstructor;
20+
import static com.google.errorprone.util.ASTHelpers.getSymbol;
21+
1922
import com.google.auto.value.AutoValue;
2023
import com.google.common.collect.ImmutableList;
2124
import com.google.errorprone.VisitorState;
25+
import com.sun.source.tree.BindingPatternTree;
26+
import com.sun.source.tree.DeconstructionPatternTree;
2227
import com.sun.source.tree.MethodInvocationTree;
2328
import com.sun.source.tree.NewClassTree;
2429
import com.sun.source.tree.Tree;
30+
import com.sun.tools.javac.code.Symbol.ClassSymbol;
2531
import com.sun.tools.javac.code.Symbol.MethodSymbol;
2632
import com.sun.tools.javac.code.Symbol.VarSymbol;
2733
import java.util.List;
34+
import org.jspecify.annotations.Nullable;
2835

2936
/**
3037
* Holds information about the method invocation (or new class construction) that we are processing.
@@ -64,6 +71,26 @@ static InvocationInfo createFromNewClass(
6471
state);
6572
}
6673

74+
static @Nullable InvocationInfo createFromDeconstructionPattern(
75+
DeconstructionPatternTree tree, VisitorState state) {
76+
var symbol = getSymbol(tree.getDeconstructor());
77+
if (!(symbol instanceof ClassSymbol cs)) {
78+
return null;
79+
}
80+
81+
var constructor = canonicalConstructor(cs, state);
82+
ImmutableList.Builder<Tree> actuals = ImmutableList.builder();
83+
ImmutableList.Builder<VarSymbol> formals = ImmutableList.builder();
84+
for (int i = 0; i < constructor.getParameters().size(); ++i) {
85+
// Skip over anything that isn't binding. There might be nested patterns here, or "_"s.
86+
if (tree.getNestedPatterns().get(i) instanceof BindingPatternTree) {
87+
actuals.add(((BindingPatternTree) tree.getNestedPatterns().get(i)).getVariable());
88+
formals.add(constructor.getParameters().get(i));
89+
}
90+
}
91+
return new AutoValue_InvocationInfo(tree, constructor, actuals.build(), formals.build(), state);
92+
}
93+
6794
private static ImmutableList<VarSymbol> getFormalParametersWithoutVarArgs(
6895
MethodSymbol invokedMethodSymbol) {
6996
List<VarSymbol> formalParameters = invokedMethodSymbol.getParameters();

core/src/test/java/com/google/errorprone/bugpatterns/argumentselectiondefects/ArgumentSelectionDefectCheckerTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,36 @@ record Foo(String first, String second) {}
400400
.doTest();
401401
}
402402

403+
@Test
404+
public void recordDeconstruction() {
405+
testHelper
406+
.addSourceLines(
407+
"Test.java",
408+
"""
409+
class Test {
410+
void test(Foo foo) {
411+
switch (foo) {
412+
// BUG: Diagnostic contains: may have been swapped
413+
case Foo(String second, String first, _) -> {}
414+
default -> {}
415+
}
416+
}
417+
418+
void test2(Foo foo) {
419+
switch (foo) {
420+
// BUG: Diagnostic contains: may have been swapped
421+
case Foo(_, _, Foo(String second, String first, _)) -> {}
422+
default -> {}
423+
}
424+
}
425+
}
426+
427+
record Foo(String first, String second, Foo foo) {}
428+
""")
429+
.setArgs("--enable-preview", "--release", Integer.toString(Runtime.version().feature()))
430+
.doTest();
431+
}
432+
403433
public record Foo(String first, String second) {}
404434

405435
@Test

0 commit comments

Comments
 (0)