use of com.sun.source.tree.MethodTree in project checker-framework by typetools.
the class GuiEffectVisitor method scanUp.
/**
* This method is called to traverse the path back up from any anonymous inner class or lambda
* which has been inferred to be UI affecting and re-run {@code commonAssignmentCheck} as needed
* on places where the class declaration or lambda expression are being assigned to a variable,
* passed as a parameter or returned from a method. This is necessary because the normal visitor
* traversal only checks assignments on the way down the AST, before inference has had a chance to
* run.
*
* @param path the path to traverse up from a UI-affecting class
*/
private void scanUp(TreePath path) {
Tree tree = path.getLeaf();
switch(tree.getKind()) {
case ASSIGNMENT:
AssignmentTree assignmentTree = (AssignmentTree) tree;
commonAssignmentCheck(atypeFactory.getAnnotatedType(assignmentTree.getVariable()), atypeFactory.getAnnotatedType(assignmentTree.getExpression()), assignmentTree.getExpression(), "assignment");
break;
case VARIABLE:
VariableTree variableTree = (VariableTree) tree;
commonAssignmentCheck(atypeFactory.getAnnotatedType(variableTree), atypeFactory.getAnnotatedType(variableTree.getInitializer()), variableTree.getInitializer(), "assignment");
break;
case METHOD_INVOCATION:
MethodInvocationTree invocationTree = (MethodInvocationTree) tree;
List<? extends ExpressionTree> args = invocationTree.getArguments();
ParameterizedExecutableType mType = atypeFactory.methodFromUse(invocationTree);
AnnotatedExecutableType invokedMethod = mType.executableType;
ExecutableElement method = invokedMethod.getElement();
CharSequence methodName = ElementUtils.getSimpleNameOrDescription(method);
List<? extends VariableElement> methodParams = method.getParameters();
List<AnnotatedTypeMirror> paramTypes = AnnotatedTypes.expandVarArgsParameters(atypeFactory, invokedMethod, invocationTree.getArguments());
for (int i = 0; i < args.size(); ++i) {
if (args.get(i).getKind() == Tree.Kind.NEW_CLASS || args.get(i).getKind() == Tree.Kind.LAMBDA_EXPRESSION) {
commonAssignmentCheck(paramTypes.get(i), atypeFactory.getAnnotatedType(args.get(i)), args.get(i), "argument", methodParams.get(i), methodName);
}
}
break;
case RETURN:
ReturnTree returnTree = (ReturnTree) tree;
if (returnTree.getExpression().getKind() == Tree.Kind.NEW_CLASS || returnTree.getExpression().getKind() == Tree.Kind.LAMBDA_EXPRESSION) {
Tree enclosing = TreePathUtil.enclosingMethodOrLambda(path);
AnnotatedTypeMirror ret = null;
if (enclosing.getKind() == Tree.Kind.METHOD) {
MethodTree enclosingMethod = (MethodTree) enclosing;
boolean valid = validateTypeOf(enclosing);
if (valid) {
ret = atypeFactory.getMethodReturnType(enclosingMethod, returnTree);
}
} else {
ret = atypeFactory.getFunctionTypeFromTree((LambdaExpressionTree) enclosing).getReturnType();
}
if (ret != null) {
commonAssignmentCheck(ret, atypeFactory.getAnnotatedType(returnTree.getExpression()), returnTree.getExpression(), "return");
}
}
break;
case METHOD:
// without either being assigned to a field or returned.
return;
case CLASS:
// boundaries
assert false;
return;
default:
scanUp(path.getParentPath());
}
}
use of com.sun.source.tree.MethodTree in project checker-framework by typetools.
the class GuiEffectVisitor method visitMethodInvocation.
// Check that the invoked effect is <= permitted effect (effStack.peek())
@Override
public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
if (debugSpew) {
System.err.println("For invocation " + node + " in " + currentMethods.peek().getName());
}
// Target method annotations
ExecutableElement methodElt = TreeUtils.elementFromUse(node);
if (debugSpew) {
System.err.println("methodElt found");
}
Tree callerTree = TreePathUtil.enclosingMethodOrLambda(getCurrentPath());
if (callerTree == null) {
// Static initializer; let's assume this is safe to have the UI effect
if (debugSpew) {
System.err.println("No enclosing method: likely static initializer");
}
return super.visitMethodInvocation(node, p);
}
if (debugSpew) {
System.err.println("callerTree found: " + callerTree.getKind());
}
Effect targetEffect = atypeFactory.getComputedEffectAtCallsite(node, receiverType, methodElt);
Effect callerEffect = null;
if (callerTree.getKind() == Tree.Kind.METHOD) {
ExecutableElement callerElt = TreeUtils.elementFromDeclaration((MethodTree) callerTree);
if (debugSpew) {
System.err.println("callerElt found");
}
callerEffect = atypeFactory.getDeclaredEffect(callerElt);
final DeclaredType callerReceiverType = classType.getUnderlyingType();
assert callerReceiverType != null;
final TypeElement callerReceiverElt = (TypeElement) callerReceiverType.asElement();
// long.
if (TypesUtils.isAnonymous(callerReceiverType) && // Skip if already inferred @UI
!effStack.peek().isUI() && // Ignore if explicitly annotated
!atypeFactory.fromElement(callerReceiverElt).hasAnnotation(AlwaysSafe.class) && !atypeFactory.fromElement(callerReceiverElt).hasAnnotation(UI.class)) {
boolean overridesPolymorphic = false;
Map<AnnotatedTypeMirror.AnnotatedDeclaredType, ExecutableElement> overriddenMethods = AnnotatedTypes.overriddenMethods(elements, atypeFactory, callerElt);
for (Map.Entry<AnnotatedTypeMirror.AnnotatedDeclaredType, ExecutableElement> pair : overriddenMethods.entrySet()) {
AnnotatedTypeMirror.AnnotatedDeclaredType overriddenType = pair.getKey();
AnnotatedExecutableType overriddenMethod = AnnotatedTypes.asMemberOf(types, atypeFactory, overriddenType, pair.getValue());
if (atypeFactory.getDeclAnnotation(overriddenMethod.getElement(), PolyUIEffect.class) != null && atypeFactory.getDeclAnnotation(overriddenType.getUnderlyingType().asElement(), PolyUIType.class) != null) {
overridesPolymorphic = true;
break;
}
}
// @UI anon class
if (overridesPolymorphic && targetEffect.isUI()) {
// Mark the anonymous class as @UI
atypeFactory.constrainAnonymousClassToUI(callerReceiverElt);
// Then re-calculate this method's effect (it might still not be an
// @PolyUIEffect method).
callerEffect = atypeFactory.getDeclaredEffect(callerElt);
effStack.pop();
effStack.push(callerEffect);
}
}
// --- the traversal goes straight from the class to the initializer.
assert (currentMethods.peek() == null || callerEffect.equals(effStack.peek()));
} else if (callerTree.getKind() == Tree.Kind.LAMBDA_EXPRESSION) {
callerEffect = atypeFactory.getInferedEffectForLambdaExpression((LambdaExpressionTree) callerTree);
// lambda
if (targetEffect.isUI() && callerEffect.isPoly()) {
atypeFactory.constrainLambdaToUI((LambdaExpressionTree) callerTree);
callerEffect = new Effect(UIEffect.class);
}
}
assert callerEffect != null;
if (!Effect.lessThanOrEqualTo(targetEffect, callerEffect)) {
checker.reportError(node, "call.ui", targetEffect, callerEffect);
if (debugSpew) {
System.err.println("Issuing error for node: " + node);
}
}
if (debugSpew) {
System.err.println("Successfully finished main non-recursive checkinv of invocation " + node);
}
return super.visitMethodInvocation(node, p);
}
use of com.sun.source.tree.MethodTree in project checker-framework by typetools.
the class BaseTypeValidator method extractParameterizedTypeTree.
/**
* If {@code tree} has a {@link ParameterizedTypeTree}, then the tree and its type is returned.
* Otherwise null and {@code type} are returned.
*
* @param tree tree to search
* @param type type to return if no {@code ParameterizedTypeTree} is found
* @return if {@code tree} has a {@code ParameterizedTypeTree}, then returns the tree and its
* type. Otherwise, returns null and {@code type}.
*/
private Pair<@Nullable ParameterizedTypeTree, AnnotatedDeclaredType> extractParameterizedTypeTree(Tree tree, AnnotatedDeclaredType type) {
ParameterizedTypeTree typeargtree = null;
switch(tree.getKind()) {
case VARIABLE:
Tree lt = ((VariableTree) tree).getType();
if (lt instanceof ParameterizedTypeTree) {
typeargtree = (ParameterizedTypeTree) lt;
} else {
// System.out.println("Found a: " + lt);
}
break;
case PARAMETERIZED_TYPE:
typeargtree = (ParameterizedTypeTree) tree;
break;
case NEW_CLASS:
NewClassTree nct = (NewClassTree) tree;
ExpressionTree nctid = nct.getIdentifier();
if (nctid.getKind() == Tree.Kind.PARAMETERIZED_TYPE) {
typeargtree = (ParameterizedTypeTree) nctid;
/*
* This is quite tricky... for anonymous class instantiations,
* the type at this point has no type arguments. By doing the
* following, we get the type arguments again.
*/
type = (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(typeargtree);
}
break;
case ANNOTATED_TYPE:
AnnotatedTypeTree tr = (AnnotatedTypeTree) tree;
ExpressionTree undtr = tr.getUnderlyingType();
if (undtr instanceof ParameterizedTypeTree) {
typeargtree = (ParameterizedTypeTree) undtr;
} else if (undtr instanceof IdentifierTree) {
// @Something D -> Nothing to do
} else {
// TODO: add more test cases to ensure that nested types are
// handled correctly,
// e.g. @Nullable() List<@Nullable Object>[][]
Pair<ParameterizedTypeTree, AnnotatedDeclaredType> p = extractParameterizedTypeTree(undtr, type);
typeargtree = p.first;
type = p.second;
}
break;
case IDENTIFIER:
case ARRAY_TYPE:
case NEW_ARRAY:
case MEMBER_SELECT:
case UNBOUNDED_WILDCARD:
case EXTENDS_WILDCARD:
case SUPER_WILDCARD:
case TYPE_PARAMETER:
// Nothing to do.
break;
case METHOD:
// If a MethodTree is passed, it's just the return type that is validated.
// See BaseTypeVisitor#validateTypeOf.
MethodTree methodTree = (MethodTree) tree;
if (methodTree.getReturnType() instanceof ParameterizedTypeTree) {
typeargtree = (ParameterizedTypeTree) methodTree.getReturnType();
}
break;
default:
// No need to do anything further.
break;
}
return Pair.of(typeargtree, type);
}
use of com.sun.source.tree.MethodTree 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.
*
* @param atypeFactory the type factory, for looking up types
* @param path the path whole leaf to look up a type for
* @return the type of path's leaf
*/
// AST node comparisons
@SuppressWarnings("interning:not.interned")
public static AnnotatedTypeMirror assignedTo(AnnotatedTypeFactory atypeFactory, TreePath path) {
Tree assignmentContext = TreePathUtil.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);
if (TreeUtils.isSuperConstructorCall(methodInvocation)) {
receiver = atypeFactory.getSelfType(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;
// TODO: 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;
if (newClassTree.getEnclosingExpression() instanceof NewClassTree && (newClassTree.getEnclosingExpression() == path.getLeaf())) {
return null;
}
ExecutableElement constructorElt = TreeUtils.constructor(newClassTree);
AnnotatedTypeMirror receiver = atypeFactory.fromNewClass(newClassTree);
res = assignedToExecutable(atypeFactory, path, constructorElt, receiver, newClassTree.getArguments());
} else if (assignmentContext instanceof ReturnTree) {
HashSet<Tree.Kind> kinds = new HashSet<>(Arrays.asList(Tree.Kind.LAMBDA_EXPRESSION, Tree.Kind.METHOD));
Tree enclosing = TreePathUtil.enclosingOfKind(path, kinds);
if (enclosing.getKind() == Tree.Kind.METHOD) {
res = atypeFactory.getAnnotatedType((MethodTree) enclosing).getReturnType();
} else {
AnnotatedExecutableType fninf = atypeFactory.getFunctionTypeFromTree((LambdaExpressionTree) enclosing);
res = fninf.getReturnType();
}
} else if (assignmentContext instanceof VariableTree) {
res = assignedToVariable(atypeFactory, assignmentContext);
} else {
throw new BugInCF("AnnotatedTypes.assignedTo: shouldn't be here");
}
if (res != null && TypesUtils.isPrimitive(res.getUnderlyingType())) {
return atypeFactory.getBoxedType((AnnotatedPrimitiveType) res);
} else {
return res;
}
}
use of com.sun.source.tree.MethodTree in project checker-framework by typetools.
the class QualifierDefaults method nearestEnclosingExceptLocal.
/**
* Determines the nearest enclosing element for a tree by climbing the tree toward the root and
* obtaining the element for the first declaration (variable, method, or class) that encloses the
* tree. Initializers of local variables are handled in a special way: within an initializer we
* look for the DefaultQualifier(s) annotation and keep track of the previously visited tree.
* TODO: explain the behavior better.
*
* @param tree the tree
* @return the nearest enclosing element for a tree
*/
private Element nearestEnclosingExceptLocal(Tree tree) {
TreePath path = atypeFactory.getPath(tree);
if (path == null) {
Element element = atypeFactory.getEnclosingElementForArtificialTree(tree);
if (element != null) {
return element;
} else {
return TreeUtils.elementFromTree(tree);
}
}
Tree prev = null;
for (Tree t : path) {
switch(TreeUtils.getKindRecordAsClass(t)) {
case ANNOTATED_TYPE:
case ANNOTATION:
// If the tree is in an annotation, then there is no relevant scope.
return null;
case VARIABLE:
VariableTree vtree = (VariableTree) t;
ExpressionTree vtreeInit = vtree.getInitializer();
// check cached value
@SuppressWarnings("interning:not.interned") boolean sameAsPrev = (vtreeInit != null && prev == vtreeInit);
if (sameAsPrev) {
Element elt = TreeUtils.elementFromDeclaration((VariableTree) t);
AnnotationMirror d = atypeFactory.getDeclAnnotation(elt, DefaultQualifier.class);
AnnotationMirror ds = atypeFactory.getDeclAnnotation(elt, DefaultQualifier.List.class);
if (d == null && ds == null) {
break;
}
}
if (prev != null && prev.getKind() == Tree.Kind.MODIFIERS) {
// argument became incompatible with the declared type.
break;
}
return TreeUtils.elementFromDeclaration((VariableTree) t);
case METHOD:
return TreeUtils.elementFromDeclaration((MethodTree) t);
// Including RECORD
case CLASS:
case ENUM:
case INTERFACE:
case ANNOTATION_TYPE:
return TreeUtils.elementFromDeclaration((ClassTree) t);
// Do nothing.
default:
}
prev = t;
}
return null;
}
Aggregations