use of org.checkerframework.checker.guieffect.qual.UIEffect in project checker-framework by typetools.
the class GuiEffectTypeFactory method getDeclaredEffect.
/**
* Calling context annotations
*
* <p>To make anon-inner-classes work, I need to climb the inheritance DAG, until I:
*
* <ul>
* <li>find the class/interface that declares this calling method (an anon inner class is a
* separate class that implements an interface)
* <li>check whether *that* declaration specifies @UI on either the type or method
* </ul>
*
* A method has the UI effect when:
*
* <ol>
* <li>A method is UI if annotated @UIEffect
* <li>A method is UI if the enclosing class is annotated @UI or @UIType and the method is not
* annotated @AlwaysSafe
* <li>A method is UI if the corresponding method in the super-class/interface is UI, and this
* method is not annotated @AlwaysSafe, and this method resides in an anonymous inner
* class (named classes still require a package/class/method annotation to make it UI,
* only anon inner classes have this inheritance-by-default)
* <ul>
* <li>A method must be *annotated* UI if the method it overrides is *annotated* UI
* <li>A method must be *annotated* UI if it overrides a UI method and the enclosing
* class is not UI
* </ul>
* <li>It is an error if a method is UI but the same method in a super-type is not UI
* <li>It is an error if two super-types specify the same method, where one type says it's UI
* and one says it's not (it's possible to simply enforce the weaker (safe) effect, but
* this seems more principled, it's easier --- backwards-compatible --- to change our
* minds about this later)
* </ol>
*/
public Effect getDeclaredEffect(ExecutableElement methodElt) {
if (debugSpew) {
System.err.println("begin mayHaveUIEffect(" + methodElt + ")");
}
AnnotationMirror targetUIP = getDeclAnnotation(methodElt, UIEffect.class);
AnnotationMirror targetSafeP = getDeclAnnotation(methodElt, SafeEffect.class);
AnnotationMirror targetPolyP = getDeclAnnotation(methodElt, PolyUIEffect.class);
TypeElement targetClassElt = (TypeElement) methodElt.getEnclosingElement();
if (debugSpew) {
System.err.println("targetClassElt found");
}
// Short-circuit if the method is explicitly annotated
if (targetSafeP != null) {
if (debugSpew) {
System.err.println("Method marked @SafeEffect");
}
return new Effect(SafeEffect.class);
} else if (targetUIP != null) {
if (debugSpew) {
System.err.println("Method marked @UIEffect");
}
return new Effect(UIEffect.class);
} else if (targetPolyP != null) {
if (debugSpew) {
System.err.println("Method marked @PolyUIEffect");
}
return new Effect(PolyUIEffect.class);
}
if (isUIType(targetClassElt)) {
// Already checked, no explicit @SafeEffect annotation
return new Effect(UIEffect.class);
}
// developer to be explicit.
if (isAnonymousType(targetClassElt)) {
// Refine this for polymorphic parents
boolean canInheritParentEffects = true;
DeclaredType directSuper = (DeclaredType) targetClassElt.getSuperclass();
TypeElement superElt = (TypeElement) directSuper.asElement();
// Anonymous subtypes of polymorphic classes other than object can't inherit
if (getDeclAnnotation(superElt, PolyUIType.class) != null && !TypesUtils.isObject(directSuper)) {
canInheritParentEffects = false;
} else {
for (TypeMirror ifaceM : targetClassElt.getInterfaces()) {
DeclaredType iface = (DeclaredType) ifaceM;
TypeElement ifaceElt = (TypeElement) iface.asElement();
if (getDeclAnnotation(ifaceElt, PolyUIType.class) != null) {
canInheritParentEffects = false;
}
}
}
if (canInheritParentEffects) {
Effect.EffectRange r = findInheritedEffectRange(targetClassElt, methodElt);
return (r != null ? Effect.min(r.min, r.max) : new Effect(SafeEffect.class));
}
}
return new Effect(SafeEffect.class);
}
use of org.checkerframework.checker.guieffect.qual.UIEffect in project checker-framework by typetools.
the class GuiEffectTypeFactory method getComputedEffectAtCallsite.
/**
* Get the effect of a method call at its callsite, acknowledging polymorphic instantiation
* using type use annotations.
*
* @param node The method invocation as an AST node.
* @param callerReceiver The type of the receiver object if available. Used to resolve direct
* calls like "super()"
* @param methodElt The element of the callee method.
* @return The computed effect (SafeEffect or UIEffect) for the method call.
*/
public Effect getComputedEffectAtCallsite(MethodInvocationTree node, AnnotatedTypeMirror.AnnotatedDeclaredType callerReceiver, ExecutableElement methodElt) {
Effect targetEffect = getDeclaredEffect(methodElt);
if (targetEffect.isPoly()) {
AnnotatedTypeMirror srcType = null;
if (node.getMethodSelect().getKind() == Tree.Kind.MEMBER_SELECT) {
ExpressionTree src = ((MemberSelectTree) node.getMethodSelect()).getExpression();
srcType = getAnnotatedType(src);
} else if (node.getMethodSelect().getKind() == Tree.Kind.IDENTIFIER) {
// Tree.Kind.IDENTIFIER, e.g. a direct call like "super()"
if (callerReceiver == null) {
// Not enought information provided to instantiate this type-polymorphic effects
return targetEffect;
}
srcType = callerReceiver;
} else {
ErrorReporter.errorAbort("Unexpected getMethodSelect() kind at callsite " + node);
}
// Instantiate type-polymorphic effects
if (srcType.hasAnnotation(AlwaysSafe.class)) {
targetEffect = new Effect(SafeEffect.class);
} else if (srcType.hasAnnotation(UI.class)) {
targetEffect = new Effect(UIEffect.class);
}
// Poly substitution would be a noop.
}
return targetEffect;
}
use of org.checkerframework.checker.guieffect.qual.UIEffect 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 = TreeUtils.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, visitorState.getMethodReceiver(), 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);
// 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);
// Perform lambda polymorphic effect inference: @PolyUI lambda, calling @UIEffect => @UI lambda
if (targetEffect.isUI() && callerEffect.isPoly()) {
atypeFactory.constrainLambdaToUI((LambdaExpressionTree) callerTree);
callerEffect = new Effect(UIEffect.class);
}
}
assert callerEffect != null;
if (!Effect.LE(targetEffect, callerEffect)) {
checker.report(Result.failure("call.invalid.ui", targetEffect, callerEffect), node);
if (debugSpew) {
System.err.println("Issuing error for node: " + node);
}
}
if (debugSpew) {
System.err.println("Successfully finished main non-recursive checkinv of invocation " + node);
}
Void result = super.visitMethodInvocation(node, p);
// Check arguments to this method invocation for UI-lambdas, this must be re-checked after visiting the lambda
// body due to inference.
List<? extends ExpressionTree> args = node.getArguments();
Pair<AnnotatedExecutableType, List<AnnotatedTypeMirror>> mfuPair = atypeFactory.methodFromUse(node);
AnnotatedExecutableType invokedMethod = mfuPair.first;
List<AnnotatedTypeMirror> argsTypes = AnnotatedTypes.expandVarArgs(atypeFactory, invokedMethod, node.getArguments());
for (int i = 0; i < args.size(); ++i) {
if (args.get(i).getKind() == Tree.Kind.LAMBDA_EXPRESSION) {
lambdaAssignmentCheck(argsTypes.get(i), (LambdaExpressionTree) args.get(i), "argument.type.incompatible");
}
}
return result;
}
Aggregations