Compare commits

..

1 Commits

Author SHA1 Message Date
Stephan Schroevers
f9910160c7 WIP: Generate documentation
(Manual run with `mvn exec:java@generate-bugpattern-docs -pl refaster-runner`.)

TODO: Actually hook this up in a website
2023-01-07 18:31:57 +01:00
76 changed files with 459 additions and 1276 deletions

View File

@@ -1,3 +1 @@
--batch-mode
--errors
--strict-checksums
--batch-mode --errors --strict-checksums

View File

@@ -36,9 +36,7 @@ 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.
#### Maven
it:
1. First, follow Error Prone's [installation
guide][error-prone-installation-guide].
@@ -96,31 +94,6 @@ it, read the installation guide for Maven or Gradle below.
Prone Support. Alternatively reference this project's `self-check` profile
definition. -->
#### Gradle
1. First, follow the [installation guide]
[error-prone-gradle-installation-guide] of the `gradle-errorprone-plugin`.
2. Next, edit your `build.gradle` file to add one or more Error Prone Support
modules:
```groovy
dependencies {
// Error Prone itself.
errorprone("com.google.errorprone:error_prone_core:${errorProneVersion}")
// Error Prone Support's additional bug checkers.
errorprone("tech.picnic.error-prone-support:error-prone-contrib:${errorProneSupportVersion}")
// Error Prone Support's Refaster rules.
errorprone("tech.picnic.error-prone-support:refaster-runner:${errorProneSupportVersion}")
}
tasks.withType(JavaCompile).configureEach {
options.errorprone.disableWarningsInGeneratedCode = true
// Add other Error Prone flags here. See:
// - https://github.com/tbroyer/gradle-errorprone-plugin#configuration
// - https://errorprone.info/docs/flags
}
```
### Seeing it in action
Consider the following example code:
@@ -234,7 +207,6 @@ guidelines][contributing].
[error-prone-bugchecker]: https://github.com/google/error-prone/blob/master/check_api/src/main/java/com/google/errorprone/bugpatterns/BugChecker.java
[error-prone-fork-jitpack]: https://jitpack.io/#PicnicSupermarket/error-prone
[error-prone-fork-repo]: https://github.com/PicnicSupermarket/error-prone
[error-prone-gradle-installation-guide]: https://github.com/tbroyer/gradle-errorprone-plugin
[error-prone-installation-guide]: https://errorprone.info/docs/installation#maven
[error-prone-orig-repo]: https://github.com/google/error-prone
[error-prone-pull-3301]: https://github.com/google/error-prone/pull/3301

View File

@@ -1,87 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.8.1-SNAPSHOT</version>
</parent>
<artifactId>documentation-support</artifactId>
<name>Picnic :: Error Prone Support :: Documentation Support</name>
<description>Data extraction support for the purpose of documentation generation.</description>
<dependencies>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotation</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_check_api</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_test_helpers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.google.auto</groupId>
<artifactId>auto-common</artifactId>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,103 +0,0 @@
package tech.picnic.errorprone.documentation;
import static com.google.common.base.Verify.verify;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.Objects.requireNonNull;
import com.google.auto.common.AnnotationMirrors;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.util.Context;
import javax.lang.model.element.AnnotationValue;
import tech.picnic.errorprone.documentation.BugPatternExtractor.BugPatternDocumentation;
/**
* An {@link Extractor} that describes how to extract data from a {@code @BugPattern} annotation.
*/
@Immutable
final class BugPatternExtractor implements Extractor<BugPatternDocumentation> {
@Override
public BugPatternDocumentation extract(ClassTree tree, Context context) {
ClassSymbol symbol = ASTHelpers.getSymbol(tree);
BugPattern annotation = symbol.getAnnotation(BugPattern.class);
requireNonNull(annotation, "BugPattern annotation must be present");
return new AutoValue_BugPatternExtractor_BugPatternDocumentation(
symbol.getQualifiedName().toString(),
annotation.name().isEmpty() ? tree.getSimpleName().toString() : annotation.name(),
ImmutableList.copyOf(annotation.altNames()),
annotation.link(),
ImmutableList.copyOf(annotation.tags()),
annotation.summary(),
annotation.explanation(),
annotation.severity(),
annotation.disableable(),
annotation.documentSuppression() ? getSuppressionAnnotations(tree) : ImmutableList.of());
}
@Override
public boolean canExtract(ClassTree tree) {
return ASTHelpers.hasDirectAnnotationWithSimpleName(tree, BugPattern.class.getSimpleName());
}
/**
* Returns the fully-qualified class names of suppression annotations specified by the {@link
* BugPattern} annotation located on the given tree.
*
* @implNote This method cannot simply invoke {@link BugPattern#suppressionAnnotations()}, as that
* will yield an "Attempt to access Class objects for TypeMirrors" exception.
*/
private static ImmutableList<String> getSuppressionAnnotations(ClassTree tree) {
AnnotationTree annotationTree =
ASTHelpers.getAnnotationWithSimpleName(
ASTHelpers.getAnnotations(tree), BugPattern.class.getSimpleName());
requireNonNull(annotationTree, "BugPattern annotation must be present");
Attribute.Array types =
doCast(
AnnotationMirrors.getAnnotationValue(
ASTHelpers.getAnnotationMirror(annotationTree), "suppressionAnnotations"),
Attribute.Array.class);
return types.getValue().stream()
.map(v -> doCast(v, Attribute.Class.class).classType.toString())
.collect(toImmutableList());
}
@SuppressWarnings("unchecked")
private static <T extends AnnotationValue> T doCast(AnnotationValue value, Class<T> target) {
verify(target.isInstance(value), "Value '%s' is not of type '%s'", value, target);
return (T) value;
}
@AutoValue
abstract static class BugPatternDocumentation {
abstract String fullyQualifiedName();
abstract String name();
abstract ImmutableList<String> altNames();
abstract String link();
abstract ImmutableList<String> tags();
abstract String summary();
abstract String explanation();
abstract SeverityLevel severityLevel();
abstract boolean canDisable();
abstract ImmutableList<String> suppressionAnnotations();
}
}

View File

@@ -1,56 +0,0 @@
package tech.picnic.errorprone.documentation;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.auto.service.AutoService;
import com.google.common.annotations.VisibleForTesting;
import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;
import com.sun.tools.javac.api.BasicJavacTask;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A compiler {@link Plugin} that analyzes and extracts relevant information for documentation
* purposes from processed files.
*/
// XXX: Find a better name for this class; it doesn't generate documentation per se.
@AutoService(Plugin.class)
public final class DocumentationGenerator implements Plugin {
@VisibleForTesting static final String OUTPUT_DIRECTORY_FLAG = "-XoutputDirectory";
private static final Pattern OUTPUT_DIRECTORY_FLAG_PATTERN =
Pattern.compile(Pattern.quote(OUTPUT_DIRECTORY_FLAG) + "=(.*)");
/** Instantiates a new {@link DocumentationGenerator} instance. */
public DocumentationGenerator() {}
@Override
public String getName() {
return getClass().getSimpleName();
}
@Override
public void init(JavacTask javacTask, String... args) {
checkArgument(args.length == 1, "Precisely one path must be provided");
javacTask.addTaskListener(
new DocumentationGeneratorTaskListener(
((BasicJavacTask) javacTask).getContext(), getOutputPath(args[0])));
}
@VisibleForTesting
static Path getOutputPath(String pathArg) {
Matcher matcher = OUTPUT_DIRECTORY_FLAG_PATTERN.matcher(pathArg);
checkArgument(
matcher.matches(), "'%s' must be of the form '%s=<value>'", pathArg, OUTPUT_DIRECTORY_FLAG);
String path = matcher.group(1);
try {
return Path.of(path);
} catch (InvalidPathException e) {
throw new IllegalArgumentException(String.format("Invalid path '%s'", path), e);
}
}
}

View File

@@ -1,91 +0,0 @@
package tech.picnic.errorprone.documentation;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.source.tree.ClassTree;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskEvent.Kind;
import com.sun.source.util.TaskListener;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.util.Context;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.tools.JavaFileObject;
/**
* A {@link TaskListener} that identifies and extracts relevant content for documentation generation
* and writes it to disk.
*/
// XXX: Find a better name for this class; it doesn't generate documentation per se.
final class DocumentationGeneratorTaskListener implements TaskListener {
private static final ObjectMapper OBJECT_MAPPER =
new ObjectMapper().setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
private final Context context;
private final Path docsPath;
DocumentationGeneratorTaskListener(Context context, Path path) {
this.context = context;
this.docsPath = path;
}
@Override
public void started(TaskEvent taskEvent) {
if (taskEvent.getKind() == Kind.ANALYZE) {
createDocsDirectory();
}
}
@Override
public void finished(TaskEvent taskEvent) {
if (taskEvent.getKind() != Kind.ANALYZE) {
return;
}
ClassTree classTree = JavacTrees.instance(context).getTree(taskEvent.getTypeElement());
JavaFileObject sourceFile = taskEvent.getSourceFile();
if (classTree == null || sourceFile == null) {
return;
}
ExtractorType.findMatchingType(classTree)
.ifPresent(
extractorType ->
writeToFile(
extractorType.getIdentifier(),
getSimpleClassName(sourceFile.toUri()),
extractorType.getExtractor().extract(classTree, context)));
}
private void createDocsDirectory() {
try {
Files.createDirectories(docsPath);
} catch (IOException e) {
throw new IllegalStateException(
String.format("Error while creating directory with path '%s'", docsPath), e);
}
}
private <T> void writeToFile(String identifier, String className, T data) {
File file = docsPath.resolve(String.format("%s-%s.json", identifier, className)).toFile();
try (FileWriter fileWriter = new FileWriter(file, UTF_8)) {
OBJECT_MAPPER.writeValue(fileWriter, data);
} catch (IOException e) {
throw new UncheckedIOException(String.format("Cannot write to file '%s'", file.getPath()), e);
}
}
private static String getSimpleClassName(URI path) {
return Paths.get(path).getFileName().toString().replace(".java", "");
}
}

