Search in sources :

Example 1 with UIEffect

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);
}
Also used : AnnotationMirror(javax.lang.model.element.AnnotationMirror) PolyUIEffect(org.checkerframework.checker.guieffect.qual.PolyUIEffect) UIEffect(org.checkerframework.checker.guieffect.qual.UIEffect) PolyUIType(org.checkerframework.checker.guieffect.qual.PolyUIType) AnnotatedTypeMirror(org.checkerframework.framework.type.AnnotatedTypeMirror) TypeMirror(javax.lang.model.type.TypeMirror) TypeElement(javax.lang.model.element.TypeElement) SafeEffect(org.checkerframework.checker.guieffect.qual.SafeEffect) PolyUIEffect(org.checkerframework.checker.guieffect.qual.PolyUIEffect) UIEffect(org.checkerframework.checker.guieffect.qual.UIEffect) DeclaredType(javax.lang.model.type.DeclaredType)

Example 2 with UIEffect

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;
}
Also used : PolyUI(org.checkerframework.checker.guieffect.qual.PolyUI) UI(org.checkerframework.checker.guieffect.qual.UI) MemberSelectTree(com.sun.source.tree.MemberSelectTree) SafeEffect(org.checkerframework.checker.guieffect.qual.SafeEffect) LambdaExpressionTree(com.sun.source.tree.LambdaExpressionTree) ExpressionTree(com.sun.source.tree.ExpressionTree) SafeEffect(org.checkerframework.checker.guieffect.qual.SafeEffect) PolyUIEffect(org.checkerframework.checker.guieffect.qual.PolyUIEffect) UIEffect(org.checkerframework.checker.guieffect.qual.UIEffect) AnnotatedTypeMirror(org.checkerframework.framework.type.AnnotatedTypeMirror)

Example 3 with UIEffect

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;
}
Also used : LambdaExpressionTree(com.sun.source.tree.LambdaExpressionTree) AnnotatedExecutableType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType) ExecutableElement(javax.lang.model.element.ExecutableElement) ReturnTree(com.sun.source.tree.ReturnTree) MethodTree(com.sun.source.tree.MethodTree) VariableTree(com.sun.source.tree.VariableTree) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) AssignmentTree(com.sun.source.tree.AssignmentTree) LambdaExpressionTree(com.sun.source.tree.LambdaExpressionTree) Tree(com.sun.source.tree.Tree) ExpressionTree(com.sun.source.tree.ExpressionTree) SafeEffect(org.checkerframework.checker.guieffect.qual.SafeEffect) PolyUIEffect(org.checkerframework.checker.guieffect.qual.PolyUIEffect) UIEffect(org.checkerframework.checker.guieffect.qual.UIEffect) List(java.util.List) AnnotatedTypeMirror(org.checkerframework.framework.type.AnnotatedTypeMirror)

Aggregations

PolyUIEffect (org.checkerframework.checker.guieffect.qual.PolyUIEffect)3 SafeEffect (org.checkerframework.checker.guieffect.qual.SafeEffect)3 UIEffect (org.checkerframework.checker.guieffect.qual.UIEffect)3 AnnotatedTypeMirror (org.checkerframework.framework.type.AnnotatedTypeMirror)3 ExpressionTree (com.sun.source.tree.ExpressionTree)2 LambdaExpressionTree (com.sun.source.tree.LambdaExpressionTree)2 AssignmentTree (com.sun.source.tree.AssignmentTree)1 MemberSelectTree (com.sun.source.tree.MemberSelectTree)1 MethodInvocationTree (com.sun.source.tree.MethodInvocationTree)1 MethodTree (com.sun.source.tree.MethodTree)1 ReturnTree (com.sun.source.tree.ReturnTree)1 Tree (com.sun.source.tree.Tree)1 VariableTree (com.sun.source.tree.VariableTree)1 List (java.util.List)1 AnnotationMirror (javax.lang.model.element.AnnotationMirror)1 ExecutableElement (javax.lang.model.element.ExecutableElement)1 TypeElement (javax.lang.model.element.TypeElement)1 DeclaredType (javax.lang.model.type.DeclaredType)1 TypeMirror (javax.lang.model.type.TypeMirror)1 PolyUI (org.checkerframework.checker.guieffect.qual.PolyUI)1