Search in sources :

Example 11 with VisitorState

use of com.google.errorprone.VisitorState in project error-prone by google.

the class CompileTimeConstantExpressionMatcherTest method assertCompilerMatchesOnAssignment.

// Helper methods.
private void assertCompilerMatchesOnAssignment(final Map<String, Boolean> expectedMatches, String... lines) {
    final Matcher<ExpressionTree> matcher = new CompileTimeConstantExpressionMatcher();
    final Scanner scanner = new Scanner() {

        @Override
        public Void visitAssignment(AssignmentTree t, VisitorState state) {
            ExpressionTree lhs = t.getVariable();
            if (expectedMatches.containsKey(lhs.toString())) {
                boolean matches = matcher.matches(t.getExpression(), state);
                if (expectedMatches.get(lhs.toString())) {
                    assertTrue("Matcher should match expression" + t.getExpression(), matches);
                } else {
                    assertFalse("Matcher should not match expression" + t.getExpression(), matches);
                }
            }
            return super.visitAssignment(t, state);
        }
    };
    CompilationTestHelper.newInstance(ScannerSupplier.fromScanner(scanner), getClass()).expectResult(Result.OK).addSourceLines("test/CompileTimeConstantExpressionMatcherTestCase.java", lines).doTest();
}
Also used : Scanner(com.google.errorprone.scanner.Scanner) VisitorState(com.google.errorprone.VisitorState) ExpressionTree(com.sun.source.tree.ExpressionTree) AssignmentTree(com.sun.source.tree.AssignmentTree)

Example 12 with VisitorState

use of com.google.errorprone.VisitorState in project error-prone by google.

the class ChainingConstructorIgnoresParameter method evaluateCallers.

private Description evaluateCallers(MethodSymbol symbol) {
    List<VariableTree> paramTypes = paramTypesForMethod.get(symbol);
    if (paramTypes == null) {
        // We haven't seen the declaration yet. We'll evaluate the call when we do.
        return NO_MATCH;
    }
    for (Caller caller : callersToEvaluate.removeAll(symbol)) {
        VisitorState state = caller.state;
        MethodInvocationTree invocation = caller.tree;
        MethodTree callerConstructor = state.findEnclosing(MethodTree.class);
        if (callerConstructor == null) {
            // impossible, at least in compilable code?
            continue;
        }
        Map<String, Type> availableParams = indexTypeByName(callerConstructor.getParameters());
        /*
       * TODO(cpovirk): Better handling of varargs: If the last parameter type is varargs and it is
       * called as varargs (rather than by passing an array), then rewrite the parameter types to
       * (p0, p1, ..., p[n-2], p[n-1] = element type of varargs parameter if an argument is
       * supplied, p[n] = ditto, etc.). For now, we settle for not crashing in the face of a
       * mismatch between the number of parameters declared and the number supplied.
       *
       * (Use MethodSymbol.isVarArgs.)
       */
        for (int i = 0; i < paramTypes.size() && i < invocation.getArguments().size(); i++) {
            VariableTree formalParam = paramTypes.get(i);
            String formalParamName = formalParam.getName().toString();
            Type formalParamType = getType(formalParam.getType());
            Type availableParamType = availableParams.get(formalParamName);
            ExpressionTree actualParam = invocation.getArguments().get(i);
            if (/*
             * The caller has no param of this type. (Or if it did, we couldn't determine the type.
             * Does that ever happen?) If the param doesn't exist, the caller can't be failing to
             * pass it.
             */
            availableParamType == null || /*
             * We couldn't determine the type of the formal parameter. (Does this ever happen?)
             */
            formalParamType == null || /*
             * The caller is passing the expected parameter (or "ImmutableList.copyOf(parameter),"
             * "new File(parameter)," etc.).
             */
            referencesIdentifierWithName(formalParamName, actualParam, state)) {
                continue;
            }
            if (state.getTypes().isAssignable(availableParamType, formalParamType)) {
                reportMatch(invocation, state, actualParam, formalParamName);
            }
        /*
         * If formal parameter is of an incompatible type, the caller might in theory still intend
         * to pass a dervied expression. For example, "Foo(String file)" might intend to call
         * "Foo(File file)" by passing "new File(file)." If this comes up in practice, we could
         * provide the dummy suggested fix "someExpression(formalParamName)." However, my research
         * suggests that this will rarely if ever be what the user wants.
         */
        }
    }
    // All matches are reported through reportMatch calls instead of return values.
    return NO_MATCH;
}
Also used : ASTHelpers.getType(com.google.errorprone.util.ASTHelpers.getType) Type(com.sun.tools.javac.code.Type) VisitorState(com.google.errorprone.VisitorState) MethodTree(com.sun.source.tree.MethodTree) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) VariableTree(com.sun.source.tree.VariableTree) ExpressionTree(com.sun.source.tree.ExpressionTree)

