[#5899] jOOQ-checker should provide both checker-framework and ErrorProne checks

This commit is contained in:
lukaseder 2019-02-14 12:17:44 +01:00
parent 8f151a3d3b
commit 83fc7d7333
7 changed files with 172 additions and 192 deletions

View File

@ -53,7 +53,19 @@
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotation</artifactId>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
</dependency>

View File

@ -37,23 +37,15 @@
*/
package org.jooq.checker;
import static org.checkerframework.javacutil.TreeUtils.elementFromDeclaration;
import static org.checkerframework.javacutil.TreeUtils.enclosingClass;
import static org.checkerframework.javacutil.TreeUtils.enclosingMethod;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import javax.lang.model.element.Element;
import org.jooq.checker.Tools.Printer;
import org.checkerframework.framework.source.Result;
import org.checkerframework.framework.source.SourceChecker;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.util.TreePath;
/**
* Common base class for checkers.
*
@ -61,15 +53,12 @@ import com.sun.source.util.TreePath;
*/
abstract class AbstractChecker extends SourceChecker {
void error(Object node, String message) {
Void error(Object node, String message) {
getChecker().report(Result.failure(message, node), node);
return null;
}
void warn(Object node, String message) {
getChecker().report(Result.warning(message, node), node);
}
static void print(Printer printer) {
static Void print(Printer printer) {
try (PrintWriter writer = new PrintWriter(new FileWriter("error.txt"))){
writer.println("This is probably a bug in jOOQ-checker.");
writer.println("If you think this is a bug in jOOQ, please report it here: https://github.com/jOOQ/jOOQ/issues/new");
@ -78,19 +67,7 @@ abstract class AbstractChecker extends SourceChecker {
printer.print(writer);
}
catch (IOException ignore) {}
}
static Element enclosing(TreePath path) {
MethodTree enclosingMethod = enclosingMethod(path);
if (enclosingMethod != null)
return elementFromDeclaration(enclosingMethod);
ClassTree enclosingClass = enclosingClass(path);
return elementFromDeclaration(enclosingClass);
}
interface Printer {
void print(PrintWriter writer);
return null;
}
}

View File

@ -38,14 +38,7 @@
package org.jooq.checker;
import static com.sun.source.util.TreePath.getPath;
import static org.checkerframework.javacutil.TreeUtils.elementFromUse;
import java.io.PrintWriter;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import org.jooq.Allow;
import org.jooq.PlainSQL;
import org.checkerframework.framework.source.SourceVisitor;
@ -66,38 +59,12 @@ public class PlainSQLChecker extends AbstractChecker {
@Override
public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
try {
ExecutableElement elementFromUse = elementFromUse(node);
PlainSQL plainSQL = elementFromUse.getAnnotation(PlainSQL.class);
// In the absence of a @PlainSQL annotation,
// all jOOQ API method calls will type check.
if (plainSQL != null) {
boolean allowed = false;
Element enclosing = enclosing(getPath(root, node));
moveUpEnclosingLoop:
while (enclosing != null) {
if (enclosing.getAnnotation(Allow.PlainSQL.class) != null) {
allowed = true;
break moveUpEnclosingLoop;
}
enclosing = enclosing.getEnclosingElement();
}
if (!allowed)
error(node, "Plain SQL usage not allowed at current scope. Use @Allow.PlainSQL.");
}
}
catch (final Exception e) {
print(new Printer() {
@Override
public void print(PrintWriter t) {
e.printStackTrace(t);
}
});
}
Tools.checkPlainSQL(
node,
() -> Tools.enclosing(getPath(root, node)),
message -> error(node, message),
printer -> print(printer)
);
return super.visitMethodInvocation(node, p);
}

View File

@ -38,16 +38,7 @@
package org.jooq.checker;
import static com.sun.source.util.TreePath.getPath;
import static java.util.Arrays.asList;
import static org.checkerframework.javacutil.TreeUtils.elementFromUse;
import java.io.PrintWriter;
import java.util.EnumSet;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import org.jooq.Allow;
import org.jooq.Require;
import org.jooq.SQLDialect;
import org.jooq.Support;
@ -70,85 +61,12 @@ public class SQLDialectChecker extends AbstractChecker {
@Override
public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
try {
ExecutableElement elementFromUse = elementFromUse(node);
Support support = elementFromUse.getAnnotation(Support.class);
// In the absence of a @Support annotation, all jOOQ API method calls will type check.
if (support != null) {
Element enclosing = enclosing(getPath(root, node));
// [#7929] "Empty" @Support annotations expand to all SQLDialects
EnumSet<SQLDialect> supported = EnumSet.copyOf(
support.value().length > 0
? asList(support.value())
: asList(SQLDialect.values())
);
EnumSet<SQLDialect> allowed = EnumSet.noneOf(SQLDialect.class);
EnumSet<SQLDialect> required = EnumSet.noneOf(SQLDialect.class);
boolean evaluateRequire = true;
while (enclosing != null) {
Allow allow = enclosing.getAnnotation(Allow.class);
if (allow != null)
allowed.addAll(asList(allow.value()));
if (evaluateRequire) {
Require require = enclosing.getAnnotation(Require.class);
if (require != null) {
evaluateRequire = false;
required.clear();
required.addAll(asList(require.value()));
}
}
enclosing = enclosing.getEnclosingElement();
}
if (allowed.isEmpty())
error(node, "No jOOQ API usage is allowed at current scope. Use @Allow.");
boolean allowedFail = true;
allowedLoop:
for (SQLDialect a : allowed) {
for (SQLDialect s : supported) {
if (a.supports(s)) {
allowedFail = false;
break allowedLoop;
}
}
}
if (allowedFail)
error(node, "The allowed dialects in scope " + allowed + " do not include any of the supported dialects: " + supported);
boolean requiredFail = false;
requiredLoop:
for (SQLDialect r : required) {
for (SQLDialect s : supported)
if (r.supports(s))
continue requiredLoop;
requiredFail = true;
break requiredLoop;
}
if (requiredFail)
error(node, "Not all of the required dialects " + required + " from the current scope are supported " + supported);
}
}
catch (final Exception e) {
print(new Printer() {
@Override
public void print(PrintWriter t) {
e.printStackTrace(t);
}
});
}
Tools.checkSQLDialect(
node,
() -> Tools.enclosing(getPath(root, node)),
message -> error(node, message),
printer -> print(printer)
);
return super.visitMethodInvocation(node, p);
}

View File

@ -9,4 +9,18 @@ $ pwd
$ cd jOOQ-examples/jOOQ-checker-framework-example
...
$ mvn clean install
```
The above runs a build without any checkers or matchers.
In order to use the checker framework (supports only Java 8, currently), run
```
$ mvn clean install -P checker-framework
```
In order to use ErrorProne, run
```
$ mvn clean install -P error-prone
```

View File

@ -19,6 +19,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.jooq.version>3.12.0-SNAPSHOT</org.jooq.version>
<java.version>1.8</java.version>
</properties>
<dependencies>
@ -36,42 +37,107 @@
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<goals>
<goal>properties</goal>
</goals>
</execution>
</executions>
</plugin>
<profiles>
<profile>
<id>checker-framework</id>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<!-- [java-9] -->
<release>10</release>
<!-- [/java-9] -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<goals>
<goal>properties</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- IntelliJ needs these https://youtrack.jetbrains.com/issue/IDEA-195472 -->
<source>10</source>
<target>10</target>
<fork>true</fork>
<annotationProcessors>
<annotationProcessor>org.jooq.checker.SQLDialectChecker</annotationProcessor>
<!-- <annotationProcessor>org.jooq.checker.PlainSQLChecker</annotationProcessor> -->
</annotationProcessors>
<compilerArgs>
<arg>-Xbootclasspath/p:1.8</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<!-- [java-9] -->
<release>${java.version}</release>
<!-- [/java-9] -->
<!-- IntelliJ needs these https://youtrack.jetbrains.com/issue/IDEA-195472 -->
<source>${java.version}</source>
<target>${java.version}</target>
<fork>true</fork>
<annotationProcessors>
<annotationProcessor>org.jooq.checker.SQLDialectChecker</annotationProcessor>
<!-- <annotationProcessor>org.jooq.checker.PlainSQLChecker</annotationProcessor> -->
</annotationProcessors>
<compilerArgs>
<arg>-Xbootclasspath/p:1.8</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<!-- Please refer to https://errorprone.info/docs/installation for details -->
<profile>
<id>error-prone</id>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<compilerId>javac-with-errorprone</compilerId>
<forceJavacCompilerUse>true</forceJavacCompilerUse>
<!-- [java-9] -->
<release>${java.version}</release>
<!-- [/java-9] -->
<!-- IntelliJ needs these https://youtrack.jetbrains.com/issue/IDEA-195472 -->
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.jooq</groupId>
<artifactId>jooq-checker</artifactId>
<version>3.12.0-SNAPSHOT</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-Xbootclasspath/p:1.8</arg>
<arg>-XDcompilePolicy=simple</arg>
<arg>-Xplugin:ErrorProne</arg>
</compilerArgs>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-javac-errorprone</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>${org.jooq.version}</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-checker</artifactId>
<version>${org.jooq.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

26
pom.xml
View File

@ -168,6 +168,32 @@
<artifactId>spring-context</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<!-- checker framework and error prone dependency for use with jOOQ-checker -->
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>2.3.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotation</artifactId>
<version>2.3.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc4</version>
<optional>true</optional>
</dependency>
</dependencies>
</dependencyManagement>