Search in sources :

Example 1 with UnionTypeTree

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

the class ClassNewInstance method fixExceptions.

// if the match occurrs inside the body of a try statement with existing catch clauses
// update or add a catch block to handle the new exceptions
private boolean fixExceptions(final VisitorState state, SuggestedFix.Builder fix) {
    TryTree tryTree = null;
    OUTER: for (TreePath path = state.getPath(); path != null; path = path.getParentPath()) {
        if (path.getLeaf() instanceof CatchTree) {
            // don't add more catch blocks if newInstance() was called in a catch block
            return false;
        } else if (path.getLeaf() instanceof TryTree && !((TryTree) path.getLeaf()).getCatches().isEmpty()) {
            tryTree = (TryTree) path.getLeaf();
            break;
        }
    }
    if (tryTree == null) {
        return false;
    }
    ImmutableMap.Builder<Type, CatchTree> catches = ImmutableMap.builder();
    for (CatchTree c : tryTree.getCatches()) {
        catches.put(ASTHelpers.getType(c.getParameter().getType()), c);
    }
    UnhandledResult<CatchTree> result = unhandled(catches.build(), state);
    if (result.unhandled.isEmpty()) {
        // no fix needed
        return true;
    }
    {
        // if there's an existing multi-catch at the end that handles reflective exceptions,
        // replace all of them with ROE and leave any non-reflective exceptions.
        // earlier catch blocks are left unchanged.
        CatchTree last = Iterables.getLast(tryTree.getCatches());
        Tree lastType = last.getParameter().getType();
        if (lastType.getKind() == Tree.Kind.UNION_TYPE) {
            Type roe = state.getTypeFromString(ReflectiveOperationException.class.getName());
            Set<String> exceptions = new LinkedHashSet<>();
            boolean foundReflective = false;
            for (Tree alternate : ((UnionTypeTree) lastType).getTypeAlternatives()) {
                if (ASTHelpers.isSubtype(ASTHelpers.getType(alternate), roe, state)) {
                    foundReflective = true;
                    exceptions.add("ReflectiveOperationException");
                } else {
                    exceptions.add(state.getSourceForNode(alternate));
                }
            }
            if (foundReflective) {
                fix.replace(lastType, Joiner.on(" | ").join(exceptions));
                return true;
            }
        }
    }
    // check for duplicated catch blocks that handle reflective exceptions exactly the same way,
    // and merge them into a single block that catches ROE
    Set<String> uniq = new HashSet<>();
    for (CatchTree ct : result.handles.values()) {
        uniq.add(state.getSourceForNode(ct.getBlock()));
    }
    // the catch blocks are all unique, append a new fresh one
    if (uniq.size() != 1) {
        CatchTree last = Iterables.getLast(tryTree.getCatches());
        // borrow the variable name of the previous catch variable, in case the naive 'e' conflicts
        // with something in the current scope
        String name = last.getParameter().getName().toString();
        fix.postfixWith(last, String.format("catch (ReflectiveOperationException %s) {" + " throw new LinkageError(%s.getMessage(), %s); }", name, name, name));
        return true;
    }
    // if the catch blocks contain calls to newInstance, don't delete any of them to avoid
    // overlapping fixes
    final AtomicBoolean newInstanceInCatch = new AtomicBoolean(false);
    ((JCTree) result.handles.values().iterator().next()).accept(new TreeScanner() {

        @Override
        public void visitApply(JCTree.JCMethodInvocation tree) {
            if (NEW_INSTANCE.matches(tree, state)) {
                newInstanceInCatch.set(true);
            }
        }
    });
    if (newInstanceInCatch.get()) {
        fix.replace(Iterables.getLast(result.handles.values()).getParameter().getType(), "ReflectiveOperationException");
        return true;
    }
    // otherwise, merge the duplicated catch blocks into a single block that
    // handles ROE
    boolean first = true;
    for (CatchTree ct : result.handles.values()) {
        if (first) {
            fix.replace(ct.getParameter().getType(), "ReflectiveOperationException");
            first = false;
        } else {
            fix.delete(ct);
        }
    }
    return true;
}
Also used : HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) CatchTree(com.sun.source.tree.CatchTree) JCTree(com.sun.tools.javac.tree.JCTree) ImmutableMap(com.google.common.collect.ImmutableMap) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Type(com.sun.tools.javac.code.Type) TreePath(com.sun.source.util.TreePath) TreeScanner(com.sun.tools.javac.tree.TreeScanner) MethodTree(com.sun.source.tree.MethodTree) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) CatchTree(com.sun.source.tree.CatchTree) Tree(com.sun.source.tree.Tree) ExpressionTree(com.sun.source.tree.ExpressionTree) JCTree(com.sun.tools.javac.tree.JCTree) TryTree(com.sun.source.tree.TryTree) UnionTypeTree(com.sun.source.tree.UnionTypeTree) TryTree(com.sun.source.tree.TryTree) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet)

Aggregations

ImmutableMap (com.google.common.collect.ImmutableMap)1 ImmutableSet (com.google.common.collect.ImmutableSet)1 CatchTree (com.sun.source.tree.CatchTree)1 ExpressionTree (com.sun.source.tree.ExpressionTree)1 MethodInvocationTree (com.sun.source.tree.MethodInvocationTree)1 MethodTree (com.sun.source.tree.MethodTree)1 Tree (com.sun.source.tree.Tree)1 TryTree (com.sun.source.tree.TryTree)1 UnionTypeTree (com.sun.source.tree.UnionTypeTree)1 TreePath (com.sun.source.util.TreePath)1 Type (com.sun.tools.javac.code.Type)1 JCTree (com.sun.tools.javac.tree.JCTree)1 TreeScanner (com.sun.tools.javac.tree.TreeScanner)1 HashSet (java.util.HashSet)1 LinkedHashSet (java.util.LinkedHashSet)1 Set (java.util.Set)1 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)1