Search in sources :

Example 21 with StatementTree

use of com.sun.source.tree.StatementTree 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;
    // parens and blocks), terminate.
    if (!Heuristics.matchParents(getCurrentPath(), Tree.Kind.IF, Tree.Kind.METHOD)) {
        return false;
    }
    // Ensure the if statement is the first statement in the method
    TreePath parentPath = getCurrentPath().getParentPath();
    // Retrieve the enclosing if statement tree and method tree
    Tree tree, ifStatementTree = null;
    MethodTree methodTree = null;
    while ((tree = parentPath.getLeaf()) != null) {
        if (tree.getKind() == Tree.Kind.IF) {
            ifStatementTree = tree;
        } else if (tree.getKind() == Tree.Kind.METHOD) {
            methodTree = (MethodTree) tree;
            break;
        }
        parentPath = parentPath.getParentPath();
    }
    // The call to Heuristics.matchParents already ensured there is an enclosing if statement
    assert ifStatementTree != null;
    // The call to Heuristics.matchParents already ensured there is an enclosing method
    assert methodTree != null;
    StatementTree stmnt = methodTree.getBody().getStatements().get(0);
    // The call to Heuristics.matchParents already ensured the enclosing method has at least one statement (an if statement) in the body
    assert stmnt != null;
    if (stmnt != ifStatementTree) {
        // The if statement is not the first statement in the method.
        return false;
    }
    ExecutableElement enclosing = TreeUtils.elementFromDeclaration(visitorState.getMethodTree());
    assert enclosing != 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().size() > 0) {
                return visit(tree.getStatements().get(0), p);
            }
            return false;
        }

        @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));
        }
    };
    // "return 0" statement (for the Comparator.compare heuristic).
    if (overrides(enclosing, Comparator.class, "compare")) {
        final boolean returnsZero = Heuristics.Matchers.withIn(Heuristics.Matchers.ofKind(Tree.Kind.IF, matcherIfReturnsZero)).match(getCurrentPath());
        if (!returnsZero) {
            return false;
        }
        assert enclosing.getParameters().size() == 2;
        Element p1 = enclosing.getParameters().get(0);
        Element p2 = enclosing.getParameters().get(1);
        return (p1.equals(lhs) && p2.equals(rhs)) || (p2.equals(lhs) && p1.equals(rhs));
    } else if (overrides(enclosing, Object.class, "equals")) {
        assert enclosing.getParameters().size() == 1;
        Element param = enclosing.getParameters().get(0);
        Element thisElt = getThis(trees.getScope(getCurrentPath()));
        assert thisElt != null;
        return (thisElt.equals(lhs) && param.equals(rhs)) || (param.equals(lhs) && thisElt.equals(rhs));
    } else if (overrides(enclosing, Comparable.class, "compareTo")) {
        final boolean returnsZero = Heuristics.Matchers.withIn(Heuristics.Matchers.ofKind(Tree.Kind.IF, matcherIfReturnsZero)).match(getCurrentPath());
        if (!returnsZero) {
            return false;
        }
        assert enclosing.getParameters().size() == 1;
        Element param = enclosing.getParameters().get(0);
        Element thisElt = getThis(trees.getScope(getCurrentPath()));
        assert thisElt != null;
        return (thisElt.equals(lhs) && param.equals(rhs)) || (param.equals(lhs) && thisElt.equals(rhs));
    }
    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) ReturnTree(com.sun.source.tree.ReturnTree) IfTree(com.sun.source.tree.IfTree) StatementTree(com.sun.source.tree.StatementTree) TreePath(com.sun.source.util.TreePath) 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) 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 22 with StatementTree

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

the class StreamResourceLeak method addFix.