View File

@@ -1,33 +0,0 @@
package tech.picnic.errorprone.documentation;
import com.google.errorprone.annotations.Immutable;
import com.sun.source.tree.ClassTree;
import com.sun.tools.javac.util.Context;
/**
* Interface implemented by classes that define how to extract data of some type {@link T} from a
* given {@link ClassTree}.
*
* @param <T> The type of data that is extracted.
*/
@Immutable
interface Extractor<T> {
/**
* Extracts and returns an instance of {@link T} using the provided arguments.
*
* @param tree The {@link ClassTree} to analyze and from which to extract instances of {@link T}.
* @param context The {@link Context} in which the current compilation takes place.
* @return A non-null instance of {@link T}.
*/
// XXX: Drop `Context` parameter unless used.
T extract(ClassTree tree, Context context);
/**
* Tells whether this {@link Extractor} can extract documentation content from the given {@link
* ClassTree}.
*
* @param tree The {@link ClassTree} of interest.
* @return {@code true} iff data extraction is supported.
*/
boolean canExtract(ClassTree tree);
}

View File

@@ -1,35 +0,0 @@
package tech.picnic.errorprone.documentation;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.sun.source.tree.ClassTree;
import java.util.EnumSet;
import java.util.Optional;
/** An enumeration of {@link Extractor} types. */
enum ExtractorType {
BUG_PATTERN("bugpattern", new BugPatternExtractor());
private static final ImmutableSet<ExtractorType> TYPES =
Sets.immutableEnumSet(EnumSet.allOf(ExtractorType.class));
private final String identifier;
private final Extractor<?> extractor;
ExtractorType(String identifier, Extractor<?> extractor) {
this.identifier = identifier;
this.extractor = extractor;
}
String getIdentifier() {
return identifier;
}
Extractor<?> getExtractor() {
return extractor;
}
static Optional<ExtractorType> findMatchingType(ClassTree tree) {
return TYPES.stream().filter(type -> type.getExtractor().canExtract(tree)).findFirst();
}
}

View File

@@ -1,7 +0,0 @@
/**
* A Java compiler plugin that extracts data from compiled classes, in support of the Error Prone
* Support documentation.
*/
@com.google.errorprone.annotations.CheckReturnValue
@org.jspecify.annotations.NullMarked
package tech.picnic.errorprone.documentation;

View File

@@ -1,153 +0,0 @@
package tech.picnic.errorprone.documentation;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import com.google.common.io.Resources;
import com.google.errorprone.BugPattern;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.sun.source.tree.ClassTree;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
final class BugPatternExtractorTest {
@Test
void noBugPattern(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerWithoutAnnotation.java",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"public final class TestCheckerWithoutAnnotation extends BugChecker {}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void minimalBugPattern(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"MinimalBugChecker.java",
"package pkg;",
"",
"import com.google.errorprone.BugPattern;",
"import com.google.errorprone.BugPattern.SeverityLevel;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"@BugPattern(summary = \"MinimalBugChecker summary\", severity = SeverityLevel.ERROR)",
"public final class MinimalBugChecker extends BugChecker {}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-MinimalBugChecker.json",
"bugpattern-documentation-minimal.json");
}
@Test
void completeBugPattern(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"CompleteBugChecker.java",
"package pkg;",
"",
"import com.google.errorprone.BugPattern;",
"import com.google.errorprone.BugPattern.SeverityLevel;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import org.junit.jupiter.api.Test;",
"",
"@BugPattern(",
" name = \"OtherName\",",
" summary = \"CompleteBugChecker summary\",",
" linkType = BugPattern.LinkType.CUSTOM,",
" link = \"https://error-prone.picnic.tech\",",
" explanation = \"Example explanation\",",
" severity = SeverityLevel.SUGGESTION,",
" altNames = \"Check\",",
" tags = BugPattern.StandardTags.SIMPLIFICATION,",
" disableable = false,",
" suppressionAnnotations = {BugPattern.class, Test.class})",
"public final class CompleteBugChecker extends BugChecker {}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-CompleteBugChecker.json",
"bugpattern-documentation-complete.json");
}
@Test
void undocumentedSuppressionBugPattern(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"UndocumentedSuppressionBugPattern.java",
"package pkg;",
"",
"import com.google.errorprone.BugPattern;",
"import com.google.errorprone.BugPattern.SeverityLevel;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"@BugPattern(",
" summary = \"UndocumentedSuppressionBugPattern summary\",",
" severity = SeverityLevel.WARNING,",
" documentSuppression = false)",
"public final class UndocumentedSuppressionBugPattern extends BugChecker {}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-UndocumentedSuppressionBugPattern.json",
"bugpattern-documentation-undocumented-suppression.json");
}
@Test
void bugPatternAnnotationIsAbsent() {
CompilationTestHelper.newInstance(TestChecker.class, getClass())
.addSourceLines(
"TestChecker.java",
"import com.google.errorprone.bugpatterns.BugChecker;",
"",
"// BUG: Diagnostic contains: Can extract: false",
"public final class TestChecker extends BugChecker {}")
.doTest();
}
private static void verifyFileMatchesResource(
Path outputDirectory, String fileName, String resourceName) throws IOException {
assertThat(Files.readString(outputDirectory.resolve(fileName)))
.isEqualToIgnoringWhitespace(getResource(resourceName));
}
// XXX: Once we support only JDK 15+, drop this method in favour of including the resources as
// text blocks in this class. (This also requires renaming the `verifyFileMatchesResource`
// method.)
private static String getResource(String resourceName) throws IOException {
return Resources.toString(
Resources.getResource(BugPatternExtractorTest.class, resourceName), UTF_8);
}
/** A {@link BugChecker} that validates the {@link BugPatternExtractor}. */
@BugPattern(summary = "Validates `BugPatternExtractor` extraction", severity = ERROR)
public static final class TestChecker extends BugChecker implements ClassTreeMatcher {
private static final long serialVersionUID = 1L;
@Override
public Description matchClass(ClassTree tree, VisitorState state) {
BugPatternExtractor extractor = new BugPatternExtractor();
assertThatThrownBy(() -> extractor.extract(tree, state.context))
.isInstanceOf(NullPointerException.class)
.hasMessage("BugPattern annotation must be present");
return buildDescription(tree)
.setMessage(String.format("Can extract: %s", extractor.canExtract(tree)))
.build();
}
}
}

View File

@@ -1,45 +0,0 @@
package tech.picnic.errorprone.documentation;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.FileManagers;
import com.google.errorprone.FileObjects;
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.file.JavacFileManager;
import java.nio.file.Path;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
// XXX: Generalize and move this class so that it can also be used by `refaster-compiler`.
// XXX: Add support for this class to the `ErrorProneTestHelperSourceFormat` check.
public final class Compilation {
private Compilation() {}
public static void compileWithDocumentationGenerator(
Path outputDirectory, String fileName, String... lines) {
compileWithDocumentationGenerator(outputDirectory.toAbsolutePath().toString(), fileName, lines);
}
public static void compileWithDocumentationGenerator(
String outputDirectory, String fileName, String... lines) {
compile(
ImmutableList.of("-Xplugin:DocumentationGenerator -XoutputDirectory=" + outputDirectory),
FileObjects.forSourceLines(fileName, lines));
}
private static void compile(ImmutableList<String> options, JavaFileObject javaFileObject) {
JavacFileManager javacFileManager = FileManagers.testFileManager();
JavaCompiler compiler = JavacTool.create();
JavacTaskImpl task =
(JavacTaskImpl)
compiler.getTask(
null,
javacFileManager,
null,
options,
ImmutableList.of(),
ImmutableList.of(javaFileObject));
task.call();
}
}

View File

