Compare commits

...

18 Commits

Author SHA1 Message Date
Stephan Schroevers
f84e17b396 Disable external link checking until the website branch is up-to-date with master again 2023-10-21 13:28:26 +02:00
Stephan Schroevers
11022cd958 Fix Refaster exclusion regexes 2023-10-21 13:20:48 +02:00
Stephan Schroevers
f0145f74ab Move SuggestedFixRules test files 2023-10-21 13:20:48 +02:00
Pieter Dirk Soels
86e50aaa7b Improve extractor matching and update README 2023-10-21 13:20:48 +02:00
Pieter Dirk Soels
8bf1bd4105 Move new rule collection 2023-10-21 13:20:48 +02:00
Pieter Dirk Soels
db876b2257 Drop Docgen Maven profile 2023-10-21 13:20:48 +02:00
Pieter Dirk Soels
3427b947cf Fit Refaster extractors into new documentation support setup 2023-10-21 13:20:48 +02:00
Rick Ossendrijver
f3571f13dd Introduce documentation generation
This is a squash commit of the following previous commits:

---

Introduce `documentation-support` module to extract website data from source code

By adding a compilation `TaskListener` that extracts data from the Bug pattern
and Refaster rule collection (test) classes and writing to JSON output files
in the target directory. This extraction happens as part of the Maven build
using the `docgen` profile.

---

Improve website styling

Co-authored-by: japborst <japborst@gmail.com>
Co-authored-by: Gijs de Jong <berichtaangijs@gmail.com>

---

Generate Markdown files from existing content for the website

---

Upgrade dependencies to the latest versions

---

Compile and install project jars before docgen

---

Run validation in build and exclude self-url

---

Reintroduce `htmlproofer`, improve templates, cleanup setup and dependencies

---

Delete directory if it exists

---

Add SCSS for GitHub button and fix bug pattern sample rendering

---

Fix bug pattern GitHub link

---

Small styling tweaks (incl. for dark theme)

---

Use single mvn command

---

Move mustache templates

---

Hardcode anchors for headings

---

Add supression to bugpatterns

---

Remove self ignore for html-proofer

---

Add refaster supressions and use callouts

---

Use v0.4.1-SNAPSHOT

---

Revert "Use single mvn command"

This reverts commit 594471d1ed23a1c19d7fe88d925d1b7f828716cd.

---

Extract Refaster samples from source code instead of AST

---

Skip verification, for now

---

Add notes on disabling bugpatterns

---

Set default layout and image

---

Fix mobile navigation

---

Revert "Set default layout and image"

This reverts commit 67a4aa7b5b4d14c0f2b783f345f53affe6ef3ec5.

---

Add supression for refaster rules

---

Post-rebase fixes

---

Fix the tests

---

Doh

---

Version bump

---

Exclude ThirdPartyLibraryTest from Bug Pattern test output

---

Remove only last occurence of 'Test' in Bug Pattern tests doc generation

---

Move `MapRulesTest` resources

---

Add exclusion for docgen of `SourceCodeTest`

---

Bump version

---

Extra fixes after rebase

---

Delete custom nav footer and `assets/images/favicon.ico`

---

Post rebase fixes with version bump to `0.8.1-SNAPSHOT`

---

Use new BugPatternTest extraction method
2023-10-21 13:20:47 +02:00
Pieter Dirk Soels
c9612f22af PSM-1717 Pass ClassLoader to ServiceLoader 2023-10-21 13:20:47 +02:00
Stephan Schroevers
03a4466878 Fix JDK 11 compatibility 2023-10-21 13:20:47 +02:00
Stephan Schroevers
7146ff1ceb Suggestions 2023-10-21 13:20:47 +02:00
Stephan Schroevers
e116854d3d Suggestions 2023-10-21 13:20:47 +02:00
Stephan Schroevers
379b453e82 Suggestions 2023-10-21 13:20:47 +02:00
Rick Ossendrijver
ecafaa6801 This actually kills the mutant 2023-10-21 13:20:47 +02:00
Rick Ossendrijver
1ef7f5b04b Kill another mutant and drop unused imports 2023-10-21 13:20:47 +02:00
Rick Ossendrijver
3d5a75333b Further simplify testing setup 2023-10-21 13:20:47 +02:00
Rick Ossendrijver
9ee59a6030 Simplify tests 2023-10-21 13:20:47 +02:00
Rick Ossendrijver
4bcba7f2a0 Introduce BugPatternTestExtractor with tests 2023-10-21 13:20:47 +02:00
161 changed files with 1636 additions and 258 deletions

