![JAR search and dependency download from the Maven repository](/logo.png)
com.uber.nullaway.handlers.Handler Maven / Gradle / Ivy
Show all versions of nullaway Show documentation
/*
* Copyright (c) 2017 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.common.collect.ImmutableSet;
import com.google.errorprone.VisitorState;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ReturnTree;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.util.Context;
import com.uber.nullaway.ErrorMessage;
import com.uber.nullaway.LibraryModels;
import com.uber.nullaway.NullAway;
import com.uber.nullaway.Nullness;
import com.uber.nullaway.dataflow.AccessPath;
import com.uber.nullaway.dataflow.AccessPathNullnessAnalysis;
import com.uber.nullaway.dataflow.AccessPathNullnessPropagation;
import com.uber.nullaway.dataflow.NullnessStore;
import com.uber.nullaway.dataflow.cfg.NullAwayCFGBuilder;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import org.checkerframework.nullaway.dataflow.cfg.UnderlyingAST;
import org.checkerframework.nullaway.dataflow.cfg.node.FieldAccessNode;
import org.checkerframework.nullaway.dataflow.cfg.node.LocalVariableNode;
import org.checkerframework.nullaway.dataflow.cfg.node.MethodInvocationNode;
/**
* The general interface representing a handler.
*
* Handlers are used to model specific libraries and APIs, as opposed to general features of the
* Java language, which are handled by the nullability checker core and dataflow packages.
*/
public interface Handler {
/**
* Called when NullAway matches a particular top level class.
*
*
This also means we are starting a new Compilation Unit, which allows us to clear CU-specific
* state.
*
* @param analysis A reference to the running NullAway analysis.
* @param tree The AST node for the class being matched.
* @param state The current visitor state.
* @param classSymbol The class symbol for the class being matched.
*/
void onMatchTopLevelClass(
NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol);
/**
* 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.
*/
void onMatchMethod(
NullAway analysis, MethodTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol);
/**
* 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.
*/
void onMatchMethodInvocation(
NullAway analysis,
MethodInvocationTree tree,
VisitorState state,
Symbol.MethodSymbol methodSymbol);
/**
* 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.
*/
void onMatchLambdaExpression(
NullAway analysis,
LambdaExpressionTree tree,
VisitorState state,
Symbol.MethodSymbol methodSymbol);
/**
* 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.
*/
void onMatchMethodReference(
NullAway analysis,
MemberReferenceTree tree,
VisitorState state,
Symbol.MethodSymbol methodSymbol);
/**
* Called when NullAway first matches a return statement.
*
* @param analysis A reference to the running NullAway analysis.
* @param tree The AST node for the return statement being matched.
* @param state The current visitor state.
*/
void onMatchReturn(NullAway analysis, ReturnTree tree, VisitorState state);
/**
* Called after the analysis determines if a expression can be null or not, allowing handlers to
* override.
*
* @param analysis A reference to the running NullAway analysis.
* @param expr The expression in question.
* @param exprSymbol The symbol of the expression, might be null
* @param state The current visitor state.
* @param exprMayBeNull Whether or not the expression may be null according to the base analysis
* or upstream handlers.
* @return Whether or not the expression may be null, as updated by this handler.
*/
boolean onOverrideMayBeNullExpr(
NullAway analysis,
ExpressionTree expr,
@Nullable Symbol exprSymbol,
VisitorState state,
boolean exprMayBeNull);
/**
* Called to potentially override the nullability of an annotated or unannotated method's return,
* when only the method symbol (and not a full invocation tree) is available. This is used
* primarily for checking subtyping / method overrides.
*
* @param methodSymbol The method symbol for the method in question.
* @param state The current visitor state.
* @param isAnnotated A boolean flag indicating whether the called method is considered to be
* within annotated or unannotated code, used to avoid querying for this information multiple
* times within the same handler chain.
* @param returnNullness return nullness computed by upstream handlers or NullAway core.
* @return Updated return nullability computed by this handler.
*/
Nullness onOverrideMethodReturnNullability(
Symbol.MethodSymbol methodSymbol,
VisitorState state,
boolean isAnnotated,
Nullness returnNullness);
/**
* Called after the analysis determines the nullability of a method's arguments, allowing handlers
* to override.
*
*
The passed Map object maps argument positions to nullness information and is sparse, where
* the nullness of missing indexes is determined by base analysis and depends on if the code is
* considered isAnnotated or not. We use a mutable map for performance, but it should not outlive
* the chain of handler invocations.
*
* @param context The current context.
* @param methodSymbol The method symbol for the method in question.
* @param isAnnotated A boolean flag indicating whether the called method is considered to be
* within isAnnotated or unannotated code, used to avoid querying for this information
* multiple times within the same handler chain.
* @param argumentPositionNullness Nullness info for each argument position as computed by
* upstream handlers and/or the base analysis. Some entries may be {@code null}, indicating
* upstream handlers and the base analysis consider the parameter to be nullness-unknown,
* usually since the parameter is from unannotated code.
* @return The updated nullness info for each argument position, as computed by the current
* handler.
*/
Nullness[] onOverrideMethodInvocationParametersNullability(
Context context,
Symbol.MethodSymbol methodSymbol,
boolean isAnnotated,
Nullness[] argumentPositionNullness);
/**
* Called when the Dataflow analysis generates the initial NullnessStore for a method or lambda.
*
* @param underlyingAST The AST node for the method's (or lambda's) body, using the checkers
* framework UnderlyingAST class.
* @param parameters The formal parameters of the method.
* @param result The state of the initial NullnessStore for the method (or lambda) at the point
* this hook is called, represented as a builder.
* @return The desired state of the initial NullnessStore for the method (or lambda), after this
* hook is called, represented as a builder. Usually, implementors of this hook will either
* take {@code result} and call {@code setInformation(...)} on it to add additional nullness
* facts, or replace it with a new builder altogether.
*/
NullnessStore.Builder onDataflowInitialStore(
UnderlyingAST underlyingAST,
List parameters,
NullnessStore.Builder result);
/**
* Called when the Dataflow analysis visits each method invocation.
*
* @param node The AST node for the method callsite.
* @param symbol The symbol of the called method
* @param state The current visitor state.
* @param apContext the current access path context information (see {@link
* AccessPath.AccessPathContext}).
* @param inputs NullnessStore information known before the method invocation.
* @param thenUpdates NullnessStore updates to be added along the then path, handlers can add via
* the set() method.
* @param elseUpdates NullnessStore updates to be added along the else path, handlers can add via
* the set() method.
* @param bothUpdates NullnessStore updates to be added along both paths, handlers can add via the
* set() method.
* @return The Nullness information for this method's return computed by this handler. See
* NullnessHint and CompositeHandler for more information about how this values get merged
* into a final Nullness value.
*/
NullnessHint onDataflowVisitMethodInvocation(
MethodInvocationNode node,
Symbol.MethodSymbol symbol,
VisitorState state,
AccessPath.AccessPathContext apContext,
AccessPathNullnessPropagation.SubNodeValues inputs,
AccessPathNullnessPropagation.Updates thenUpdates,
AccessPathNullnessPropagation.Updates elseUpdates,
AccessPathNullnessPropagation.Updates bothUpdates);
/**
* Called when the Dataflow analysis visits each field access.
*
* @param node The AST node for the field access.
* @param symbol The {@link Symbol} object for the above node, provided for convenience.
* @param types {@link Types} for the current compilation
* @param context the javac Context object (or Error Prone SubContext)
* @param apContext the current access path context information (see {@link
* AccessPath.AccessPathContext}).
* @param inputs NullnessStore information known before the method invocation.
* @param updates NullnessStore updates to be added, handlers can add via the set() method.
* @return The Nullness information for this field computed by this handler. See NullnessHint and
* CompositeHandler for more information about how this values get merged into a final
* Nullness value.
*/
NullnessHint onDataflowVisitFieldAccess(
FieldAccessNode node,
Symbol symbol,
Types types,
Context context,
AccessPath.AccessPathContext apContext,
AccessPathNullnessPropagation.SubNodeValues inputs,
AccessPathNullnessPropagation.Updates updates);
/**
* Called when the Dataflow analysis visits a return statement.
*
* @param tree The AST node for the return statement being matched.
* @param thenStore The NullnessStore for the true case of the expression inside the return
* statement.
* @param elseStore The NullnessStore for the false case of the expression inside the return
* statement.
*/
void onDataflowVisitReturn(ReturnTree tree, NullnessStore thenStore, NullnessStore elseStore);
/**
* Called when the Dataflow analysis visits the result expression inside the body of lambda.
*
* This is only called for lambda expressions with a single expression as their body. For
* lambdas with a block of code as their body, onDataflowVisitReturn will be called instead, one
* or more times.
*
*
It is not expected to be called for anything other than boolean expressions, which are the
* only ones for which providing separate then/else stores makes sense. For simply getting the
* final exit store of the lambda, see Dataflow.finalResult or
* AccessPathNullnessAnalysis.forceRunOnMethod.
*
* @param tree The AST node for the expression being matched.
* @param thenStore The NullnessStore for the true case of the expression inside the return
* statement.
* @param elseStore The NullnessStore for the false case of the expression inside the return
* statement.
*/
void onDataflowVisitLambdaResultExpression(
ExpressionTree tree, NullnessStore thenStore, NullnessStore elseStore);
/**
* It should return an error wrapped in Optional if any of the handlers detect an error in
* dereference.
*
* @param expr The AST node for the expression being matched.
* @param baseExpr The AST node for the base of dereference expression being matched.
* @param state The current visitor state.
* @return {@link ErrorMessage} wrapped in {@link Optional} if dereference causes some error,
* otherwise returns empty Optional
*/
Optional onExpressionDereference(
ExpressionTree expr, ExpressionTree baseExpr, VisitorState state);
/**
* Called when the store access paths are filtered for local variable information before an
* expression.
*
* @param accessPath The access path that needs to be checked if filtered.
* @param state The current visitor state.
* @return true if the nullability information for this accesspath should be treated as part of
* the surrounding context when processing a lambda expression or anonymous class declaration.
*/
boolean includeApInfoInSavedContext(AccessPath accessPath, VisitorState state);
/**
* Called during dataflow analysis initialization to register structurally immutable types.
*
* Handlers declare structurally immutable types, requesting that they be treated as constants
* when they appear as arguments of method inside an AccessPath. Whenever a static final field of
* one of these types appears as an argument to a method in an access path (e.g. get(Foo.f) where
* Foo.f is a static final field of an immutable type T returned by this method), it is treated
* the same as a String or primitive type compile-time constant for the purposes of tracking the
* nullability of that access path.
*
* @return A set of fully qualified immutable type names.
*/
ImmutableSet onRegisterImmutableTypes();
/**
* Called when a method writes a {@code @NonNull} value to a class field.
*
* @param field Symbol of the initialized class field.
* @param analysis nullness dataflow analysis
* @param state VisitorState.
*/
void onNonNullFieldAssignment(
Symbol field, AccessPathNullnessAnalysis analysis, VisitorState state);
/**
* Called during AST to CFG translation (CFGTranslationPhaseOne) immediately after translating a
* MethodInvocationTree.
*
* @param phase a reference to the NullAwayCFGTranslationPhaseOne object and its utility
* functions.
* @param tree the MethodInvocationTree being translated.
* @param originalNode the resulting MethodInvocationNode right before this handler is called.
* @return a MethodInvocationNode which might be originalNode or a modified version, this is
* passed to the next handler in the chain.
*/
MethodInvocationNode onCFGBuildPhase1AfterVisitMethodInvocation(
NullAwayCFGBuilder.NullAwayCFGTranslationPhaseOne phase,
MethodInvocationTree tree,
MethodInvocationNode originalNode);
/**
* Called to determine when a method acts as a cast-to-non-null operation on its parameters.
*
* 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.
* @return The index of the parameter for which the method should act as a cast (if any). This
* 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.
*/
@Nullable
Integer castToNonNullArgumentPositionsForMethod(
NullAway analysis,
VisitorState state,
Symbol.MethodSymbol methodSymbol,
List extends ExpressionTree> actualParams,
@Nullable Integer previousArgumentPosition);
/**
* A three value enum for handlers implementing onDataflowVisitMethodInvocation to communicate
* their knowledge of the method return nullability to the rest of NullAway.
*/
public enum NullnessHint {
/**
* No new information about return nullability, defer to the core algorithm and other handlers.
*/
UNKNOWN(0, Nullness.NONNULL),
/**
* The method is nullable in general (e.g. due to a library model or a return type annotation).
* Override core behavior and mark this method as Nullable, unless a handler returns
* FORCE_NONNULL.
*/
HINT_NULLABLE(1, Nullness.NULLABLE),
/**
* Handler asserts this method is guaranteed to be NonNull for the current invocation (e.g. used
* by ContractHandler when all preconditions for a "-> !null" clause are known to hold).
*/
FORCE_NONNULL(2, Nullness.NONNULL);
private final int priority;
private final Nullness nullness;
NullnessHint(int priority, Nullness nullness) {
this.priority = priority;
this.nullness = nullness;
}
public Nullness toNullness() {
return nullness;
}
public NullnessHint merge(NullnessHint other) {
if (other.priority > this.priority) {
return other;
} else {
return this;
}
}
}
}