PsiElementVisitor (@NotNull ProblemsHolder holder, boolean isOnTheFly) { return new JavaElementVisitor() { @Override public void visitForStatement(@NotNull PsiForStatement statement) { super.visitForStatement(statement); CountingLoop loop = CountingLoop.from(statement); if (loop == null || loop.isIncluding() || loop.isDescending()) return; if (!ExpressionUtils.isZero(loop.getInitializer())) return; PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(ControlFlowUtils.stripBraces(statement.getBody())); if (assignment == null) return; IndexedContainer container = getContainer(loop, assignment); if (container == null || !(container.getQualifier().getType() instanceof PsiArrayType)) return; PsiExpression index = container.extractIndexFromGetExpression(assignment.getLExpression()); if (!ExpressionUtils.isReferenceTo(index, loop.getCounter())) return; PsiExpression rValue = assignment.getRExpression(); if (rValue == null) return; if (!isChangedInLoop(loop, rValue)) { PsiType lType = container.getElementType(); Object defaultValue = PsiTypesUtil.getDefaultValue(lType); if (isDefaultValue(rValue, defaultValue, lType) && isFilledWithDefaultValues(container.getQualifier(), statement, defaultValue)) { holder.registerProblem(statement, getRange(statement, ProblemHighlightType.WARNING), JavaBundle.message("inspection.explicit.array.filling.redundant.loop.description"), QuickFixFactory.getInstance() .createDeleteFix(statement, CommonQuickFixBundle.message("fix.remove.statement", PsiKeyword.FOR))); return; } registerProblem(statement, false); return; } if (!JavaFeature.ADVANCED_COLLECTIONS_API.isFeatureSupported(holder.getFile())) return; if (!StreamApiUtil.isSupportedStreamElement(container.getElementType())) return; if (!LambdaGenerationUtil.canBeUncheckedLambda(rValue, Predicate.isEqual(loop.getCounter()))) return; registerProblem(statement, true); } private static boolean isChangedInLoop(@NotNull CountingLoop loop, @NotNull PsiExpression rValue) { if (VariableAccessUtils.collectUsedVariables(rValue).contains(loop.getCounter()) || SideEffectChecker.mayHaveSideEffects(rValue)) { return true; } return ExpressionUtils.nonStructuralChildren(rValue) .filter(c -> c instanceof PsiCallExpression) .anyMatch(call -> !ClassUtils.isImmutable(call.getType()) && !ConstructionUtils.isEmptyArrayInitializer(call)); } private static boolean isDefaultValue(@NotNull PsiExpression expression, @Nullable Object defaultValue, @Nullable PsiType lType) { if (ExpressionUtils.isNullLiteral(expression) && defaultValue == null) return true; Object constantValue = ExpressionUtils.computeConstantExpression(expression); PsiType rType = expression.getType(); if (rType instanceof PsiPrimitiveType && lType instanceof PsiPrimitiveType) { if (defaultValue instanceof Number && constantValue instanceof Number) { return ((Number)defaultValue).doubleValue() == ((Number)constantValue).doubleValue(); } } return constantValue != null && constantValue.equals(defaultValue); } private static boolean isFilledWithDefaultValues(@NotNull PsiExpression expression, @NotNull PsiForStatement statement, @Nullable Object defaultValue) { PsiReferenceExpression arrayRef = tryCast(PsiUtil.skipParenthesizedExprDown(expression), PsiReferenceExpression.class); if (arrayRef == null) return false; PsiVariable arrayVar = tryCast(arrayRef.resolve(), PsiVariable.class); if (arrayVar == null) return false; PsiCodeBlock block = tryCast(PsiUtil.getVariableCodeBlock(arrayVar, null), PsiCodeBlock.class); if (block == null) return false; ControlFlow flow = createControlFlow(block); if (flow == null) return false; int statementStart = flow.getStartOffset(statement); if (statementStart == -1) return false; int statementEnd = flow.getEndOffset(statement); if (statementEnd == -1) return false; PsiElement[] defs = getDefs(block, arrayVar, arrayRef, defaultValue); if (defs == null) return false; Set<Integer> exclude = getDefsOffsets(flow, defs); if (exclude == null) return false; for (int i = statementStart; i < statementEnd; i++) { exclude.add(i); } return !ContainerUtil.exists(defs, def -> ControlFlowUtils.isVariableReferencedBeforeStatementEntry(flow, flow.getEndOffset(def) + 1, statement, arrayVar, exclude)); } @Nullable private static ControlFlow createControlFlow(@NotNull PsiCodeBlock block) { try { return ControlFlowFactory.getInstance(block.getProject()) .getControlFlow(block, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance()); } catch (AnalysisCanceledException ignored) { return null; } } private static PsiElement @Nullable [] getDefs(@NotNull PsiCodeBlock block, @NotNull PsiVariable arrayVar, @NotNull PsiReferenceExpression arrayRef, @Nullable Object defaultValue) { PsiElement[] defs = DefUseUtil.getDefs(block, arrayVar, arrayRef); PsiExpression[] expressions = new PsiExpression[defs.length]; for (int i = 0; i < defs.length; i++) { PsiElement def = defs[i]; PsiVariable variable = tryCast(def, PsiVariable.class); if (variable != null) { PsiExpression initializer = variable.getInitializer(); if (!isNewArrayCreation(initializer, defaultValue)) return null; expressions[i] = initializer; continue; } PsiAssignmentExpression assignment = PsiTreeUtil.getParentOfType(def, PsiAssignmentExpression.class); if (assignment != null) { if (!isNewArrayCreation(assignment.getRExpression(), defaultValue)) return null; expressions[i] = assignment; continue; } return null; } return expressions; } private static boolean isNewArrayCreation(@Nullable PsiExpression expression, @Nullable Object defaultValue) { PsiExpression arrInitExpr = PsiUtil.skipParenthesizedExprDown(expression); PsiNewExpression newExpression = tryCast(arrInitExpr, PsiNewExpression.class); PsiArrayInitializerExpression initializer; if (newExpression == null) { initializer = tryCast(arrInitExpr, PsiArrayInitializerExpression.class); if (initializer == null) return false; } else { initializer = newExpression.getArrayInitializer(); } if (initializer == null) return true; return ContainerUtil.and(initializer.getInitializers(), init -> isDefaultValue(init, defaultValue, init.getType())); } @Nullable private static Set<Integer> getDefsOffsets(@NotNull ControlFlow flow, PsiElement @NotNull [] defs) { Set<Integer> set = new HashSet<>(); for (PsiElement def : defs) { int start = flow.getStartOffset(def); if (start == -1) return null; int end = flow.getEndOffset(def); if (end == -1) return null; for (int i = start; i < end; i++) { set.add(i); } } return set; } private void registerProblem(@NotNull PsiForStatement statement, boolean isSetAll) { String message = JavaBundle.message("inspection.explicit.array.filling.description", isSetAll ? "setAll" : "fill"); ReplaceWithArraysCallFix fix = new ReplaceWithArraysCallFix(!isSetAll); ProblemHighlightType type = ProblemHighlightType.WARNING; if (isSetAll && !mySuggestSetAll) { if (!isOnTheFly) return; type = ProblemHighlightType.INFORMATION; } TextRange range = getRange(statement, type); if (isSetAll && mySuggestSetAll && isOnTheFly) { var disableForSetAllFix = new UpdateInspectionOptionFix(ExplicitArrayFillingInspection.this, "mySuggestSetAll", JavaBundle.message("inspection.explicit.array.filling.no.suggestion.for.set.all"), false); holder.problem(statement, message).highlight(type).range(range).fix(fix).fix(disableForSetAllFix).register(); return; } holder.registerProblem(statement, message, type, range, fix); } @NotNull private TextRange getRange(@NotNull PsiForStatement statement, @NotNull ProblemHighlightType type) { PsiStatement initialization = statement.getInitialization(); LOG.assertTrue(initialization != null); TextRange range = TextRange.from(initialization.getStartOffsetInParent(), initialization.getTextLength()); boolean wholeStatement = isOnTheFly && (InspectionProjectProfileManager.isInformationLevel(getShortName(), statement) || type == ProblemHighlightType.INFORMATION); PsiJavaToken rParenth = statement.getRParenth(); if (wholeStatement && rParenth != null) { range = new TextRange(0, rParenth.getStartOffsetInParent() + 1); } return range; } }; }