private PsiElement getStatementToInsertBefore() {
    PsiElement declarationScope =
        myVariable instanceof PsiParameter
            ? ((PsiParameter) myVariable).getDeclarationScope()
            : PsiUtil.getVariableCodeBlock(myVariable, null);
    if (declarationScope == null) return null;

    PsiElement statement = myClass;
    nextInnerClass:
    do {
      statement = PsiUtil.getEnclosingStatement(statement);

      if (statement == null || statement.getParent() == null) {
        return null;
      }
      PsiElement element = statement;
      while (element != declarationScope && !(element instanceof PsiFile)) {
        if (element instanceof PsiClass) {
          statement = statement.getParent();
          continue nextInnerClass;
        }
        element = element.getParent();
      }
      return statement;
    } while (true);
  }
  private static int getQuickFixType(PsiVariable variable) {
    PsiElement outerCodeBlock = PsiUtil.getVariableCodeBlock(variable, null);
    if (outerCodeBlock == null) return -1;
    List<PsiReferenceExpression> outerReferences = new ArrayList<PsiReferenceExpression>();
    collectReferences(outerCodeBlock, variable, outerReferences);

    int type = MAKE_FINAL;
    for (PsiReferenceExpression expression : outerReferences) {
      // if it happens that variable referenced from another inner class, make sure it can be make
      // final from there
      PsiClass innerClass =
          HighlightControlFlowUtil.getInnerClassVariableReferencedFrom(variable, expression);

      if (innerClass != null) {
        int thisType = MAKE_FINAL;
        if (writtenInside(variable, innerClass)) {
          // cannot make parameter array
          if (variable instanceof PsiParameter) return -1;
          thisType = MAKE_ARRAY;
        }
        if (thisType == MAKE_FINAL && !canBeFinal(variable, outerReferences)) {
          thisType = COPY_TO_FINAL;
        }
        type = Math.max(type, thisType);
      }
    }
    return type;
  }
  private static boolean isEffectivelyFinal(
      PsiVariable variable, PsiElement scope, PsiJavaCodeReferenceElement context) {
    boolean effectivelyFinal;
    if (variable instanceof PsiParameter) {
      effectivelyFinal =
          notAccessedForWriting(
              variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope()));
    } else {
      final ControlFlow controlFlow;
      try {
        controlFlow = getControlFlow(PsiUtil.getVariableCodeBlock(variable, context));
      } catch (AnalysisCanceledException e) {
        return true;
      }

      if (ControlFlowUtil.isVariableDefinitelyAssigned(variable, controlFlow)) {
        final Collection<ControlFlowUtil.VariableInfo> initializedTwice =
            ControlFlowUtil.getInitializedTwice(controlFlow);
        effectivelyFinal =
            !initializedTwice.contains(new ControlFlowUtil.VariableInfo(variable, null));
        if (effectivelyFinal) {
          effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(scope));
        }
      } else {
        effectivelyFinal = false;
      }
    }
    return effectivelyFinal;
  }
  private void removeVariableAndReferencingStatements(Editor editor) {
    final List<PsiElement> references = new ArrayList<>();
    final List<PsiElement> sideEffects = new ArrayList<>();
    final boolean[] canCopeWithSideEffects = {true};
    try {
      PsiElement context =
          myVariable instanceof PsiField
              ? ((PsiField) myVariable).getContainingClass()
              : PsiUtil.getVariableCodeBlock(myVariable, null);
      if (context != null) {
        RemoveUnusedVariableUtil.collectReferences(context, myVariable, references);
      }
      // do not forget to delete variable declaration
      references.add(myVariable);
      // check for side effects
      for (PsiElement element : references) {
        Boolean result =
            RemoveUnusedVariableUtil.processUsage(
                element, myVariable, sideEffects, RemoveUnusedVariableUtil.RemoveMode.CANCEL);
        if (result == null) return;
        canCopeWithSideEffects[0] &= result;
      }
    } catch (IncorrectOperationException e) {
      LOG.error(e);
    }

    final RemoveUnusedVariableUtil.RemoveMode deleteMode =
        showSideEffectsWarning(sideEffects, myVariable, editor, canCopeWithSideEffects[0]);

    ApplicationManager.getApplication()
        .runWriteAction(
            () -> {
              try {
                RemoveUnusedVariableUtil.deleteReferences(myVariable, references, deleteMode);
              } catch (IncorrectOperationException e) {
                LOG.error(e);
              }
            });
  }
  public static boolean isEffectivelyFinal(
      @NotNull PsiVariable variable,
      @NotNull PsiElement scope,
      @Nullable PsiJavaCodeReferenceElement context) {
    boolean effectivelyFinal;
    if (variable instanceof PsiParameter) {
      effectivelyFinal =
          notAccessedForWriting(
              variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope()));
    } else {
      final ControlFlow controlFlow;
      try {
        PsiElement codeBlock = PsiUtil.getVariableCodeBlock(variable, context);
        if (codeBlock == null) return true;
        controlFlow = getControlFlow(codeBlock);
      } catch (AnalysisCanceledException e) {
        return true;
      }

      final List<PsiReferenceExpression> readBeforeWriteLocals =
          ControlFlowUtil.getReadBeforeWriteLocals(controlFlow);
      for (PsiReferenceExpression expression : readBeforeWriteLocals) {
        if (expression.resolve() == variable) {
          return PsiUtil.isAccessedForReading(expression);
        }
      }

      final Collection<ControlFlowUtil.VariableInfo> initializedTwice =
          ControlFlowUtil.getInitializedTwice(controlFlow);
      effectivelyFinal =
          !initializedTwice.contains(new ControlFlowUtil.VariableInfo(variable, null));
      if (effectivelyFinal) {
        effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(scope));
      }
    }
    return effectivelyFinal;
  }
  private void makeArray(PsiVariable variable) throws IncorrectOperationException {
    PsiType type = variable.getType();

    PsiElementFactory factory = JavaPsiFacade.getInstance(myClass.getProject()).getElementFactory();
    PsiType newType = type.createArrayType();

    PsiDeclarationStatement variableDeclarationStatement;
    PsiExpression initializer = variable.getInitializer();
    if (initializer == null) {
      String expression = "[1]";
      while (type instanceof PsiArrayType) {
        expression += "[1]";
        type = ((PsiArrayType) type).getComponentType();
      }
      PsiExpression init =
          factory.createExpressionFromText("new " + type.getCanonicalText() + expression, variable);
      variableDeclarationStatement =
          factory.createVariableDeclarationStatement(variable.getName(), newType, init);
    } else {
      PsiExpression init =
          factory.createExpressionFromText("{ " + initializer.getText() + " }", variable);
      variableDeclarationStatement =
          factory.createVariableDeclarationStatement(variable.getName(), newType, init);
    }
    PsiVariable newVariable = (PsiVariable) variableDeclarationStatement.getDeclaredElements()[0];
    PsiUtil.setModifierProperty(newVariable, PsiModifier.FINAL, true);
    PsiElement newExpression =
        factory.createExpressionFromText(variable.getName() + "[0]", variable);

    PsiElement outerCodeBlock = PsiUtil.getVariableCodeBlock(variable, null);
    if (outerCodeBlock == null) return;
    List<PsiReferenceExpression> outerReferences = new ArrayList<PsiReferenceExpression>();
    collectReferences(outerCodeBlock, variable, outerReferences);
    replaceReferences(outerReferences, newExpression);
    variable.replace(newVariable);
  }
  @Nullable
  public static HighlightInfo checkVariableInitializedBeforeUsage(
      @NotNull PsiReferenceExpression expression,
      @NotNull PsiVariable variable,
      @NotNull Map<PsiElement, Collection<PsiReferenceExpression>> uninitializedVarProblems,
      @NotNull PsiFile containingFile) {
    if (variable instanceof ImplicitVariable) return null;
    if (!PsiUtil.isAccessedForReading(expression)) return null;
    int startOffset = expression.getTextRange().getStartOffset();
    final PsiElement topBlock;
    if (variable.hasInitializer()) {
      topBlock = PsiUtil.getVariableCodeBlock(variable, variable);
      if (topBlock == null) return null;
    } else {
      PsiElement scope =
          variable instanceof PsiField
              ? ((PsiField) variable).getContainingClass()
              : variable.getParent() != null ? variable.getParent().getParent() : null;
      if (scope instanceof PsiCodeBlock && scope.getParent() instanceof PsiSwitchStatement) {
        scope = PsiTreeUtil.getParentOfType(scope, PsiCodeBlock.class);
      }

      topBlock =
          FileTypeUtils.isInServerPageFile(scope) && scope instanceof PsiFile
              ? scope
              : PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope);
      if (variable instanceof PsiField) {
        // non final field already initialized with default value
        if (!variable.hasModifierProperty(PsiModifier.FINAL)) return null;
        // final field may be initialized in ctor or class initializer only
        // if we're inside non-ctr method, skip it
        if (PsiUtil.findEnclosingConstructorOrInitializer(expression) == null
            && HighlightUtil.findEnclosingFieldInitializer(expression) == null) {
          return null;
        }
        if (topBlock == null) return null;
        final PsiElement parent = topBlock.getParent();
        // access to final fields from inner classes always allowed
        if (inInnerClass(expression, ((PsiField) variable).getContainingClass(), containingFile))
          return null;
        final PsiCodeBlock block;
        final PsiClass aClass;
        if (parent instanceof PsiMethod) {
          PsiMethod constructor = (PsiMethod) parent;
          if (!containingFile
              .getManager()
              .areElementsEquivalent(
                  constructor.getContainingClass(), ((PsiField) variable).getContainingClass()))
            return null;
          // static variables already initialized in class initializers
          if (variable.hasModifierProperty(PsiModifier.STATIC)) return null;
          // as a last chance, field may be initialized in this() call
          final List<PsiMethod> redirectedConstructors =
              JavaHighlightUtil.getChainedConstructors(constructor);
          for (int j = 0;
              redirectedConstructors != null && j < redirectedConstructors.size();
              j++) {
            PsiMethod redirectedConstructor = redirectedConstructors.get(j);
            // variable must be initialized before its usage
            // ???
            // if (startOffset < redirectedConstructor.getTextRange().getStartOffset()) continue;
            PsiCodeBlock body = redirectedConstructor.getBody();
            if (body != null && variableDefinitelyAssignedIn(variable, body)) {
              return null;
            }
          }
          block = constructor.getBody();
          aClass = constructor.getContainingClass();
        } else if (parent instanceof PsiClassInitializer) {
          final PsiClassInitializer classInitializer = (PsiClassInitializer) parent;
          if (!containingFile
              .getManager()
              .areElementsEquivalent(
                  classInitializer.getContainingClass(),
                  ((PsiField) variable).getContainingClass())) return null;
          block = classInitializer.getBody();
          aClass = classInitializer.getContainingClass();
        } else {
          // field reference outside code block
          // check variable initialized before its usage
          final PsiField field = (PsiField) variable;

          aClass = field.getContainingClass();
          if (aClass == null
              || isFieldInitializedInOtherFieldInitializer(
                  aClass, field, field.hasModifierProperty(PsiModifier.STATIC))) {
            return null;
          }
          final PsiField anotherField =
              PsiTreeUtil.getTopmostParentOfType(expression, PsiField.class);
          int offset = startOffset;
          if (anotherField != null
              && anotherField.getContainingClass() == aClass
              && !field.hasModifierProperty(PsiModifier.STATIC)) {
            offset = 0;
          }
          block = null;
          // initializers will be checked later
          final PsiMethod[] constructors = aClass.getConstructors();
          for (PsiMethod constructor : constructors) {
            // variable must be initialized before its usage
            if (offset < constructor.getTextRange().getStartOffset()) continue;
            PsiCodeBlock body = constructor.getBody();
            if (body != null && variableDefinitelyAssignedIn(variable, body)) {
              return null;
            }
            // as a last chance, field may be initialized in this() call
            final List<PsiMethod> redirectedConstructors =
                JavaHighlightUtil.getChainedConstructors(constructor);
            for (int j = 0;
                redirectedConstructors != null && j < redirectedConstructors.size();
                j++) {
              PsiMethod redirectedConstructor = redirectedConstructors.get(j);
              // variable must be initialized before its usage
              if (offset < redirectedConstructor.getTextRange().getStartOffset()) continue;
              PsiCodeBlock redirectedBody = redirectedConstructor.getBody();
              if (redirectedBody != null
                  && variableDefinitelyAssignedIn(variable, redirectedBody)) {
                return null;
              }
            }
          }
        }

        if (aClass != null) {
          // field may be initialized in class initializer
          final PsiClassInitializer[] initializers = aClass.getInitializers();
          for (PsiClassInitializer initializer : initializers) {
            PsiCodeBlock body = initializer.getBody();
            if (body == block) break;
            // variable referenced in initializer must be initialized in initializer preceding
            // assignment
            // variable referenced in field initializer or in class initializer
            boolean shouldCheckInitializerOrder =
                block == null || block.getParent() instanceof PsiClassInitializer;
            if (shouldCheckInitializerOrder
                && startOffset < initializer.getTextRange().getStartOffset()) continue;
            if (initializer.hasModifierProperty(PsiModifier.STATIC)
                == variable.hasModifierProperty(PsiModifier.STATIC)) {
              if (variableDefinitelyAssignedIn(variable, body)) return null;
            }
          }
        }
      }
    }
    if (topBlock == null) return null;
    Collection<PsiReferenceExpression> codeBlockProblems = uninitializedVarProblems.get(topBlock);
    if (codeBlockProblems == null) {
      try {
        final ControlFlow controlFlow = getControlFlow(topBlock);
        codeBlockProblems = ControlFlowUtil.getReadBeforeWriteLocals(controlFlow);
      } catch (AnalysisCanceledException | IndexNotReadyException e) {
        codeBlockProblems = Collections.emptyList();
      }
      uninitializedVarProblems.put(topBlock, codeBlockProblems);
    }
    if (codeBlockProblems.contains(expression)) {
      final String name = expression.getElement().getText();
      String description = JavaErrorMessages.message("variable.not.initialized", name);
      HighlightInfo highlightInfo =
          HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
              .range(expression)
              .descriptionAndTooltip(description)
              .create();
      QuickFixAction.registerQuickFixAction(
          highlightInfo, QUICK_FIX_FACTORY.createAddVariableInitializerFix(variable));
      if (variable instanceof PsiField) {
        QuickFixAction.registerQuickFixAction(
            highlightInfo,
            QUICK_FIX_FACTORY.createModifierListFix(variable, PsiModifier.FINAL, false, false));
      }
      return highlightInfo;
    }

    return null;
  }