View File

@@ -21,16 +21,19 @@ jobs:
bundler-cache: true
- name: Configure Github Pages
uses: actions/configure-pages@v2.1.3
- name: Set up JDK
uses: actions/setup-java@v3.5.1
with:
java-version: 17.0.4
distribution: temurin
cache: maven
- name: Compile project and extract data
# XXX: Remove `-Dverification.skip` and update `website/README.md` once
# module `docgen` has no errors.
run: mvn -T1C clean install -DskipTests -Dverification.skip -Pdocgen
- name: Generate documentation
run: ./generate-docs.sh
- name: Build website with Jekyll
working-directory: ./website
run: bundle exec jekyll build
- name: Validate HTML output
working-directory: ./website
# XXX: Drop `--disable_external true` once we fully adopted the
# "Refaster rules" terminology on our website and in the code.
run: bundle exec htmlproofer --disable_external true --check-external-hash false ./_site
run: bundle exec ruby generate-docs.rb
- name: Upload website as artifact
uses: actions/upload-pages-artifact@v1.0.5
with:

View File

@@ -27,6 +27,10 @@
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_check_api</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_core</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_test_helpers</artifactId>

View File

@@ -5,17 +5,19 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.Objects.requireNonNull;
import com.google.auto.common.AnnotationMirrors;
import com.google.auto.service.AutoService;
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.VisitorState;
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 java.util.Optional;
import javax.lang.model.element.AnnotationValue;
import tech.picnic.errorprone.documentation.BugPatternExtractor.BugPatternDocumentation;
@@ -23,29 +25,38 @@ import tech.picnic.errorprone.documentation.BugPatternExtractor.BugPatternDocume
* 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");
@AutoService(Extractor.class)
public final class BugPatternExtractor implements Extractor<BugPatternDocumentation> {
/** Instantiates a new {@link BugPatternExtractor} instance. */
public BugPatternExtractor() {}
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 String identifier() {
return "bugpattern";
}
@Override
public boolean canExtract(ClassTree tree) {
return ASTHelpers.hasDirectAnnotationWithSimpleName(tree, BugPattern.class.getSimpleName());
public Optional<BugPatternDocumentation> tryExtract(ClassTree tree, VisitorState state) {
ClassSymbol symbol = ASTHelpers.getSymbol(tree);
BugPattern annotation = symbol.getAnnotation(BugPattern.class);
if (annotation == null) {
return Optional.empty();
}
return Optional.of(
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()));
}
/**

View File

@@ -0,0 +1,200 @@
package tech.picnic.errorprone.documentation;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.anything;
import static com.google.errorprone.matchers.Matchers.argument;
import static com.google.errorprone.matchers.Matchers.classLiteral;
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
import static com.google.errorprone.matchers.Matchers.instanceMethod;
import static com.google.errorprone.matchers.Matchers.toType;
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
import com.google.auto.service.AutoService;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreeScanner;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import org.jspecify.annotations.Nullable;
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.BugPatternTestDocumentation;
/**
* An {@link Extractor} that describes how to extract data from classes that test a {@code
* BugChecker}.
*/
@Immutable
@AutoService(Extractor.class)
public final class BugPatternTestExtractor implements Extractor<BugPatternTestDocumentation> {
private static final Pattern TEST_CLASS_NAME_PATTERN = Pattern.compile("(.*)Test");
private static final Matcher<Tree> JUNIT_TEST_METHOD =
toType(MethodTree.class, hasAnnotation("org.junit.jupiter.api.Test"));
private static final Matcher<MethodInvocationTree> BUG_PATTERN_TEST_METHOD =
allOf(
staticMethod()
.onDescendantOfAny(
"com.google.errorprone.CompilationTestHelper",
"com.google.errorprone.BugCheckerRefactoringTestHelper")
.named("newInstance"),
argument(0, classLiteral(anything())));
/** Instantiates a new {@link BugPatternTestExtractor} instance. */
public BugPatternTestExtractor() {}
@Override
public String identifier() {
return "bugpattern-test";
}
// XXX: Improve support for correctly extracting multiple sources from a single
// `{BugCheckerRefactoring,Compilation}TestHelper` test.
@Override
public Optional<BugPatternTestDocumentation> tryExtract(ClassTree tree, VisitorState state) {
return getClassUnderTest(tree)
.filter(bugPatternName -> testsBugPattern(bugPatternName, tree, state))
.map(
bugPatternName -> {
CollectBugPatternTests scanner = new CollectBugPatternTests();
for (Tree m : tree.getMembers()) {
if (JUNIT_TEST_METHOD.matches(m, state)) {
scanner.scan(m, state);
}
}
return new AutoValue_BugPatternTestExtractor_BugPatternTestDocumentation(
bugPatternName, scanner.getIdentificationTests(), scanner.getReplacementTests());
});
}
private static boolean testsBugPattern(
String bugPatternName, ClassTree tree, VisitorState state) {
AtomicBoolean result = new AtomicBoolean(false);
new TreeScanner<@Nullable Void, @Nullable Void>() {
@Override
public @Nullable Void visitMethodInvocation(MethodInvocationTree node, @Nullable Void v) {
if (BUG_PATTERN_TEST_METHOD.matches(node, state)) {
MemberSelectTree firstArgumentTree = (MemberSelectTree) node.getArguments().get(0);
result.compareAndSet(
/* expectedValue= */ false,
bugPatternName.equals(firstArgumentTree.getExpression().toString()));
}
return super.visitMethodInvocation(node, v);
}
}.scan(tree, null);
return result.get();
}
private static Optional<String> getClassUnderTest(ClassTree tree) {
return Optional.of(TEST_CLASS_NAME_PATTERN.matcher(tree.getSimpleName().toString()))
.filter(java.util.regex.Matcher::matches)
.map(m -> m.group(1));
}
private static final class CollectBugPatternTests
extends TreeScanner<@Nullable Void, VisitorState> {
private static final Matcher<ExpressionTree> IDENTIFICATION_SOURCE_LINES =
instanceMethod()
.onDescendantOf("com.google.errorprone.CompilationTestHelper")
.named("addSourceLines");
private static final Matcher<ExpressionTree> REPLACEMENT_INPUT =
instanceMethod()
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper")
.named("addInputLines");
private static final Matcher<ExpressionTree> REPLACEMENT_OUTPUT =
instanceMethod()
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper.ExpectOutput")
.named("addOutputLines");
private final List<String> identificationTests = new ArrayList<>();
private final List<BugPatternReplacementTestDocumentation> replacementTests = new ArrayList<>();
public ImmutableList<String> getIdentificationTests() {
return ImmutableList.copyOf(identificationTests);
}
public ImmutableList<BugPatternReplacementTestDocumentation> getReplacementTests() {
return ImmutableList.copyOf(replacementTests);
}
// XXX: Consider:
// - Whether to omit or handle differently identification tests without `// BUG: Diagnostic
// (contains|matches)` markers.
// - Whether to omit or handle differently replacement tests with identical input and output.
// (Though arguably we should have a separate checker which replaces such cases with
// `.expectUnchanged()`.)
// - Whether to track `.expectUnchanged()` test cases.
@Override
public @Nullable Void visitMethodInvocation(MethodInvocationTree node, VisitorState state) {
if (IDENTIFICATION_SOURCE_LINES.matches(node, state)) {
getSourceCode(node).ifPresent(identificationTests::add);
} else if (REPLACEMENT_OUTPUT.matches(node, state)) {
ExpressionTree receiver = ASTHelpers.getReceiver(node);
// XXX: Make this code nicer.
if (REPLACEMENT_INPUT.matches(receiver, state)) {
getSourceCode(node)
.ifPresent(
output ->
getSourceCode((MethodInvocationTree) receiver)
.ifPresent(
input ->
replacementTests.add(
new AutoValue_BugPatternTestExtractor_BugPatternReplacementTestDocumentation(
input, output))));
}
}
return super.visitMethodInvocation(node, state);
}
// XXX: Duplicated from `ErrorProneTestSourceFormat`. Can we do better?
private static Optional<String> getSourceCode(MethodInvocationTree tree) {
List<? extends ExpressionTree> sourceLines =
tree.getArguments().subList(1, tree.getArguments().size());
StringBuilder source = new StringBuilder();
for (ExpressionTree sourceLine : sourceLines) {
String value = ASTHelpers.constValue(sourceLine, String.class);
if (value == null) {
return Optional.empty();
}
source.append(value).append('\n');
}
return Optional.of(source.toString());
}
}
// XXX: Rename?
@AutoValue
abstract static class BugPatternTestDocumentation {
abstract String name();
abstract ImmutableList<String> identificationTests();
abstract ImmutableList<BugPatternReplacementTestDocumentation> replacementTests();
}
// XXX: Rename?
@AutoValue
abstract static class BugPatternReplacementTestDocumentation {
abstract String inputLines();
abstract String outputLines();
}
}

View File

@@ -5,10 +5,14 @@ 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.google.common.collect.ImmutableList;
import com.google.errorprone.VisitorState;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskEvent.Kind;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.util.Context;
import java.io.File;
@@ -19,6 +23,7 @@ import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ServiceLoader;
import javax.tools.JavaFileObject;
/**
@@ -27,6 +32,13 @@ import javax.tools.JavaFileObject;
*/
// XXX: Find a better name for this class; it doesn't generate documentation per se.
final class DocumentationGeneratorTaskListener implements TaskListener {
@SuppressWarnings({"rawtypes", "unchecked"})
private static final ImmutableList<Extractor<?>> EXTRACTORS =
(ImmutableList)
ImmutableList.copyOf(
ServiceLoader.load(
Extractor.class, DocumentationGeneratorTaskListener.class.getClassLoader()));
private static final ObjectMapper OBJECT_MAPPER =
new ObjectMapper().setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
@@ -51,19 +63,25 @@ final class DocumentationGeneratorTaskListener implements TaskListener {
return;
}
ClassTree classTree = JavacTrees.instance(context).getTree(taskEvent.getTypeElement());
JavaFileObject sourceFile = taskEvent.getSourceFile();
if (classTree == null || sourceFile == null) {
CompilationUnitTree compilationUnit = taskEvent.getCompilationUnit();
ClassTree classTree = JavacTrees.instance(context).getTree(taskEvent.getTypeElement());
if (sourceFile == null || compilationUnit == null || classTree == null) {
return;
}
ExtractorType.findMatchingType(classTree)
.ifPresent(
extractorType ->
writeToFile(
extractorType.getIdentifier(),
getSimpleClassName(sourceFile.toUri()),
extractorType.getExtractor().extract(classTree, context)));
VisitorState state =
VisitorState.createForUtilityPurposes(context)
.withPath(new TreePath(new TreePath(compilationUnit), classTree));
for (Extractor<?> extractor : EXTRACTORS) {
extractor
.tryExtract(classTree, state)
.ifPresent(
data ->
writeToFile(
extractor.identifier(), getSimpleClassName(sourceFile.toUri()), data));
}
}
private void createDocsDirectory() {

View File

@@ -1,8 +1,9 @@
package tech.picnic.errorprone.documentation;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Immutable;
import com.sun.source.tree.ClassTree;
import com.sun.tools.javac.util.Context;
import java.util.Optional;
/**
* Interface implemented by classes that define how to extract data of some type {@link T} from a
@@ -13,21 +14,20 @@ import com.sun.tools.javac.util.Context;
@Immutable
interface Extractor<T> {
/**
* Extracts and returns an instance of {@link T} using the provided arguments.
* Returns the unique identifier of this extractor.
*
* @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}.
* @return A non-{@code null} string.
*/
// XXX: Drop `Context` parameter unless used.
T extract(ClassTree tree, Context context);
String identifier();
/**
* Tells whether this {@link Extractor} can extract documentation content from the given {@link
* ClassTree}.
* Attempts to extract an instance of type {@link T} using the provided arguments.
*
* @param tree The {@link ClassTree} of interest.
* @return {@code true} iff data extraction is supported.
* @param tree The {@link ClassTree} to analyze and from which to extract an instance of type
* {@link T}.
* @param state A {@link VisitorState} describing the context in which the given {@link ClassTree}
* is found.
* @return An instance of type {@link T}, if possible.
*/
boolean canExtract(ClassTree tree);
Optional<T> tryExtract(ClassTree tree, VisitorState state);
}

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

@@ -0,0 +1,65 @@
package tech.picnic.errorprone.documentation;
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Immutable;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
import java.util.Optional;
import java.util.regex.Pattern;
import tech.picnic.errorprone.documentation.models.RefasterTemplateCollectionTestData;
import tech.picnic.errorprone.documentation.models.RefasterTemplateTestData;
@Immutable
@AutoService(Extractor.class)
public final class RefasterTestInputExtractor
implements Extractor<RefasterTemplateCollectionTestData> {
private static final Pattern TEST_CLASS_NAME_PATTERN = Pattern.compile("(.*)Test");
@Override
public String identifier() {
return "refaster-test-input";
}
@Override
public Optional<RefasterTemplateCollectionTestData> tryExtract(
ClassTree tree, VisitorState state) {
if (!state.getPath().getCompilationUnit().getPackageName().toString().contains("input")) {
return Optional.empty();
}
Optional<String> className = getClassUnderTest(tree);
if (className.isEmpty()) {
return Optional.empty();
}
ImmutableList<RefasterTemplateTestData> templateTests =
tree.getMembers().stream()
.filter(m -> m instanceof MethodTree)
.map(MethodTree.class::cast)
.filter(m -> m.getName().toString().startsWith("test"))
.map(
m ->
RefasterTemplateTestData.create(
m.getName().toString().replace("test", ""), getSourceCode(m, state)))
.collect(toImmutableList());
return Optional.of(
RefasterTemplateCollectionTestData.create(className.orElseThrow(), true, templateTests));
}
private static Optional<String> getClassUnderTest(ClassTree tree) {
return Optional.of(TEST_CLASS_NAME_PATTERN.matcher(tree.getSimpleName().toString()))
.filter(java.util.regex.Matcher::matches)
.map(m -> m.group(1));
}
// XXX: Duplicated from `SourceCode`. Can we do better?
private String getSourceCode(MethodTree tree, VisitorState state) {
String src = state.getSourceForNode(tree);
return src != null ? src : tree.toString();
}
}

View File

@@ -0,0 +1,65 @@
package tech.picnic.errorprone.documentation;
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Immutable;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
import java.util.Optional;
import java.util.regex.Pattern;
import tech.picnic.errorprone.documentation.models.RefasterTemplateCollectionTestData;
import tech.picnic.errorprone.documentation.models.RefasterTemplateTestData;
@Immutable
@AutoService(Extractor.class)
public final class RefasterTestOutputExtractor
implements Extractor<RefasterTemplateCollectionTestData> {
private static final Pattern TEST_CLASS_NAME_PATTERN = Pattern.compile("(.*)Test");
@Override
public String identifier() {
return "refaster-test-output";
}
@Override
public Optional<RefasterTemplateCollectionTestData> tryExtract(
ClassTree tree, VisitorState state) {
if (!state.getPath().getCompilationUnit().getPackageName().toString().contains("output")) {
return Optional.empty();
}
Optional<String> className = getClassUnderTest(tree);
if (className.isEmpty()) {
return Optional.empty();
}
ImmutableList<RefasterTemplateTestData> templateTests =
tree.getMembers().stream()
.filter(m -> m instanceof MethodTree)
.map(MethodTree.class::cast)
.filter(m -> m.getName().toString().startsWith("test"))
.map(
m ->
RefasterTemplateTestData.create(
m.getName().toString().replace("test", ""), getSourceCode(m, state)))
.collect(toImmutableList());
return Optional.of(
RefasterTemplateCollectionTestData.create(className.orElseThrow(), true, templateTests));
}
private static Optional<String> getClassUnderTest(ClassTree tree) {
return Optional.of(TEST_CLASS_NAME_PATTERN.matcher(tree.getSimpleName().toString()))
.filter(java.util.regex.Matcher::matches)
.map(m -> m.group(1));
}
// XXX: Duplicated from `SourceCode`. Can we do better?
private String getSourceCode(MethodTree tree, VisitorState state) {
String src = state.getSourceForNode(tree);
return src != null ? src : tree.toString();
}
}

View File

@@ -0,0 +1,25 @@
package tech.picnic.errorprone.documentation.models;
import com.google.auto.value.AutoValue;
import java.util.List;
import tech.picnic.errorprone.documentation.models.AutoValue_RefasterTemplateCollectionData;
/**
* Object containing all data related to a Refaster template collection. This is solely used for
* serialization.
*/
@AutoValue
public abstract class RefasterTemplateCollectionData {
public static RefasterTemplateCollectionData create(
String name, String description, String link, List<RefasterTemplateData> templates) {
return new AutoValue_RefasterTemplateCollectionData(name, description, link, templates);
}
abstract String name();
abstract String description();
abstract String link();
abstract List<RefasterTemplateData> templates();
}

View File

@@ -0,0 +1,20 @@
package tech.picnic.errorprone.documentation.models;
import com.google.auto.value.AutoValue;
import java.util.List;
import tech.picnic.errorprone.documentation.models.AutoValue_RefasterTemplateCollectionTestData;
@AutoValue
public abstract class RefasterTemplateCollectionTestData {
public static RefasterTemplateCollectionTestData create(
String templateCollection, boolean isInput, List<RefasterTemplateTestData> templatesTests) {
return new AutoValue_RefasterTemplateCollectionTestData(
templateCollection, isInput, templatesTests);
}
abstract String templateCollection();
abstract boolean isInput();
abstract List<RefasterTemplateTestData> templateTests();
}

View File

@@ -0,0 +1,21 @@
package tech.picnic.errorprone.documentation.models;
import com.google.auto.value.AutoValue;
import com.google.errorprone.BugPattern.SeverityLevel;
import tech.picnic.errorprone.documentation.models.AutoValue_RefasterTemplateData;
@AutoValue
public abstract class RefasterTemplateData {
public static RefasterTemplateData create(
String name, String description, String link, SeverityLevel severityLevel) {
return new AutoValue_RefasterTemplateData(name, description, link, severityLevel);
}
abstract String name();
abstract String description();
abstract String link();
abstract SeverityLevel severityLevel();
}

View File

@@ -0,0 +1,15 @@
package tech.picnic.errorprone.documentation.models;
import com.google.auto.value.AutoValue;
import tech.picnic.errorprone.documentation.models.AutoValue_RefasterTemplateTestData;
@AutoValue
public abstract class RefasterTemplateTestData {
public static RefasterTemplateTestData create(String templateName, String templateTestContent) {
return new AutoValue_RefasterTemplateTestData(templateName, templateTestContent);
}
abstract String templateName();
abstract String templateTestContent();
}

View File

@@ -3,7 +3,6 @@ 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;
@@ -139,14 +138,10 @@ final class BugPatternExtractorTest {
@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)))
.setMessage(
String.format(
"Can extract: %s", new BugPatternExtractor().tryExtract(tree, state).isPresent()))
.build();
}
}

