diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml
index 839fc984..88a9008b 100644
--- a/.github/workflows/deploy-website.yml
+++ b/.github/workflows/deploy-website.yml
@@ -39,26 +39,30 @@ jobs:
www.bestpractices.dev:443
www.youtube.com:443
youtrack.jetbrains.com:443
- - name: Check out code
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - name: Check out code and set up JDK and Maven
+ uses: s4u/setup-maven-action@4f7fb9d9675e899ca81c6161dadbba0189a4ebb1 # v1.18.0
with:
- persist-credentials: false
+ java-version: 17.0.13
+ java-distribution: temurin
+ maven-version: 3.9.9
- uses: ruby/setup-ruby@1a615958ad9d422dd932dc1d5823942ee002799f # v1.227.0
with:
working-directory: ./website
bundler-cache: true
- name: Configure Github Pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
+ - name: Compile project and extract data
+ run: mvn -T1C clean install -DskipTests -Dverification.skip
- name: Generate documentation
- run: ./generate-docs.sh
+ run: mvn exec:java@generate-docs -pl documentation-support
- 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
+ # XXX: Bealdung and StackOverflow return HTTP 403 responses when run on
+ # a GitHub Action node.
+ run: bundle exec htmlproofer --no-check-external-hash --swap-url 'https\://error-prone.picnic.tech:' --ignore-urls '/^https:\/\/(www\.baeldung\.com|stackoverflow\.com)\/.*/' ./_site
- name: Upload website as artifact
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1
with:
diff --git a/documentation-support/pom.xml b/documentation-support/pom.xml
index 4c29bb57..45ebb996 100644
--- a/documentation-support/pom.xml
+++ b/documentation-support/pom.xml
@@ -28,10 +28,18 @@
com.fasterxml.jackson.core
jackson-annotations
+
+ com.fasterxml.jackson.core
+ jackson-core
+
com.fasterxml.jackson.core
jackson-databind
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+
com.fasterxml.jackson.datatype
jackson-datatype-guava
@@ -76,6 +84,10 @@
com.google.guava
guava
+
+ io.github.java-diff-utils
+ java-diff-utils
+
org.assertj
assertj-core
@@ -104,4 +116,29 @@
test
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+
+ generate-docs
+
+ java
+
+
+ tech.picnic.errorprone.documentation.JekyllCollectionGenerator
+
+ ${maven.multiModuleProjectDirectory}
+
+
+
+
+
+
+
+
diff --git a/documentation-support/src/main/java/tech/picnic/errorprone/documentation/JekyllCollectionGenerator.java b/documentation-support/src/main/java/tech/picnic/errorprone/documentation/JekyllCollectionGenerator.java
new file mode 100644
index 00000000..0482b776
--- /dev/null
+++ b/documentation-support/src/main/java/tech/picnic/errorprone/documentation/JekyllCollectionGenerator.java
@@ -0,0 +1,332 @@
+package tech.picnic.errorprone.documentation;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableListMultimap.flatteningToImmutableListMultimap;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.common.collect.ImmutableTable.toImmutableTable;
+import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.joining;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import com.github.difflib.DiffUtils;
+import com.github.difflib.UnifiedDiffUtils;
+import com.github.difflib.patch.Patch;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Sets;
+import com.google.errorprone.BugPattern.SeverityLevel;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+import org.jspecify.annotations.Nullable;
+import tech.picnic.errorprone.documentation.BugPatternExtractor.BugPatternDocumentation;
+import tech.picnic.errorprone.documentation.BugPatternTestExtractor.BugPatternTestCase;
+import tech.picnic.errorprone.documentation.BugPatternTestExtractor.BugPatternTestCases;
+import tech.picnic.errorprone.documentation.BugPatternTestExtractor.IdentificationTestEntry;
+import tech.picnic.errorprone.documentation.BugPatternTestExtractor.ReplacementTestEntry;
+import tech.picnic.errorprone.documentation.BugPatternTestExtractor.TestEntry;
+import tech.picnic.errorprone.documentation.RefasterRuleCollectionTestExtractor.RefasterTestCase;
+import tech.picnic.errorprone.documentation.RefasterRuleCollectionTestExtractor.RefasterTestCases;
+
+/**
+ * A command line utility that produces configuration files for the Jekyll-based Error Prone Support
+ * website.
+ */
+// XXX: Expand the class documentation.
+// XXX: Rename this class. Then also update the reference in `website/.gitignore`.
+// XXX: Now that we have bug checkers in multiple Maven modules, we should
+// likely document the source of each check on the website, perhaps even
+// grouping them by module.
+public final class JekyllCollectionGenerator {
+ // XXX: Find a bette name. Also, externalize this.
+ private static final PathMatcher PATH_MATCHER =
+ FileSystems.getDefault().getPathMatcher("glob:**/target/docs/*.json");
+
+ // XXX: Review class setup.
+ private JekyllCollectionGenerator() {}
+
+ /**
+ * Runs the application.
+ *
+ * @param args Arguments to the application; must specify the path to the Error Prone Support
+ * project root, and nothing else.
+ * @throws IOException If any file could not be read or written.
+ */
+ public static void main(String[] args) throws IOException {
+ checkArgument(args.length == 1, "Precisely one project root path must be provided");
+ Path projectRoot = Path.of(args[0]).toAbsolutePath();
+
+ generateIndex(projectRoot);
+ PageGenerator.apply(projectRoot);
+ }
+
+ private static void generateIndex(Path projectRoot) throws IOException {
+ try (BufferedWriter writer =
+ Files.newBufferedWriter(projectRoot.resolve("website").resolve("index.md"), UTF_8)) {
+ writer.write("---");
+ writer.newLine();
+ writer.write("layout: default");
+ writer.newLine();
+ writer.write("title: Home");
+ writer.newLine();
+ writer.write("nav_order: 1");
+ writer.newLine();
+ writer.write("---");
+ writer.newLine();
+ writer.write(
+ Files.readString(projectRoot.resolve("README.md")).replace("=\"website/", "=\""));
+ }
+ }
+
+ // XXX: Review this class should be split in two: one for bug patterns and one for Refaster rules.
+ private static final class PageGenerator extends SimpleFileVisitor {
+ private static final Splitter LINE_SPLITTER = Splitter.on(System.lineSeparator());
+ private static final YAMLMapper YAML_MAPPER =
+ YAMLMapper.builder()
+ .visibility(PropertyAccessor.FIELD, Visibility.ANY)
+ .disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET)
+ .enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
+ .enable(YAMLGenerator.Feature.USE_PLATFORM_LINE_BREAKS)
+ .build();
+
+ private final List bugPatterns = new ArrayList<>();
+ private final List bugPatternTests = new ArrayList<>();
+ private final List refasterRuleCollectionTests = new ArrayList<>();
+
+ static void apply(Path projectRoot) throws IOException {
+ PageGenerator pageGenerator = new PageGenerator();
+ Files.walkFileTree(projectRoot, pageGenerator);
+ pageGenerator.writePages(projectRoot);
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ if (!PATH_MATCHER.matches(file)) {
+ return FileVisitResult.CONTINUE;
+ }
+
+ // XXX: If we use a consistent ID separator, then this can become a switch statement. Now we
+ // depend on evaluation order.
+ // XXX: Alternatively, use polymorphism and let Jackson figure it out.
+ // XXX: If we stick with an ID-based approach, then deduplicate the ID references here and in
+ // the `Extractor` implementations.
+ String fileName = file.getFileName().toString();
+ if (fileName.startsWith("bugpattern-test")) {
+ bugPatternTests.add(Json.read(file, BugPatternTestCases.class));
+ } else if (fileName.startsWith("bugpattern")) {
+ bugPatterns.add(Json.read(file, BugPatternDocumentation.class));
+ } else if (fileName.startsWith("refaster-rule-collection-test")) {
+ refasterRuleCollectionTests.add(Json.read(file, RefasterTestCases.class));
+ } else {
+ // XXX: Handle differently?
+ throw new IllegalStateException("Unexpected file: " + fileName);
+ }
+
+ return FileVisitResult.CONTINUE;
+ }
+
+ private void writePages(Path projectRoot) throws IOException {
+ Path website = projectRoot.resolve("website");
+ writePages(
+ website.resolve("_bugpatterns"),
+ getJekyllBugPatternDescriptions(projectRoot),
+ JekyllBugPatternDescription::name);
+ writePages(
+ website.resolve("_refasterrules"),
+ getJekyllRefasterRuleCollectionDescription(),
+ JekyllRefasterRuleCollectionDescription::name);
+ }
+
+ private static void writePages(
+ Path directory, ImmutableList documents, Function nameExtractor)
+ throws IOException {
+ for (T document : documents) {
+ Files.createDirectories(directory);
+ try (BufferedWriter writer =
+ Files.newBufferedWriter(
+ directory.resolve(nameExtractor.apply(document) + ".md"), UTF_8)) {
+ YAML_MAPPER.writeValue(writer, document);
+ writer.write("---");
+ writer.newLine();
+ }
+ }
+ }
+
+ private ImmutableList getJekyllBugPatternDescriptions(
+ Path projectRoot) {
+ ImmutableListMultimap bugPatternTestCases =
+ bugPatternTests.stream()
+ .flatMap(testCases -> testCases.testCases().stream())
+ .collect(
+ flatteningToImmutableListMultimap(
+ BugPatternTestCase::classUnderTest, t -> t.entries().stream()));
+
+ return bugPatterns.stream()
+ .map(
+ b ->
+ new AutoValue_JekyllCollectionGenerator_JekyllBugPatternDescription(
+ b.name(),
+ b.name(),
+ b.summary(),
+ b.severityLevel(),
+ b.tags(),
+ // XXX: Derive `Path` from filesytem.
+ projectRoot.relativize(Path.of(b.source())).toString(),
+ bugPatternTestCases.get(b.fullyQualifiedName()).stream()
+ .filter(t -> t.type() == TestEntry.TestType.IDENTIFICATION)
+ .map(t -> ((IdentificationTestEntry) t).code())
+ .collect(toImmutableList()),
+ bugPatternTestCases.get(b.fullyQualifiedName()).stream()
+ .filter(t -> t.type() == TestEntry.TestType.REPLACEMENT)
+ .map(t -> generateDiff((ReplacementTestEntry) t))
+ .collect(toImmutableList())))
+ .collect(toImmutableList());
+ }
+
+ private ImmutableList
+ getJekyllRefasterRuleCollectionDescription() {
+ ImmutableTable> refasterTests =
+ refasterRuleCollectionTests.stream()
+ .collect(
+ toImmutableTable(
+ RefasterTestCases::ruleCollection,
+ RefasterTestCases::isInput,
+ RefasterTestCases::testCases));
+
+ return refasterTests.rowMap().entrySet().stream()
+ .map(
+ c ->
+ new AutoValue_JekyllCollectionGenerator_JekyllRefasterRuleCollectionDescription(
+ c.getKey(),
+ c.getKey(),
+ // XXX: Derive severity from input.
+ SUGGESTION,
+ // XXX: Derive tags from input (or drop this feature).
+ ImmutableList.of("Simplification"),
+ // XXX: Derive source location from input.
+ String.format(
+ "error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/%s.java",
+ c.getKey()),
+ getRules(c.getValue().get(true), c.getValue().get(false))))
+ .collect(toImmutableList());
+ }
+
+ private static ImmutableList getRules(
+ @Nullable List inputTests, @Nullable List outputTests) {
+ ImmutableMap inputs = indexRefasterTestData(inputTests);
+ ImmutableMap outputs = indexRefasterTestData(outputTests);
+
+ return Sets.intersection(inputs.keySet(), outputs.keySet()).stream()
+ .map(
+ name ->
+ new AutoValue_JekyllCollectionGenerator_JekyllRefasterRuleCollectionDescription_Rule(
+ name,
+ // XXX: Derive severity from input.
+ SUGGESTION,
+ // XXX: Derive tags from input (or drop this feature).
+ ImmutableList.of("Simplification"),
+ generateDiff(
+ requireNonNull(inputs.get(name), "Input"),
+ requireNonNull(outputs.get(name), "Output"))))
+ .collect(toImmutableList());
+ }
+
+ private static ImmutableMap indexRefasterTestData(
+ @Nullable List data) {
+ return data == null
+ ? ImmutableMap.of()
+ : data.stream()
+ .collect(toImmutableMap(RefasterTestCase::name, RefasterTestCase::content));
+ }
+
+ private static String generateDiff(ReplacementTestEntry testEntry) {
+ return generateDiff(testEntry.input(), testEntry.output());
+ }
+
+ private static String generateDiff(String before, String after) {
+ // XXX: Extract splitter.
+ List originalLines = LINE_SPLITTER.splitToList(before);
+ List replacementLines = LINE_SPLITTER.splitToList(after);
+
+ Patch diff = DiffUtils.diff(originalLines, replacementLines);
+
+ return UnifiedDiffUtils.generateUnifiedDiff(
+ "", "", originalLines, diff, Integer.MAX_VALUE / 2)
+ .stream()
+ .skip(3)
+ .collect(joining(System.lineSeparator()));
+ }
+ }
+
+ @AutoValue
+ abstract static class JekyllBugPatternDescription {
+ // XXX: Make this a derived property?
+ abstract String title();
+
+ abstract String name();
+
+ abstract String summary();
+
+ abstract SeverityLevel severity();
+
+ abstract ImmutableList tags();
+
+ // XXX: The documentation could link to the original test code. Perhaps even with the correct
+ // line numbers.
+ abstract String source();
+
+ // XXX: The `identification` and `replacement` fields have odd names.
+ abstract ImmutableList identification();
+
+ abstract ImmutableList replacement();
+ }
+
+ @AutoValue
+ abstract static class JekyllRefasterRuleCollectionDescription {
+ // XXX: Make this a derived property?
+ abstract String title();
+
+ abstract String name();
+
+ abstract SeverityLevel severity();
+
+ abstract ImmutableList tags();
+
+ // XXX: The documentation could link to the original test code. Perhaps even with the correct
+ // line numbers. If we do this, we should do the same for individual rules.
+ abstract String source();
+
+ abstract ImmutableList rules();
+
+ @AutoValue
+ abstract static class Rule {
+ abstract String name();
+
+ abstract SeverityLevel severity();
+
+ abstract ImmutableList tags();
+
+ abstract String diff();
+ }
+ }
+}
diff --git a/documentation-support/src/main/java/tech/picnic/errorprone/documentation/models/RefasterTemplateCollectionData.java b/documentation-support/src/main/java/tech/picnic/errorprone/documentation/models/RefasterTemplateCollectionData.java
new file mode 100644
index 00000000..3747c3fd
--- /dev/null
+++ b/documentation-support/src/main/java/tech/picnic/errorprone/documentation/models/RefasterTemplateCollectionData.java
@@ -0,0 +1,27 @@
+package tech.picnic.errorprone.documentation.models;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Object containing all data related to a Refaster template collection. This is solely used for
+ * serialization.
+ */
+// XXX: This class is not yet used.
+@AutoValue
+@JsonDeserialize(as = AutoValue_RefasterTemplateCollectionData.class)
+abstract class RefasterTemplateCollectionData {
+ static RefasterTemplateCollectionData create(
+ String name, String description, String link, ImmutableList templates) {
+ return new AutoValue_RefasterTemplateCollectionData(name, description, link, templates);
+ }
+
+ abstract String name();
+
+ abstract String description();
+
+ abstract String link();
+
+ abstract ImmutableList templates();
+}
diff --git a/documentation-support/src/main/java/tech/picnic/errorprone/documentation/models/RefasterTemplateData.java b/documentation-support/src/main/java/tech/picnic/errorprone/documentation/models/RefasterTemplateData.java
new file mode 100644
index 00000000..f3bce291
--- /dev/null
+++ b/documentation-support/src/main/java/tech/picnic/errorprone/documentation/models/RefasterTemplateData.java
@@ -0,0 +1,23 @@
+package tech.picnic.errorprone.documentation.models;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.auto.value.AutoValue;
+import com.google.errorprone.BugPattern.SeverityLevel;
+
+// XXX: This class is not yet used.
+@AutoValue
+@JsonDeserialize(as = AutoValue_RefasterTemplateData.class)
+abstract class RefasterTemplateData {
+ 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();
+}
diff --git a/documentation-support/src/test/java/tech/picnic/errorprone/documentation/JekyllCollectionGeneratorTest.java b/documentation-support/src/test/java/tech/picnic/errorprone/documentation/JekyllCollectionGeneratorTest.java
new file mode 100644
index 00000000..547078cc
--- /dev/null
+++ b/documentation-support/src/test/java/tech/picnic/errorprone/documentation/JekyllCollectionGeneratorTest.java
@@ -0,0 +1,13 @@
+package tech.picnic.errorprone.documentation;
+
+import java.io.IOException;
+import org.junit.jupiter.api.Test;
+
+// XXX: Implement tests.
+final class JekyllCollectionGeneratorTest {
+ @Test
+ void foo() throws IOException {
+ JekyllCollectionGenerator.main(
+ new String[] {"/home/sschroevers/workspace/picnic/error-prone-support"});
+ }
+}
diff --git a/generate-docs.sh b/generate-docs.sh
deleted file mode 100755
index 1caf0f3d..00000000
--- a/generate-docs.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env bash
-
-set -e -u -o pipefail
-
-REPOSITORY_ROOT="$(git rev-parse --show-toplevel)"
-WEBSITE_ROOT="${REPOSITORY_ROOT}/website"
-
-generate_homepage() {
- local homepage="${WEBSITE_ROOT}/index.md"
-
- echo "Generating ${homepage}..."
- cat - "${REPOSITORY_ROOT}/README.md" > "${homepage}" << EOF
----
-layout: default
-title: Home
-nav_order: 1
----
-EOF
-
- local macos_compat=""
- [[ "${OSTYPE}" == "darwin"* ]] && macos_compat="yes"
- sed -i ${macos_compat:+".bak"} 's/src="website\//src="/g' "${homepage}"
- sed -i ${macos_compat:+".bak"} 's/srcset="website\//srcset="/g' "${homepage}"
-}
-
-# Generate the website.
-generate_homepage
diff --git a/pom.xml b/pom.xml
index b144221c..da557c95 100644
--- a/pom.xml
+++ b/pom.xml
@@ -360,6 +360,11 @@
nullaway
${version.nullaway}
+
+ io.github.java-diff-utils
+ java-diff-utils
+ 4.12
+
io.micrometer
micrometer-bom
@@ -1807,6 +1812,11 @@
org.apache.maven.plugins
maven-enforcer-plugin
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.4.1
+
org.codehaus.mojo
license-maven-plugin
diff --git a/website/.gitignore b/website/.gitignore
index 7caf6d3d..9bd53d5a 100644
--- a/website/.gitignore
+++ b/website/.gitignore
@@ -1,12 +1,13 @@
# Generated by Bundler and Jekyll.
.bundle/
-Gemfile.lock
.jekyll-cache/
.jekyll-metadata
.sass-cache/
_site/
vendor/
-# Generated by `../generate-docs.sh`.
-*.bak
+# XXX: Update generator name when it's renamed.
+# Generated by `JekyllCollectionGenerator`.
index.md
+_bugpatterns/
+_refasterrules/
diff --git a/website/.ruby-version b/website/.ruby-version
index ef538c28..fa7adc7a 100644
--- a/website/.ruby-version
+++ b/website/.ruby-version
@@ -1 +1 @@
-3.1.2
+3.3.5
diff --git a/website/Gemfile b/website/Gemfile
index 734e7a14..2d652b4b 100644
--- a/website/Gemfile
+++ b/website/Gemfile
@@ -1,8 +1,27 @@
ruby File.read(".ruby-version").strip
source "https://rubygems.org"
-gem "html-proofer", "4.4.1"
-gem "jekyll", "4.2.2"
-gem "jekyll-sitemap", "1.4"
-gem "just-the-docs", "0.4.0.rc2"
-gem "webrick", "1.7"
+
+# XXX: This dependency group is declared to suppress Ruby depreciation
+# warnings. Drop once redundant.
+group :standard_library_replacements do
+ gem "base64", "0.2.0"
+ gem "csv", "3.3.0"
+ gem "logger", "1.6.1"
+end
+
+group :jekyll_site_dependencies do
+ gem "jekyll", "4.3.4"
+ gem "jekyll-sitemap", "1.4.0"
+ gem "just-the-docs", "0.10.0"
+ gem "rake", "13.2.1"
+ # XXX: Drop this `sass-embedded` version pinning once various parts of the
+ # ecosystem caught up. See
+ # https://github.com/just-the-docs/just-the-docs/issues/1541#issuecomment-2401649789.
+ gem "sass-embedded", "1.78.0"
+ gem "webrick", "1.9.0"
+end
+
+group :website_validation_dependencies do
+ gem "html-proofer", "5.0.9"
+end
diff --git a/website/Gemfile.lock b/website/Gemfile.lock
new file mode 100644
index 00000000..0625ee1b
--- /dev/null
+++ b/website/Gemfile.lock
@@ -0,0 +1,211 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ Ascii85 (2.0.1)
+ addressable (2.8.7)
+ public_suffix (>= 2.0.2, < 7.0)
+ afm (0.2.2)
+ async (2.18.0)
+ console (~> 1.26)
+ fiber-annotation
+ io-event (~> 1.6, >= 1.6.5)
+ base64 (0.2.0)
+ bigdecimal (3.1.8)
+ colorator (1.1.0)
+ concurrent-ruby (1.3.4)
+ console (1.27.0)
+ fiber-annotation
+ fiber-local (~> 1.1)
+ json
+ csv (3.3.0)
+ em-websocket (0.5.3)
+ eventmachine (>= 0.12.9)
+ http_parser.rb (~> 0)
+ ethon (0.16.0)
+ ffi (>= 1.15.0)
+ eventmachine (1.2.7)
+ ffi (1.17.0-aarch64-linux-gnu)
+ ffi (1.17.0-aarch64-linux-musl)
+ ffi (1.17.0-arm-linux-gnu)
+ ffi (1.17.0-arm-linux-musl)
+ ffi (1.17.0-arm64-darwin)
+ ffi (1.17.0-x86-linux-gnu)
+ ffi (1.17.0-x86-linux-musl)
+ ffi (1.17.0-x86_64-darwin)
+ ffi (1.17.0-x86_64-linux-gnu)
+ ffi (1.17.0-x86_64-linux-musl)
+ fiber-annotation (0.2.0)
+ fiber-local (1.1.0)
+ fiber-storage
+ fiber-storage (1.0.0)
+ forwardable-extended (2.6.0)
+ google-protobuf (4.28.3)
+ bigdecimal
+ rake (>= 13)
+ google-protobuf (4.28.3-aarch64-linux)
+ bigdecimal
+ rake (>= 13)
+ google-protobuf (4.28.3-arm64-darwin)
+ bigdecimal
+ rake (>= 13)
+ google-protobuf (4.28.3-x86-linux)
+ bigdecimal
+ rake (>= 13)
+ google-protobuf (4.28.3-x86_64-darwin)
+ bigdecimal
+ rake (>= 13)
+ google-protobuf (4.28.3-x86_64-linux)
+ bigdecimal
+ rake (>= 13)
+ hashery (2.1.2)
+ html-proofer (5.0.9)
+ addressable (~> 2.3)
+ async (~> 2.1)
+ nokogiri (~> 1.13)
+ pdf-reader (~> 2.11)
+ rainbow (~> 3.0)
+ typhoeus (~> 1.3)
+ yell (~> 2.0)
+ zeitwerk (~> 2.5)
+ http_parser.rb (0.8.0)
+ i18n (1.14.6)
+ concurrent-ruby (~> 1.0)
+ io-event (1.7.3)
+ jekyll (4.3.4)
+ addressable (~> 2.4)
+ colorator (~> 1.0)
+ em-websocket (~> 0.5)
+ i18n (~> 1.0)
+ jekyll-sass-converter (>= 2.0, < 4.0)
+ jekyll-watch (~> 2.0)
+ kramdown (~> 2.3, >= 2.3.1)
+ kramdown-parser-gfm (~> 1.0)
+ liquid (~> 4.0)
+ mercenary (>= 0.3.6, < 0.5)
+ pathutil (~> 0.9)
+ rouge (>= 3.0, < 5.0)
+ safe_yaml (~> 1.0)
+ terminal-table (>= 1.8, < 4.0)
+ webrick (~> 1.7)
+ jekyll-include-cache (0.2.1)
+ jekyll (>= 3.7, < 5.0)
+ jekyll-sass-converter (3.0.0)
+ sass-embedded (~> 1.54)
+ jekyll-seo-tag (2.8.0)
+ jekyll (>= 3.8, < 5.0)
+ jekyll-sitemap (1.4.0)
+ jekyll (>= 3.7, < 5.0)
+ jekyll-watch (2.2.1)
+ listen (~> 3.0)
+ json (2.7.5)
+ just-the-docs (0.10.0)
+ jekyll (>= 3.8.5)
+ jekyll-include-cache
+ jekyll-seo-tag (>= 2.0)
+ rake (>= 12.3.1)
+ kramdown (2.4.0)
+ rexml
+ kramdown-parser-gfm (1.1.0)
+ kramdown (~> 2.0)
+ liquid (4.0.4)
+ listen (3.9.0)
+ rb-fsevent (~> 0.10, >= 0.10.3)
+ rb-inotify (~> 0.9, >= 0.9.10)
+ logger (1.6.1)
+ mercenary (0.4.0)
+ nokogiri (1.16.7-aarch64-linux)
+ racc (~> 1.4)
+ nokogiri (1.16.7-arm-linux)
+ racc (~> 1.4)
+ nokogiri (1.16.7-arm64-darwin)
+ racc (~> 1.4)
+ nokogiri (1.16.7-x86-linux)
+ racc (~> 1.4)
+ nokogiri (1.16.7-x86_64-darwin)
+ racc (~> 1.4)
+ nokogiri (1.16.7-x86_64-linux)
+ racc (~> 1.4)
+ pathutil (0.16.2)
+ forwardable-extended (~> 2.6)
+ pdf-reader (2.13.0)
+ Ascii85 (>= 1.0, < 3.0, != 2.0.0)
+ afm (~> 0.2.1)
+ hashery (~> 2.0)
+ ruby-rc4
+ ttfunk
+ public_suffix (6.0.1)
+ racc (1.8.1)
+ rainbow (3.1.1)
+ rake (13.2.1)
+ rb-fsevent (0.11.2)
+ rb-inotify (0.11.1)
+ ffi (~> 1.0)
+ rexml (3.3.9)
+ rouge (4.4.0)
+ ruby-rc4 (0.1.5)
+ safe_yaml (1.0.5)
+ sass-embedded (1.78.0-aarch64-linux-gnu)
+ google-protobuf (~> 4.27)
+ sass-embedded (1.78.0-aarch64-linux-musl)
+ google-protobuf (~> 4.27)
+ sass-embedded (1.78.0-arm-linux-gnueabihf)
+ google-protobuf (~> 4.27)
+ sass-embedded (1.78.0-arm-linux-musleabihf)
+ google-protobuf (~> 4.27)
+ sass-embedded (1.78.0-arm64-darwin)
+ google-protobuf (~> 4.27)
+ sass-embedded (1.78.0-x86-linux-gnu)
+ google-protobuf (~> 4.27)
+ sass-embedded (1.78.0-x86-linux-musl)
+ google-protobuf (~> 4.27)
+ sass-embedded (1.78.0-x86_64-darwin)
+ google-protobuf (~> 4.27)
+ sass-embedded (1.78.0-x86_64-linux-gnu)
+ google-protobuf (~> 4.27)
+ sass-embedded (1.78.0-x86_64-linux-musl)
+ google-protobuf (~> 4.27)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
+ ttfunk (1.8.0)
+ bigdecimal (~> 3.1)
+ typhoeus (1.4.1)
+ ethon (>= 0.9.0)
+ unicode-display_width (2.6.0)
+ webrick (1.9.0)
+ yell (2.2.2)
+ zeitwerk (2.7.1)
+
+PLATFORMS
+ aarch64-linux
+ aarch64-linux-gnu
+ aarch64-linux-musl
+ arm-linux
+ arm-linux-gnu
+ arm-linux-gnueabihf
+ arm-linux-musl
+ arm-linux-musleabihf
+ arm64-darwin
+ x86-linux
+ x86-linux-gnu
+ x86-linux-musl
+ x86_64-darwin
+ x86_64-linux-gnu
+ x86_64-linux-musl
+
+DEPENDENCIES
+ base64 (= 0.2.0)
+ csv (= 3.3.0)
+ html-proofer (= 5.0.9)
+ jekyll (= 4.3.4)
+ jekyll-sitemap (= 1.4.0)
+ just-the-docs (= 0.10.0)
+ logger (= 1.6.1)
+ rake (= 13.2.1)
+ sass-embedded (= 1.78.0)
+ webrick (= 1.9.0)
+
+RUBY VERSION
+ ruby 3.3.5p100
+
+BUNDLED WITH
+ 2.5.16
diff --git a/website/README.md b/website/README.md
index 49111ad1..b22bb7c4 100644
--- a/website/README.md
+++ b/website/README.md
@@ -7,18 +7,30 @@ statically generated using [Jekyll][jekyll].
# Local development
To view the website on `localhost`, first follow the [Jekyll installation
-instructions][jekyll-docs-installation]. Once done, in this directory execute:
+instructions][jekyll-docs-installation]. Once done, run the following Maven
+commands in the root of the repository to extract the (test) data from the bug
+patterns and Refaster rule collections and to transform this data into a
+Jekyll-digestible format. Unless and relevant Java code has been changed, these
+commands needs to be executed once.
+
+```sh
+mvn -T1C clean install -DskipTests -Dverification.skip
+mvn exec:java@generate-docs -pl documentation-support
+```
+
+Then to build the website for local development, execute in this directory:
```sh
bundle install
-../generate-docs.sh && bundle exec jekyll serve --livereload
+bundle exec jekyll serve --livereload
```
The website will now be [available][localhost-port-4000] on port 4000. Source
-code modifications (including the result of rerunning `../generate-docs.sh`)
-will automatically be reflected. (An exception is `_config.yml`: changes to
-this file require a server restart.) Subsequent server restarts do not require
-running `bundle install`, unless `Gemfile` has been updated in the interim.
+code modifications (including the result of rerunning `mvn
+exec:java@generate-docs -pl documentation-support`) will automatically be
+reflected. (An exception is `_config.yml`: changes to this file require a
+server restart.) Subsequent server restarts do not require running `bundle
+install`, unless `Gemfile` has been updated in the interim.
If you are not familiar with Jekyll, be sure to check out its
[documentation][jekyll-docs]. It is recommended to follow the provided
diff --git a/website/_config.yml b/website/_config.yml
index ac900637..c0c53f71 100644
--- a/website/_config.yml
+++ b/website/_config.yml
@@ -8,15 +8,40 @@ description: >-
theme: just-the-docs
plugins:
- - jekyll-sitemap
+- jekyll-sitemap
# Files and directories not to be deployed through GitHub pages.
exclude:
- - Gemfile
- - Gemfile.lock
- - generate-version-compatibility-overview.sh
- - README.md
- - vendor
+- Gemfile
+- Gemfile.lock
+- generate-version-compatibility-overview.sh
+- README.md
+- vendor
+
+collections:
+ bugpatterns:
+ output: true
+ refasterrules:
+ output: true
+
+defaults:
+- scope:
+ type: "bugpatterns"
+ values:
+ layout: "bugpattern"
+- scope:
+ type: "refasterrules"
+ values:
+ layout: "refasterrule"
+
+just_the_docs:
+ collections:
+ bugpatterns:
+ name: Bug Patterns
+ nav_fold: true
+ refasterrules:
+ name: Refaster Rules
+ nav_fold: true
# See https://jekyllrb.com/docs/permalinks/#built-in-formats.
permalink: pretty
@@ -25,9 +50,9 @@ permalink: pretty
# See
# https://just-the-docs.github.io/just-the-docs/docs/navigation-structure/#external-navigation-links.
nav_external_links:
- - title: Error Prone Support on GitHub
- url: https://github.com/PicnicSupermarket/error-prone-support
- hide_icon: false
+- title: Error Prone Support on GitHub
+ url: https://github.com/PicnicSupermarket/error-prone-support
+ hide_icon: false
callouts:
summary:
@@ -40,9 +65,9 @@ callouts:
social:
name: Picnic
links:
- - https://github.com/PicnicSupermarket
- - https://twitter.com/picnic
- - https://www.linkedin.com/company/picnictechnologies
+ - https://github.com/PicnicSupermarket
+ - https://twitter.com/picnic
+ - https://www.linkedin.com/company/picnictechnologies
twitter:
username: picnic
card: summary
diff --git a/website/_data/severities.yml b/website/_data/severities.yml
new file mode 100644
index 00000000..7c0749ba
--- /dev/null
+++ b/website/_data/severities.yml
@@ -0,0 +1,6 @@
+ERROR:
+ color: red
+WARNING:
+ color: yellow
+SUGGESTION:
+ color: green
diff --git a/website/_layouts/bugpattern.md b/website/_layouts/bugpattern.md
new file mode 100644
index 00000000..e11239b5
--- /dev/null
+++ b/website/_layouts/bugpattern.md
@@ -0,0 +1,94 @@
+---
+# XXX: To be implemented:
+# - Support for alt names.
+# - Support for explanations.
+# - Support for "can disable".
+# - Support for custom suppression annotations.
+layout: default
+---
+
+{% capture markdown_layout %}
+
+# {{ page.name }}
+
+{{ page.severity }}
+ {: .label .label-{{ site.data.severities[page.severity].color }} }
+
+{% for tag in page.tags %}
+{{ tag }}
+ {: .label }
+{% endfor %}
+
+
+ View source code on GitHub
+
+
+
+{: .summary-title }
+> Summary
+>
+> {{ page.summary }}
+
+{% comment %}
+ # XXX: Here, include a more elaborate explantion, if available.
+{% endcomment %}
+
+{: .note-title }
+> Suppression
+>
+> Suppress false positives by adding the suppression annotation `@SuppressWarnings("{{ page.name }}")` to
+> the enclosing element.
+>
+> Disable this pattern completely by adding `-Xep:{{ page.name }}:OFF` as compiler argument.
+> [Learn more][error-prone-flags].
+{% comment %}
+ # XXX: Create an internal page on documenting the usage of compiler flags.
+{% endcomment %}
+
+{% if page.replacement or page.identification %}
+
+## Samples
+
+{% comment %}
+ # XXX: Either make this "Samples" header useful, or drop it. (In which case
+ # the wrapping conjunctive guard should also go.)
+{% endcomment %}
+
+{% if page.replacement %}
+
+### Replacement
+
+Shows the difference in example code before and after the bug pattern is
+applied.
+
+{% for diff in page.replacement %}
+{% highlight diff %}
+{{ diff }}
+{% endhighlight %}
+{% endfor %}
+
+{% endif %}
+
+{% if page.identification %}
+
+### Identification
+
+Shows code lines which will (not) be flagged by this bug pattern. \
+A `//BUG: Diagnostic contains:` comment is placed above any violating line.
+
+{% for source in page.identification %}
+{% highlight java %}
+{{ source }}
+{% endhighlight %}
+{% endfor %}
+
+{% endif %}
+
+{% endif %}
+
+[error-prone-flags]: https://errorprone.info/docs/flags
+
+{% endcapture %}
+{{ markdown_layout | markdownify }}
diff --git a/website/_layouts/refasterrule.md b/website/_layouts/refasterrule.md
new file mode 100644
index 00000000..4afc86f5
--- /dev/null
+++ b/website/_layouts/refasterrule.md
@@ -0,0 +1,78 @@
+---
+layout: default
+---
+
+{% capture markdown_layout %}
+
+# {{ page.name }}
+{: .no_toc }
+
+{{ page.severity }}
+ {: .label .label-{{ site.data.severities[page.severity].color }} }
+
+{% for tag in page.tags %}
+{{ tag }}
+ {: .label }
+{% endfor %}
+
+
+ View source code on GitHub
+
+
+
+{: .note-title }
+> Suppression
+>
+> Disable all rules by adding `-XepOpt:Refaster:NamePattern=^(?!{{page.name}}\$).*` as
+> compiler argument.
+{% comment %}
+ # XXX: Create an internal page on documenting the usage of compiler flags.
+{% endcomment %}
+
+
+
+ Table of contents
+
+ {: .text-delta }
+ 1. TOC
+ {:toc}
+
+
+{% for rule in page.rules %}
+## {{rule.name}}
+
+{{ page.severity }}
+ {: .label .label-{{ site.data.severities[rule.severity].color }} }
+
+{% for tag in rule.tags %}
+{{ tag }}
+ {: .label }
+{% endfor %}
+
+{: .note-title }
+> Suppression
+>
+> Suppress false positives by adding the suppression annotation `@SuppressWarnings("{{rule.name}}")` to
+> the enclosing element.
+>
+> Disable this rule by adding `-XepOpt:Refaster:NamePattern=^(?!{{page.name}}\${{rule.name}}).*`
+> as compiler argument.
+{% comment %}
+ # XXX: Create an internal page on documenting the usage of compiler flags.
+{% endcomment %}
+
+### Samples
+{: .no_toc .text-delta }
+
+Shows the difference in example code before and after the Refaster rule is
+applied.
+
+{% highlight diff %}
+{{ rule.diff }}
+{% endhighlight %}
+{% endfor %}
+
+{% endcapture %}
+{{ markdown_layout | markdownify }}
diff --git a/website/_sass/color_schemes/_variables.scss b/website/_sass/color_schemes/_variables.scss
index fe5b7683..e8ccfe85 100644
--- a/website/_sass/color_schemes/_variables.scss
+++ b/website/_sass/color_schemes/_variables.scss
@@ -2,4 +2,4 @@
// https://github.com/just-the-docs/just-the-docs/blob/main/_sass/support/_variables.scss.
// Grid system.
-$nav-width: 400px;
+$nav-width: 25rem;
diff --git a/website/bugpatterns.md b/website/bugpatterns.md
deleted file mode 100644
index 51ce7bf7..00000000
--- a/website/bugpatterns.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-layout: default
-title: Bug Patterns
-nav_order: 2
-has_children: true
----
-
-# Bug Patterns
diff --git a/website/refasterrules.md b/website/refasterrules.md
deleted file mode 100644
index bf1d43dc..00000000
--- a/website/refasterrules.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-layout: default
-title: Refaster Rules
-nav_order: 2
-has_children: true
----
-
-# Refaster Rules