use of com.sun.source.tree.CatchTree in project checker-framework by typetools.
the class CFGTranslationPhaseOne method visitTry.
@Override
public Node visitTry(TryTree tree, Void p) {
List<? extends CatchTree> catches = tree.getCatches();
BlockTree finallyBlock = tree.getFinallyBlock();
extendWithNode(new MarkerNode(tree, "start of try statement #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
List<Pair<TypeMirror, Label>> catchLabels = CollectionsPlume.mapList((CatchTree c) -> {
return Pair.of(TreeUtils.typeOf(c.getParameter().getType()), new Label());
}, catches);
// Store return/break/continue labels, just in case we need them for a finally block.
TryFinallyScopeCell oldReturnTargetL = returnTargetL;
TryFinallyScopeCell oldBreakTargetL = breakTargetL;
Map<Name, Label> oldBreakLabels = breakLabels;
TryFinallyScopeCell oldContinueTargetL = continueTargetL;
Map<Name, Label> oldContinueLabels = continueLabels;
Label finallyLabel = null;
Label exceptionalFinallyLabel = null;
if (finallyBlock != null) {
finallyLabel = new Label();
exceptionalFinallyLabel = new Label();
tryStack.pushFrame(new TryFinallyFrame(exceptionalFinallyLabel));
returnTargetL = new TryFinallyScopeCell();
breakTargetL = new TryFinallyScopeCell();
breakLabels = new TryFinallyScopeMap();
continueTargetL = new TryFinallyScopeCell();
continueLabels = new TryFinallyScopeMap();
}
Label doneLabel = new Label();
tryStack.pushFrame(new TryCatchFrame(types, catchLabels));
// Must scan the resources *after* we push frame to tryStack. Otherwise we can lose catch
// blocks.
// TODO: Should we handle try-with-resources blocks by also generating code for automatically
// closing the resources?
List<? extends Tree> resources = tree.getResources();
for (Tree resource : resources) {
scan(resource, p);
}
extendWithNode(new MarkerNode(tree, "start of try block #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
scan(tree.getBlock(), p);
extendWithNode(new MarkerNode(tree, "end of try block #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
extendWithExtendedNode(new UnconditionalJump(CFGBuilder.firstNonNull(finallyLabel, doneLabel)));
tryStack.popFrame();
int catchIndex = 0;
for (CatchTree c : catches) {
addLabelForNextNode(catchLabels.get(catchIndex).second);
extendWithNode(new MarkerNode(tree, "start of catch block for " + c.getParameter().getType() + " #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
scan(c, p);
extendWithNode(new MarkerNode(tree, "end of catch block for " + c.getParameter().getType() + " #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
catchIndex++;
extendWithExtendedNode(new UnconditionalJump(CFGBuilder.firstNonNull(finallyLabel, doneLabel)));
}
if (finallyLabel != null) {
// Reset values before analyzing the finally block!
tryStack.popFrame();
{
// Scan 'finallyBlock' for only 'finallyLabel' (a successful path)
addLabelForNextNode(finallyLabel);
extendWithNode(new MarkerNode(tree, "start of finally block #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
scan(finallyBlock, p);
extendWithNode(new MarkerNode(tree, "end of finally block #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
extendWithExtendedNode(new UnconditionalJump(doneLabel));
}
if (hasExceptionalPath(exceptionalFinallyLabel)) {
// If an exceptional path exists, scan 'finallyBlock' for 'exceptionalFinallyLabel', and
// scan copied 'finallyBlock' for 'finallyLabel' (a successful path). If there is no
// successful path, it will be removed in later phase.
// TODO: Don't we need a separate finally block for each kind of exception?
addLabelForNextNode(exceptionalFinallyLabel);
extendWithNode(new MarkerNode(tree, "start of finally block for Throwable #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
scan(finallyBlock, p);
NodeWithExceptionsHolder throwing = extendWithNodeWithException(new MarkerNode(tree, "end of finally block for Throwable #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()), throwableType);
throwing.setTerminatesExecution(true);
}
if (returnTargetL.wasAccessed()) {
addLabelForNextNode(returnTargetL.peekLabel());
returnTargetL = oldReturnTargetL;
extendWithNode(new MarkerNode(tree, "start of finally block for return #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
scan(finallyBlock, p);
extendWithNode(new MarkerNode(tree, "end of finally block for return #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
extendWithExtendedNode(new UnconditionalJump(returnTargetL.accessLabel()));
} else {
returnTargetL = oldReturnTargetL;
}
if (breakTargetL.wasAccessed()) {
addLabelForNextNode(breakTargetL.peekLabel());
breakTargetL = oldBreakTargetL;
extendWithNode(new MarkerNode(tree, "start of finally block for break #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
scan(finallyBlock, p);
extendWithNode(new MarkerNode(tree, "end of finally block for break #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
extendWithExtendedNode(new UnconditionalJump(breakTargetL.accessLabel()));
} else {
breakTargetL = oldBreakTargetL;
}
Map<Name, Label> accessedBreakLabels = ((TryFinallyScopeMap) breakLabels).getAccessedNames();
if (!accessedBreakLabels.isEmpty()) {
breakLabels = oldBreakLabels;
for (Map.Entry<Name, Label> access : accessedBreakLabels.entrySet()) {
addLabelForNextNode(access.getValue());
extendWithNode(new MarkerNode(tree, "start of finally block for break label " + access.getKey() + " #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
scan(finallyBlock, p);
extendWithNode(new MarkerNode(tree, "end of finally block for break label " + access.getKey() + " #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
extendWithExtendedNode(new UnconditionalJump(breakLabels.get(access.getKey())));
}
} else {
breakLabels = oldBreakLabels;
}
if (continueTargetL.wasAccessed()) {
addLabelForNextNode(continueTargetL.peekLabel());
continueTargetL = oldContinueTargetL;
extendWithNode(new MarkerNode(tree, "start of finally block for continue #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
scan(finallyBlock, p);
extendWithNode(new MarkerNode(tree, "end of finally block for continue #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
extendWithExtendedNode(new UnconditionalJump(continueTargetL.accessLabel()));
} else {
continueTargetL = oldContinueTargetL;
}
Map<Name, Label> accessedContinueLabels = ((TryFinallyScopeMap) continueLabels).getAccessedNames();
if (!accessedContinueLabels.isEmpty()) {
continueLabels = oldContinueLabels;
for (Map.Entry<Name, Label> access : accessedContinueLabels.entrySet()) {
addLabelForNextNode(access.getValue());
extendWithNode(new MarkerNode(tree, "start of finally block for continue label " + access.getKey() + " #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
scan(finallyBlock, p);
extendWithNode(new MarkerNode(tree, "end of finally block for continue label " + access.getKey() + " #" + TreeUtils.treeUids.get(tree), env.getTypeUtils()));
extendWithExtendedNode(new UnconditionalJump(continueLabels.get(access.getKey())));
}
} else {
continueLabels = oldContinueLabels;
}
}
addLabelForNextNode(doneLabel);
return null;
}
use of com.sun.source.tree.CatchTree 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));
}
use of com.sun.source.tree.CatchTree in project error-prone by google.
the class ThreadJoinLoop method matchMethodInvocation.
@Override
public Description matchMethodInvocation(MethodInvocationTree methodInvocationTree, VisitorState visitorState) {
String threadString;
if (methodInvocationTree.getMethodSelect() instanceof MemberSelectTree) {
threadString = ((MemberSelectTree) methodInvocationTree.getMethodSelect()).getExpression().toString();
} else {
threadString = "this";
}
// with calculating time with declared variables)
if (!methodInvocationTree.getArguments().isEmpty()) {
return Description.NO_MATCH;
}
if (MATCH_THREAD_JOIN.matches(methodInvocationTree, visitorState)) {
TreePath treePath = ASTHelpers.findPathFromEnclosingNodeToTopLevel(visitorState.getPath(), TryTree.class);
if (treePath == null) {
return Description.NO_MATCH;
}
TreePath pathToLoop = ASTHelpers.findPathFromEnclosingNodeToTopLevel(treePath, WhileLoopTree.class);
// checks to make sure that if there is a while loop with only one statement (the try catch
// block)
boolean hasWhileLoopOneStatement = false;
if (pathToLoop != null) {
Tree statements = ((WhileLoopTree) pathToLoop.getLeaf()).getStatement();
if (statements instanceof BlockTree) {
if (((BlockTree) statements).getStatements().size() == 1) {
hasWhileLoopOneStatement = true;
}
}
}
Type interruptedType = visitorState.getSymtab().interruptedExceptionType;
Type exceptionType = visitorState.getSymtab().exceptionType;
TryTree tryTree = (TryTree) treePath.getLeaf();
// scans the try tree block for any other actions so that we do not accidentally delete
// important actions when replacing
TreeScannerMethodInvocations treeScanner = new TreeScannerMethodInvocations();
treeScanner.scan(tryTree.getBlock(), methodInvocationTree.toString());
if (treeScanner.count > 0) {
return Description.NO_MATCH;
}
if (tryTree.getFinallyBlock() != null) {
return Description.NO_MATCH;
}
List<? extends CatchTree> catches = tryTree.getCatches();
for (CatchTree tree : catches) {
Type typeSym = ASTHelpers.getType(tree.getParameter().getType());
if (Objects.equals(interruptedType, typeSym) || Objects.equals(exceptionType, typeSym)) {
List<? extends StatementTree> statementTrees = tree.getBlock().getStatements();
// replaces the while loop with the try block or replaces just the try block
if (statementTrees.isEmpty() || (statementTrees.size() == 1 && statementTrees.get(0).toString().equals(";"))) {
SuggestedFix.Builder builder = SuggestedFix.builder();
builder.replace(hasWhileLoopOneStatement ? pathToLoop.getLeaf() : tryTree, "Uninterruptibles.joinUninterruptibly(" + threadString + ");");
builder.addImport("com.google.common.util.concurrent.Uninterruptibles");
return describeMatch(methodInvocationTree, builder.build());
}
}
}
}
return Description.NO_MATCH;
}
use of com.sun.source.tree.CatchTree in project error-prone by google.
the class ProvidesNull method matchReturn.
/**
* Matches explicit "return null" statements in methods annotated with {@code @Provides} but not
* {@code @Nullable}. Suggests either annotating the method with {@code @Nullable} or throwing a
* {@link RuntimeException} instead.
*/
// TODO(eaftan): Use nullness dataflow analysis when it's ready
@Override
public Description matchReturn(ReturnTree returnTree, VisitorState state) {
ExpressionTree returnExpression = returnTree.getExpression();
if (returnExpression == null || returnExpression.getKind() != Kind.NULL_LITERAL) {
return Description.NO_MATCH;
}
TreePath path = state.getPath();
MethodTree enclosingMethod = null;
while (true) {
if (path == null || path.getLeaf() instanceof LambdaExpressionTree) {
return Description.NO_MATCH;
} else if (path.getLeaf() instanceof MethodTree) {
enclosingMethod = (MethodTree) path.getLeaf();
break;
} else {
path = path.getParentPath();
}
}
MethodSymbol enclosingMethodSym = ASTHelpers.getSymbol(enclosingMethod);
if (enclosingMethodSym == null) {
return Description.NO_MATCH;
}
if (!ASTHelpers.hasAnnotation(enclosingMethodSym, "dagger.Provides", state) || ASTHelpers.hasDirectAnnotationWithSimpleName(enclosingMethodSym, "Nullable")) {
return Description.NO_MATCH;
}
Fix addNullableFix = SuggestedFix.builder().prefixWith(enclosingMethod, "@Nullable\n").addImport("javax.annotation.Nullable").build();
CatchTree enclosingCatch = ASTHelpers.findEnclosingNode(state.getPath(), CatchTree.class);
if (enclosingCatch == null) {
// If not in a catch block, suggest adding @Nullable first, then throwing an exception.
Fix throwRuntimeExceptionFix = SuggestedFix.replace(returnTree, "throw new RuntimeException();");
return buildDescription(returnTree).addFix(addNullableFix).addFix(throwRuntimeExceptionFix).build();
} else {
// If in a catch block, suggest throwing an exception first, then adding @Nullable.
String replacement = String.format("throw new RuntimeException(%s);", enclosingCatch.getParameter().getName());
Fix throwRuntimeExceptionFix = SuggestedFix.replace(returnTree, replacement);
return buildDescription(returnTree).addFix(throwRuntimeExceptionFix).addFix(addNullableFix).build();
}
}
use of com.sun.source.tree.CatchTree in project error-prone by google.
the class CatchFail method deleteFix.
// Extract the argument to a call to assertWithMessage, e.g. in:
// assertWithMessage("message").fail();
Optional<Fix> deleteFix(TryTree tree, ImmutableList<CatchTree> catchBlocks, VisitorState state) {
SuggestedFix.Builder fix = SuggestedFix.builder();
if (tree.getFinallyBlock() != null || catchBlocks.size() < tree.getCatches().size()) {
// If the try statement has a finally region, or other catch blocks, delete only the
// unnecessary blocks.
catchBlocks.stream().forEachOrdered(fix::delete);
} else {
// The try statement has no finally region and all catch blocks are unnecessary. Replace it
// with the try statements, deleting all catches.
List<? extends StatementTree> tryStatements = tree.getBlock().getStatements();
String source = state.getSourceCode().toString();
// Replace the full region to work around a GJF partial formatting bug that prevents it from
// re-indenting unchanged lines. This means that fixes may overlap, but that's (hopefully)
// unlikely.
// TODO(b/24140798): emit more precise replacements if GJF is fixed
fix.replace(tree, source.substring(((JCTree) tryStatements.get(0)).getStartPosition(), state.getEndPosition(Iterables.getLast(tryStatements))));
}
MethodTree enclosing = findEnclosing(state.getPath());
if (enclosing == null) {
// There isn't an enclosing method, possibly because we're in a lambda or initializer block.
return Optional.empty();
}
if (isExpectedExceptionTest(ASTHelpers.getSymbol(enclosing), state)) {
// tests, so don't use that fix for methods annotated with @Test(expected=...).
return Optional.empty();
}
// Fix up the enclosing method's throws declaration to include the new thrown exception types.
Collection<Type> thrownTypes = ASTHelpers.getSymbol(enclosing).getThrownTypes();
Types types = state.getTypes();
// Find all types in the deleted catch blocks that are not already in the throws declaration.
ImmutableList<Type> toThrow = catchBlocks.stream().map(c -> ASTHelpers.getType(c.getParameter())).flatMap(t -> t instanceof UnionClassType ? ImmutableList.copyOf(((UnionClassType) t).getAlternativeTypes()).stream() : Stream.of(t)).filter(t -> thrownTypes.stream().noneMatch(x -> types.isAssignable(t, x))).collect(toImmutableList());
if (!toThrow.isEmpty()) {
if (!TEST_CASE.matches(enclosing, state)) {
// not be a safe local refactoring.
return Optional.empty();
}
String throwsString = toThrow.stream().map(t -> SuggestedFixes.qualifyType(state, fix, t)).distinct().collect(joining(", "));
if (enclosing.getThrows().isEmpty()) {
// Add a new throws declaration.
fix.prefixWith(enclosing.getBody(), "throws " + throwsString);
} else {
// Append to an existing throws declaration.
fix.postfixWith(Iterables.getLast(enclosing.getThrows()), ", " + throwsString);
}
}
return Optional.of(fix.build());
}
Aggregations