View File

@@ -0,0 +1,352 @@
package tech.picnic.errorprone.documentation;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.io.Resources;
import java.io.IOException;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
final class BugPatternTestExtractorTest {
@Test
void noBugPatternTest(@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 bugPatternTestFileWithoutTestSuffix(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerWithWrongSuffix.java",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import com.google.errorprone.CompilationTestHelper;",
"",
"final class TestCheckerWithoutTestSuffix {",
" private static class TestCheckerWithout extends BugChecker {}",
"",
" CompilationTestHelper compilationTestHelper = CompilationTestHelper.newInstance(TestCheckerWithout.class, getClass());",
"}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void minimalBugPatternTest(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import com.google.errorprone.CompilationTestHelper;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" CompilationTestHelper compilationTestHelper = CompilationTestHelper.newInstance(TestChecker.class, getClass());",
"}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-test-TestCheckerTest.json",
"bugpattern-test-documentation-minimal.json");
}
@Test
void differentBugPatternAsClassVariableTest(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import com.google.errorprone.CompilationTestHelper;",
"",
"final class TestCheckerTest {",
" private static class DifferentChecker extends BugChecker {}",
"",
" CompilationTestHelper compilationTestHelper = CompilationTestHelper.newInstance(DifferentChecker.class, getClass());",
"}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void differentBugPatternAsLocalVariable(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import com.google.errorprone.CompilationTestHelper;",
"import org.junit.jupiter.api.Test;",
"",
"final class TestCheckerTest {",
" private static class DifferentChecker extends BugChecker {}",
"",
" @Test",
" void identification() {",
" CompilationTestHelper.newInstance(DifferentChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"class A {}\")",
" .doTest();",
" }",
"}");
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
}
@Test
void bugPatternTestSingleIdentification(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import com.google.errorprone.CompilationTestHelper;",
"import org.junit.jupiter.api.Test;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" @Test",
" void identification() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"class A {}\")",
" .doTest();",
" }",
"}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-test-TestCheckerTest.json",
"bugpattern-test-documentation-identification.json");
}
@Test
void bugPatternTestIdentificationMultipleSourceLines(@TempDir Path outputDirectory)
throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"package pkg;",
"",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import com.google.errorprone.CompilationTestHelper;",
"import org.junit.jupiter.api.Test;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" @Test",
" void identification() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"class A {}\")",
" .addSourceLines(\"B.java\", \"class B {}\")",
" .doTest();",
" }",
"}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-test-TestCheckerTest.json",
"bugpattern-test-documentation-identification-two-sources.json");
}
@Test
void bugPatternTestSingleReplacement(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import org.junit.jupiter.api.Test;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" @Test",
" void replacement() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A {}\")",
" .doTest(TestMode.TEXT_MATCH);",
" }",
"}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-test-TestCheckerTest.json",
"bugpattern-test-documentation-replacement.json");
}
@Test
void bugPatternTestMultipleReplacementSources(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import org.junit.jupiter.api.Test;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" @Test",
" void replacement() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A {}\")",
" .addInputLines(\"B.java\", \"class B {}\")",
" .addOutputLines(\"B.java\", \"class B {}\")",
" .doTest(TestMode.TEXT_MATCH);",
" }",
"}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-test-TestCheckerTest.json",
"bugpattern-test-documentation-replacement-two-sources.json");
}
@Test
void bugPatternReplacementExpectUnchanged(@TempDir Path outputDirectory) throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import org.junit.jupiter.api.Test;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" @Test",
" void replacement() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .expectUnchanged()",
" .doTest(TestMode.TEXT_MATCH);",
" }",
"}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-test-TestCheckerTest.json",
"bugpattern-test-documentation-replacement-expect-unchanged.json");
}
@Test
void bugPatternTestIdentificationAndReplacement(@TempDir Path outputDirectory)
throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import com.google.errorprone.CompilationTestHelper;",
"import org.junit.jupiter.api.Test;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" @Test",
" void identification() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"class A {}\")",
" .doTest();",
" }",
"",
" @Test",
" void replacement() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A {}\")",
" .doTest(TestMode.TEXT_MATCH);",
" }",
"}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-test-TestCheckerTest.json",
"bugpattern-test-documentation-identification-and-replacement.json");
}
@Test
void bugPatternTestMultipleIdentificationAndReplacement(@TempDir Path outputDirectory)
throws IOException {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"TestCheckerTest.java",
"package pkg;",
"",
"import static com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers.SECOND;",
"",
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
"import com.google.errorprone.bugpatterns.BugChecker;",
"import com.google.errorprone.CompilationTestHelper;",
"import org.junit.jupiter.api.Test;",
"",
"final class TestCheckerTest {",
" private static class TestChecker extends BugChecker {}",
"",
" @Test",
" void identification() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"A.java\", \"class A {}\")",
" .doTest();",
" }",
"",
" @Test",
" void identification2() {",
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
" .addSourceLines(\"B.java\", \"class B {}\")",
" .doTest();",
" }",
"",
" @Test",
" void replacementFirstSuggestedFix() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .addInputLines(\"A.java\", \"class A {}\")",
" .addOutputLines(\"A.java\", \"class A {}\")",
" .doTest(TestMode.TEXT_MATCH);",
" }",
"",
" @Test",
" void replacementSecondSuggestedFix() {",
" BugCheckerRefactoringTestHelper.newInstance(TestChecker.class, getClass())",
" .setFixChooser(SECOND)",
" .addInputLines(\"B.java\", \"class B {}\")",
" .addOutputLines(\"B.java\", \"class B {}\")",
" .doTest(TestMode.TEXT_MATCH);",
" }",
"}");
verifyFileMatchesResource(
outputDirectory,
"bugpattern-test-TestCheckerTest.json",
"bugpattern-test-documentation-multiple-identification-and-replacement.json");
}
private static void verifyFileMatchesResource(
Path outputDirectory, String fileName, String resourceName) throws IOException {
assertThat(outputDirectory.resolve(fileName))
.content(UTF_8)
.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(BugPatternTestExtractorTest.class, resourceName), UTF_8);
}
}

