Search in sources :

Example 16 with BinaryTree

use of com.sun.source.tree.BinaryTree 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)

Example 17 with BinaryTree

use of com.sun.source.tree.BinaryTree in project checker-framework by typetools.

the class InterningVisitor method suppressEarlyCompareTo.

/**
 * Pattern matches to prevent false positives of the form {@code (a == b || a.compareTo(b) == 0)}.
 * Returns true iff the given node fits this pattern.
 *
 * @return true iff the node fits the pattern (a == b || a.compareTo(b) == 0)
 */
private boolean suppressEarlyCompareTo(final BinaryTree node) {
    // Only handle == binary trees
    if (node.getKind() != Tree.Kind.EQUAL_TO) {
        return false;
    }
    Tree left = TreeUtils.withoutParens(node.getLeftOperand());
    Tree right = TreeUtils.withoutParens(node.getRightOperand());
    // Only valid if we're comparing identifiers.
    if (!(left.getKind() == Tree.Kind.IDENTIFIER && right.getKind() == Tree.Kind.IDENTIFIER)) {
        return false;
    }
    final Element lhs = TreeUtils.elementFromUse((IdentifierTree) left);
    final Element rhs = TreeUtils.elementFromUse((IdentifierTree) right);
    // looking for ((a == b || a.compareTo(b) == 0)
    Heuristics.Matcher matcherEqOrCompareTo = new Heuristics.Matcher() {

        @Override
        public Boolean visitBinary(BinaryTree tree, Void p) {
            if (tree.getKind() == Tree.Kind.EQUAL_TO) {
                // a.compareTo(b) == 0
                // looking for a.compareTo(b) or
                ExpressionTree leftTree = tree.getLeftOperand();
                // b.compareTo(a)
                // looking for 0
                ExpressionTree rightTree = tree.getRightOperand();
                if (rightTree.getKind() != Tree.Kind.INT_LITERAL) {
                    return false;
                }
                LiteralTree rightLiteral = (LiteralTree) rightTree;
                if (!rightLiteral.getValue().equals(0)) {
                    return false;
                }
                return visit(leftTree, p);
            } else {
                // a == b || a.compareTo(b) == 0
                @// AST node comparisons
                SuppressWarnings(// AST node comparisons
                "interning:assignment") @InternedDistinct ExpressionTree // looking for a==b
                leftTree = tree.getLeftOperand();
                // looking for a.compareTo(b) == 0
                ExpressionTree rightTree = tree.getRightOperand();
                // or b.compareTo(a) == 0
                if (leftTree != node) {
                    return false;
                }
                if (rightTree.getKind() != Tree.Kind.EQUAL_TO) {
                    return false;
                }
                return visit(rightTree, p);
            }
        }

        @Override
        public Boolean visitMethodInvocation(MethodInvocationTree tree, Void p) {
            if (!TreeUtils.isMethodInvocation(tree, comparableCompareTo, checker.getProcessingEnvironment())) {
                return false;
            }
            List<? extends ExpressionTree> args = tree.getArguments();
            if (args.size() != 1) {
                return false;
            }
            ExpressionTree arg = args.get(0);
            if (arg.getKind() != Tree.Kind.IDENTIFIER) {
                return false;
            }
            Element argElt = TreeUtils.elementFromUse(arg);
            ExpressionTree exp = tree.getMethodSelect();
            if (exp.getKind() != Tree.Kind.MEMBER_SELECT) {
                return false;
            }
            MemberSelectTree member = (MemberSelectTree) exp;
            if (member.getExpression().getKind() != Tree.Kind.IDENTIFIER) {
                return false;
            }
            Element refElt = TreeUtils.elementFromUse(member.getExpression());
            if (!((refElt.equals(lhs) && argElt.equals(rhs)) || (refElt.equals(rhs) && argElt.equals(lhs)))) {
                return false;
            }
            return true;
        }
    };
    boolean okay = new Heuristics.Within(new Heuristics.OfKind(Tree.Kind.CONDITIONAL_OR, matcherEqOrCompareTo)).match(getCurrentPath());
    return okay;
}
Also used : Heuristics(org.checkerframework.framework.util.Heuristics) TypeElement(javax.lang.model.element.TypeElement) ExecutableElement(javax.lang.model.element.ExecutableElement) Element(javax.lang.model.element.Element) MemberSelectTree(com.sun.source.tree.MemberSelectTree) BinaryTree(com.sun.source.tree.BinaryTree) LiteralTree(com.sun.source.tree.LiteralTree) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) InternedDistinct(org.checkerframework.checker.interning.qual.InternedDistinct) 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) ConditionalExpressionTree(com.sun.source.tree.ConditionalExpressionTree) ExpressionTree(com.sun.source.tree.ExpressionTree)

