PsiElementVisitor (@NotNull ProblemsHolder holder, boolean isOnTheFly) { if (!HighlightingFeature.RECORD_PATTERNS_IN_FOR_EACH.isAvailable(holder.getFile())) return PsiElementVisitor.EMPTY_VISITOR; boolean useVar = IntroduceVariableBase.createVarType() || forceUseVar; return new JavaElementVisitor() { private final Options options = new Options(maxNotUsedComponentCounts, Math.min(maxComponentCounts, MAX_COMPONENT_COUNTS), Math.min(maxLevel, MAX_LEVEL), useVar, isOnTheFly); @Override public void visitForeachStatement(@NotNull PsiForeachStatement statement) { super.visitForeachStatement(statement); ComponentContext context = createContext(statement, options); if (context == null) return; processComponentContext(context, this::highlight); } @Override public void visitForeachPatternStatement(@NotNull PsiForeachPatternStatement statement) { super.visitForeachPatternStatement(statement); boolean showAsInfo = false; if (options.maxLevel <= 0) { return; } PsiStatement foreachBody = statement.getBody(); if (foreachBody == null) { return; } PsiPattern pattern = statement.getIterationPattern(); if (!(pattern instanceof PsiDeconstructionPattern deconstructionPattern)) { return; } Deque<DeconstructionContext> queueToProcess = new ArrayDeque<>(); queueToProcess.add(new DeconstructionContext(1, deconstructionPattern, statement)); while (!queueToProcess.isEmpty()) { DeconstructionContext currentContext = queueToProcess.poll(); int level = currentContext.level; if (level >= options.maxLevel + 1) { if (!options.isOnTheFly) { return; } showAsInfo = true; } PsiDeconstructionPattern currentDeconstruction = currentContext.pattern; PsiDeconstructionList list = currentDeconstruction.getDeconstructionList(); for (PsiPattern component : list.getDeconstructionComponents()) { if (component instanceof PsiDeconstructionPattern newDeconstruction) { queueToProcess.add(new DeconstructionContext(level + 1, newDeconstruction, currentContext.foreachPatternStatement)); continue; } if (component instanceof PsiTypeTestPattern typeTestPattern) { PsiTypeElement typeElement = typeTestPattern.getCheckType(); PsiPatternVariable variable = typeTestPattern.getPatternVariable(); if (typeElement == null || variable == null) { continue; } PsiType type = typeElement.getType(); PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; if (type instanceof PsiClassType referenceType) { substitutor = referenceType.resolveGenerics().getSubstitutor(); } PsiClass parameterClass = PsiUtil.resolveClassInClassTypeOnly(type); if (parameterClass == null) { continue; } ShowInfoFolder showInfoFolder = new ShowInfoFolder(); showInfoFolder.showInfo = showAsInfo; ComponentContext context = new ComponentContext(level, variable, parameterClass, substitutor, currentContext.foreachPatternStatement, options, showInfoFolder); processComponentContext(context, this::highlight); } } } } private void highlight(@NotNull ComponentContext context, @NotNull Map<String, List<PsiElement>> components) { if (context.showInfoFolder.showInfo && !context.options.isOnTheFly) { return; } registerProblemForIdentifier(context.currentParameter, context, holder); } private void registerProblemForIdentifier(@NotNull PsiVariable variable, @NotNull ComponentContext context, @NotNull ProblemsHolder holder) { PsiIdentifier[] identifiers = PsiTreeUtil.getChildrenOfType(variable, PsiIdentifier.class); if (identifiers == null || identifiers.length != 1) { return; } PsiIdentifier identifier = identifiers[0]; LocalQuickFix localQuickFix = getLocalQuickFix(context); if (context.showInfoFolder.showInfo) { holder.registerProblem(identifier, InspectionGadgetsBundle.message("inspection.enhanced.for.with.record.pattern.can.be.used.message"), ProblemHighlightType.INFORMATION, localQuickFix); } else { List<LocalQuickFix> fixes = new ArrayList<>(); fixes.add(localQuickFix); int length = context.recordClass.getRecordComponents().length; if (length > 1) { fixes.add( LocalQuickFix.from(new UpdateInspectionOptionFix( ForEachWithRecordPatternCanBeUsedInspection.this, "maxComponentCounts", InspectionGadgetsBundle.message("inspection.enhanced.for.with.record.pattern.can.be.used.maximum.number.disabled", length), length - 1))); } Integer level = context.level; if (level != null && level > 0) { fixes.add(LocalQuickFix.from(new UpdateInspectionOptionFix( ForEachWithRecordPatternCanBeUsedInspection.this, "maxLevel", InspectionGadgetsBundle.message("inspection.enhanced.for.with.record.pattern.can.be.used.maximum.depth.disabled", level), level - 1))); } holder.registerProblem(identifier, InspectionGadgetsBundle.message("inspection.enhanced.for.with.record.pattern.can.be.used.message"), fixes.toArray(LocalQuickFix.EMPTY_ARRAY)); } } @NotNull private LocalQuickFix getLocalQuickFix(@NotNull ComponentContext context) { return new ForEachWithRecordCanBeUsedFix(context.base, context.currentParameter); } }; }