Example 13 with VisitorState

use of com.google.errorprone.VisitorState in project error-prone by google.

the class FunctionalInterfaceMethodChanged method matchMethod.

@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
    ClassTree enclosingClazz = ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class);
    if (tree.getModifiers().getFlags().contains(Modifier.DEFAULT) && IS_FUNCTIONAL_INTERFACE.matches(enclosingClazz, state)) {
        Types types = Types.instance(state.context);
        Set<Symbol> functionalSuperInterfaceSams = enclosingClazz.getImplementsClause().stream().filter(t -> IS_FUNCTIONAL_INTERFACE.matches(t, state)).map(ASTHelpers::getSymbol).map(TypeSymbol.class::cast).map(// TypeSymbol to single abstract method of the type
        types::findDescriptorSymbol).collect(toImmutableSet());
        // We designate an override of a superinterface SAM "behavior preserving" if it just
        // calls the SAM of this interface.
        Symbol thisInterfaceSam = types.findDescriptorSymbol(ASTHelpers.getSymbol(enclosingClazz));
        // relatively crude: doesn't verify that the same args are passed in the same order
        // so it can get false positives for behavior-preservingness (false negatives for the check)
        TreeVisitor<Boolean, VisitorState> behaviorPreserving = new SimpleTreeVisitor<Boolean, VisitorState>(false) {

            @Override
            public Boolean visitMethod(MethodTree node, VisitorState state) {
                return node.getBody() != null && node.getBody().accept(this, state);
            }

            @Override
            public Boolean visitBlock(BlockTree node, VisitorState state) {
                return node.getStatements().size() == 1 && Iterables.getOnlyElement(node.getStatements()).accept(this, state);
            }

            @Override
            public Boolean visitExpressionStatement(ExpressionStatementTree node, VisitorState state) {
                return node.getExpression().accept(this, state);
            }

            @Override
            public Boolean visitReturn(ReturnTree node, VisitorState state) {
                return node.getExpression().accept(this, state);
            }

            @Override
            public Boolean visitMethodInvocation(MethodInvocationTree node, VisitorState state) {
                return ASTHelpers.getSymbol(node) == thisInterfaceSam;
            }
        };
        if (!Collections.disjoint(ASTHelpers.findSuperMethods(ASTHelpers.getSymbol(tree), types), functionalSuperInterfaceSams) && !tree.accept(behaviorPreserving, state)) {
            return describeMatch(tree);
        }
    }
    return Description.NO_MATCH;
}
Also used : Types(com.sun.tools.javac.code.Types) MethodTree(com.sun.source.tree.MethodTree) TypeSymbol(com.sun.tools.javac.code.Symbol.TypeSymbol) Symbol(com.sun.tools.javac.code.Symbol) SimpleTreeVisitor(com.sun.source.util.SimpleTreeVisitor) ClassTree(com.sun.source.tree.ClassTree) ExpressionStatementTree(com.sun.source.tree.ExpressionStatementTree) ReturnTree(com.sun.source.tree.ReturnTree) VisitorState(com.google.errorprone.VisitorState) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) BlockTree(com.sun.source.tree.BlockTree) TypeSymbol(com.sun.tools.javac.code.Symbol.TypeSymbol)

Example 14 with VisitorState

use of com.google.errorprone.VisitorState in project error-prone by google.

the class MissingCasesInEnumSwitch method matchSwitch.

