diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 381baa9cef..8a1f6b97f4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4269..b740cf1339 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/nullaway/src/main/java/com/uber/nullaway/NullAway.java b/nullaway/src/main/java/com/uber/nullaway/NullAway.java index f5bff29c74..ab54ba797d 100644 --- a/nullaway/src/main/java/com/uber/nullaway/NullAway.java +++ b/nullaway/src/main/java/com/uber/nullaway/NullAway.java @@ -98,6 +98,7 @@ import com.uber.nullaway.generics.GenericsChecks; import com.uber.nullaway.handlers.Handler; import com.uber.nullaway.handlers.Handlers; +import com.uber.nullaway.handlers.MethodAnalysisContext; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -384,7 +385,7 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState return Description.NO_MATCH; } Symbol.MethodSymbol methodSymbol = getSymbolForMethodInvocation(tree); - handler.onMatchMethodInvocation(this, tree, state, methodSymbol); + handler.onMatchMethodInvocation(tree, new MethodAnalysisContext(this, state, methodSymbol)); // assuming this list does not include the receiver List actualParams = tree.getArguments(); return handleInvocation(tree, state, methodSymbol, actualParams); @@ -644,7 +645,7 @@ public Description matchMethod(MethodTree tree, VisitorState state) { // overridden method (if overridden method is in an annotated // package) Symbol.MethodSymbol methodSymbol = ASTHelpers.getSymbol(tree); - handler.onMatchMethod(this, tree, state, methodSymbol); + handler.onMatchMethod(tree, new MethodAnalysisContext(this, state, methodSymbol)); boolean isOverriding = ASTHelpers.hasAnnotation(methodSymbol, "java.lang.Override", state); boolean exhaustiveOverride = config.exhaustiveOverride(); if (isOverriding || !exhaustiveOverride) { @@ -712,8 +713,13 @@ public Description matchParameterizedType(ParameterizedTypeTree tree, VisitorSta return Description.NO_MATCH; } if (config.isJSpecifyMode()) { - GenericsChecks.checkInstantiationForParameterizedTypedTree( - tree, state, this, config, handler); + Symbol baseClass = ASTHelpers.getSymbol(tree); + boolean isNullUnmarked = + baseClass != null && codeAnnotationInfo.isSymbolUnannotated(baseClass, config, handler); + if (!isNullUnmarked) { + GenericsChecks.checkInstantiationForParameterizedTypedTree( + tree, state, this, config, handler); + } } return Description.NO_MATCH; } @@ -952,7 +958,8 @@ public Description matchLambdaExpression(LambdaExpressionTree tree, VisitorState // we need to update environment mapping before running the handler, as some handlers // (like Rx nullability) run dataflow analysis updateEnvironmentMapping(state.getPath(), state); - handler.onMatchLambdaExpression(this, tree, state, funcInterfaceMethod); + handler.onMatchLambdaExpression( + tree, new MethodAnalysisContext(this, state, funcInterfaceMethod)); if (codeAnnotationInfo.isSymbolUnannotated(funcInterfaceMethod, config, handler)) { return Description.NO_MATCH; } @@ -996,7 +1003,7 @@ public Description matchMemberReference(MemberReferenceTree tree, VisitorState s Symbol.MethodSymbol referencedMethod = ASTHelpers.getSymbol(tree); Symbol.MethodSymbol funcInterfaceSymbol = NullabilityUtil.getFunctionalInterfaceMethod(tree, state.getTypes()); - handler.onMatchMethodReference(this, tree, state, referencedMethod); + handler.onMatchMethodReference(tree, new MethodAnalysisContext(this, state, referencedMethod)); return checkOverriding(funcInterfaceSymbol, referencedMethod, tree, state); } @@ -1440,7 +1447,7 @@ private boolean okToReadBeforeInitialized(TreePath path, VisitorState state) { } else { castToNonNullArg = handler.castToNonNullArgumentPositionsForMethod( - this, state, methodSymbol, arguments, null); + arguments, null, new MethodAnalysisContext(this, state, methodSymbol)); } if (castToNonNullArg != null && leaf.equals(arguments.get(castToNonNullArg))) { return true; @@ -1813,7 +1820,7 @@ private Description checkCastToNonNullTakesNullable( } else { castToNonNullPosition = handler.castToNonNullArgumentPositionsForMethod( - this, state, methodSymbol, actualParams, null); + actualParams, null, new MethodAnalysisContext(this, state, methodSymbol)); } if (castToNonNullPosition != null) { ExpressionTree actual = actualParams.get(castToNonNullPosition); diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/AbstractFieldContractHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/AbstractFieldContractHandler.java index d90cc80e88..831d633359 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/AbstractFieldContractHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/AbstractFieldContractHandler.java @@ -60,22 +60,22 @@ protected AbstractFieldContractHandler(String annotName) { /** * Verifies that the method being processed adheres to the annotation specifications. * - * @param analysis NullAway instance. * @param tree Method tree under processing. - * @param state Error Prone {@link VisitorState}. - * @param methodSymbol Processing method symbol. + * @param methodAnalysisContext The MethodAnalysisContext object */ @Override - public void onMatchMethod( - NullAway analysis, MethodTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol) { + public void onMatchMethod(MethodTree tree, MethodAnalysisContext methodAnalysisContext) { + + Symbol.MethodSymbol methodSymbol = methodAnalysisContext.methodSymbol(); + VisitorState state = methodAnalysisContext.state(); Set annotationContent = NullabilityUtil.getAnnotationValueArray(methodSymbol, annotName, false); boolean isAnnotated = annotationContent != null; boolean isValid = isAnnotated && validateAnnotationSyntax( - castToNonNull(annotationContent), analysis, tree, state, methodSymbol) - && validateAnnotationSemantics(analysis, state, tree, methodSymbol); + castToNonNull(annotationContent), tree, methodAnalysisContext) + && validateAnnotationSemantics(tree, methodAnalysisContext); if (isAnnotated && !isValid) { return; } @@ -90,8 +90,9 @@ && validateAnnotationSyntax( } else { fieldNames = Collections.emptySet(); } - validateOverridingRules(fieldNames, analysis, state, tree, closestOverriddenMethod); - super.onMatchMethod(analysis, tree, state, methodSymbol); + validateOverridingRules( + fieldNames, methodAnalysisContext.analysis(), state, tree, closestOverriddenMethod); + super.onMatchMethod(tree, methodAnalysisContext); } /** @@ -117,9 +118,10 @@ protected abstract void validateOverridingRules( * Validates that a method implementation matches the semantics of the annotation. * * @return Returns true, if the annotation conforms to the semantic rules. + * @param methodAnalysisContext The MethodAnalysisContext object */ protected abstract boolean validateAnnotationSemantics( - NullAway analysis, VisitorState state, MethodTree tree, Symbol.MethodSymbol methodSymbol); + MethodTree tree, MethodAnalysisContext methodAnalysisContext); /** * Validates whether the parameter inside annotation conforms to the syntax rules. Parameters must @@ -137,14 +139,13 @@ protected abstract boolean validateAnnotationSemantics( *

