use of org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType in project checker-framework by typetools.
the class BaseTypeVisitor method visitMethodInvocation.
/**
* Performs a method invocation check.
*
* <p>An invocation of a method, m, on the receiver, r is valid only if:
*
* <ul>
* <li>passed arguments are subtypes of corresponding m parameters
* <li>r is a subtype of m receiver type
* <li>if m is generic, passed type arguments are subtypes of m type variables
* </ul>
*/
@Override
public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
// hard to check), also see CFGBuilder.visitMethodInvocation.
if (TreeUtils.elementFromUse(node) == null || TreeUtils.isEnumSuper(node)) {
return super.visitMethodInvocation(node, p);
}
if (shouldSkipUses(node)) {
return super.visitMethodInvocation(node, p);
}
ParameterizedExecutableType mType = atypeFactory.methodFromUse(node);
AnnotatedExecutableType invokedMethod = mType.executableType;
List<AnnotatedTypeMirror> typeargs = mType.typeArgs;
if (!atypeFactory.ignoreUninferredTypeArguments) {
for (AnnotatedTypeMirror typearg : typeargs) {
if (typearg.getKind() == TypeKind.WILDCARD && ((AnnotatedWildcardType) typearg).isUninferredTypeArgument()) {
checker.reportError(node, "type.arguments.not.inferred", invokedMethod.getElement().getSimpleName());
// only issue error once per method
break;
}
}
}
List<AnnotatedTypeParameterBounds> paramBounds = CollectionsPlume.mapList(AnnotatedTypeVariable::getBounds, invokedMethod.getTypeVariables());
ExecutableElement method = invokedMethod.getElement();
CharSequence methodName = ElementUtils.getSimpleNameOrDescription(method);
try {
checkTypeArguments(node, paramBounds, typeargs, node.getTypeArguments(), methodName, invokedMethod.getTypeVariables());
List<AnnotatedTypeMirror> params = AnnotatedTypes.expandVarArgsParameters(atypeFactory, invokedMethod, node.getArguments());
checkArguments(params, node.getArguments(), methodName, method.getParameters());
checkVarargs(invokedMethod, node);
if (ElementUtils.isMethod(invokedMethod.getElement(), vectorCopyInto, atypeFactory.getProcessingEnv())) {
typeCheckVectorCopyIntoArgument(node, params);
}
ExecutableElement invokedMethodElement = invokedMethod.getElement();
if (!ElementUtils.isStatic(invokedMethodElement) && !TreeUtils.isSuperConstructorCall(node)) {
checkMethodInvocability(invokedMethod, node);
}
// check precondition annotations
checkPreconditions(node, atypeFactory.getContractsFromMethod().getPreconditions(invokedMethodElement));
if (TreeUtils.isSuperConstructorCall(node)) {
checkSuperConstructorCall(node);
} else if (TreeUtils.isThisConstructorCall(node)) {
checkThisConstructorCall(node);
}
} catch (RuntimeException t) {
// is fixed this should be removed and crashes should be reported normally.
if (node.getTypeArguments().size() == typeargs.size()) {
// They type arguments were explicitly written.
throw t;
}
if (!atypeFactory.ignoreUninferredTypeArguments) {
checker.reportError(node, "type.arguments.not.inferred", invokedMethod.getElement().getSimpleName());
}
// else ignore the crash.
}
// Do not call super, as that would observe the arguments without
// a set assignment context.
scan(node.getMethodSelect(), p);
// super.visitMethodInvocation(node, p);
return null;
}
use of org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType in project checker-framework by typetools.
the class BaseTypeVisitor method visitNewClass.
/**
* Performs a new class invocation check.
*
* <p>An invocation of a constructor, c, is valid only if:
*
* <ul>
* <li>passed arguments are subtypes of corresponding c parameters
* <li>if c is generic, passed type arguments are subtypes of c type variables
* </ul>
*/
@Override
public Void visitNewClass(NewClassTree node, Void p) {
if (checker.shouldSkipUses(TreeUtils.constructor(node))) {
return super.visitNewClass(node, p);
}
ParameterizedExecutableType fromUse = atypeFactory.constructorFromUse(node);
AnnotatedExecutableType constructorType = fromUse.executableType;
List<AnnotatedTypeMirror> typeargs = fromUse.typeArgs;
List<? extends ExpressionTree> passedArguments = node.getArguments();
List<AnnotatedTypeMirror> params = AnnotatedTypes.expandVarArgsParameters(atypeFactory, constructorType, passedArguments);
ExecutableElement constructor = constructorType.getElement();
CharSequence constructorName = ElementUtils.getSimpleNameOrDescription(constructor);
checkArguments(params, passedArguments, constructorName, constructor.getParameters());
checkVarargs(constructorType, node);
List<AnnotatedTypeParameterBounds> paramBounds = CollectionsPlume.mapList(AnnotatedTypeVariable::getBounds, constructorType.getTypeVariables());
checkTypeArguments(node, paramBounds, typeargs, node.getTypeArguments(), constructorName, constructor.getTypeParameters());
boolean valid = validateTypeOf(node);
if (valid) {
AnnotatedDeclaredType dt = atypeFactory.getAnnotatedType(node);
atypeFactory.getDependentTypesHelper().checkTypeForErrorExpressions(dt, node);
checkConstructorInvocation(dt, constructorType, node);
}
// Do not call super, as that would observe the arguments without
// a set assignment context.
scan(node.getEnclosingExpression(), p);
scan(node.getIdentifier(), p);
scan(node.getClassBody(), p);
return null;
}
use of org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType in project checker-framework by typetools.
the class LockVisitor method visitMethodInvocation.
/**
* When visiting a method invocation, issue an error if the side effect annotation on the called
* method causes the side effect guarantee of the enclosing method to be violated. For example, a
* method annotated with @ReleasesNoLocks may not call a method annotated with @MayReleaseLocks.
* Also check that matching @GuardSatisfied(index) on a method's formal receiver/parameters
* matches those in corresponding locations on the method call site.
*
* @param node the MethodInvocationTree of the method call being visited
*/
@Override
public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
ExecutableElement methodElement = TreeUtils.elementFromUse(node);
SideEffectAnnotation seaOfInvokedMethod = atypeFactory.methodSideEffectAnnotation(methodElement, false);
MethodTree enclosingMethod = TreePathUtil.enclosingMethod(atypeFactory.getPath(node));
ExecutableElement enclosingMethodElement = null;
if (enclosingMethod != null) {
enclosingMethodElement = TreeUtils.elementFromDeclaration(enclosingMethod);
}
if (enclosingMethodElement != null) {
SideEffectAnnotation seaOfContainingMethod = atypeFactory.methodSideEffectAnnotation(enclosingMethodElement, false);
if (seaOfInvokedMethod.isWeakerThan(seaOfContainingMethod)) {
checker.reportError(node, "method.guarantee.violated", seaOfContainingMethod.getNameOfSideEffectAnnotation(), enclosingMethodElement.getSimpleName(), methodElement.getSimpleName(), seaOfInvokedMethod.getNameOfSideEffectAnnotation());
}
}
if (methodElement != null) {
// Handle releasing of explicit locks. Verify that the lock expression is effectively final.
ExpressionTree receiverTree = TreeUtils.getReceiverTree(node);
ensureReceiverOfExplicitUnlockCallIsEffectivelyFinal(methodElement, receiverTree);
// Handle acquiring of explicit locks. Verify that the lock expression is effectively final.
// If the method causes expression "this" or "#1" to be locked, verify that those expressions
// are effectively final. TODO: generalize to any expression. This is currently designed only
// to support methods in ReentrantLock and ReentrantReadWriteLock (which use the "this"
// expression), as well as Thread.holdsLock (which uses the "#1" expression).
AnnotationMirror ensuresLockHeldAnno = atypeFactory.getDeclAnnotation(methodElement, EnsuresLockHeld.class);
List<String> expressions = new ArrayList<>();
if (ensuresLockHeldAnno != null) {
expressions.addAll(AnnotationUtils.getElementValueArray(ensuresLockHeldAnno, atypeFactory.ensuresLockHeldValueElement, String.class));
}
AnnotationMirror ensuresLockHeldIfAnno = atypeFactory.getDeclAnnotation(methodElement, EnsuresLockHeldIf.class);
if (ensuresLockHeldIfAnno != null) {
expressions.addAll(AnnotationUtils.getElementValueArray(ensuresLockHeldIfAnno, atypeFactory.ensuresLockHeldIfExpressionElement, String.class));
}
for (String expr : expressions) {
if (expr.equals("this")) {
// final. So nothing to be checked for them.
if (receiverTree != null) {
ensureExpressionIsEffectivelyFinal(receiverTree);
}
} else if (expr.equals("#1")) {
ExpressionTree firstParameter = node.getArguments().get(0);
if (firstParameter != null) {
ensureExpressionIsEffectivelyFinal(firstParameter);
}
}
}
}
// Check that matching @GuardSatisfied(index) on a method's formal receiver/parameters
// matches those in corresponding locations on the method call site.
ParameterizedExecutableType mType = atypeFactory.methodFromUse(node);
AnnotatedExecutableType invokedMethod = mType.executableType;
List<AnnotatedTypeMirror> paramTypes = AnnotatedTypes.expandVarArgsParameters(atypeFactory, invokedMethod, node.getArguments());
// Index on @GuardSatisfied at each location. -1 when no @GuardSatisfied annotation was present.
// Note that @GuardSatisfied with no index is normally represented as having index -1.
// We would like to ignore a @GuardSatisfied with no index for these purposes, so if it is
// encountered we leave its index as -1.
// The first element of the array is reserved for the receiver.
int[] guardSatisfiedIndex = // + 1 for the receiver parameter type
new int[paramTypes.size() + 1];
// Retrieve receiver types from method definition and method call
guardSatisfiedIndex[0] = -1;
AnnotatedTypeMirror methodDefinitionReceiver = null;
AnnotatedTypeMirror methodCallReceiver = null;
ExecutableElement invokedMethodElement = invokedMethod.getElement();
if (!ElementUtils.isStatic(invokedMethodElement) && invokedMethod.getElement().getKind() != ElementKind.CONSTRUCTOR) {
methodDefinitionReceiver = invokedMethod.getReceiverType();
if (methodDefinitionReceiver != null && methodDefinitionReceiver.hasAnnotation(checkerGuardSatisfiedClass)) {
guardSatisfiedIndex[0] = atypeFactory.getGuardSatisfiedIndex(methodDefinitionReceiver);
methodCallReceiver = atypeFactory.getReceiverType(node);
}
}
for (int i = 0; i < paramTypes.size(); i++) {
guardSatisfiedIndex[i + 1] = -1;
AnnotatedTypeMirror paramType = paramTypes.get(i);
if (paramType.hasAnnotation(checkerGuardSatisfiedClass)) {
guardSatisfiedIndex[i + 1] = atypeFactory.getGuardSatisfiedIndex(paramType);
}
}
// Combine all of the actual parameters into one list of AnnotationMirrors
ArrayList<AnnotationMirror> passedArgAnnotations = new ArrayList<>(guardSatisfiedIndex.length);
passedArgAnnotations.add(methodCallReceiver == null ? null : methodCallReceiver.getAnnotationInHierarchy(atypeFactory.GUARDEDBYUNKNOWN));
for (ExpressionTree tree : node.getArguments()) {
passedArgAnnotations.add(atypeFactory.getAnnotatedType(tree).getAnnotationInHierarchy(atypeFactory.GUARDEDBYUNKNOWN));
}
for (int i = 0; i < guardSatisfiedIndex.length; i++) {
if (guardSatisfiedIndex[i] != -1) {
for (int j = i + 1; j < guardSatisfiedIndex.length; j++) {
if (guardSatisfiedIndex[i] == guardSatisfiedIndex[j]) {
// The @GuardedBy/@GuardSatisfied/@GuardedByUnknown/@GuardedByBottom
// annotations must be identical on the corresponding actual parameters.
AnnotationMirror arg1Anno = passedArgAnnotations.get(i);
AnnotationMirror arg2Anno = passedArgAnnotations.get(j);
if (arg1Anno != null && arg2Anno != null) {
boolean bothAreGSwithNoIndex = false;
if (atypeFactory.areSameByClass(arg1Anno, checkerGuardSatisfiedClass) && atypeFactory.areSameByClass(arg2Anno, checkerGuardSatisfiedClass)) {
if (atypeFactory.getGuardSatisfiedIndex(arg1Anno) == -1 && atypeFactory.getGuardSatisfiedIndex(arg2Anno) == -1) {
// Generally speaking, two @GuardSatisfied annotations with no
// index are incomparable.
// TODO: If they come from the same variable, they are
// comparable. Fix and add a test case.
bothAreGSwithNoIndex = true;
}
}
if (bothAreGSwithNoIndex || !(atypeFactory.getQualifierHierarchy().isSubtype(arg1Anno, arg2Anno) || atypeFactory.getQualifierHierarchy().isSubtype(arg2Anno, arg1Anno))) {
// TODO: allow these strings to be localized
String formalParam1 = null;
if (i == 0) {
formalParam1 = "The receiver type";
} else {
// i, not i-1, so the index is 1-based
formalParam1 = "Parameter #" + i;
}
// j, not j-1, so the index is 1-based
String formalParam2 = "parameter #" + j;
checker.reportError(node, "guardsatisfied.parameters.must.match", formalParam1, formalParam2, invokedMethod.toString(), guardSatisfiedIndex[i], arg1Anno, arg2Anno);
}
}
}
}
}
}
return super.visitMethodInvocation(node, p);
}
use of org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType 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 org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType in project checker-framework by typetools.
the class DefaultReflectionResolver method resolveMethodCall.
/**
* Resolves a call to {@link Method#invoke(Object, Object...)}.
*
* @param factory the {@link AnnotatedTypeFactory} of the underlying type system
* @param tree the method invocation tree that has to be resolved
* @param origResult the original result from {@code factory.methodFromUse}
* @return the resolved type of the call
*/
private ParameterizedExecutableType resolveMethodCall(AnnotatedTypeFactory factory, MethodInvocationTree tree, ParameterizedExecutableType origResult) {
debugReflection("Try to resolve reflective method call: " + tree);
List<MethodInvocationTree> possibleMethods = resolveReflectiveMethod(tree, factory);
// Reflective method could not be resolved
if (possibleMethods.isEmpty()) {
return origResult;
}
Set<? extends AnnotationMirror> returnLub = null;
Set<? extends AnnotationMirror> receiverGlb = null;
Set<? extends AnnotationMirror> paramsGlb = null;
// Iterate over all possible methods: lub return types, and glb receiver and parameter types
for (MethodInvocationTree resolvedTree : possibleMethods) {
debugReflection("Resolved method invocation: " + resolvedTree);
if (!checkMethodArguments(resolvedTree)) {
debugReflection("Spoofed tree's arguments did not match declaration" + resolvedTree);
// QualifierPolymorphism.PolyCollector.visitArray(...)
continue;
}
ParameterizedExecutableType resolvedResult = factory.methodFromUse(resolvedTree);
// Lub return types
returnLub = lub(returnLub, resolvedResult.executableType.getReturnType().getAnnotations(), factory);
// Check for static methods whose receiver is null
if (resolvedResult.executableType.getReceiverType() == null) {
// If the method is static the first argument to Method.invoke isn't used, so assume top.
receiverGlb = glb(receiverGlb, factory.getQualifierHierarchy().getTopAnnotations(), factory);
} else {
receiverGlb = glb(receiverGlb, resolvedResult.executableType.getReceiverType().getAnnotations(), factory);
}
// distinguish the types of different formal parameters.
for (AnnotatedTypeMirror mirror : resolvedResult.executableType.getParameterTypes()) {
paramsGlb = glb(paramsGlb, mirror.getAnnotations(), factory);
}
}
if (returnLub == null) {
// None of the spoofed tree's arguments matched the declared method
return origResult;
}
/*
* Clear all original (return, receiver, parameter type) annotations and
* set lub/glb annotations from resolved method(s)
*/
// return value
origResult.executableType.getReturnType().clearPrimaryAnnotations();
origResult.executableType.getReturnType().addAnnotations(returnLub);
// receiver type
origResult.executableType.getParameterTypes().get(0).clearPrimaryAnnotations();
origResult.executableType.getParameterTypes().get(0).addAnnotations(receiverGlb);
// parameter types
if (paramsGlb != null) {
AnnotatedArrayType origArrayType = (AnnotatedArrayType) origResult.executableType.getParameterTypes().get(1);
origArrayType.getComponentType().clearPrimaryAnnotations();
origArrayType.getComponentType().addAnnotations(paramsGlb);
}
debugReflection("Resolved annotations: " + origResult.executableType);
return origResult;
}
Aggregations