@Override
public Description matchSwitch(SwitchTree tree, VisitorState state) {
    Type switchType = ASTHelpers.getType(tree.getExpression());
    if (switchType.asElement().getKind() != ElementKind.ENUM) {
        return Description.NO_MATCH;
    }
    // default case is present
    if (tree.getCases().stream().anyMatch(c -> c.getExpression() == null)) {
        return Description.NO_MATCH;
    }
    ImmutableSet<String> handled = tree.getCases().stream().map(CaseTree::getExpression).filter(IdentifierTree.class::isInstance).map(e -> ((IdentifierTree) e).getName().toString()).collect(toImmutableSet());
    Set<String> unhandled = Sets.difference(ASTHelpers.enumValues(switchType.asElement()), handled);
    if (unhandled.isEmpty()) {
        return Description.NO_MATCH;
    }
    return buildDescription(tree).setMessage(buildMessage(unhandled)).build();
}
Also used : ImmutableSet(com.google.common.collect.ImmutableSet) ElementKind(javax.lang.model.element.ElementKind) ERROR(com.google.errorprone.BugPattern.SeverityLevel.ERROR) Set(java.util.Set) CaseTree(com.sun.source.tree.CaseTree) SwitchTreeMatcher(com.google.errorprone.bugpatterns.BugChecker.SwitchTreeMatcher) SwitchTree(com.sun.source.tree.SwitchTree) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) VisitorState(com.google.errorprone.VisitorState) Description(com.google.errorprone.matchers.Description) IdentifierTree(com.sun.source.tree.IdentifierTree) ImmutableSet.toImmutableSet(com.google.common.collect.ImmutableSet.toImmutableSet) BugPattern(com.google.errorprone.BugPattern) JDK(com.google.errorprone.BugPattern.Category.JDK) ASTHelpers(com.google.errorprone.util.ASTHelpers) Type(com.sun.tools.javac.code.Type) Type(com.sun.tools.javac.code.Type) IdentifierTree(com.sun.source.tree.IdentifierTree)

Example 15 with VisitorState

use of com.google.errorprone.VisitorState in project error-prone by google.

the class ComparisonContractViolated method matchMethod.

