use of org.checkerframework.dataflow.cfg.node.MethodInvocationNode in project checker-framework by typetools.
the class AccumulationTransfer method accumulate.
/**
* Updates the estimate of how many things {@code node} has accumulated.
*
* <p>If the node is an invocation of a method that returns its receiver, then its receiver's type
* will also be updated. In a chain of method calls, this process will continue backward as long
* as each receiver is itself a receiver-returning method invocation.
*
* <p>For example, suppose {@code node} is the expression {@code a.b().c()}, the new value (added
* by the accumulation analysis because of the {@code .c()} call) is "foo", and b and c return
* their receiver. This method will directly update the estimate of {@code a.b().c()} to include
* "foo". In addition, the estimates for the expressions {@code a.b()} and {@code a} would have
* their estimates updated to include "foo", because c and b (respectively) return their
* receivers. Note that due to what kind of values can be held in the store, this information is
* lost outside the method chain. That is, the returns-receiver propagated information is lost
* outside the expression in which the returns-receiver method invocations are nested.
*
* <p>As a concrete example, consider the Called Methods accumulation checker: if {@code build}
* requires a, b, and c to be called, then {@code foo.a().b().c().build();} will typecheck (they
* are in one fluent method chain), but {@code foo.a().b().c(); foo.build();} will not -- the
* store does not keep the information that a, b, and c have been called outside the chain. {@code
* foo}'s type will be {@code CalledMethods("a")}, because only {@code a()} was called directly on
* {@code foo}. For such code to typecheck, the Called Methods accumulation checker uses an
* additional rule: the return type of a receiver-returning method {@code rr()} is {@code
* CalledMethods("rr")}. This rule is implemented directly in the {@link
* org.checkerframework.framework.type.treeannotator.TreeAnnotator} subclass defined in the Called
* Methods type factory.
*
* @param node the node whose estimate should be expanded
* @param result the transfer result containing the store to be modified
* @param values the new accumulation values
*/
public void accumulate(Node node, TransferResult<CFValue, CFStore> result, String... values) {
List<String> valuesAsList = Arrays.asList(values);
// If dataflow has already recorded information about the target, fetch it and integrate
// it into the list of values in the new annotation.
JavaExpression target = JavaExpression.fromNode(node);
if (CFAbstractStore.canInsertJavaExpression(target)) {
CFValue flowValue = result.getRegularStore().getValue(target);
if (flowValue != null) {
Set<AnnotationMirror> flowAnnos = flowValue.getAnnotations();
assert flowAnnos.size() <= 1;
for (AnnotationMirror anno : flowAnnos) {
if (atypeFactory.isAccumulatorAnnotation(anno)) {
List<String> oldFlowValues = atypeFactory.getAccumulatedValues(anno);
if (!oldFlowValues.isEmpty()) {
// valuesAsList cannot have its length changed -- it is backed by an
// array -- but if oldFlowValues is not empty, it is a new, modifiable list.
oldFlowValues.addAll(valuesAsList);
valuesAsList = oldFlowValues;
}
}
}
}
}
AnnotationMirror newAnno = atypeFactory.createAccumulatorAnnotation(valuesAsList);
insertIntoStores(result, target, newAnno);
Tree tree = node.getTree();
if (tree != null && tree.getKind() == Tree.Kind.METHOD_INVOCATION) {
Node receiver = ((MethodInvocationNode) node).getTarget().getReceiver();
if (receiver != null && atypeFactory.returnsThis((MethodInvocationTree) tree)) {
accumulate(receiver, result, values);
}
}
}
use of org.checkerframework.dataflow.cfg.node.MethodInvocationNode in project checker-framework by typetools.
the class AliasingTransfer method visitMethodInvocation.
/**
* Case 3: Given a method invocation expression, if the parent of the expression is not a
* statement, check if there are any arguments of the method call annotated as
* {@literal @}LeakedToResult and remove it from the store, since it might be leaked.
*/
@Override
public TransferResult<CFValue, CFStore> visitMethodInvocation(MethodInvocationNode n, TransferInput<CFValue, CFStore> in) {
Tree parent = n.getTreePath().getParentPath().getLeaf();
boolean parentIsStatement = parent.getKind() == Tree.Kind.EXPRESSION_STATEMENT;
if (!parentIsStatement) {
ExecutableElement methodElement = TreeUtils.elementFromUse(n.getTree());
List<Node> args = n.getArguments();
List<? extends VariableElement> params = methodElement.getParameters();
assert (args.size() == params.size()) : "Number of arguments in " + "the method call " + n + " is different from the" + " number of parameters for the method declaration: " + methodElement.getSimpleName();
CFStore store = in.getRegularStore();
for (int i = 0; i < args.size(); i++) {
Node arg = args.get(i);
VariableElement param = params.get(i);
if (factory.getAnnotatedType(param).hasAnnotation(LeakedToResult.class)) {
// If argument can leak to result, and parent is not a
// single statement, remove that node from store.
store.clearValue(JavaExpression.fromNode(arg));
}
}
// Now, doing the same as above for the receiver parameter
Node receiver = n.getTarget().getReceiver();
AnnotatedExecutableType annotatedType = factory.getAnnotatedType(methodElement);
AnnotatedDeclaredType receiverType = annotatedType.getReceiverType();
if (receiverType != null && receiverType.hasAnnotation(LeakedToResult.class)) {
store.clearValue(JavaExpression.fromNode(receiver));
}
}
// If parent is a statement, processPostconditions will handle the pseudo-assignments.
return super.visitMethodInvocation(n, in);
}
use of org.checkerframework.dataflow.cfg.node.MethodInvocationNode in project checker-framework by typetools.
the class AliasingTransfer method visitAssignment.
/**
* Case 1: For every assignment, the LHS is refined if the RHS has type {@literal @}Unique and is
* a method invocation or a new class instance.
*/
@Override
public TransferResult<CFValue, CFStore> visitAssignment(AssignmentNode n, TransferInput<CFValue, CFStore> in) {
Node rhs = n.getExpression();
Tree treeRhs = rhs.getTree();
AnnotatedTypeMirror rhsType = factory.getAnnotatedType(treeRhs);
if (rhsType.hasAnnotation(Unique.class) && (rhs instanceof MethodInvocationNode || rhs instanceof ObjectCreationNode)) {
// Do normal refinement.
return super.visitAssignment(n, in);
}
// Widen the type of the rhs if the RHS's declared type wasn't @Unique.
JavaExpression rhsExpr = JavaExpression.fromNode(rhs);
in.getRegularStore().clearValue(rhsExpr);
return new RegularTransferResult<>(null, in.getRegularStore());
}
use of org.checkerframework.dataflow.cfg.node.MethodInvocationNode in project checker-framework by typetools.
the class NullnessTransfer method visitMethodInvocation.
/*
* Provided that m is of a type that implements interface java.util.Map:
* <ul>
* <li>Given a call m.get(k), if k is @KeyFor("m") and m's value type is @NonNull,
* then the result is @NonNull in the thenStore and elseStore of the transfer result.
* </ul>
*/
@Override
public TransferResult<NullnessValue, NullnessStore> visitMethodInvocation(MethodInvocationNode n, TransferInput<NullnessValue, NullnessStore> in) {
TransferResult<NullnessValue, NullnessStore> result = super.visitMethodInvocation(n, in);
// Make receiver non-null.
Node receiver = n.getTarget().getReceiver();
makeNonNull(result, receiver);
// For all formal parameters with a non-null annotation, make the actual argument non-null.
// The point of this is to prevent cascaded errors -- the Nullness Checker will issue a
// warning for the method invocation, but not for subsequent uses of the argument. See test
// case FlowNullness.java.
MethodInvocationTree tree = n.getTree();
ExecutableElement method = TreeUtils.elementFromUse(tree);
AnnotatedExecutableType methodType = nullnessTypeFactory.getAnnotatedType(method);
List<AnnotatedTypeMirror> methodParams = methodType.getParameterTypes();
List<? extends ExpressionTree> methodArgs = tree.getArguments();
for (int i = 0; i < methodParams.size() && i < methodArgs.size(); ++i) {
if (methodParams.get(i).hasAnnotation(NONNULL)) {
makeNonNull(result, n.getArgument(i));
}
}
// the map, and the map's value type is not @Nullable.
if (keyForTypeFactory != null && keyForTypeFactory.isMapGet(n)) {
String mapName = JavaExpression.fromNode(receiver).toString();
AnnotatedTypeMirror receiverType = nullnessTypeFactory.getReceiverType(n.getTree());
if (keyForTypeFactory.isKeyForMap(mapName, methodArgs.get(0)) && !hasNullableValueType(receiverType)) {
makeNonNull(result, n);
refineToNonNull(result);
}
}
return result;
}
use of org.checkerframework.dataflow.cfg.node.MethodInvocationNode in project checker-framework by typetools.
the class MustCallConsistencyAnalyzer method getParametersOfInvocation.
/**
* Get the elements representing the formal parameters of a method or constructor, from an
* invocation of that method or constructor.
*
* @param node a method invocation or object creation node
* @return a list of the declarations of the formal parameters of the method or constructor being
* invoked
*/
private List<? extends VariableElement> getParametersOfInvocation(Node node) {
ExecutableElement executableElement;
if (node instanceof MethodInvocationNode) {
MethodInvocationNode invocationNode = (MethodInvocationNode) node;
executableElement = TreeUtils.elementFromUse(invocationNode.getTree());
} else if (node instanceof ObjectCreationNode) {
executableElement = TreeUtils.elementFromUse(((ObjectCreationNode) node).getTree());
} else {
throw new TypeSystemError("unexpected node type " + node.getClass());
}
return executableElement.getParameters();
}
Aggregations