Search in sources :

Example 1 with PurityResult

use of org.checkerframework.dataflow.util.PurityChecker.PurityResult in project checker-framework by typetools.

the class BaseTypeVisitor method visitMethod.

/**
 * Performs pseudo-assignment check: checks that the method obeys override and subtype rules to
 * all overridden methods.
 *
 * <p>The override rule specifies that a method, m1, may override a method m2 only if:
 *
 * <ul>
 *   <li>m1 return type is a subtype of m2
 *   <li>m1 receiver type is a supertype of m2
 *   <li>m1 parameters are supertypes of corresponding m2 parameters
 * </ul>
 *
 * Also, it issues a "missing.this" error for static method annotated receivers.
 */
@Override
public Void visitMethod(MethodTree node, Void p) {
    // We copy the result from getAnnotatedType to ensure that
    // circular types (e.g. K extends Comparable<K>) are represented
    // by circular AnnotatedTypeMirrors, which avoids problems with
    // later checks.
    // TODO: Find a cleaner way to ensure circular AnnotatedTypeMirrors.
    AnnotatedExecutableType methodType = atypeFactory.getAnnotatedType(node).deepCopy();
    AnnotatedDeclaredType preMRT = visitorState.getMethodReceiver();
    MethodTree preMT = visitorState.getMethodTree();
    visitorState.setMethodReceiver(methodType.getReceiverType());
    visitorState.setMethodTree(node);
    ExecutableElement methodElement = TreeUtils.elementFromDeclaration(node);
    try {
        if (TreeUtils.isAnonymousConstructor(node)) {
            // We shouldn't dig deeper
            return null;
        }
        // check method purity if needed
        {
            boolean anyPurityAnnotation = PurityUtils.hasPurityAnnotation(atypeFactory, node);
            boolean checkPurityAlways = checker.hasOption("suggestPureMethods");
            boolean checkPurityAnnotations = checker.hasOption("checkPurityAnnotations");
            if (checkPurityAnnotations && (anyPurityAnnotation || checkPurityAlways)) {
                // check "no" purity
                List<Pure.Kind> kinds = PurityUtils.getPurityKinds(atypeFactory, node);
                // @Deterministic makes no sense for a void method or constructor
                boolean isDeterministic = kinds.contains(Pure.Kind.DETERMINISTIC);
                if (isDeterministic) {
                    if (TreeUtils.isConstructor(node)) {
                        checker.report(Result.warning("purity.deterministic.constructor"), node);
                    } else if (TreeUtils.typeOf(node.getReturnType()).getKind() == TypeKind.VOID) {
                        checker.report(Result.warning("purity.deterministic.void.method"), node);
                    }
                }
                // Report errors if necessary.
                PurityResult r = PurityChecker.checkPurity(atypeFactory.getPath(node.getBody()), atypeFactory, checker.hasOption("assumeSideEffectFree"));
                if (!r.isPure(kinds)) {
                    reportPurityErrors(r, node, kinds);
                }
                // as such (if the feature is activated).
                if (checkPurityAlways) {
                    Collection<Pure.Kind> additionalKinds = new HashSet<>(r.getTypes());
                    additionalKinds.removeAll(kinds);
                    if (TreeUtils.isConstructor(node)) {
                        additionalKinds.remove(Pure.Kind.DETERMINISTIC);
                    }
                    if (!additionalKinds.isEmpty()) {
                        if (additionalKinds.size() == 2) {
                            checker.report(Result.warning("purity.more.pure", node.getName()), node);
                        } else if (additionalKinds.contains(Pure.Kind.SIDE_EFFECT_FREE)) {
                            checker.report(Result.warning("purity.more.sideeffectfree", node.getName()), node);
                        } else if (additionalKinds.contains(Pure.Kind.DETERMINISTIC)) {
                            checker.report(Result.warning("purity.more.deterministic", node.getName()), node);
                        } else {
                            assert false : "BaseTypeVisitor reached undesirable state";
                        }
                    }
                }
            }
        }
        // Passing the whole method/constructor validates the return type
        validateTypeOf(node);
        // Validate types in throws clauses
        for (ExpressionTree thr : node.getThrows()) {
            validateTypeOf(thr);
        }
        if (atypeFactory.getDependentTypesHelper() != null) {
            atypeFactory.getDependentTypesHelper().checkMethod(node, methodType);
        }
        AnnotatedDeclaredType enclosingType = (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(methodElement.getEnclosingElement());
        // Find which method this overrides!
        Map<AnnotatedDeclaredType, ExecutableElement> overriddenMethods = AnnotatedTypes.overriddenMethods(elements, atypeFactory, methodElement);
        for (Map.Entry<AnnotatedDeclaredType, ExecutableElement> pair : overriddenMethods.entrySet()) {
            AnnotatedDeclaredType overriddenType = pair.getKey();
            AnnotatedExecutableType overriddenMethod = AnnotatedTypes.asMemberOf(types, atypeFactory, overriddenType, pair.getValue());
            if (!checkOverride(node, enclosingType, overriddenMethod, overriddenType)) {
                // the same method, not adding any value. See Issue 373.
                break;
            }
        }
        return super.visitMethod(node, p);
    } finally {
        boolean abstractMethod = methodElement.getModifiers().contains(Modifier.ABSTRACT) || methodElement.getModifiers().contains(Modifier.NATIVE);
        // check well-formedness of pre/postcondition
        List<String> formalParamNames = new ArrayList<>();
        for (VariableTree param : node.getParameters()) {
            formalParamNames.add(param.getName().toString());
        }
        checkContractsAtMethodDeclaration(node, methodElement, formalParamNames, abstractMethod);
        visitorState.setMethodReceiver(preMRT);
        visitorState.setMethodTree(preMT);
    }
}
Also used : PurityResult(org.checkerframework.dataflow.util.PurityChecker.PurityResult) MethodTree(com.sun.source.tree.MethodTree) ExecutableElement(javax.lang.model.element.ExecutableElement) ArrayList(java.util.ArrayList) VariableTree(com.sun.source.tree.VariableTree) AnnotatedExecutableType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType) AnnotatedDeclaredType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType) Collection(java.util.Collection) LambdaExpressionTree(com.sun.source.tree.LambdaExpressionTree) ConditionalExpressionTree(com.sun.source.tree.ConditionalExpressionTree) ExpressionTree(com.sun.source.tree.ExpressionTree) ArrayList(java.util.ArrayList) List(java.util.List) Map(java.util.Map) HashMap(java.util.HashMap) Pure(org.checkerframework.dataflow.qual.Pure)

Aggregations

ConditionalExpressionTree (com.sun.source.tree.ConditionalExpressionTree)1 ExpressionTree (com.sun.source.tree.ExpressionTree)1 LambdaExpressionTree (com.sun.source.tree.LambdaExpressionTree)1 MethodTree (com.sun.source.tree.MethodTree)1 VariableTree (com.sun.source.tree.VariableTree)1 ArrayList (java.util.ArrayList)1 Collection (java.util.Collection)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Map (java.util.Map)1 ExecutableElement (javax.lang.model.element.ExecutableElement)1 Pure (org.checkerframework.dataflow.qual.Pure)1 PurityResult (org.checkerframework.dataflow.util.PurityChecker.PurityResult)1 AnnotatedDeclaredType (org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType)1 AnnotatedExecutableType (org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType)1