use of org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType 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() == 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.toString() + " is different from the" + " number of parameters for the method declaration: " + methodElement.getSimpleName().toString();
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(FlowExpressions.internalReprOf(factory, 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(FlowExpressions.internalReprOf(factory, receiver));
}
}
// pseudo-assignments.
return super.visitMethodInvocation(n, in);
}
use of org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType in project checker-framework by typetools.
the class AliasingVisitor method visitMethodInvocation.
/**
* Checks that if a method call is being invoked inside a constructor with result type
* {@literal @}Unique, it must not leak the "this" reference. There are 3 ways to make sure that
* this is not happening:
*
* <ol>
* <li>{@code this} is not an argument of the method call.
* <li>{@code this} is an argument of the method call, but the respective parameter is
* annotated as {@literal @}NonLeaked.
* <li>{@code this} is an argument of the method call, but the respective parameter is
* annotated as {@literal @}LeakedToResult AND the result of the method call is not being
* stored (the method call is a statement).
* </ol>
*
* The private method {@code isUniqueCheck} handles cases 2 and 3.
*/
@Override
public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
// @Unique. We also want to avoid visiting the <init> method.
if (isInUniqueConstructor(node)) {
if (TreeUtils.isSuperCall(node)) {
// Check if a call to super() might create an alias: that
// happens when the parent's respective constructor is not @Unique.
AnnotatedTypeMirror superResult = atypeFactory.getAnnotatedType(node);
if (!superResult.hasAnnotation(Unique.class)) {
checker.report(Result.failure("unique.leaked"), node);
}
} else {
// TODO: Currently the type of "this" doesn't always return
// the type of the constructor result, therefore we need
// this "else" block. Once constructors are implemented
// correctly we could remove that code below, since the type
// of "this" in a @Unique constructor will be @Unique.
Tree parent = getCurrentPath().getParentPath().getLeaf();
boolean parentIsStatement = parent.getKind() == Kind.EXPRESSION_STATEMENT;
ExecutableElement methodElement = TreeUtils.elementFromUse(node);
List<? extends VariableElement> params = methodElement.getParameters();
List<? extends ExpressionTree> args = node.getArguments();
assert (args.size() == params.size()) : "Number of arguments in" + " the method call " + node.toString() + " is different from the " + "number of parameters for the method declaration: " + methodElement.getSimpleName().toString();
for (int i = 0; i < args.size(); i++) {
// For every argument we check if it is a reference to "this".
if (TreeUtils.isExplicitThisDereference(args.get(i))) {
// If it is a reference to "this", there is still hope that
// it is not being leaked (2. and 3. from the javadoc).
VariableElement param = params.get(i);
boolean hasNonLeaked = atypeFactory.getAnnotatedType(param).hasAnnotation(NonLeaked.class);
boolean hasLeakedToResult = atypeFactory.getAnnotatedType(param).hasAnnotation(LeakedToResult.class);
isUniqueCheck(node, parentIsStatement, hasNonLeaked, hasLeakedToResult);
} else {
// Not possible to leak reference here (case 1. from the javadoc).
}
}
// Now, doing the same as above for the receiver parameter
AnnotatedExecutableType annotatedType = atypeFactory.getAnnotatedType(methodElement);
AnnotatedDeclaredType receiverType = annotatedType.getReceiverType();
if (receiverType != null) {
boolean hasNonLeaked = receiverType.hasAnnotation(NonLeaked.class);
boolean hasLeakedToResult = receiverType.hasAnnotation(LeakedToResult.class);
isUniqueCheck(node, parentIsStatement, hasNonLeaked, hasLeakedToResult);
}
}
}
return super.visitMethodInvocation(node, p);
}
use of org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType in project checker-framework by typetools.
the class BaseTypeVisitor method checkMethodReferenceAsOverride.
/**
* Check that a method reference is allowed. Using the OverrideChecker class.
*
* @param memberReferenceTree the tree for the method reference
* @return true if the method reference is allowed
*/
protected boolean checkMethodReferenceAsOverride(MemberReferenceTree memberReferenceTree, Void p) {
Pair<AnnotatedDeclaredType, AnnotatedExecutableType> result = atypeFactory.getFnInterfaceFromTree(memberReferenceTree);
// The type to which the member reference is assigned -- also known as the target type of the reference.
AnnotatedDeclaredType functionalInterface = result.first;
// The type of the single method that is declared by the functional interface.
AnnotatedExecutableType functionType = result.second;
// ========= Overriding Type =========
// This doesn't get the correct type for a "MyOuter.super" based on the receiver of the
// enclosing method.
// That is handled separately in method receiver check.
// The type of the expression or type use, <expression>::method or <type use>::method.
final ExpressionTree qualifierExpression = memberReferenceTree.getQualifierExpression();
final ReferenceKind memRefKind = ((JCMemberReference) memberReferenceTree).kind;
AnnotatedTypeMirror enclosingType;
if (memberReferenceTree.getMode() == ReferenceMode.NEW || memRefKind == ReferenceKind.UNBOUND || memRefKind == ReferenceKind.STATIC) {
// The "qualifier expression" is a type tree.
enclosingType = atypeFactory.getAnnotatedTypeFromTypeTree(qualifierExpression);
} else {
// The "qualifier expression" is an expression.
enclosingType = atypeFactory.getAnnotatedType(qualifierExpression);
}
// ========= Overriding Executable =========
// The ::method element, see JLS 15.13.1 Compile-Time Declaration of a Method Reference
ExecutableElement compileTimeDeclaration = (ExecutableElement) TreeUtils.elementFromTree(memberReferenceTree);
if (enclosingType.getKind() == TypeKind.DECLARED && ((AnnotatedDeclaredType) enclosingType).wasRaw()) {
if (memRefKind == ReferenceKind.UNBOUND) {
// The method reference is of the form: Type # instMethod
// and Type is a raw type.
// If the first parameter of the function type, p1, is a subtype
// of type, then type should be p1. This has the effect of "inferring" the
// class type parameter.
AnnotatedTypeMirror p1 = functionType.getParameterTypes().get(0);
TypeMirror asSuper = TypesUtils.asSuper(p1.getUnderlyingType(), enclosingType.getUnderlyingType(), atypeFactory.getProcessingEnv());
if (asSuper != null) {
enclosingType = AnnotatedTypes.asSuper(atypeFactory, p1, enclosingType);
}
}
// else method reference is something like ArrayList::new
// TODO: Use diamond, <>, inference to infer the class type arguments.
// for now this case is skipped below in checkMethodReferenceInference.
}
// The type of the compileTimeDeclaration if it were invoked with a receiver expression
// of type {@code type}
AnnotatedExecutableType invocationType = atypeFactory.methodFromUse(memberReferenceTree, compileTimeDeclaration, enclosingType).first;
if (checkMethodReferenceInference(memberReferenceTree, invocationType, enclosingType)) {
// #checkMethodReferenceInference issued a warning.
return true;
}
// functionType.getReturnType()
if (invocationType.getTypeVariables().isEmpty() && !functionType.getTypeVariables().isEmpty()) {
functionType = functionType.getErased();
}
// Use the function type's parameters to resolve polymorphic qualifiers.
QualifierPolymorphism poly = new QualifierPolymorphism(atypeFactory.getProcessingEnv(), atypeFactory);
poly.annotate(functionType, invocationType);
AnnotatedTypeMirror invocationReturnType;
if (compileTimeDeclaration.getKind() == ElementKind.CONSTRUCTOR) {
if (enclosingType.getKind() == TypeKind.ARRAY) {
// Special casing for the return of array constructor
invocationReturnType = enclosingType;
} else {
invocationReturnType = atypeFactory.getResultingTypeOfConstructorMemberReference(memberReferenceTree, invocationType);
}
} else {
invocationReturnType = invocationType.getReturnType();
}
AnnotatedTypeMirror functionTypeReturnType = functionType.getReturnType();
if (functionTypeReturnType.getKind() == TypeKind.VOID) {
// If the functional interface return type is void, the overriding return
// type doesn't matter.
functionTypeReturnType = invocationReturnType;
}
// Check the member reference as if invocationType overrides functionType.
OverrideChecker overrideChecker = createOverrideChecker(memberReferenceTree, invocationType, enclosingType, invocationReturnType, functionType, functionalInterface, functionTypeReturnType);
return overrideChecker.checkOverride();
}
use of org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType in project checker-framework by typetools.
the class DependentTypesHelper method viewpointAdaptExecutable.
private void viewpointAdaptExecutable(ExpressionTree tree, ExpressionTree receiverTree, AnnotatedExecutableType typeFromUse, List<? extends ExpressionTree> args) {
Element element = TreeUtils.elementFromUse(tree);
AnnotatedExecutableType viewpointAdaptedType = (AnnotatedExecutableType) factory.getAnnotatedType(element);
if (!hasDependentType(viewpointAdaptedType)) {
return;
}
FlowExpressions.Receiver receiver;
if (receiverTree == null) {
receiver = FlowExpressions.internalReprOfImplicitReceiver(TreeUtils.elementFromUse(tree));
} else {
receiver = FlowExpressions.internalReprOf(factory, receiverTree);
}
List<FlowExpressions.Receiver> argReceivers = new ArrayList<>(args.size());
for (ExpressionTree argTree : args) {
argReceivers.add(FlowExpressions.internalReprOf(factory, argTree));
}
TreePath currentPath = factory.getPath(tree);
FlowExpressionContext context = new FlowExpressionContext(receiver, argReceivers, factory.getContext());
// typeForUse cannot be viewpoint adapted directly because it is the type post type variable
// substitution. Dependent type annotations on type arguments do not (and cannot) be
// viewpoint adapted along with the dependent type annotations that are on the method
// declaration. For example:
// Map<String, String> map = ...;
// List<@KeyFor("map") String> list = ...;
// list.get(0)
// If the type of List.get is viewpoint adapted for the invocation "list.get(0)", then
// typeFromUse would be @KeyFor("map") String get(int).
// Instead, use the type for the method (viewpointAdaptedType) and viewpoint adapt that
// type.
// Then copy annotations from the viewpoint adapted type to typeFromUse, if that annotation
// is not on a type that was substituted for a type variable.
standardizeDoNotUseLocals(context, currentPath, viewpointAdaptedType);
new ViewpointAdaptedCopier().visit(viewpointAdaptedType, typeFromUse);
}
use of org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType in project checker-framework by typetools.
the class TypeArgInferenceUtil method assignedTo.
/**
* Returns the annotated type that the leaf of path is assigned to, if it is within an
* assignment context. Returns the annotated type that the method invocation at the leaf is
* assigned to. If the result is a primitive, return the boxed version.
*
* @return type that path leaf is assigned to
*/
public static AnnotatedTypeMirror assignedTo(AnnotatedTypeFactory atypeFactory, TreePath path) {
Tree assignmentContext = TreeUtils.getAssignmentContext(path);
AnnotatedTypeMirror res;
if (assignmentContext == null) {
res = null;
} else if (assignmentContext instanceof AssignmentTree) {
ExpressionTree variable = ((AssignmentTree) assignmentContext).getVariable();
res = atypeFactory.getAnnotatedType(variable);
} else if (assignmentContext instanceof CompoundAssignmentTree) {
ExpressionTree variable = ((CompoundAssignmentTree) assignmentContext).getVariable();
res = atypeFactory.getAnnotatedType(variable);
} else if (assignmentContext instanceof MethodInvocationTree) {
MethodInvocationTree methodInvocation = (MethodInvocationTree) assignmentContext;
// TODO move to getAssignmentContext
if (methodInvocation.getMethodSelect() instanceof MemberSelectTree && ((MemberSelectTree) methodInvocation.getMethodSelect()).getExpression() == path.getLeaf()) {
return null;
}
ExecutableElement methodElt = TreeUtils.elementFromUse(methodInvocation);
AnnotatedTypeMirror receiver = atypeFactory.getReceiverType(methodInvocation);
res = assignedToExecutable(atypeFactory, path, methodElt, receiver, methodInvocation.getArguments());
} else if (assignmentContext instanceof NewArrayTree) {
// TODO: I left the previous implementation below, it definitely caused infinite loops
// TODO: if you called it from places like the TreeAnnotator.
res = null;
// FIXME: This may cause infinite loop
// AnnotatedTypeMirror type =
// atypeFactory.getAnnotatedType((NewArrayTree)assignmentContext);
// type = AnnotatedTypes.innerMostType(type);
// return type;
} else if (assignmentContext instanceof NewClassTree) {
// This need to be basically like MethodTree
NewClassTree newClassTree = (NewClassTree) assignmentContext;
ExecutableElement constructorElt = TreeUtils.constructor(newClassTree);
AnnotatedTypeMirror receiver = atypeFactory.fromNewClass(newClassTree);
res = assignedToExecutable(atypeFactory, path, constructorElt, receiver, newClassTree.getArguments());
} else if (assignmentContext instanceof ReturnTree) {
HashSet<Kind> kinds = new HashSet<>(Arrays.asList(Kind.LAMBDA_EXPRESSION, Kind.METHOD));
Tree enclosing = TreeUtils.enclosingOfKind(path, kinds);
if (enclosing.getKind() == Kind.METHOD) {
res = (atypeFactory.getAnnotatedType((MethodTree) enclosing)).getReturnType();
} else {
Pair<AnnotatedDeclaredType, AnnotatedExecutableType> fninf = atypeFactory.getFnInterfaceFromTree((LambdaExpressionTree) enclosing);
res = fninf.second.getReturnType();
}
} else if (assignmentContext instanceof VariableTree) {
res = assignedToVariable(atypeFactory, assignmentContext);
} else {
ErrorReporter.errorAbort("AnnotatedTypes.assignedTo: shouldn't be here!");
res = null;
}
if (res != null && TypesUtils.isPrimitive(res.getUnderlyingType())) {
return atypeFactory.getBoxedType((AnnotatedPrimitiveType) res);
} else {
return res;
}
}
Aggregations