-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce documentation-support
module
#428
Merged
Merged
Changes from 50 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
a1355c1
Introduce `documentation-generator` module
rickie 1116991
Fix some mutants and improve code
rickie 826f0a3
Two more mutants
rickie ae3a8e3
Was doing full build from wrong directory
rickie 53fe306
Fixes to make build green for all JDKs
rickie 304fa74
Make build Windows compatible
rickie 5a0d7a2
Try new thing for Windows build
rickie 1657c83
PSM-1716 Simplify path generation
nathankooij 2323a08
PSM-1716 Split wrongPath test based on OS
nathankooij 15453af
PSM-1716 Sort
nathankooij aa3b779
PSM-1716 Drop trailing character
nathankooij ce42b4d
PSM-1716 Suggestions
nathankooij 03a2768
Apply suggestions and minor improvements
rickie 783989f
Change `basePath` usage to not get Windows issue?
rickie e9143ee
Rename files and improve tests
rickie b0864d0
Optimize `DocGenTaskListener#started`
rickie f8b0261
Rename test and use correct `TaskEvent.Kind`
rickie 4e03c9a
Major cleanup in the testing setup
rickie 1fffc62
Further simplify compiler test setup
rickie d8ed827
PSM-1716 Suggestions
nathankooij 344af56
PSM-1716 Forgot to change the message
nathankooij 2fe7c30
PSM-1716 Fix violation
nathankooij 2b94450
PSM-1716 Try `setWritable` for Windows instead
nathankooij d0fccf2
PSM-1716 Fix violation
nathankooij a36664a
PSM-1716 Windows
nathankooij fd847c9
PSM-1716 Do pass the parameter
nathankooij 1967f1c
PSM-1716 Fix error message on Windows
nathankooij c43809d
`s/package tech.picnic.errorprone.{plugin -> documentation}`
rickie 60f506e
Suggestions
rickie 6cd66c1
Use `Splitter` API instead of `String#split`
rickie 58e30e6
PSM-1716 Drop `documentation-generator` Maven profile
rickie 69ed2ae
This is a works on my machine....
rickie fdf4204
Apply suggestion
rickie d3ecbc0
Try something else to fix build
rickie c4de095
`s/disableable/canDisable/`
rickie fe71a5d
Revert "This is a works on my machine...."
rickie c369106
Revert "PSM-1716 Drop `documentation-generator` Maven profile"
rickie 401b31a
Dont update the images
rickie f3c8618
Further revert my mistake
rickie 9a7720a
Version bump
Stephan202 357d736
Suggestions
Stephan202 d1204b1
Make `TaskListenerCompiler` a util
rickie 5440cf1
Add dot and `static` modifier
rickie 46faf0d
Configure `testFileManager` to output test files to
rickie 5406bd3
Move tests and apply suggestions
rickie 12b1008
Rename module and some classes
rickie 52492ea
Bump version
Stephan202 b85f222
PSM-1716 Suggestions
Stephan202 9dfc173
PSM-1716 Rename enum
Stephan202 485c85c
Wanna test this...
Stephan202 2f8dd30
Suggestions
Stephan202 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<?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> |
103 changes: 103 additions & 0 deletions
103
...ation-support/src/main/java/tech/picnic/errorprone/documentation/BugPatternExtractor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
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(); | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
...on-support/src/main/java/tech/picnic/errorprone/documentation/DocumentationGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
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. | ||
*/ | ||
@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); | ||
} | ||
} | ||
} |
90 changes: 90 additions & 0 deletions
90
...rc/main/java/tech/picnic/errorprone/documentation/DocumentationGeneratorTaskListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
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. | ||
*/ | ||
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", ""); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
documentation-support/src/main/java/tech/picnic/errorprone/documentation/Extractor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
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); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 This class doesn't actually generate documentation. Let me chew on an alternative name 🤔
(Likewise for
DocumentationGeneratorTaskListener
.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
K, let's defer this; will add an
XXX
comment.