kelloggm / checkerframework-gradle-plugin

Gradle plugin to use the Checker Framework for Java
Apache License 2.0
66 stars 15 forks source link

Compile tasks for additional source sets don't include the checker framework dependency #247

Open beikov opened 1 year ago

beikov commented 1 year ago

Originally reported here https://github.com/typetools/checker-framework/issues/5787, the problem is that an additional source set which I added did not receive the compileOnly dependencies like the main and test source sets. This leads to this strange error

Caused by: java.lang.NullPointerException: Cannot invoke "java.util.TreeSet.clear()" because "this.messageStore" is null
    at org.checkerframework.common.basetype.BaseTypeChecker.typeProcess(BaseTypeChecker.java:537)
    at org.checkerframework.javacutil.AbstractTypeProcessor$AttributionTaskListener.finished(AbstractTypeProcessor.java:188)
    at jdk.compiler/com.sun.tools.javac.api.ClientCodeWrapper$WrappedTaskListener.finished(ClientCodeWrapper.java:854)
    at jdk.compiler/com.sun.tools.javac.api.MultiTaskListener.finished(MultiTaskListener.java:132)
    at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1394)
    at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1351)
    at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:946)
    at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:104)
    at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.invocationHelper(JavacTaskImpl.java:152)
    ... 31 more

because the compile task doesn't have the -qual artifact in the compileOnly configuration. I understand that this might be a self made problem, but I think that the plugin should try to add the dependencies to all compile tasks.

kelloggm commented 1 year ago

Thanks for reporting and figuring out the root cause! I would have expected the plugin to handle your use case properly: in some places, we're applying CF options to all tasks derived from AbstractCompile, but we don't do that when adding dependencies.

The reason we don't is that AbstractCompile doesn't give us the right APIs to add checker.jar to the processorPath in a safe way (and, depending on the rest of the project's configuration, we might not want to do so blindly, because of e.g., the need to avoid/continue triggering annotation processor search).

However, your issue is with checker-qual.jar rather than checker.jar. So, one solution would be to maintain the current behavior for the checkers themselves (which we definitely don't want to accidentally put onto a classpath where they don't belong, because they're a large dependency), but to blindly add checker-qual to the classpath of any AbstractCompile task we come across (using the recommended method in the Gradle docs to add a dependency to all tasks matching a particular type). The downside of this approach is that we might accidentally add checker-qual to some classpaths where it doesn't belong. That jar is small (it only contains the annotations), so the cost there is relatively low even if we add it somewhere it doesn't belong, but users might not like that behavior.

chaosflaws commented 8 months ago

@kelloggm FWIW, I did run into this issue today, with a very simple setup. In fact, simply applying the plugin with at least one checker and adding one source set triggers the error. MWE

You suggested adding checker-qual blindly to all AbstractCompile tasks - in this case, an easier and more conservative approach might be to detect what is happening and make the error message more clear?

@all A workaround is to simply add a compile-only dependency on the offending source set:

sourceSets {
    abcd {}
}

dependencies {
    abcdCompileOnly 'org.checkerframework:checker-qual:3.42.0'
}