Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions nullaway/src/main/java/com/uber/nullaway/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,13 @@ public interface Config {
*/
boolean isJarInferEnabled();

/**
* Checks if JDK inference should be enabled.
*
* @return true if JDK inference should be enabled
*/
boolean isJDKInferEnabled();

/**
* Gets the URL to show with NullAway error messages.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ public boolean isJarInferEnabled() {
throw new IllegalStateException(ERROR_MESSAGE);
}

@Override
public boolean isJDKInferEnabled() {
throw new IllegalStateException(ERROR_MESSAGE);
}

@Override
public String getErrorURL() {
throw new IllegalStateException(ERROR_MESSAGE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ final class ErrorProneCLIFlagsConfig implements Config {
/** --- JarInfer configs --- */
static final String FL_JI_ENABLED = EP_FL_NAMESPACE + ":JarInferEnabled";

static final String FL_JDK_ENABLED = EP_FL_NAMESPACE + ":JDKInferEnabled";

static final String FL_ERROR_URL = EP_FL_NAMESPACE + ":ErrorURL";

/** --- Serialization configs --- */
Expand Down Expand Up @@ -247,6 +249,8 @@ final class ErrorProneCLIFlagsConfig implements Config {
/** --- JarInfer configs --- */
private final boolean jarInferEnabled;

private final boolean jdkInferEnabled;

private final String errorURL;

/** --- Fully qualified names of custom nonnull/nullable annotation --- */
Expand Down Expand Up @@ -333,6 +337,7 @@ final class ErrorProneCLIFlagsConfig implements Config {

/* --- JarInfer configs --- */
jarInferEnabled = flags.getBoolean(FL_JI_ENABLED).orElse(false);
jdkInferEnabled = flags.getBoolean(FL_JDK_ENABLED).orElse(false);
errorURL = flags.get(FL_ERROR_URL).orElse(DEFAULT_URL);
if (acknowledgeAndroidRecent && !isAcknowledgeRestrictive) {
throw new IllegalStateException(
Expand Down Expand Up @@ -589,6 +594,11 @@ public boolean isJarInferEnabled() {
return jarInferEnabled;
}

@Override
public boolean isJDKInferEnabled() {
return jdkInferEnabled;
}

@Override
public String getErrorURL() {
return errorURL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,8 +478,9 @@ private static LibraryModels loadLibraryModels(Config config) {
ServiceLoader.load(LibraryModels.class, LibraryModels.class.getClassLoader());
ImmutableSet.Builder<LibraryModels> libModelsBuilder = new ImmutableSet.Builder<>();
libModelsBuilder.add(new DefaultLibraryModels(config)).addAll(externalLibraryModels);
if (config.isJarInferEnabled()) {
libModelsBuilder.add(new ExternalStubxLibraryModels());
if (config.isJarInferEnabled() || config.isJDKInferEnabled()) {
libModelsBuilder.add(
new ExternalStubxLibraryModels(config.isJarInferEnabled(), config.isJDKInferEnabled()));
}
return new CombinedLibraryModels(libModelsBuilder.build(), config);
}
Expand Down Expand Up @@ -1560,26 +1561,42 @@ private static class ExternalStubxLibraryModels implements LibraryModels {
private final Multimap<String, Integer> methodTypeParamNullableUpperBoundCache;
private final Map<String, SetMultimap<Integer, NestedAnnotationInfo>> nestedAnnotationInfo;

ExternalStubxLibraryModels() {
ExternalStubxLibraryModels(boolean isJarInferEnabled, boolean isJDKInferEnabled) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Constructor now accepts two separate booleans — consider clarity.

Two positional boolean parameters (isJarInferEnabled, isJDKInferEnabled) can be accidentally swapped at call sites without a compiler error. This is a minor readability concern for now since there's only one call site (Line 483), but worth noting.

🤖 Prompt for AI Agents
In `@nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java`
at line 1564, The constructor ExternalStubxLibraryModels(boolean
isJarInferEnabled, boolean isJDKInferEnabled) uses two positional booleans that
are easy to swap; change the API to accept a descriptive config object or
factory so callers pass named fields. Create a small value class (e.g.,
LibraryInferOptions or InferConfig) with boolean fields jarInferEnabled and
jdkInferEnabled, update ExternalStubxLibraryModels to take that single config
parameter, and update the existing call site to construct and pass the config
(or alternatively add clearly named static factory methods on
ExternalStubxLibraryModels like createWith(jarEnabled, jdkEnabled)). Ensure the
constructor and any usages are updated consistently.

String libraryModelLogName = "LM";
StubxCacheUtil cacheUtil = new StubxCacheUtil(libraryModelLogName);
// hardcoded loading of stubx files from android-jarinfer-models-sdkXX artifacts
try {
InputStream androidStubxIS =
Class.forName(ANDROID_MODEL_CLASS)
.getClassLoader()
.getResourceAsStream(ANDROID_ASTUBX_LOCATION);
if (androidStubxIS != null) {
cacheUtil.parseStubStream(androidStubxIS, "android.jar: " + ANDROID_ASTUBX_LOCATION);
astubxLoadLog("Loaded Android RT models.");
if (isJarInferEnabled) {
try {
InputStream androidStubxIS =
Class.forName(ANDROID_MODEL_CLASS)
.getClassLoader()
.getResourceAsStream(ANDROID_ASTUBX_LOCATION);
if (androidStubxIS != null) {
cacheUtil.parseStubStream(androidStubxIS, "android.jar: " + ANDROID_ASTUBX_LOCATION);
astubxLoadLog("Loaded Android RT models.");
}
} catch (ClassNotFoundException e) {
astubxLoadLog(
"Cannot find Android RT models locator class."
+ " This is expected if not in an Android project, or the Android SDK JarInfer models Jar has not been set up for this build.");

} catch (Exception e) {
astubxLoadLog("Cannot load Android RT models.");
}
} catch (ClassNotFoundException e) {
astubxLoadLog(
"Cannot find Android RT models locator class."
+ " This is expected if not in an Android project, or the Android SDK JarInfer models Jar has not been set up for this build.");
}

} catch (Exception e) {
astubxLoadLog("Cannot load Android RT models.");
// hardcoded loading of stubx files from jdk nullness inferred output.astubx
if (isJDKInferEnabled) {
try (InputStream in = getClass().getClassLoader().getResourceAsStream("output.astubx")) {
if (in == null) {
astubxLoadLog("JDK astubx model not found on classpath: output.astubx");
} else {
cacheUtil.parseStubStream(in, "output.astubx");
astubxLoadLog("Loaded JDK astubx model.");
}
} catch (Exception e) {
astubxLoadLog("Failed to load JDK astubx: " + e);
}
}

argAnnotCache = cacheUtil.getArgAnnotCache();
Expand Down
Binary file added nullaway/src/main/resources/output.astubx
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.uber.nullaway;

import com.google.errorprone.CompilationTestHelper;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class LibraryModelsHandlerTest extends NullAwayTestsBase {

@Test
public void jdkInferEnabledLoadsAstubxModel() {
CompilationTestHelper compilationTestHelper =
makeTestHelperWithArgs(
List.of(
"-XepOpt:NullAway:AnnotatedPackages=foo",
"-XepOpt:NullAway:JDKInferEnabled=true"))
.addSourceLines(
"Test.java",
"package foo;",
"import javax.naming.directory.Attributes;",
"import org.jspecify.annotations.NullMarked;",
"@NullMarked",
"class Test {",
" void use(Attributes attrs) {",
" // BUG: Diagnostic contains: @Nullable",
" attrs.get(\"key\").toString();",
" }",
"}");
compilationTestHelper.doTest();
}

@Test
public void jdkInferDisabledDoesNotLoadAstubxModel() {
CompilationTestHelper compilationTestHelper =
makeTestHelperWithArgs(List.of("-XepOpt:NullAway:AnnotatedPackages=foo"))
.addSourceLines(
"Test.java",
"package foo;",
"import javax.naming.directory.Attributes;",
"import org.jspecify.annotations.NullMarked;",
"@NullMarked",
"class Test {",
" void use(Attributes attrs) {",
" attrs.get(\"key\").toString();",
" }",
"}");
compilationTestHelper.doTest();
}
}