View File

@@ -1,19 +1,29 @@
package tech.picnic.errorprone.documentation;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.nio.charset.StandardCharsets.UTF_8;
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.auto.service.AutoService;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.Immutable;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.Tree;
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 java.util.Optional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.EnabledOnOs;
@@ -74,4 +84,55 @@ final class DocumentationGeneratorTaskListenerTest {
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Precisely one path must be provided");
}
@Test
void extraction(@TempDir Path outputDirectory) {
Compilation.compileWithDocumentationGenerator(
outputDirectory,
"DocumentationGeneratorTaskListenerTestClass.java",
"class DocumentationGeneratorTaskListenerTestClass {}");
// XXX: Once we support only JDK 15+, use a text block for the `expected` string.
assertThat(
outputDirectory.resolve(
"documentation-generator-task-listener-test-DocumentationGeneratorTaskListenerTestClass.json"))
.content(UTF_8)
.isEqualToIgnoringWhitespace(
"{\"className\":\"DocumentationGeneratorTaskListenerTestClass\",\"path\":[\"CLASS: DocumentationGeneratorTaskListenerTestClass\",\"COMPILATION_UNIT\"]}");
}
@Immutable
@AutoService(Extractor.class)
public static final class TestExtractor implements Extractor<ExtractionParameters> {
@Override
public String identifier() {
return "documentation-generator-task-listener-test";
}
@Override
public Optional<ExtractionParameters> tryExtract(ClassTree tree, VisitorState state) {
return Optional.of(tree.getSimpleName().toString())
.filter(n -> n.contains(DocumentationGeneratorTaskListenerTest.class.getSimpleName()))
.map(
className ->
new AutoValue_DocumentationGeneratorTaskListenerTest_ExtractionParameters(
className,
Streams.stream(state.getPath())
.map(TestExtractor::describeTree)
.collect(toImmutableList())));
}
private static String describeTree(Tree tree) {
return (tree instanceof ClassTree)
? String.join(": ", String.valueOf(tree.getKind()), ((ClassTree) tree).getSimpleName())
: tree.getKind().toString();
}
}
@AutoValue
abstract static class ExtractionParameters {
abstract String className();
abstract ImmutableList<String> path();
}
}

