public Boolean visitEnhancedForLoop(EnhancedForLoopTree node, TreePath p) { if (p == null) { super.visitEnhancedForLoop(node, p); return false; } EnhancedForLoopTree ef = (EnhancedForLoopTree) p.getLeaf(); if (!scan(node.getVariable(), ef.getVariable(), p)) return false; if (!scan(node.getExpression(), ef.getExpression(), p)) return false; return scan(node.getStatement(), ef.getStatement(), p); }
@Override public Object visitVariable(VariableTree node, Object p) { TreePath path = getCurrentPath(); Tree parent = path.getParentPath().getLeaf(); if (parent instanceof StatementTree) { boolean count = true; if (parent instanceof ForLoopTree) { count = !((ForLoopTree)parent).getInitializer().contains(node); } else if (parent instanceof EnhancedForLoopTree) { count = ((EnhancedForLoopTree)parent).getVariable() != node; } if (count) { statements++; } } return super.visitVariable(node, p); }
@TriggerTreeKind(Tree.Kind.ENHANCED_FOR_LOOP) @Messages("ERR_ForLoopToFunctionalHint=Can use functional operations") public static ErrorDescription computeWarning(HintContext ctx) { if (ctx.getInfo().getElements().getTypeElement("java.util.stream.Streams") == null && !DISABLE_CHECK_FOR_STREAM) return null; PreconditionsChecker pc = new PreconditionsChecker(ctx.getPath().getLeaf(), ctx.getInfo()); if (pc.isSafeToRefactor()) { EnhancedForLoopTree eflt = (EnhancedForLoopTree)ctx.getPath().getLeaf(); StatementTree stmt = eflt.getStatement(); if (stmt == null) { return null; } if (stmt.getKind() == Tree.Kind.BLOCK) { BlockTree bt = (BlockTree)stmt; if (bt.getStatements() == null || bt.getStatements().isEmpty()) { return null; } } Fix fix = new FixImpl(ctx.getInfo(), ctx.getPath(), null).toEditorFix(); return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), Bundle.ERR_ForLoopToFunctionalHint(), fix); } return null; }
@Override public List<? extends TypeMirror> visitEnhancedForLoop(EnhancedForLoopTree node, Object p) { TypeMirror varType = info.getTrees().getTypeMirror(new TreePath(getCurrentPath(), node.getVariable())); if (!Utilities.isValidType(varType)) { return null; } else { TypeMirror arrayType = info.getTypes().getArrayType(varType); TypeElement iterableEl = info.getElements().getTypeElement("java.lang.Iterable"); // NOI18N if (iterableEl == null || iterableEl.getKind() != ElementKind.INTERFACE) { return null; } TypeMirror iterableForVar = isPrimitiveType(varType.getKind()) ? info.getTypes().getDeclaredType(iterableEl, info.getTypes().getWildcardType( info.getTypes().boxedClass((PrimitiveType)varType).asType(), null)) : info.getTypes().getDeclaredType(iterableEl, info.getTypes().getWildcardType(varType, null) ); List<TypeMirror> result = new ArrayList<TypeMirror>(2); result.add(arrayType); result.add(iterableForVar); return result; } }
@Override public Void visitEnhancedForLoop(EnhancedForLoopTree node, Void unused) { sync(node); builder.open(ZERO); token("for"); builder.space(); token("("); builder.open(ZERO); visitToDeclare( DeclarationKind.NONE, Direction.HORIZONTAL, node.getVariable(), Optional.of(node.getExpression()), ":", Optional.<String>absent()); builder.close(); token(")"); builder.close(); visitStatement( node.getStatement(), CollapseEmptyOrNot.YES, AllowLeadingBlankLine.YES, AllowTrailingBlankLine.NO); return null; }
/** Returns true if {@code collection} is modified by an enclosing loop. */ static boolean enclosingLoop(TreePath path, ExpressionTree collection) { for (Tree node : path) { switch (node.getKind()) { case METHOD: case CLASS: case LAMBDA_EXPRESSION: return false; case ENHANCED_FOR_LOOP: if (sameVariable(collection, ((EnhancedForLoopTree) node).getExpression())) { return true; } break; default: // fall out } } return false; }
@Override public Tree visitEnhancedForLoop(EnhancedForLoopTree tree, Void p) { EnhancedForLoopTree n = make.EnhancedForLoop(tree.getVariable(), tree.getExpression(), tree.getStatement()); model.setType(n, model.getType(tree)); comments.copyComments(tree, n); model.setPos(n, model.getPos(tree)); return n; }
@Override public Void visitEnhancedForLoop(EnhancedForLoopTree tree, EnumSet<UseTypes> d) { scan(tree.getVariable(), EnumSet.of(UseTypes.WRITE)); if (tree.getExpression().getKind() == Kind.IDENTIFIER) handlePossibleIdentifier(new TreePath(getCurrentPath(), tree.getExpression()), EnumSet.of(UseTypes.READ)); scan(tree.getExpression(), null); scan(tree.getStatement(), null); return null; }
private static List<? extends TypeMirror> computeEnhancedForLoop(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { EnhancedForLoopTree efl = (EnhancedForLoopTree) parent.getLeaf(); if (efl.getExpression() != error) { return null; } TypeMirror argument = info.getTrees().getTypeMirror(new TreePath(new TreePath(parent, efl.getVariable()), efl.getVariable().getType())); if (argument == null) return null; if (argument.getKind().isPrimitive()) { types.add(ElementKind.PARAMETER); types.add(ElementKind.LOCAL_VARIABLE); types.add(ElementKind.FIELD); return Collections.singletonList(info.getTypes().getArrayType(argument)); } TypeElement iterable = info.getElements().getTypeElement("java.lang.Iterable"); //NOI18N if (iterable == null) { return null; } types.add(ElementKind.PARAMETER); types.add(ElementKind.LOCAL_VARIABLE); types.add(ElementKind.FIELD); return Collections.singletonList(info.getTypes().getDeclaredType(iterable, argument)); }
private static List<? extends TypeMirror> computeVariableDeclaration(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { VariableTree vt = (VariableTree) parent.getLeaf(); if (vt.getInitializer() == error) { types.add(ElementKind.PARAMETER); types.add(ElementKind.LOCAL_VARIABLE); types.add(ElementKind.FIELD); return Collections.singletonList(info.getTrees().getTypeMirror(new TreePath(parent, vt.getType()))); } TreePath context = parent.getParentPath(); if (vt.getType() != error || context == null) { return null; } switch (context.getLeaf().getKind()) { case ENHANCED_FOR_LOOP: ExpressionTree iterableTree = ((EnhancedForLoopTree) context.getLeaf()).getExpression(); TreePath iterablePath = new TreePath(context, iterableTree); TypeMirror type = getIterableGenericType(info, iterablePath); types.add(ElementKind.LOCAL_VARIABLE); return Collections.singletonList(type); default: types.add(ElementKind.CLASS); return Collections.<TypeMirror>emptyList(); } }
@Override public Object visitEnhancedForLoop(EnhancedForLoopTree node, Object p) { boolean saveFlag = switchCase; switchCase = false; complexity++; Object o = super.visitEnhancedForLoop(node, p); this.switchCase = saveFlag; return o; }
@Override public Object visitEnhancedForLoop(EnhancedForLoopTree node, Object p) { depth++; Object o = super.visitEnhancedForLoop(node, p); depth--; return o; }
@Override public Void visitEnhancedForLoop(EnhancedForLoopTree tree, List<Node> d) { List<Node> below = new ArrayList<Node>(); addCorrespondingType(below); addCorrespondingComments(below); super.visitEnhancedForLoop(tree, below); d.add(new TreeNode(info, getCurrentPath(), below)); return null; }
@Override protected void performRewrite(TransformationContext ctx) { EnhancedForLoopTree loop = (EnhancedForLoopTree) ctx.getPath().getLeaf(); pc = new PreconditionsChecker(loop, ctx.getWorkingCopy()); refactorer = new Refactorer(loop, ctx.getWorkingCopy(), pc); if (pc.isSafeToRefactor() && refactorer.isRefactorable()) { ctx.getWorkingCopy().rewrite(ctx.getPath().getLeaf(), refactorer.refactor(ctx.getWorkingCopy().getTreeMaker())); } }
public PreconditionsChecker(Tree forLoop, CompilationInfo workingCopy) { if (forLoop.getKind() == Tree.Kind.ENHANCED_FOR_LOOP) { this.isForLoop = true; this.workingCopy = workingCopy; this.hasUncaughtException = workingCopy.getTreeUtilities() .getUncaughtExceptions(TreePath.getPath(workingCopy.getCompilationUnit(), forLoop)).stream().anyMatch(this::filterCheckedExceptions); this.innerVariables = this.getInnerVariables(forLoop, workingCopy.getTrees()); this.visitor = new ForLoopTreeVisitor(this.innerVariables, workingCopy, new TreePath(workingCopy.getCompilationUnit()), (EnhancedForLoopTree) forLoop); this.isIterable = this.isIterbale(((EnhancedForLoopTree) forLoop).getExpression()); visitor.scan(TreePath.getPath(workingCopy.getCompilationUnit(), forLoop), workingCopy.getTrees()); } else { this.isForLoop = false; } }
@Hint(displayName="#LBL_Braces_For", description="#DSC_Braces_For", category="braces", id=BRACES_ID + "FOR_LOOP", enabled=false, suppressWarnings={"", "ControlFlowStatementWithoutBraces"}) @TriggerTreeKind({Tree.Kind.FOR_LOOP, Tree.Kind.ENHANCED_FOR_LOOP}) public static ErrorDescription checkFor(HintContext ctx) { StatementTree st; switch (ctx.getPath().getLeaf().getKind()){ case FOR_LOOP: st = ((ForLoopTree) ctx.getPath().getLeaf()).getStatement(); break; case ENHANCED_FOR_LOOP: st = ((EnhancedForLoopTree) ctx.getPath().getLeaf()).getStatement(); break; default: throw new IllegalStateException(); } return checkStatement(ctx, "LBL_Braces_For", st, ctx.getPath()); }
@TriggerPattern(value="$mods$ $type[] $name = $init$;") public static ErrorDescription after(HintContext ctx) { VariableElement var = testElement(ctx); if (var == null) return null; Tree parent = ctx.getPath().getParentPath().getLeaf(); if (parent.getKind() == Kind.ENHANCED_FOR_LOOP && ((EnhancedForLoopTree) parent).getVariable() == ctx.getPath().getLeaf()) { return null; } TreePath init = ctx.getVariables().get("$init$"); if (init != null) { boolean asWrite = true; if (init.getLeaf().getKind() == Kind.NEW_ARRAY) { NewArrayTree nat = (NewArrayTree) init.getLeaf(); if (nat.getInitializers() == null || nat.getInitializers().isEmpty()) { asWrite = false; } } if (asWrite) { record(ctx.getInfo(), var, State.WRITE); } } return produceWarning(ctx, "ERR_UnbalancedArray"); }
@TriggerPattern(value="$mods$ $type $name = $init$;") public static ErrorDescription after(HintContext ctx) { if (testElement(ctx) == null) return null; TreePath init = ctx.getVariables().get("$init$"); if (init != null) { if (init.getLeaf().getKind() != Kind.NEW_CLASS) return null; NewClassTree nct = (NewClassTree) init.getLeaf(); if (nct.getClassBody() != null || nct.getArguments().size() > 1) return null; if (nct.getArguments().size() == 1) { TypeMirror tm = ctx.getInfo().getTrees().getTypeMirror(new TreePath(init, nct.getArguments().get(0))); if (tm == null || tm.getKind() != TypeKind.INT) return null; } } if ( ctx.getPath().getParentPath().getLeaf().getKind() == Kind.ENHANCED_FOR_LOOP && ((EnhancedForLoopTree) ctx.getPath().getParentPath().getLeaf()).getVariable() == ctx.getPath().getLeaf()) { return null; } return produceWarning(ctx, "ERR_UnbalancedCollection"); }
@Override public State visitEnhancedForLoop(EnhancedForLoopTree node, Void p) { State s; registerBreakTarget((node)); returnIfRecurse(s = scan(node.getExpression(), p)); // if the expression does not recurse, it might evaluate to an empty Iterable, and skip the entire body. // PENDING: speculatively report recursions, if unconditionally reachable from cycles ? return s; }
@TriggerPattern("for ($type $varName : $expression) { $stmts$; }") public static ErrorDescription run(HintContext ctx) { TreePath tp = ctx.getPath(); EnhancedForLoopTree efl = (EnhancedForLoopTree) tp.getLeaf(); long statementStart = ctx.getInfo().getTrees().getSourcePositions().getStartPosition(ctx.getInfo().getCompilationUnit(), efl.getStatement()); int caret = ctx.getCaretLocation(); if (caret >= statementStart) { return null; } TypeMirror expressionType = ctx.getInfo().getTrees().getTypeMirror(new TreePath(tp, efl.getExpression())); if (expressionType == null || expressionType.getKind() != TypeKind.DECLARED) { return null; } ExecutableElement iterator = findIterable(ctx.getInfo()); Types t = ctx.getInfo().getTypes(); if (iterator == null || !t.isSubtype(((DeclaredType) expressionType), t.erasure(iterator.getEnclosingElement().asType()))) { return null; } FixImpl fix = new FixImpl(TreePathHandle.create(tp, ctx.getInfo())); List<Fix> fixes = Collections.<Fix>singletonList(fix.toEditorFix()); return ErrorDescriptionFactory.createErrorDescription(ctx.getSeverity(), NbBundle.getMessage(ExpandEnhancedForLoop.class, "ERR_ExpandEhancedForLoop"), fixes, ctx.getInfo().getFileObject(), caret, caret); }
public static Collection<? extends Element> getForwardReferences(TreePath path, int pos, SourcePositions sourcePositions, Trees trees) { HashSet<Element> refs = new HashSet<Element>(); while(path != null) { switch(path.getLeaf().getKind()) { case BLOCK: if (path.getParentPath().getLeaf().getKind() == Tree.Kind.LAMBDA_EXPRESSION) break; case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE: return refs; case VARIABLE: refs.add(trees.getElement(path)); TreePath parent = path.getParentPath(); if (TreeUtilities.CLASS_TREE_KINDS.contains(parent.getLeaf().getKind())) { boolean isStatic = ((VariableTree)path.getLeaf()).getModifiers().getFlags().contains(Modifier.STATIC); for(Tree member : ((ClassTree)parent.getLeaf()).getMembers()) { if (member.getKind() == Tree.Kind.VARIABLE && sourcePositions.getStartPosition(path.getCompilationUnit(), member) >= pos && (isStatic || !((VariableTree)member).getModifiers().getFlags().contains(Modifier.STATIC))) refs.add(trees.getElement(new TreePath(parent, member))); } } return refs; case ENHANCED_FOR_LOOP: EnhancedForLoopTree efl = (EnhancedForLoopTree)path.getLeaf(); if (sourcePositions.getEndPosition(path.getCompilationUnit(), efl.getExpression()) >= pos) refs.add(trees.getElement(new TreePath(path, efl.getVariable()))); } path = path.getParentPath(); } return refs; }
@Override public List<Tree> visitEnhancedForLoop(EnhancedForLoopTree node, ExpressionScanner.ExpressionsInfo p) { List<Tree> expr = null; if (acceptsTree(node.getExpression())) { expr = scan(node.getExpression(), p); } List<Tree> bodyr = scan(node.getStatement(), p); if (expr != null && expr.size() > 0 && bodyr != null && bodyr.size() > 0) { p.addNextExpression(expr.get(expr.size() - 1), bodyr.get(0)); p.addNextExpression(bodyr.get(bodyr.size() - 1), expr.get(0)); } return reduce(expr, bodyr); }
@Override public UEnhancedForLoop visitEnhancedForLoop(EnhancedForLoopTree tree, Void v) { return UEnhancedForLoop.create( visitVariable(tree.getVariable(), null), template(tree.getExpression()), template(tree.getStatement())); }
@Override @Nullable public Unifier visitEnhancedForLoop(EnhancedForLoopTree loop, @Nullable Unifier unifier) { unifier = getVariable().unify(loop.getVariable(), unifier); unifier = getExpression().unify(loop.getExpression(), unifier); return getStatement().unify(loop.getStatement(), unifier); }
/** * Matches an enhanced for loop if all the given matchers match. * * @param variableMatcher The matcher to apply to the variable. * @param expressionMatcher The matcher to apply to the expression. * @param statementMatcher The matcher to apply to the statement. */ public static Matcher<EnhancedForLoopTree> enhancedForLoop( final Matcher<VariableTree> variableMatcher, final Matcher<ExpressionTree> expressionMatcher, final Matcher<StatementTree> statementMatcher) { return new Matcher<EnhancedForLoopTree>() { @Override public boolean matches(EnhancedForLoopTree t, VisitorState state) { return variableMatcher.matches(t.getVariable(), state) && expressionMatcher.matches(t.getExpression(), state) && statementMatcher.matches(t.getStatement(), state); } }; }
/** * Finds all variable declarations which are unused at this point in the AST (i.e. they might be * used further on). */ public static ImmutableSet<VarSymbol> findUnusedIdentifiers(VisitorState state) { ImmutableSet.Builder<VarSymbol> definedVariables = ImmutableSet.builder(); ImmutableSet.Builder<Symbol> usedSymbols = ImmutableSet.builder(); Tree prev = state.getPath().getLeaf(); for (Tree curr : state.getPath().getParentPath()) { createFindIdentifiersScanner(usedSymbols, prev).scan(curr, null); switch (curr.getKind()) { case BLOCK: // If we see a block then walk over each statement to see if it defines a variable for (StatementTree statement : ((BlockTree) curr).getStatements()) { if (statement.equals(prev)) { // break if we see the tree we have just processed so that we only consider things // declared/used before us in the tree break; } addIfVariable(statement, definedVariables); } break; case FOR_LOOP: ForLoopTree forLoop = (ForLoopTree) curr; forLoop.getInitializer().stream().forEach(t -> addIfVariable(t, definedVariables)); break; case ENHANCED_FOR_LOOP: EnhancedForLoopTree enhancedFor = (EnhancedForLoopTree) curr; addIfVariable(enhancedFor.getVariable(), definedVariables); break; default: break; } prev = curr; } return ImmutableSet.copyOf(Sets.difference(definedVariables.build(), usedSymbols.build())); }
@Override public Void visitEnhancedForLoop(EnhancedForLoopTree tree, VisitorState visitorState) { VisitorState state = visitorState.withPath(getCurrentPath()); for (EnhancedForLoopTreeMatcher matcher : enhancedForLoopMatchers) { if (!isSuppressed(matcher, state)) { try { reportMatch(matcher.matchEnhancedForLoop(tree, state), tree, state); } catch (Throwable t) { handleError(matcher, t); } } } return super.visitEnhancedForLoop(tree, state); }
@Override public Choice<Unifier> visitEnhancedForLoop(EnhancedForLoopTree loop, Unifier unifier) { return getVariable() .unify(loop.getVariable(), unifier) .thenChoose(unifications(getExpression(), loop.getExpression())) .thenChoose(unifications(getStatement(), loop.getStatement())); }
@Override public Result visitEnhancedForLoop(EnhancedForLoopTree node, BreakContext cxt) { cxt.loopDepth++; try { return node.getStatement().accept(this, cxt).or(NEVER_EXITS); } finally { cxt.loopDepth--; } }
@Override public Choice<State<JCEnhancedForLoop>> visitEnhancedForLoop( final EnhancedForLoopTree node, State<?> state) { return chooseSubtrees( state, s -> unifyExpression(node.getExpression(), s), s -> unifyStatement(node.getStatement(), s), (expr, stmt) -> maker().ForeachLoop((JCVariableDecl) node.getVariable(), expr, stmt)); }
@Override public boolean matches(TryTree tryTree, VisitorState state) { return ASTHelpers.findEnclosingNode(state.getPath(), DoWhileLoopTree.class) != null || ASTHelpers.findEnclosingNode(state.getPath(), EnhancedForLoopTree.class) != null || ASTHelpers.findEnclosingNode(state.getPath(), WhileLoopTree.class) != null || ASTHelpers.findEnclosingNode(state.getPath(), ForLoopTree.class) != null; }
@Override public Description matchEnhancedForLoop(EnhancedForLoopTree tree, VisitorState state) { if (!foreachMatcher.matches(tree, state)) { return Description.NO_MATCH; } return buildDescription(tree) .setMessage( "Iterating over a Collection or iterating over a primitive array using a" + " non-primitive element type will trigger allocation, which " + COMMON_MESSAGE_SUFFIX) .build(); }
@Override public PurityResult visitEnhancedForLoop(EnhancedForLoopTree node, PurityResult p) { PurityResult r = scan(node.getVariable(), p); r = scan(node.getExpression(), r); r = scan(node.getStatement(), r); return r; }
/** * Performs a subtype check, to test whether the node expression * iterable type is a subtype of the variable type in the enhanced for * loop. * * If the subtype check fails, it issues a "enhancedfor.type.incompatible" error. */ @Override public Void visitEnhancedForLoop(EnhancedForLoopTree node, Void p) { AnnotatedTypeMirror var = atypeFactory.getAnnotatedType(node.getVariable()); AnnotatedTypeMirror iterableType = atypeFactory.getAnnotatedType(node.getExpression()); AnnotatedTypeMirror iteratedType = AnnotatedTypes.getIteratedType(checker.getProcessingEnvironment(), atypeFactory, iterableType); boolean valid = validateTypeOf(node.getVariable()); if (valid) { commonAssignmentCheck(var, iteratedType, node.getExpression(), "enhancedfor.type.incompatible", true); } return super.visitEnhancedForLoop(node, p); }