* * @return Returns true, if the annotation conforms to the syntax rules. + * @param methodAnalysisContext The MethodAnalysisContext object */ protected boolean validateAnnotationSyntax( - Set content, - NullAway analysis, - MethodTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol) { + Set content, MethodTree tree, MethodAnalysisContext methodAnalysisContext) { String message; + VisitorState state = methodAnalysisContext.state(); + NullAway analysis = methodAnalysisContext.analysis(); if (content.isEmpty()) { // we should not allow useless annotations. message = @@ -187,7 +188,8 @@ protected boolean validateAnnotationSyntax( fieldName = fieldName.substring(fieldName.lastIndexOf(".") + 1); } } - Symbol.ClassSymbol classSymbol = castToNonNull(ASTHelpers.enclosingClass(methodSymbol)); + Symbol.ClassSymbol classSymbol = + castToNonNull(ASTHelpers.enclosingClass(methodAnalysisContext.methodSymbol())); VariableElement field = getInstanceFieldOfClass(classSymbol, fieldName); if (field == null) { message = @@ -197,6 +199,7 @@ protected boolean validateAnnotationSyntax( + fieldName + " in class " + classSymbol.getSimpleName(); + state.reportMatch( analysis .getErrorBuilder() diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java index 171836276b..4656031de9 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/BaseNoOpHandler.java @@ -73,35 +73,25 @@ public void onMatchTopLevelClass( } @Override - public void onMatchMethod( - NullAway analysis, MethodTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol) { + public void onMatchMethod(MethodTree tree, MethodAnalysisContext methodAnalysisContext) { // NoOp } @Override public void onMatchMethodInvocation( - NullAway analysis, - MethodInvocationTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol) { + MethodInvocationTree tree, MethodAnalysisContext methodAnalysisContext) { // NoOp } @Override public void onMatchLambdaExpression( - NullAway analysis, - LambdaExpressionTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol) { + LambdaExpressionTree tree, MethodAnalysisContext methodAnalysisContext) { // NoOp } @Override public void onMatchMethodReference( - NullAway analysis, - MemberReferenceTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol) { + MemberReferenceTree tree, MethodAnalysisContext methodAnalysisContext) { // NoOp } @@ -238,11 +228,9 @@ public MethodInvocationNode onCFGBuildPhase1AfterVisitMethodInvocation( @Override @Nullable public Integer castToNonNullArgumentPositionsForMethod( - NullAway analysis, - VisitorState state, - Symbol.MethodSymbol methodSymbol, List actualParams, - @Nullable Integer previousArgumentPosition) { + @Nullable Integer previousArgumentPosition, + MethodAnalysisContext methodAnalysisContext) { // NoOp return previousArgumentPosition; } diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java index b05128a00a..afe0cb5074 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/CompositeHandler.java @@ -80,43 +80,33 @@ public void onMatchTopLevelClass( } @Override - public void onMatchMethod( - NullAway analysis, MethodTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol) { + public void onMatchMethod(MethodTree tree, MethodAnalysisContext methodAnalysisContext) { for (Handler h : handlers) { - h.onMatchMethod(analysis, tree, state, methodSymbol); + h.onMatchMethod(tree, methodAnalysisContext); } } @Override public void onMatchLambdaExpression( - NullAway analysis, - LambdaExpressionTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol) { + LambdaExpressionTree tree, MethodAnalysisContext methodAnalysisContext) { for (Handler h : handlers) { - h.onMatchLambdaExpression(analysis, tree, state, methodSymbol); + h.onMatchLambdaExpression(tree, methodAnalysisContext); } } @Override public void onMatchMethodReference( - NullAway analysis, - MemberReferenceTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol) { + MemberReferenceTree tree, MethodAnalysisContext methodAnalysisContext) { for (Handler h : handlers) { - h.onMatchMethodReference(analysis, tree, state, methodSymbol); + h.onMatchMethodReference(tree, methodAnalysisContext); } } @Override public void onMatchMethodInvocation( - NullAway analysis, - MethodInvocationTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol) { + MethodInvocationTree tree, MethodAnalysisContext methodAnalysisContext) { for (Handler h : handlers) { - h.onMatchMethodInvocation(analysis, tree, state, methodSymbol); + h.onMatchMethodInvocation(tree, methodAnalysisContext); } } @@ -310,15 +300,13 @@ public MethodInvocationNode onCFGBuildPhase1AfterVisitMethodInvocation( @Override @Nullable public Integer castToNonNullArgumentPositionsForMethod( - NullAway analysis, - VisitorState state, - Symbol.MethodSymbol methodSymbol, List actualParams, - @Nullable Integer previousArgumentPosition) { + @Nullable Integer previousArgumentPosition, + MethodAnalysisContext methodAnalysisContext) { for (Handler h : handlers) { previousArgumentPosition = h.castToNonNullArgumentPositionsForMethod( - analysis, state, methodSymbol, actualParams, previousArgumentPosition); + actualParams, previousArgumentPosition, methodAnalysisContext); } return previousArgumentPosition; } diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java index 08477a056f..538c587e95 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/Handler.java @@ -78,55 +78,37 @@ void onMatchTopLevelClass( /** * Called when NullAway first matches a particular method node. * - * @param analysis A reference to the running NullAway analysis. * @param tree The AST node for the method being matched. - * @param state The current visitor state. - * @param methodSymbol The method symbol for the method being matched. + * @param methodAnalysisContext The MethodAnalysisContext object */ - void onMatchMethod( - NullAway analysis, MethodTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol); + void onMatchMethod(MethodTree tree, MethodAnalysisContext methodAnalysisContext); /** * Called when NullAway first matches a particular method call-site. * - * @param analysis A reference to the running NullAway analysis. * @param tree The AST node for the method invocation (call-site) being matched. - * @param state The current visitor state. - * @param methodSymbol The method symbol for the method being called. + * @param methodAnalysisContext The MethodAnalysisContext object */ void onMatchMethodInvocation( - NullAway analysis, - MethodInvocationTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol); + MethodInvocationTree tree, MethodAnalysisContext methodAnalysisContext); /** * Called when NullAway first matches a particular lambda expression. * - * @param analysis A reference to the running NullAway analysis. * @param tree The AST node for the lambda expression being matched. - * @param state The current visitor state. - * @param methodSymbol The method symbol for the functional interface of the lambda being matched. + * @param methodAnalysisContext The MethodAnalysisContext object */ void onMatchLambdaExpression( - NullAway analysis, - LambdaExpressionTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol); + LambdaExpressionTree tree, MethodAnalysisContext methodAnalysisContext); /** * Called when NullAway first matches a particular method reference expression * - * @param analysis A reference to the running NullAway analysis. * @param tree The AST node for the method reference expression being matched. - * @param state The current visitor state. - * @param methodSymbol The method symbol for the reference being matched. + * @param methodAnalysisContext The MethodAnalysisContext object */ void onMatchMethodReference( - NullAway analysis, - MemberReferenceTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol); + MemberReferenceTree tree, MethodAnalysisContext methodAnalysisContext); /** * Called when NullAway first matches a return statement. @@ -386,9 +368,6 @@ MethodInvocationNode onCFGBuildPhase1AfterVisitMethodInvocation( *

See {@link LibraryModels#castToNonNullMethods()} for more information about general * configuration of castToNonNull methods. * - * @param analysis A reference to the running NullAway analysis. - * @param state The current visitor state. - * @param methodSymbol The method symbol for the potential castToNonNull method. * @param actualParams The actual parameters from the invocation node * @param previousArgumentPosition The result computed by the previous handler in the chain, if * any. @@ -396,14 +375,13 @@ MethodInvocationNode onCFGBuildPhase1AfterVisitMethodInvocation( * value can be set only once through the full chain of handlers, with each handler deciding * whether to propagate or override the value previousArgumentPosition passed by the previous * handler in the chain. + * @param methodAnalysisContext The MethodAnalysisContext object */ @Nullable Integer castToNonNullArgumentPositionsForMethod( - NullAway analysis, - VisitorState state, - Symbol.MethodSymbol methodSymbol, List actualParams, - @Nullable Integer previousArgumentPosition); + @Nullable Integer previousArgumentPosition, + MethodAnalysisContext methodAnalysisContext); /** * Method to override the nullability of the upper bound for a generic type variable on a class. 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 b85b8a560a..48b6bf6524 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java @@ -186,12 +186,12 @@ public boolean onOverrideMayBeNullExpr( @Override @Nullable public Integer castToNonNullArgumentPositionsForMethod( - NullAway analysis, - VisitorState state, - Symbol.MethodSymbol methodSymbol, List actualParams, - @Nullable Integer previousArgumentPosition) { - OptimizedLibraryModels optLibraryModels = getOptLibraryModels(state.context); + @Nullable Integer previousArgumentPosition, + MethodAnalysisContext methodAnalysisContext) { + Symbol.MethodSymbol methodSymbol = methodAnalysisContext.methodSymbol(); + OptimizedLibraryModels optLibraryModels = + getOptLibraryModels(methodAnalysisContext.state().context); ImmutableSet newPositions = optLibraryModels.castToNonNullMethod(methodSymbol); if (newPositions.size() > 1) { // Library models sanity check diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/MethodAnalysisContext.java b/nullaway/src/main/java/com/uber/nullaway/handlers/MethodAnalysisContext.java new file mode 100644 index 0000000000..63a9e46a26 --- /dev/null +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/MethodAnalysisContext.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024 Uber Technologies, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.nullaway.handlers; + +import com.google.errorprone.VisitorState; +import com.sun.tools.javac.code.Symbol.MethodSymbol; +import com.uber.nullaway.NullAway; +import java.util.Objects; + +/** + * Context object for checks on methods. Contains the {@link NullAway} instance, the {@link + * MethodSymbol} for the method being analyzed, and the {@link VisitorState} for the analysis. + */ +public class MethodAnalysisContext { + private final NullAway analysis; + private final MethodSymbol methodSymbol; + private final VisitorState state; + + public NullAway analysis() { + return analysis; + } + + public VisitorState state() { + return state; + } + + public MethodSymbol methodSymbol() { + return methodSymbol; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || !(o instanceof MethodAnalysisContext)) { + return false; + } + MethodAnalysisContext that = (MethodAnalysisContext) o; + return analysis.equals(that.analysis) + && state.equals(that.state) + && methodSymbol.equals(that.methodSymbol); + } + + @Override + public int hashCode() { + return Objects.hash(analysis, state, methodSymbol); + } + + @Override + public String toString() { + return "MethodAnalysisContext{" + + "analysis=" + + analysis + + ", state=" + + state + + ", methodSymbol=" + + methodSymbol + + '}'; + } + + public MethodAnalysisContext(NullAway analysis, VisitorState state, MethodSymbol methodSymbol) { + this.analysis = analysis; + this.state = state; + this.methodSymbol = methodSymbol; + } +} diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/StreamNullabilityPropagator.java b/nullaway/src/main/java/com/uber/nullaway/handlers/StreamNullabilityPropagator.java index 59ef183b29..4d7b702e48 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/StreamNullabilityPropagator.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/StreamNullabilityPropagator.java @@ -208,10 +208,10 @@ public void onMatchTopLevelClass( @Override public void onMatchMethodInvocation( - NullAway analysis, - MethodInvocationTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol) { + MethodInvocationTree tree, MethodAnalysisContext methodAnalysisContext) { + + Symbol.MethodSymbol methodSymbol = methodAnalysisContext.methodSymbol(); + VisitorState state = methodAnalysisContext.state(); Type receiverType = ASTHelpers.getReceiverType(tree); for (StreamTypeRecord streamType : models) { if (streamType.matchesType(receiverType, state)) { @@ -415,8 +415,7 @@ private void handleMapOrCollectAnonClassBody( } @Override - public void onMatchMethod( - NullAway analysis, MethodTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol) { + public void onMatchMethod(MethodTree tree, MethodAnalysisContext methodAnalysisContext) { if (mapOrCollectRecordToFilterMap.containsKey(tree)) { bodyToMethodOrLambda.put(tree.getBody(), tree); } @@ -424,16 +423,15 @@ public void onMatchMethod( @Override public void onMatchLambdaExpression( - NullAway analysis, - LambdaExpressionTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol) { + LambdaExpressionTree tree, MethodAnalysisContext methodAnalysisContext) { + VisitorState state = methodAnalysisContext.state(); if (filterMethodOrLambdaSet.contains(tree) && tree.getBodyKind().equals(LambdaExpressionTree.BodyKind.EXPRESSION)) { expressionBodyToFilterLambda.put((ExpressionTree) tree.getBody(), tree); // Single expression lambda, onMatchReturn will not be triggered, force the dataflow analysis // here - AccessPathNullnessAnalysis nullnessAnalysis = analysis.getNullnessAnalysis(state); + AccessPathNullnessAnalysis nullnessAnalysis = + methodAnalysisContext.analysis().getNullnessAnalysis(state); nullnessAnalysis.forceRunOnMethod(state.getPath(), state.context); } if (mapOrCollectRecordToFilterMap.containsKey(tree)) { @@ -443,10 +441,8 @@ public void onMatchLambdaExpression( @Override public void onMatchMethodReference( - NullAway analysis, - MemberReferenceTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol) { + MemberReferenceTree tree, MethodAnalysisContext methodAnalysisContext) { + VisitorState state = methodAnalysisContext.state(); MapOrCollectMethodToFilterInstanceRecord callInstanceRecord = mapOrCollectRecordToFilterMap.get(tree); if (callInstanceRecord != null && ((JCTree.JCMemberReference) tree).kind.isUnbound()) { @@ -475,7 +471,9 @@ public void onMatchMethodReference( // We are only looking for method APs continue; } - if (!element.getSimpleName().equals(methodSymbol.getSimpleName())) { + if (!element + .getSimpleName() + .equals(methodAnalysisContext.methodSymbol().getSimpleName())) { // Check for the name match continue; } @@ -487,7 +485,7 @@ public void onMatchMethodReference( // We found our method, and it was non-null when called inside the filter, so we mark the // return of the // method reference as non-null here - analysis.setComputedNullness(tree, Nullness.NONNULL); + methodAnalysisContext.analysis().setComputedNullness(tree, Nullness.NONNULL); } } } diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/contract/ContractCheckHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/contract/ContractCheckHandler.java index d0386909d6..20a4b9190b 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/contract/ContractCheckHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/contract/ContractCheckHandler.java @@ -38,6 +38,7 @@ import com.uber.nullaway.NullAway; import com.uber.nullaway.Nullness; import com.uber.nullaway.handlers.BaseNoOpHandler; +import com.uber.nullaway.handlers.MethodAnalysisContext; /** * This Handler parses the jetbrains @Contract annotation and tries to check if the contract is @@ -58,8 +59,7 @@ public ContractCheckHandler(Config config) { } @Override - public void onMatchMethod( - NullAway analysis, MethodTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol) { + public void onMatchMethod(MethodTree tree, MethodAnalysisContext methodAnalysisContext) { Symbol.MethodSymbol callee = ASTHelpers.getSymbol(tree); Preconditions.checkNotNull(callee); // Check to see if this method has an @Contract annotation @@ -72,6 +72,8 @@ public void onMatchMethod( } String clause = clauses[0]; + NullAway analysis = methodAnalysisContext.analysis(); + VisitorState state = methodAnalysisContext.state(); String[] antecedent = getAntecedent(clause, tree, analysis, state, callee, tree.getParameters().size()); String consequent = getConsequent(clause, tree, analysis, state, callee); diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/contract/fieldcontract/EnsuresNonNullHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/contract/fieldcontract/EnsuresNonNullHandler.java index bf2767c876..106abd949e 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/contract/fieldcontract/EnsuresNonNullHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/contract/fieldcontract/EnsuresNonNullHandler.java @@ -37,6 +37,7 @@ import com.uber.nullaway.dataflow.AccessPath; import com.uber.nullaway.dataflow.AccessPathNullnessPropagation; import com.uber.nullaway.handlers.AbstractFieldContractHandler; +import com.uber.nullaway.handlers.MethodAnalysisContext; import com.uber.nullaway.handlers.contract.ContractUtils; import java.util.Collections; import java.util.Iterator; @@ -70,7 +71,10 @@ public EnsuresNonNullHandler() { */ @Override protected boolean validateAnnotationSemantics( - NullAway analysis, VisitorState state, MethodTree tree, Symbol.MethodSymbol methodSymbol) { + MethodTree tree, MethodAnalysisContext methodAnalysisContext) { + NullAway analysis = methodAnalysisContext.analysis(); + VisitorState state = methodAnalysisContext.state(); + Symbol.MethodSymbol methodSymbol = methodAnalysisContext.methodSymbol(); String message; if (tree.getBody() == null) { return true; diff --git a/nullaway/src/main/java/com/uber/nullaway/handlers/contract/fieldcontract/RequiresNonNullHandler.java b/nullaway/src/main/java/com/uber/nullaway/handlers/contract/fieldcontract/RequiresNonNullHandler.java index 9c6e0206e4..113318ef3f 100644 --- a/nullaway/src/main/java/com/uber/nullaway/handlers/contract/fieldcontract/RequiresNonNullHandler.java +++ b/nullaway/src/main/java/com/uber/nullaway/handlers/contract/fieldcontract/RequiresNonNullHandler.java @@ -40,6 +40,7 @@ import com.uber.nullaway.dataflow.AccessPath; import com.uber.nullaway.dataflow.NullnessStore; import com.uber.nullaway.handlers.AbstractFieldContractHandler; +import com.uber.nullaway.handlers.MethodAnalysisContext; import com.uber.nullaway.handlers.contract.ContractUtils; import java.util.Collections; import java.util.Iterator; @@ -71,7 +72,7 @@ public RequiresNonNullHandler() { /** All methods can add the precondition of {@code RequiresNonNull}. */ @Override protected boolean validateAnnotationSemantics( - NullAway analysis, VisitorState state, MethodTree tree, Symbol.MethodSymbol methodSymbol) { + MethodTree tree, MethodAnalysisContext methodAnalysisContext) { return true; } @@ -129,13 +130,14 @@ protected void validateOverridingRules( */ @Override public void onMatchMethodInvocation( - NullAway analysis, - MethodInvocationTree tree, - VisitorState state, - Symbol.MethodSymbol methodSymbol) { + MethodInvocationTree tree, MethodAnalysisContext methodAnalysisContext) { + + VisitorState state = methodAnalysisContext.state(); + Symbol.MethodSymbol methodSymbol = methodAnalysisContext.methodSymbol(); + NullAway analysis = methodAnalysisContext.analysis(); Set fieldNames = getAnnotationValueArray(methodSymbol, annotName, false); if (fieldNames == null) { - super.onMatchMethodInvocation(analysis, tree, state, methodSymbol); + super.onMatchMethodInvocation(tree, methodAnalysisContext); return; } fieldNames = ContractUtils.trimReceivers(fieldNames); diff --git a/nullaway/src/test/java/com/uber/nullaway/jspecify/GenericsTests.java b/nullaway/src/test/java/com/uber/nullaway/jspecify/GenericsTests.java index 1fac4d03a0..e1294b460f 100644 --- a/nullaway/src/test/java/com/uber/nullaway/jspecify/GenericsTests.java +++ b/nullaway/src/test/java/com/uber/nullaway/jspecify/GenericsTests.java @@ -1737,6 +1737,34 @@ public void testRawTypeReceiverCast() { .doTest(); } + @Test + public void testUseOfUnannotatedCode() { + makeHelper() + .addSourceLines( + "Test.java", + "package com.uber;", + "import org.jspecify.annotations.NullMarked;", + "import org.jspecify.annotations.NullUnmarked;", + "import org.jspecify.annotations.Nullable;", + "class Test {", + " @NullUnmarked", + " static class NullUnmarkedClass {", + " }", + " @NullMarked", + " static class MarkedClass {", + " static void testInstantiation() {", + " // NullUnmarkedClass is marked @NullUnmarked, so we get no error", + " // even though the type variable does not have a @Nullable upper bound", + " new NullUnmarkedClass<@Nullable String>();", + " }", + " static void testAssignment() {", + " NullUnmarkedClass<@Nullable Integer> var = null;", + " }", + " }", + "}") + .doTest(); + } + public void boxInteger() { makeHelper() .addSourceLines(