From 433b8b90c09cdbb44e2663345814cda523536a33 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sun, 11 Feb 2024 16:57:13 +0100 Subject: [PATCH] Require JDK 17 rather than JDK 11 (#603) By raising this baseline the project can now use Java 17 language features such as text blocks, switch expressions and `instanceof` pattern matching. The code has been updated to make use of these constructs. Note that the project can still be used by builds that target an older version of Java, as long as those builds are executed using JDK 17+. --- .github/workflows/build.yml | 2 +- README.md | 5 +- .../BugPatternTestExtractor.java | 12 ++-- ...ocumentationGeneratorTaskListenerTest.java | 15 ++-- error-prone-contrib/pom.xml | 1 - .../bugpatterns/AmbiguousJsonCreator.java | 4 +- .../CanonicalAnnotationSyntax.java | 14 ++-- .../bugpatterns/CanonicalClassNameUsage.java | 5 +- .../errorprone/bugpatterns/DirectReturn.java | 14 ++-- .../bugpatterns/FluxFlatMapUsage.java | 5 +- .../FormatStringConcatenation.java | 4 +- .../bugpatterns/IdentityConversion.java | 5 +- .../ImmutablesSortedSetComparator.java | 5 +- .../bugpatterns/IsInstanceLambdaUsage.java | 5 +- .../bugpatterns/JUnitValueSource.java | 23 +++--- ...cographicalAnnotationAttributeListing.java | 15 ++-- .../MockitoMockClassReference.java | 25 ++++--- .../bugpatterns/MongoDBTextFilterUsage.java | 4 +- .../bugpatterns/NestedPublishers.java | 5 +- .../bugpatterns/NonStaticImport.java | 7 +- .../bugpatterns/PrimitiveComparison.java | 62 +++++++++------- .../RedundantStringConversion.java | 28 ++++---- .../bugpatterns/RequestMappingAnnotation.java | 7 +- .../bugpatterns/RequestParamType.java | 4 +- .../bugpatterns/SpringMvcAnnotation.java | 36 ++++------ .../errorprone/bugpatterns/StaticImport.java | 14 ++-- .../errorprone/bugpatterns/TimeZoneUsage.java | 4 +- .../errorprone/refasterrules/StringRules.java | 12 +++- .../bugpatterns/MethodReferenceUsage.java | 70 +++++++++---------- .../ErrorProneRuntimeClasspath.java | 5 +- .../ExhaustiveRefasterTypeMigration.java | 5 +- .../bugpatterns/RefasterAnyOfUsage.java | 25 +++---- .../UnqualifiedSuggestedFixImport.java | 18 +++-- .../utils/AnnotationAttributeMatcher.java | 5 +- .../errorprone/utils/MoreJUnitMatchers.java | 9 ++- pom.xml | 15 +--- .../refaster/plugin/RefasterRuleCompiler.java | 5 ++ refaster-runner/pom.xml | 3 - .../errorprone/refaster/runner/Refaster.java | 17 ++--- .../refaster/runner/RefasterTest.java | 19 +++-- .../errorprone/refaster/matchers/IsEmpty.java | 3 +- .../matchers/IsLikelyTrivialComputation.java | 28 ++++---- .../matchers/ThrowsCheckedException.java | 8 +-- .../matchers/AbstractMatcherTestChecker.java | 7 +- refaster-test-support/pom.xml | 3 - .../refaster/test/RefasterRuleCollection.java | 11 +-- 46 files changed, 283 insertions(+), 315 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4b3c49e985..ef03140a36 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: os: [ ubuntu-22.04 ] - jdk: [ 11.0.20, 17.0.8, 21.0.0 ] + jdk: [ 17.0.8, 21.0.0 ] distribution: [ temurin ] experimental: [ false ] include: diff --git a/README.md b/README.md index 2d06024f33..6b08e469ea 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,9 @@ high-quality and consistent Java code_][picnic-blog-ep-post]. ### Installation This library is built on top of [Error Prone][error-prone-orig-repo]. To use -it, read the installation guide for Maven or Gradle below. +it, read the installation guide for Maven or Gradle below. The library requires +that your build is executed using JDK 17 or above, but supports builds that +[target][baeldung-java-source-target-options] older versions of Java. #### Maven @@ -263,6 +265,7 @@ guidelines][contributing]. If you want to report a security vulnerability, please do so through a private channel; please see our [security policy][security] for details. +[baeldung-java-source-target-options]: https://www.baeldung.com/java-source-target-options [bug-checks]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ [bug-checks-identity-conversion]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java [codeql-badge]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/codeql.yml/badge.svg?branch=master&event=push diff --git a/documentation-support/src/main/java/tech/picnic/errorprone/documentation/BugPatternTestExtractor.java b/documentation-support/src/main/java/tech/picnic/errorprone/documentation/BugPatternTestExtractor.java index a8cc2e41d0..7ca51f683a 100644 --- a/documentation-support/src/main/java/tech/picnic/errorprone/documentation/BugPatternTestExtractor.java +++ b/documentation-support/src/main/java/tech/picnic/errorprone/documentation/BugPatternTestExtractor.java @@ -135,8 +135,8 @@ private static Optional getClassUnderTest( } ExpressionTree receiver = ASTHelpers.getReceiver(tree); - return receiver instanceof MethodInvocationTree - ? getClassUnderTest((MethodInvocationTree) receiver, state) + return receiver instanceof MethodInvocationTree methodInvocation + ? getClassUnderTest(methodInvocation, state) : Optional.empty(); } @@ -154,8 +154,8 @@ private static void extractIdentificationTestCases( } ExpressionTree receiver = ASTHelpers.getReceiver(tree); - if (receiver instanceof MethodInvocationTree) { - extractIdentificationTestCases((MethodInvocationTree) receiver, sink, state); + if (receiver instanceof MethodInvocationTree methodInvocation) { + extractIdentificationTestCases(methodInvocation, sink, state); } } @@ -184,8 +184,8 @@ private static void extractReplacementTestCases( } ExpressionTree receiver = ASTHelpers.getReceiver(tree); - if (receiver instanceof MethodInvocationTree) { - extractReplacementTestCases((MethodInvocationTree) receiver, sink, state); + if (receiver instanceof MethodInvocationTree methodInvocation) { + extractReplacementTestCases(methodInvocation, sink, state); } } diff --git a/documentation-support/src/test/java/tech/picnic/errorprone/documentation/DocumentationGeneratorTaskListenerTest.java b/documentation-support/src/test/java/tech/picnic/errorprone/documentation/DocumentationGeneratorTaskListenerTest.java index 2394b15a14..685c7cd6f2 100644 --- a/documentation-support/src/test/java/tech/picnic/errorprone/documentation/DocumentationGeneratorTaskListenerTest.java +++ b/documentation-support/src/test/java/tech/picnic/errorprone/documentation/DocumentationGeneratorTaskListenerTest.java @@ -93,13 +93,20 @@ void extraction(@TempDir Path outputDirectory) { "DocumentationGeneratorTaskListenerTestClass.java", "class DocumentationGeneratorTaskListenerTestClass {}"); - // XXX: Once we support only JDK 15+, use a text block for the `expected` string. assertThat( outputDirectory.resolve( "documentation-generator-task-listener-test-DocumentationGeneratorTaskListenerTestClass.json")) .content(UTF_8) .isEqualToIgnoringWhitespace( - "{\"className\":\"DocumentationGeneratorTaskListenerTestClass\",\"path\":[\"CLASS: DocumentationGeneratorTaskListenerTestClass\",\"COMPILATION_UNIT\"]}"); + """ + { + "className": "DocumentationGeneratorTaskListenerTestClass", + "path": [ + "CLASS: DocumentationGeneratorTaskListenerTestClass", + "COMPILATION_UNIT" + ] + } + """); } @Immutable @@ -125,8 +132,8 @@ public Optional tryExtract(ClassTree tree, VisitorState st } private static String describeTree(Tree tree) { - return (tree instanceof ClassTree) - ? String.join(": ", String.valueOf(tree.getKind()), ((ClassTree) tree).getSimpleName()) + return (tree instanceof ClassTree clazz) + ? String.join(": ", String.valueOf(tree.getKind()), clazz.getSimpleName()) : tree.getKind().toString(); } } diff --git a/error-prone-contrib/pom.xml b/error-prone-contrib/pom.xml index eb819f6c7b..94a0493af9 100644 --- a/error-prone-contrib/pom.xml +++ b/error-prone-contrib/pom.xml @@ -279,7 +279,6 @@ - -Xplugin:RefasterRuleCompiler -Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/AmbiguousJsonCreator.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/AmbiguousJsonCreator.java index 4bb8ea1bc6..0d56605e28 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/AmbiguousJsonCreator.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/AmbiguousJsonCreator.java @@ -18,7 +18,7 @@ import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.MethodTree; -import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.code.Symbol; import java.util.Map; import javax.lang.model.element.AnnotationValue; @@ -46,7 +46,7 @@ public Description matchAnnotation(AnnotationTree tree, VisitorState state) { } ClassTree clazz = state.findEnclosing(ClassTree.class); - if (clazz == null || clazz.getKind() != Tree.Kind.ENUM) { + if (clazz == null || clazz.getKind() != Kind.ENUM) { return Description.NO_MATCH; } diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalAnnotationSyntax.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalAnnotationSyntax.java index 601af99d8c..f9939c44fe 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalAnnotationSyntax.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalAnnotationSyntax.java @@ -19,7 +19,6 @@ import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.NewArrayTree; -import com.sun.source.tree.Tree.Kind; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -119,7 +118,7 @@ private static Optional dropRedundantCurlies(AnnotationTree tree, VisitorSt * the expression as a whole. */ ExpressionTree value = - (arg.getKind() == Kind.ASSIGNMENT) ? ((AssignmentTree) arg).getExpression() : arg; + (arg instanceof AssignmentTree assignment) ? assignment.getExpression() : arg; /* Store a fix for each expression that was successfully simplified. */ simplifyAttributeValue(value, state) @@ -130,13 +129,10 @@ private static Optional dropRedundantCurlies(AnnotationTree tree, VisitorSt } private static Optional simplifyAttributeValue(ExpressionTree expr, VisitorState state) { - if (expr.getKind() != Kind.NEW_ARRAY) { - /* There are no curly braces or commas to be dropped here. */ - return Optional.empty(); - } - - NewArrayTree array = (NewArrayTree) expr; - return simplifySingletonArray(array, state).or(() -> dropTrailingComma(array, state)); + /* Drop curly braces or commas if possible. */ + return expr instanceof NewArrayTree newArray + ? simplifySingletonArray(newArray, state).or(() -> dropTrailingComma(newArray, state)) + : Optional.empty(); } /** Returns the expression describing the array's sole element, if any. */ diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalClassNameUsage.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalClassNameUsage.java index f64a15a0c6..6a597d0c4f 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalClassNameUsage.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalClassNameUsage.java @@ -81,9 +81,8 @@ private static boolean isPassedToCanonicalNameUsingType(VisitorState state) { path = path.getParentPath(); } - return path.getLeaf() instanceof MethodInvocationTree - && isOwnedByCanonicalNameUsingType( - ASTHelpers.getSymbol((MethodInvocationTree) path.getLeaf())); + return path.getLeaf() instanceof MethodInvocationTree methodInvocation + && isOwnedByCanonicalNameUsingType(ASTHelpers.getSymbol(methodInvocation)); } private static boolean isOwnedByCanonicalNameUsingType(MethodSymbol symbol) { diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/DirectReturn.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/DirectReturn.java index 8b75500d94..089e38bcee 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/DirectReturn.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/DirectReturn.java @@ -101,19 +101,17 @@ public Description matchBlock(BlockTree tree, VisitorState state) { } private static Optional tryMatchAssignment(Symbol targetSymbol, Tree tree) { - if (tree instanceof ExpressionStatementTree) { - return tryMatchAssignment(targetSymbol, ((ExpressionStatementTree) tree).getExpression()); + if (tree instanceof ExpressionStatementTree expressionStatement) { + return tryMatchAssignment(targetSymbol, expressionStatement.getExpression()); } - if (tree instanceof AssignmentTree) { - AssignmentTree assignment = (AssignmentTree) tree; + if (tree instanceof AssignmentTree assignment) { return targetSymbol.equals(ASTHelpers.getSymbol(assignment.getVariable())) ? Optional.of(assignment.getExpression()) : Optional.empty(); } - if (tree instanceof VariableTree) { - VariableTree declaration = (VariableTree) tree; + if (tree instanceof VariableTree declaration) { return declaration.getModifiers().getAnnotations().isEmpty() && targetSymbol.equals(ASTHelpers.getSymbol(declaration)) ? Optional.ofNullable(declaration.getInitializer()) @@ -151,11 +149,11 @@ private static boolean isIdentifierSymbolReferencedInAssociatedFinallyBlock( Streams.stream(state.getPath()).skip(1), Streams.stream(state.getPath()), (tree, child) -> { - if (!(tree instanceof TryTree)) { + if (!(tree instanceof TryTree tryTree)) { return null; } - BlockTree finallyBlock = ((TryTree) tree).getFinallyBlock(); + BlockTree finallyBlock = tryTree.getFinallyBlock(); return !child.equals(finallyBlock) ? finallyBlock : null; }) .anyMatch(finallyBlock -> referencesIdentifierSymbol(symbol, finallyBlock)); diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/FluxFlatMapUsage.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/FluxFlatMapUsage.java index ebc2a0a7c2..d50cb7d1e6 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/FluxFlatMapUsage.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/FluxFlatMapUsage.java @@ -50,8 +50,9 @@ @AutoService(BugChecker.class) @BugPattern( summary = - "`Flux#flatMap` and `Flux#flatMapSequential` have subtle semantics; " - + "please use `Flux#concatMap` or explicitly specify the desired amount of concurrency", + """ + `Flux#flatMap` and `Flux#flatMapSequential` have subtle semantics; please use \ + `Flux#concatMap` or explicitly specify the desired amount of concurrency""", link = BUG_PATTERNS_BASE_URL + "FluxFlatMapUsage", linkType = CUSTOM, severity = ERROR, diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/FormatStringConcatenation.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/FormatStringConcatenation.java index ceb0f66d42..744c6e732c 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/FormatStringConcatenation.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/FormatStringConcatenation.java @@ -245,8 +245,8 @@ private static class ReplacementArgumentsConstructor } private void appendExpression(Tree tree) { - if (tree instanceof LiteralTree) { - formatString.append(((LiteralTree) tree).getValue()); + if (tree instanceof LiteralTree literal) { + formatString.append(literal.getValue()); } else { formatString.append(formatSpecifier); formatArguments.add(tree); diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java index b35331f087..65c37c5c8e 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java @@ -124,8 +124,9 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState return buildDescription(tree) .setMessage( - "This method invocation appears redundant; remove it or suppress this warning and " - + "add a comment explaining its purpose") + """ + This method invocation appears redundant; remove it or suppress this warning and add a \ + comment explaining its purpose""") .addFix(SuggestedFix.replace(tree, SourceCode.treeToString(sourceTree, state))) .addFix(SuggestedFixes.addSuppressWarnings(state, canonicalName())) .build(); diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImmutablesSortedSetComparator.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImmutablesSortedSetComparator.java index dadf1b0910..16088bd5f1 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImmutablesSortedSetComparator.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImmutablesSortedSetComparator.java @@ -43,8 +43,9 @@ @AutoService(BugChecker.class) @BugPattern( summary = - "`SortedSet` properties of a `@Value.Immutable` or `@Value.Modifiable` type must be " - + "annotated with `@Value.NaturalOrder` or `@Value.ReverseOrder`", + """ + `SortedSet` properties of a `@Value.Immutable` or `@Value.Modifiable` type must be \ + annotated with `@Value.NaturalOrder` or `@Value.ReverseOrder`""", link = BUG_PATTERNS_BASE_URL + "ImmutablesSortedSetComparator", linkType = CUSTOM, severity = ERROR, diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IsInstanceLambdaUsage.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IsInstanceLambdaUsage.java index 5c9ed225d5..2fa02a75e8 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IsInstanceLambdaUsage.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IsInstanceLambdaUsage.java @@ -16,7 +16,6 @@ import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.InstanceOfTree; import com.sun.source.tree.LambdaExpressionTree; -import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.VariableTree; import tech.picnic.errorprone.utils.SourceCode; @@ -41,12 +40,12 @@ public IsInstanceLambdaUsage() {} @Override public Description matchLambdaExpression(LambdaExpressionTree tree, VisitorState state) { - if (tree.getParameters().size() != 1 || tree.getBody().getKind() != Kind.INSTANCE_OF) { + if (tree.getParameters().size() != 1 + || !(tree.getBody() instanceof InstanceOfTree instanceOf)) { return Description.NO_MATCH; } VariableTree param = Iterables.getOnlyElement(tree.getParameters()); - InstanceOfTree instanceOf = (InstanceOfTree) tree.getBody(); if (!ASTHelpers.getSymbol(param).equals(ASTHelpers.getSymbol(instanceOf.getExpression()))) { return Description.NO_MATCH; } diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java index 3c529227e4..6327e392ae 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/JUnitValueSource.java @@ -265,8 +265,8 @@ private static Optional tryExtractValueSourceAttributeValue( arguments.stream() .map( arg -> - arg instanceof MethodInvocationTree - ? Iterables.getOnlyElement(((MethodInvocationTree) arg).getArguments()) + arg instanceof MethodInvocationTree methodInvocation + ? Iterables.getOnlyElement(methodInvocation.getArguments()) : arg) .map(argument -> SourceCode.treeToString(argument, state)) .collect(joining(", "))) @@ -276,16 +276,12 @@ private static Optional tryExtractValueSourceAttributeValue( private static String toValueSourceAttributeName(Type type) { String typeString = type.tsym.name.toString(); - switch (typeString) { - case "Class": - return "classes"; - case "Character": - return "chars"; - case "Integer": - return "ints"; - default: - return typeString.toLowerCase(Locale.ROOT) + 's'; - } + return switch (typeString) { + case "Class" -> "classes"; + case "Character" -> "chars"; + case "Integer" -> "ints"; + default -> typeString.toLowerCase(Locale.ROOT) + 's'; + }; } private static Optional getElementIfSingleton(Collection collection) { @@ -297,11 +293,10 @@ private static Optional getElementIfSingleton(Collection collection) { private static Matcher isSingleDimensionArrayCreationWithAllElementsMatching( Matcher elementMatcher) { return (tree, state) -> { - if (!(tree instanceof NewArrayTree)) { + if (!(tree instanceof NewArrayTree newArray)) { return false; } - NewArrayTree newArray = (NewArrayTree) tree; return newArray.getDimensions().isEmpty() && !newArray.getInitializers().isEmpty() && newArray.getInitializers().stream() diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java index bd6fe649b2..fa92cf5ead 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java @@ -31,7 +31,6 @@ import com.sun.source.tree.NewArrayTree; import com.sun.source.tree.PrimitiveTypeTree; import com.sun.source.tree.Tree; -import com.sun.source.tree.Tree.Kind; import com.sun.source.util.TreeScanner; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Type; @@ -122,13 +121,9 @@ private Optional sortArrayElements(AnnotationTree tree, VisitorState state) } private static Optional extractArray(ExpressionTree expr) { - if (expr.getKind() == Kind.ASSIGNMENT) { - return extractArray(((AssignmentTree) expr).getExpression()); - } - - return Optional.of(expr) - .filter(e -> e.getKind() == Kind.NEW_ARRAY) - .map(NewArrayTree.class::cast); + return expr instanceof AssignmentTree assignment + ? extractArray(assignment.getExpression()) + : Optional.of(expr).filter(NewArrayTree.class::isInstance).map(NewArrayTree.class::cast); } private static Optional suggestSorting( @@ -200,8 +195,8 @@ private static ImmutableList> getStructure(ExpressionTree public @Nullable Void visitLiteral(LiteralTree node, @Nullable Void unused) { Object value = ASTHelpers.constValue(node); nodes.add( - value instanceof String - ? STRING_ARGUMENT_SPLITTER.splitToStream((String) value).collect(toImmutableList()) + value instanceof String str + ? STRING_ARGUMENT_SPLITTER.splitToStream(str).collect(toImmutableList()) : ImmutableList.of(String.valueOf(value))); return super.visitLiteral(node, unused); diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/MockitoMockClassReference.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/MockitoMockClassReference.java index aa891245e0..32d3c823b5 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/MockitoMockClassReference.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/MockitoMockClassReference.java @@ -67,20 +67,19 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState return describeMatch(tree, SuggestedFixes.removeElement(arguments.get(0), arguments, state)); } + // XXX: Use switch pattern matching once the targeted JDK supports this. private static boolean isTypeDerivableFromContext(MethodInvocationTree tree, VisitorState state) { Tree parent = state.getPath().getParentPath().getLeaf(); - switch (parent.getKind()) { - case VARIABLE: - return !ASTHelpers.hasImplicitType((VariableTree) parent, state) - && MoreASTHelpers.areSameType(tree, parent, state); - case ASSIGNMENT: - return MoreASTHelpers.areSameType(tree, parent, state); - case RETURN: - return MoreASTHelpers.findMethodExitedOnReturn(state) - .filter(m -> MoreASTHelpers.areSameType(tree, m.getReturnType(), state)) - .isPresent(); - default: - return false; - } + return switch (parent.getKind()) { + case VARIABLE -> + !ASTHelpers.hasImplicitType((VariableTree) parent, state) + && MoreASTHelpers.areSameType(tree, parent, state); + case ASSIGNMENT -> MoreASTHelpers.areSameType(tree, parent, state); + case RETURN -> + MoreASTHelpers.findMethodExitedOnReturn(state) + .filter(m -> MoreASTHelpers.areSameType(tree, m.getReturnType(), state)) + .isPresent(); + default -> false; + }; } } diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/MongoDBTextFilterUsage.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/MongoDBTextFilterUsage.java index 88116e8eb7..ac9b44be47 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/MongoDBTextFilterUsage.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/MongoDBTextFilterUsage.java @@ -24,7 +24,9 @@ @AutoService(BugChecker.class) @BugPattern( summary = - "Avoid MongoDB's `$text` filter operator, as it can trigger heavy queries and even cause the server to run out of memory", + """ + Avoid MongoDB's `$text` filter operator, as it can trigger heavy queries and even cause \ + the server to run out of memory""", link = BUG_PATTERNS_BASE_URL + "MongoDBTextFilterUsage", linkType = CUSTOM, severity = SUGGESTION, diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NestedPublishers.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NestedPublishers.java index 58f418aaa7..cdc95b3252 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NestedPublishers.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NestedPublishers.java @@ -34,8 +34,9 @@ @AutoService(BugChecker.class) @BugPattern( summary = - "Avoid `Publisher`s that emit other `Publishers`s; " - + "the resultant code is hard to reason about", + """ + Avoid `Publisher`s that emit other `Publishers`s; the resultant code is hard to reason \ + about""", link = BUG_PATTERNS_BASE_URL + "NestedPublishers", linkType = CUSTOM, severity = WARNING, diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NonStaticImport.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NonStaticImport.java index 4ba2beca7e..adbd88f7e1 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NonStaticImport.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/NonStaticImport.java @@ -169,10 +169,9 @@ private static ImmutableTable getUndesire ImmutableTable.builder(); for (ImportTree importTree : tree.getImports()) { Tree qualifiedIdentifier = importTree.getQualifiedIdentifier(); - if (importTree.isStatic() && qualifiedIdentifier instanceof MemberSelectTree) { - MemberSelectTree memberSelectTree = (MemberSelectTree) qualifiedIdentifier; - String type = SourceCode.treeToString(memberSelectTree.getExpression(), state); - String member = memberSelectTree.getIdentifier().toString(); + if (importTree.isStatic() && qualifiedIdentifier instanceof MemberSelectTree memberSelect) { + String type = SourceCode.treeToString(memberSelect.getExpression(), state); + String member = memberSelect.getIdentifier().toString(); if (shouldNotBeStaticallyImported(type, member)) { imports.put( type, diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/PrimitiveComparison.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/PrimitiveComparison.java index e49bbe3f3c..4369fcdac9 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/PrimitiveComparison.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/PrimitiveComparison.java @@ -22,6 +22,7 @@ import com.google.errorprone.matchers.Matcher; import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.LambdaExpressionTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; @@ -44,8 +45,9 @@ @AutoService(BugChecker.class) @BugPattern( summary = - "Ensure invocations of `Comparator#comparing{,Double,Int,Long}` match the return type" - + " of the provided function", + """ + Ensure invocations of `Comparator#comparing{,Double,Int,Long}` match the return type of \ + the provided function""", link = BUG_PATTERNS_BASE_URL + "PrimitiveComparison", linkType = CUSTOM, severity = WARNING, @@ -147,38 +149,44 @@ private static String getPreferredMethod(Type cmpType, boolean isStatic, Visitor return isStatic ? "comparing" : "thenComparing"; } + // XXX: Use switch pattern matching once the targeted JDK supports this. private static Optional getPotentiallyBoxedReturnType(ExpressionTree tree) { - switch (tree.getKind()) { - case LAMBDA_EXPRESSION: - /* Return the lambda expression's actual return type. */ - return Optional.ofNullable(ASTHelpers.getType(((LambdaExpressionTree) tree).getBody())); - case MEMBER_REFERENCE: - /* Return the method's declared return type. */ - // XXX: Very fragile. Do better. - Type subType2 = ((JCMemberReference) tree).referentType; - return Optional.of(subType2.getReturnType()); - default: - /* This appears to be a genuine `{,ToInt,ToLong,ToDouble}Function`. */ - return Optional.empty(); + if (tree instanceof LambdaExpressionTree lambdaExpression) { + /* Return the lambda expression's actual return type. */ + return Optional.ofNullable(ASTHelpers.getType(lambdaExpression.getBody())); } + + // XXX: The match against a concrete type and reference to one of its fields is fragile. Do + // better. + if (tree instanceof JCMemberReference memberReference) { + /* Return the method's declared return type. */ + Type subType = memberReference.referentType; + return Optional.of(subType.getReturnType()); + } + + /* This appears to be a genuine `{,ToInt,ToLong,ToDouble}Function`. */ + return Optional.empty(); } + // XXX: Use switch pattern matching once the targeted JDK supports this. private static Fix suggestFix( MethodInvocationTree tree, String preferredMethodName, VisitorState state) { ExpressionTree expr = tree.getMethodSelect(); - switch (expr.getKind()) { - case IDENTIFIER: - SuggestedFix.Builder fix = SuggestedFix.builder(); - String replacement = - SuggestedFixes.qualifyStaticImport( - Comparator.class.getCanonicalName() + '.' + preferredMethodName, fix, state); - return fix.replace(expr, replacement).build(); - case MEMBER_SELECT: - MemberSelectTree ms = (MemberSelectTree) tree.getMethodSelect(); - return SuggestedFix.replace( - ms, SourceCode.treeToString(ms.getExpression(), state) + '.' + preferredMethodName); - default: - throw new VerifyException("Unexpected type of expression: " + expr.getKind()); + + if (expr instanceof IdentifierTree) { + SuggestedFix.Builder fix = SuggestedFix.builder(); + String replacement = + SuggestedFixes.qualifyStaticImport( + Comparator.class.getCanonicalName() + '.' + preferredMethodName, fix, state); + return fix.replace(expr, replacement).build(); } + + if (expr instanceof MemberSelectTree memberSelect) { + return SuggestedFix.replace( + memberSelect, + SourceCode.treeToString(memberSelect.getExpression(), state) + '.' + preferredMethodName); + } + + throw new VerifyException("Unexpected type of expression: " + expr.getKind()); } } diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RedundantStringConversion.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RedundantStringConversion.java index fea909d00e..bc6dadbd2f 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RedundantStringConversion.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RedundantStringConversion.java @@ -331,36 +331,32 @@ private Optional trySimplify( } private Optional trySimplify(ExpressionTree tree, VisitorState state) { - if (tree.getKind() != Kind.METHOD_INVOCATION) { + if (!(tree instanceof MethodInvocationTree methodInvocation)) { return Optional.empty(); } - MethodInvocationTree methodInvocation = (MethodInvocationTree) tree; if (!conversionMethodMatcher.matches(methodInvocation, state)) { return Optional.empty(); } - switch (methodInvocation.getArguments().size()) { - case 0: - return trySimplifyNullaryMethod(methodInvocation, state); - case 1: - return trySimplifyUnaryMethod(methodInvocation, state); - default: - throw new IllegalStateException( - "Cannot simplify method call with two or more arguments: " - + SourceCode.treeToString(tree, state)); - } + return switch (methodInvocation.getArguments().size()) { + case 0 -> trySimplifyNullaryMethod(methodInvocation, state); + case 1 -> trySimplifyUnaryMethod(methodInvocation, state); + default -> + throw new IllegalStateException( + "Cannot simplify method call with two or more arguments: " + + SourceCode.treeToString(tree, state)); + }; } private static Optional trySimplifyNullaryMethod( MethodInvocationTree methodInvocation, VisitorState state) { - if (!instanceMethod().matches(methodInvocation, state)) { + if (!instanceMethod().matches(methodInvocation, state) + || !(methodInvocation.getMethodSelect() instanceof MemberSelectTree memberSelect)) { return Optional.empty(); } - return Optional.of(methodInvocation.getMethodSelect()) - .filter(methodSelect -> methodSelect.getKind() == Kind.MEMBER_SELECT) - .map(methodSelect -> ((MemberSelectTree) methodSelect).getExpression()) + return Optional.of(memberSelect.getExpression()) .filter(expr -> !"super".equals(SourceCode.treeToString(expr, state))); } diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestMappingAnnotation.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestMappingAnnotation.java index 52f0f41d85..4e90849167 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestMappingAnnotation.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestMappingAnnotation.java @@ -105,9 +105,10 @@ public Description matchMethod(MethodTree tree, VisitorState state) { && LACKS_PARAMETER_ANNOTATION.matches(tree, state) ? buildDescription(tree) .setMessage( - "Not all parameters of this request mapping method are annotated; this may be a " - + "mistake. If the unannotated parameters represent query string parameters, " - + "annotate them with `@RequestParam`.") + """ + Not all parameters of this request mapping method are annotated; this may be a \ + mistake. If the unannotated parameters represent query string parameters, annotate \ + them with `@RequestParam`.""") .build() : Description.NO_MATCH; } diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestParamType.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestParamType.java index 4455f7f149..ee736da113 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestParamType.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestParamType.java @@ -34,7 +34,9 @@ @AutoService(BugChecker.class) @BugPattern( summary = - "By default, `@RequestParam` does not support `ImmutableCollection` and `ImmutableMap` subtypes", + """ + By default, `@RequestParam` does not support `ImmutableCollection` and `ImmutableMap` \ + subtypes""", link = BUG_PATTERNS_BASE_URL + "RequestParamType", linkType = CUSTOM, severity = ERROR, diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/SpringMvcAnnotation.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/SpringMvcAnnotation.java index 644d00fe43..dbc27412b8 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/SpringMvcAnnotation.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/SpringMvcAnnotation.java @@ -1,6 +1,5 @@ package tech.picnic.errorprone.bugpatterns; -import static com.google.common.base.Verify.verify; import static com.google.errorprone.BugPattern.LinkType.CUSTOM; import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION; import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION; @@ -25,7 +24,6 @@ import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.NewArrayTree; -import com.sun.source.tree.Tree.Kind; import java.util.Optional; import tech.picnic.errorprone.utils.AnnotationAttributeMatcher; import tech.picnic.errorprone.utils.SourceCode; @@ -80,31 +78,25 @@ private static Optional trySimplification( } private static Optional extractUniqueMethod(ExpressionTree arg, VisitorState state) { - verify( - arg.getKind() == Kind.ASSIGNMENT, - "Annotation attribute is not an assignment: %s", - arg.getKind()); - - ExpressionTree expr = ((AssignmentTree) arg).getExpression(); - if (expr.getKind() != Kind.NEW_ARRAY) { - return Optional.of(extractMethod(expr, state)); + if (!(arg instanceof AssignmentTree assignment)) { + throw new VerifyException("Annotation attribute is not an assignment:" + arg.getKind()); } - NewArrayTree newArray = (NewArrayTree) expr; - return Optional.of(newArray.getInitializers()) - .filter(args -> args.size() == 1) - .map(args -> extractMethod(args.get(0), state)); + ExpressionTree expr = assignment.getExpression(); + return expr instanceof NewArrayTree newArray + ? Optional.of(newArray.getInitializers()) + .filter(args -> args.size() == 1) + .map(args -> extractMethod(args.get(0), state)) + : Optional.of(extractMethod(expr, state)); } + // XXX: Use switch pattern matching once the targeted JDK supports this. private static String extractMethod(ExpressionTree expr, VisitorState state) { - switch (expr.getKind()) { - case IDENTIFIER: - return SourceCode.treeToString(expr, state); - case MEMBER_SELECT: - return ((MemberSelectTree) expr).getIdentifier().toString(); - default: - throw new VerifyException("Unexpected type of expression: " + expr.getKind()); - } + return switch (expr.getKind()) { + case IDENTIFIER -> SourceCode.treeToString(expr, state); + case MEMBER_SELECT -> ((MemberSelectTree) expr).getIdentifier().toString(); + default -> throw new VerifyException("Unexpected type of expression: " + expr.getKind()); + }; } private static Fix replaceAnnotation( diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/StaticImport.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/StaticImport.java index 5d2bd97210..cd1d783164 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/StaticImport.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/StaticImport.java @@ -44,6 +44,7 @@ import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.code.Type; import java.nio.charset.StandardCharsets; import java.time.ZoneOffset; @@ -209,15 +210,10 @@ private static boolean isCandidateContext(VisitorState state) { Tree parentTree = requireNonNull(state.getPath().getParentPath(), "MemberSelectTree lacks enclosing node") .getLeaf(); - switch (parentTree.getKind()) { - case IMPORT: - case MEMBER_SELECT: - return false; - case METHOD_INVOCATION: - return ((MethodInvocationTree) parentTree).getTypeArguments().isEmpty(); - default: - return true; - } + + return parentTree instanceof MethodInvocationTree methodInvocation + ? methodInvocation.getTypeArguments().isEmpty() + : (parentTree.getKind() != Kind.IMPORT && parentTree.getKind() != Kind.MEMBER_SELECT); } private static boolean isCandidate(MemberSelectTree tree) { diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/TimeZoneUsage.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/TimeZoneUsage.java index 4bc0650e18..eb9a3b072b 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/TimeZoneUsage.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/TimeZoneUsage.java @@ -34,7 +34,9 @@ @AutoService(BugChecker.class) @BugPattern( summary = - "Derive the current time from an existing `Clock` Spring bean, and don't rely on a `Clock`'s time zone", + """ + Derive the current time from an existing `Clock` Spring bean, and don't rely on a \ + `Clock`'s time zone""", link = BUG_PATTERNS_BASE_URL + "TimeZoneUsage", linkType = CUSTOM, severity = WARNING, diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StringRules.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StringRules.java index d4c2cdf0f7..f3610a8612 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StringRules.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/StringRules.java @@ -29,7 +29,9 @@ final class StringRules { private StringRules() {} /** Prefer {@link String#isEmpty()} over alternatives that consult the string's length. */ - // XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes. + // XXX: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence` + // subtypes. This does require a mechanism (perhaps an annotation, or a separate Maven module) to + // make sure that non-String expressions are rewritten only if client code also targets JDK 15+. static final class StringIsEmpty { @BeforeTemplate boolean before(String str) { @@ -44,7 +46,9 @@ boolean after(String str) { } /** Prefer a method reference to {@link String#isEmpty()} over the equivalent lambda function. */ - // XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes. + // XXX: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence` + // subtypes. However, `CharSequence::isEmpty` isn't as nice as `String::isEmpty`, so we might want + // to introduce a rule that suggests `String::isEmpty` where possible. // XXX: As it stands, this rule is a special case of what `MethodReferenceUsage` tries to achieve. // If/when `MethodReferenceUsage` becomes production ready, we should simply drop this check. static final class StringIsEmptyPredicate { @@ -60,7 +64,9 @@ Predicate after() { } /** Prefer a method reference to {@link String#isEmpty()} over the equivalent lambda function. */ - // XXX: Once we target JDK 15+, generalize this rule to cover all `CharSequence` subtypes. + // XXX: Now that we build with JDK 15+, this rule can be generalized to cover all `CharSequence` + // subtypes. However, `CharSequence::isEmpty` isn't as nice as `String::isEmpty`, so we might want + // to introduce a rule that suggests `String::isEmpty` where possible. static final class StringIsNotEmptyPredicate { @BeforeTemplate Predicate before() { diff --git a/error-prone-experimental/src/main/java/tech/picnic/errorprone/experimental/bugpatterns/MethodReferenceUsage.java b/error-prone-experimental/src/main/java/tech/picnic/errorprone/experimental/bugpatterns/MethodReferenceUsage.java index 8795235faf..1bea7ef9fb 100644 --- a/error-prone-experimental/src/main/java/tech/picnic/errorprone/experimental/bugpatterns/MethodReferenceUsage.java +++ b/error-prone-experimental/src/main/java/tech/picnic/errorprone/experimental/bugpatterns/MethodReferenceUsage.java @@ -27,7 +27,6 @@ import com.sun.source.tree.ParenthesizedTree; import com.sun.source.tree.ReturnTree; import com.sun.source.tree.Tree; -import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.VariableTree; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; @@ -84,22 +83,19 @@ public Description matchLambdaExpression(LambdaExpressionTree tree, VisitorState .orElse(Description.NO_MATCH); } + // XXX: Use switch pattern matching once the targeted JDK supports this. private static Optional constructMethodRef( LambdaExpressionTree lambdaExpr, Tree subTree) { - switch (subTree.getKind()) { - case BLOCK: - return constructMethodRef(lambdaExpr, (BlockTree) subTree); - case EXPRESSION_STATEMENT: - return constructMethodRef(lambdaExpr, ((ExpressionStatementTree) subTree).getExpression()); - case METHOD_INVOCATION: - return constructMethodRef(lambdaExpr, (MethodInvocationTree) subTree); - case PARENTHESIZED: - return constructMethodRef(lambdaExpr, ((ParenthesizedTree) subTree).getExpression()); - case RETURN: - return constructMethodRef(lambdaExpr, ((ReturnTree) subTree).getExpression()); - default: - return Optional.empty(); - } + return switch (subTree.getKind()) { + case BLOCK -> constructMethodRef(lambdaExpr, (BlockTree) subTree); + case EXPRESSION_STATEMENT -> + constructMethodRef(lambdaExpr, ((ExpressionStatementTree) subTree).getExpression()); + case METHOD_INVOCATION -> constructMethodRef(lambdaExpr, (MethodInvocationTree) subTree); + case PARENTHESIZED -> + constructMethodRef(lambdaExpr, ((ParenthesizedTree) subTree).getExpression()); + case RETURN -> constructMethodRef(lambdaExpr, ((ReturnTree) subTree).getExpression()); + default -> Optional.empty(); + }; } private static Optional constructMethodRef( @@ -117,33 +113,35 @@ private static Optional constructMethodRef( .flatMap(expectedInstance -> constructMethodRef(lambdaExpr, subTree, expectedInstance)); } - @SuppressWarnings( - "java:S1151" /* Extracting `IDENTIFIER` case block to separate method does not improve readability. */) + // XXX: Review whether to use switch pattern matching once the targeted JDK supports this. private static Optional constructMethodRef( LambdaExpressionTree lambdaExpr, MethodInvocationTree subTree, Optional expectedInstance) { ExpressionTree methodSelect = subTree.getMethodSelect(); - switch (methodSelect.getKind()) { - case IDENTIFIER: - if (expectedInstance.isPresent()) { - /* Direct method call; there is no matching "implicit parameter". */ - return Optional.empty(); - } - Symbol sym = ASTHelpers.getSymbol(methodSelect); - return ASTHelpers.isStatic(sym) - ? constructFix(lambdaExpr, sym.owner, methodSelect) - : constructFix(lambdaExpr, "this", methodSelect); - case MEMBER_SELECT: - return constructMethodRef(lambdaExpr, (MemberSelectTree) methodSelect, expectedInstance); - default: - throw new VerifyException("Unexpected type of expression: " + methodSelect.getKind()); + + if (methodSelect instanceof IdentifierTree) { + if (expectedInstance.isPresent()) { + /* Direct method call; there is no matching "implicit parameter". */ + return Optional.empty(); + } + + Symbol sym = ASTHelpers.getSymbol(methodSelect); + return ASTHelpers.isStatic(sym) + ? constructFix(lambdaExpr, sym.owner, methodSelect) + : constructFix(lambdaExpr, "this", methodSelect); + } + + if (methodSelect instanceof MemberSelectTree memberSelect) { + return constructMethodRef(lambdaExpr, memberSelect, expectedInstance); } + + throw new VerifyException("Unexpected type of expression: " + methodSelect.getKind()); } private static Optional constructMethodRef( LambdaExpressionTree lambdaExpr, MemberSelectTree subTree, Optional expectedInstance) { - if (subTree.getExpression().getKind() != Kind.IDENTIFIER) { + if (!(subTree.getExpression() instanceof IdentifierTree identifier)) { // XXX: Could be parenthesized. Handle. Also in other classes. /* * Only suggest a replacement if the method select's expression provably doesn't have @@ -152,12 +150,12 @@ private static Optional constructMethodRef( return Optional.empty(); } - Name lhs = ((IdentifierTree) subTree.getExpression()).getName(); + Name lhs = identifier.getName(); if (expectedInstance.isEmpty()) { return constructFix(lambdaExpr, lhs, subTree.getIdentifier()); } - Type lhsType = ASTHelpers.getType(subTree.getExpression()); + Type lhsType = ASTHelpers.getType(identifier); if (lhsType == null || !expectedInstance.orElseThrow().equals(lhs)) { return Optional.empty(); } @@ -182,8 +180,8 @@ private static Optional> matchArguments( for (int i = 0; i < args.size(); i++) { ExpressionTree arg = args.get(i); - if (arg.getKind() != Kind.IDENTIFIER - || !((IdentifierTree) arg).getName().equals(expectedArguments.get(i + diff))) { + if (!(arg instanceof IdentifierTree identifier) + || !identifier.getName().equals(expectedArguments.get(i + diff))) { return Optional.empty(); } } diff --git a/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/ErrorProneRuntimeClasspath.java b/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/ErrorProneRuntimeClasspath.java index ae384cfb46..1a13aba94f 100644 --- a/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/ErrorProneRuntimeClasspath.java +++ b/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/ErrorProneRuntimeClasspath.java @@ -50,8 +50,9 @@ @AutoService(BugChecker.class) @BugPattern( summary = - "Prefer `Class#getCanonicalName()` over an equivalent string literal if and only if the " - + "type will be on the runtime classpath", + """ + Prefer `Class#getCanonicalName()` over an equivalent string literal if and only if the \ + type will be on the runtime classpath""", link = BUG_PATTERNS_BASE_URL + "ErrorProneRuntimeClasspath", linkType = CUSTOM, severity = SUGGESTION, diff --git a/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/ExhaustiveRefasterTypeMigration.java b/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/ExhaustiveRefasterTypeMigration.java index b5c5c0d7a4..9874b0f4d8 100644 --- a/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/ExhaustiveRefasterTypeMigration.java +++ b/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/ExhaustiveRefasterTypeMigration.java @@ -72,8 +72,9 @@ @AutoService(BugChecker.class) @BugPattern( summary = - "The set of unmigrated methods listed by the `@TypeMigration` annotation must be minimal " - + "yet exhaustive", + """ + The set of unmigrated methods listed by the `@TypeMigration` annotation must be minimal \ + yet exhaustive""", link = BUG_PATTERNS_BASE_URL + "ExhaustiveRefasterTypeMigration", linkType = CUSTOM, severity = WARNING, diff --git a/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/RefasterAnyOfUsage.java b/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/RefasterAnyOfUsage.java index c0e9ebdc4c..e2f85603e6 100644 --- a/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/RefasterAnyOfUsage.java +++ b/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/RefasterAnyOfUsage.java @@ -42,21 +42,18 @@ public RefasterAnyOfUsage() {} @Override public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { - if (REFASTER_ANY_OF.matches(tree, state)) { - switch (tree.getArguments().size()) { - case 0: - // We can't safely fix this case; dropping the expression may produce non-compilable code. - return describeMatch(tree); - case 1: - return describeMatch( - tree, - SuggestedFix.replace( - tree, SourceCode.treeToString(tree.getArguments().get(0), state))); - default: - /* Handled below. */ - } + int argumentCount = tree.getArguments().size(); + if (argumentCount > 1 || !REFASTER_ANY_OF.matches(tree, state)) { + return Description.NO_MATCH; } - return Description.NO_MATCH; + if (argumentCount == 0) { + /* We can't safely fix this case; dropping the expression may produce non-compilable code. */ + return describeMatch(tree); + } + + return describeMatch( + tree, + SuggestedFix.replace(tree, SourceCode.treeToString(tree.getArguments().get(0), state))); } } diff --git a/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/UnqualifiedSuggestedFixImport.java b/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/UnqualifiedSuggestedFixImport.java index 8d762cd336..1de8b046d7 100644 --- a/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/UnqualifiedSuggestedFixImport.java +++ b/error-prone-guidelines/src/main/java/tech/picnic/errorprone/guidelines/bugpatterns/UnqualifiedSuggestedFixImport.java @@ -45,16 +45,14 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState return Description.NO_MATCH; } - switch (ASTHelpers.getSymbol(tree).getSimpleName().toString()) { - case "addImport": - return createDescription( - tree, "SuggestedFix.Builder#addImport", "SuggestedFixes#qualifyType"); - case "addStaticImport": - return createDescription( - tree, "SuggestedFix.Builder#addStaticImport", "SuggestedFixes#qualifyStaticImport"); - default: - return Description.NO_MATCH; - } + return switch (ASTHelpers.getSymbol(tree).getSimpleName().toString()) { + case "addImport" -> + createDescription(tree, "SuggestedFix.Builder#addImport", "SuggestedFixes#qualifyType"); + case "addStaticImport" -> + createDescription( + tree, "SuggestedFix.Builder#addStaticImport", "SuggestedFixes#qualifyStaticImport"); + default -> Description.NO_MATCH; + }; } private Description createDescription( diff --git a/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/AnnotationAttributeMatcher.java b/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/AnnotationAttributeMatcher.java index 1da2ce124c..4a95fe3862 100644 --- a/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/AnnotationAttributeMatcher.java +++ b/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/AnnotationAttributeMatcher.java @@ -9,7 +9,6 @@ import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.ExpressionTree; -import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.code.Type; import java.io.Serializable; import java.util.HashSet; @@ -116,8 +115,8 @@ public Stream extractMatchingArguments(AnnotationTree tree) { } private static String extractAttributeName(ExpressionTree expr) { - return (expr.getKind() == Kind.ASSIGNMENT) - ? ASTHelpers.getSymbol(((AssignmentTree) expr).getVariable()).getSimpleName().toString() + return (expr instanceof AssignmentTree assignment) + ? ASTHelpers.getSymbol(assignment.getVariable()).getSimpleName().toString() : "value"; } diff --git a/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/MoreJUnitMatchers.java b/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/MoreJUnitMatchers.java index 9439523a7b..d2115342a5 100644 --- a/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/MoreJUnitMatchers.java +++ b/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/MoreJUnitMatchers.java @@ -99,14 +99,13 @@ public static ImmutableList getMethodSourceFactoryDescriptors( String methodName = method.getName().toString(); ExpressionTree value = AnnotationMatcherUtils.getArgument(methodSourceAnnotation, "value"); - if (!(value instanceof NewArrayTree)) { + if (!(value instanceof NewArrayTree newArray)) { return ImmutableList.of(toMethodSourceFactoryDescriptor(value, methodName)); } - return ((NewArrayTree) value) - .getInitializers().stream() - .map(name -> toMethodSourceFactoryDescriptor(name, methodName)) - .collect(toImmutableList()); + return newArray.getInitializers().stream() + .map(name -> toMethodSourceFactoryDescriptor(name, methodName)) + .collect(toImmutableList()); } private static String toMethodSourceFactoryDescriptor( diff --git a/pom.xml b/pom.xml index fe5e7397a7..005c986020 100644 --- a/pom.xml +++ b/pom.xml @@ -211,7 +211,7 @@ 2.24.1 0.1.22 1.0 - 11 + 17 3.9.5 5.10.0 1.0.1 @@ -467,14 +467,6 @@ org.mongodb mongodb-driver-core 4.11.1 - - - - org.mongodb - bson-record-codec - - org.openrewrite @@ -2016,11 +2008,6 @@ sonar - - - sonar.projectKey - - true diff --git a/refaster-compiler/src/main/java/tech/picnic/errorprone/refaster/plugin/RefasterRuleCompiler.java b/refaster-compiler/src/main/java/tech/picnic/errorprone/refaster/plugin/RefasterRuleCompiler.java index e7423961df..950fa7b019 100644 --- a/refaster-compiler/src/main/java/tech/picnic/errorprone/refaster/plugin/RefasterRuleCompiler.java +++ b/refaster-compiler/src/main/java/tech/picnic/errorprone/refaster/plugin/RefasterRuleCompiler.java @@ -25,4 +25,9 @@ public void init(JavacTask javacTask, String... args) { javacTask.addTaskListener( new RefasterRuleCompilerTaskListener(((BasicJavacTask) javacTask).getContext())); } + + @Override + public boolean autoStart() { + return true; + } } diff --git a/refaster-runner/pom.xml b/refaster-runner/pom.xml index 7a33e17399..1386418758 100644 --- a/refaster-runner/pom.xml +++ b/refaster-runner/pom.xml @@ -124,9 +124,6 @@ ${project.version} - - -Xplugin:RefasterRuleCompiler - diff --git a/refaster-runner/src/main/java/tech/picnic/errorprone/refaster/runner/Refaster.java b/refaster-runner/src/main/java/tech/picnic/errorprone/refaster/runner/Refaster.java index a4322d2bb4..274b9b5710 100644 --- a/refaster-runner/src/main/java/tech/picnic/errorprone/refaster/runner/Refaster.java +++ b/refaster-runner/src/main/java/tech/picnic/errorprone/refaster/runner/Refaster.java @@ -136,16 +136,13 @@ private Optional getSeverityOverride(VisitorState state) { } private static Optional toSeverityLevel(Severity severity) { - switch (severity) { - case DEFAULT: - return Optional.empty(); - case WARN: - return Optional.of(WARNING); - case ERROR: - return Optional.of(ERROR); - default: - throw new IllegalStateException(String.format("Unsupported severity='%s'", severity)); - } + return switch (severity) { + case DEFAULT -> Optional.empty(); + case WARN -> Optional.of(WARNING); + case ERROR -> Optional.of(ERROR); + default -> + throw new IllegalStateException(String.format("Unsupported severity='%s'", severity)); + }; } /** diff --git a/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/RefasterTest.java b/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/RefasterTest.java index 8dd3bc2aff..6561d13b42 100644 --- a/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/RefasterTest.java +++ b/refaster-runner/src/test/java/tech/picnic/errorprone/refaster/runner/RefasterTest.java @@ -191,17 +191,14 @@ private static ImmutableList extractRefasterSeverities( } private static SeverityLevel toSeverityLevel(String compilerDiagnosticsPrefix) { - switch (compilerDiagnosticsPrefix) { - case "Note": - return SUGGESTION; - case "warning": - return WARNING; - case "error": - return ERROR; - default: - throw new IllegalStateException( - String.format("Unrecognized diagnostics prefix '%s'", compilerDiagnosticsPrefix)); - } + return switch (compilerDiagnosticsPrefix) { + case "Note" -> SUGGESTION; + case "warning" -> WARNING; + case "error" -> ERROR; + default -> + throw new IllegalStateException( + String.format("Unrecognized diagnostics prefix '%s'", compilerDiagnosticsPrefix)); + }; } @Test diff --git a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/IsEmpty.java b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/IsEmpty.java index fd44397a57..d61cd180e6 100644 --- a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/IsEmpty.java +++ b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/IsEmpty.java @@ -140,11 +140,10 @@ private boolean isEmptyCollectionConstructor(ExpressionTree tree, VisitorState s } private static boolean isEmptyArrayCreation(ExpressionTree tree) { - if (!(tree instanceof NewArrayTree)) { + if (!(tree instanceof NewArrayTree newArray)) { return false; } - NewArrayTree newArray = (NewArrayTree) tree; return (!newArray.getDimensions().isEmpty() && ZERO.equals(ASTHelpers.constValue(newArray.getDimensions().get(0), Integer.class))) || (newArray.getInitializers() != null && newArray.getInitializers().isEmpty()); diff --git a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/IsLikelyTrivialComputation.java b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/IsLikelyTrivialComputation.java index 9cfa30b511..dbf56e80e6 100644 --- a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/IsLikelyTrivialComputation.java +++ b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/IsLikelyTrivialComputation.java @@ -23,7 +23,7 @@ public IsLikelyTrivialComputation() {} @Override public boolean matches(ExpressionTree expressionTree, VisitorState state) { - if (expressionTree instanceof MethodInvocationTree) { + if (expressionTree instanceof MethodInvocationTree methodInvocation) { // XXX: Method invocations are generally *not* trivial computations, but we make an exception // for nullary method invocations on the result of a trivial computation. This exception // allows this `Matcher` to by the `OptionalOrElseGet` Refaster rule, such that it does not @@ -31,7 +31,6 @@ public boolean matches(ExpressionTree expressionTree, VisitorState state) { // references. Once the `MethodReferenceUsage` bug checker is production-ready, this exception // should be removed. (But at that point, instead defining a `RequiresComputation` matcher may // be more appropriate.) - MethodInvocationTree methodInvocation = (MethodInvocationTree) expressionTree; if (methodInvocation.getArguments().isEmpty() && matches(methodInvocation.getMethodSelect())) { return true; @@ -44,9 +43,8 @@ && matches(methodInvocation.getMethodSelect())) { // XXX: Some `BinaryTree`s may represent what could be considered "trivial computations". // Depending on feedback such trees may be matched in the future. private static boolean matches(ExpressionTree expressionTree) { - if (expressionTree instanceof ArrayAccessTree) { - return matches(((ArrayAccessTree) expressionTree).getExpression()) - && matches(((ArrayAccessTree) expressionTree).getIndex()); + if (expressionTree instanceof ArrayAccessTree arrayAccess) { + return matches(arrayAccess.getExpression()) && matches(arrayAccess.getIndex()); } if (expressionTree instanceof LiteralTree) { @@ -65,26 +63,26 @@ private static boolean matches(ExpressionTree expressionTree) { return true; } - if (expressionTree instanceof MemberReferenceTree) { - return matches(((MemberReferenceTree) expressionTree).getQualifierExpression()); + if (expressionTree instanceof MemberReferenceTree memberReference) { + return matches(memberReference.getQualifierExpression()); } - if (expressionTree instanceof MemberSelectTree) { - return matches(((MemberSelectTree) expressionTree).getExpression()); + if (expressionTree instanceof MemberSelectTree memberSelect) { + return matches(memberSelect.getExpression()); } - if (expressionTree instanceof ParenthesizedTree) { - return matches(((ParenthesizedTree) expressionTree).getExpression()); + if (expressionTree instanceof ParenthesizedTree parenthesized) { + return matches(parenthesized.getExpression()); } - if (expressionTree instanceof TypeCastTree) { - return matches(((TypeCastTree) expressionTree).getExpression()); + if (expressionTree instanceof TypeCastTree typeCast) { + return matches(typeCast.getExpression()); } - if (expressionTree instanceof UnaryTree) { + if (expressionTree instanceof UnaryTree unary) { // XXX: Arguably side-effectful options such as pre- and post-increment and -decrement are not // trivial. - return matches(((UnaryTree) expressionTree).getExpression()); + return matches(unary.getExpression()); } return false; diff --git a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/ThrowsCheckedException.java b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/ThrowsCheckedException.java index 21b520f841..dca3c552a3 100644 --- a/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/ThrowsCheckedException.java +++ b/refaster-support/src/main/java/tech/picnic/errorprone/refaster/matchers/ThrowsCheckedException.java @@ -23,12 +23,12 @@ public ThrowsCheckedException() {} @Override public boolean matches(ExpressionTree tree, VisitorState state) { - if (tree instanceof LambdaExpressionTree) { - return throwsCheckedException((LambdaExpressionTree) tree, state); + if (tree instanceof LambdaExpressionTree lambdaExpression) { + return throwsCheckedException(lambdaExpression, state); } - if (tree instanceof MemberReferenceTree) { - return throwsCheckedException((MemberReferenceTree) tree, state); + if (tree instanceof MemberReferenceTree memberReference) { + return throwsCheckedException(memberReference, state); } Type type = ASTHelpers.getType(tree); diff --git a/refaster-support/src/test/java/tech/picnic/errorprone/refaster/matchers/AbstractMatcherTestChecker.java b/refaster-support/src/test/java/tech/picnic/errorprone/refaster/matchers/AbstractMatcherTestChecker.java index 2f1f7e7375..1eaba1215d 100644 --- a/refaster-support/src/test/java/tech/picnic/errorprone/refaster/matchers/AbstractMatcherTestChecker.java +++ b/refaster-support/src/test/java/tech/picnic/errorprone/refaster/matchers/AbstractMatcherTestChecker.java @@ -37,9 +37,8 @@ public Description matchCompilationUnit(CompilationUnitTree compilationUnit, Vis new TreePathScanner<@Nullable Void, @Nullable Void>() { @Override public @Nullable Void scan(@Nullable Tree tree, @Nullable Void unused) { - if (tree instanceof ExpressionTree) { + if (tree instanceof ExpressionTree expressionTree) { TreePath path = new TreePath(getCurrentPath(), tree); - ExpressionTree expressionTree = (ExpressionTree) tree; if (!isMethodSelect(expressionTree, path) && delegate.matches(expressionTree, state.withPath(path))) { state.reportMatch(describeMatch(tree)); @@ -94,7 +93,7 @@ private static boolean isMethodSelect(ExpressionTree tree, TreePath path) { } Tree parentTree = parentPath.getLeaf(); - return parentTree instanceof MethodInvocationTree - && ((MethodInvocationTree) parentTree).getMethodSelect().equals(tree); + return parentTree instanceof MethodInvocationTree methodInvocation + && methodInvocation.getMethodSelect().equals(tree); } } diff --git a/refaster-test-support/pom.xml b/refaster-test-support/pom.xml index a74a030f28..590ff0a287 100644 --- a/refaster-test-support/pom.xml +++ b/refaster-test-support/pom.xml @@ -106,9 +106,6 @@ ${project.version} - - -Xplugin:RefasterRuleCompiler - diff --git a/refaster-test-support/src/main/java/tech/picnic/errorprone/refaster/test/RefasterRuleCollection.java b/refaster-test-support/src/main/java/tech/picnic/errorprone/refaster/test/RefasterRuleCollection.java index 5fb620f496..d58ec2c745 100644 --- a/refaster-test-support/src/main/java/tech/picnic/errorprone/refaster/test/RefasterRuleCollection.java +++ b/refaster-test-support/src/main/java/tech/picnic/errorprone/refaster/test/RefasterRuleCollection.java @@ -154,8 +154,8 @@ private void reportIncorrectClassName(CompilationUnitTree tree, VisitorState sta String expectedClassName = ruleCollectionUnderTest + "Test"; for (Tree typeDeclaration : tree.getTypeDecls()) { - if (typeDeclaration instanceof ClassTree) { - if (!((ClassTree) typeDeclaration).getSimpleName().contentEquals(expectedClassName)) { + if (typeDeclaration instanceof ClassTree classTree) { + if (!classTree.getSimpleName().contentEquals(expectedClassName)) { state.reportMatch( describeMatch( typeDeclaration, @@ -276,9 +276,10 @@ private void reportUnexpectedMatches( unexpectedMatchesByLineNumber.entries().stream() .map( e -> - String.format( - "Rule `%s` matches on line %s, while it should match in a method named `test%s`.", - e.getValue(), e.getKey(), e.getValue())) + """ + Rule `%s` matches on line %s, while it should match in a method named \ + `test%s`.""" + .formatted(e.getValue(), e.getKey(), e.getValue())) .collect(toImmutableSet()), state); }