View File

@@ -0,0 +1,12 @@
{
"name": "TestChecker",
"identificationTests": [
"class A {}\n"
],
"replacementTests": [
{
"inputLines": "class A {}\n",
"outputLines": "class A {}\n"
}
]
}

View File

@@ -0,0 +1,8 @@
{
"name": "TestChecker",
"identificationTests": [
"class B {}\n",
"class A {}\n"
],
"replacementTests": []
}

View File

@@ -0,0 +1,7 @@
{
"name": "TestChecker",
"identificationTests": [
"class A {}\n"
],
"replacementTests": []
}

View File

@@ -0,0 +1,5 @@
{
"name": "TestChecker",
"identificationTests": [],
"replacementTests": []
}

View File

@@ -0,0 +1,17 @@
{
"name": "TestChecker",
"identificationTests": [
"class A {}\n",
"class B {}\n"
],
"replacementTests": [
{
"inputLines": "class A {}\n",
"outputLines": "class A {}\n"
},
{
"inputLines": "class B {}\n",
"outputLines": "class B {}\n"
}
]
}

View File

@@ -0,0 +1,5 @@
{
"name": "TestChecker",
"identificationTests": [],
"replacementTests": []
}

View File

@@ -0,0 +1,14 @@
{
"name": "TestChecker",
"identificationTests": [],
"replacementTests": [
{
"inputLines": "class B {}\n",
"outputLines": "class B {}\n"
},
{
"inputLines": "class A {}\n",
"outputLines": "class A {}\n"
}
]
}

