typetools / checker-framework

Pluggable type-checking for Java
http://checkerframework.org/
Other
990 stars 347 forks source link

The Checker Framework crashes when @Builder and @Singular are used together #4254

Closed RamAnvesh closed 3 years ago

RamAnvesh commented 3 years ago

Checker Framework versions: checker-3.10.0.jar checker-util-3.10.0.jar checker-qual-3.10.0.jar

javac Version: javac-9-dev-r4023-3.jar

import java.util.List;

@Builder public class LombokTest { @Singular List strings; }

 * outputs

[ERROR] error: SourceChecker.typeProcess: unexpected Throwable (ClassCastException) while processing LombokTest.java; message: java.lang.Integer cannot be cast to java.lang.Void [ERROR] ; The Checker Framework crashed. Please report the crash. Compilation unit: LombokTest.java Last visited tree at line 8 column 1: @Builder Exception: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Void; java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Void at org.checkerframework.dataflow.cfg.node.NullLiteralNode.getValue(NullLiteralNode.java:30) at org.checkerframework.dataflow.cfg.node.NullLiteralNode.getValue(NullLiteralNode.java:16) at org.checkerframework.dataflow.cfg.node.ValueLiteralNode.equals(ValueLiteralNode.java:58) at org.checkerframework.dataflow.cfg.node.NullLiteralNode.equals(NullLiteralNode.java:45) at java.util.ArrayList.indexOf(ArrayList.java:321) at java.util.ArrayList.contains(ArrayList.java:304) at org.checkerframework.dataflow.analysis.AbstractAnalysis.getValue(AbstractAnalysis.java:199) at org.checkerframework.dataflow.analysis.AbstractAnalysis.getValue(AbstractAnalysis.java:282) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.getInferredValueFor(GenericAnnotatedTypeFactory.java:1745) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.addComputedTypeAnnotations(GenericAnnotatedTypeFactory.java:1696) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.addComputedTypeAnnotations(GenericAnnotatedTypeFactory.java:1643) at org.checkerframework.framework.type.AnnotatedTypeFactory.getAnnotatedType(AnnotatedTypeFactory.java:1058) at org.checkerframework.framework.type.AnnotatedTypeFactory.binaryTreeArgTypes(AnnotatedTypeFactory.java:2604) at org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator.visitBinary(PropagationTreeAnnotator.java:142) at org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator.visitBinary(PropagationTreeAnnotator.java:33) at com.sun.tools.javac.tree.JCTree$JCBinary.accept(JCTree.java:1993) at com.sun.source.util.SimpleTreeVisitor.visit(SimpleTreeVisitor.java:80) at org.checkerframework.framework.type.treeannotator.ListTreeAnnotator.defaultAction(ListTreeAnnotator.java:52) at org.checkerframework.framework.type.treeannotator.ListTreeAnnotator.defaultAction(ListTreeAnnotator.java:20) at com.sun.source.util.SimpleTreeVisitor.visitBinary(SimpleTreeVisitor.java:515) at org.checkerframework.framework.type.treeannotator.TreeAnnotator.visitBinary(TreeAnnotator.java:55) at org.checkerframework.framework.type.treeannotator.TreeAnnotator.visitBinary(TreeAnnotator.java:21) at com.sun.tools.javac.tree.JCTree$JCBinary.accept(JCTree.java:1993) at com.sun.source.util.SimpleTreeVisitor.visit(SimpleTreeVisitor.java:80) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.addComputedTypeAnnotations(GenericAnnotatedTypeFactory.java:1683) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.addComputedTypeAnnotations(GenericAnnotatedTypeFactory.java:1643) at org.checkerframework.framework.type.AnnotatedTypeFactory.getAnnotatedType(AnnotatedTypeFactory.java:1058) at org.checkerframework.framework.flow.CFAbstractTransfer.getValueFromFactory(CFAbstractTransfer.java:237) at org.checkerframework.framework.flow.CFAbstractTransfer.visitNode(CFAbstractTransfer.java:634) at org.checkerframework.framework.flow.CFAbstractTransfer.visitNode(CFAbstractTransfer.java:97) at org.checkerframework.dataflow.cfg.node.AbstractNodeVisitor.visitEqualTo(AbstractNodeVisitor.java:190) at org.checkerframework.framework.flow.CFAbstractTransfer.visitEqualTo(CFAbstractTransfer.java:787) at org.checkerframework.framework.flow.CFAbstractTransfer.visitEqualTo(CFAbstractTransfer.java:97) at org.checkerframework.dataflow.cfg.node.EqualToNode.accept(EqualToNode.java:31) at org.checkerframework.dataflow.analysis.AbstractAnalysis.callTransferFunction(AbstractAnalysis.java:340) at org.checkerframework.dataflow.analysis.ForwardAnalysisImpl.callTransferFunction(ForwardAnalysisImpl.java:390) at org.checkerframework.dataflow.analysis.ForwardAnalysisImpl.performAnalysisBlock(ForwardAnalysisImpl.java:130) at org.checkerframework.dataflow.analysis.ForwardAnalysisImpl.performAnalysis(ForwardAnalysisImpl.java:107) at org.checkerframework.framework.flow.CFAbstractAnalysis.performAnalysis(CFAbstractAnalysis.java:109) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.analyze(GenericAnnotatedTypeFactory.java:1385) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.performFlowAnalysis(GenericAnnotatedTypeFactory.java:1293) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.checkAndPerformFlowAnalysis(GenericAnnotatedTypeFactory.java:1730) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.preProcessClassTree(GenericAnnotatedTypeFactory.java:360) at org.checkerframework.common.basetype.BaseTypeVisitor.visitClass(BaseTypeVisitor.java:328) at org.checkerframework.common.basetype.BaseTypeVisitor.visitClass(BaseTypeVisitor.java:176) at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:808) at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:56) at org.checkerframework.framework.source.SourceVisitor.visit(SourceVisitor.java:82) at org.checkerframework.framework.source.SourceChecker.typeProcess(SourceChecker.java:976) at org.checkerframework.common.basetype.BaseTypeChecker.typeProcess(BaseTypeChecker.java:507) at org.checkerframework.common.basetype.BaseTypeChecker.typeProcess(BaseTypeChecker.java:500) at org.checkerframework.javacutil.AbstractTypeProcessor$AttributionTaskListener.finished(AbstractTypeProcessor.java:190) at com.sun.tools.javac.api.ClientCodeWrapper$WrappedTaskListener.finished(ClientCodeWrapper.java:817) at com.sun.tools.javac.api.MultiTaskListener.finished(MultiTaskListener.java:120) at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1425) at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1384) at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:980) at com.sun.tools.javac.main.Main.compile(Main.java:302) at com.sun.tools.javac.main.Main.compile(Main.java:162) at com.sun.tools.javac.Main.compile(Main.java:55) at com.sun.tools.javac.Main.main(Main.java:41) error: SourceChecker.typeProcess: unexpected Throwable (ClassCastException) while processing LombokTest.java; message: java.lang.Integer cannot be cast to java.lang.Void ; The Checker Framework crashed. Please report the crash. Compilation unit: LombokTest.java Last visited tree at line 8 column 1: @Builder Exception: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Void; java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Void at org.checkerframework.dataflow.cfg.node.NullLiteralNode.getValue(NullLiteralNode.java:30) at org.checkerframework.dataflow.cfg.node.NullLiteralNode.getValue(NullLiteralNode.java:16) at org.checkerframework.dataflow.cfg.node.ValueLiteralNode.equals(ValueLiteralNode.java:58) at org.checkerframework.dataflow.cfg.node.NullLiteralNode.equals(NullLiteralNode.java:45) at java.util.ArrayList.indexOf(ArrayList.java:321) at java.util.ArrayList.contains(ArrayList.java:304) at org.checkerframework.dataflow.analysis.AbstractAnalysis.getValue(AbstractAnalysis.java:199) at org.checkerframework.dataflow.analysis.TransferInput.getValueOfSubNode(TransferInput.java:178) at org.checkerframework.framework.flow.CFAbstractTransfer.visitEqualTo(CFAbstractTransfer.java:792) at org.checkerframework.framework.flow.CFAbstractTransfer.visitEqualTo(CFAbstractTransfer.java:97) at org.checkerframework.dataflow.cfg.node.EqualToNode.accept(EqualToNode.java:31) at org.checkerframework.dataflow.analysis.AbstractAnalysis.callTransferFunction(AbstractAnalysis.java:340) at org.checkerframework.dataflow.analysis.ForwardAnalysisImpl.callTransferFunction(ForwardAnalysisImpl.java:390) at org.checkerframework.dataflow.analysis.ForwardAnalysisImpl.performAnalysisBlock(ForwardAnalysisImpl.java:130) at org.checkerframework.dataflow.analysis.ForwardAnalysisImpl.performAnalysis(ForwardAnalysisImpl.java:107) at org.checkerframework.framework.flow.CFAbstractAnalysis.performAnalysis(CFAbstractAnalysis.java:109) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.analyze(GenericAnnotatedTypeFactory.java:1385) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.performFlowAnalysis(GenericAnnotatedTypeFactory.java:1293) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.checkAndPerformFlowAnalysis(GenericAnnotatedTypeFactory.java:1730) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.preProcessClassTree(GenericAnnotatedTypeFactory.java:360) at org.checkerframework.common.basetype.BaseTypeVisitor.visitClass(BaseTypeVisitor.java:328) at org.checkerframework.common.basetype.BaseTypeVisitor.visitClass(BaseTypeVisitor.java:176) at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:808) at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:56) at org.checkerframework.framework.source.SourceVisitor.visit(SourceVisitor.java:82) at org.checkerframework.framework.source.SourceChecker.typeProcess(SourceChecker.java:976) at org.checkerframework.common.basetype.BaseTypeChecker.typeProcess(BaseTypeChecker.java:507) at org.checkerframework.javacutil.AbstractTypeProcessor$AttributionTaskListener.finished(AbstractTypeProcessor.java:190) at com.sun.tools.javac.api.ClientCodeWrapper$WrappedTaskListener.finished(ClientCodeWrapper.java:817) at com.sun.tools.javac.api.MultiTaskListener.finished(MultiTaskListener.java:120) at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1425) at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1384) at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:980) at com.sun.tools.javac.main.Main.compile(Main.java:302) at com.sun.tools.javac.main.Main.compile(Main.java:162) at com.sun.tools.javac.Main.compile(Main.java:55) at com.sun.tools.javac.Main.main(Main.java:41)


 * expectation.
