• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.uber.nullaway.dataflow;
2 
3 import static com.uber.nullaway.Nullness.NONNULL;
4 import static com.uber.nullaway.Nullness.NULLABLE;
5 
6 import com.google.errorprone.util.ASTHelpers;
7 import com.sun.source.tree.ClassTree;
8 import com.sun.source.tree.LambdaExpressionTree;
9 import com.sun.source.tree.VariableTree;
10 import com.sun.tools.javac.code.Symbol;
11 import com.sun.tools.javac.code.Type;
12 import com.sun.tools.javac.code.Types;
13 import com.sun.tools.javac.util.Context;
14 import com.uber.nullaway.CodeAnnotationInfo;
15 import com.uber.nullaway.Config;
16 import com.uber.nullaway.NullabilityUtil;
17 import com.uber.nullaway.Nullness;
18 import com.uber.nullaway.handlers.Handler;
19 import java.util.List;
20 import java.util.Objects;
21 import javax.annotation.Nullable;
22 import javax.lang.model.element.Element;
23 import org.checkerframework.nullaway.dataflow.cfg.UnderlyingAST;
24 import org.checkerframework.nullaway.dataflow.cfg.node.LocalVariableNode;
25 
26 class CoreNullnessStoreInitializer extends NullnessStoreInitializer {
27 
28   @Override
getInitialStore( UnderlyingAST underlyingAST, List<LocalVariableNode> parameters, Handler handler, Context context, Types types, Config config)29   public NullnessStore getInitialStore(
30       UnderlyingAST underlyingAST,
31       List<LocalVariableNode> parameters,
32       Handler handler,
33       Context context,
34       Types types,
35       Config config) {
36     if (underlyingAST.getKind().equals(UnderlyingAST.Kind.ARBITRARY_CODE)) {
37       // not a method or a lambda; an initializer expression or block
38       UnderlyingAST.CFGStatement ast = (UnderlyingAST.CFGStatement) underlyingAST;
39       return getEnvNullnessStoreForClass(ast.getClassTree(), context);
40     }
41     boolean isLambda = underlyingAST.getKind().equals(UnderlyingAST.Kind.LAMBDA);
42     if (isLambda) {
43       return lambdaInitialStore(
44           (UnderlyingAST.CFGLambda) underlyingAST,
45           parameters,
46           handler,
47           context,
48           types,
49           config,
50           getCodeAnnotationInfo(context));
51     } else {
52       return methodInitialStore(
53           (UnderlyingAST.CFGMethod) underlyingAST, parameters, handler, context, config);
54     }
55   }
56 
methodInitialStore( UnderlyingAST.CFGMethod underlyingAST, List<LocalVariableNode> parameters, Handler handler, Context context, Config config)57   private static NullnessStore methodInitialStore(
58       UnderlyingAST.CFGMethod underlyingAST,
59       List<LocalVariableNode> parameters,
60       Handler handler,
61       Context context,
62       Config config) {
63     ClassTree classTree = underlyingAST.getClassTree();
64     NullnessStore envStore = getEnvNullnessStoreForClass(classTree, context);
65     NullnessStore.Builder result = envStore.toBuilder();
66     for (LocalVariableNode param : parameters) {
67       Element element = param.getElement();
68       Nullness assumed =
69           Nullness.hasNullableAnnotation((Symbol) element, config) ? NULLABLE : NONNULL;
70       result.setInformation(AccessPath.fromLocal(param), assumed);
71     }
72     result = handler.onDataflowInitialStore(underlyingAST, parameters, result);
73     return result.build();
74   }
75 
lambdaInitialStore( UnderlyingAST.CFGLambda underlyingAST, List<LocalVariableNode> parameters, Handler handler, Context context, Types types, Config config, CodeAnnotationInfo codeAnnotationInfo)76   private static NullnessStore lambdaInitialStore(
77       UnderlyingAST.CFGLambda underlyingAST,
78       List<LocalVariableNode> parameters,
79       Handler handler,
80       Context context,
81       Types types,
82       Config config,
83       CodeAnnotationInfo codeAnnotationInfo) {
84     // include nullness info for locals from enclosing environment
85     EnclosingEnvironmentNullness environmentNullness =
86         EnclosingEnvironmentNullness.instance(context);
87     NullnessStore environmentMapping =
88         Objects.requireNonNull(
89             environmentNullness.getEnvironmentMapping(underlyingAST.getLambdaTree()),
90             "no environment stored for lambda");
91     NullnessStore.Builder result = environmentMapping.toBuilder();
92     LambdaExpressionTree code = underlyingAST.getLambdaTree();
93     // need to check annotation for i'th parameter of functional interface declaration
94     Symbol.MethodSymbol fiMethodSymbol = NullabilityUtil.getFunctionalInterfaceMethod(code, types);
95     com.sun.tools.javac.util.List<Symbol.VarSymbol> fiMethodParameters =
96         fiMethodSymbol.getParameters();
97     // This obtains the types of the functional interface method parameters with preserved
98     // annotations in case of generic type arguments.  Only used in JSpecify mode.
99     List<Type> overridenMethodParamTypeList =
100         types.memberType(ASTHelpers.getType(code), fiMethodSymbol).getParameterTypes();
101     // If fiArgumentPositionNullness[i] == null, parameter position i is unannotated
102     Nullness[] fiArgumentPositionNullness = new Nullness[fiMethodParameters.size()];
103     final boolean isFIAnnotated = !codeAnnotationInfo.isSymbolUnannotated(fiMethodSymbol, config);
104     if (isFIAnnotated) {
105       for (int i = 0; i < fiMethodParameters.size(); i++) {
106         if (Nullness.hasNullableAnnotation(fiMethodParameters.get(i), config)) {
107           // Get the Nullness if the Annotation is directly written with the parameter
108           fiArgumentPositionNullness[i] = NULLABLE;
109         } else if (config.isJSpecifyMode()
110             && Nullness.hasNullableAnnotation(
111                 overridenMethodParamTypeList.get(i).getAnnotationMirrors().stream(), config)) {
112           // Get the Nullness if the Annotation is indirectly applied through a generic type if we
113           // are in JSpecify mode
114           fiArgumentPositionNullness[i] = NULLABLE;
115         } else {
116           fiArgumentPositionNullness[i] = NONNULL;
117         }
118       }
119     }
120     fiArgumentPositionNullness =
121         handler.onOverrideMethodInvocationParametersNullability(
122             context, fiMethodSymbol, isFIAnnotated, fiArgumentPositionNullness);
123 
124     for (int i = 0; i < parameters.size(); i++) {
125       LocalVariableNode param = parameters.get(i);
126       VariableTree variableTree = code.getParameters().get(i);
127       Element element = param.getElement();
128       Nullness assumed;
129       // we treat lambda parameters differently; they "inherit" the nullability of the
130       // corresponding functional interface parameter, unless they are explicitly annotated
131       if (Nullness.hasNullableAnnotation((Symbol) element, config)) {
132         assumed = NULLABLE;
133       } else if (!NullabilityUtil.lambdaParamIsImplicitlyTyped(variableTree)) {
134         // the parameter has a declared type with no @Nullable annotation
135         // treat as non-null
136         assumed = NONNULL;
137       } else {
138         assumed = fiArgumentPositionNullness[i] == null ? NONNULL : fiArgumentPositionNullness[i];
139       }
140       result.setInformation(AccessPath.fromLocal(param), assumed);
141     }
142     result = handler.onDataflowInitialStore(underlyingAST, parameters, result);
143     return result.build();
144   }
145 
146   @Nullable private CodeAnnotationInfo codeAnnotationInfo;
147 
getCodeAnnotationInfo(Context context)148   private CodeAnnotationInfo getCodeAnnotationInfo(Context context) {
149     if (codeAnnotationInfo == null) {
150       codeAnnotationInfo = CodeAnnotationInfo.instance(context);
151     }
152     return codeAnnotationInfo;
153   }
154 }
155