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.common.collect.ImmutableSet; 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.Types; 12 import com.sun.tools.javac.util.Context; 13 import com.uber.nullaway.Config; 14 import com.uber.nullaway.NullabilityUtil; 15 import com.uber.nullaway.Nullness; 16 import com.uber.nullaway.handlers.Handler; 17 import java.util.List; 18 import java.util.Objects; 19 import javax.lang.model.element.Element; 20 import org.checkerframework.nullaway.dataflow.cfg.UnderlyingAST; 21 import org.checkerframework.nullaway.dataflow.cfg.node.LocalVariableNode; 22 23 class CoreNullnessStoreInitializer extends NullnessStoreInitializer { 24 25 @Override getInitialStore( UnderlyingAST underlyingAST, List<LocalVariableNode> parameters, Handler handler, Context context, Types types, Config config)26 public NullnessStore getInitialStore( 27 UnderlyingAST underlyingAST, 28 List<LocalVariableNode> parameters, 29 Handler handler, 30 Context context, 31 Types types, 32 Config config) { 33 if (underlyingAST.getKind().equals(UnderlyingAST.Kind.ARBITRARY_CODE)) { 34 // not a method or a lambda; an initializer expression or block 35 UnderlyingAST.CFGStatement ast = (UnderlyingAST.CFGStatement) underlyingAST; 36 return getEnvNullnessStoreForClass(ast.getClassTree(), context); 37 } 38 boolean isLambda = underlyingAST.getKind().equals(UnderlyingAST.Kind.LAMBDA); 39 if (isLambda) { 40 return lambdaInitialStore( 41 (UnderlyingAST.CFGLambda) underlyingAST, parameters, handler, context, types, config); 42 } else { 43 return methodInitialStore( 44 (UnderlyingAST.CFGMethod) underlyingAST, parameters, handler, context, config); 45 } 46 } 47 methodInitialStore( UnderlyingAST.CFGMethod underlyingAST, List<LocalVariableNode> parameters, Handler handler, Context context, Config config)48 private static NullnessStore methodInitialStore( 49 UnderlyingAST.CFGMethod underlyingAST, 50 List<LocalVariableNode> parameters, 51 Handler handler, 52 Context context, 53 Config config) { 54 ClassTree classTree = underlyingAST.getClassTree(); 55 NullnessStore envStore = getEnvNullnessStoreForClass(classTree, context); 56 NullnessStore.Builder result = envStore.toBuilder(); 57 for (LocalVariableNode param : parameters) { 58 Element element = param.getElement(); 59 Nullness assumed = 60 Nullness.hasNullableAnnotation((Symbol) element, config) ? NULLABLE : NONNULL; 61 result.setInformation(AccessPath.fromLocal(param), assumed); 62 } 63 result = handler.onDataflowInitialStore(underlyingAST, parameters, result); 64 return result.build(); 65 } 66 lambdaInitialStore( UnderlyingAST.CFGLambda underlyingAST, List<LocalVariableNode> parameters, Handler handler, Context context, Types types, Config config)67 private static NullnessStore lambdaInitialStore( 68 UnderlyingAST.CFGLambda underlyingAST, 69 List<LocalVariableNode> parameters, 70 Handler handler, 71 Context context, 72 Types types, 73 Config config) { 74 // include nullness info for locals from enclosing environment 75 EnclosingEnvironmentNullness environmentNullness = 76 EnclosingEnvironmentNullness.instance(context); 77 NullnessStore environmentMapping = 78 Objects.requireNonNull( 79 environmentNullness.getEnvironmentMapping(underlyingAST.getLambdaTree()), 80 "no environment stored for lambda"); 81 NullnessStore.Builder result = environmentMapping.toBuilder(); 82 LambdaExpressionTree code = underlyingAST.getLambdaTree(); 83 // need to check annotation for i'th parameter of functional interface declaration 84 Symbol.MethodSymbol fiMethodSymbol = NullabilityUtil.getFunctionalInterfaceMethod(code, types); 85 com.sun.tools.javac.util.List<Symbol.VarSymbol> fiMethodParameters = 86 fiMethodSymbol.getParameters(); 87 ImmutableSet<Integer> nullableParamsFromHandler = 88 handler.onUnannotatedInvocationGetExplicitlyNullablePositions( 89 context, fiMethodSymbol, ImmutableSet.of()); 90 91 for (int i = 0; i < parameters.size(); i++) { 92 LocalVariableNode param = parameters.get(i); 93 VariableTree variableTree = code.getParameters().get(i); 94 Element element = param.getElement(); 95 Nullness assumed; 96 // we treat lambda parameters differently; they "inherit" the nullability of the 97 // corresponding functional interface parameter, unless they are explicitly annotated 98 if (Nullness.hasNullableAnnotation((Symbol) element, config)) { 99 assumed = NULLABLE; 100 } else if (!NullabilityUtil.lambdaParamIsImplicitlyTyped(variableTree)) { 101 // the parameter has a declared type with no @Nullable annotation 102 // treat as non-null 103 assumed = NONNULL; 104 } else { 105 if (NullabilityUtil.isUnannotated(fiMethodSymbol, config)) { 106 // assume parameter is non-null unless handler tells us otherwise 107 assumed = nullableParamsFromHandler.contains(i) ? NULLABLE : NONNULL; 108 } else { 109 assumed = 110 Nullness.hasNullableAnnotation(fiMethodParameters.get(i), config) 111 ? NULLABLE 112 : NONNULL; 113 } 114 } 115 result.setInformation(AccessPath.fromLocal(param), assumed); 116 } 117 result = handler.onDataflowInitialStore(underlyingAST, parameters, result); 118 return result.build(); 119 } 120 } 121