@Override
protected void addFix(Description.Builder description, ExpressionTree tree, VisitorState state) {
    TreePath parentPath = state.getPath().getParentPath();
    Tree parent = parentPath.getLeaf();
    SuggestedFix.Builder fix = SuggestedFix.builder();
    String streamType = SuggestedFixes.prettyType(state, fix, ASTHelpers.getReturnType(tree));
    if (parent instanceof MemberSelectTree) {
        StatementTree statement = state.findEnclosing(StatementTree.class);
        if (statement instanceof VariableTree) {
            // Variables need to be declared outside the try-with-resources:
            // e.g. `int count = Files.lines(p).count();`
            // -> `int count; try (Stream<String> stream = Files.lines(p)) { count = stream.count(); }`
            VariableTree var = (VariableTree) statement;
            int pos = ((JCTree) var).getStartPosition();
            int initPos = ((JCTree) var.getInitializer()).getStartPosition();
            int eqPos = pos + state.getSourceForNode(var).substring(0, initPos - pos).lastIndexOf('=');
            fix.replace(eqPos, initPos, String.format(";\ntry (%s stream = %s) {\n%s =", streamType, state.getSourceForNode(tree), var.getName().toString()));
        } else {
            // the non-variable case, e.g. `return Files.lines(p).count()`
            // -> try (Stream<Stream> stream = Files.lines(p)) { return stream.count(); }`
            fix.prefixWith(statement, String.format("try (%s stream = %s) {\n", streamType, state.getSourceForNode(tree)));
        }
        fix.replace(tree, "stream");
        fix.postfixWith(statement, "}");
        description.addFix(fix.build());
    } else if (parent instanceof VariableTree) {
        // If the stream is assigned to a variable, wrap the variable in a try-with-resources
        // that includes all statements in the same block that reference the variable.
        Tree grandParent = parentPath.getParentPath().getLeaf();
        if (!(grandParent instanceof BlockTree)) {
            return;
        }
        List<? extends StatementTree> statements = ((BlockTree) grandParent).getStatements();
        int idx = statements.indexOf(parent);
        int lastUse = idx;
        for (int i = idx + 1; i < statements.size(); i++) {
            boolean[] found = { false };
            statements.get(i).accept(new TreeScanner<Void, Void>() {

                @Override
                public Void visitIdentifier(IdentifierTree tree, Void unused) {
                    if (Objects.equals(ASTHelpers.getSymbol(tree), ASTHelpers.getSymbol(parent))) {
                        found[0] = true;
                    }
                    return null;
                }
            }, null);
            if (found[0]) {
                lastUse = i;
            }
        }
        fix.prefixWith(parent, "try (");
        fix.replace(state.getEndPosition(((VariableTree) parent).getInitializer()), state.getEndPosition(parent), ") {");
        fix.postfixWith(statements.get(lastUse), "}");
        description.addFix(fix.build());
    } else if (parent instanceof EnhancedForLoopTree) {
        // If the stream is used in a loop (e.g. directory streams), wrap the loop in
        // try-with-resources.
        fix.prefixWith(parent, String.format("try (%s stream = %s) {\n", streamType, state.getSourceForNode(tree)));
        fix.replace(tree, "stream");
        fix.postfixWith(parent, "}");
        description.addFix(fix.build());
    } else if (parent instanceof MethodInvocationTree) {
        // If the stream is used in a method that is called in an expression statement, wrap it in
        // try-with-resources.
        Tree grandParent = parentPath.getParentPath().getLeaf();
        if (!(grandParent instanceof ExpressionStatementTree)) {
            return;
        }
        fix.prefixWith(parent, String.format("try (%s stream = %s) {\n", streamType, state.getSourceForNode(tree)));
        fix.replace(tree, "stream");
        fix.postfixWith(grandParent, "}");
        description.addFix(fix.build());
    }
}
Also used : MemberSelectTree(com.sun.source.tree.MemberSelectTree) VariableTree(com.sun.source.tree.VariableTree) JCTree(com.sun.tools.javac.tree.JCTree) IdentifierTree(com.sun.source.tree.IdentifierTree) ExpressionStatementTree(com.sun.source.tree.ExpressionStatementTree) ExpressionStatementTree(com.sun.source.tree.ExpressionStatementTree) StatementTree(com.sun.source.tree.StatementTree) TreePath(com.sun.source.util.TreePath) SuggestedFix(com.google.errorprone.fixes.SuggestedFix) TreeScanner(com.sun.source.util.TreeScanner) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) 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) MemberSelectTree(com.sun.source.tree.MemberSelectTree) JCTree(com.sun.tools.javac.tree.JCTree) ExpressionStatementTree(com.sun.source.tree.ExpressionStatementTree) BlockTree(com.sun.source.tree.BlockTree) StatementTree(com.sun.source.tree.StatementTree) EnhancedForLoopTree(com.sun.source.tree.EnhancedForLoopTree) BlockTree(com.sun.source.tree.BlockTree) List(java.util.List) EnhancedForLoopTree(com.sun.source.tree.EnhancedForLoopTree)