View File

@@ -0,0 +1,10 @@
{
"name": "TestChecker",
"identificationTests": [],
"replacementTests": [
{
"inputLines": "class A {}\n",
"outputLines": "class A {}\n"
}
]
}

View File

@@ -257,4 +257,33 @@
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
<id>docgen</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>add-test-source</id>
<goals>
<goal>add-test-source</goal>
</goals>
<phase>generate-test-sources</phase>
<configuration>
<sources>
<source>src/test/resources</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -152,7 +152,7 @@ public final class ErrorProneTestHelperSourceFormat extends BugChecker
StringBuilder source = new StringBuilder();
for (ExpressionTree sourceLine : sourceLines) {
Object value = ASTHelpers.constValue(sourceLine);
String value = ASTHelpers.constValue(sourceLine, String.class);
if (value == null) {
return Optional.empty();
}

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIOException;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import java.math.BigDecimal;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugCheckerRefactoringTestHelper;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.reverseOrder;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.BoundType;
import com.google.common.collect.ImmutableSet;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.collect.ImmutableListMultimap.flatteningToImmutableListMultimap;
import static com.google.common.collect.ImmutableListMultimap.toImmutableListMultimap;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.Comparator.naturalOrder;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static java.util.function.Function.identity;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.collect.ImmutableMultiset.toImmutableMultiset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.collect.ImmutableSetMultimap.flatteningToImmutableSetMultimap;
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
import static java.util.Comparator.naturalOrder;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.collect.ImmutableSortedMultiset.toImmutableSortedMultiset;
import static java.util.Comparator.naturalOrder;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
import static java.util.Comparator.naturalOrder;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import org.junit.jupiter.params.provider.Arguments;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertFalse;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static java.util.Objects.requireNonNullElse;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.base.Preconditions.checkNotNull;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Ints;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.MoreCollectors.toOptional;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import io.reactivex.BackpressureStrategy;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static java.util.Comparator.comparingInt;
import static java.util.Comparator.reverseOrder;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.errorprone.fixes.SuggestedFix;
import com.sun.source.tree.ExpressionTree;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertEqualsNoOrder;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import com.google.common.collect.ImmutableSet;
import java.time.Clock;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.input;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.HEAD;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -1,4 +1,4 @@
package tech.picnic.errorprone.refasterrules;
package tech.picnic.errorprone.refasterrules.output;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIOException;

Some files were not shown because too many files have changed in this diff Show More