diff --git a/nullaway/src/main/java/com/uber/nullaway/CodeAnnotationInfo.java b/nullaway/src/main/java/com/uber/nullaway/CodeAnnotationInfo.java index 849d3e938d..55de1b66a5 100644 --- a/nullaway/src/main/java/com/uber/nullaway/CodeAnnotationInfo.java +++ b/nullaway/src/main/java/com/uber/nullaway/CodeAnnotationInfo.java @@ -106,8 +106,17 @@ private static boolean fromAnnotatedPackage( * {@code @Generated}; false otherwise */ public boolean isGenerated(Symbol symbol, Config config) { - Symbol.ClassSymbol outermostClassSymbol = - get(castToNonNull(ASTHelpers.enclosingClass(symbol)), config).outermostClassSymbol; + Symbol.ClassSymbol classSymbol = ASTHelpers.enclosingClass(symbol); + if (classSymbol == null) { + Preconditions.checkArgument( + isClassFieldOfPrimitiveType( + symbol), // One known case where this can happen: int.class, void.class, etc. + String.format( + "Unexpected symbol passed to CodeAnnotationInfo.isGenerated(...) with null enclosing class: %s", + symbol)); + return false; + } + Symbol.ClassSymbol outermostClassSymbol = get(classSymbol, config).outermostClassSymbol; return ASTHelpers.hasDirectAnnotationWithSimpleName(outermostClassSymbol, "Generated"); } diff --git a/nullaway/src/test/java/com/uber/nullaway/NullAwayJSpecifyTests.java b/nullaway/src/test/java/com/uber/nullaway/NullAwayJSpecifyTests.java index 166da7262e..0e653b4576 100644 --- a/nullaway/src/test/java/com/uber/nullaway/NullAwayJSpecifyTests.java +++ b/nullaway/src/test/java/com/uber/nullaway/NullAwayJSpecifyTests.java @@ -1049,7 +1049,7 @@ public void nullMarkedStaticImports() { } @Test - public void dotClassSanityTest() { + public void dotClassSanityTest1() { // Check that we do not crash while determining the nullmarked-ness of primitive.class (e.g. // int.class) makeTestHelperWithArgs( @@ -1064,6 +1064,7 @@ public void dotClassSanityTest() { "package com.uber;", "import com.example.jspecify.future.annotations.NullMarked;", "import org.jspecify.nullness.Nullable;", + "import java.lang.reflect.Field;", "@NullMarked", "public class Test {", " public void takesClass(Class c) {", @@ -1078,6 +1079,54 @@ public void dotClassSanityTest() { " // NEEDED TO TRIGGER DATAFLOW:", " return flag ? Test.class : new Object();", " }", + " public boolean test2(Field field) {", + " if (field.getType() == int.class || field.getType() == Integer.class) {", + " return true;", + " }", + " return false;", + " }", + "}") + .doTest(); + } + + @Test + public void dotClassSanityTest2() { + // Check that we do not crash while determining the nullmarked-ness of primitive.class (e.g. + // int.class) + makeTestHelperWithArgs( + Arrays.asList( + "-d", + temporaryFolder.getRoot().getAbsolutePath(), + // Flag is required for now, but might no longer be need with @NullMarked! + "-XepOpt:NullAway:AnnotatedPackages=com.uber.dontcare", + "-XepOpt:NullAway:AcknowledgeRestrictiveAnnotations=true", + "-XepOpt:NullAway:TreatGeneratedAsUnannotated=true")) + .addSourceLines( + "Test.java", + "package com.uber;", + "import com.example.jspecify.future.annotations.NullMarked;", + "import org.jspecify.nullness.Nullable;", + "import java.lang.reflect.Field;", + "@NullMarked", + "public class Test {", + " public void takesClass(Class c) {", + " }", + " public Object test(boolean flag) {", + " takesClass(Test.class);", + " takesClass(String.class);", + " takesClass(int.class);", + " takesClass(boolean.class);", + " takesClass(float.class);", + " takesClass(void.class);", + " // NEEDED TO TRIGGER DATAFLOW:", + " return flag ? Test.class : new Object();", + " }", + " public boolean test2(Field field) {", + " if (field.getType() == int.class || field.getType() == Integer.class) {", + " return true;", + " }", + " return false;", + " }", "}") .doTest(); }