Example 23 with StatementTree

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

the class ModifyCollectionInEnhancedForLoop method blockEndsInBreakOrReturn.

private static boolean blockEndsInBreakOrReturn(VisitorState state) {
    TreePath statementPath = state.findPathToEnclosing(StatementTree.class);
    if (statementPath == null) {
        return false;
    }
    Tree parent = statementPath.getParentPath().getLeaf();
    if (!(parent instanceof BlockTree)) {
        return false;
    }
    StatementTree statement = (StatementTree) statementPath.getLeaf();
    List<? extends StatementTree> statements = ((BlockTree) parent).getStatements();
    int idx = statements.indexOf(statement);
    if (idx == -1 || idx == statements.size()) {
        return false;
    }
    switch(getLast(statements).getKind()) {
        case BREAK:
        case RETURN:
            return true;
        default:
            return false;
    }
}
Also used : StatementTree(com.sun.source.tree.StatementTree) TreePath(com.sun.source.util.TreePath) ExpressionTree(com.sun.source.tree.ExpressionTree) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) BlockTree(com.sun.source.tree.BlockTree) StatementTree(com.sun.source.tree.StatementTree) EnhancedForLoopTree(com.sun.source.tree.EnhancedForLoopTree) Tree(com.sun.source.tree.Tree) BlockTree(com.sun.source.tree.BlockTree)

Example 24 with StatementTree

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

the class FindIdentifiers method findAllIdents.

/**
 * Finds the set of all bare variable identifiers in scope at the current location. Identifiers
 * are ordered by ascending distance/scope count from the current location to match shadowing
 * rules. That is, if two variables with the same simple names appear in the set, the one that
 * appears first in iteration order is the one you get if you use the bare name in the source
 * code.
 *
 * <p>We do not report variables that would require a qualfied access. We also do not handle
 * wildcard imports.
 */
