Compare commits

..

2 Commits

24 changed files with 165 additions and 928 deletions

View File

@@ -39,30 +39,26 @@ jobs:
www.bestpractices.dev:443
www.youtube.com:443
youtrack.jetbrains.com:443
- name: Check out code and set up JDK and Maven
uses: s4u/setup-maven-action@4f7fb9d9675e899ca81c6161dadbba0189a4ebb1 # v1.18.0
- name: Check out code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
java-version: 17.0.13
java-distribution: temurin
maven-version: 3.9.9
persist-credentials: false
- 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: mvn exec:java@generate-docs -pl documentation-support
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: 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
# 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
- name: Upload website as artifact
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1
with:

View File

@@ -28,18 +28,10 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-guava</artifactId>
@@ -84,10 +76,6 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
@@ -116,29 +104,4 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-docs</id>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>tech.picnic.errorprone.documentation.JekyllCollectionGenerator</mainClass>
<arguments>
<argument>${maven.multiModuleProjectDirectory}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -1,332 +0,0 @@
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<Path> {
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<BugPatternDocumentation> bugPatterns = new ArrayList<>();
private final List<BugPatternTestCases> bugPatternTests = new ArrayList<>();
private final List<RefasterTestCases> 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 <T> void writePages(
Path directory, ImmutableList<T> documents, Function<T, String> 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<JekyllBugPatternDescription> getJekyllBugPatternDescriptions(
Path projectRoot) {
ImmutableListMultimap<String, TestEntry> 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<JekyllRefasterRuleCollectionDescription>
getJekyllRefasterRuleCollectionDescription() {
ImmutableTable<String, Boolean, List<RefasterTestCase>> 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<JekyllRefasterRuleCollectionDescription.Rule> getRules(
@Nullable List<RefasterTestCase> inputTests, @Nullable List<RefasterTestCase> outputTests) {
ImmutableMap<String, String> inputs = indexRefasterTestData(inputTests);
ImmutableMap<String, String> 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<String, String> indexRefasterTestData(
@Nullable List<RefasterTestCase> 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<String> originalLines = LINE_SPLITTER.splitToList(before);
List<String> replacementLines = LINE_SPLITTER.splitToList(after);
Patch<String> 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<String> 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<String> identification();
abstract ImmutableList<String> replacement();
}
@AutoValue
abstract static class JekyllRefasterRuleCollectionDescription {
// XXX: Make this a derived property?
abstract String title();
abstract String name();
abstract SeverityLevel severity();
abstract ImmutableList<String> 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<Rule> rules();
@AutoValue
abstract static class Rule {
abstract String name();
abstract SeverityLevel severity();
abstract ImmutableList<String> tags();
abstract String diff();
}
}
}

View File

@@ -1,27 +0,0 @@
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<RefasterTemplateData> templates) {
return new AutoValue_RefasterTemplateCollectionData(name, description, link, templates);
}
abstract String name();
abstract String description();
abstract String link();
abstract ImmutableList<RefasterTemplateData> templates();
}

View File

@@ -1,23 +0,0 @@
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();
}

View File

@@ -1,13 +0,0 @@
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"});
}
}

View File

@@ -0,0 +1,47 @@
package tech.picnic.errorprone.refasterrules;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import org.springframework.test.json.JsonCompareMode;
import org.springframework.test.web.reactive.server.WebTestClient.BodyContentSpec;
import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation;
/** Refaster rules related to Spring Test expressions and statements. */
@OnlineDocumentation
final class SpringTestRules {
private SpringTestRules() {}
/**
* Prefer {@link BodyContentSpec#json(String, JsonCompareMode)} over alternatives that implicitly
* perform a {@link JsonCompareMode#LENIENT lenient} comparison or are deprecated.
*/
static final class BodyContentSpecJsonLenient {
@BeforeTemplate
@SuppressWarnings("deprecation" /* This deprecated method invocation will be rewritten. */)
BodyContentSpec before(BodyContentSpec spec, String expectedJson) {
return Refaster.anyOf(spec.json(expectedJson), spec.json(expectedJson, /* strict= */ false));
}
@AfterTemplate
BodyContentSpec after(BodyContentSpec spec, String expectedJson) {
return spec.json(expectedJson, JsonCompareMode.LENIENT);
}
}
/**
* Prefer {@link BodyContentSpec#json(String, JsonCompareMode)} over the deprecated alternative.
*/
static final class BodyContentSpecJsonStrict {
@BeforeTemplate
@SuppressWarnings("deprecation" /* This deprecated method invocation will be rewritten. */)
BodyContentSpec before(BodyContentSpec spec, String expectedJson) {
return spec.json(expectedJson, /* strict= */ true);
}
@AfterTemplate
BodyContentSpec after(BodyContentSpec spec, String expectedJson) {
return spec.json(expectedJson, JsonCompareMode.STRICT);
}
}
}

View File

