diff --git a/nullaway/src/main/java/com/uber/nullaway/fixserialization/location/AbstractSymbolLocation.java b/nullaway/src/main/java/com/uber/nullaway/fixserialization/location/AbstractSymbolLocation.java index 75a9a455b9..da31bf7666 100644 --- a/nullaway/src/main/java/com/uber/nullaway/fixserialization/location/AbstractSymbolLocation.java +++ b/nullaway/src/main/java/com/uber/nullaway/fixserialization/location/AbstractSymbolLocation.java @@ -53,7 +53,15 @@ public AbstractSymbolLocation(ElementKind type, Symbol target) { + "."); this.type = type; this.enclosingClass = castToNonNull(ASTHelpers.enclosingClass(target)); - URI pathInURI = enclosingClass.sourcefile != null ? enclosingClass.sourcefile.toUri() : null; + // We currently serialize the URI for the classfile if the URI for the sourcefile is not + // available, but only if said URI corresponds to a "file:" or "jimfs:" scheme (i.e. not + // "jar:"). It's likely that this is no longer needed and should be removed as a follow up: + // https://github.com/uber/NullAway/issues/716 Leaving this workaround up temporarily for the + // sake of experiments with version `1.3.6-alpha-N` of the auto-annotator. + URI pathInURI = + enclosingClass.sourcefile != null + ? enclosingClass.sourcefile.toUri() + : (enclosingClass.classfile != null ? enclosingClass.classfile.toUri() : null); this.path = Serializer.pathToSourceFileFromURI(pathInURI); } } diff --git a/nullaway/src/test/java/com/uber/nullaway/NullAwaySerializationTest.java b/nullaway/src/test/java/com/uber/nullaway/NullAwaySerializationTest.java index 86eff7ce20..d106be313a 100644 --- a/nullaway/src/test/java/com/uber/nullaway/NullAwaySerializationTest.java +++ b/nullaway/src/test/java/com/uber/nullaway/NullAwaySerializationTest.java @@ -1638,6 +1638,63 @@ public void suggestNullableArgumentOnBytecodeNoFileInfo() { } } + @Test + public void suggestNullableArgumentOnBytecodeClassFileInfoOnly() { + // Simulate a build system which elides sourcefile/classfile info + try (MockedStatic astHelpersMockedStatic = + Mockito.mockStatic(ASTHelpers.class, Mockito.CALLS_REAL_METHODS)) { + astHelpersMockedStatic + .when(() -> ASTHelpers.enclosingClass(any(Symbol.class))) + .thenAnswer( + (Answer) + invocation -> { + Symbol.ClassSymbol answer = (Symbol.ClassSymbol) invocation.callRealMethod(); + if (answer.sourcefile != null + && answer + .sourcefile + .toUri() + .toASCIIString() + .contains("com/uber/nullaway/testdata/unannotated")) { + answer.sourcefile = null; + } + return answer; + }); + SerializationTestHelper tester = new SerializationTestHelper<>(root); + tester + .setArgs( + Arrays.asList( + "-d", + temporaryFolder.getRoot().getAbsolutePath(), + "-XepOpt:NullAway:AnnotatedPackages=com.uber", + // Explicitly avoid excluding com.uber.nullaway.testdata.unannotated, + // so we can suggest fixes there + "-XepOpt:NullAway:SerializeFixMetadata=true", + "-XepOpt:NullAway:FixSerializationConfigPath=" + configPath)) + .addSourceLines( + "com/uber/UsesUnannotated.java", + "package com.uber;", + "import com.uber.nullaway.testdata.unannotated.MinimalUnannotatedClass;", + "public class UsesUnannotated {", + " Object test(boolean flag) {", + " // BUG: Diagnostic contains: passing @Nullable parameter 'null' where @NonNull is required", + " return MinimalUnannotatedClass.foo(null);", + " }", + "}") + .setExpectedOutputs( + new FixDisplay( + "nullable", + "foo(java.lang.Object)", + "x", + "PARAMETER", + "com.uber.nullaway.testdata.unannotated.MinimalUnannotatedClass", + // From Symbol.classfile! + "com/uber/nullaway/testdata/unannotated/MinimalUnannotatedClass.java")) + .setFactory(fixDisplayFactory) + .setOutputFileNameAndHeader(SUGGEST_FIX_FILE_NAME, SUGGEST_FIX_FILE_HEADER) + .doTest(); + } + } + @Test public void fieldRegionComputationWithMemberSelectTest() { SerializationTestHelper tester = new SerializationTestHelper<>(root);