eisop / checker-framework

Pluggable type-checking for Java
https://eisop.github.io/
Other
19 stars 18 forks source link

Crash when both branches of a `ConditionalExpression` are the `null` literal #819

Open fonghou opened 2 months ago

fonghou commented 2 months ago

Thanks for submitting an issue. As explained in the instructions for submitting an issue at https://eisop.github.io/cf/manual/#reporting-bugs, please include four pieces of information:

BASE_DIR=~/github/javac-diagnostics-wrapper

export CLASSPATH=$BASE_DIR/build/libs/javac-diagnostics-wrapper-all.jar:$BASE_DIR/checker/dist/checker.jar

export JAVA_TOOL_OPTIONS="--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"

java io.github.wmdietl.diagnostics.json.javac.Main -processor org.checkerframework.checker.nullness.NullnessChecker "$@" 2>/dev/null | tee checker.log

 * inputs,

import org.jspecify.annotations.*;

public class CheckNull { public static void main(String[] args) { // OK: nullToEmpty accepts null but won't return it int length1 = nullToEmpty(null).length();

// Not OK: emptyToNull doesn't accept null; also, it might return null!
// emptyToNull(null).length();

}

static @Nullable String emptyToNull(@NonNull String x) { return x.isEmpty() ? null : null; // <<<< this is the line triggered the crash. }

static @NonNull String nullToEmpty(@Nullable String x) { return x == null ? "" : x; } }

 * outputs, and
 [checker.log](https://github.com/user-attachments/files/16327103/checker.log)
 * expectation.

"SourceChecker.typeProcess: unexpected Throwable (AssertionError) while processing src/main/java/cljweb/CheckNull.java; message: unexpected type: ; The Checker Framework crashed. Please report the crash. Version: Checker Framework 3.42.0-eisop4. Checker: class org.checkerframework.checker.nullness.KeyForSubchecker Visitor: class org.checkerframework.common.basetype.BaseTypeVisitor Compilation unit: src/main/java/cljweb/CheckNull.java Last visited tree at line 5 column 1: public class CheckNull { Exception: java.lang.AssertionError: unexpected type: ; java.lang.AssertionError: unexpected type: at jdk.compiler/com.sun.tools.javac.tree.TreeMaker.Type(TreeMaker.java:883) at jdk.compiler/com.sun.tools.javac.tree.TreeMaker.VarDef(TreeMaker.java:905) at org.checkerframework.javacutil.trees.TreeBuilder.buildVariableDecl(TreeBuilder.java:303) at org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne.visitConditionalExpression(CFGTranslationPhaseOne.java:2848) at org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne.visitConditionalExpression(CFGTranslationPhaseOne.java:215) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCConditional.accept(JCTree.java:1548) at org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne.scan(CFGTranslationPhaseOne.java:581) at org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne.visitReturn(CFGTranslationPhaseOne.java:3649) at org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne.visitReturn(CFGTranslationPhaseOne.java:215) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCReturn.accept(JCTree.java:1736) at org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne.scan(CFGTranslationPhaseOne.java:581) at org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne.visitBlock(CFGTranslationPhaseOne.java:2460) at org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne.visitBlock(CFGTranslationPhaseOne.java:215) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1104) at org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne.scan(CFGTranslationPhaseOne.java:581) at org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne.process(CFGTranslationPhaseOne.java:473) at org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne.process(CFGTranslationPhaseOne.java:527) at org.checkerframework.framework.flow.CFCFGBuilder.build(CFCFGBuilder.java:79) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.analyze(GenericAnnotatedTypeFactory.java:1611) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.performFlowAnalysis(GenericAnnotatedTypeFactory.java:1523) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.checkAndPerformFlowAnalysis(GenericAnnotatedTypeFactory.java:2135) at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.preProcessClassTree(GenericAnnotatedTypeFactory.java:445) at org.checkerframework.common.basetype.BaseTypeVisitor.visitClass(BaseTypeVisitor.java:591) at org.checkerframework.common.basetype.BaseTypeVisitor.visitClass(BaseTypeVisitor.java:195) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:855) at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:66) at org.checkerframework.framework.source.SourceVisitor.visit(SourceVisitor.java:89) at org.checkerframework.framework.source.SourceChecker.typeProcess(SourceChecker.java:1136) at org.checkerframework.common.basetype.BaseTypeChecker.typeProcess(BaseTypeChecker.java:556) at org.checkerframework.common.basetype.BaseTypeChecker.typeProcess(BaseTypeChecker.java:549) at org.checkerframework.javacutil.AbstractTypeProcessor$AttributionTaskListener.finished(AbstractTypeProcessor.java:193) at jdk.compiler/com.sun.tools.javac.api.ClientCodeWrapper$WrappedTaskListener.finished(ClientCodeWrapper.java:876) at jdk.compiler/com.sun.tools.javac.api.MultiTaskListener.finished(MultiTaskListener.java:133) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1436) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1393) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:976) 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) at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:100) at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:94) at io.github.wmdietl.diagnostics.JavacDiagnosticsWrapper.run(JavacDiagnosticsWrapper.java:46) at io.github.wmdietl.diagnostics.json.javac.Main.main(Main.java:19)"

wmdietl commented 2 months ago

Thanks for the report. More minimal example to crash the Nullness Checker:

class CheckNull {
  void foo(boolean b) {
    Object o = b ? null : null;
  }
}

The crash only seems to happen when both branches of a ternary operator are the null literal. So hopefully this won't affect any real code. A simple transformation already doesn't crash:

class CheckNull {
  void foo(boolean b) {
    Object a = null;
    Object o = b ? a : null;
  }
}