Compare commits

...

15 Commits

Author SHA1 Message Date
japborst
525df33e42 gdejong/docgen -> gdejong/docgen_old 2022-10-10 21:06:24 +02:00
japborst
368f70fc98 Rename refastertemplates -> refasterrules 2022-10-10 21:05:24 +02:00
japborst
ec023cd8cf Remove trailing space for code blocks 2022-09-27 16:49:02 +02:00
Rick Ossendrijver
9ae3c70dff Update .gitignore, fix typo, and sort links in README 2022-09-27 16:15:42 +02:00
japborst
f781c85143 Fix GitHub source path 2022-09-27 11:06:44 +02:00
japborst
d1054bfb8b Deploy to GH pages 2022-09-27 10:20:29 +02:00
japborst
ac86140420 Reduce # regex 2022-09-27 10:20:29 +02:00
japborst
1ed4892d34 Generate output lines 2022-09-27 10:20:29 +02:00
japborst
8ee643e9b9 Style summary, and support explanations 2022-09-27 10:20:29 +02:00
japborst
272245b9d4 Exclude generated files 2022-09-27 10:20:29 +02:00
Gijs de Jong
d8009e9c26 [WIP] Initial regex extraction + docgen 2022-09-27 10:20:29 +02:00
japborst
d7f1fc6cff Use absolute path for Picnic logo 2022-09-27 10:20:19 +02:00
japborst
8760f7f31f A few SEO improvements 2022-09-24 13:43:42 +02:00
japborst
81ddfdb6fb Add Picnic logo; small improvements 2022-09-21 11:50:41 +02:00
japborst
ccf3ce4962 Bootstrap documentation website 2022-09-21 11:33:41 +02:00
45 changed files with 1769 additions and 31 deletions

View File

@@ -2,8 +2,8 @@ name: Build and verify
on:
pull_request:
push:
branches:
- 'master'
branches: [$default-branch]
workflow_dispatch:
permissions:
contents: read
jobs:

46
.github/workflows/deploy-website.yaml vendored Normal file
View File

@@ -0,0 +1,46 @@
name: Update `error-prone.picnic.tech` website contents
on:
push:
branches:
- 'gdejong/docgen_old'
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@v3.0.2
- name: Set up JDK
uses: actions/setup-java@v3.4.1
with:
java-version: 11.0.16
distribution: temurin
cache: maven
- name: Configure Github Pages
uses: actions/configure-pages@v2.0.0
- name: Generate documentation
run: bash ./generate-docs.sh
- name: Build website with Jekyll
uses: actions/jekyll-build-pages@v1.0.5
with:
source: website/
destination: ./_site
- name: Upload website artifact
uses: actions/upload-pages-artifact@v1.0.3
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-22.04
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1.0.9

View File