No Crash
mernst commented 3 years ago

Thanks for getting in touch. I'm sorry you are having trouble. Unfortunately, I cannot reproduce the problem, because you did not provide a command that can be pasted into a command shell.

Using the version of the Checker Framework at HEAD, I ran:

javacheck -processor nullness -cp /home/mernst/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.18/481f5bfed3ae29f656eedfe9e98c8365b8ba5c57/lombok-1.18.18.jar LombokTest.java

and the output was:

LombokTest.java:7: error: [initialization.field.uninitialized] the default constructor does not initialize field strings
@Singular List strings;
               ^
1 error

If you can provide a way to reproduce your problem, we will take a look. Thanks!

RamAnvesh commented 3 years ago

I upgraded to lombok-1.18.18 and this fixed the issue.

Sorry I tried to reproduce the issue on commandline and I am not able to do this either for 1.18.16 or 1.18.18 But in Maven, the framework is reliably crashing when using 1.18.16 but not when using 1.18.18.

I am unblocked. Please feel free to close this issue/investigate further as you see fit. Please let me know if I can help in some way - given the commandline is behaving differently from maven based use.

Thanks.

mernst commented 3 years ago

Thanks for investigating. I'm glad that upgrading worked around the problem.

I got the same result (no crash) with lombok-1.18.16.jar on the classpath. (This test was again using the Checker Framework from HEAD.)

