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); }