aboutsummaryrefslogtreecommitdiff
path: root/nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java
diff options
context:
space:
mode:
Diffstat (limited to 'nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java')
-rw-r--r--nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java68
1 files changed, 68 insertions, 0 deletions
diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java
index e000c4f..fe87278 100644
--- a/nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java
+++ b/nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java
@@ -22,6 +22,7 @@
package com.uber.nullaway.handlers;
+import static com.uber.nullaway.LibraryModels.FieldRef.fieldRef;
import static com.uber.nullaway.LibraryModels.MethodRef.methodRef;
import static com.uber.nullaway.Nullness.NONNULL;
import static com.uber.nullaway.Nullness.NULLABLE;
@@ -57,6 +58,7 @@ import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Function;
import javax.annotation.Nullable;
+import org.checkerframework.nullaway.dataflow.cfg.node.FieldAccessNode;
import org.checkerframework.nullaway.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.nullaway.dataflow.cfg.node.Node;
@@ -81,6 +83,25 @@ public class LibraryModelsHandler extends BaseNoOpHandler {
}
@Override
+ public boolean onOverrideFieldNullability(Symbol field) {
+ return isNullableFieldInLibraryModels(field);
+ }
+
+ @Override
+ public NullnessHint onDataflowVisitFieldAccess(
+ FieldAccessNode node,
+ Symbol symbol,
+ Types types,
+ Context context,
+ AccessPath.AccessPathContext apContext,
+ AccessPathNullnessPropagation.SubNodeValues inputs,
+ AccessPathNullnessPropagation.Updates updates) {
+ return isNullableFieldInLibraryModels(symbol)
+ ? NullnessHint.HINT_NULLABLE
+ : NullnessHint.UNKNOWN;
+ }
+
+ @Override
public Nullness[] onOverrideMethodInvocationParametersNullability(
Context context,
Symbol.MethodSymbol methodSymbol,
@@ -132,6 +153,9 @@ public class LibraryModelsHandler extends BaseNoOpHandler {
@Nullable Symbol exprSymbol,
VisitorState state,
boolean exprMayBeNull) {
+ if (isNullableFieldInLibraryModels(exprSymbol)) {
+ return true;
+ }
if (!(expr.getKind() == Tree.Kind.METHOD_INVOCATION
&& exprSymbol instanceof Symbol.MethodSymbol)) {
return exprMayBeNull;
@@ -233,6 +257,32 @@ public class LibraryModelsHandler extends BaseNoOpHandler {
}
}
+ /**
+ * Check if the given symbol is a field that is marked as nullable in any of our library models.
+ *
+ * @param symbol The symbol to check.
+ * @return True if the symbol is a field that is marked as nullable in any of our library models.
+ */
+ private boolean isNullableFieldInLibraryModels(@Nullable Symbol symbol) {
+ if (libraryModels.nullableFields().isEmpty()) {
+ // no need to do any work if there are no nullable fields.
+ return false;
+ }
+ if (symbol instanceof Symbol.VarSymbol && symbol.getKind().isField()) {
+ Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbol;
+ Symbol.ClassSymbol classSymbol = varSymbol.enclClass();
+ if (classSymbol == null) {
+ // e.g. .class expressions
+ return false;
+ }
+ String fieldName = varSymbol.getSimpleName().toString();
+ String enclosingClassName = classSymbol.flatName().toString();
+ // This check could be optimized further in the future if needed
+ return libraryModels.nullableFields().contains(fieldRef(enclosingClassName, fieldName));
+ }
+ return false;
+ }
+
private void setConditionalArgumentNullness(
AccessPathNullnessPropagation.Updates thenUpdates,
AccessPathNullnessPropagation.Updates elseUpdates,
@@ -824,6 +874,12 @@ public class LibraryModelsHandler extends BaseNoOpHandler {
public ImmutableSetMultimap<MethodRef, Integer> castToNonNullMethods() {
return CAST_TO_NONNULL_METHODS;
}
+
+ @Override
+ public ImmutableSet<FieldRef> nullableFields() {
+ // No nullable fields by default.
+ return ImmutableSet.of();
+ }
}
private static class CombinedLibraryModels implements LibraryModels {
@@ -846,6 +902,8 @@ public class LibraryModelsHandler extends BaseNoOpHandler {
private final ImmutableSet<MethodRef> nonNullReturns;
+ private final ImmutableSet<FieldRef> nullableFields;
+
private final ImmutableSetMultimap<MethodRef, Integer> castToNonNullMethods;
private final ImmutableList<StreamTypeRecord> customStreamNullabilitySpecs;
@@ -870,6 +928,7 @@ public class LibraryModelsHandler extends BaseNoOpHandler {
new ImmutableSetMultimap.Builder<>();
ImmutableList.Builder<StreamTypeRecord> customStreamNullabilitySpecsBuilder =
new ImmutableList.Builder<>();
+ ImmutableSet.Builder<FieldRef> nullableFieldsBuilder = new ImmutableSet.Builder<>();
for (LibraryModels libraryModels : models) {
for (Map.Entry<MethodRef, Integer> entry : libraryModels.failIfNullParameters().entries()) {
if (shouldSkipModel(entry.getKey())) {
@@ -932,6 +991,9 @@ public class LibraryModelsHandler extends BaseNoOpHandler {
for (StreamTypeRecord streamTypeRecord : libraryModels.customStreamNullabilitySpecs()) {
customStreamNullabilitySpecsBuilder.add(streamTypeRecord);
}
+ for (FieldRef fieldRef : libraryModels.nullableFields()) {
+ nullableFieldsBuilder.add(fieldRef);
+ }
}
failIfNullParameters = failIfNullParametersBuilder.build();
explicitlyNullableParameters = explicitlyNullableParametersBuilder.build();
@@ -943,6 +1005,7 @@ public class LibraryModelsHandler extends BaseNoOpHandler {
nonNullReturns = nonNullReturnsBuilder.build();
castToNonNullMethods = castToNonNullMethodsBuilder.build();
customStreamNullabilitySpecs = customStreamNullabilitySpecsBuilder.build();
+ nullableFields = nullableFieldsBuilder.build();
}
private boolean shouldSkipModel(MethodRef key) {
@@ -990,6 +1053,11 @@ public class LibraryModelsHandler extends BaseNoOpHandler {
}
@Override
+ public ImmutableSet<FieldRef> nullableFields() {
+ return nullableFields;
+ }
+
+ @Override
public ImmutableSetMultimap<MethodRef, Integer> castToNonNullMethods() {
return castToNonNullMethods;
}