You could make the problem reproducible for us by providing a complete Maven project (including the pom.xml file and the source code files); then the command to reproduce would be a mvn command.

Or, you could close this issue.

RamAnvesh commented 3 years ago

Created repo: https://github.com/RamAnvesh/checker-framework-4254

Command: mvn clean install

RamAnvesh commented 3 years ago

@mernst Is this maven project (https://github.com/RamAnvesh/checker-framework-4254) sufficient to reproduce the issue?

I think the issue with the command line is that though Lombok is on the classpath, it is not acting as an annotation processor and hence is not generating any bytecode. That's why the error we are getting is that which we would have got if there were no Lombok annotations in the class (no constructor), where as the issue seems to be in the generated byte code. The above maven project invokes lombok annotation processor and hence is reproducing the issue.

mernst commented 3 years ago

I can reproduce the problem by running

git clone https://github.com/RamAnvesh/checker-framework-4254
cd checker-framework-4254
mvn clean install

Thanks for the details.

mernst commented 3 years ago

When running the Checker Framework and Lombok together, you need to run delombok. This is explained in the section Type-checking code with Lombok annotations in the Checker Framework Manual. I don't see that in your Maven build file, however. Could you please correct the build file?

an-cwi commented 3 years ago

I'm using the CF Gradle plugin and it's been working fine with various Lombok annotations for a long time. Just today I tried using @Builder and @Singular together, and have the same issue, so it seems to be something specific to that combination.

I tried to create a minimal reproduction of the issue, but unfortunately was unsuccessful (the Gradle build succeeds).

Versions: CF Gradle plugin: 0.5.17 CF: 3.11.0

mernst commented 3 years ago

@an-cwi Thanks for chiming in. We are all frustrated by the inability to reproduce the issue.

If I understand correctly, you have a reproduction of the issue, but it's not minimal. Can you share the non-minimal example? If not, would it be possible for you to turn on some debugging options and share the logs with us?

https://github.com/an-cwi/checker-framework-4254/blob/main/build.gradle suggests that you are using lombok-1.18.16. @RamAnvesh upgraded from lombok-1.18.16 to lombok-1.18.18 and that fixed the problem for him. Could you please try that?

an-cwi commented 3 years ago

Tried updating lombok (pushed to repo), but no change.

I wouldn't be able to share the full reproduction unfortunately (private corporate repo), and there are a lot of config differences between it and minimal repo, so it would take a while to isolate systematically. For starters, I can certainly enable some extra debugging options and share logs, though. Suggestions?

mernst commented 3 years ago

Thanks!

  1. Can you share the stack trace? Is it the same as the stack trace in the original bug report? That one shows that in NullLiteralNode.getValue, the LiteralTree for the NullLiteralNode contains an Integer, which should not be possible.
public class NullLiteralNode extends ValueLiteralNode {

    /**
     * Create a new NullLiteralNode.
     *
     * @param t the tree for the literal value
     */
    public NullLiteralNode(LiteralTree t) {
        super(t);
        assert t.getKind() == Tree.Kind.NULL_LITERAL;
    }

    @Override
    public Void getValue() {
        return (Void) tree.getValue();
    }
  1. Can you run with assertions enabled? I am wondering whether:

    • the NullLiteralNode constructor is called with an Integer LiteralTree but the assertion is disabled, or
    • Lombok changes the AST later.
  2. Can you run using a special version of the Checker Framework (I would supply jar files with extra diagnostics), or is that inconvenient and you only want to use the Checker Framework from Maven Central?

an-cwi commented 3 years ago
  1. The stack trace appears to be identical with respect to CF, only differing when you get down to where Gradle invokes javac stack-trace.txt

  2. I haven't found an option to run CF with assertions enabled. It appears the -ea flag is only available when running java, vs. when compiling it, and I'm not familiar with how CF itself is invoked during compilation. Could you please guide me?

  3. I think I could run locally with a custom JAR

mernst commented 3 years ago

Thanks for the stack trace. It's good to know that the symptoms are the same.

javac is a Java program that runs on a JVM, and you can pass command-line arguments to that JVM using the -J command-line argument. For example, you can pass -J-ea to javac to enable assertions. (Note there is only one, not two, hyphens before the "ea".)

But, I'll send you a jar instead. Do you want it here or (to reduce clutter here) as a gist or directly by email?

an-cwi commented 3 years ago

Thanks for the explanation of passing args to the JVM. I see that noted in the CF manual now (though not about assertions specifically). It turns out that in Gradle -ea can be added directly through CompileOptions.forkOptions.jvmArgs. While it doesn't look like the CF Gradle plugin itself has support for forkOptions.jvmArgs, I configured the compileJava task directly to add the -ea flag.

Error output is the same, no assertions failing.

Let's follow up offline to continue debugging.

mernst commented 3 years ago

This is indeed a bug in Lombok: https://github.com/rzwitserloot/lombok/issues/2695 . It was fixed in release lombok-1.18.18.

Two mysteries remain:

an-cwi commented 3 years ago

Turns out updating to lombok:1.18.18 did fix the issue in my full project; really sorry for not trying that sooner. Not sure about the minimal reproduction though...

mernst commented 3 years ago

I'm glad this is working for you now, and you have confirmed that the fix for the original poster worked for you too. I had interpreted your comment "Tried updating lombok (pushed to repo), but no change." as saying that you had tried Lombok 1.18.18 as the original poster had.

I'm still curious why Lombok and the Checker Framework ran at the same time; this seems like a possible problem with the Gradle plugin.

an-cwi commented 3 years ago

Yeah, if I remember correctly, I only tried updating lombok in the minimal reproduction at that point. Clearly I should have also tried it in the main project at the same time. Apologies again for the wild goose chase.

mernst commented 3 years ago

The main thing is that the problem is resolved for you. That is great. Please let us know if you have other problems. I'm going to close this issue now.