@@ -1,77 +0,0 @@
package tech.picnic.errorprone.documentation;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.nio.file.attribute.AclEntryPermission.ADD_SUBDIRECTORY;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.condition.OS.WINDOWS;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.AclEntry;
import java.nio.file.attribute.AclFileAttributeView;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.io.TempDir;
final class DocumentationGeneratorTaskListenerTest {
@EnabledOnOs(WINDOWS)
@Test
void readOnlyFileSystemWindows(@TempDir Path outputDirectory) throws IOException {
AclFileAttributeView view =
Files.getFileAttributeView(outputDirectory, AclFileAttributeView.class);
view.setAcl(
view.getAcl().stream()
.map(
entry ->
AclEntry.newBuilder(entry)
.setPermissions(
Sets.difference(entry.permissions(), ImmutableSet.of(ADD_SUBDIRECTORY)))
.build())
.collect(toImmutableList()));
readOnlyFileSystemFailsToWrite(outputDirectory.resolve("nonexistent"));
}
@DisabledOnOs(WINDOWS)
@Test
void readOnlyFileSystemNonWindows(@TempDir Path outputDirectory) {
assertThat(outputDirectory.toFile().setWritable(false))
.describedAs("Failed to make test directory unwritable")
.isTrue();
readOnlyFileSystemFailsToWrite(outputDirectory.resolve("nonexistent"));
}
private static void readOnlyFileSystemFailsToWrite(Path outputDirectory) {
assertThatThrownBy(
() ->
Compilation.compileWithDocumentationGenerator(
outputDirectory, "A.java", "class A {}"))
.hasRootCauseInstanceOf(FileSystemException.class)
.hasCauseInstanceOf(IllegalStateException.class)
.hasMessageEndingWith("Error while creating directory with path '%s'", outputDirectory);
}
@Test
void noClassNoOutput(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(outputDirectory, "A.java", "package pkg;");
assertThat(outputDirectory).isEmptyDirectory();
}
@Test
void excessArguments(@TempDir Path outputDirectory) {
assertThatThrownBy(
() ->
Compilation.compileWithDocumentationGenerator(
outputDirectory.toAbsolutePath() + " extra-arg", "A.java", "package pkg;"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Precisely one path must be provided");
}
}

View File

@@ -1,38 +0,0 @@
package tech.picnic.errorprone.documentation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static tech.picnic.errorprone.documentation.DocumentationGenerator.OUTPUT_DIRECTORY_FLAG;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
final class DocumentationGeneratorTest {
@ParameterizedTest
@ValueSource(strings = {"bar", "foo"})
void getOutputPath(String path) {
assertThat(DocumentationGenerator.getOutputPath(OUTPUT_DIRECTORY_FLAG + '=' + path))
.isEqualTo(Path.of(path));
}
@ParameterizedTest
@ValueSource(strings = {"", "-XoutputDirectory", "invalidOption=Test", "nothing"})
void getOutputPathWithInvalidArgument(String pathArg) {
assertThatThrownBy(() -> DocumentationGenerator.getOutputPath(pathArg))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("'%s' must be of the form '%s=<value>'", pathArg, OUTPUT_DIRECTORY_FLAG);
}
@Test
void getOutputPathWithInvalidPath() {
String basePath = "path-with-null-char-\0";
assertThatThrownBy(
() -> DocumentationGenerator.getOutputPath(OUTPUT_DIRECTORY_FLAG + '=' + basePath))
.isInstanceOf(IllegalArgumentException.class)
.hasCauseInstanceOf(InvalidPathException.class)
.hasMessageEndingWith("Invalid path '%s'", basePath);
}
}

View File

@@ -1,19 +0,0 @@
{
"fullyQualifiedName": "pkg.CompleteBugChecker",
"name": "OtherName",
"altNames": [
"Check"
],
"link": "https://error-prone.picnic.tech",
"tags": [
"Simplification"
],
"summary": "CompleteBugChecker summary",
"explanation": "Example explanation",
"severityLevel": "SUGGESTION",
"canDisable": false,
"suppressionAnnotations": [
"com.google.errorprone.BugPattern",
"org.junit.jupiter.api.Test"
]
}

View File

@@ -1,14 +0,0 @@
{
"fullyQualifiedName": "pkg.MinimalBugChecker",
"name": "MinimalBugChecker",
"altNames": [],
"link": "",
"tags": [],
"summary": "MinimalBugChecker summary",
"explanation": "",
"severityLevel": "ERROR",
"canDisable": true,
"suppressionAnnotations": [
"java.lang.SuppressWarnings"
]
}

View File

@@ -1,12 +0,0 @@
{
"fullyQualifiedName": "pkg.UndocumentedSuppressionBugPattern",
"name": "UndocumentedSuppressionBugPattern",
"altNames": [],
"link": "",
"tags": [],
"summary": "UndocumentedSuppressionBugPattern summary",
"explanation": "",
"severityLevel": "WARNING",
"canDisable": true,
"suppressionAnnotations": []
}

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.8.1-SNAPSHOT</version>
<version>0.7.1-SNAPSHOT</version>
</parent>
<artifactId>error-prone-contrib</artifactId>
@@ -39,14 +39,6 @@
<artifactId>error_prone_test_helpers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>documentation-support</artifactId>
<!-- This dependency is declared only as a hint to Maven that
compilation depends on it; see the `maven-compiler-plugin`'s
`annotationProcessorPaths` configuration below. -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-support</artifactId>
@@ -146,10 +138,6 @@
<artifactId>value-annotations</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
@@ -225,11 +213,6 @@
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths combine.children="append">
<path>
<groupId>${project.groupId}</groupId>
<artifactId>documentation-support</artifactId>
<version>${project.version}</version>
</path>
<path>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-compiler</artifactId>
@@ -243,7 +226,6 @@
</annotationProcessorPaths>
<compilerArgs combine.children="append">
<arg>-Xplugin:RefasterRuleCompiler</arg>
<arg>-Xplugin:DocumentationGenerator -XoutputDirectory=${project.build.directory}/docs</arg>
</compilerArgs>
</configuration>
</plugin>
@@ -260,5 +242,11 @@
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -9,6 +9,7 @@ import static com.google.errorprone.matchers.Matchers.hasModifier;
import static com.google.errorprone.matchers.Matchers.not;
import static java.util.function.Predicate.not;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import static tech.picnic.errorprone.bugpatterns.util.JavaKeywords.isValidIdentifier;
import static tech.picnic.errorprone.bugpatterns.util.MoreJUnitMatchers.SETUP_OR_TEARDOWN_METHOD;
import static tech.picnic.errorprone.bugpatterns.util.MoreJUnitMatchers.TEST_METHOD;
@@ -24,11 +25,15 @@ import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ImportTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Type;
import java.util.Optional;
import javax.lang.model.element.Modifier;
import tech.picnic.errorprone.bugpatterns.util.ConflictDetection;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
/** A {@link BugChecker} that flags non-canonical JUnit method declarations. */
// XXX: Consider introducing a class-level check that enforces that test classes:
@@ -85,7 +90,7 @@ public final class JUnitMethodDeclaration extends BugChecker implements MethodTr
tryCanonicalizeMethodName(symbol)
.ifPresent(
newName ->
ConflictDetection.findMethodRenameBlocker(symbol, newName, state)
findMethodRenameBlocker(symbol, newName, state)
.ifPresentOrElse(
blocker -> reportMethodRenameBlocker(tree, blocker, state),
() -> fixBuilder.merge(SuggestedFixes.renameMethod(tree, newName, state))));
@@ -101,7 +106,61 @@ public final class JUnitMethodDeclaration extends BugChecker implements MethodTr
.build());
}
private static Optional<String> tryCanonicalizeMethodName(MethodSymbol symbol) {
/**
* If applicable, returns a human-readable argument against assigning the given name to an
* existing method.
*
* <p>This method implements imperfect heuristics. Things it currently does not consider include
* the following:
*
* <ul>
* <li>Whether the rename would merely introduce a method overload, rather than clashing with an
* existing method declaration.
* <li>Whether the rename would cause a method in a superclass to be overridden.
* <li>Whether the rename would in fact clash with a static import. (It could be that a static
* import of the same name is only referenced from lexical scopes in which the method under
* consideration cannot be referenced directly.)
* </ul>
*/
private static Optional<String> findMethodRenameBlocker(
MethodSymbol method, String newName, VisitorState state) {
if (isExistingMethodName(method.owner.type, newName, state)) {
return Optional.of(
String.format(
"a method named `%s` is already defined in this class or a supertype", newName));
}
if (isSimpleNameStaticallyImported(newName, state)) {
return Optional.of(String.format("`%s` is already statically imported", newName));
}
if (!isValidIdentifier(newName)) {
return Optional.of(String.format("`%s` is not a valid identifier", newName));
}
return Optional.empty();
}
private static boolean isExistingMethodName(Type clazz, String name, VisitorState state) {
return ASTHelpers.matchingMethods(state.getName(name), method -> true, clazz, state.getTypes())
.findAny()
.isPresent();
}
private static boolean isSimpleNameStaticallyImported(String simpleName, VisitorState state) {
return state.getPath().getCompilationUnit().getImports().stream()
.filter(ImportTree::isStatic)
.map(ImportTree::getQualifiedIdentifier)
.map(tree -> getStaticImportSimpleName(tree, state))
.anyMatch(simpleName::contentEquals);
}
private static CharSequence getStaticImportSimpleName(Tree tree, VisitorState state) {
String source = SourceCode.treeToString(tree, state);
return source.subSequence(source.lastIndexOf('.') + 1, source.length());
}
private static Optional<String> tryCanonicalizeMethodName(Symbol symbol) {
return Optional.of(symbol.getQualifiedName().toString())
.filter(name -> name.startsWith(TEST_PREFIX))
.map(name -> name.substring(TEST_PREFIX.length()))

View File

@@ -1,75 +0,0 @@
package tech.picnic.errorprone.bugpatterns.util;
import static tech.picnic.errorprone.bugpatterns.util.JavaKeywords.isValidIdentifier;
import com.google.errorprone.VisitorState;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ImportTree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Type;
import java.util.Optional;
/** A set of helper methods for detecting conflicts that would be caused when applying fixes. */
public final class ConflictDetection {
private ConflictDetection() {}
/**
* If applicable, returns a human-readable argument against assigning the given name to an
* existing method.
*
* <p>This method implements imperfect heuristics. Things it currently does not consider include
* the following:
*
* <ul>
* <li>Whether the rename would merely introduce a method overload, rather than clashing with an
* existing method declaration in its class or a supertype.
* <li>Whether the rename would in fact clash with a static import. (It could be that a static
* import of the same name is only referenced from lexical scopes in which the method under
* consideration cannot be referenced directly.)
* </ul>
*
* @param method The method considered for renaming.
* @param newName The newly proposed name for the method.
* @param state The {@link VisitorState} to use when searching for blockers.
* @return A human-readable argument against assigning the proposed name to the given method, or
* {@link Optional#empty()} if no blocker was found.
*/
public static Optional<String> findMethodRenameBlocker(
MethodSymbol method, String newName, VisitorState state) {
if (isExistingMethodName(method.owner.type, newName, state)) {
return Optional.of(
String.format(
"a method named `%s` is already defined in this class or a supertype", newName));
}
if (isSimpleNameStaticallyImported(newName, state)) {
return Optional.of(String.format("`%s` is already statically imported", newName));
}
if (!isValidIdentifier(newName)) {
return Optional.of(String.format("`%s` is not a valid identifier", newName));
}
return Optional.empty();
}
private static boolean isExistingMethodName(Type clazz, String name, VisitorState state) {
return ASTHelpers.matchingMethods(state.getName(name), method -> true, clazz, state.getTypes())
.findAny()
.isPresent();
}
private static boolean isSimpleNameStaticallyImported(String simpleName, VisitorState state) {
return state.getPath().getCompilationUnit().getImports().stream()
.filter(ImportTree::isStatic)
.map(ImportTree::getQualifiedIdentifier)
.map(tree -> getStaticImportSimpleName(tree, state))
.anyMatch(simpleName::contentEquals);
}
private static CharSequence getStaticImportSimpleName(Tree tree, VisitorState state) {
String source = SourceCode.treeToString(tree, state);
return source.subSequence(source.lastIndexOf('.') + 1, source.length());
}
}

View File

@@ -1,29 +0,0 @@
package tech.picnic.errorprone.refasterrules;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import java.util.stream.Stream;
import org.jooq.Record;
import org.jooq.ResultQuery;
import tech.picnic.errorprone.refaster.annotation.Description;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
@OnlineDocumentation
final class JooqRules {
private JooqRules() {}
/** Prefer eagerly fetching data over (lazy) streaming to avoid potentially leaking resources. */
@Description(
"Prefer eagerly fetching data over (lazy) streaming to avoid potentially leaking resources.")
static final class ResultQueryFetchStream {
@BeforeTemplate
Stream<?> before(ResultQuery<? extends Record> resultQuery) {
return resultQuery.stream();
}
@AfterTemplate
Stream<?> after(ResultQuery<? extends Record> resultQuery) {
return resultQuery.fetch().stream();
}
}
}

View File

@@ -369,8 +369,7 @@ final class ReactorRules {
static final class MonoIdentity<T> {
@BeforeTemplate
Mono<T> before(Mono<T> mono) {
return Refaster.anyOf(
mono.switchIfEmpty(Mono.empty()), mono.flux().next(), mono.flux().singleOrEmpty());
return mono.switchIfEmpty(Mono.empty());
}
@BeforeTemplate
@@ -474,11 +473,6 @@ final class ReactorRules {
* Flux}.
*/
abstract static class MonoFlatMapToFlux<T, S> {
// XXX: It would be more expressive if this `@Placeholder` were replaced with a `Function<?
// super T, ? extends Mono<? extends S>>` parameter, so that compatible non-lambda expression
// arguments to `flatMapMany` are also matched. However, the type inferred for lambda and method
// reference expressions passed to `flatMapMany` appears to always be `Function<T, Publisher<?
// extends S>>`, which doesn't match. Find a solution.
@Placeholder(allowsIdentity = true)
abstract Mono<S> transformation(@MayOptionallyUse T value);

View File

@@ -3,16 +3,21 @@ package tech.picnic.errorprone.bugpatterns;
import static com.google.common.base.Predicates.containsPattern;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class AmbiguousJsonCreatorTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(AmbiguousJsonCreator.class, getClass())
.expectErrorMessage(
"X",
containsPattern("`JsonCreator.Mode` should be set for single-argument creators"));
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(AmbiguousJsonCreator.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(AmbiguousJsonCreator.class, getClass())
.expectErrorMessage(
"X", containsPattern("`JsonCreator.Mode` should be set for single-argument creators"))
compilationTestHelper
.addSourceLines(
"Container.java",
"import com.fasterxml.jackson.annotation.JsonCreator;",
@@ -113,7 +118,7 @@ final class AmbiguousJsonCreatorTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(AmbiguousJsonCreator.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import com.fasterxml.jackson.annotation.JsonCreator;",
@@ -138,6 +143,6 @@ final class AmbiguousJsonCreatorTest {
" return FOO;",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
.doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH);
}
}

View File

@@ -7,9 +7,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class AssertJIsNullTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(AssertJIsNull.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(AssertJIsNull.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(AssertJIsNull.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static org.assertj.core.api.Assertions.assertThat;",
@@ -33,7 +38,7 @@ final class AssertJIsNullTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(AssertJIsNull.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static org.assertj.core.api.Assertions.assertThat;",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class AutowiredConstructorTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(AutowiredConstructor.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(AutowiredConstructor.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(AutowiredConstructor.class, getClass())
compilationTestHelper
.addSourceLines(
"Container.java",
"import com.google.errorprone.annotations.Immutable;",
@@ -66,7 +71,7 @@ final class AutowiredConstructorTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(AutowiredConstructor.class, getClass())
refactoringTestHelper
.addInputLines(
"Container.java",
"import org.springframework.beans.factory.annotation.Autowired;",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class CanonicalAnnotationSyntaxTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(CanonicalAnnotationSyntax.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(CanonicalAnnotationSyntax.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(CanonicalAnnotationSyntax.class, getClass())
compilationTestHelper
.addSourceLines(
"pkg/A.java",
"package pkg;",
@@ -128,7 +133,7 @@ final class CanonicalAnnotationSyntaxTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(CanonicalAnnotationSyntax.class, getClass())
refactoringTestHelper
.addInputLines(
"pkg/A.java",
"package pkg;",

View File

@@ -8,9 +8,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class CollectorMutabilityTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(CollectorMutability.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(CollectorMutability.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(CollectorMutability.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static com.google.common.collect.ImmutableList.toImmutableList;",
@@ -62,7 +67,7 @@ final class CollectorMutabilityTest {
@Test
void identificationWithoutGuavaOnClasspath() {
CompilationTestHelper.newInstance(CollectorMutability.class, getClass())
compilationTestHelper
.withClasspath()
.addSourceLines(
"A.java",
@@ -79,7 +84,7 @@ final class CollectorMutabilityTest {
@Test
void replacementFirstSuggestedFix() {
BugCheckerRefactoringTestHelper.newInstance(CollectorMutability.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static java.util.stream.Collectors.toList;",
@@ -136,7 +141,7 @@ final class CollectorMutabilityTest {
@Test
void replacementSecondSuggestedFix() {
BugCheckerRefactoringTestHelper.newInstance(CollectorMutability.class, getClass())
refactoringTestHelper
.setFixChooser(SECOND)
.addInputLines(
"A.java",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class EmptyMethodTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(EmptyMethod.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(EmptyMethod.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"class A {",
@@ -64,7 +69,7 @@ final class EmptyMethodTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(EmptyMethod.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"final class A {",

View File

@@ -6,9 +6,15 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class ErrorProneTestHelperSourceFormatTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(ErrorProneTestHelperSourceFormat.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(
ErrorProneTestHelperSourceFormat.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(ErrorProneTestHelperSourceFormat.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
@@ -57,7 +63,7 @@ final class ErrorProneTestHelperSourceFormatTest {
* Verifies that import sorting and code formatting is performed unconditionally, while unused
* imports are removed unless part of a `BugCheckerRefactoringTestHelper` expected output file.
*/
BugCheckerRefactoringTestHelper.newInstance(ErrorProneTestHelperSourceFormat.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",

View File

@@ -4,9 +4,12 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class ExplicitEnumOrderingTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(ExplicitEnumOrdering.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(ExplicitEnumOrdering.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static java.lang.annotation.RetentionPolicy.CLASS;",

View File

@@ -1,5 +1,7 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugCheckerRefactoringTestHelper.newInstance;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
@@ -7,9 +9,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class FluxFlatMapUsageTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(FluxFlatMapUsage.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
newInstance(FluxFlatMapUsage.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(FluxFlatMapUsage.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.function.BiFunction;",
@@ -66,7 +73,7 @@ final class FluxFlatMapUsageTest {
@Test
void replacementFirstSuggestedFix() {
BugCheckerRefactoringTestHelper.newInstance(FluxFlatMapUsage.class, getClass())
refactoringTestHelper
.setFixChooser(FixChoosers.FIRST)
.addInputLines(
"A.java",
@@ -101,7 +108,7 @@ final class FluxFlatMapUsageTest {
@Test
void replacementSecondSuggestedFix() {
BugCheckerRefactoringTestHelper.newInstance(FluxFlatMapUsage.class, getClass())
refactoringTestHelper
.setFixChooser(FixChoosers.SECOND)
.addInputLines(
"A.java",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class FormatStringConcatenationTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(FormatStringConcatenation.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(FormatStringConcatenation.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(FormatStringConcatenation.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static com.google.common.base.Preconditions.checkArgument;",
@@ -304,7 +309,7 @@ final class FormatStringConcatenationTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(FormatStringConcatenation.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static com.google.common.base.Preconditions.checkArgument;",

View File

@@ -7,9 +7,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class IdentityConversionTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(IdentityConversion.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(IdentityConversion.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(IdentityConversion.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static com.google.errorprone.matchers.Matchers.instanceMethod;",
@@ -179,7 +184,7 @@ final class IdentityConversionTest {
@Test
void replacementFirstSuggestedFix() {
BugCheckerRefactoringTestHelper.newInstance(IdentityConversion.class, getClass())
refactoringTestHelper
.setFixChooser(FixChoosers.FIRST)
.addInputLines(
"A.java",
@@ -286,7 +291,7 @@ final class IdentityConversionTest {
@Test
void replacementSecondSuggestedFix() {
BugCheckerRefactoringTestHelper.newInstance(IdentityConversion.class, getClass())
refactoringTestHelper
.setFixChooser(FixChoosers.SECOND)
.addInputLines(
"A.java",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class ImmutablesSortedSetComparatorTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(ImmutablesSortedSetComparator.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(ImmutablesSortedSetComparator.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(ImmutablesSortedSetComparator.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import com.google.common.collect.ContiguousSet;",
@@ -104,7 +109,7 @@ final class ImmutablesSortedSetComparatorTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(ImmutablesSortedSetComparator.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import com.google.common.collect.ImmutableSortedSet;",
@@ -142,7 +147,7 @@ final class ImmutablesSortedSetComparatorTest {
@Test
void replacementWithImportClash() {
BugCheckerRefactoringTestHelper.newInstance(ImmutablesSortedSetComparator.class, getClass())
refactoringTestHelper
.addInputLines(
"MySpringService.java",
"import com.google.common.collect.ImmutableSortedSet;",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class IsInstanceLambdaUsageTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(IsInstanceLambdaUsage.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(IsInstanceLambdaUsage.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(IsInstanceLambdaUsage.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.stream.Stream;",
@@ -41,7 +46,7 @@ final class IsInstanceLambdaUsageTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(IsInstanceLambdaUsage.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import java.util.stream.Stream;",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class JUnitClassModifiersTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(JUnitClassModifiers.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(JUnitClassModifiers.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(JUnitClassModifiers.class, getClass())
compilationTestHelper
.addSourceLines(
"Container.java",
"import org.junit.jupiter.api.Test;",
@@ -93,7 +98,7 @@ final class JUnitClassModifiersTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(JUnitClassModifiers.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import org.junit.jupiter.api.Test;",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class JUnitMethodDeclarationTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(JUnitMethodDeclaration.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(JUnitMethodDeclaration.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(JUnitMethodDeclaration.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
@@ -340,7 +345,7 @@ final class JUnitMethodDeclarationTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(JUnitMethodDeclaration.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static org.junit.jupiter.params.provider.Arguments.arguments;",

View File

@@ -7,9 +7,22 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class LexicographicalAnnotationAttributeListingTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(
LexicographicalAnnotationAttributeListing.class, getClass());
private final CompilationTestHelper restrictedCompilationTestHelper =
CompilationTestHelper.newInstance(LexicographicalAnnotationAttributeListing.class, getClass())
.setArgs(
ImmutableList.of(
"-XepOpt:LexicographicalAnnotationAttributeListing:Includes=pkg.A.Foo,pkg.A.Bar",
"-XepOpt:LexicographicalAnnotationAttributeListing:Excludes=pkg.A.Bar#value"));
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(
LexicographicalAnnotationAttributeListing.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(LexicographicalAnnotationAttributeListing.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static java.math.RoundingMode.DOWN;",
@@ -152,8 +165,7 @@ final class LexicographicalAnnotationAttributeListingTest {
// `CanonicalAnnotationSyntax` checker correct the situation in a subsequent run.
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(
LexicographicalAnnotationAttributeListing.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static java.math.RoundingMode.DOWN;",
@@ -234,11 +246,7 @@ final class LexicographicalAnnotationAttributeListingTest {
@Test
void filtering() {
/* Some violations are not flagged because they are not in- or excluded. */
CompilationTestHelper.newInstance(LexicographicalAnnotationAttributeListing.class, getClass())
.setArgs(
ImmutableList.of(
"-XepOpt:LexicographicalAnnotationAttributeListing:Includes=pkg.A.Foo,pkg.A.Bar",
"-XepOpt:LexicographicalAnnotationAttributeListing:Excludes=pkg.A.Bar#value"))
restrictedCompilationTestHelper
.addSourceLines(
"pkg/A.java",
"package pkg;",

View File

@@ -6,9 +6,15 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class LexicographicalAnnotationListingTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(LexicographicalAnnotationListing.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(
LexicographicalAnnotationListing.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(LexicographicalAnnotationListing.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.lang.annotation.ElementType;",
@@ -127,7 +133,7 @@ final class LexicographicalAnnotationListingTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(LexicographicalAnnotationListing.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import java.lang.annotation.ElementType;",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class MethodReferenceUsageTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(MethodReferenceUsage.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(MethodReferenceUsage.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(MethodReferenceUsage.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import com.google.common.collect.Streams;",
@@ -318,7 +323,7 @@ final class MethodReferenceUsageTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(MethodReferenceUsage.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static java.util.Collections.emptyList;",

View File

@@ -6,12 +6,16 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class MissingRefasterAnnotationTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(MissingRefasterAnnotation.class, getClass())
.expectErrorMessage(
"X",
containsPattern(
"The Refaster rule contains a method without any Refaster annotations"));
@Test
void identification() {
CompilationTestHelper.newInstance(MissingRefasterAnnotation.class, getClass())
.expectErrorMessage(
"X",
containsPattern("The Refaster rule contains a method without any Refaster annotations"))
compilationTestHelper
.addSourceLines(
"A.java",
"import com.google.errorprone.refaster.annotation.AfterTemplate;",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class MockitoStubbingTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(MockitoStubbing.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(MockitoStubbing.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(MockitoStubbing.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static org.mockito.ArgumentMatchers.eq;",
@@ -50,7 +55,7 @@ final class MockitoStubbingTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(MockitoStubbing.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static org.mockito.ArgumentMatchers.eq;",

View File

@@ -4,9 +4,12 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class NestedOptionalsTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(NestedOptionals.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(NestedOptionals.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Optional;",
@@ -39,7 +42,7 @@ final class NestedOptionalsTest {
@Test
void identificationOptionalTypeNotLoaded() {
CompilationTestHelper.newInstance(NestedOptionals.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.time.Duration;",

View File

@@ -7,9 +7,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class NonEmptyMonoTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(NonEmptyMono.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(NonEmptyMono.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(NonEmptyMono.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static com.google.common.collect.ImmutableList.toImmutableList;",
@@ -106,7 +111,7 @@ final class NonEmptyMonoTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(NonEmptyMono.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static com.google.common.collect.ImmutableList.toImmutableList;",

View File

@@ -5,21 +5,22 @@ import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
// XXX: There are no tests for multiple replacements within the same expression:
// - Error Prone doesn't currently support this, it seems.
// - The `BugCheckerRefactoringTestHelper` throws an exception in this case.
// - During actual compilation only the first replacement is applied.
// XXX: Can we perhaps work-around this by describing the fixes in reverse order?
final class PrimitiveComparisonTest {
/**
* Tests the identification of {@code byte} comparisons for which boxing can be avoided.
*
* @implNote The logic for {@code char} and {@code short} is exactly analogous to the {@code byte}
* case.
*/
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(PrimitiveComparison.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(PrimitiveComparison.class, getClass());
// XXX: There are no tests for multiple replacements within the same expression:
// - Error Prone doesn't currently support this, it seems.
// - The `BugCheckerRefactoringTestHelper` throws an exception in this case.
// - During actual compilation only the first replacement is applied.
// XXX: Can we perhaps work-around this by describing the fixes in reverse order?
// The logic for `char` and `short` is exactly analogous to the `byte` case.
@Test
void byteComparison() {
CompilationTestHelper.newInstance(PrimitiveComparison.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
@@ -119,7 +120,7 @@ final class PrimitiveComparisonTest {
@Test
void intComparison() {
CompilationTestHelper.newInstance(PrimitiveComparison.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
@@ -226,7 +227,7 @@ final class PrimitiveComparisonTest {
@Test
void longComparison() {
CompilationTestHelper.newInstance(PrimitiveComparison.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
@@ -317,7 +318,7 @@ final class PrimitiveComparisonTest {
@Test
void floatComparison() {
CompilationTestHelper.newInstance(PrimitiveComparison.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
@@ -385,7 +386,7 @@ final class PrimitiveComparisonTest {
@Test
void doubleComparison() {
CompilationTestHelper.newInstance(PrimitiveComparison.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
@@ -460,7 +461,7 @@ final class PrimitiveComparisonTest {
@Test
void stringComparison() {
CompilationTestHelper.newInstance(PrimitiveComparison.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
@@ -496,7 +497,7 @@ final class PrimitiveComparisonTest {
@Test
void replacementWithPrimitiveVariants() {
BugCheckerRefactoringTestHelper.newInstance(PrimitiveComparison.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import java.util.Comparator;",
@@ -576,7 +577,7 @@ final class PrimitiveComparisonTest {
@Test
void replacementWithBoxedVariants() {
BugCheckerRefactoringTestHelper.newInstance(PrimitiveComparison.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import java.util.Comparator;",
@@ -642,7 +643,7 @@ final class PrimitiveComparisonTest {
@Test
void replacementWithPrimitiveVariantsUsingStaticImports() {
BugCheckerRefactoringTestHelper.newInstance(PrimitiveComparison.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static java.util.Comparator.comparing;",
@@ -681,7 +682,7 @@ final class PrimitiveComparisonTest {
@Test
void replacementWithBoxedVariantsUsingStaticImports() {
BugCheckerRefactoringTestHelper.newInstance(PrimitiveComparison.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static java.util.Comparator.comparingDouble;",
@@ -722,7 +723,7 @@ final class PrimitiveComparisonTest {
@Test
void replacementWithPrimitiveVariantsInComplexSyntacticalContext() {
BugCheckerRefactoringTestHelper.newInstance(PrimitiveComparison.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import java.util.Comparator;",
@@ -754,7 +755,7 @@ final class PrimitiveComparisonTest {
@Test
void replacementWithBoxedVariantsInComplexSyntacticalContext() {
BugCheckerRefactoringTestHelper.newInstance(PrimitiveComparison.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import java.util.Comparator;",

View File

@@ -12,6 +12,13 @@ import org.junit.jupiter.api.Test;
final class RedundantStringConversionTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass());
private final CompilationTestHelper customizedCompilationTestHelper =
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.setArgs(
ImmutableList.of(
"-XepOpt:RedundantStringConversion:ExtraConversionMethods=java.lang.Enum#name(),A#name(),A.B#toString(int)"));
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(RedundantStringConversion.class, getClass());
@Test
void identificationOfIdentityTransformation() {
@@ -409,10 +416,7 @@ final class RedundantStringConversionTest {
@Test
void identificationOfCustomConversionMethod() {
CompilationTestHelper.newInstance(RedundantStringConversion.class, getClass())
.setArgs(
ImmutableList.of(
"-XepOpt:RedundantStringConversion:ExtraConversionMethods=java.lang.Enum#name(),A#name(),A.B#toString(int)"))
customizedCompilationTestHelper
.addSourceLines(
"A.java",
"import java.math.RoundingMode;",
@@ -505,7 +509,7 @@ final class RedundantStringConversionTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(RedundantStringConversion.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"class A {",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class RefasterAnyOfUsageTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(RefasterAnyOfUsage.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(RefasterAnyOfUsage.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(RefasterAnyOfUsage.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import com.google.errorprone.refaster.Refaster;",
@@ -33,7 +38,7 @@ final class RefasterAnyOfUsageTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(RefasterAnyOfUsage.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import com.google.errorprone.refaster.Refaster;",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class RefasterRuleModifiersTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(RefasterRuleModifiers.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(RefasterRuleModifiers.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(RefasterRuleModifiers.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",
@@ -131,7 +136,7 @@ final class RefasterRuleModifiersTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(RefasterRuleModifiers.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import com.google.errorprone.refaster.annotation.BeforeTemplate;",

View File

@@ -4,9 +4,12 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class RequestMappingAnnotationTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(RequestMappingAnnotation.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(RequestMappingAnnotation.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import java.io.InputStream;",

View File

@@ -4,9 +4,16 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class RequestParamTypeTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(RequestParamType.class, getClass());
private final CompilationTestHelper restrictedCompilationTestHelper =
CompilationTestHelper.newInstance(RequestParamType.class, getClass())
.setArgs(
"-XepOpt:RequestParamType:SupportedCustomTypes=com.google.common.collect.ImmutableSet,com.google.common.collect.ImmutableSortedMultiset");
@Test
void identification() {
CompilationTestHelper.newInstance(RequestParamType.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableBiMap;",
@@ -63,9 +70,7 @@ final class RequestParamTypeTest {
@Test
void identificationRestricted() {
CompilationTestHelper.newInstance(RequestParamType.class, getClass())
.setArgs(
"-XepOpt:RequestParamType:SupportedCustomTypes=com.google.common.collect.ImmutableSet,com.google.common.collect.ImmutableSortedMultiset")
restrictedCompilationTestHelper
.addSourceLines(
"A.java",
"import com.google.common.collect.ImmutableBiMap;",

View File

@@ -7,9 +7,14 @@ import org.junit.jupiter.api.Test;
import org.springframework.scheduling.annotation.Scheduled;
final class ScheduledTransactionTraceTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(ScheduledTransactionTrace.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(ScheduledTransactionTrace.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(ScheduledTransactionTrace.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import com.newrelic.api.agent.Trace;",
@@ -41,7 +46,7 @@ final class ScheduledTransactionTraceTest {
@Test
void identificationWithoutNewRelicAgentApiOnClasspath() {
CompilationTestHelper.newInstance(ScheduledTransactionTrace.class, getClass())
compilationTestHelper
.withClasspath(Scheduled.class)
.addSourceLines(
"A.java",
@@ -56,7 +61,7 @@ final class ScheduledTransactionTraceTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(ScheduledTransactionTrace.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import com.newrelic.api.agent.Trace;",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class Slf4jLogStatementTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(Slf4jLogStatement.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(Slf4jLogStatement.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(Slf4jLogStatement.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import org.slf4j.Logger;",
@@ -91,7 +96,7 @@ final class Slf4jLogStatementTest {
// XXX: Drop what's unused.
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(Slf4jLogStatement.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import org.slf4j.Logger;",

View File

@@ -6,9 +6,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class SpringMvcAnnotationTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(SpringMvcAnnotation.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(SpringMvcAnnotation.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(SpringMvcAnnotation.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static org.springframework.web.bind.annotation.RequestMethod.DELETE;",
@@ -80,7 +85,7 @@ final class SpringMvcAnnotationTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(SpringMvcAnnotation.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static org.springframework.web.bind.annotation.RequestMethod.PATCH;",

View File

@@ -8,6 +8,11 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class StaticImportTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(StaticImport.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(StaticImport.class, getClass());
@Test
void candidateMethodsAreNotRedundant() {
assertThat(StaticImport.STATIC_IMPORT_CANDIDATE_MEMBERS.keySet())
@@ -28,7 +33,7 @@ final class StaticImportTest {
@Test
void identification() {
CompilationTestHelper.newInstance(StaticImport.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static com.google.common.collect.ImmutableMap.toImmutableMap;",
@@ -113,7 +118,7 @@ final class StaticImportTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(StaticImport.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"import static java.util.function.Predicate.not;",

View File

@@ -1,5 +1,7 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.BugCheckerRefactoringTestHelper.newInstance;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
@@ -7,9 +9,14 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class StringCaseLocaleUsageTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(StringCaseLocaleUsage.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
newInstance(StringCaseLocaleUsage.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(StringCaseLocaleUsage.class, getClass())
compilationTestHelper
.addSourceLines(
"A.java",
"import static java.util.Locale.ROOT;",
@@ -47,7 +54,7 @@ final class StringCaseLocaleUsageTest {
@Test
void replacementFirstSuggestedFix() {
BugCheckerRefactoringTestHelper.newInstance(StringCaseLocaleUsage.class, getClass())
refactoringTestHelper
.setFixChooser(FixChoosers.FIRST)
.addInputLines(
"A.java",
@@ -86,7 +93,7 @@ final class StringCaseLocaleUsageTest {
@Test
void replacementSecondSuggestedFix() {
BugCheckerRefactoringTestHelper.newInstance(StringCaseLocaleUsage.class, getClass())
refactoringTestHelper
.setFixChooser(FixChoosers.SECOND)
.addInputLines(
"A.java",

View File

@@ -8,12 +8,17 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class StringJoinTest {
private final CompilationTestHelper compilationHelper =
CompilationTestHelper.newInstance(StringJoin.class, getClass())
.expectErrorMessage(
"valueOf", containsPattern("Prefer `String#valueOf` over `String#format`"))
.expectErrorMessage("join", containsPattern("Prefer `String#join` over `String#format`"));
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(StringJoin.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(StringJoin.class, getClass())
.expectErrorMessage(
"valueOf", containsPattern("Prefer `String#valueOf` over `String#format`"))
.expectErrorMessage("join", containsPattern("Prefer `String#join` over `String#format`"))
compilationHelper
.addSourceLines(
"A.java",
"import java.util.Formattable;",
@@ -58,7 +63,7 @@ final class StringJoinTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(StringJoin.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"class A {",

View File

@@ -4,9 +4,12 @@ import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class TimeZoneUsageTest {
private final CompilationTestHelper compilationHelper =
CompilationTestHelper.newInstance(TimeZoneUsage.class, getClass());
@Test
void identification() {
CompilationTestHelper.newInstance(TimeZoneUsage.class, getClass())
compilationHelper
.addSourceLines(
"A.java",
"import static java.time.ZoneOffset.UTC;",

View File

@@ -1,73 +0,0 @@
package tech.picnic.errorprone.bugpatterns.util;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import com.google.errorprone.BugPattern;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.MethodTree;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import org.junit.jupiter.api.Test;
final class ConflictDetectionTest {
@Test
void matcher() {
CompilationTestHelper.newInstance(RenameBlockerFlagger.class, getClass())
.addSourceLines(
"pkg/A.java",
"package pkg;",
"",
"import static pkg.A.B.method3t;",
"",
"import pkg.A.method4t;",
"",
"class A {",
" void method1() {",
" method3t();",
" method4(method4t.class);",
" }",
"",
" // BUG: Diagnostic contains: a method named `method2t` is already defined in this class or a",
" // supertype",
" void method2() {}",
"",
" void method2t() {}",
"",
" // BUG: Diagnostic contains: `method3t` is already statically imported",
" void method3() {}",
"",
" void method4(Object o) {}",
"",
" // BUG: Diagnostic contains: `int` is not a valid identifier",
" void in() {}",
"",
" static class B {",
" static void method3t() {}",
" }",
"",
" class method4t {}",
"}")
.doTest();
}
/**
* A {@link BugChecker} that uses {@link ConflictDetection#findMethodRenameBlocker(MethodSymbol,
* String, VisitorState)} to flag methods of which the name cannot be suffixed with a {@code t}.
*/
@BugPattern(summary = "Interacts with `ConflictDetection` for testing purposes", severity = ERROR)
public static final class RenameBlockerFlagger extends BugChecker implements MethodTreeMatcher {
private static final long serialVersionUID = 1L;
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
return ConflictDetection.findMethodRenameBlocker(
ASTHelpers.getSymbol(tree), tree.getName() + "t", state)
.map(blocker -> buildDescription(tree).setMessage(blocker).build())
.orElse(Description.NO_MATCH);
}
}
}

View File

@@ -40,6 +40,9 @@ final class MethodMatcherFactoryTest {
"com.example.A#m2(java.lang.String)",
"com.example.sub.B#m3(int,int)"));
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(MatchedMethodsFlagger.class, getClass());
@Test
void createWithMalformedSignatures() {
MethodMatcherFactory factory = new MethodMatcherFactory();
@@ -55,7 +58,7 @@ final class MethodMatcherFactoryTest {
@Test
void matcher() {
CompilationTestHelper.newInstance(MatchedMethodsFlagger.class, getClass())
compilationTestHelper
.addSourceLines(
"com/example/A.java",
"package com.example;",

View File

@@ -49,7 +49,6 @@ final class RefasterRulesTest {
ImmutableSortedMultisetRules.class,
ImmutableSortedSetRules.class,
IntStreamRules.class,
JooqRules.class,
JUnitRules.class,
JUnitToAssertJRules.class,
LongStreamRules.class,

View File

@@ -1,11 +0,0 @@
package tech.picnic.errorprone.refasterrules;
import java.util.stream.Stream;
import org.jooq.impl.DSL;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class JooqRulesTest implements RefasterRuleCollectionTestCase {
Stream<?> testResultQueryFetchStream() {
return DSL.select().stream();
}
}

View File

@@ -1,11 +0,0 @@
package tech.picnic.errorprone.refasterrules;
import java.util.stream.Stream;
import org.jooq.impl.DSL;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class JooqRulesTest implements RefasterRuleCollectionTestCase {
Stream<?> testResultQueryFetchStream() {
return DSL.select().fetch().stream();
}
}

View File

@@ -116,11 +116,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
ImmutableSet<Mono<?>> testMonoIdentity() {
return ImmutableSet.of(
Mono.just(1).switchIfEmpty(Mono.empty()),
Mono.just(2).flux().next(),
Mono.just(3).flux().singleOrEmpty(),
Mono.<Void>empty().then());
return ImmutableSet.of(Mono.just(1).switchIfEmpty(Mono.empty()), Mono.<Void>empty().then());
}
ImmutableSet<Flux<Integer>> testFluxSwitchIfEmptyOfEmptyPublisher() {

View File

@@ -121,7 +121,7 @@ final class ReactorRulesTest implements RefasterRuleCollectionTestCase {
}
ImmutableSet<Mono<?>> testMonoIdentity() {
return ImmutableSet.of(Mono.just(1), Mono.just(2), Mono.just(3), Mono.<Void>empty());
return ImmutableSet.of(Mono.just(1), Mono.<Void>empty());
}
ImmutableSet<Flux<Integer>> testFluxSwitchIfEmptyOfEmptyPublisher() {

View File

@@ -24,7 +24,7 @@ final class TestNGToAssertJRulesTest implements RefasterRuleCollectionTestCase {
public ImmutableSet<?> elidedTypesAndStaticImports() {
return ImmutableSet.of(
(Runnable) () -> assertEquals(new byte[0], null),
(Runnable) () -> assertEqualsNoOrder((Object[]) null, null),
(Runnable) () -> assertEqualsNoOrder(null, null),
(Runnable) () -> assertFalse(true),
(Runnable) () -> assertNotEquals(new byte[0], null),
(Runnable) () -> assertNotNull(null),

View File

@@ -29,7 +29,7 @@ final class TestNGToAssertJRulesTest implements RefasterRuleCollectionTestCase {
public ImmutableSet<?> elidedTypesAndStaticImports() {
return ImmutableSet.of(
(Runnable) () -> assertEquals(new byte[0], null),
(Runnable) () -> assertEqualsNoOrder((Object[]) null, null),
(Runnable) () -> assertEqualsNoOrder(null, null),
(Runnable) () -> assertFalse(true),
(Runnable) () -> assertNotEquals(new byte[0], null),
(Runnable) () -> assertNotNull(null),

172
pom.xml
View File

@@ -4,7 +4,7 @@
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.8.1-SNAPSHOT</version>
<version>0.7.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Picnic :: Error Prone Support</name>
@@ -39,7 +39,6 @@
</developers>
<modules>
<module>documentation-support</module>
<module>error-prone-contrib</module>
<module>refaster-compiler</module>
<module>refaster-runner</module>
@@ -150,15 +149,15 @@
<version.auto-value>1.10.1</version.auto-value>
<version.error-prone>${version.error-prone-orig}</version.error-prone>
<version.error-prone-fork>v${version.error-prone-orig}-picnic-1</version.error-prone-fork>
<version.error-prone-orig>2.18.0</version.error-prone-orig>
<version.error-prone-slf4j>0.1.18</version.error-prone-slf4j>
<version.error-prone-orig>2.17.0</version.error-prone-orig>
<version.error-prone-slf4j>0.1.17</version.error-prone-slf4j>
<version.guava-beta-checker>1.0</version.guava-beta-checker>
<version.jdk>11</version.jdk>
<version.maven>3.8.7</version.maven>
<version.mockito>5.1.1</version.mockito>
<version.maven>3.8.6</version.maven>
<version.mockito>4.11.0</version.mockito>
<version.nopen-checker>1.0.1</version.nopen-checker>
<version.nullaway>0.10.9</version.nullaway>
<version.pitest-git>1.0.4</version.pitest-git>
<version.nullaway>0.10.7</version.nullaway>
<version.pitest-git>1.0.3</version.pitest-git>
<version.surefire>2.22.2</version.surefire>
</properties>
@@ -189,11 +188,6 @@
<artifactId>error_prone_test_helpers</artifactId>
<version>${version.error-prone}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>documentation-support</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>refaster-compiler</artifactId>
@@ -217,7 +211,7 @@
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.14.2</version>
<version>2.14.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -275,7 +269,7 @@
<dependency>
<groupId>com.newrelic.agent.java</groupId>
<artifactId>newrelic-api</artifactId>
<version>8.0.0</version>
<version>7.11.1</version>
</dependency>
<!-- Specified as a workaround for
https://github.com/mojohaus/versions-maven-plugin/issues/244. -->
@@ -287,7 +281,7 @@
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>2022.0.2</version>
<version>2022.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -304,7 +298,7 @@
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.2.8</version>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
@@ -328,11 +322,6 @@
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.13.0</version>
</dependency>
<!-- Specified so that Renovate will file Maven upgrade PRs, which
subsequently will cause `maven-enforcer-plugin` to require that
developers build the project using the latest Maven release. -->
@@ -349,14 +338,14 @@
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-bom</artifactId>
<version>3.24.2</version>
<version>3.24.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>3.30.0</version>
<version>3.29.0</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
@@ -368,11 +357,6 @@
<artifactId>value-annotations</artifactId>
<version>2.9.3</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>3.16.14</version>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
@@ -381,7 +365,7 @@
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.9.2</version>
<version>5.9.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -400,19 +384,19 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>5.3.25</version>
<version>5.3.24</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.7.8</version>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.7.1</version>
<version>7.4.0</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -423,7 +407,7 @@
<plugin>
<groupId>com.github.ekryd.sortpom</groupId>
<artifactId>sortpom-maven-plugin</artifactId>
<version>3.2.1</version>
<version>3.2.0</version>
<configuration>
<createBackupFile>false</createBackupFile>
<encoding>${project.build.sourceEncoding}</encoding>
@@ -538,7 +522,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.2.1</version>
<version>3.2.0</version>
<configuration>
<checkstyleRules>
<!-- We only enable rules that are not enforced by
@@ -726,9 +710,6 @@
<module name="InnerAssignment" />
<module name="InvalidJavadocPosition" />
<module name="JavadocBlockTagLocation" />
<module name="JavadocStyle">
<property name="checkEmptyJavadoc" value="true" />
</module>
<module name="LocalVariableName" />
<module name="MatchXpath">
<property name="query" value="//BLOCK_COMMENT_BEGIN[./COMMENT_CONTENT[contains(@text, '@author')]]" />
@@ -788,12 +769,12 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>10.7.0</version>
<version>10.6.0</version>
</dependency>
<dependency>
<groupId>io.spring.nohttp</groupId>
<artifactId>nohttp-checkstyle</artifactId>
<version>0.0.11</version>
<version>0.0.10</version>
</dependency>
</dependencies>
<executions>
@@ -829,6 +810,11 @@
<artifactId>error_prone_core</artifactId>
<version>${version.error-prone}</version>
</path>
<path>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_docgen_processor</artifactId>
<version>${version.error-prone}</version>
</path>
<path>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
@@ -867,7 +853,6 @@
</annotationProcessorPaths>
<compilerArgs>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
@@ -889,7 +874,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.5.0</version>
<version>3.4.0</version>
<configuration>
<!-- XXX: Drop `ignoreAllNonTestScoped` once
https://issues.apache.org/jira/browse/MNG-6058 is
@@ -904,7 +889,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.0</version>
<version>3.0.0</version>
<configuration>
<retryFailedDeploymentCount>3</retryFailedDeploymentCount>
</configuration>
@@ -912,41 +897,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.2.1</version>
<version>3.1.0</version>
<configuration>
<fail>false</fail>
<rules>
<banCircularDependencies />
<banDependencyManagementScope />
<banDuplicateClasses>
<dependencies>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>dataflow-errorprone</artifactId>
<!-- This package is contained in
Checker Framework's `checker-qual` and
`dataflow-errorprone` modules. Error
Prone requires the latter, while Guava
pulls in the former. Since the package
contains mostly annotations, this is
not expected to cause trouble in
practice. -->
<ignoreClasses>
<ignoreClass>org.checkerframework.dataflow.qual.*</ignoreClass>
</ignoreClasses>
</dependency>
</dependencies>
<findAllDuplicates>true</findAllDuplicates>
</banDuplicateClasses>
<banDuplicatePomDependencyVersions />
<!-- XXX: Enable this rule once it no longer
"downloads the internet". See
https://issues.apache.org/jira/browse/MENFORCER-467.
<banDynamicVersions />-->
<dependencyConvergence />
<enforceBytecodeVersion>
<maxJdkVersion>${version.jdk}</maxJdkVersion>
</enforceBytecodeVersion>
<requireEncoding>
<acceptAsciiSubset>true</acceptAsciiSubset>
<encoding>ISO-8859-1</encoding>
@@ -958,8 +914,8 @@
<requireMavenVersion>
<version>${version.maven}</version>
</requireMavenVersion>
<requireNoRepositories />
<requirePluginVersions />
<requireSameVersionsReactor />
<requireUpperBoundDeps>
<excludes>
<!-- XXX:
@@ -1138,6 +1094,37 @@
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<mainClass>com.google.errorprone.DocGenTool</mainClass>
<arguments>
<argument>-bug_patterns=${project.build.directory}/generated-sources/annotations/bugPatterns.txt</argument>
<argument>-docs_repository=${project.build.directory}/generated-wiki/</argument>
<argument>-explanations=${basedir}/src/main/docs/bugpattern/</argument>
<argument>-target=external</argument>
</arguments>
<includePluginDependencies>true</includePluginDependencies>
</configuration>
<dependencies>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_docgen</artifactId>
<version>${version.error-prone}</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>generate-bugpattern-docs</id>
<goals>
<goal>java</goal>
</goals>
<phase>process-classes</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
@@ -1183,8 +1170,6 @@
| BSD 3-clause
| BSD License 3
| Eclipse Distribution License (New BSD License)
| Eclipse Distribution License - v 1.0
| EDL 1.0
| New BSD License
</licenseMerge>
<licenseMerge>
@@ -1290,7 +1275,7 @@
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<version>1.11.0</version>
<version>1.10.4</version>
<configuration>
<excludedClasses>
<!-- AutoValue generated classes. -->
@@ -1329,7 +1314,7 @@
<dependency>
<groupId>org.pitest</groupId>
<artifactId>pitest-junit5-plugin</artifactId>
<version>1.1.2</version>
<version>1.1.1</version>
</dependency>
</dependencies>
<executions>
@@ -1383,30 +1368,6 @@
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<configuration>
<rules>
<banDuplicateClasses>
<!-- The original
`error_prone_annotations` dependency
contains the same classes as those
provided by the corresponding Picnic
Error Prone fork artifact. -->
<dependencies combine.children="append">
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<ignoreClasses>
<ignoreClass>*</ignoreClass>
</ignoreClasses>
</dependency>
</dependencies>
</banDuplicateClasses>
</rules>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
@@ -1650,10 +1611,6 @@
<!-- XXX: Enable this once we open-source
this library. -->
-Xep:BetaApi:OFF
<!-- XXX: Enable once compatible with
`UnnecessarilyVisible`. See
https://github.com/google/error-prone/issues/3706. -->
-Xep:InjectOnBugCheckers:OFF
<!-- We don't target JDK 7. -->
-Xep:Java7ApiChecker:OFF
<!-- We don't target JDK 8. -->
@@ -1662,9 +1619,6 @@
-Xep:StaticOrDefaultInterfaceMethod:OFF
<!-- We generally discourage `var` use. -->
-Xep:Varifier:OFF
<!-- Yoda conditions are not always more
readable than the alternative. -->
-Xep:YodaCondition:OFF
-XepOpt:CheckReturnValue:CheckAllConstructors=true
<!-- XXX: Enable once there are fewer
false-positives.

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.8.1-SNAPSHOT</version>
<version>0.7.1-SNAPSHOT</version>
</parent>
<artifactId>refaster-compiler</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.8.1-SNAPSHOT</version>
<version>0.7.1-SNAPSHOT</version>
</parent>
<artifactId>refaster-runner</artifactId>

View File

@@ -0,0 +1,16 @@
Error Prone's out-of-the-box support for the application of
[Refaster][refaster] templates is somewhat cumbersome. Additionally, by default
the focus of Refaster templates is on one-off code refactorings.
This plugin attempts to bring Refaster templates on equal footing with other
Error Prone plugins by locating all Refaster templates on the classpath and
reporing any match. The suggested changes can be applied using Error Prone's
built-in [patch][patching] functionality.
XXX: Expand documentation. Mention:
- The `refaster-resource-compiler`.
- How checks can be restricted using the `NamePattern` flag (see the Javadoc)
- An concrete patching example, with and without `NamePattern`.
[refaster]: https://errorprone.info/docs/refaster
[patching]: https://errorprone.info/docs/patching

View File

@@ -161,7 +161,6 @@ public final class Refaster extends BugChecker implements CompilationUnitTreeMat
* that could cause {@link VisitorState#reportMatch(Description)}} to override the reported
* severity).
*/
@SuppressWarnings("RestrictedApiChecker" /* We create a heavily customized `Description` here. */)
private static Description augmentDescription(
Description description, Optional<SeverityLevel> severityOverride) {
return Description.builder(

View File

@@ -50,6 +50,12 @@ final class RefasterTest {
"\\[Refaster Rule\\] FooRules\\.ExtraGrouping\\.StringOfSizeThreeRule: "
+ "A custom description about matching three-char strings\\s+.+\\s+"
+ "\\(see https://example.com/custom\\)"));
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(Refaster.class, getClass());
private final BugCheckerRefactoringTestHelper restrictedRefactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(Refaster.class, getClass())
.setArgs(
"-XepOpt:Refaster:NamePattern=.*\\$(StringOfSizeZeroVerboseRule|StringOfSizeTwoRule)$");
@Test
void identification() {
@@ -214,7 +220,7 @@ final class RefasterTest {
@Test
void replacement() {
BugCheckerRefactoringTestHelper.newInstance(Refaster.class, getClass())
refactoringTestHelper
.addInputLines(
"A.java",
"class A {",
@@ -240,9 +246,7 @@ final class RefasterTest {
@Test
void restrictedReplacement() {
BugCheckerRefactoringTestHelper.newInstance(Refaster.class, getClass())
.setArgs(
"-XepOpt:Refaster:NamePattern=.*\\$(StringOfSizeZeroVerboseRule|StringOfSizeTwoRule)$")
restrictedRefactoringTestHelper
.addInputLines(
"A.java",
"class A {",

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.8.1-SNAPSHOT</version>
<version>0.7.1-SNAPSHOT</version>
</parent>
<artifactId>refaster-support</artifactId>

View File

@@ -77,7 +77,6 @@ public abstract class AnnotatedCompositeCodeTransformer implements CodeTransform
}
}
@SuppressWarnings("RestrictedApiChecker" /* We create a heavily customized `Description` here. */)
private Description augmentDescription(
Description description, CodeTransformer delegate, Context context) {
String shortCheckName = getShortCheckName(description.checkName);

View File

@@ -171,7 +171,6 @@ final class AnnotatedCompositeCodeTransformerTest {
return description(name, Optional.of(""), WARNING, "");
}
@SuppressWarnings("RestrictedApiChecker" /* We create a heavily customized `Description` here. */)
private static Description description(
String name, Optional<String> link, SeverityLevel severityLevel, String message) {
return Description.builder(DUMMY_POSITION, name, link.orElse(null), severityLevel, message)

View File

@@ -35,7 +35,9 @@ abstract class AbstractMatcherTestChecker extends BugChecker implements Compilat
@Override
public @Nullable Void scan(Tree tree, @Nullable Void unused) {
if (tree instanceof ExpressionTree && delegate.matches((ExpressionTree) tree, state)) {
state.reportMatch(describeMatch(tree));
state.reportMatch(
Description.builder(tree, canonicalName(), null, defaultSeverity(), message())
.build());
}
return super.scan(tree, unused);

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error-prone-support</artifactId>
<version>0.8.1-SNAPSHOT</version>
<version>0.7.1-SNAPSHOT</version>
</parent>
<artifactId>refaster-test-support</artifactId>