List<ProblemDescriptor> (final PsiCodeBlock body, final InspectionManager manager, final boolean onTheFly) { if (body == null) return null; final ControlFlow flow; try { ControlFlowPolicy policy = new ControlFlowPolicy() { @Override public PsiVariable getUsedVariable(@NotNull PsiReferenceExpression refExpr) { if (refExpr.isQualified()) return null; PsiElement refElement = refExpr.resolve(); if (refElement instanceof PsiLocalVariable || refElement instanceof PsiParameter) { if (!isVariableDeclaredInMethod((PsiVariable)refElement)) return null; return (PsiVariable)refElement; } return null; } @Override public boolean isParameterAccepted(@NotNull PsiParameter psiParameter) { return isVariableDeclaredInMethod(psiParameter); } @Override public boolean isLocalVariableAccepted(@NotNull PsiLocalVariable psiVariable) { return isVariableDeclaredInMethod(psiVariable); } private boolean isVariableDeclaredInMethod(PsiVariable psiVariable) { return PsiTreeUtil.getParentOfType(psiVariable, PsiClass.class) == PsiTreeUtil.getParentOfType(body, PsiClass.class); } }; flow = ControlFlowFactory.getInstance(body.getProject()).getControlFlow(body, policy, false); } catch (AnalysisCanceledException e) { return null; } int start = flow.getStartOffset(body); int end = flow.getEndOffset(body); final Collection<PsiVariable> writtenVariables = ControlFlowUtil.getWrittenVariables(flow, start, end, false); final List<ProblemDescriptor> problems = new ArrayList<>(); final HashSet<PsiVariable> result = new HashSet<>(); body.accept(new JavaRecursiveElementWalkingVisitor() { @Override public void visitCodeBlock(@NotNull PsiCodeBlock block) { if (block.getParent() instanceof PsiLambdaExpression && block != body) { final List<ProblemDescriptor> descriptors = checkCodeBlock(block, manager, onTheFly); if (descriptors != null) { problems.addAll(descriptors); } return; } super.visitCodeBlock(block); Set<PsiVariable> declared = getDeclaredVariables(block); if (declared.isEmpty()) return; PsiElement anchor = block; if (block.getParent() instanceof PsiSwitchBlock) { anchor = block.getParent(); //special case: switch legs Set<PsiReferenceExpression> writeRefs = SyntaxTraverser.psiTraverser().withRoot(block) .filter(PsiReferenceExpression.class) .filter(ref -> PsiUtil.isOnAssignmentLeftHand(ref)).toSet(); for (PsiReferenceExpression ref : writeRefs) { PsiElement resolve = ref.resolve(); if (resolve instanceof PsiVariable && declared.contains(resolve) && ((PsiVariable)resolve).hasInitializer()) { declared.remove(resolve); } } } int from = flow.getStartOffset(anchor); int end = flow.getEndOffset(anchor); List<PsiVariable> ssa = ControlFlowUtil.getSSAVariables(flow, from, end, true); for (PsiVariable psiVariable : ssa) { if (declared.contains(psiVariable) && (!psiVariable.hasInitializer() || !VariableAccessUtils.variableIsAssigned(psiVariable, block))) { result.add(psiVariable); } } } @Override public void visitResourceVariable(@NotNull PsiResourceVariable variable) { if (PsiTreeUtil.getParentOfType(variable, PsiClass.class) != PsiTreeUtil.getParentOfType(body, PsiClass.class)) { return; } result.add(variable); } @Override public void visitCatchSection(@NotNull PsiCatchSection section) { super.visitCatchSection(section); if (!REPORT_CATCH_PARAMETERS) return; final PsiParameter parameter = section.getParameter(); if (PsiTreeUtil.getParentOfType(parameter, PsiClass.class) != PsiTreeUtil.getParentOfType(body, PsiClass.class)) { return; } final PsiCodeBlock catchBlock = section.getCatchBlock(); if (catchBlock == null) return; final int from = flow.getStartOffset(catchBlock); final int end = flow.getEndOffset(catchBlock); if (!ControlFlowUtil.getWrittenVariables(flow, from, end, false).contains(parameter)) { writtenVariables.remove(parameter); result.add(parameter); } } @Override public void visitForeachStatement(@NotNull PsiForeachStatement statement) { super.visitForeachStatement(statement); if (!REPORT_FOREACH_PARAMETERS) return; final PsiParameter param = statement.getIterationParameter(); if (PsiTreeUtil.getParentOfType(param, PsiClass.class) != PsiTreeUtil.getParentOfType(body, PsiClass.class)) { return; } final PsiStatement body = statement.getBody(); if (body == null) return; int from = flow.getStartOffset(body); int end = flow.getEndOffset(body); if (!ControlFlowUtil.getWrittenVariables(flow, from, end, false).contains(param)) { writtenVariables.remove(param); result.add(param); } } @Override public void visitPatternVariable(@NotNull PsiPatternVariable variable) { super.visitPatternVariable(variable); if (!REPORT_PATTERN_VARIABLES) return; if (PsiTreeUtil.getParentOfType(variable, PsiClass.class) != PsiTreeUtil.getParentOfType(body, PsiClass.class)) { return; } PsiElement context = PsiTreeUtil.getParentOfType(variable, PsiInstanceOfExpression.class, PsiSwitchLabelStatementBase.class, PsiForeachPatternStatement.class, PsiForeachStatement.class); int from; int end; if (context instanceof PsiInstanceOfExpression instanceOf) { from = flow.getEndOffset(instanceOf); end = flow.getEndOffset(body); } else if (context instanceof PsiSwitchLabelStatementBase label) { PsiExpression guardExpression = label.getGuardExpression(); if (guardExpression != null) { from = flow.getStartOffset(guardExpression); } else { from = flow.getEndOffset(label); } end = flow.getEndOffset(body); } else if (context instanceof PsiForeachPatternStatement forEach) { PsiStatement body = forEach.getBody(); if (body == null) return; from = flow.getStartOffset(body); end = flow.getEndOffset(body); } else { return; } from = MathUtil.clamp(from, 0, flow.getInstructions().size()); end = MathUtil.clamp(end, from, flow.getInstructions().size()); if (!ControlFlowUtil.getWrittenVariables(flow, from, end, false).contains(variable)) { writtenVariables.remove(variable); result.add(variable); } } private Set<PsiVariable> getDeclaredVariables(PsiCodeBlock block) { final HashSet<PsiVariable> result = new HashSet<>(); PsiElement[] children = block.getChildren(); for (PsiElement child : children) { child.accept(new JavaElementVisitor() { @Override public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) { visitReferenceElement(expression); } @Override public void visitDeclarationStatement(@NotNull PsiDeclarationStatement statement) { PsiElement[] declaredElements = statement.getDeclaredElements(); for (PsiElement declaredElement : declaredElements) { if (declaredElement instanceof PsiVariable) result.add((PsiVariable)declaredElement); } } @Override public void visitForStatement(@NotNull PsiForStatement statement) { super.visitForStatement(statement); final PsiStatement initialization = statement.getInitialization(); if (!(initialization instanceof PsiDeclarationStatement declarationStatement)) { return; } final PsiElement[] declaredElements = declarationStatement.getDeclaredElements(); for (final PsiElement declaredElement : declaredElements) { if (declaredElement instanceof PsiVariable) { result.add((PsiVariable)declaredElement); } } } }); } return result; } @Override public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) { if (expression.getParent() instanceof PsiMethodCallExpression) { super.visitReferenceExpression(expression); } } }); if (body.getParent() instanceof PsiParameterListOwner methodOrLambda && REPORT_PARAMETERS && !(methodOrLambda instanceof SyntheticElement)) { // e.g. JspHolderMethod for (PsiParameter parameter : methodOrLambda.getParameterList().getParameters()) { if (parameter.getTypeElement() != null) { result.add(parameter); } } } for (Iterator<PsiVariable> iterator = result.iterator(); iterator.hasNext(); ) { final PsiVariable variable = iterator.next(); if (shouldBeIgnored(variable)) { iterator.remove(); continue; } final PsiElement parent = variable.getParent(); if (!(parent instanceof PsiDeclarationStatement declarationStatement)) { continue; } final PsiElement[] elements = declarationStatement.getDeclaredElements(); final PsiElement grandParent = parent.getParent(); if (elements.length > 1 && grandParent instanceof PsiForStatement) { iterator.remove(); // do not report when more than 1 variable declared in for loop } } for (PsiVariable writtenVariable : writtenVariables) { if (writtenVariable instanceof PsiParameter) { result.remove(writtenVariable); } } if (result.isEmpty() && problems.isEmpty()) return null; for (PsiVariable variable : result) { if (!variable.isPhysical()) continue; final PsiIdentifier nameIdentifier = variable.getNameIdentifier(); PsiElement problemElement = nameIdentifier != null ? nameIdentifier : variable; if (variable instanceof PsiParameter && !(((PsiParameter)variable).getDeclarationScope() instanceof PsiForeachStatementBase)) { problems.add(manager.createProblemDescriptor(problemElement, JavaAnalysisBundle.message("inspection.can.be.local.parameter.problem.descriptor"), myQuickFix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, onTheFly)); } else { problems.add(manager.createProblemDescriptor(problemElement, JavaAnalysisBundle.message("inspection.can.be.local.variable.problem.descriptor"), myQuickFix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, onTheFly)); } } return problems; }