public static LinkedHashSet<VarSymbol> findAllIdents(VisitorState state) {
    ImmutableSet.Builder<VarSymbol> result = new ImmutableSet.Builder<>();
    Tree prev = state.getPath().getLeaf();
    for (Tree curr : state.getPath().getParentPath()) {
        switch(curr.getKind()) {
            case BLOCK:
                for (StatementTree stmt : ((BlockTree) curr).getStatements()) {
                    if (stmt.equals(prev)) {
                        break;
                    }
                    addIfVariable(stmt, result);
                }
                break;
            case METHOD:
                for (VariableTree param : ((MethodTree) curr).getParameters()) {
                    result.add(ASTHelpers.getSymbol(param));
                }
                break;
            case CATCH:
                result.add(ASTHelpers.getSymbol(((CatchTree) curr).getParameter()));
                break;
            case CLASS:
            case INTERFACE:
            case ENUM:
            case ANNOTATION_TYPE:
                // field is referred to by qualified name, but we don't support that.
                for (Tree member : ((ClassTree) curr).getMembers()) {
                    if (member.equals(prev)) {
                        break;
                    }
                    addIfVariable(member, result);
                }
                // Collect inherited fields.
                Type classType = ASTHelpers.getType(curr);
                List<Type> classTypeClosure = state.getTypes().closure(classType);
                List<Type> superTypes = classTypeClosure.size() <= 1 ? Collections.emptyList() : classTypeClosure.subList(1, classTypeClosure.size());
                for (Type type : superTypes) {
                    Scope scope = type.tsym.members();
                    ImmutableList.Builder<VarSymbol> varsList = ImmutableList.builder();
                    for (Symbol var : scope.getSymbols(VarSymbol.class::isInstance)) {
                        varsList.add((VarSymbol) var);
                    }
                    result.addAll(varsList.build().reverse());
                }
                break;
            case FOR_LOOP:
                addAllIfVariable(((ForLoopTree) curr).getInitializer(), result);
                break;
            case ENHANCED_FOR_LOOP:
                result.add(ASTHelpers.getSymbol(((EnhancedForLoopTree) curr).getVariable()));
                break;
            case TRY:
                TryTree tryTree = (TryTree) curr;
                boolean inResources = false;
                for (Tree resource : tryTree.getResources()) {
                    if (resource.equals(prev)) {
                        inResources = true;
                        break;
                    }
                }
                if (inResources) {
                    // Case 1: we're in one of the resource declarations
                    for (Tree resource : tryTree.getResources()) {
                        if (resource.equals(prev)) {
                            break;
                        }
                        addIfVariable(resource, result);
                    }
                } else if (tryTree.getBlock().equals(prev)) {
                    // Case 2: We're in the block (not a catch or finally)
                    addAllIfVariable(tryTree.getResources(), result);
                }
                break;
            case COMPILATION_UNIT:
                for (ImportTree importTree : ((CompilationUnitTree) curr).getImports()) {
                    if (importTree.isStatic() && importTree.getQualifiedIdentifier().getKind() == Kind.MEMBER_SELECT) {
                        MemberSelectTree memberSelectTree = (MemberSelectTree) importTree.getQualifiedIdentifier();
                        Scope scope = state.getTypes().membersClosure(ASTHelpers.getType(memberSelectTree.getExpression()), /* skipInterface= */
                        false);
                        for (Symbol var : scope.getSymbols(sym -> sym instanceof VarSymbol && sym.getSimpleName().equals(memberSelectTree.getIdentifier()))) {
                            result.add((VarSymbol) var);
                        }
                    }
                }
                break;
            default:
                // other node types don't introduce variables
                break;
        }
        prev = curr;
    }
    // TODO(eaftan): switch out collector for ImmutableSet.toImmutableSet()
    return result.build().stream().filter(var -> isVisible(var, state.getPath())).collect(Collectors.toCollection(LinkedHashSet::new));
}
Also used : MethodSymbol(com.sun.tools.javac.code.Symbol.MethodSymbol) ClassType(com.sun.tools.javac.code.Type.ClassType) Modifier(javax.lang.model.element.Modifier) ClassSymbol(com.sun.tools.javac.code.Symbol.ClassSymbol) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) IdentifierTree(com.sun.source.tree.IdentifierTree) CatchTree(com.sun.source.tree.CatchTree) ForLoopTree(com.sun.source.tree.ForLoopTree) Method(java.lang.reflect.Method) TypeSymbol(com.sun.tools.javac.code.Symbol.TypeSymbol) TreePath(com.sun.source.util.TreePath) ImmutableSet(com.google.common.collect.ImmutableSet) Symbol(com.sun.tools.javac.code.Symbol) Set(java.util.Set) MemberSelectTree(com.sun.source.tree.MemberSelectTree) Env(com.sun.tools.javac.comp.Env) CompilationUnitTree(com.sun.source.tree.CompilationUnitTree) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) JCCompilationUnit(com.sun.tools.javac.tree.JCTree.JCCompilationUnit) TreeScanner(com.sun.source.util.TreeScanner) Objects(java.util.Objects) List(java.util.List) WriteableScope(com.sun.tools.javac.code.Scope.WriteableScope) JCMethodDecl(com.sun.tools.javac.tree.JCTree.JCMethodDecl) PackageSymbol(com.sun.tools.javac.code.Symbol.PackageSymbol) BlockTree(com.sun.source.tree.BlockTree) StatementTree(com.sun.source.tree.StatementTree) EnhancedForLoopTree(com.sun.source.tree.EnhancedForLoopTree) Flags(com.sun.tools.javac.code.Flags) Name(com.sun.tools.javac.util.Name) Scope(com.sun.tools.javac.code.Scope) Type(com.sun.tools.javac.code.Type) MethodTree(com.sun.source.tree.MethodTree) MemberEnter(com.sun.tools.javac.comp.MemberEnter) VariableTree(com.sun.source.tree.VariableTree) AttrContext(com.sun.tools.javac.comp.AttrContext) ArrayList(java.util.ArrayList) VisitorState(com.google.errorprone.VisitorState) BiPredicate(java.util.function.BiPredicate) Kind(com.sun.source.tree.Tree.Kind) ImmutableList(com.google.common.collect.ImmutableList) NewClassTree(com.sun.source.tree.NewClassTree) StreamSupport(java.util.stream.StreamSupport) ImportTree(com.sun.source.tree.ImportTree) Tree(com.sun.source.tree.Tree) ClassTree(com.sun.source.tree.ClassTree) LinkedHashSet(java.util.LinkedHashSet) Nullable(javax.annotation.Nullable) VarSymbol(com.sun.tools.javac.code.Symbol.VarSymbol) Enter(com.sun.tools.javac.comp.Enter) ElementKind(javax.lang.model.element.ElementKind) KindSelector(com.sun.tools.javac.code.Kinds.KindSelector) TryTree(com.sun.source.tree.TryTree) Collections(java.util.Collections) Resolve(com.sun.tools.javac.comp.Resolve) CompilationUnitTree(com.sun.source.tree.CompilationUnitTree) MethodTree(com.sun.source.tree.MethodTree) CatchTree(com.sun.source.tree.CatchTree) ImmutableList(com.google.common.collect.ImmutableList) MethodSymbol(com.sun.tools.javac.code.Symbol.MethodSymbol) ClassSymbol(com.sun.tools.javac.code.Symbol.ClassSymbol) TypeSymbol(com.sun.tools.javac.code.Symbol.TypeSymbol) Symbol(com.sun.tools.javac.code.Symbol) PackageSymbol(com.sun.tools.javac.code.Symbol.PackageSymbol) VarSymbol(com.sun.tools.javac.code.Symbol.VarSymbol) MemberSelectTree(com.sun.source.tree.MemberSelectTree) VariableTree(com.sun.source.tree.VariableTree) NewClassTree(com.sun.source.tree.NewClassTree) ClassTree(com.sun.source.tree.ClassTree) VarSymbol(com.sun.tools.javac.code.Symbol.VarSymbol) StatementTree(com.sun.source.tree.StatementTree) ClassType(com.sun.tools.javac.code.Type.ClassType) Type(com.sun.tools.javac.code.Type) ImmutableSet(com.google.common.collect.ImmutableSet) WriteableScope(com.sun.tools.javac.code.Scope.WriteableScope) Scope(com.sun.tools.javac.code.Scope) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) IdentifierTree(com.sun.source.tree.IdentifierTree) CatchTree(com.sun.source.tree.CatchTree) ForLoopTree(com.sun.source.tree.ForLoopTree) MemberSelectTree(com.sun.source.tree.MemberSelectTree) CompilationUnitTree(com.sun.source.tree.CompilationUnitTree) BlockTree(com.sun.source.tree.BlockTree) StatementTree(com.sun.source.tree.StatementTree) EnhancedForLoopTree(com.sun.source.tree.EnhancedForLoopTree) MethodTree(com.sun.source.tree.MethodTree) VariableTree(com.sun.source.tree.VariableTree) NewClassTree(com.sun.source.tree.NewClassTree) ImportTree(com.sun.source.tree.ImportTree) Tree(com.sun.source.tree.Tree) ClassTree(com.sun.source.tree.ClassTree) TryTree(com.sun.source.tree.TryTree) BlockTree(com.sun.source.tree.BlockTree) EnhancedForLoopTree(com.sun.source.tree.EnhancedForLoopTree) TryTree(com.sun.source.tree.TryTree) ImportTree(com.sun.source.tree.ImportTree)