@@ -1,30 +1,33 @@
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="logo-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="logo.svg">
<img alt="Error Prone Support logo" src="logo.svg" width="50%">
</picture>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="logo-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="logo.svg">
<img alt="Error Prone Support logo" src="logo.svg" width="50%">
</picture>
</div>
# Error Prone Support
Error Prone Support is a Picnic-opinionated extension of Google's [Error
Prone][error-prone-orig-repo]. It aims to improve code quality, focussing on
maintainability, consistency and avoidance of common gotchas.
Error Prone Support is a [Picnic][picnic-blog]-opinionated extension of
Google's [Error Prone][error-prone-orig-repo]. It aims to improve code quality,
focussing on maintainability, consistency and avoidance of common pitfalls.
> Error Prone is a static analysis tool for Java that catches common
> programming mistakes at compile-time.
Read more on how Picnic uses Error Prone (Support) in the blog post [_Picnic
loves Error Prone: producing high-quality and consistent Java
code_][picnic-blog-ep-post].
[![Maven Central][maven-central-badge]][maven-central-search]
[![GitHub Actions][github-actions-build-badge]][github-actions-build-master]
[![License][license-badge]][license]
[![PRs Welcome][pr-badge]][contributing]
[Getting started](#-getting-started) • [Building](#-building) •
[Getting started](#-getting-started) •
[Developing Error Prone Support](#-developing-error-prone-support) •
[How it works](#-how-it-works) • [Contributing](#%EF%B8%8F-contributing)
</div>
---
## ⚡ Getting started
@@ -60,7 +63,7 @@ it:
<artifactId>error-prone-contrib</artifactId>
<version>${error-prone-support.version}</version>
</path>
<!-- Error Prone Support's Refaster templates. -->
<!-- Error Prone Support's Refaster rules. -->
<path>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>refaster-runner</artifactId>
@@ -119,9 +122,10 @@ $ mvn clean install
[INFO] -------------------------------------------------------------
[WARNING] COMPILATION WARNING :
[INFO] -------------------------------------------------------------
[WARNING] Example.java:[9,34] [tech.picnic.errorprone.refastertemplates.BigDecimalTemplates.BigDecimalZero]
[WARNING] Example.java:[9,34] [tech.picnic.errorprone.refasterrules.BigDecimalTemplates.BigDecimalZero]
Did you mean 'return BigDecimal.ZERO;'?
[WARNING] Example.java:[14,35] [IdentityConversion] This method invocation appears redundant; remove it or suppress this warning and add a comment explaining its purpose
[WARNING] Example.java:[13,35] [IdentityConversion] This method invocation appears redundant; remove it or suppress this warning and add a comment explaining its purpose
(see https://error-prone.picnic.tech/bugpatterns/IdentityConversion)
Did you mean 'return set;' or '@SuppressWarnings("IdentityConversion") public ImmutableSet<Integer> getSet() {'?
[INFO] 2 warnings
[INFO] -------------------------------------------------------------
@@ -132,17 +136,18 @@ Two things are kicking in here:
1. An Error Prone [`BugChecker`][error-prone-bugchecker] that flags unnecessary
[identity conversions][bug-checks-identity-conversion].
2. A [Refaster][refaster] template capable of
[rewriting][refaster-templates-bigdecimal] expressions of the form
2. A [Refaster][refaster] rule capable of
[rewriting][refaster-rules-bigdecimal] expressions of the form
`BigDecimal.valueOf(0)` and `new BigDecimal(0)` to `BigDecimal.ZERO`.
Be sure to check out all [bug checks][bug-checks] and [refaster
templates][refaster-templates].
rules][refaster-rules].
## 👷 Building
## 👷 Developing Error Prone Support
This is a [Maven][maven] project, so running `mvn clean install`
performs a full clean build. Some relevant flags:
This is a [Maven][maven] project, so running `mvn clean install` performs a
full clean build and installs the library to your local Maven repository. Some
relevant flags:
- `-Dverification.warn` makes the warnings and errors emitted by various
plugins and the Java compiler non-fatal, where possible.
@@ -198,9 +203,9 @@ Want to report or fix a bug, suggest or add a new feature, or improve the
documentation? That's awesome! Please read our [contribution
guidelines][contributing].
[bug-checks]: error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/
[bug-checks-identity-conversion]: error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java
[contributing]: CONTRIBUTING.md
[bug-checks]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/
[bug-checks-identity-conversion]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/IdentityConversion.java
[contributing]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/CONTRIBUTING.md
[error-prone-bugchecker]: https://github.com/google/error-prone/blob/master/check_api/src/main/java/com/google/errorprone/bugpatterns/BugChecker.java
[error-prone-fork-jitpack]: https://jitpack.io/#PicnicSupermarket/error-prone
[error-prone-fork-repo]: https://github.com/PicnicSupermarket/error-prone
@@ -212,13 +217,15 @@ guidelines][contributing].
[google-java-format]: https://github.com/google/google-java-format
[idea-288052]: https://youtrack.jetbrains.com/issue/IDEA-288052
[license-badge]: https://img.shields.io/github/license/PicnicSupermarket/error-prone-support
[license]: LICENSE.md
[license]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/LICENSE.md
[maven-central-badge]: https://img.shields.io/maven-central/v/tech.picnic.error-prone-support/error-prone-support?color=blue
[maven-central-search]: https://search.maven.org/artifact/tech.picnic.error-prone-support/error-prone-support
[maven]: https://maven.apache.org
[picnic-blog]: https://blog.picnic.nl
[picnic-blog-ep-post]: https://blog.picnic.nl/picnic-loves-error-prone-producing-high-quality-and-consistent-java-code-b8a566be6886
[pitest]: https://pitest.org
[pitest-maven]: https://pitest.org/quickstart/maven
[pr-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
[refaster]: https://errorprone.info/docs/refaster
[refaster-templates-bigdecimal]: error-prone-contrib/src/main/java/tech/picnic/errorprone/refastertemplates/BigDecimalTemplates.java
[refaster-templates]: error-prone-contrib/src/main/java/tech/picnic/errorprone/refastertemplates/
[refaster-rules-bigdecimal]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/BigDecimalTemplates.java
[refaster-rules]: https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/

162
docgen/pom.xml Normal file
View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2011 The Error Prone Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<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 http://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.2.1-SNAPSHOT</version>
</parent>
<name>Picnic :: Error Prone Support :: DocGen</name>
<artifactId>error_prone_docgen</artifactId>
<licenses>
<license>
<name>Apache 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>${version.auto-value}</version>
</path>
<path>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>${version.auto-service}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.mustache</include>
</includes>
</resource>
</resources>
</build>
<dependencies>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotation</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_core</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>error-prone-contrib</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>tech.picnic.error-prone-support</groupId>
<artifactId>error_prone_docgen_processor</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.30</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
<version>1.82</version>
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value-annotations</artifactId>
<version>${version.auto-value}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.9.10</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>run-annotation-processor</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>site</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>com.google.errorprone.DocGenTool</mainClass>
<arguments>
<argument>
-bug_patterns=${basedir}/../error-prone-contrib/target/generated-sources/annotations/bugPatterns.txt
</argument>
<argument>-docs_repository=${basedir}/target/generated-wiki/</argument>
<argument>-explanations=${basedir}/../docs/bugpatterns/</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,160 @@
/*
* Copyright 2014 The Error Prone Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.errorprone;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.LineProcessor;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.gson.Gson;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import javax.annotation.Nullable;
/**
* Reads each line of the bugpatterns.txt tab-delimited data file, and generates a GitHub Jekyll
* page for each one.
*
* @author alexeagle@google.com (Alex Eagle)
*/
class BugPatternFileGenerator implements LineProcessor<List<BugPatternInstance>> {
private final Path outputDir;
private final Path explanationDir;
private final List<BugPatternInstance> result;
private final Function<BugPatternInstance, SeverityLevel> severityRemapper;
/** The base url for links to bugpatterns. */
@Nullable private final String baseUrl;
public BugPatternFileGenerator(
Path bugpatternDir,
Path explanationDir,
String baseUrl,
Function<BugPatternInstance, SeverityLevel> severityRemapper) {
this.outputDir = bugpatternDir;
this.explanationDir = explanationDir;
this.severityRemapper = severityRemapper;
this.baseUrl = baseUrl;
result = new ArrayList<>();
}
@Override
public boolean processLine(String line) throws IOException {
BugPatternInstance pattern = new Gson().fromJson(line, BugPatternInstance.class);
pattern.severity = severityRemapper.apply(pattern);
result.add(pattern);
// replace spaces in filename with underscores
Path checkPath = Paths.get(pattern.name.replace(' ', '_') + ".md");
try (Writer writer = Files.newBufferedWriter(outputDir.resolve(checkPath), UTF_8)) {
// load side-car explanation file, if it exists
Path sidecarExplanation = explanationDir.resolve(checkPath);
if (Files.exists(sidecarExplanation)) {
if (!pattern.explanation.isEmpty()) {
throw new AssertionError(
String.format(
"%s specifies an explanation via @BugPattern and side-car", pattern.name));
}
pattern.explanation = Files.readString(sidecarExplanation).trim();
}
// Construct an appropriate page for this {@code BugPattern}. Include altNames if
// there are any, and explain the correct way to suppress.
ImmutableMap.Builder<String, Object> templateData =
ImmutableMap.<String, Object>builder()
.put(
"tags", Arrays.stream(pattern.tags).map(Style::styleTag).collect(joining("\n\n")))
.put("severity", Style.styleSeverity(pattern.severity))
.put("name", pattern.name)
.put("bugpattern", String.format("%s.java", pattern.className.replace(".", "/")))
.put("className", pattern.className)
.put("summary", pattern.summary.trim())
.put("altNames", Joiner.on(", ").join(pattern.altNames))
.put("explanation", pattern.explanation.trim());
if (pattern.sampleInput != null && pattern.sampleOutput != null) {
templateData.put("hasSamples", true);
templateData.put("sampleInput", pattern.sampleInput);
templateData.put("sampleOutput", pattern.sampleOutput);
}
if (baseUrl != null) {
templateData.put("baseUrl", baseUrl);
}
if (pattern.documentSuppression) {
String suppressionString;
if (pattern.suppressionAnnotations.length == 0) {
suppressionString = "This check may not be suppressed.";
} else {
suppressionString =
pattern.suppressionAnnotations.length == 1
? "Suppress false positives by adding the suppression annotation %s to the "
+ "enclosing element."
: "Suppress false positives by adding one of these suppression annotations to "
+ "the enclosing element: %s";
suppressionString =
String.format(
suppressionString,
Arrays.stream(pattern.suppressionAnnotations)
.map((String anno) -> standardizeAnnotation(anno, pattern.name))
.collect(joining(", ")));
}
templateData.put("suppression", suppressionString);
}
MustacheFactory mf = new DefaultMustacheFactory();
Mustache mustache = mf.compile("com/google/errorprone/resources/bugpattern.mustache");
mustache.execute(writer, templateData.buildOrThrow());
}
return true;
}
private String standardizeAnnotation(String fullAnnotationName, String patternName) {
String annotationName =
fullAnnotationName.endsWith(".class")
? fullAnnotationName.substring(0, fullAnnotationName.length() - ".class".length())
: fullAnnotationName;
if (annotationName.equals(SuppressWarnings.class.getName())) {
annotationName = SuppressWarnings.class.getSimpleName() + "(\"" + patternName + "\")";
}
return "`@" + annotationName + "`";
}
@Override
public List<BugPatternInstance> getResult() {
return result;
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2015 The Error Prone Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.errorprone;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.io.Files.asCharSource;
import static com.google.errorprone.scanner.BuiltInCheckerSuppliers.ENABLED_ERRORS;
import static com.google.errorprone.scanner.BuiltInCheckerSuppliers.ENABLED_WARNINGS;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.beust.jcommander.IStringConverter;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.StreamSupport;
/**
* Utility main which consumes the same tab-delimited text file and generates GitHub pages for the
* BugPatterns.
*/
public final class DocGenTool {
@Parameters(separators = "=")
static class Options {
@Parameter(names = "-bug_patterns", description = "Path to bugPatterns.txt", required = true)
private String bugPatterns;
@Parameter(
names = "-explanations",
description = "Path to side-car explanations",
required = true)
private String explanations;
@Parameter(names = "-docs_repository", description = "Path to docs repository", required = true)
private String docsRepository;
@Parameter(
names = "-base_url",
description = "The base url for links to bugpatterns",
arity = 1)
private String baseUrl = null;
}
public static void main(String[] args) throws IOException {
Options options = new Options();
new JCommander(options).parse(args);
Path bugPatterns = Paths.get(options.bugPatterns);
if (!Files.exists(bugPatterns)) {
usage("Cannot find bugPatterns file: " + options.bugPatterns);
}
Path explanationDir = Paths.get(options.explanations);
if (!Files.exists(explanationDir)) {
usage("Cannot find explanations dir: " + options.explanations);
}
Path wikiDir = Paths.get(options.docsRepository);
Files.createDirectories(wikiDir);
Path bugpatternDir = wikiDir.resolve("bugpatterns");
if (!Files.exists(bugpatternDir)) {
Files.createDirectories(bugpatternDir);
}
BugPatternFileGenerator generator =
new BugPatternFileGenerator(
bugpatternDir,
explanationDir,
options.baseUrl,
input -> input.severity);
try (Writer w =
Files.newBufferedWriter(wikiDir.resolve("bugpatterns.md"), StandardCharsets.UTF_8)) {
List<BugPatternInstance> patterns =
asCharSource(bugPatterns.toFile(), UTF_8).readLines(generator);
}
}
private static ImmutableSet<String> enabledCheckNames() {
return StreamSupport.stream(
Iterables.concat(ENABLED_ERRORS, ENABLED_WARNINGS).spliterator(), false)
.map(BugCheckerInfo::canonicalName)
.collect(toImmutableSet());
}
private static void usage(String err) {
System.err.println(err);
System.exit(1);
}
private DocGenTool() {}
}

View File

@@ -0,0 +1,27 @@
package com.google.errorprone;
import com.google.errorprone.BugPattern.SeverityLevel;
public final class Style {
public static String styleSeverity (SeverityLevel severityLevel) {
return String.format("%s\n {: .label .label-%s}", severityLevel.toString(), getSeverityLabelColour(severityLevel));
}
private static String getSeverityLabelColour (SeverityLevel severityLevel) {
switch (severityLevel) {
case ERROR:
return "red";
case WARNING:
return "yellow";
case SUGGESTION:
return "green";
default:
return "blue";
}
}
public static String styleTag (String tagName) {
return String.format("%s\n {: .label }", tagName);
}
}

View File

@@ -0,0 +1,50 @@
---
layout: default
title: {{name}}
description: "{{{summary}}}"
parent: Bug Patterns
nav_order: 1
---
<!--
*** AUTO-GENERATED, DO NOT MODIFY ***
To make changes, edit the @BugPattern annotation or the explanation in docs/bugpattern.
-->
# {{name}}
{{{severity}}}
{{{tags}}}
{: .summary }
{{{summary}}}
{{{explanation}}}
{{#suppression}}
## Suppression
{{{suppression}}}
{{/suppression}}
{{#hasSamples}}
## Samples
### Before
```java
{{{sampleInput}}}
```
### After
```java
{{{sampleOutput}}}
```
{{/hasSamples}}
<a href="https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/{{bugpattern}}" 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>

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2014 The Error Prone Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.errorprone;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.io.CharStreams;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.gson.Gson;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class BugPatternFileGeneratorTest {
@Rule public TemporaryFolder tmpfolder = new TemporaryFolder();
private Path wikiDir;
private Path explanationDirBase;
@Before
public void setUp() throws Exception {
wikiDir = tmpfolder.newFolder("wiki").toPath();
explanationDirBase = tmpfolder.newFolder("explanations").toPath();
}
private static BugPatternInstance deadExceptionTestInfo() {
BugPatternInstance instance = new BugPatternInstance();
instance.className = "com.google.errorprone.bugpatterns.DeadException";
instance.name = "DeadException";
instance.summary = "Exception created but not thrown";
instance.explanation =
"The exception is created with new, but is not thrown, and the reference is lost.";
instance.altNames = new String[] {"ThrowableInstanceNeverThrown"};
instance.tags = new String[] {"LikelyError"};
instance.severity = SeverityLevel.ERROR;
instance.suppressionAnnotations = new String[] {"java.lang.SuppressWarnings.class"};
return instance;
}
private static final String BUGPATTERN_LINE;
static {
BugPatternInstance instance = deadExceptionTestInfo();
BUGPATTERN_LINE = new Gson().toJson(instance);
}
private static final String BUGPATTERN_LINE_SIDECAR;
static {
BugPatternInstance instance = deadExceptionTestInfo();
instance.explanation = "";
BUGPATTERN_LINE_SIDECAR = new Gson().toJson(instance);
}
// Assert that the generator produces the same output it did before.
// This is brittle, but you can open the golden file
// src/test/resources/com/google/errorprone/DeadException.md
// in the same Jekyll environment you use for prod, and verify it looks good.
@Test
public void regressionTest_frontmatter_pygments() throws Exception {
BugPatternFileGenerator generator =
new BugPatternFileGenerator(
wikiDir, explanationDirBase, null, input -> input.severity);
generator.processLine(BUGPATTERN_LINE);
String expected =
CharStreams.toString(
new InputStreamReader(
getClass().getResourceAsStream("testdata/DeadException_frontmatter_pygments.md"),
UTF_8));
String actual =
CharStreams.toString(Files.newBufferedReader(wikiDir.resolve("DeadException.md"), UTF_8));
assertThat(actual.trim()).isEqualTo(expected.trim());
}
@Test
public void regressionTest_nofrontmatter_gfm() throws Exception {
BugPatternFileGenerator generator =
new BugPatternFileGenerator(
wikiDir, explanationDirBase, null, input -> input.severity);
generator.processLine(BUGPATTERN_LINE);
String expected =
CharStreams.toString(
new InputStreamReader(
getClass().getResourceAsStream("testdata/DeadException_nofrontmatter_gfm.md"),
UTF_8));
String actual = new String(Files.readAllBytes(wikiDir.resolve("DeadException.md")), UTF_8);
assertThat(actual.trim()).isEqualTo(expected.trim());
}
@Test
public void regressionTest_sidecar() throws Exception {
BugPatternFileGenerator generator =
new BugPatternFileGenerator(
wikiDir, explanationDirBase, null, input -> input.severity);
Files.write(
explanationDirBase.resolve("DeadException.md"),
Arrays.asList(
"The exception is created with new, but is not thrown, and the reference is lost."),
UTF_8);
generator.processLine(BUGPATTERN_LINE_SIDECAR);
String expected =
CharStreams.toString(
new InputStreamReader(
getClass().getResourceAsStream("testdata/DeadException_nofrontmatter_gfm.md"),
UTF_8));
String actual = new String(Files.readAllBytes(wikiDir.resolve("DeadException.md")), UTF_8);
assertThat(actual.trim()).isEqualTo(expected.trim());
}
@Test
public void testEscapeAngleBracketsInSummary() throws Exception {
// Create a BugPattern with angle brackets in the summary
BugPatternInstance instance = new BugPatternInstance();
instance.className = "com.google.errorprone.bugpatterns.DontDoThis";
instance.name = "DontDoThis";
instance.summary = "Don't do this; do List<Foo> instead";
instance.explanation = "This is a bad idea, you want `List<Foo>` instead";
instance.altNames = new String[0];
instance.tags = new String[] {"LikelyError"};
instance.severity = SeverityLevel.ERROR;
instance.suppressionAnnotations = new String[] {"java.lang.SuppressWarnings.class"};
// Write markdown file
BugPatternFileGenerator generator =
new BugPatternFileGenerator(
wikiDir, explanationDirBase, null, input -> input.severity);
generator.processLine(new Gson().toJson(instance));
String expected =
CharStreams.toString(
new InputStreamReader(
getClass().getResourceAsStream("testdata/DontDoThis_nofrontmatter_gfm.md"), UTF_8));
String actual = new String(Files.readAllBytes(wikiDir.resolve("DontDoThis.md")), UTF_8);
assertThat(actual.trim()).isEqualTo(expected.trim());
}
}

View File

@@ -0,0 +1,20 @@
---
title: DeadException
summary: Exception created but not thrown
layout: bugpattern
tags: LikelyError
severity: ERROR
---
<!--
*** AUTO-GENERATED, DO NOT MODIFY ***
To make changes, edit the @BugPattern annotation or the explanation in docs/bugpattern.
-->
_Alternate names: ThrowableInstanceNeverThrown_
## The problem
The exception is created with new, but is not thrown, and the reference is lost.
## Suppression
Suppress false positives by adding the suppression annotation `@SuppressWarnings("DeadException")` to the enclosing element.

View File

@@ -0,0 +1,21 @@
<!--
*** AUTO-GENERATED, DO NOT MODIFY ***
To make changes, edit the @BugPattern annotation or the explanation in docs/bugpattern.
-->
# DeadException
__Exception created but not thrown__
<div style="float:right;"><table id="metadata">
<tr><td>Severity</td><td>ERROR</td></tr>
<tr><td>Tags</td><td>LikelyError</td></tr>
</table></div>
_Alternate names: ThrowableInstanceNeverThrown_
## The problem
The exception is created with new, but is not thrown, and the reference is lost.
## Suppression
Suppress false positives by adding the suppression annotation `@SuppressWarnings("DeadException")` to the enclosing element.

View File

@@ -0,0 +1,20 @@
<!--
*** AUTO-GENERATED, DO NOT MODIFY ***
To make changes, edit the @BugPattern annotation or the explanation in docs/bugpattern.
-->
# DontDoThis
__Don&#39;t do this; do List&lt;Foo&gt; instead__
<div style="float:right;"><table id="metadata">
<tr><td>Severity</td><td>ERROR</td></tr>
<tr><td>Tags</td><td>LikelyError</td></tr>
</table></div>
## The problem
This is a bad idea, you want `List<Foo>` instead
## Suppression
Suppress false positives by adding the suppression annotation `@SuppressWarnings("DontDoThis")` to the enclosing element.

94
docgen_processor/pom.xml Normal file
View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2011 The Error Prone Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<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 http://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.2.1-SNAPSHOT</version>
</parent>
<name>Picnic :: Error Prone Support :: DocGen Processor</name>
<artifactId>error_prone_docgen_processor</artifactId>
<licenses>
<license>
<name>Apache 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<dependencies>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_annotation</artifactId>
</dependency>
<dependency>
<groupId>${groupId.error-prone}</groupId>
<artifactId>error_prone_core</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service-annotations</artifactId>
<version>${version.auto-service}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>com.google.googlejavaformat</groupId>
<artifactId>google-java-format</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>${version.auto-service}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,168 @@
/*
* Copyright 2015 The Error Prone Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.errorprone;
import com.google.common.base.Preconditions;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.googlejavaformat.java.Formatter;
import com.google.googlejavaformat.java.FormatterException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import org.apache.commons.text.StringEscapeUtils;
/** A serialization-friendly POJO of the information in a {@link BugPattern}. */
public final class BugPatternInstance {
private static final Formatter FORMATTER = new Formatter();
private static final Pattern INPUT_LINES_PATTERN = Pattern.compile("\\.addInputLines\\((.*?)\\)\n", Pattern.DOTALL);
private static final Pattern OUTPUT_LINES_PATTERN = Pattern.compile("\\.addOutputLines\\((.*?)\\)\n", Pattern.DOTALL);
private static final Pattern LINES_PATTERN = Pattern.compile("\"(.*)\"");
public String className;
public String name;
public String summary;
public String explanation;
public String[] altNames;
public String category;
public String[] tags;
public SeverityLevel severity;
public String[] suppressionAnnotations;
public boolean documentSuppression = true;
public String testContent;
public String sampleInput;
public String sampleOutput;
public static BugPatternInstance fromElement(Element element) {
BugPatternInstance instance = new BugPatternInstance();
instance.className = element.toString();
BugPattern annotation = element.getAnnotation(BugPattern.class);
instance.name = annotation.name().isEmpty() ? element.getSimpleName().toString() : annotation.name();
instance.altNames = annotation.altNames();
instance.tags = annotation.tags();
instance.severity = annotation.severity();
instance.summary = annotation.summary();
instance.explanation = annotation.explanation();
instance.documentSuppression = annotation.documentSuppression();
Map<String, Object> keyValues = getAnnotation(element, BugPattern.class.getName());
Object suppression = keyValues.get("suppressionAnnotations");
if (suppression == null) {
instance.suppressionAnnotations = new String[] { SuppressWarnings.class.getName() };
} else {
Preconditions.checkState(suppression instanceof List);
@SuppressWarnings("unchecked") // Always List<? extends AnnotationValue>, see above.
List<? extends AnnotationValue> resultList = (List<? extends AnnotationValue>) suppression;
instance.suppressionAnnotations = resultList.stream().map(AnnotationValue::toString).toArray(String[]::new);
}
Path testPath = getPath(instance);
System.out.println("test class for " + instance.name + " = " + testPath.toAbsolutePath());
try {
instance.testContent = String.join("\n", Files.readAllLines(testPath));
instance.sampleInput = getInputLines(instance.testContent);
instance.sampleOutput = getOutputLines(instance.testContent);
} catch (IOException e) {
e.printStackTrace();
}
return instance;
}
private static Path getPath(BugPatternInstance instance) {
return Path.of(
"error-prone-contrib/src/test/java/"
+ instance.className.replace(".", "/")
+ "Test.java");
}
private static String getInputLines(String content) {
return getLines(INPUT_LINES_PATTERN, content);
}
private static String getOutputLines(String content) {
return getLines(OUTPUT_LINES_PATTERN, content);
}
private static String getLines(Pattern pattern, String content) {
Matcher match = pattern.matcher(content);
if (!match.find()) {
return null;
}
String argument = match.group(1);
List<String> lines = findAllGroups(argument, LINES_PATTERN);
// Remove in/A.java and out/A.java
lines.remove(0);
String sampleCode = String.join("\n", lines);
sampleCode = StringEscapeUtils.unescapeJava(sampleCode);
try {
// Trim to remove trailing line-break.
return FORMATTER.formatSource(sampleCode).trim();
} catch (FormatterException e) {
e.printStackTrace();
return null;
}
}
private static List<String> findAllGroups(String text, Pattern pattern) {
List<String> list = new ArrayList<>();
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
for (int i = 1; i <= matcher.groupCount(); i++) {
list.add(matcher.group(i));
}
}
return list;
}
private static Map<String, Object> getAnnotation(Element element, String name) {
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
if (mirror.getAnnotationType().toString().equals(name)) {
return annotationKeyValues(mirror);
}
}
throw new IllegalArgumentException(String.format("%s has no annotation %s", element, name));
}
private static Map<String, Object> annotationKeyValues(AnnotationMirror mirror) {
Map<String, Object> result = new LinkedHashMap<>();
for (ExecutableElement key : mirror.getElementValues().keySet()) {
result.put(key.getSimpleName().toString(), mirror.getElementValues().get(key).getValue());
}
return result;
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2011 The Error Prone Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.errorprone;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.auto.service.AutoService;
import com.google.googlejavaformat.java.Formatter;
import com.google.gson.Gson;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
/**
* Annotation processor which visits all classes that have a {@code BugPattern} annotation, and
* writes a tab-delimited text file dumping the data found.
*
* @author eaftan@google.com (Eddie Aftandilian)
* @author alexeagle@google.com (Alex Eagle)
*/
@AutoService(Processor.class)
@SupportedAnnotationTypes("com.google.errorprone.BugPattern")
public class DocGenProcessor extends AbstractProcessor {
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
private final Gson gson = new Gson();
private PrintWriter pw;
/** {@inheritDoc} */
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
try {
FileObject manifest =
processingEnv
.getFiler()
.createResource(StandardLocation.SOURCE_OUTPUT, "", "bugPatterns.txt");
pw = new PrintWriter(new OutputStreamWriter(manifest.openOutputStream(), UTF_8), true);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(BugPattern.class)) {
System.out.println("[DOCGEN] HANDLING: " + element.getSimpleName());
gson.toJson(BugPatternInstance.fromElement(element), pw);
pw.println();
}
if (roundEnv.processingOver()) {
// this was the last round, do cleanup
cleanup();
}
return false;
}
/** Perform cleanup after last round of annotation processing. */
private void cleanup() {
pw.close();
}
}

View File

@@ -0,0 +1,90 @@
package tech.picnic.errorprone.docgen;
import static java.util.stream.Collectors.joining;
import com.google.googlejavaformat.java.Formatter;
import com.google.googlejavaformat.java.FormatterException;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
class ExampleExtractorTest {
private static final String INPUT =
String.join("\n",
"@Test",
"void replacementFirstSuggestedFix() {",
" refactoringTestHelper",
" .addInputLines(",
" \"A.java\",",
" \"import static java.util.stream.Collectors.toList;\",",
" \"import static java.util.stream.Collectors.toMap;\",",
" \"import static java.util.stream.Collectors.toSet;\",",
" \"\",",
" \"import java.util.stream.Collectors;\",",
" \"import java.util.stream.Stream;\",",
" \"import reactor.core.publisher.Flux;\",",
" \"\",",
" \"class A {\",",
" \" void m() {\",",
" \" Flux.just(1).collect(Collectors.toList());\",",
" \" Flux.just(2).collect(toList());\",",
" \"\",",
" \" Stream.of(\"foo\").collect(Collectors.toMap(String::getBytes, String::length));\",",
" \" Stream.of(\"bar\").collect(toMap(String::getBytes, String::length));\",",
" \" Flux.just(\"baz\").collect(Collectors.toMap(String::getBytes, String::length, (a, b) -> b));\",",
" \" Flux.just(\"qux\").collect(toMap(String::getBytes, String::length, (a, b) -> b));\",",
" \"\",",
" \" Stream.of(1).collect(Collectors.toSet());\",",
" \" Stream.of(2).collect(toSet());\",",
" \" }\",",
" \"}\")",
" .addOutputLines(",
" \"A.java\",",
" \"import static com.google.common.collect.ImmutableList.toImmutableList;\",",
" \"import static com.google.common.collect.ImmutableMap.toImmutableMap;\",",
" \"import static com.google.common.collect.ImmutableSet.toImmutableSet;\",",
" \"import static java.util.stream.Collectors.toList;\",",
" \"import static java.util.stream.Collectors.toMap;\",",
" \"import static java.util.stream.Collectors.toSet;\",",
" \"\",",
" \"import java.util.stream.Collectors;\",",
" \"import java.util.stream.Stream;\",",
" \"import reactor.core.publisher.Flux;\",",
" \"\",",
" \"class A {\",",
" \" void m() {\",",
" \" Flux.just(1).collect(toImmutableList());\",",
" \" Flux.just(2).collect(toImmutableList());\",",
" \"\",",
" \" Stream.of(\"foo\").collect(toImmutableMap(String::getBytes, String::length));\",",
" \" Stream.of(\"bar\").collect(toImmutableMap(String::getBytes, String::length));\",",
" \" Flux.just(\"baz\").collect(toImmutableMap(String::getBytes, String::length, (a, b) -> b));\",",
" \" Flux.just(\"qux\").collect(toImmutableMap(String::getBytes, String::length, (a, b) -> b));\",",
" \"\",",
" \" Stream.of(1).collect(toImmutableSet());\",",
" \" Stream.of(2).collect(toImmutableSet());\",",
" \" }\",",
" \"}\")",
" .doTest(TestMode.TEXT_MATCH);",
"}");
@Test
void regexTest() throws FormatterException {
final Formatter FORMATTER = new Formatter();
Pattern pattern =
Pattern.compile("\\.addInputLines\\((\n.*?\".*?\",)\n(.*?)\\)\n", Pattern.DOTALL);
Matcher matcher = pattern.matcher(INPUT);
int count = matcher.groupCount();
if(!matcher.find()) {
System.out.println("no match!");
return;
}
String src = matcher.group(2);
System.out.println("\\\"foo\\\"".replaceAll("\\\\\"(.*?)\\\\\"", "\"$1\""));
}
}

View File

@@ -0,0 +1 @@
There's not much use to keep empty methods.

View File

@@ -0,0 +1,12 @@
## Problem
The results of the `BigDecimal` constructor can be somewhat unpredictable. One
might assume that writing `new BigDecimal(0.1)` in Java creates a `BigDecimal`
which is exactly equal to `0.1` (an unscaled value of `1`, with a scale of
`1`), but it is actually equal to
`0.1000000000000000055511151231257827021181583404541015625`.
This is because
`0.1` cannot be represented exactly as a `double` (or, for that matter, as a
binary fraction of any finite length). Thus, the value that is being passed in
to the constructor is not exactly equal to `0.1`, appearances notwithstanding.

View File

@@ -1,5 +1,6 @@
<?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">
<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>
@@ -231,11 +232,57 @@
<ignoredUnusedDeclaredDependencies>
<!-- XXX: Figure out why the plugin thinks this
dependency is unused. -->
<ignoredUnusedDeclaredDependency>${project.groupId}:refaster-support</ignoredUnusedDeclaredDependency>
<ignoredUnusedDeclaredDependency>${project.groupId}:refaster-support
</ignoredUnusedDeclaredDependency>
</ignoredUnusedDeclaredDependencies>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<!-- run annotation processor -->
<profile>
<id>run-annotation-processor</id>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>error_prone_docgen_processor</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>${version.auto-value}</version>
</path>
<path>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>${version.auto-service}</version>
</path>
<path>
<groupId>${project.groupId}</groupId>
<artifactId>error_prone_docgen_processor</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

98
generate-docs.sh Executable file
View File

@@ -0,0 +1,98 @@
#!/usr/bin/env bash
WEBSITE_FOLDER="website"
DOCS_FOLDER="docs"
BUGPATTERN_FOLDER="${WEBSITE_FOLDER}/bugpatterns"
BUGPATTERN_DOCS_FOLDER="${DOCS_FOLDER}/bugpatterns"
REFASTER_FOLDER="${WEBSITE_FOLDER}/refasterrules"
REFASTER_DOCS_FOLDER="${DOCS_FOLDER}/refasterrules"
HOMEPAGE="${WEBSITE_FOLDER}/index.md"
configure() {
cd "$(git rev-parse --show-toplevel || echo .)"
mkdir "${BUGPATTERN_FOLDER}" 2>/dev/null
mkdir "${REFASTER_FOLDER}" 2>/dev/null
}
generate_homepage() {
echo "Generating ${HOMEPAGE}"
cat > "${HOMEPAGE}" << EOF
---
# Do not modify. This file is generated.
layout: default
title: Home
nav_order: 1
---
EOF
cat "README.md" >> ${HOMEPAGE}
SEDOPTION="-i"
if [[ "$OSTYPE" == "darwin"* ]]; then
SEDOPTION="-i .bak"
fi
sed $SEDOPTION 's/src="/src="assets\/images\//g' ${HOMEPAGE}
sed $SEDOPTION 's/srcset="/srcset="assets\/images\//g' ${HOMEPAGE}
}
generate_bugpattern_docs() {
# The "mvn clean" is necessary since the wiki docs are generated by an
# annotation processor that also compiles the code. If Maven thinks the code
# does not need to be recompiled, the wiki docs will not be generated either.
mvn clean
# This will create markdown files for each bug pattern in `docgen/target/generated-wiki/bugpatterns`
mvn -P run-annotation-processor compile site -Dverification.skip
cp -r docgen/target/generated-wiki/bugpatterns/*.md website/bugpatterns
}
generate_refaster_docs() {
TEMPLATES=$(find error-prone-contrib/src/main/java/tech/picnic/errorprone/refastertemplates -type f -iname "*.java" ! -iname "package-info.java")
for TEMPLATE in $TEMPLATES; do
NAME=$(basename "${TEMPLATE}" ".java")
FILENAME="${REFASTER_FOLDER}/${NAME}.md"
EXTRA_DOCS=$(cat "${REFASTER_DOCS_FOLDER}/${NAME}.md" 2>/dev/null)
echo "Generating ${FILENAME}"
cat > "${FILENAME}" << EOF
---
layout: default
title: ${NAME}
parent: Refaster Rules
nav_order: 1
---
# ${NAME}
Style
{: .label .label-blue }
Error
{: .label .label-red }
${EXTRA_DOCS}
## Samples
\`\`\`java
public static void sample() {}
\`\`\`
<a href="https://github.com/PicnicSupermarket/error-prone-support/blob/master/error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/${NAME}.java" 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>
EOF
done
}
# Do it
configure
generate_homepage
generate_bugpattern_docs
generate_refaster_docs

View File

@@ -39,6 +39,8 @@
</developers>
<modules>
<module>docgen</module>
<module>docgen_processor</module>
<module>error-prone-contrib</module>
<module>refaster-compiler</module>
<module>refaster-runner</module>

16
website/.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
### Jekyll ###
_site/
.sass-cache/
.jekyll-cache/
.jekyll-metadata
Gemfile.lock
# Ignore folders generated by Bundler
.bundle/
vendor/
# Generated content
*.bak
index.md
bugpatterns/
refasterrules/

9
website/404.md Normal file
View File

@@ -0,0 +1,9 @@
---
layout: default
nav_exclude: true
search_exclude: true
---
## Page not found :(
The requested page could not be found.

4
website/Gemfile Normal file
View File

@@ -0,0 +1,4 @@
source "https://rubygems.org"
gem "github-pages", "~> 227"
gem "rake", "~> 13.0" # Required for "just-the-docs" theme
gem "webrick", "~> 1.7"

44
website/README.md Normal file
View File

@@ -0,0 +1,44 @@
# Error Prone Support website
This directory contains the majority of the source code that powers
[error-prone.picnic.tech][error-prone-support-website]. The website is
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:
```sh
bundle install
../generate-docs.sh && bundle exec jekyll serve --livereload
```
The website will now be [available][localhost-port-4000] on port 4000. Source
code modifications 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.
Documentation can be regenerated whist jekyll is running, by executing:
```sh
../generate-docs.sh
```
If you are not familiar with Jekyll, be sure to check out its
[documentation][jekyll-docs]. It is recommended to follow the provided
step-by-step tutorial.
# Deployment
The website is regenerated and deployed using the
[`deploy-website.yaml`][error-prone-support-website-deploy-workflow] GitHub
Actions workflow any time a change is merged to `master`.
[error-prone-support-website]: https://error-prone.picnic.tech
[error-prone-support-website-deploy-workflow]: https://github.com/PicnicSupermarket/error-prone-support/actions/workflows/deploy-website.yaml
[jekyll]: https://jekyllrb.com
[jekyll-docs]: https://jekyllrb.com/docs/
[jekyll-docs-installation]: https://jekyllrb.com/docs/installation/
[localhost-port-4000]: http://127.0.0.1:4000

49
website/_config.yml Normal file
View File

@@ -0,0 +1,49 @@
# General configuration.
title: Error Prone Support
logo: assets/images/favicon.svg
url: https://error-prone.picnic.tech/
description: >-
Error Prone Support is a Picnic-opinionated extension of Google's Error
Prone. It aims to improve code quality, focussing on maintainability,
consistency and avoidance of common gotchas.
remote_theme: just-the-docs/just-the-docs
plugins:
- jekyll-remote-theme
# Do not deploy through GitHub pages.
exclude:
- Gemfile
- Gemfile.lock
- README.md
- vendor
permalink: pretty # Use /doc/ vs. /doc.html
# Theme configuration.
search_enabled: true
heading_anchors: true
callouts:
summary:
title: Summary
color: blue
warning:
title: Warning
color: red
nav_external_links:
- title: Error Prone Support on GitHub
url: https://github.com/PicnicSupermarket/error-prone-support
hide_icon: false
twitter:
username: picnic
card: summary
social:
name: Picnic
links:
- https://github.com/PicnicSupermarket
- https://twitter.com/picnic
- https://www.linkedin.com/company/picnictechnologies

View File

@@ -0,0 +1,5 @@
<img src="/assets/images/picnic-logo@2x.png" alt="Picnic Logo" id="logo" />
<p align="center">
Copyright &copy; 2017-2022 Picnic Technologies BV
</p>

View File

@@ -0,0 +1,17 @@
<!-- Generated from https://realfavicongenerator.net/ -->
<link rel="apple-touch-icon" sizes="180x180" href="/assets/images/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/images/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/images/favicon-16x16.png">
<link rel="manifest" href="/assets/images/site.webmanifest">
<link rel="mask-icon" href="/assets/images/safari-pinned-tab.svg" color="#5bbad5">
<link rel="shortcut icon" href="/assets/images/favicon.ico">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-config" content="/assets/images/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<!-- Support light and dark mode, as it's not natively supported.
See: https://github.com/just-the-docs/just-the-docs/issues/234 -->
<link rel="stylesheet" href="{{ '/assets/css/just-the-docs-light.css' | relative_url }}"
media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="{{ '/assets/css/just-the-docs-dark.css' | relative_url }}"
media="(prefers-color-scheme: dark)">

View File

@@ -0,0 +1,24 @@
// We should override $nav-width(-md), however this breaks code highlighting and other styles.
// This appears an issue wrt the recommended way:
// https://github.com/just-the-docs/just-the-docs/issues/982
@include mq(lg) {
.side-bar {
min-width: 400px;
}
.site-nav, .site-header {
width: 400px;
}
}
// Add support for external anchor icons.
.external > svg {
width: 1rem;
vertical-align: text-bottom;
}
footer > img#logo {
width: 2rem;
margin: 0 auto;
display: block;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/assets/images/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 66 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 63 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="2623.000000pt" height="2623.000000pt" viewBox="0 0 2623.000000 2623.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,2623.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M12907 25594 c-1 -1 -87 -5 -190 -9 -104 -3 -207 -8 -230 -10 -23 -2
-78 -7 -122 -10 -44 -3 -109 -8 -145 -11 -36 -2 -78 -6 -95 -9 -16 -2 -59 -6
-95 -10 -36 -3 -76 -8 -90 -10 -14 -2 -50 -7 -80 -10 -30 -4 -66 -8 -80 -10
-14 -2 -61 -9 -105 -15 -44 -6 -91 -13 -105 -16 -14 -2 -41 -7 -60 -10 -461
-71 -1060 -217 -1545 -378 -124 -41 -379 -132 -410 -146 -11 -5 -67 -28 -125
-51 -505 -197 -1190 -545 -1630 -827 -82 -52 -373 -246 -385 -255 -5 -5 -53
-38 -105 -75 -239 -168 -595 -450 -796 -631 -27 -25 -93 -84 -145 -130 -114
-103 -545 -533 -650 -651 -156 -173 -189 -212 -316 -365 -32 -39 -71 -88 -88
-108 -16 -21 -35 -44 -40 -50 -23 -26 -207 -269 -282 -372 -118 -163 -384
-564 -437 -660 -6 -11 -43 -74 -82 -140 -39 -66 -88 -151 -108 -188 -20 -37
-46 -84 -58 -105 -12 -20 -71 -136 -132 -257 -101 -200 -142 -285 -199 -415
-11 -25 -31 -70 -44 -100 -72 -159 -275 -683 -298 -770 -3 -14 -12 -41 -20
-60 -21 -55 -127 -402 -149 -490 -2 -8 -18 -68 -36 -134 -17 -65 -34 -130 -36
-145 -3 -14 -16 -71 -29 -126 -24 -104 -37 -161 -36 -170 0 -2 -4 -19 -9 -36
-5 -18 -12 -50 -16 -73 -3 -22 -7 -43 -9 -46 -2 -4 -7 -29 -11 -56 -3 -27 -8
-52 -10 -55 -2 -4 -6 -24 -9 -45 -3 -22 -10 -61 -15 -89 -6 -27 -12 -68 -15
-90 -3 -22 -8 -51 -10 -65 -3 -14 -7 -42 -10 -62 -3 -21 -7 -55 -10 -75 -3
-21 -7 -49 -10 -63 -2 -14 -7 -52 -10 -85 -3 -33 -7 -71 -10 -85 -5 -28 -13
-106 -20 -195 -3 -33 -7 -78 -10 -100 -2 -22 -7 -83 -10 -135 -4 -52 -9 -122
-11 -155 -3 -33 -7 -269 -10 -525 -3 -256 -9 -468 -12 -472 -4 -3 -29 -19 -57
-35 -203 -116 -785 -512 -935 -636 -8 -7 -65 -51 -125 -97 -119 -92 -382 -308
-455 -374 -25 -23 -79 -70 -120 -106 -273 -240 -657 -632 -850 -870 -320 -394
-512 -753 -546 -1020 -25 -193 12 -365 112 -516 127 -192 309 -298 601 -348
174 -30 530 -26 747 8 17 3 54 8 81 11 28 4 57 8 65 10 8 2 35 7 60 10 25 3
52 8 60 10 8 2 31 7 50 10 19 3 87 17 150 31 63 15 121 27 130 29 36 8 266 69
395 106 109 31 572 182 632 205 8 4 11 -466 11 -1913 0 -1055 3 -1952 6 -1993
41 -532 186 -1020 438 -1475 82 -149 279 -437 355 -520 10 -11 45 -52 78 -90
109 -127 291 -302 440 -423 89 -72 374 -272 389 -272 2 0 33 -18 69 -39 82
-50 369 -191 388 -191 8 0 19 -4 25 -9 9 -9 52 -26 214 -82 182 -64 401 -114
660 -154 27 -4 95 -11 205 -21 135 -12 518 -5 615 10 14 3 52 7 85 11 33 3 69
8 80 10 11 3 37 7 57 10 21 2 57 9 80 14 24 6 57 12 73 15 17 3 66 15 110 26
44 12 88 23 98 25 101 22 417 140 572 214 53 25 82 34 86 27 4 -6 10 -36 13
-66 4 -30 11 -77 16 -105 5 -27 11 -61 14 -75 4 -27 13 -79 20 -120 7 -37 22
-111 51 -245 14 -66 27 -131 30 -145 3 -14 27 -115 55 -225 28 -110 52 -207
54 -215 11 -46 28 -107 33 -120 3 -8 7 -19 8 -25 9 -39 27 -104 35 -125 5 -14
23 -72 40 -130 63 -208 154 -476 227 -665 74 -190 87 -224 145 -360 111 -257
276 -584 372 -735 42 -65 150 -225 155 -230 4 -3 22 -26 41 -53 70 -96 196
-227 288 -301 159 -127 307 -186 481 -191 401 -11 693 267 904 861 58 163 130
425 152 554 3 19 10 53 15 75 5 22 12 60 15 85 4 24 8 47 10 50 1 3 6 28 9 55
4 28 9 57 11 65 2 8 7 36 10 62 5 46 8 73 20 153 6 43 10 75 20 170 4 33 8 71
10 85 1 14 5 61 9 105 4 44 9 96 11 115 4 34 19 257 24 366 1 21 2 21 119 -12
124 -36 334 -88 392 -98 19 -3 46 -8 60 -11 14 -3 36 -7 50 -10 14 -3 34 -7
45 -9 32 -6 226 -35 278 -41 26 -3 66 -7 90 -10 245 -31 756 -36 997 -11 19 2
67 7 105 11 71 6 80 7 170 19 159 21 359 60 575 111 193 45 635 192 712 235
29 16 38 12 81 -37 211 -238 328 -363 532 -568 197 -197 293 -283 429 -385 26
-19 53 -40 60 -47 24 -21 239 -164 311 -206 69 -41 319 -167 330 -167 3 0 24
-8 48 -19 219 -98 513 -170 797 -197 54 -5 382 -5 408 1 13 2 52 7 86 10 68 6
255 38 298 51 9 3 25 6 35 8 86 17 315 89 423 133 251 102 409 200 545 338
149 151 214 297 220 492 9 309 -179 590 -595 887 -138 99 -400 252 -560 326
l-66 31 -53 104 c-28 57 -72 141 -97 186 -24 45 -43 84 -41 86 2 1 28 -7 58
-18 78 -29 296 -97 379 -118 71 -18 221 -49 270 -57 84 -13 75 -11 220 -29
174 -22 537 -22 723 -1 26 3 68 8 94 11 111 12 280 45 416 81 32 8 68 17 80
20 65 12 444 150 504 183 10 6 69 35 131 66 98 48 327 179 342 195 3 3 38 27
78 54 40 27 101 71 135 97 34 27 67 52 72 56 77 57 356 325 440 423 28 32 139
170 157 195 4 5 18 24 31 40 85 108 258 396 337 560 108 226 224 560 266 768
25 125 50 281 59 377 3 25 8 70 12 100 3 30 7 930 8 2000 2 1667 4 1945 16
1942 8 -2 106 -34 219 -72 175 -58 327 -107 365 -116 6 -1 51 -14 100 -29 106
-31 397 -105 440 -112 17 -2 55 -11 85 -18 30 -8 82 -18 115 -24 33 -6 74 -13
90 -16 279 -51 619 -72 848 -52 379 34 643 195 764 465 75 169 78 383 8 599
-38 115 -152 341 -245 482 -183 280 -375 506 -715 846 -198 199 -347 339 -494
465 -36 30 -72 62 -80 70 -9 8 -66 55 -126 105 -61 49 -115 94 -121 100 -6 5
-26 21 -44 35 -18 14 -81 62 -139 107 -133 103 -129 101 -311 235 -165 121
-495 341 -695 462 l-80 48 -1 346 c0 304 -9 625 -19 732 -2 19 -6 78 -10 130
-4 52 -8 108 -10 125 -2 16 -6 59 -9 95 -6 64 -9 88 -21 185 -11 88 -14 115
-19 153 -3 20 -8 57 -11 82 -3 25 -8 56 -10 70 -3 14 -7 40 -10 58 -3 17 -7
44 -10 60 -2 15 -7 43 -10 62 -18 111 -30 174 -65 345 -22 105 -49 228 -60
275 -11 47 -23 95 -25 108 -11 47 -77 293 -115 422 -221 761 -566 1552 -987
2265 -97 163 -104 175 -278 438 -688 1039 -1542 1917 -2595 2667 -133 95 -576
380 -590 380 -6 0 -10 5 -10 10 0 6 -4 10 -9 10 -5 0 -55 27 -111 60 -56 33
-104 60 -107 60 -3 0 -17 8 -31 18 -38 27 -156 89 -397 212 -546 277 -1134
507 -1740 680 -99 29 -187 53 -195 55 -8 2 -89 22 -180 44 -91 23 -178 44
-195 47 -16 3 -46 9 -65 14 -19 5 -51 12 -70 15 -19 3 -42 8 -50 10 -8 2 -26
6 -40 9 -38 6 -142 25 -170 31 -33 7 -127 23 -170 29 -19 3 -48 8 -65 11 -64
11 -92 15 -140 20 -27 3 -75 10 -105 15 -30 5 -86 12 -125 15 -38 4 -77 8 -85
10 -28 5 -171 18 -330 30 -52 4 -111 8 -130 10 -114 10 -835 26 -843 19z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/assets/images/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/assets/images/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

6
website/bugpatterns.md Normal file
View File

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

6
website/refasterrules.md Normal file
View File

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