Skip to content

Commit

Permalink
Implement new Java 21 features in dataflow (#6192)
Browse files Browse the repository at this point in the history
Co-authored-by: Michael Ernst <[email protected]>
  • Loading branch information
smillst and mernst authored Oct 2, 2023
1 parent db0d3da commit 18b9ec2
Show file tree
Hide file tree
Showing 29 changed files with 947 additions and 148 deletions.
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ allprojects {
doNotFormat += ['**/java17/', '**/*record*/']
}

// As of 2023-09-24, google-java-format cannot parse Java 21 language features.
// See https://github.com/google/google-java-format/releases.
if (true) {
doNotFormat += ['**/java21/']
}

if (!isJava21orHigher) {
doNotFormat += ['**/java21/']
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<FenumAnnotatedTypeFactory> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,14 +419,16 @@ public Void visitIf(IfTree tree, Void p) {
public Void visitInstanceOf(InstanceOfTree tree, Void p) {
// The "reference type" is the type after "instanceof".
Tree refTypeTree = tree.getType();
if (refTypeTree.getKind() == Tree.Kind.ANNOTATED_TYPE) {
List<? extends AnnotationMirror> annotations =
TreeUtils.annotationsFromTree((AnnotatedTypeTree) refTypeTree);
if (AnnotationUtils.containsSame(annotations, NULLABLE)) {
checker.reportError(tree, "instanceof.nullable");
}
if (AnnotationUtils.containsSame(annotations, NONNULL)) {
checker.reportWarning(tree, "instanceof.nonnull.redundant");
if (refTypeTree != null) {
if (refTypeTree.getKind() == Tree.Kind.ANNOTATED_TYPE) {
List<? extends AnnotationMirror> annotations =
TreeUtils.annotationsFromTree((AnnotatedTypeTree) refTypeTree);
if (AnnotationUtils.containsSame(annotations, NULLABLE)) {
checker.reportError(tree, "instanceof.nullable");
}
if (AnnotationUtils.containsSame(annotations, NONNULL)) {
checker.reportWarning(tree, "instanceof.nonnull.redundant");
}
}
}
// Don't call super because it will issue an incorrect instanceof.unsafe warning.
Expand Down
23 changes: 23 additions & 0 deletions checker/tests/nullness/java17/Issue5967.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import java.util.function.Supplier;
import org.checkerframework.checker.nullness.qual.NonNull;

// @below-java17-jdk-skip-test
public final class Issue5967 {

enum TestEnum {
FIRST,
SECOND;
}

public static void main(String[] args) {
TestEnum testEnum = TestEnum.FIRST;
Supplier<Integer> supplier =
switch (testEnum) {
case FIRST:
yield () -> 1;
case SECOND:
yield () -> 2;
};
@NonNull Supplier<Integer> supplier1 = supplier;
}
}
2 changes: 1 addition & 1 deletion checker/tests/nullness/java17/SwitchTestIssue5412.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ public String foo1a(MyEnum b) {
// The default case is dead code, so it would be possible for type-checking
// to skip it and not issue this warning. But giving the warning is also
// good.
// :: error: (switch.expression)
default -> null;
};
// :: error: (return)
return s;
}

Expand Down
98 changes: 98 additions & 0 deletions checker/tests/nullness/java21/FlowSwitch.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// @below-java21-jdk-skip-test

// None of the WPI formats support the new Java 21 languages features, so skip inference until they do.
// @infer-jaifs-skip-test
// @infer-ajava-skip-test
// @infer-stubs-skip-test

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) {
case -1, 1:
msg = "-1 or 1";
break;
case Integer j when j > 0:
msg = "pos";
break;
case Integer j:
msg = "everything else";
break;
}
msg.toString();
}

void test2(Integer i) {
String msg = null;
switch (i) {
case -1, 1:
msg = "-1 or 1";
break;
default:
msg = "everythingything else";
break;
case 2:
msg = "pos";
break;
}
msg.toString();
}

class A {}

class B extends A {}

sealed interface I permits C, D {}

final class C implements I {}

final class D implements I {}

record Pair<T>(T x, T y) {}

void testE(Pair<A> p1) {
B e = switch (p1) {
case Pair<A>(A a, B b) -> b;
case Pair<A>(B b, A a) -> b;
default -> null;
};
B e2 = null;
switch (p1) {
case Pair<A>(A a, B b) -> e2 = b;
case Pair<A>(B b, A a) -> e2 = b;
default -> e2 = new B();
}
e2.toString();
}

void test3(Pair<I> p2) {
String s = null;
I e = null;
switch (p2) {
case Pair<I>(I i, C c) -> {e = c; s="";}
case Pair<I>(I i, D d) -> {e = d; s="";}
}
s.toString();
e.toString();

I e2 = null;
switch (p2) {
case Pair<I>(C c, I i) -> e2 = c;
case Pair<I>(D d, C c) -> e2 = d;
case Pair<I>(D d1, D d2) -> e2 = d2;
}
}
}
31 changes: 31 additions & 0 deletions checker/tests/nullness/java21/SimpleCaseGuard.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// @below-java21-jdk-skip-test

// None of the WPI formats support the new Java 21 languages features, so skip inference until they do.
// @infer-jaifs-skip-test
// @infer-ajava-skip-test
// @infer-stubs-skip-test

import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

public class SimpleCaseGuard {

@Nullable String field;

void test2(Object obj, boolean b) {
switch (obj) {
case String s when field != null -> {
@NonNull String z = field;
}
case String s -> {
// :: error: (assignment)
@NonNull String z = field;
}
default -> {
// :: error: (assignment)
@NonNull String z = field;
}
}
}

}
Loading

0 comments on commit 18b9ec2

Please sign in to comment.