Example 18 with BinaryTree

use of com.sun.source.tree.BinaryTree in project error-prone by google.

the class ShortCircuitBoolean method matchBinary.

@Override
public Description matchBinary(BinaryTree tree, VisitorState state) {
    switch(tree.getKind()) {
        case AND:
        case OR:
            break;
        default:
            return NO_MATCH;
    }
    if (!isSameType(getType(tree), state.getSymtab().booleanType, state)) {
        return NO_MATCH;
    }
    Iterator<Tree> stateIterator = state.getPath().getParentPath().iterator();
    Tree parent = stateIterator.next();
    if (parent instanceof BinaryTree && (parent.getKind() == Kind.AND || parent.getKind() == Kind.OR)) {
        return NO_MATCH;
    } else {
        SuggestedFix.Builder fix = SuggestedFix.builder();
        new TreeScannerBinary(state).scan(tree, fix);
        return describeMatch(tree, fix.build());
    }
}
Also used : SuggestedFix(com.google.errorprone.fixes.SuggestedFix) BinaryTree(com.sun.source.tree.BinaryTree) BinaryTree(com.sun.source.tree.BinaryTree) JCTree(com.sun.tools.javac.tree.JCTree) Tree(com.sun.source.tree.Tree)

Example 19 with BinaryTree

use of com.sun.source.tree.BinaryTree in project error-prone by google.

the class PreconditionsCheckNotNullPrimitive method describe.

/**
 * If the call to Preconditions.checkNotNull is part of an expression (assignment, return, etc.),
 * we substitute the argument for the method call. E.g.: {@code bar =
 * Preconditions.checkNotNull(foo); ==> bar = foo;}
 *
 * <p>If the argument to Preconditions.checkNotNull is a comparison using == or != and one of the
 * operands is null, we call checkNotNull on the non-null operand. E.g.: {@code checkNotNull(a ==
 * null); ==> checkNotNull(a);}
 *
 * <p>If the argument is a method call or binary tree and its return type is boolean, change it to
 * a checkArgument/checkState. E.g.: {@code Preconditions.checkNotNull(foo.hasFoo()) ==>
 * Preconditions.checkArgument(foo.hasFoo())}
 *
 * <p>Otherwise, delete the checkNotNull call. E.g.: {@code Preconditions.checkNotNull(foo); ==>
 * [delete the line]}
 */
public Description describe(MethodInvocationTree methodInvocationTree, VisitorState state) {
    ExpressionTree arg1 = methodInvocationTree.getArguments().get(0);
    Tree parent = state.getPath().getParentPath().getLeaf();
    // Assignment, return, etc.
    if (parent.getKind() != Kind.EXPRESSION_STATEMENT) {
        return describeMatch(arg1, SuggestedFix.replace(methodInvocationTree, arg1.toString()));
    }
    // Comparison to null
    if (arg1.getKind() == Kind.EQUAL_TO || arg1.getKind() == Kind.NOT_EQUAL_TO) {
        BinaryTree binaryExpr = (BinaryTree) arg1;
        if (binaryExpr.getLeftOperand().getKind() == Kind.NULL_LITERAL) {
            return describeMatch(arg1, SuggestedFix.replace(arg1, binaryExpr.getRightOperand().toString()));
        }
        if (binaryExpr.getRightOperand().getKind() == Kind.NULL_LITERAL) {
            return describeMatch(arg1, SuggestedFix.replace(arg1, binaryExpr.getLeftOperand().toString()));
        }
    }
    if ((arg1 instanceof BinaryTree || arg1.getKind() == Kind.METHOD_INVOCATION || arg1.getKind() == Kind.LOGICAL_COMPLEMENT) && ((JCExpression) arg1).type == state.getSymtab().booleanType) {
        return describeMatch(arg1, createCheckArgumentOrStateCall(methodInvocationTree, state, arg1));
    }
    return describeMatch(arg1, SuggestedFix.delete(parent));
}
Also used : JCExpression(com.sun.tools.javac.tree.JCTree.JCExpression) BinaryTree(com.sun.source.tree.BinaryTree) ExpressionTree(com.sun.source.tree.ExpressionTree) MethodTree(com.sun.source.tree.MethodTree) BinaryTree(com.sun.source.tree.BinaryTree) VariableTree(com.sun.source.tree.VariableTree) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) IdentifierTree(com.sun.source.tree.IdentifierTree) Tree(com.sun.source.tree.Tree) ExpressionTree(com.sun.source.tree.ExpressionTree)

