@Override public Void visitSwitch(SwitchTree node, Void unused) { sync(node); token("switch"); builder.space(); token("("); scan(skipParen(node.getExpression()), null); token(")"); builder.space(); tokenBreakTrailingComment("{", plusTwo); builder.blankLineWanted(BlankLineWanted.NO); builder.open(plusTwo); boolean first = true; for (CaseTree caseTree : node.getCases()) { if (!first) { builder.blankLineWanted(BlankLineWanted.PRESERVE); } scan(caseTree, null); first = false; } builder.close(); builder.forcedBreak(); builder.blankLineWanted(BlankLineWanted.NO); token("}", plusFour); return null; }
private boolean testBlock(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, BlockTree blockTree) { boolean success = true; for (StatementTree st : blockTree.getStatements()) { if (isLegal(st)) { success &= testStatement(writer, sp, text, cut, st); } if (st instanceof IfTree) { IfTree ifTree = (IfTree) st; success &= testBranch(writer, sp, text, cut, ifTree.getThenStatement()); success &= testBranch(writer, sp, text, cut, ifTree.getElseStatement()); } else if (st instanceof WhileLoopTree) { WhileLoopTree whileLoopTree = (WhileLoopTree) st; success &= testBranch(writer, sp, text, cut, whileLoopTree.getStatement()); } else if (st instanceof DoWhileLoopTree) { DoWhileLoopTree doWhileLoopTree = (DoWhileLoopTree) st; success &= testBranch(writer, sp, text, cut, doWhileLoopTree.getStatement()); } else if (st instanceof ForLoopTree) { ForLoopTree forLoopTree = (ForLoopTree) st; success &= testBranch(writer, sp, text, cut, forLoopTree.getStatement()); } else if (st instanceof LabeledStatementTree) { LabeledStatementTree labelTree = (LabeledStatementTree) st; success &= testBranch(writer, sp, text, cut, labelTree.getStatement()); } else if (st instanceof SwitchTree) { SwitchTree switchTree = (SwitchTree) st; for (CaseTree caseTree : switchTree.getCases()) { for (StatementTree statementTree : caseTree.getStatements()) { success &= testBranch(writer, sp, text, cut, statementTree); } } } } return success; }
@Override public Result visitSwitch(SwitchTree node, BreakContext cxt) { Result result = null; boolean seenDefault = false; cxt.loopDepth++; try { for (CaseTree caseTree : node.getCases()) { if (caseTree.getExpression() == null) { seenDefault = true; } if (result == null) { result = caseTree.accept(this, cxt); } else { result = result.or(caseTree.accept(this, cxt)); } } if (!seenDefault) { result = result.or(NEVER_EXITS); } return result; } finally { cxt.loopDepth--; } }
@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(); }
@Override public Void visitSwitch(SwitchTree node, Void p) { ExpressionTree expr = node.getExpression(); AnnotatedTypeMirror exprType = atypeFactory.getAnnotatedType(expr); for (CaseTree caseExpr : node.getCases()) { ExpressionTree realCaseExpr = caseExpr.getExpression(); if (realCaseExpr != null) { AnnotatedTypeMirror caseType = atypeFactory.getAnnotatedType(realCaseExpr); this.commonAssignmentCheck(exprType, caseType, caseExpr, "switch.type.incompatible", false); } } return super.visitSwitch(node, p); }
@Override public Tree visitSwitch(SwitchTree tree, Void p) { SwitchTree n = make.Switch(tree.getExpression(), tree.getCases()); model.setType(n, model.getType(tree)); comments.copyComments(tree, n); model.setPos(n, model.getPos(tree)); return n; }
public Boolean visitSwitch(SwitchTree node, TreePath p) { if (p == null) { super.visitSwitch(node, p); return false; } SwitchTree st = (SwitchTree) p.getLeaf(); if (!scan(node.getExpression(), st.getExpression(), p)) { return false; } return checkLists(node.getCases(), st.getCases(), p); }
@Override public Void visitSwitch(SwitchTree node, EnumSet<UseTypes> p) { scan(node.getExpression(), EnumSet.of(UseTypes.READ)); for (CaseTree ct : node.getCases()) { scan(ct, null); } return null; }
@Override public Number visitSwitch(SwitchTree node, Void p) { List<? extends CaseTree> cases = (List<? extends CaseTree>) resolveMultiParameters(node.getCases()); SwitchTree nue = make.Switch(node.getExpression(), cases); rewrite(node, nue); return super.visitSwitch(node, p); }
@Override public Object visitSwitch(SwitchTree node, Object p) { depth++; Object o = super.visitSwitch(node, p); depth--; return o; }
@Override public Void visitSwitch(SwitchTree tree, List<Node> d) { List<Node> below = new ArrayList<Node>(); addCorrespondingType(below); addCorrespondingComments(below); super.visitSwitch(tree, below); d.add(new TreeNode(info, getCurrentPath(), below)); return null; }
@Override public Boolean visitSwitch(SwitchTree node, Void p) { boolean lastCaseExit = false; boolean defaultSeen = false; Set<Element> enumValues = null; if (node.getExpression() != null) { TypeMirror exprType = info.getTrees().getTypeMirror(new TreePath(getCurrentPath(), node.getExpression())); if (isValidType(exprType) && exprType.getKind() == TypeKind.DECLARED) { Element el = ((DeclaredType)exprType).asElement(); enumValues = new HashSet<>(); for (Element f : el.getEnclosedElements()) { if (f.getKind() == ElementKind.ENUM_CONSTANT) { enumValues.add(f); } } } } for (CaseTree ct : node.getCases()) { Boolean res = scan(ct, null); if (res == Boolean.FALSE) { return res; } lastCaseExit = res == Boolean.TRUE; if (ct.getExpression() == null) { defaultSeen = true; } else if (enumValues != null ) { TreePath casePath = new TreePath(getCurrentPath(), ct); Element v = info.getTrees().getElement(new TreePath( casePath, ct.getExpression())); if (v != null) { enumValues.remove(v); } } } if (enumValues != null && enumValues.isEmpty()) { defaultSeen = true; } return lastCaseExit == Boolean.TRUE && defaultSeen; }
@Override public List<? extends TypeMirror> visitSwitch(SwitchTree node, Object p) { if (theExpression == null) { initExpression(node.getExpression()); } for (CaseTree cs : node.getCases()) { if (cs.getExpression() != null) { TreePath casePath = new TreePath(getCurrentPath(), cs); TypeMirror caseType = info.getTrees().getTypeMirror(new TreePath(casePath, cs.getExpression())); return Collections.singletonList(caseType); } } // cannot determine return null; }
@Override public List<Tree> visitSwitch(SwitchTree node, ExpressionScanner.ExpressionsInfo p) { List<Tree> result = null; if (acceptsTree(node)) { result = scan(node.getExpression(), p); } return reduce(result, scan(node.getCases(), p)); }
@Override public Void visitSwitch(SwitchTree tree, VisitorState visitorState) { VisitorState state = visitorState.withPath(getCurrentPath()); for (SwitchTreeMatcher matcher : switchMatchers) { if (!isSuppressed(matcher, state)) { try { reportMatch(matcher.matchSwitch(tree, state), tree, state); } catch (Throwable t) { handleError(matcher, t); } } } return super.visitSwitch(tree, state); }
@Override public Choice<State<JCSwitch>> visitSwitch(final SwitchTree node, State<?> state) { return chooseSubtrees( state, s -> unifyExpression(node.getExpression(), s), s -> unify(node.getCases(), s), (expr, cases) -> maker().Switch(expr, List.convert(JCCase.class, cases))); }
/** * The target of a jump statement (break or continue) is (1) the enclosing loop if the jump is * unlabeled (2) the enclosing LabeledStatementTree with matching label if the jump is labeled * (3) the enclosing switch statement if the jump is a break * * <p>If the target of a break or continue statement is encountered before reaching a finally * block, return NO_MATCH. */ @Override protected MatchResult matchAncestor(Tree leaf, Tree prevTree) { // (1) if (label == null) { switch (leaf.getKind()) { case WHILE_LOOP: case DO_WHILE_LOOP: case FOR_LOOP: case ENHANCED_FOR_LOOP: return MatchResult.NO_MATCH; default: break; } } // (2) if (label != null && leaf instanceof LabeledStatementTree && label.equals(((LabeledStatementTree) leaf).getLabel())) { return MatchResult.NO_MATCH; } // (3) if (jumpType == JumpType.BREAK && leaf instanceof SwitchTree) { return MatchResult.NO_MATCH; } return super.matchAncestor(leaf, prevTree); }
@Override public Description matchSwitch(SwitchTree tree, VisitorState state) { if (tree.getCases().size() != 2 || tree.getCases().get(0).getStatements().isEmpty()) { return NO_MATCH; } return Reachability.canCompleteNormally( Iterables.getLast(tree.getCases().get(0).getStatements())) ? describeMatch(tree.getCases().get(1)) : NO_MATCH; }
/** * The target of a jump statement (break or continue) is * (1) the enclosing loop if the jump is unlabeled * (2) the enclosing LabeledStatementTree with matching label if the jump is labeled * (3) the enclosing switch statement if the jump is a break * * If the target of a break or continue statement is encountered before reaching a finally * block, return NO_MATCH. */ @Override protected MatchResult matchAncestor(Tree leaf, Tree prevTree) { // (1) if (label == null) { switch (leaf.getKind()) { case WHILE_LOOP: case DO_WHILE_LOOP: case FOR_LOOP: case ENHANCED_FOR_LOOP: return MatchResult.NO_MATCH; default: break; } } // (2) if (label != null && leaf instanceof LabeledStatementTree && label.equals(((LabeledStatementTree) leaf).getLabel())) { return MatchResult.NO_MATCH; } // (3) if (jumpType == JumpType.BREAK && leaf instanceof SwitchTree) { return MatchResult.NO_MATCH; } return super.matchAncestor(leaf, prevTree); }
@Override public Void visitSwitch(SwitchTree expected, Tree actual) { Optional<SwitchTree> other = checkTypeAndCast(expected, actual); if (!other.isPresent()) { addTypeMismatch(expected, actual); return null; } scan(expected.getExpression(), other.get().getExpression()); parallelScan(expected.getCases(), other.get().getCases()); return null; }
public void testAddCase1() throws Exception { testFile = new File(getWorkDir(), "Test.java"); String test = "public class Test {\n" + " void m(int p) {\n" + " switch (p) {\n" + " case 0:\n" + " System.err.println(1);\n" + " break;\n" + " ca|se 2:\n" + " System.err.println(2);\n" + " break;\n" + " }\n" + " }\n" + "}\n"; String golden = "public class Test {\n" + " void m(int p) {\n" + " switch (p) {\n" + " case 0:\n" + " System.err.println(1);\n" + " break;\n" + " case 1:\n" + " case 2:\n" + " case 3:\n" + " System.err.println(2);\n" + " break;\n" + " }\n" + " }\n" + "}\n"; final int index = test.indexOf("|"); assertTrue(index != -1); TestUtilities.copyStringToFile(testFile, test.replace("|", "")); JavaSource src = getJavaSource(testFile); Task<WorkingCopy> task = new Task<WorkingCopy>() { public void run(WorkingCopy copy) throws IOException { if (copy.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0) { return; } TreeMaker make = copy.getTreeMaker(); TreePath node = copy.getTreeUtilities().pathFor(index); assertTrue(node.getLeaf().getKind() == Kind.CASE); SwitchTree st = (SwitchTree) node.getParentPath().getLeaf(); List<CaseTree> newCases = new LinkedList<CaseTree>(); newCases.add(st.getCases().get(0)); newCases.add(make.Case(make.Literal(1), Collections.<StatementTree>emptyList())); newCases.add(make.Case(make.Literal(2), Collections.<StatementTree>emptyList())); newCases.add(make.Case(make.Literal(3), st.getCases().get(1).getStatements())); copy.rewrite(st, make.Switch(st.getExpression(), newCases)); } }; src.runModificationTask(task).commit(); String res = TestUtilities.copyFileToString(testFile); System.err.println(res); assertEquals(golden, res); }
@Override public Boolean visitSwitch(SwitchTree node, Element p) { return null; }
@Override public Object visitSwitch(SwitchTree node, Object p) { statements++; return super.visitSwitch(node, p); }
@Override public State visitSwitch(SwitchTree node, Void p) { registerBreakTarget(node); State s; if (returnIfRecurse(s= scan(node.getExpression(), p))) { return s; } // look for the default case, but cannot return immediately as some return / break inside could // slip unnoticed. boolean defaultFound = false; Set<Tree> saveBreaks = breakContinueJumps; Set<Tree> collectBreaks = Collections.emptySet(); State lastState = State.NO; boolean saveDefinite = definitePath; definitePath = false; for (CaseTree ct : node.getCases()) { if (ct.getExpression() == null) { defaultFound = true; } knownResult = null; breakContinueJumps = new HashSet<Tree>(); s = scan(ct, p); if (s == State.RETURN) { // possible return reachable from the branch, bail out definitePath = saveDefinite; return knownResult = s; // the branch just jumped off } else { // the branch recurses. But if the branch also contains breaks out of the switch, or out of outer // cycles, those breaks must have been found before the recursion instruction. Any jumps to // nested statements should have been cleared by scan(). boolean self = breakContinueJumps.remove(node); if (self || !breakContinueJumps.isEmpty()) { // at least one way out saveBreaks.addAll(breakContinueJumps); saveBreaks.addAll(collectBreaks); breakContinueJumps = saveBreaks; definitePath = saveDefinite; recursionPoints.clear(); return State.NO; } lastState = s; } } definitePath = saveDefinite; if (defaultFound) { return lastState; } else { recursionPoints.clear(); return State.NO; } }
public Boolean visitSwitch(SwitchTree node, ConstructorData p) { scan(node.getExpression(), null); Map< Element, State> origVariable2State = new HashMap< Element, State>(variable2State); variable2State = new HashMap< Element, State>(); boolean exhaustive = false; for (CaseTree ct : node.getCases()) { variable2State = mergeOr(variable2State, origVariable2State); if (ct.getExpression() == null) { exhaustive = true; } scan(ct, null); } if (!exhaustive) { variable2State = mergeOr(variable2State, origVariable2State); } return null; }
@Override protected void performRewrite(final TransformationContext ctx) { WorkingCopy wc = ctx.getWorkingCopy(); final TreeMaker make = wc.getTreeMaker(); final TreePath tp = ctx.getPath(); SwitchTree st = (SwitchTree) tp.getLeaf(); int insertIndex = 0; boolean hadDefault = false; for (CaseTree ct : st.getCases()) { if (ct.getExpression() == null) { hadDefault = true; break; } insertIndex++; } for (String name : names) { st = make.insertSwitchCase(st, insertIndex++, make.Case(make.Identifier(name), Collections.singletonList(make.Break(null)))); } if (!hadDefault && defaultTemplate != null) { StatementTree stmt = ctx.getWorkingCopy().getTreeUtilities().parseStatement(defaultTemplate, new SourcePositions[1]); Scope s = ctx.getWorkingCopy().getTrees().getScope(tp); ctx.getWorkingCopy().getTreeUtilities().attributeTree(stmt, s); st = GeneratorUtilities.get(ctx.getWorkingCopy()).importFQNs(make.addSwitchCase(st, make.Case(null, Collections.singletonList(stmt)))); new ErrorAwareTreePathScanner<Void, Void>() { @Override public Void visitIdentifier(IdentifierTree node, Void p) { if (node.getName().contentEquals("$expression")) { ExpressionTree expression = ((SwitchTree) tp.getLeaf()).getExpression(); if (expression.getKind() == Tree.Kind.PARENTHESIZED) { expression = ((ParenthesizedTree) expression).getExpression(); } if (JavaFixUtilities.requiresParenthesis(expression, node, getCurrentPath().getParentPath().getLeaf())) { expression = make.Parenthesized(expression); } ctx.getWorkingCopy().rewrite(node, expression); } return super.visitIdentifier(node, p); } }.scan(new TreePath(ctx.getPath(), st), null); } wc.rewrite(tp.getLeaf(), st); }
@Override public R visitSwitch(SwitchTree st, P p) { return null; }
@Override public List<T> visitSwitch(SwitchTree node, T p) { return checkForCriteria(node); }
@Override public Object visitSwitch(SwitchTree t, Trees p) { info("SwitchTree" + CL + t.getKind() + SP + t); return super.visitSwitch(t, p); }
@Override public Description matchSwitch(SwitchTree tree, VisitorState state) { Optional<? extends CaseTree> maybeDefault = tree.getCases().stream().filter(c -> c.getExpression() == null).findAny(); if (!maybeDefault.isPresent()) { return NO_MATCH; } // Collect all case trees in the statement group containing the default List<CaseTree> defaultStatementGroup = new ArrayList<>(); Iterator<? extends CaseTree> it = tree.getCases().iterator(); while (it.hasNext()) { CaseTree caseTree = it.next(); defaultStatementGroup.add(caseTree); if (caseTree.getExpression() == null) { while (it.hasNext() && caseTree.getStatements().isEmpty()) { caseTree = it.next(); defaultStatementGroup.add(caseTree); } break; } if (!caseTree.getStatements().isEmpty()) { defaultStatementGroup.clear(); } } // Find the position of the default case within the statement group int idx = defaultStatementGroup.indexOf(maybeDefault.get()); SuggestedFix.Builder fix = SuggestedFix.builder(); CaseTree defaultTree = defaultStatementGroup.get(idx); if (it.hasNext()) { // If there are trailing cases after the default statement group, move the default to the end. // Only emit a fix if the default doesn't fall through. if (!Reachability.canCompleteNormally(getLast(defaultStatementGroup))) { int start = ((JCTree) defaultStatementGroup.get(0)).getStartPosition(); int end = state.getEndPosition(getLast(defaultStatementGroup)); String replacement; String source = state.getSourceCode().toString(); // If the default case isn't the last case in its statement group, move it to the end. if (idx != defaultStatementGroup.size() - 1) { int caseEnd = ((JCTree) getLast(defaultStatementGroup).getStatements().get(0)).getStartPosition(); int cutStart = ((JCTree) defaultTree).getStartPosition(); int cutEnd = state.getEndPosition(defaultTree); replacement = source.substring(start, cutStart) + source.substring(cutEnd, caseEnd) + "\n" + source.substring(cutStart, cutEnd) + source.substring(caseEnd, end); } else { replacement = source.substring(start, end); } fix.replace(start, end, "").postfixWith(getLast(tree.getCases()), replacement); } } else if (idx != defaultStatementGroup.size() - 1) { // If the default case isn't the last case in its statement group, move it to the end. fix.delete(defaultTree) .prefixWith( getLast(defaultStatementGroup).getStatements().get(0), state.getSourceForNode(defaultTree)); } else { return NO_MATCH; } Description.Builder description = buildDescription(defaultStatementGroup.get(0)); if (!fix.isEmpty()) { description.addFix(fix.build()); } return description.build(); }
@Override public Description matchSwitch(SwitchTree tree, VisitorState state) { Type switchType = ASTHelpers.getType(tree.getExpression()); if (switchType.asElement().getKind() == ElementKind.ENUM) { // enum switches can omit the default if they're exhaustive, which is enforced separately // by MissingCasesInEnumSwitch return NO_MATCH; } Optional<? extends CaseTree> maybeDefault = tree.getCases().stream().filter(c -> c.getExpression() == null).findFirst(); if (!maybeDefault.isPresent()) { Description.Builder description = buildDescription(tree); if (!tree.getCases().isEmpty()) { // Inserting the default after the last case is easier than finding the closing brace // for the switch statement. Hopefully we don't often see switches with zero cases. CaseTree lastCase = getLast(tree.getCases()); String replacement; if (lastCase.getStatements().isEmpty() || Reachability.canCompleteNormally(Iterables.getLast(lastCase.getStatements()))) { replacement = "\nbreak;\ndefault: // fall out\n"; } else { replacement = "\ndefault: // fall out\n"; } description.addFix(SuggestedFix.postfixWith(getLast(tree.getCases()), replacement)); } return description.build(); } CaseTree defaultCase = maybeDefault.get(); if (!defaultCase.getStatements().isEmpty()) { return NO_MATCH; } // If `default` case is empty, and last in switch, add `// fall out` comment // TODO(epmjohnston): Maybe move comment logic to go/bugpattern/FallThrough int idx = tree.getCases().indexOf(defaultCase); if (idx != tree.getCases().size() - 1) { return NO_MATCH; } int end = state.getEndPosition(tree); if (ErrorProneTokens.getTokens( state.getSourceCode().subSequence(state.getEndPosition(defaultCase), end).toString(), state.context) .stream() .anyMatch(t -> !t.comments().isEmpty())) { return NO_MATCH; } return buildDescription(defaultCase) .setMessage("Default case should be documented with a comment") .addFix(SuggestedFix.postfixWith(defaultCase, " // fall out")) .build(); }
@Override public Node visitSwitch(SwitchTree tree, Void p) { switchExpr = unbox(scan(tree.getExpression(), p)); extendWithNode(new MarkerNode(tree, "start of switch statement", env.getTypeUtils())); Label oldBreakTargetL = breakTargetL; breakTargetL = new Label(); for (CaseTree caseTree : tree.getCases()) { scan(caseTree, p); } addLabelForNextNode(breakTargetL); breakTargetL = oldBreakTargetL; return null; }
@Override public PurityResult visitSwitch(SwitchTree node, PurityResult p) { PurityResult r = scan(node.getExpression(), p); r = scan(node.getCases(), r); return r; }
@Override public Void visitSwitch(SwitchTree node, Void p) { checkForNullability(node.getExpression(), SWITCHING_NULLABLE); return super.visitSwitch(node, p); }
@Override public Node visitSwitch(SwitchTree tree, Void p) { SwitchBuilder builder = new SwitchBuilder(tree, p); builder.build(); return null; }
private SwitchBuilder(SwitchTree tree, Void p) { this.switchTree = tree; this.caseBodyLabels = new Label[switchTree.getCases().size() + 1]; this.p = p; }
@Override public Wrapper visitSwitch(SwitchTree arg0, Void arg1) { return new SwitchWrapper(arg0, // visit(arg0.getExpression(), ExpressionWrapper.class), // visitList(arg0.getCases(), CaseWrapper.class)); }