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