Example 20 with BinaryTree

use of com.sun.source.tree.BinaryTree in project error-prone by google.

the class AbstractReferenceEquality method addFixes.

protected void addFixes(Description.Builder builder, BinaryTree tree, VisitorState state) {
    ExpressionTree lhs = tree.getLeftOperand();
    ExpressionTree rhs = tree.getRightOperand();
    Optional<Fix> fixToReplaceOrStatement = inOrStatementWithEqualsCheck(state, tree);
    if (fixToReplaceOrStatement.isPresent()) {
        builder.addFix(fixToReplaceOrStatement.get());
        return;
    }
    // Swap the order (e.g. rhs.equals(lhs) if the rhs is a non-null constant, and the lhs is not
    if (ASTHelpers.constValue(lhs) == null && ASTHelpers.constValue(rhs) != null) {
        ExpressionTree tmp = lhs;
        lhs = rhs;
        rhs = tmp;
    }
    String prefix = tree.getKind() == Kind.NOT_EQUAL_TO ? "!" : "";
    String lhsSource = state.getSourceForNode(lhs);
    String rhsSource = state.getSourceForNode(rhs);
    Nullness nullness = getNullness(lhs, state);
    // If the lhs is possibly-null, provide both options.
    if (nullness != NONNULL) {
        if (state.isAndroidCompatible()) {
            builder.addFix(SuggestedFix.builder().replace(tree, String.format("%sObjects.equal(%s, %s)", prefix, lhsSource, rhsSource)).addImport("com.google.common.base.Objects").build());
        } else {
            builder.addFix(SuggestedFix.builder().replace(tree, String.format("%sObjects.equals(%s, %s)", prefix, lhsSource, rhsSource)).addImport("java.util.Objects").build());
        }
    }
    if (nullness != NULL) {
        builder.addFix(SuggestedFix.replace(tree, String.format("%s%s.equals(%s)", prefix, lhs instanceof BinaryTree ? String.format("(%s)", lhsSource) : lhsSource, rhsSource)));
    }
}
Also used : SuggestedFix(com.google.errorprone.fixes.SuggestedFix) Fix(com.google.errorprone.fixes.Fix) BinaryTree(com.sun.source.tree.BinaryTree) ExpressionTree(com.sun.source.tree.ExpressionTree) Nullness(com.google.errorprone.dataflow.nullnesspropagation.Nullness)

Aggregations

BinaryTree (com.sun.source.tree.BinaryTree)30 ExpressionTree (com.sun.source.tree.ExpressionTree)26 Tree (com.sun.source.tree.Tree)17 LiteralTree (com.sun.source.tree.LiteralTree)15 ConditionalExpressionTree (com.sun.source.tree.ConditionalExpressionTree)13 IdentifierTree (com.sun.source.tree.IdentifierTree)11 MethodInvocationTree (com.sun.source.tree.MethodInvocationTree)11 MemberSelectTree (com.sun.source.tree.MemberSelectTree)10 ClassTree (com.sun.source.tree.ClassTree)9 CompoundAssignmentTree (com.sun.source.tree.CompoundAssignmentTree)9 TypeCastTree (com.sun.source.tree.TypeCastTree)9 MethodTree (com.sun.source.tree.MethodTree)8 PrimitiveTypeTree (com.sun.source.tree.PrimitiveTypeTree)8 VariableTree (com.sun.source.tree.VariableTree)8 AnnotatedTypeTree (com.sun.source.tree.AnnotatedTypeTree)6 BlockTree (com.sun.source.tree.BlockTree)6 ReturnTree (com.sun.source.tree.ReturnTree)6 StatementTree (com.sun.source.tree.StatementTree)6 TypeMirror (javax.lang.model.type.TypeMirror)6 SuggestedFix (com.google.errorprone.fixes.SuggestedFix)5