Example 25 with StatementTree

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

the class AbstractExpectedExceptionChecker method buildBaseFix.

protected BaseFix buildBaseFix(VisitorState state, List<Tree> expectations, @Nullable StatementTree failure) {
    String exceptionClass = "Throwable";
    // additional assertions to perform on the captured exception (if any)
    List<String> newAsserts = new ArrayList<>();
    Builder fix = SuggestedFix.builder();
    for (Tree expectation : expectations) {
        MethodInvocationTree invocation = (MethodInvocationTree) ((ExpressionStatementTree) expectation).getExpression();
        MethodSymbol symbol = ASTHelpers.getSymbol(invocation);
        Symtab symtab = state.getSymtab();
        List<? extends ExpressionTree> args = invocation.getArguments();
        switch(symbol.getSimpleName().toString()) {
            case "expect":
                Type type = ASTHelpers.getType(getOnlyElement(invocation.getArguments()));
                if (isSubtype(type, symtab.classType, state)) {
                    // expect(Class<?>)
                    exceptionClass = state.getSourceForNode(getReceiver(getOnlyElement(args)));
                } else if (isSubtype(type, state.getTypeFromString("org.hamcrest.Matcher"), state)) {
                    Type matcherType = state.getTypes().asSuper(type, state.getSymbolFromString("org.hamcrest.Matcher"));
                    if (!matcherType.getTypeArguments().isEmpty()) {
                        Type matchType = getOnlyElement(matcherType.getTypeArguments());
                        if (isSubtype(matchType, symtab.throwableType, state)) {
                            exceptionClass = SuggestedFixes.qualifyType(state, fix, matchType);
                        }
                    }
                    // expect(Matcher)
                    fix.addStaticImport("org.hamcrest.MatcherAssert.assertThat");
                    newAsserts.add(String.format("assertThat(thrown, %s);", state.getSourceForNode(getOnlyElement(args))));
                }
                break;
            case "expectCause":
                ExpressionTree matcher = getOnlyElement(invocation.getArguments());
                if (IS_A.matches(matcher, state)) {
                    fix.addStaticImport("com.google.common.truth.Truth.assertThat");
                    newAsserts.add(String.format("assertThat(thrown).hasCauseThat().isInstanceOf(%s);", state.getSourceForNode(getOnlyElement(((MethodInvocationTree) matcher).getArguments()))));
                } else {
                    fix.addStaticImport("org.hamcrest.MatcherAssert.assertThat");
                    newAsserts.add(String.format("assertThat(thrown.getCause(), %s);", state.getSourceForNode(getOnlyElement(args))));
                }
                break;
            case "expectMessage":
                if (isSubtype(getOnlyElement(symbol.getParameters()).asType(), symtab.stringType, state)) {
                    // expectedMessage(String)
                    fix.addStaticImport("com.google.common.truth.Truth.assertThat");
                    newAsserts.add(String.format("assertThat(thrown).hasMessageThat().contains(%s);", state.getSourceForNode(getOnlyElement(args))));
                } else {
                    // expectedMessage(Matcher)
                    fix.addStaticImport("org.hamcrest.MatcherAssert.assertThat");
                    newAsserts.add(String.format("assertThat(thrown.getMessage(), %s);", state.getSourceForNode(getOnlyElement(args))));
                }
                break;
            default:
                throw new AssertionError("unknown expect method: " + symbol.getSimpleName());
        }
    }
    // remove all interactions with the ExpectedException rule
    fix.replace(((JCTree) expectations.get(0)).getStartPosition(), state.getEndPosition(getLast(expectations)), "");
    if (failure != null) {
        fix.delete(failure);
    }
    return new BaseFix(fix.build(), exceptionClass, newAsserts);
}
Also used : Builder(com.google.errorprone.fixes.SuggestedFix.Builder) ArrayList(java.util.ArrayList) Symtab(com.sun.tools.javac.code.Symtab) Type(com.sun.tools.javac.code.Type) MethodSymbol(com.sun.tools.javac.code.Symbol.MethodSymbol) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) MethodTree(com.sun.source.tree.MethodTree) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) Tree(com.sun.source.tree.Tree) ExpressionTree(com.sun.source.tree.ExpressionTree) JCTree(com.sun.tools.javac.tree.JCTree) ExpressionStatementTree(com.sun.source.tree.ExpressionStatementTree) BlockTree(com.sun.source.tree.BlockTree) StatementTree(com.sun.source.tree.StatementTree) ExpressionTree(com.sun.source.tree.ExpressionTree)

