PsiElementVisitor (@NotNull ProblemsHolder holder, boolean isOnTheFly) { return new JavaElementVisitor() { @Override public void visitTryStatement(@NotNull PsiTryStatement statement) { super.visitTryStatement(statement); final PsiCatchSection[] catchSections = statement.getCatchSections(); for (final PsiCatchSection section : catchSections) { checkCatchSection(section); } } private void checkCatchSection(PsiCatchSection section) { final PsiParameter parameter = section.getParameter(); if (parameter == null || parameter.isUnnamed()) return; final PsiIdentifier identifier = parameter.getNameIdentifier(); if (identifier == null) return; final String parameterName = parameter.getName(); if (PsiUtil.isIgnoredName(parameterName)) { if (!m_ignoreUsedIgnoredName && VariableAccessUtils.variableIsUsed(parameter, section)) { holder.registerProblem(identifier, InspectionGadgetsBundle.message("inspection.catch.ignores.exception.used.message")); } return; } if (parameterName.equals("cannotHappen")) { // Just ignore parameter named as 'cannotHappen' (this code style appears in OpenJDK source) return; } if ((parameterName.equals("expected") || parameterName.equals("ok")) && TestUtils.isInTestSourceContent(section)) { return; } final PsiElement catchToken = section.getFirstChild(); if (catchToken == null) return; final PsiCodeBlock block = section.getCatchBlock(); if (block == null) return; SuppressForTestsScopeFix fix = SuppressForTestsScopeFix.build(CatchMayIgnoreExceptionInspection.this, section); if (ControlFlowUtils.isEmpty(block, m_ignoreCatchBlocksWithComments, true)) { var renameFix = QuickFixFactory.getInstance().createRenameToIgnoredFix(parameter, false); AddCatchBodyFix addBodyFix = getAddBodyFix(block); holder.registerProblem(catchToken, InspectionGadgetsBundle.message("inspection.catch.ignores.exception.empty.message"), LocalQuickFix.notNullElements(renameFix, addBodyFix, fix)); } else if (!VariableAccessUtils.variableIsUsed(parameter, section)) { if (!m_ignoreNonEmptyCatchBlock && (!m_ignoreCatchBlocksWithComments || PsiTreeUtil.getChildOfType(block, PsiComment.class) == null)) { holder.registerProblem(identifier, InspectionGadgetsBundle.message("inspection.catch.ignores.exception.unused.message"), LocalQuickFix.notNullElements( QuickFixFactory.getInstance().createRenameToIgnoredFix(parameter, false), fix)); } } else { String className = mayIgnoreVMException(parameter, block); if (className != null) { String message = InspectionGadgetsBundle.message("inspection.catch.ignores.exception.vm.ignored.message", className); holder.registerProblem(catchToken, message, LocalQuickFix.notNullElements(fix)); } } } @Nullable private AddCatchBodyFix getAddBodyFix(PsiCodeBlock block) { if (ControlFlowUtils.isEmpty(block, true, true)) { try { FileTemplate template = FileTemplateManager.getInstance(holder.getProject()).getCodeTemplate(JavaTemplateUtil.TEMPLATE_CATCH_BODY); if (!StringUtil.isEmptyOrSpaces(template.getText())) { return new AddCatchBodyFix(); } } catch (IllegalStateException ignored) { } } return null; } /** * Returns class name of important VM exception (like NullPointerException) if given catch block may ignore it * * @param parameter a catch block parameter * @param block a catch block body * @return class name of important VM exception (like NullPointerException) if given catch block may ignore it */ private static @Nullable String mayIgnoreVMException(PsiParameter parameter, PsiCodeBlock block) { PsiType type = parameter.getType(); if (!type.equalsToText(CommonClassNames.JAVA_LANG_THROWABLE) && !type.equalsToText(CommonClassNames.JAVA_LANG_EXCEPTION) && !type.equalsToText(CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION) && !type.equalsToText(CommonClassNames.JAVA_LANG_ERROR)) { return null; } // Let's assume that exception is NPE or SOE with null cause and null message and see what happens during dataflow. // Will it produce any side-effect? String className = type.equalsToText(CommonClassNames.JAVA_LANG_ERROR) ? "java.lang.StackOverflowError" : CommonClassNames.JAVA_LANG_NULL_POINTER_EXCEPTION; Project project = parameter.getProject(); PsiClassType exception = JavaPsiFacade.getElementFactory(project).createTypeByFQClassName(className, parameter.getResolveScope()); PsiClass exceptionClass = exception.resolve(); if (exceptionClass == null) return null; DfaValueFactory factory = new DfaValueFactory(project); ControlFlow flow = ControlFlowAnalyzer.buildFlow(block, factory, true); if (flow == null) return null; var interpreter = new CatchDataFlowInterpreter(exception, parameter, flow); DfaMemoryState memState = new JvmDfaMemoryStateImpl(factory); DfaVariableValue stableExceptionVar = PlainDescriptor.createVariableValue(factory, new LightParameter("tmp", exception, block)); DfaVariableValue exceptionVar = PlainDescriptor.createVariableValue(factory, parameter); memState.applyCondition(exceptionVar.eq(stableExceptionVar)); memState.applyCondition(exceptionVar.cond(RelationType.IS, DfTypes.typedObject(exception, Nullability.NOT_NULL))); return interpreter.interpret(memState) == RunnerResult.OK ? className : null; } }; }