@@ -73,6 +73,7 @@ final class RefasterRulesTest {
PrimitiveRules.class,
ReactorRules.class,
RxJava2AdapterRules.class,
SpringTestRules.class,
StreamRules.class,
StringRules.class,
SuggestedFixRules.class,

View File

@@ -0,0 +1,18 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.ImmutableSet;
import org.springframework.test.web.reactive.server.WebTestClient.BodyContentSpec;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class SpringTestRulesTest implements RefasterRuleCollectionTestCase {
@SuppressWarnings("deprecation" /* Rule rewrites deprecated method invocation. */)
ImmutableSet<BodyContentSpec> testBodyContentSpecJsonLenient() {
return ImmutableSet.of(
((BodyContentSpec) null).json("foo"), ((BodyContentSpec) null).json("bar", false));
}
@SuppressWarnings("deprecation" /* Rule rewrites deprecated method invocation. */)
BodyContentSpec testBodyContentSpecJsonStrict() {
return ((BodyContentSpec) null).json("foo", true);
}
}

View File

@@ -0,0 +1,20 @@
package tech.picnic.errorprone.refasterrules;
import com.google.common.collect.ImmutableSet;
import org.springframework.test.json.JsonCompareMode;
import org.springframework.test.web.reactive.server.WebTestClient.BodyContentSpec;
import tech.picnic.errorprone.refaster.test.RefasterRuleCollectionTestCase;
final class SpringTestRulesTest implements RefasterRuleCollectionTestCase {
@SuppressWarnings("deprecation" /* Rule rewrites deprecated method invocation. */)
ImmutableSet<BodyContentSpec> testBodyContentSpecJsonLenient() {
return ImmutableSet.of(
((BodyContentSpec) null).json("foo", JsonCompareMode.LENIENT),
((BodyContentSpec) null).json("bar", JsonCompareMode.LENIENT));
}
@SuppressWarnings("deprecation" /* Rule rewrites deprecated method invocation. */)
BodyContentSpec testBodyContentSpecJsonStrict() {
return ((BodyContentSpec) null).json("foo", JsonCompareMode.STRICT);
}
}

27
generate-docs.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/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

12
pom.xml
View File

@@ -213,7 +213,7 @@
<version.maven>3.9.9</version.maven>
<version.mockito>5.16.1</version.mockito>
<version.nopen-checker>1.0.1</version.nopen-checker>
<version.nullaway>0.12.4</version.nullaway>
<version.nullaway>0.12.5</version.nullaway>
<version.pitest-git>2.2.0</version.pitest-git>
<version.rewrite-templating>1.24.1</version.rewrite-templating>
<version.surefire>3.2.3</version.surefire>
@@ -360,11 +360,6 @@
<artifactId>nullaway</artifactId>
<version>${version.nullaway}</version>
</dependency>
<dependency>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-bom</artifactId>
@@ -1812,11 +1807,6 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.4.1</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>

7
website/.gitignore vendored
View File

@@ -1,13 +1,12 @@
# Generated by Bundler and Jekyll.
.bundle/
Gemfile.lock
.jekyll-cache/
.jekyll-metadata
.sass-cache/
_site/
vendor/
# XXX: Update generator name when it's renamed.
# Generated by `JekyllCollectionGenerator`.
# Generated by `../generate-docs.sh`.
*.bak
index.md
_bugpatterns/
_refasterrules/

View File

@@ -1 +1 @@
3.3.5
3.1.2

View File

@@ -1,27 +1,8 @@
ruby File.read(".ruby-version").strip
source "https://rubygems.org"
# 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
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"

View File

@@ -1,211 +0,0 @@
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

View File

@@ -7,30 +7,18 @@ statically generated using [Jekyll][jekyll].
# Local development
To view the website on `localhost`, first follow the [Jekyll installation
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:
instructions][jekyll-docs-installation]. Once done, in this directory execute:
```sh
bundle install
bundle exec jekyll serve --livereload
../generate-docs.sh && 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 `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.
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.
If you are not familiar with Jekyll, be sure to check out its
[documentation][jekyll-docs]. It is recommended to follow the provided

View File

@@ -8,40 +8,15 @@ 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
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
- Gemfile
- Gemfile.lock
- generate-version-compatibility-overview.sh
- README.md
- vendor
# See https://jekyllrb.com/docs/permalinks/#built-in-formats.
permalink: pretty
@@ -50,9 +25,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:
@@ -65,9 +40,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

View File

@@ -1,6 +0,0 @@
ERROR:
color: red
WARNING:
color: yellow
SUGGESTION:
color: green

View File

@@ -1,94 +0,0 @@
---
# 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 %}
<a href="https://github.com/PicnicSupermarket/error-prone-support/blob/master/{{ page.source }}" class="fs-3 btn external" target="_blank">
View source code on GitHub
<svg viewBox="0 0 24 24" aria-labelledby="svg-external-link-title">
<use xlink:href="#svg-external-link"></use>
</svg>
</a>
{: .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 }}

View File

@@ -1,78 +0,0 @@
---
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 %}
<a href="https://github.com/PicnicSupermarket/error-prone-support/blob/master/{{ page.source }}" class="fs-3 btn external" target="_blank">
View source code on GitHub
<svg viewBox="0 0 24 24" aria-labelledby="svg-external-link-title">
<use xlink:href="#svg-external-link"></use>
</svg>
</a>
{: .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 %}
<details open markdown="block">
<summary>
Table of contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
{% 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 }}

View File

@@ -2,4 +2,4 @@
// https://github.com/just-the-docs/just-the-docs/blob/main/_sass/support/_variables.scss.
// Grid system.
$nav-width: 25rem;
$nav-width: 400px;

8
website/bugpatterns.md Normal file
View File

@@ -0,0 +1,8 @@
---
layout: default
title: Bug Patterns
nav_order: 2
has_children: true
---
# Bug Patterns

8
website/refasterrules.md Normal file
View File

@@ -0,0 +1,8 @@
---
layout: default
title: Refaster Rules
nav_order: 2
has_children: true
---
# Refaster Rules