@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
    if (tree.getBody() == null) {
        return Description.NO_MATCH;
    }
    // Test that the match is in a Comparable.compareTo or Comparator.compare method.
    ClassTree declaringClass = ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class);
    if (!COMPARABLE_CLASS_MATCHER.matches(declaringClass, state) && !COMPARATOR_CLASS_MATCHER.matches(declaringClass, state)) {
        return Description.NO_MATCH;
    }
    if (!COMPARABLE_METHOD_MATCHER.matches(tree, state) && !COMPARATOR_METHOD_MATCHER.matches(tree, state)) {
        return Description.NO_MATCH;
    }
    final Set<ComparisonResult> seenResults = EnumSet.noneOf(ComparisonResult.class);
    final TreeVisitor<Void, VisitorState> visitReturnExpression = new SimpleTreeVisitor<Void, VisitorState>() {

        @Override
        protected Void defaultAction(Tree node, VisitorState state) {
            seenResults.add(node.accept(CONSTANT_VISITOR, state));
            return null;
        }

        @Override
        public Void visitConditionalExpression(ConditionalExpressionTree node, VisitorState state) {
            node.getTrueExpression().accept(this, state);
            node.getFalseExpression().accept(this, state);
            return null;
        }
    };
    tree.getBody().accept(new TreeScanner<Void, VisitorState>() {

        @Override
        public Void visitReturn(ReturnTree node, VisitorState state) {
            return node.getExpression().accept(visitReturnExpression, state);
        }
    }, state);
    if (seenResults.isEmpty() || seenResults.contains(ComparisonResult.NONCONSTANT)) {
        return Description.NO_MATCH;
    }
    if (!seenResults.contains(ComparisonResult.ZERO)) {
        if (tree.getBody().getStatements().size() == 1 && tree.getBody().getStatements().get(0).getKind() == Kind.RETURN) {
            ReturnTree returnTree = (ReturnTree) tree.getBody().getStatements().get(0);
            if (returnTree.getExpression().getKind() == Kind.CONDITIONAL_EXPRESSION) {
                ConditionalExpressionTree condTree = (ConditionalExpressionTree) returnTree.getExpression();
                ExpressionTree conditionExpr = condTree.getCondition();
                while (conditionExpr instanceof ParenthesizedTree) {
                    conditionExpr = ((ParenthesizedTree) conditionExpr).getExpression();
                }
                if (!(conditionExpr instanceof BinaryTree)) {
                    return describeMatch(tree);
                }
                ComparisonResult trueConst = condTree.getTrueExpression().accept(CONSTANT_VISITOR, state);
                ComparisonResult falseConst = condTree.getFalseExpression().accept(CONSTANT_VISITOR, state);
                boolean trueFirst;
                if (trueConst == ComparisonResult.NEGATIVE_CONSTANT && falseConst == ComparisonResult.POSITIVE_CONSTANT) {
                    trueFirst = true;
                } else if (trueConst == ComparisonResult.POSITIVE_CONSTANT && falseConst == ComparisonResult.NEGATIVE_CONSTANT) {
                    trueFirst = false;
                } else {
                    return describeMatch(tree);
                }
                switch(conditionExpr.getKind()) {
                    case LESS_THAN:
                    case LESS_THAN_EQUAL:
                        break;
                    case GREATER_THAN:
                    case GREATER_THAN_EQUAL:
                        trueFirst = !trueFirst;
                        break;
                    default:
                        return describeMatch(tree);
                }
                BinaryTree binaryExpr = (BinaryTree) conditionExpr;
                Type ty = ASTHelpers.getType(binaryExpr.getLeftOperand());
                Types types = Types.instance(state.context);
                Symtab symtab = Symtab.instance(state.context);
                ExpressionTree first = trueFirst ? binaryExpr.getLeftOperand() : binaryExpr.getRightOperand();
                ExpressionTree second = trueFirst ? binaryExpr.getRightOperand() : binaryExpr.getLeftOperand();
                String compareType;
                if (types.isSameType(ty, symtab.intType)) {
                    compareType = "Integer";
                } else if (types.isSameType(ty, symtab.longType)) {
                    compareType = "Long";
                } else {
                    return describeMatch(tree);
                }
                return describeMatch(condTree, SuggestedFix.replace(condTree, String.format("%s.compare(%s, %s)", compareType, state.getSourceForNode(first), state.getSourceForNode(second))));
            }
        }
        return describeMatch(tree);
    }
    if (COMPARATOR_METHOD_MATCHER.matches(tree, state) && (seenResults.contains(ComparisonResult.NEGATIVE_CONSTANT) != seenResults.contains(ComparisonResult.POSITIVE_CONSTANT))) {
        // See e.g. com.google.common.collect.Cut.BelowAll.
        return describeMatch(tree);
    } else {
        return Description.NO_MATCH;
    }
}
Also used : Types(com.sun.tools.javac.code.Types) SimpleTreeVisitor(com.sun.source.util.SimpleTreeVisitor) ClassTree(com.sun.source.tree.ClassTree) ParenthesizedTree(com.sun.source.tree.ParenthesizedTree) BinaryTree(com.sun.source.tree.BinaryTree) ConditionalExpressionTree(com.sun.source.tree.ConditionalExpressionTree) ReturnTree(com.sun.source.tree.ReturnTree) Symtab(com.sun.tools.javac.code.Symtab) Type(com.sun.tools.javac.code.Type) VisitorState(com.google.errorprone.VisitorState) ReturnTree(com.sun.source.tree.ReturnTree) LiteralTree(com.sun.source.tree.LiteralTree) MethodTree(com.sun.source.tree.MethodTree) BinaryTree(com.sun.source.tree.BinaryTree) IdentifierTree(com.sun.source.tree.IdentifierTree) Tree(com.sun.source.tree.Tree) ClassTree(com.sun.source.tree.ClassTree) ParenthesizedTree(com.sun.source.tree.ParenthesizedTree) ConditionalExpressionTree(com.sun.source.tree.ConditionalExpressionTree) ExpressionTree(com.sun.source.tree.ExpressionTree) MemberSelectTree(com.sun.source.tree.MemberSelectTree) ConditionalExpressionTree(com.sun.source.tree.ConditionalExpressionTree) ExpressionTree(com.sun.source.tree.ExpressionTree)

Aggregations

VisitorState (com.google.errorprone.VisitorState)29 MethodTree (com.sun.source.tree.MethodTree)13 Tree (com.sun.source.tree.Tree)13 ExpressionTree (com.sun.source.tree.ExpressionTree)12 MethodInvocationTree (com.sun.source.tree.MethodInvocationTree)11 ClassTree (com.sun.source.tree.ClassTree)10 Symbol (com.sun.tools.javac.code.Symbol)9 Type (com.sun.tools.javac.code.Type)9 Description (com.google.errorprone.matchers.Description)8 IdentifierTree (com.sun.source.tree.IdentifierTree)8 VariableTree (com.sun.source.tree.VariableTree)8 JCTree (com.sun.tools.javac.tree.JCTree)8 List (java.util.List)8 BugPattern (com.google.errorprone.BugPattern)7 ASTHelpers (com.google.errorprone.util.ASTHelpers)7 TreePath (com.sun.source.util.TreePath)7 MethodSymbol (com.sun.tools.javac.code.Symbol.MethodSymbol)7 ArrayList (java.util.ArrayList)7 JDK (com.google.errorprone.BugPattern.Category.JDK)6 MemberSelectTree (com.sun.source.tree.MemberSelectTree)6