/*
* Copyright 2014 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.errorprone.dataflow.nullnesspropagation;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.errorprone.dataflow.nullnesspropagation.Nullness.BOTTOM;
import static com.google.errorprone.dataflow.nullnesspropagation.Nullness.NONNULL;
import static com.google.errorprone.dataflow.nullnesspropagation.Nullness.NULLABLE;
import static com.google.errorprone.dataflow.nullnesspropagation.NullnessPropagationTransfer.tryGetMethodSymbol;
import static org.checkerframework.javacutil.TreeUtils.elementFromDeclaration;
import com.google.errorprone.dataflow.LocalStore;
import com.google.errorprone.dataflow.LocalVariableValues;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.CheckReturnValue;
import javax.lang.model.element.Element;
import org.checkerframework.dataflow.analysis.ConditionalTransferResult;
import org.checkerframework.dataflow.analysis.RegularTransferResult;
import org.checkerframework.dataflow.analysis.TransferFunction;
import org.checkerframework.dataflow.analysis.TransferInput;
import org.checkerframework.dataflow.analysis.TransferResult;
import org.checkerframework.dataflow.cfg.UnderlyingAST;
import org.checkerframework.dataflow.cfg.node.ArrayAccessNode;
import org.checkerframework.dataflow.cfg.node.ArrayCreationNode;
import org.checkerframework.dataflow.cfg.node.ArrayTypeNode;
import org.checkerframework.dataflow.cfg.node.AssertionErrorNode;
import org.checkerframework.dataflow.cfg.node.AssignmentNode;
import org.checkerframework.dataflow.cfg.node.BitwiseAndNode;
import org.checkerframework.dataflow.cfg.node.BitwiseComplementNode;
import org.checkerframework.dataflow.cfg.node.BitwiseOrNode;
import org.checkerframework.dataflow.cfg.node.BitwiseXorNode;
import org.checkerframework.dataflow.cfg.node.BooleanLiteralNode;
import org.checkerframework.dataflow.cfg.node.CaseNode;
import org.checkerframework.dataflow.cfg.node.CharacterLiteralNode;
import org.checkerframework.dataflow.cfg.node.ClassNameNode;
import org.checkerframework.dataflow.cfg.node.ConditionalAndNode;
import org.checkerframework.dataflow.cfg.node.ConditionalNotNode;
import org.checkerframework.dataflow.cfg.node.ConditionalOrNode;
import org.checkerframework.dataflow.cfg.node.DoubleLiteralNode;
import org.checkerframework.dataflow.cfg.node.EqualToNode;
import org.checkerframework.dataflow.cfg.node.ExplicitThisLiteralNode;
import org.checkerframework.dataflow.cfg.node.FieldAccessNode;
import org.checkerframework.dataflow.cfg.node.FloatLiteralNode;
import org.checkerframework.dataflow.cfg.node.FloatingDivisionNode;
import org.checkerframework.dataflow.cfg.node.FloatingRemainderNode;
import org.checkerframework.dataflow.cfg.node.FunctionalInterfaceNode;
import org.checkerframework.dataflow.cfg.node.GreaterThanNode;
import org.checkerframework.dataflow.cfg.node.GreaterThanOrEqualNode;
import org.checkerframework.dataflow.cfg.node.ImplicitThisLiteralNode;
import org.checkerframework.dataflow.cfg.node.InstanceOfNode;
import org.checkerframework.dataflow.cfg.node.IntegerDivisionNode;
import org.checkerframework.dataflow.cfg.node.IntegerLiteralNode;
import org.checkerframework.dataflow.cfg.node.IntegerRemainderNode;
import org.checkerframework.dataflow.cfg.node.LeftShiftNode;
import org.checkerframework.dataflow.cfg.node.LessThanNode;
import org.checkerframework.dataflow.cfg.node.LessThanOrEqualNode;
import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
import org.checkerframework.dataflow.cfg.node.LongLiteralNode;
import org.checkerframework.dataflow.cfg.node.MarkerNode;
import org.checkerframework.dataflow.cfg.node.MethodAccessNode;
import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.dataflow.cfg.node.NarrowingConversionNode;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.dataflow.cfg.node.NotEqualNode;
import org.checkerframework.dataflow.cfg.node.NullChkNode;
import org.checkerframework.dataflow.cfg.node.NullLiteralNode;
import org.checkerframework.dataflow.cfg.node.NumericalAdditionNode;
import org.checkerframework.dataflow.cfg.node.NumericalMinusNode;
import org.checkerframework.dataflow.cfg.node.NumericalMultiplicationNode;
import org.checkerframework.dataflow.cfg.node.NumericalPlusNode;
import org.checkerframework.dataflow.cfg.node.NumericalSubtractionNode;
import org.checkerframework.dataflow.cfg.node.ObjectCreationNode;
import org.checkerframework.dataflow.cfg.node.PackageNameNode;
import org.checkerframework.dataflow.cfg.node.ParameterizedTypeNode;
import org.checkerframework.dataflow.cfg.node.PrimitiveTypeNode;
import org.checkerframework.dataflow.cfg.node.ReturnNode;
import org.checkerframework.dataflow.cfg.node.ShortLiteralNode;
import org.checkerframework.dataflow.cfg.node.SignedRightShiftNode;
import org.checkerframework.dataflow.cfg.node.StringConcatenateAssignmentNode;
import org.checkerframework.dataflow.cfg.node.StringConcatenateNode;
import org.checkerframework.dataflow.cfg.node.StringConversionNode;
import org.checkerframework.dataflow.cfg.node.StringLiteralNode;
import org.checkerframework.dataflow.cfg.node.SuperNode;
import org.checkerframework.dataflow.cfg.node.SynchronizedNode;
import org.checkerframework.dataflow.cfg.node.TernaryExpressionNode;
import org.checkerframework.dataflow.cfg.node.ThrowNode;
import org.checkerframework.dataflow.cfg.node.TypeCastNode;
import org.checkerframework.dataflow.cfg.node.UnsignedRightShiftNode;
import org.checkerframework.dataflow.cfg.node.VariableDeclarationNode;
import org.checkerframework.dataflow.cfg.node.WideningConversionNode;
/**
* A default implementation of a transfer function for nullability analysis with more convenient
* visitor methods. The default implementation consists of labeling almost every kind of node as
* {@code NULLABLE}. The convenient visitor methods consist first of "summary" methods, like the
* {@code visitValueLiteral} method called for every {@code ValueLiteralNode} (like
* {@code AbstractNodeVisitor}), and second of more targeted parameters to the individual
* {@code visit*} methods. For example, a {@code visitTypeCast} does not need access to the full
* {@link TransferInput}{@code <Nullness, NullnessPropagationStore>}, only to
* {@linkplain TransferInput#getValueOfSubNode the subnode values it provides}. To accomplish this,
* this class provides a {@code final} implementation of the inherited {@code visitTypeCast} method
* that delegates to an overrideable {@code visitTypeCast} method with simpler parameters.
*
* <p>Despite being "abstract," this class is fairly tightly coupled to its sole current
* implementation, {@link NullnessPropagationTransfer}. I expect that changes to that class will
* sometimes require corresponding changes to this one. The value of separating the two classes
* isn't in decoupling the two so much as in hiding the boilerplate in this class.
*
* @author cpovirk@google.com (Chris Povirk)
*/
abstract class AbstractNullnessPropagationTransfer
implements TransferFunction<Nullness, LocalStore<Nullness>> {
@Override
public LocalStore<Nullness> initialStore(
UnderlyingAST underlyingAST, List<LocalVariableNode> parameters) {
return LocalStore.empty();
}
/**
* Provides the previously computed nullness values of descendant nodes. All descendant nodes have
* already been assigned a value, if only the default of {@code NULLABLE}.
*/
interface SubNodeValues {
Nullness valueOfSubNode(Node node);
}
/**
* Receives updates to the nullness values of local parameters. The transfer function
* implementation calls {@link #set} when it can conclude that a variable must have a given
* nullness value upon successful (non-exceptional) execution of the current node's expression.
*/
interface LocalVariableUpdates {
// TODO(cpovirk): consider the API setIfLocalVariable(Node, Nullness)
void set(LocalVariableNode node, Nullness value);
void set(VariableDeclarationNode node, Nullness value);
}
/** "Summary" method called by default for every {@code ValueLiteralNode}. */
Nullness visitValueLiteral() {
return NULLABLE;
}
/** "Summary" method called by default for bitwise operations. */
Nullness visitBitwiseOperation() {
return NULLABLE;
}
/** "Summary" method called by default for numerical comparisons. */
Nullness visitNumericalComparison() {
return NULLABLE;
}
/** "Summary" method called by default for numerical operations. */
Nullness visitNumericalOperation() {
return NULLABLE;
}
/** "Summary" method called by default for every {@code ThisLiteralNode}. */
Nullness visitThisLiteral() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitNullLiteral(
NullLiteralNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitNullLiteral();
return updateRegularStore(result, input, updates);
}
Nullness visitNullLiteral() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitTypeCast(
TypeCastNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness result = visitTypeCast(node, values(input));
return noStoreChanges(result, input);
}
Nullness visitTypeCast(TypeCastNode node, SubNodeValues inputs) {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitNumericalAddition(
NumericalAdditionNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness result = visitNumericalAddition();
return noStoreChanges(result, input);
}
Nullness visitNumericalAddition() {
return visitNumericalOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitNarrowingConversion(
NarrowingConversionNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness result = visitNarrowingConversion();
return noStoreChanges(result, input);
}
Nullness visitNarrowingConversion() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitEqualTo(
EqualToNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates thenUpdates = new ReadableLocalVariableUpdates();
ReadableLocalVariableUpdates elseUpdates = new ReadableLocalVariableUpdates();
visitEqualTo(node, values(input), thenUpdates, elseUpdates);
ResultingStore thenStore = updateStore(input.getThenStore(), thenUpdates);
ResultingStore elseStore = updateStore(input.getElseStore(), elseUpdates);
return conditionalResult(
thenStore.store, elseStore.store, thenStore.storeChanged | elseStore.storeChanged);
}
void visitEqualTo(EqualToNode node, SubNodeValues inputs, LocalVariableUpdates thenUpdates,
LocalVariableUpdates elseUpdates) {}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitNotEqual(
NotEqualNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates thenUpdates = new ReadableLocalVariableUpdates();
ReadableLocalVariableUpdates elseUpdates = new ReadableLocalVariableUpdates();
visitNotEqual(node, values(input), thenUpdates, elseUpdates);
ResultingStore thenStore = updateStore(input.getThenStore(), thenUpdates);
ResultingStore elseStore = updateStore(input.getElseStore(), elseUpdates);
return conditionalResult(
thenStore.store, elseStore.store, thenStore.storeChanged | elseStore.storeChanged);
}
void visitNotEqual(NotEqualNode node, SubNodeValues inputs, LocalVariableUpdates thenUpdates,
LocalVariableUpdates elseUpdates) {}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitAssignment(
AssignmentNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitAssignment(node, values(input), updates);
return updateRegularStore(result, input, updates);
}
Nullness visitAssignment(AssignmentNode node, SubNodeValues inputs,
LocalVariableUpdates updates) {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitLocalVariable(
LocalVariableNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitLocalVariable(node, input.getRegularStore());
return updateRegularStore(result, input, updates);
}
Nullness visitLocalVariable(LocalVariableNode node, LocalVariableValues<Nullness> store) {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitFieldAccess(
FieldAccessNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitFieldAccess(node, updates);
return updateRegularStore(result, input, updates);
}
Nullness visitFieldAccess(FieldAccessNode node, LocalVariableUpdates updates) {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitMethodInvocation(
MethodInvocationNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates thenUpdates = new ReadableLocalVariableUpdates();
ReadableLocalVariableUpdates elseUpdates = new ReadableLocalVariableUpdates();
ReadableLocalVariableUpdates bothUpdates = new ReadableLocalVariableUpdates();
Nullness result = visitMethodInvocation(node, thenUpdates, elseUpdates, bothUpdates);
/*
* Returning a ConditionalTransferResult for a non-boolean node causes weird test failures, even
* if I'm careful to give it its correct Nullness instead of hardcoding it to NONNULL as the
* current code does. To avoid problems, we return a RegularTransferResult when possible.
*/
if (tryGetMethodSymbol(node.getTree()).isBoolean) {
ResultingStore thenStore = updateStore(input.getThenStore(), thenUpdates, bothUpdates);
ResultingStore elseStore = updateStore(input.getElseStore(), elseUpdates, bothUpdates);
return conditionalResult(
thenStore.store, elseStore.store, thenStore.storeChanged | elseStore.storeChanged);
} else {
return updateRegularStore(result, input, bothUpdates);
}
}
Nullness visitMethodInvocation(MethodInvocationNode node, LocalVariableUpdates thenUpdates,
LocalVariableUpdates elseUpdates, LocalVariableUpdates bothUpdates) {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitConditionalAnd(
ConditionalAndNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
return conditionalResult(input.getThenStore(), input.getElseStore(), NO_STORE_CHANGE);
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitConditionalOr(
ConditionalOrNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
return conditionalResult(input.getThenStore(), input.getElseStore(), NO_STORE_CHANGE);
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitConditionalNot(
ConditionalNotNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
/*
* Weird case: We swap the contents of the THEN and ELSE stores without otherwise modifying
* them. Presumably that can still count as a change?
*/
boolean storeChanged = !input.getThenStore().equals(input.getElseStore());
return conditionalResult(input.getElseStore(), input.getThenStore(), storeChanged);
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitObjectCreation(
ObjectCreationNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness result = visitObjectCreation();
return noStoreChanges(result, input);
}
Nullness visitObjectCreation() {
return NULLABLE;
}
private static TransferResult<Nullness, LocalStore<Nullness>> noStoreChanges(
Nullness value, TransferInput<?, LocalStore<Nullness>> input) {
return new RegularTransferResult<>(value, input.getRegularStore());
}
@CheckReturnValue
private TransferResult<Nullness, LocalStore<Nullness>> updateRegularStore(
Nullness value, TransferInput<?, LocalStore<Nullness>> input,
ReadableLocalVariableUpdates updates) {
ResultingStore newStore = updateStore(input.getRegularStore(), updates);
return new RegularTransferResult<>(value, newStore.store, newStore.storeChanged);
}
private static TransferResult<Nullness, LocalStore<Nullness>> conditionalResult(
LocalStore<Nullness> thenStore, LocalStore<Nullness> elseStore,
boolean storeChanged) {
return new ConditionalTransferResult<>(NONNULL, thenStore, elseStore, storeChanged);
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitShortLiteral(
ShortLiteralNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness result = visitShortLiteral();
return noStoreChanges(result, input);
}
Nullness visitShortLiteral() {
return visitValueLiteral();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitIntegerLiteral(
IntegerLiteralNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness result = visitIntegerLiteral();
return noStoreChanges(result, input);
}
Nullness visitIntegerLiteral() {
return visitValueLiteral();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitLongLiteral(
LongLiteralNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness result = visitLongLiteral();
return noStoreChanges(result, input);
}
Nullness visitLongLiteral() {
return visitValueLiteral();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitFloatLiteral(
FloatLiteralNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness result = visitFloatLiteral();
return noStoreChanges(result, input);
}
Nullness visitFloatLiteral() {
return visitValueLiteral();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitDoubleLiteral(
DoubleLiteralNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness result = visitDoubleLiteral();
return noStoreChanges(result, input);
}
Nullness visitDoubleLiteral() {
return visitValueLiteral();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitBooleanLiteral(
BooleanLiteralNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness result = visitBooleanLiteral();
return noStoreChanges(result, input);
}
Nullness visitBooleanLiteral() {
return visitValueLiteral();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitCharacterLiteral(
CharacterLiteralNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitCharacterLiteral(node, values(input), updates);
return updateRegularStore(result, input, updates);
}
Nullness visitCharacterLiteral(CharacterLiteralNode node, SubNodeValues inputs,
LocalVariableUpdates updates) {
return visitValueLiteral();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitStringLiteral(
StringLiteralNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitStringLiteral(node, values(input), updates);
return updateRegularStore(result, input, updates);
}
Nullness visitStringLiteral(StringLiteralNode node, SubNodeValues inputs,
LocalVariableUpdates updates) {
return visitValueLiteral();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitNumericalMinus(
NumericalMinusNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitNumericalMinus();
return noStoreChanges(value, input);
}
Nullness visitNumericalMinus() {
return visitNumericalOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitNumericalPlus(
NumericalPlusNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitNumericalPlus();
return noStoreChanges(value, input);
}
Nullness visitNumericalPlus() {
return visitNumericalOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitBitwiseComplement(
BitwiseComplementNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitBitwiseComplement();
return noStoreChanges(value, input);
}
Nullness visitBitwiseComplement() {
return visitBitwiseOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitNullChk(
NullChkNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitNullChk();
return noStoreChanges(value, input);
}
Nullness visitNullChk() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitStringConcatenate(
StringConcatenateNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitStringConcatenate();
return noStoreChanges(value, input);
}
Nullness visitStringConcatenate() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitNumericalSubtraction(
NumericalSubtractionNode node,
TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitNumericalSubtraction();
return noStoreChanges(value, input);
}
Nullness visitNumericalSubtraction() {
return visitNumericalOperation();
}
@Override
public final
TransferResult<Nullness, LocalStore<Nullness>> visitNumericalMultiplication(
NumericalMultiplicationNode node,
TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitNumericalMultiplication();
return noStoreChanges(value, input);
}
Nullness visitNumericalMultiplication() {
return visitNumericalOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitIntegerDivision(
IntegerDivisionNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitIntegerDivision();
return noStoreChanges(value, input);
}
Nullness visitIntegerDivision() {
return visitNumericalOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitFloatingDivision(
FloatingDivisionNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitFloatingDivision();
return noStoreChanges(value, input);
}
Nullness visitFloatingDivision() {
return visitNumericalOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitIntegerRemainder(
IntegerRemainderNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitIntegerRemainder();
return noStoreChanges(value, input);
}
Nullness visitIntegerRemainder() {
return visitNumericalOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitFloatingRemainder(
FloatingRemainderNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitFloatingRemainder();
return noStoreChanges(value, input);
}
Nullness visitFloatingRemainder() {
return visitNumericalOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitLeftShift(
LeftShiftNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitLeftShift();
return noStoreChanges(value, input);
}
Nullness visitLeftShift() {
return visitNumericalOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitSignedRightShift(
SignedRightShiftNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitSignedRightShift();
return noStoreChanges(value, input);
}
Nullness visitSignedRightShift() {
return visitNumericalOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitUnsignedRightShift(
UnsignedRightShiftNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitUnsignedRightShift();
return noStoreChanges(value, input);
}
Nullness visitUnsignedRightShift() {
return visitNumericalOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitBitwiseAnd(
BitwiseAndNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitBitwiseAnd();
return noStoreChanges(value, input);
}
Nullness visitBitwiseAnd() {
return visitBitwiseOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitBitwiseOr(
BitwiseOrNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitBitwiseOr();
return noStoreChanges(value, input);
}
Nullness visitBitwiseOr() {
return visitBitwiseOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitBitwiseXor(
BitwiseXorNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitBitwiseXor();
return noStoreChanges(value, input);
}
Nullness visitBitwiseXor() {
return visitBitwiseOperation();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>>
visitStringConcatenateAssignment(StringConcatenateAssignmentNode node,
TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitStringConcatenateAssignment();
return noStoreChanges(value, input);
}
Nullness visitStringConcatenateAssignment() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitLessThan(
LessThanNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitLessThan();
return noStoreChanges(value, input);
}
Nullness visitLessThan() {
return visitNumericalComparison();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitLessThanOrEqual(
LessThanOrEqualNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitLessThanOrEqual();
return noStoreChanges(value, input);
}
Nullness visitLessThanOrEqual() {
return visitNumericalComparison();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitGreaterThan(
GreaterThanNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitGreaterThan();
return noStoreChanges(value, input);
}
Nullness visitGreaterThan() {
return visitNumericalComparison();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitGreaterThanOrEqual(
GreaterThanOrEqualNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitGreaterThanOrEqual();
return noStoreChanges(value, input);
}
Nullness visitGreaterThanOrEqual() {
return visitNumericalComparison();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitTernaryExpression(
TernaryExpressionNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness result = visitTernaryExpression(node, values(input));
// TODO(kmb): Return conditional result if node itself is of boolean type, as for method calls
return noStoreChanges(result, input);
}
Nullness visitTernaryExpression(TernaryExpressionNode node, SubNodeValues inputs) {
return inputs.valueOfSubNode(node.getThenOperand())
.leastUpperBound(inputs.valueOfSubNode(node.getElseOperand()));
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitVariableDeclaration(
VariableDeclarationNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
visitVariableDeclaration(node, values(input), updates);
/*
* We can return whatever we want here because a variable declaration is not an expression and
* thus no one can use its value directly. Any updates to the nullness of the variable are
* performed in the store so that they are available to future reads.
*/
Nullness result = BOTTOM;
return updateRegularStore(result, input, updates);
}
void visitVariableDeclaration(
VariableDeclarationNode node, SubNodeValues inputs, LocalVariableUpdates updates) {
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitMethodAccess(
MethodAccessNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitMethodAccess();
return noStoreChanges(value, input);
}
Nullness visitMethodAccess() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitArrayAccess(
ArrayAccessNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitArrayAccess(node, values(input), updates);
return updateRegularStore(result, input, updates);
}
Nullness visitArrayAccess(ArrayAccessNode node, SubNodeValues inputs,
LocalVariableUpdates updates) {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitImplicitThisLiteral(
ImplicitThisLiteralNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitImplicitThisLiteral();
return noStoreChanges(value, input);
}
Nullness visitImplicitThisLiteral() {
return visitThisLiteral();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitExplicitThisLiteral(
ExplicitThisLiteralNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitExplicitThisLiteral();
return noStoreChanges(value, input);
}
Nullness visitExplicitThisLiteral() {
return visitThisLiteral();
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitSuper(SuperNode node,
TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitSuper();
return noStoreChanges(value, input);
}
Nullness visitSuper() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitReturn(ReturnNode node,
TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitReturn();
return noStoreChanges(value, input);
}
Nullness visitReturn() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitStringConversion(
StringConversionNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitStringConversion();
return noStoreChanges(value, input);
}
Nullness visitStringConversion() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitWideningConversion(
WideningConversionNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitWideningConversion();
return noStoreChanges(value, input);
}
Nullness visitWideningConversion() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitInstanceOf(
InstanceOfNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates thenUpdates = new ReadableLocalVariableUpdates();
ReadableLocalVariableUpdates elseUpdates = new ReadableLocalVariableUpdates();
Nullness result = visitInstanceOf(node, values(input), thenUpdates, elseUpdates);
ResultingStore thenStore = updateStore(input.getThenStore(), thenUpdates);
ResultingStore elseStore = updateStore(input.getElseStore(), elseUpdates);
return new ConditionalTransferResult<>(result,
thenStore.store, elseStore.store, thenStore.storeChanged | elseStore.storeChanged);
}
Nullness visitInstanceOf(InstanceOfNode node, SubNodeValues inputs,
LocalVariableUpdates thenUpdates, LocalVariableUpdates elseUpdates) {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitSynchronized(
SynchronizedNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitSynchronized(node, values(input), updates);
return updateRegularStore(result, input, updates);
}
Nullness visitSynchronized(SynchronizedNode node, SubNodeValues inputs,
LocalVariableUpdates updates) {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitAssertionError(
AssertionErrorNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitAssertionError();
return noStoreChanges(value, input);
}
Nullness visitAssertionError() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitThrow(ThrowNode node,
TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitThrow(node, values(input), updates);
return updateRegularStore(result, input, updates);
}
Nullness visitThrow(ThrowNode node, SubNodeValues inputs, LocalVariableUpdates updates) {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitCase(CaseNode node,
TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitCase();
return noStoreChanges(value, input);
}
Nullness visitCase() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitMemberReference(
FunctionalInterfaceNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitMemberReference(node, values(input), updates);
return updateRegularStore(result, input, updates);
}
Nullness visitMemberReference(FunctionalInterfaceNode node, SubNodeValues inputs,
LocalVariableUpdates updates) {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitArrayCreation(
ArrayCreationNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitArrayCreation(node, values(input), updates);
return updateRegularStore(result, input, updates);
}
Nullness visitArrayCreation(ArrayCreationNode node, SubNodeValues inputs,
LocalVariableUpdates updates) {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitArrayType(
ArrayTypeNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitArrayType();
return noStoreChanges(value, input);
}
Nullness visitArrayType() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitPrimitiveType(
PrimitiveTypeNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitPrimitiveType();
return noStoreChanges(value, input);
}
Nullness visitPrimitiveType() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitClassName(
ClassNameNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitClassName();
return noStoreChanges(value, input);
}
Nullness visitClassName() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitPackageName(
PackageNameNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitPackageName();
return noStoreChanges(value, input);
}
Nullness visitPackageName() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitParameterizedType(
ParameterizedTypeNode node, TransferInput<Nullness, LocalStore<Nullness>> input) {
Nullness value = visitParameterizedType();
return noStoreChanges(value, input);
}
Nullness visitParameterizedType() {
return NULLABLE;
}
@Override
public final TransferResult<Nullness, LocalStore<Nullness>> visitMarker(MarkerNode node,
TransferInput<Nullness, LocalStore<Nullness>> input) {
ReadableLocalVariableUpdates updates = new ReadableLocalVariableUpdates();
Nullness result = visitMarker(node, values(input), updates);
return updateRegularStore(result, input, updates);
}
Nullness visitMarker(MarkerNode node, SubNodeValues inputs, LocalVariableUpdates updates) {
return NULLABLE;
}
private static final class ReadableLocalVariableUpdates implements LocalVariableUpdates {
final Map<Element, Nullness> values = new HashMap<>();
@Override
public void set(LocalVariableNode node, Nullness value) {
values.put(node.getElement(), checkNotNull(value));
}
@Override
public void set(VariableDeclarationNode node, Nullness value) {
values.put(elementFromDeclaration(node.getTree()), checkNotNull(value));
}
}
@CheckReturnValue
private static ResultingStore updateStore(
LocalStore<Nullness> oldStore, ReadableLocalVariableUpdates... updates) {
LocalStore.Builder<Nullness> builder = oldStore.toBuilder();
for (ReadableLocalVariableUpdates update : updates) {
for (Entry<Element, Nullness> entry : update.values.entrySet()) {
builder.setInformation(entry.getKey(), entry.getValue());
}
}
LocalStore<Nullness> newStore = builder.build();
return new ResultingStore(newStore, !newStore.equals(oldStore));
}
private static SubNodeValues values(
final TransferInput<Nullness, LocalStore<Nullness>> input) {
return new SubNodeValues() {
@Override
public Nullness valueOfSubNode(Node node) {
return input.getValueOfSubNode(node);
}
};
}
private static final class ResultingStore {
final LocalStore<Nullness> store;
final boolean storeChanged;
ResultingStore(LocalStore<Nullness> store, boolean storeChanged) {
this.store = store;
this.storeChanged = storeChanged;
}
}
private static final boolean NO_STORE_CHANGE = false;
}