Search in sources :

Example 1 with Heuristics

use of org.checkerframework.framework.util.Heuristics in project checker-framework by typetools.

the class InterningVisitor method suppressInsideComparison.

/**
 * Pattern matches particular comparisons to avoid common false positives in the {@link
 * Comparable#compareTo(Object)} and {@link Object#equals(Object)}.
 *
 * <p>Specifically, this method tests if: the comparison is a == comparison, it is the test of an
 * if statement that's the first statement in the method, and one of the following is true:
 *
 * <ol>
 *   <li>the method overrides {@link Comparator#compare}, the "then" branch of the if statement
 *       returns zero, and the comparison tests equality of the method's two parameters
 *   <li>the method overrides {@link Object#equals(Object)} and the comparison tests "this"
 *       against the method's parameter
 *   <li>the method overrides {@link Comparable#compareTo(Object)}, the "then" branch of the if
 *       statement returns zero, and the comparison tests "this" against the method's parameter
 * </ol>
 *
 * @param node the comparison to check
 * @return true if one of the supported heuristics is matched, false otherwise
 */
// TODO: handle != comparisons too!
// TODO: handle more methods, such as early return from addAll when this == arg
private boolean suppressInsideComparison(final BinaryTree node) {
    // Only handle == binary trees
    if (node.getKind() != Tree.Kind.EQUAL_TO) {
        return false;
    }
    Tree left = node.getLeftOperand();
    Tree right = node.getRightOperand();
    // Only valid if we're comparing identifiers.
    if (!(left.getKind() == Tree.Kind.IDENTIFIER && right.getKind() == Tree.Kind.IDENTIFIER)) {
        return false;
    }
    TreePath path = getCurrentPath();
    TreePath parentPath = path.getParentPath();
    Tree parent = parentPath.getLeaf();
    // statement in the method.
    if (parent.getKind() == Tree.Kind.RETURN) {
        // ensure the return statement is the first statement in the method
        if (parentPath.getParentPath().getParentPath().getLeaf().getKind() != Tree.Kind.METHOD) {
            return false;
        }
    // maybe set some variables??
    } else if (Heuristics.matchParents(getCurrentPath(), Tree.Kind.IF, Tree.Kind.METHOD)) {
        // Ensure the if statement is the first statement in the method
        // Retrieve the enclosing if statement tree and method tree
        Tree ifStatementTree = null;
        MethodTree methodTree = null;
        // Set ifStatementTree and methodTree
        {
            TreePath ppath = parentPath;
            Tree tree;
            while ((tree = ppath.getLeaf()) != null) {
                if (tree.getKind() == Tree.Kind.IF) {
                    ifStatementTree = tree;
                } else if (tree.getKind() == Tree.Kind.METHOD) {
                    methodTree = (MethodTree) tree;
                    break;
                }
                ppath = ppath.getParentPath();
            }
        }
        assert ifStatementTree != null;
        assert methodTree != null;
        StatementTree firstStmnt = methodTree.getBody().getStatements().get(0);
        assert firstStmnt != null;
        // comparing AST nodes
        @SuppressWarnings("interning:not.interned") boolean notSameNode = firstStmnt != ifStatementTree;
        if (notSameNode) {
            // The if statement is not the first statement in the method.
            return false;
        }
    } else {
        return false;
    }
    ExecutableElement enclosingMethod = TreeUtils.elementFromDeclaration(methodTree);
    assert enclosingMethod != null;
    final Element lhs = TreeUtils.elementFromUse((IdentifierTree) left);
    final Element rhs = TreeUtils.elementFromUse((IdentifierTree) right);
    // Matcher to check for if statement that returns zero
    Heuristics.Matcher matcherIfReturnsZero = new Heuristics.Matcher() {

        @Override
        public Boolean visitIf(IfTree tree, Void p) {
            return visit(tree.getThenStatement(), p);
        }

        @Override
        public Boolean visitBlock(BlockTree tree, Void p) {
            if (tree.getStatements().isEmpty()) {
                return false;
            }
            return visit(tree.getStatements().get(0), p);
        }

        @Override
        public Boolean visitReturn(ReturnTree tree, Void p) {
            ExpressionTree expr = tree.getExpression();
            return (expr != null && expr.getKind() == Tree.Kind.INT_LITERAL && ((LiteralTree) expr).getValue().equals(0));
        }
    };
    boolean hasCompareToMethodAnno = atypeFactory.getDeclAnnotation(enclosingMethod, CompareToMethod.class) != null;
    boolean hasEqualsMethodAnno = atypeFactory.getDeclAnnotation(enclosingMethod, EqualsMethod.class) != null;
    int params = enclosingMethod.getParameters().size();
    // "return 0" statement (for the Comparator.compare heuristic).
    if (overrides(enclosingMethod, Comparator.class, "compare") || (hasCompareToMethodAnno && params == 2)) {
        final boolean returnsZero = new Heuristics.Within(new Heuristics.OfKind(Tree.Kind.IF, matcherIfReturnsZero)).match(getCurrentPath());
        if (!returnsZero) {
            return false;
        }
        assert params == 2;
        Element p1 = enclosingMethod.getParameters().get(0);
        Element p2 = enclosingMethod.getParameters().get(1);
        return (p1.equals(lhs) && p2.equals(rhs)) || (p1.equals(rhs) && p2.equals(lhs));
    } else if (overrides(enclosingMethod, Object.class, "equals") || (hasEqualsMethodAnno && params == 1)) {
        assert params == 1;
        Element param = enclosingMethod.getParameters().get(0);
        Element thisElt = getThis(trees.getScope(getCurrentPath()));
        assert thisElt != null;
        return (thisElt.equals(lhs) && param.equals(rhs)) || (thisElt.equals(rhs) && param.equals(lhs));
    } else if (hasEqualsMethodAnno && params == 2) {
        Element p1 = enclosingMethod.getParameters().get(0);
        Element p2 = enclosingMethod.getParameters().get(1);
        return (p1.equals(lhs) && p2.equals(rhs)) || (p1.equals(rhs) && p2.equals(lhs));
    } else if (overrides(enclosingMethod, Comparable.class, "compareTo") || (hasCompareToMethodAnno && params == 1)) {
        final boolean returnsZero = new Heuristics.Within(new Heuristics.OfKind(Tree.Kind.IF, matcherIfReturnsZero)).match(getCurrentPath());
        if (!returnsZero) {
            return false;
        }
        assert params == 1;
        Element param = enclosingMethod.getParameters().get(0);
        Element thisElt = getThis(trees.getScope(getCurrentPath()));
        assert thisElt != null;
        return (thisElt.equals(lhs) && param.equals(rhs)) || (thisElt.equals(rhs) && param.equals(lhs));
    }
    return false;
}
Also used : MethodTree(com.sun.source.tree.MethodTree) Heuristics(org.checkerframework.framework.util.Heuristics) ExecutableElement(javax.lang.model.element.ExecutableElement) TypeElement(javax.lang.model.element.TypeElement) ExecutableElement(javax.lang.model.element.ExecutableElement) Element(javax.lang.model.element.Element) EqualsMethod(org.checkerframework.checker.interning.qual.EqualsMethod) ReturnTree(com.sun.source.tree.ReturnTree) IfTree(com.sun.source.tree.IfTree) Comparator(java.util.Comparator) StatementTree(com.sun.source.tree.StatementTree) TreePath(com.sun.source.util.TreePath) CompareToMethod(org.checkerframework.checker.interning.qual.CompareToMethod) ReturnTree(com.sun.source.tree.ReturnTree) LiteralTree(com.sun.source.tree.LiteralTree) MethodTree(com.sun.source.tree.MethodTree) BinaryTree(com.sun.source.tree.BinaryTree) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) NewClassTree(com.sun.source.tree.NewClassTree) IdentifierTree(com.sun.source.tree.IdentifierTree) Tree(com.sun.source.tree.Tree) ClassTree(com.sun.source.tree.ClassTree) ConditionalExpressionTree(com.sun.source.tree.ConditionalExpressionTree) IfTree(com.sun.source.tree.IfTree) ExpressionTree(com.sun.source.tree.ExpressionTree) MemberSelectTree(com.sun.source.tree.MemberSelectTree) BlockTree(com.sun.source.tree.BlockTree) StatementTree(com.sun.source.tree.StatementTree) BlockTree(com.sun.source.tree.BlockTree) ConditionalExpressionTree(com.sun.source.tree.ConditionalExpressionTree) ExpressionTree(com.sun.source.tree.ExpressionTree)

Aggregations

BinaryTree (com.sun.source.tree.BinaryTree)1 BlockTree (com.sun.source.tree.BlockTree)1 ClassTree (com.sun.source.tree.ClassTree)1 ConditionalExpressionTree (com.sun.source.tree.ConditionalExpressionTree)1 ExpressionTree (com.sun.source.tree.ExpressionTree)1 IdentifierTree (com.sun.source.tree.IdentifierTree)1 IfTree (com.sun.source.tree.IfTree)1 LiteralTree (com.sun.source.tree.LiteralTree)1 MemberSelectTree (com.sun.source.tree.MemberSelectTree)1 MethodInvocationTree (com.sun.source.tree.MethodInvocationTree)1 MethodTree (com.sun.source.tree.MethodTree)1 NewClassTree (com.sun.source.tree.NewClassTree)1 ReturnTree (com.sun.source.tree.ReturnTree)1 StatementTree (com.sun.source.tree.StatementTree)1 Tree (com.sun.source.tree.Tree)1 TreePath (com.sun.source.util.TreePath)1 Comparator (java.util.Comparator)1 Element (javax.lang.model.element.Element)1 ExecutableElement (javax.lang.model.element.ExecutableElement)1 TypeElement (javax.lang.model.element.TypeElement)1