diff --git a/checker/src/main/java/org/checkerframework/checker/fenum/FenumVisitor.java b/checker/src/main/java/org/checkerframework/checker/fenum/FenumVisitor.java index 96f729f322a..0a7580c77f3 100644 --- a/checker/src/main/java/org/checkerframework/checker/fenum/FenumVisitor.java +++ b/checker/src/main/java/org/checkerframework/checker/fenum/FenumVisitor.java @@ -15,7 +15,7 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; import org.checkerframework.javacutil.AnnotationMirrorSet; import org.checkerframework.javacutil.TreeUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.CaseUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.CaseUtils; /** The visitor for Fenum Checker. */ public class FenumVisitor extends BaseTypeVisitor { diff --git a/checker/tests/nullness/java21/FlowSwitch.java b/checker/tests/nullness/java21/FlowSwitch.java index 4d9d091a6a1..e103357346f 100644 --- a/checker/tests/nullness/java21/FlowSwitch.java +++ b/checker/tests/nullness/java21/FlowSwitch.java @@ -7,6 +7,18 @@ public class FlowSwitch { + void test0(Number n) { + String s = null; + switch (n) { + case null, default: { + // TODO: this should issue a dereference of nullable error. + n.toString(); + s = ""; + } + } + s.toString(); + } + void test1(Integer i) { String msg = null; switch (i) { @@ -36,6 +48,7 @@ void test2(Integer i) { msg = "pos"; break; } + msg.toString(); } class A {} diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java index 5b90e2ce6c1..fe0beceb586 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/builder/CFGTranslationPhaseOne.java @@ -168,16 +168,16 @@ import org.checkerframework.javacutil.SystemUtil; import org.checkerframework.javacutil.TreePathUtil; import org.checkerframework.javacutil.TreeUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.BindingPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.CaseUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.DeconstructionPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.InstanceOfUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.SwitchExpressionUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.YieldUtils; import org.checkerframework.javacutil.TypeAnnotationUtils; import org.checkerframework.javacutil.TypeKindUtils; import org.checkerframework.javacutil.TypesUtils; import org.checkerframework.javacutil.trees.TreeBuilder; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.BindingPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.CaseUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.DeconstructionPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.InstanceOfUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.SwitchExpressionUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.YieldUtils; import org.plumelib.util.ArrayMap; import org.plumelib.util.ArraySet; import org.plumelib.util.CollectionsPlume; diff --git a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java index fc060c02be7..6f900e062d1 100644 --- a/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java +++ b/framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java @@ -141,9 +141,9 @@ import org.checkerframework.javacutil.TreePathUtil; import org.checkerframework.javacutil.TreeUtils; import org.checkerframework.javacutil.TreeUtils.MemberReferenceKind; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.BindingPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.InstanceOfUtils; import org.checkerframework.javacutil.TypesUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.BindingPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.InstanceOfUtils; import org.plumelib.util.ArrayMap; import org.plumelib.util.ArraySet; import org.plumelib.util.ArraysPlume; diff --git a/framework/src/main/java/org/checkerframework/framework/ajava/ExpectedTreesVisitor.java b/framework/src/main/java/org/checkerframework/framework/ajava/ExpectedTreesVisitor.java index f7dfa2d2cf0..c4cd4ec3cca 100644 --- a/framework/src/main/java/org/checkerframework/framework/ajava/ExpectedTreesVisitor.java +++ b/framework/src/main/java/org/checkerframework/framework/ajava/ExpectedTreesVisitor.java @@ -26,8 +26,8 @@ import java.util.HashSet; import java.util.Set; import org.checkerframework.javacutil.TreeUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.BindingPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.SwitchExpressionUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.BindingPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.SwitchExpressionUtils; /** * After this visitor visits a tree, {@link #getTrees} returns all the trees that should match with diff --git a/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java b/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java index 8e0299ad7fb..2882e95f8c1 100644 --- a/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java +++ b/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java @@ -161,11 +161,11 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.TreeUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.BindingPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.CaseUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.InstanceOfUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.SwitchExpressionUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.YieldUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.BindingPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.CaseUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.InstanceOfUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.SwitchExpressionUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.YieldUtils; /** * A visitor that processes javac trees and JavaParser nodes simultaneously, matching corresponding diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/SwitchExpressionScanner.java b/javacutil/src/main/java/org/checkerframework/javacutil/SwitchExpressionScanner.java index eb82e041898..1ae0cd41f57 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/SwitchExpressionScanner.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/SwitchExpressionScanner.java @@ -9,9 +9,9 @@ import java.util.function.BiFunction; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.CaseUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.SwitchExpressionUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.YieldUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.CaseUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.SwitchExpressionUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.YieldUtils; /** * A class that visits each result expression of a switch expression and calls {@link diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index a1fbdd1d565..d0f3cfbe6cb 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -88,11 +88,11 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.signature.qual.FullyQualifiedName; import org.checkerframework.dataflow.qual.Pure; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.BindingPatternUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.CaseUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.InstanceOfUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.SwitchExpressionUtils; -import org.checkerframework.javacutil.trees.TreeUtilsAfterJava11.YieldUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.BindingPatternUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.CaseUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.InstanceOfUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.SwitchExpressionUtils; +import org.checkerframework.javacutil.TreeUtilsAfterJava11.YieldUtils; import org.plumelib.util.CollectionsPlume; import org.plumelib.util.UniqueIdMap; @@ -2220,13 +2220,14 @@ public static boolean sameTree(ExpressionTree expr1, ExpressionTree expr2) { } /** - * Returns true if this is the default case for a switch statement or expression. + * Returns true if this is the default case for a switch statement or expression. (Also, returns + * true if {@code caseTree} is {@code case null, default:}.) * * @param caseTree a case tree * @return true if {@code caseTree} is the default case for a switch statement or expression */ public static boolean isDefaultCaseTree(CaseTree caseTree) { - return CaseUtils.getLabels(caseTree).isEmpty(); + return CaseUtils.isDefaultCaseTree(caseTree); } /** diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeUtilsAfterJava11.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtilsAfterJava11.java similarity index 93% rename from javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeUtilsAfterJava11.java rename to javacutil/src/main/java/org/checkerframework/javacutil/TreeUtilsAfterJava11.java index 73d5f70333b..54cc6b10b5e 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/trees/TreeUtilsAfterJava11.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtilsAfterJava11.java @@ -1,4 +1,4 @@ -package org.checkerframework.javacutil.trees; +package org.checkerframework.javacutil; import com.sun.source.tree.CaseTree; import com.sun.source.tree.ExpressionTree; @@ -13,8 +13,6 @@ import javax.lang.model.SourceVersion; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.signature.qual.ClassGetName; -import org.checkerframework.javacutil.BugInCF; -import org.checkerframework.javacutil.TreeUtils; /** * This class contains util methods for reflective accessing Tree classes and methods that were @@ -116,14 +114,39 @@ public static boolean isCaseRule(CaseTree caseTree) { } /** - * Get the list of labels from a case expression. For {@code default}, this is empty. Otherwise, - * in JDK 11 and earlier, this is a singleton list of expression trees. In JDK 12, this is a - * list of expression trees. In JDK 21+, this is a list of expression and pattern trees. + * Get the list of labels from a case expression. For {@code default}, this is empty. For {@code + * case null, default}, the list contains {@code null}. Otherwise, in JDK 11 and earlier, this + * is a list of a single expression tree. In JDK 12+, the list may have multiple expression + * trees. In JDK 21+, the list might contain a single pattern tree. * * @param caseTree the case expression to get the labels from * @return the list of case labels in the case */ public static List getLabels(CaseTree caseTree) { + return getLabels(caseTree, false); + } + + /** + * Returns true if this is the default case for a switch statement or expression. (Also, returns + * true if {@code caseTree} is {@code case null, default:}.) + * + * @param caseTree a case tree + * @return true if {@code caseTree} is the default case for a switch statement or expression + */ + public static boolean isDefaultCaseTree(CaseTree caseTree) { + if (sourceVersionNumber >= 21) { + for (Tree label : getLabels(caseTree, true)) { + if (TreeUtils.isDefaultCaseLabelTree(label)) { + return true; + } + } + return false; + } else { + return getExpressions(caseTree).isEmpty(); + } + } + + private static List getLabels(CaseTree caseTree, boolean useDefault) { if (sourceVersionNumber >= 21) { if (GET_LABELS == null) { GET_LABELS = getMethod(CaseTree.class, "getLabels"); @@ -134,7 +157,9 @@ public static List getLabels(CaseTree caseTree) { List labels = new ArrayList<>(); for (Tree caseLabel : caseLabelTrees) { if (TreeUtils.isDefaultCaseLabelTree(caseLabel)) { - return Collections.emptyList(); + if (useDefault) { + labels.add(caseLabel); + } } else if (TreeUtils.isConstantCaseLabelTree(caseLabel)) { labels.add(ConstantCaseLabelUtils.getConstantExpression(caseLabel)); } else if (TreeUtils.isPatternCaseLabelTree(caseLabel)) { @@ -184,7 +209,7 @@ public static List getExpressions(CaseTree caseTree) { if (GET_GUARD == null) { GET_GUARD = getMethod(CaseTree.class, "getGuard"); } - return (ExpressionTree) invokeNonNullResult(GET_GUARD, caseTree); + return (ExpressionTree) invoke(GET_GUARD, caseTree); } }