Aggregations

StatementTree (com.sun.source.tree.StatementTree)37 MethodInvocationTree (com.sun.source.tree.MethodInvocationTree)19 ExpressionStatementTree (com.sun.source.tree.ExpressionStatementTree)18 BlockTree (com.sun.source.tree.BlockTree)16 Tree (com.sun.source.tree.Tree)16 ExpressionTree (com.sun.source.tree.ExpressionTree)12 TreePath (com.sun.source.util.TreePath)9 SuggestedFix (com.google.errorprone.fixes.SuggestedFix)8 MethodTree (com.sun.source.tree.MethodTree)8 MemberSelectTree (com.sun.source.tree.MemberSelectTree)7 TryTree (com.sun.source.tree.TryTree)7 VariableTree (com.sun.source.tree.VariableTree)7 CatchTree (com.sun.source.tree.CatchTree)6 JCTree (com.sun.tools.javac.tree.JCTree)6 ClassTree (com.sun.source.tree.ClassTree)5 IdentifierTree (com.sun.source.tree.IdentifierTree)5 MethodSymbol (com.sun.tools.javac.code.Symbol.MethodSymbol)5 VisitorState (com.google.errorprone.VisitorState)4 Description (com.google.errorprone.matchers.Description)4 EnhancedForLoopTree (com.sun.source.tree.EnhancedForLoopTree)4