@Override
  public @NotNull PsiElementVisitor buildVisitor(@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 = RenameToIgnoredFix.createRenameToIgnoreFix(parameter, false);
          AddCatchBodyFix addBodyFix = getAddBodyFix(block);
          holder.problem(catchToken, InspectionGadgetsBundle.message("inspection.catch.ignores.exception.empty.message"))
              .fix(renameFix).maybeFix(addBodyFix).maybeFix(fix).register();
        }
        else if (!VariableAccessUtils.variableIsUsed(parameter, section)) {
          if (!m_ignoreNonEmptyCatchBlock &&
              (!m_ignoreCatchBlocksWithComments || PsiTreeUtil.getChildOfType(block, PsiComment.class) == null)) {
            holder.problem(identifier, InspectionGadgetsBundle.message("inspection.catch.ignores.exception.unused.message"))
                .fix(RenameToIgnoredFix.createRenameToIgnoreFix(parameter, false)).maybeFix(fix).register();
          }
        }
        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));
          }
        }
      }

      private @Nullable 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;
      }
    };
  }
@Override
  public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly)