private static void checkCodeBlock(
     final PsiCodeBlock body, final Set<PsiField> candidates, Set<PsiField> usedFields) {
   try {
     final ControlFlow controlFlow =
         ControlFlowFactory.getInstance(body.getProject())
             .getControlFlow(body, AllVariablesControlFlowPolicy.getInstance());
     final List<PsiVariable> usedVars =
         ControlFlowUtil.getUsedVariables(controlFlow, 0, controlFlow.getSize());
     for (PsiVariable usedVariable : usedVars) {
       if (usedVariable instanceof PsiField) {
         final PsiField usedField = (PsiField) usedVariable;
         if (!usedFields.add(usedField)) {
           candidates.remove(usedField); // used in more than one code block
         }
       }
     }
     final List<PsiReferenceExpression> readBeforeWrites =
         ControlFlowUtil.getReadBeforeWrite(controlFlow);
     for (final PsiReferenceExpression readBeforeWrite : readBeforeWrites) {
       final PsiElement resolved = readBeforeWrite.resolve();
       if (resolved instanceof PsiField) {
         final PsiField field = (PsiField) resolved;
         PsiElement parent = body.getParent();
         if (!(parent instanceof PsiMethod)
             || !((PsiMethod) parent).isConstructor()
             || field.getInitializer() == null
             || field.hasModifierProperty(PsiModifier.STATIC)) {
           candidates.remove(field);
         }
       }
     }
   } catch (AnalysisCanceledException e) {
     candidates.clear();
   }
 }
  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;
  }
 @Override
 public boolean isValueCompatible() {
   final PsiElement body = getBody();
   if (body != null) {
     try {
       final ControlFlow controlFlow =
           ControlFlowFactory.getInstance(getProject())
               .getControlFlow(
                   body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
       if (ControlFlowUtil.findExitPointsAndStatements(
               controlFlow,
               0,
               controlFlow.getSize(),
               new IntArrayList(),
               PsiReturnStatement.class,
               PsiThrowStatement.class)
           .isEmpty()) {
         return false;
       }
     } catch (AnalysisCanceledException e) {
       return true;
     }
   }
   return true;
 }
  @Nullable
  public static HighlightInfo checkMissingReturnStatement(PsiCodeBlock body, PsiType returnType) {
    if (body == null
        || returnType == null
        || PsiType.VOID.equals(returnType.getDeepComponentType())) {
      return null;
    }

    // do not compute constant expressions for if() statement condition
    // see JLS 14.20 Unreachable Statements
    try {
      ControlFlow controlFlow = getControlFlowNoConstantEvaluate(body);
      if (!ControlFlowUtil.returnPresent(controlFlow)) {
        PsiJavaToken rBrace = body.getRBrace();
        PsiElement context = rBrace == null ? body.getLastChild() : rBrace;
        String message = JavaErrorMessages.message("missing.return.statement");
        HighlightInfo info =
            HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
                .range(context)
                .descriptionAndTooltip(message)
                .create();
        PsiElement parent = body.getParent();
        if (parent instanceof PsiMethod) {
          PsiMethod method = (PsiMethod) parent;
          QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddReturnFix(method));
          QuickFixAction.registerQuickFixAction(
              info, QUICK_FIX_FACTORY.createMethodReturnFix(method, PsiType.VOID, true));
        }
        return info;
      }
    } catch (AnalysisCanceledException ignored) {
    }

    return null;
  }
Exemple #5
0
    public PsiReturnStatement addReturnForMethod(final PsiFile file, final PsiMethod method) {
      final PsiModifierList modifiers = method.getModifierList();
      if (modifiers.hasModifierProperty(PsiModifier.ABSTRACT) || method.getBody() == null) {
        return null;
      }

      try {
        final ConvertReturnStatementsVisitor visitor =
            new ConvertReturnStatementsVisitor(factory, method, myTargetType);

        ControlFlow controlFlow;
        try {
          controlFlow = HighlightControlFlowUtil.getControlFlowNoConstantEvaluate(method.getBody());
        } catch (AnalysisCanceledException e) {
          return null; // must be an error
        }
        PsiReturnStatement returnStatement;
        if (controlFlow != null && ControlFlowUtil.processReturns(controlFlow, visitor)) {
          // extra return statement not needed
          // get latest modified return statement and select...
          returnStatement = visitor.getLatestReturn();
        } else {
          returnStatement = visitor.createReturnInLastStatement();
        }
        if (method.getContainingFile() != file) {
          UndoUtil.markPsiFileForUndo(file);
        }
        return returnStatement;
      } catch (IncorrectOperationException e) {
        LOG.error(e);
      }

      return null;
    }
 private static boolean variableDefinitelyNotAssignedIn(PsiVariable variable, PsiElement context) {
   try {
     ControlFlow controlFlow = getControlFlow(context);
     return ControlFlowUtil.isVariableDefinitelyNotAssigned(variable, controlFlow);
   } catch (AnalysisCanceledException e) {
     return false;
   }
 }
  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;
  }
  public Collection<PsiStatement> prepareExitStatements(
      final @NotNull PsiElement[] elements, final @NotNull PsiElement enclosingCodeFragment)
      throws ExitStatementsNotSameException {
    myExitPoints = new IntArrayList();
    myExitStatements =
        ControlFlowUtil.findExitPointsAndStatements(
            myControlFlow,
            myFlowStart,
            myFlowEnd,
            myExitPoints,
            ControlFlowUtil.DEFAULT_EXIT_STATEMENTS_CLASSES);
    if (ControlFlowUtil.hasObservableThrowExitPoints(
        myControlFlow, myFlowStart, myFlowEnd, elements, enclosingCodeFragment)) {
      throw new ExitStatementsNotSameException();
    }
    if (LOG.isDebugEnabled()) {
      LOG.debug("exit points:");
      for (int i = 0; i < myExitPoints.size(); i++) {
        LOG.debug("  " + myExitPoints.get(i));
      }
      LOG.debug("exit statements:");
      for (PsiStatement exitStatement : myExitStatements) {
        LOG.debug("  " + exitStatement);
      }
    }
    if (myExitPoints.isEmpty()) {
      // if the fragment never exits assume as if it exits in the end
      myExitPoints.add(myControlFlow.getEndOffset(elements[elements.length - 1]));
    }

    if (myExitPoints.size() != 1) {
      myGenerateConditionalExit = true;
      areExitStatementsTheSame();
    }
    return myExitStatements;
  }
 @NotNull
 private static Collection<ControlFlowUtil.VariableInfo> getFinalVariableProblemsInBlock(
     @NotNull Map<PsiElement, Collection<ControlFlowUtil.VariableInfo>> finalVarProblems,
     @NotNull PsiElement codeBlock) {
   Collection<ControlFlowUtil.VariableInfo> codeBlockProblems = finalVarProblems.get(codeBlock);
   if (codeBlockProblems == null) {
     try {
       final ControlFlow controlFlow = getControlFlowNoConstantEvaluate(codeBlock);
       codeBlockProblems = ControlFlowUtil.getInitializedTwice(controlFlow);
     } catch (AnalysisCanceledException e) {
       codeBlockProblems = Collections.emptyList();
     }
     finalVarProblems.put(codeBlock, codeBlockProblems);
   }
   return codeBlockProblems;
 }
  @NotNull
  public PsiVariable[] getOutputVariables(boolean collectVariablesAtExitPoints) {
    PsiVariable[] myOutputVariables =
        ControlFlowUtil.getOutputVariables(
            myControlFlow, myFlowStart, myFlowEnd, myExitPoints.toArray());
    if (collectVariablesAtExitPoints) {
      // variables declared in selected block used in return statements are to be considered output
      // variables when extracting guard methods
      final Set<PsiVariable> outputVariables = new HashSet<>(Arrays.asList(myOutputVariables));
      for (PsiStatement statement : myExitStatements) {
        statement.accept(
            new JavaRecursiveElementVisitor() {

              @Override
              public void visitReferenceExpression(PsiReferenceExpression expression) {
                super.visitReferenceExpression(expression);
                final PsiElement resolved = expression.resolve();
                if (resolved instanceof PsiVariable) {
                  final PsiVariable variable = (PsiVariable) resolved;
                  if (isWrittenInside(variable)) {
                    outputVariables.add(variable);
                  }
                }
              }

              private boolean isWrittenInside(final PsiVariable variable) {
                final List<Instruction> instructions = myControlFlow.getInstructions();
                for (int i = myFlowStart; i < myFlowEnd; i++) {
                  Instruction instruction = instructions.get(i);
                  if (instruction instanceof WriteVariableInstruction
                      && variable.equals(((WriteVariableInstruction) instruction).variable)) {
                    return true;
                  }
                }

                return false;
              }
            });
      }

      myOutputVariables = outputVariables.toArray(new PsiVariable[outputVariables.size()]);
    }
    Arrays.sort(myOutputVariables, PsiUtil.BY_POSITION);
    return myOutputVariables;
  }
 @Override
 public boolean isVoidCompatible() {
   final PsiElement body = getBody();
   if (body != null) {
     try {
       ControlFlow controlFlow =
           ControlFlowFactory.getInstance(getProject())
               .getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance());
       int startOffset = controlFlow.getStartOffset(body);
       int endOffset = controlFlow.getEndOffset(body);
       return startOffset != -1
           && endOffset != -1
           && !ControlFlowUtil.canCompleteNormally(controlFlow, startOffset, endOffset);
     } catch (AnalysisCanceledException e) {
       return true;
     }
   }
   return true;
 }
 public static HighlightInfo checkUnreachableStatement(PsiCodeBlock codeBlock) {
   if (codeBlock == null) return null;
   // do not compute constant expressions for if() statement condition
   // see JLS 14.20 Unreachable Statements
   try {
     final ControlFlow controlFlow = getControlFlowNoConstantEvaluate(codeBlock);
     final PsiElement unreachableStatement = ControlFlowUtil.getUnreachableStatement(controlFlow);
     if (unreachableStatement != null) {
       String description = JavaErrorMessages.message("unreachable.statement");
       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
           .range(unreachableStatement)
           .descriptionAndTooltip(description)
           .create();
     }
   } catch (AnalysisCanceledException e) {
     // incomplete code
   } catch (IndexNotReadyException ignored) {
   }
   return null;
 }
 @Nullable
 static HighlightInfo checkFinalVariableInitializedInLoop(
     @NotNull PsiReferenceExpression expression, @NotNull PsiElement resolved) {
   if (ControlFlowUtil.isVariableAssignedInLoop(expression, resolved)) {
     String description =
         JavaErrorMessages.message(
             "variable.assigned.in.loop", ((PsiVariable) resolved).getName());
     final HighlightInfo highlightInfo =
         HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
             .range(expression)
             .descriptionAndTooltip(description)
             .create();
     QuickFixAction.registerQuickFixAction(
         highlightInfo,
         QUICK_FIX_FACTORY.createModifierListFix(
             (PsiVariable) resolved, PsiModifier.FINAL, false, false));
     return highlightInfo;
   }
   return null;
 }
 @Nullable
 static HighlightInfo checkInitializerCompleteNormally(@NotNull PsiClassInitializer initializer) {
   final PsiCodeBlock body = initializer.getBody();
   // unhandled exceptions already reported
   try {
     final ControlFlow controlFlow = getControlFlowNoConstantEvaluate(body);
     final int completionReasons =
         ControlFlowUtil.getCompletionReasons(controlFlow, 0, controlFlow.getSize());
     if (!BitUtil.isSet(completionReasons, ControlFlowUtil.NORMAL_COMPLETION_REASON)) {
       String description =
           JavaErrorMessages.message("initializer.must.be.able.to.complete.normally");
       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
           .range(body)
           .descriptionAndTooltip(description)
           .create();
     }
   } catch (AnalysisCanceledException e) {
     // incomplete code
   }
   return null;
 }
 private static boolean canBeFinal(PsiVariable variable, List<PsiReferenceExpression> references) {
   // if there is at least one assignment to this variable, it cannot be final
   Map<PsiElement, Collection<PsiReferenceExpression>> uninitializedVarProblems =
       new THashMap<PsiElement, Collection<PsiReferenceExpression>>();
   Map<PsiElement, Collection<ControlFlowUtil.VariableInfo>> finalVarProblems =
       new THashMap<PsiElement, Collection<ControlFlowUtil.VariableInfo>>();
   for (PsiReferenceExpression expression : references) {
     if (ControlFlowUtil.isVariableAssignedInLoop(expression, variable)) return false;
     HighlightInfo highlightInfo =
         HighlightControlFlowUtil.checkVariableInitializedBeforeUsage(
             expression, variable, uninitializedVarProblems);
     if (highlightInfo != null) return false;
     highlightInfo =
         HighlightControlFlowUtil.checkFinalVariableMightAlreadyHaveBeenAssignedTo(
             variable, expression, finalVarProblems);
     if (highlightInfo != null) return false;
     if (variable instanceof PsiParameter && PsiUtil.isAccessedForWriting(expression))
       return false;
   }
   return true;
 }
 static HighlightInfo checkUnreachableStatement(@Nullable PsiCodeBlock codeBlock) {
   if (codeBlock == null) return null;
   // do not compute constant expressions for if() statement condition
   // see JLS 14.20 Unreachable Statements
   try {
     AllVariablesControlFlowPolicy policy = AllVariablesControlFlowPolicy.getInstance();
     final ControlFlow controlFlow =
         ControlFlowFactory.getInstance(codeBlock.getProject())
             .getControlFlow(codeBlock, policy, false, false);
     final PsiElement unreachableStatement = ControlFlowUtil.getUnreachableStatement(controlFlow);
     if (unreachableStatement != null) {
       String description = JavaErrorMessages.message("unreachable.statement");
       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
           .range(unreachableStatement)
           .descriptionAndTooltip(description)
           .create();
     }
   } catch (AnalysisCanceledException | IndexNotReadyException e) {
     // incomplete code
   }
   return null;
 }
 public List<PsiVariable> getInputVariables(
     final PsiElement codeFragment, PsiElement[] elements, PsiVariable[] outputVariables) {
   final List<PsiVariable> inputVariables =
       ControlFlowUtil.getInputVariables(myControlFlow, myFlowStart, myFlowEnd);
   List<PsiVariable> myInputVariables;
   if (skipVariablesFromExitStatements(outputVariables)) {
     List<PsiVariable> inputVariableList = new ArrayList<>(inputVariables);
     removeParametersUsedInExitsOnly(codeFragment, inputVariableList);
     myInputVariables = inputVariableList;
   } else {
     List<PsiVariable> inputVariableList = new ArrayList<>(inputVariables);
     for (Iterator<PsiVariable> iterator = inputVariableList.iterator(); iterator.hasNext(); ) {
       PsiVariable variable = iterator.next();
       for (PsiElement element : elements) {
         if (PsiTreeUtil.isAncestor(element, variable, false)) {
           iterator.remove();
           break;
         }
       }
     }
     myInputVariables = inputVariableList;
   }
   // varargs variables go last, otherwise order is induced by original ordering
   Collections.sort(
       myInputVariables,
       (v1, v2) -> {
         if (v1.getType() instanceof PsiEllipsisType) {
           return 1;
         }
         if (v2.getType() instanceof PsiEllipsisType) {
           return -1;
         }
         return v1.getTextOffset() - v2.getTextOffset();
       });
   return myInputVariables;
 }
 private static void findSubmemberHidesFieldCollisions(
     final PsiField field, final String newName, final List<UsageInfo> result) {
   if (field.getContainingClass() == null) return;
   if (field.hasModifierProperty(PsiModifier.PRIVATE)) return;
   final PsiClass containingClass = field.getContainingClass();
   Collection<PsiClass> inheritors = ClassInheritorsSearch.search(containingClass).findAll();
   for (PsiClass inheritor : inheritors) {
     PsiField conflictingField = inheritor.findFieldByName(newName, false);
     if (conflictingField != null) {
       result.add(new SubmemberHidesMemberUsageInfo(conflictingField, field));
     } else { // local class
       final PsiMember member = PsiTreeUtil.getParentOfType(inheritor, PsiMember.class);
       if (member != null) {
         final ArrayList<PsiVariable> variables = new ArrayList<>();
         ControlFlowUtil.collectOuterLocals(variables, inheritor, inheritor, member);
         for (PsiVariable variable : variables) {
           if (newName.equals(variable.getName())) {
             result.add(new FieldHidesLocalUsageInfo(variable, field));
           }
         }
       }
     }
   }
 }
 public Collection<ControlFlowUtil.VariableInfo> getInitializedTwice(int start) {
   return ControlFlowUtil.getInitializedTwice(myControlFlow, start, myControlFlow.getSize());
 }
 public List<PsiVariable> getUsedVariables(int start, int end) {
   return ControlFlowUtil.getUsedVariables(myControlFlow, start, end);
 }
 public boolean isReturnPresentBetween() {
   return ControlFlowUtil.returnPresentBetween(myControlFlow, myFlowStart, myFlowEnd);
 }
  @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;
  }
  @Override
  public void onReferencesBuild(RefElement refElement) {
    if (refElement instanceof RefClass) {
      final PsiClass psiClass = (PsiClass) refElement.getElement();
      if (psiClass != null) {

        if (refElement.isEntry()) {
          ((RefClassImpl) refElement).setFlag(false, CAN_BE_FINAL_MASK);
        }

        PsiMethod[] psiMethods = psiClass.getMethods();
        PsiField[] psiFields = psiClass.getFields();

        HashSet<PsiVariable> allFields = new HashSet<PsiVariable>();
        ContainerUtil.addAll(allFields, psiFields);
        ArrayList<PsiVariable> instanceInitializerInitializedFields = new ArrayList<PsiVariable>();
        boolean hasInitializers = false;
        for (PsiClassInitializer initializer : psiClass.getInitializers()) {
          PsiCodeBlock body = initializer.getBody();
          hasInitializers = true;
          ControlFlow flow;
          try {
            flow =
                ControlFlowFactory.getInstance(body.getProject())
                    .getControlFlow(
                        body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
          } catch (AnalysisCanceledException e) {
            flow = ControlFlow.EMPTY;
          }
          Collection<PsiVariable> writtenVariables = new ArrayList<PsiVariable>();
          ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false, writtenVariables);
          for (PsiVariable psiVariable : writtenVariables) {
            if (allFields.contains(psiVariable)) {
              if (instanceInitializerInitializedFields.contains(psiVariable)) {
                allFields.remove(psiVariable);
                instanceInitializerInitializedFields.remove(psiVariable);
              } else {
                instanceInitializerInitializedFields.add(psiVariable);
              }
            }
          }
          for (PsiVariable psiVariable : writtenVariables) {
            if (!instanceInitializerInitializedFields.contains(psiVariable)) {
              allFields.remove(psiVariable);
            }
          }
        }

        for (PsiMethod psiMethod : psiMethods) {
          if (psiMethod.isConstructor()) {
            PsiCodeBlock body = psiMethod.getBody();
            if (body != null) {
              hasInitializers = true;
              ControlFlow flow;
              try {
                flow =
                    ControlFlowFactory.getInstance(body.getProject())
                        .getControlFlow(
                            body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
              } catch (AnalysisCanceledException e) {
                flow = ControlFlow.EMPTY;
              }

              Collection<PsiVariable> writtenVariables =
                  ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false);
              for (PsiVariable psiVariable : writtenVariables) {
                if (instanceInitializerInitializedFields.contains(psiVariable)) {
                  allFields.remove(psiVariable);
                  instanceInitializerInitializedFields.remove(psiVariable);
                }
              }
              List<PsiMethod> redirectedConstructors =
                  HighlightControlFlowUtil.getChainedConstructors(psiMethod);
              if (redirectedConstructors == null || redirectedConstructors.isEmpty()) {
                List<PsiVariable> ssaVariables = ControlFlowUtil.getSSAVariables(flow);
                ArrayList<PsiVariable> good = new ArrayList<PsiVariable>(ssaVariables);
                good.addAll(instanceInitializerInitializedFields);
                allFields.retainAll(good);
              } else {
                allFields.removeAll(writtenVariables);
              }
            }
          }
        }

        for (PsiField psiField : psiFields) {
          if ((!hasInitializers || !allFields.contains(psiField))
              && psiField.getInitializer() == null) {
            final RefFieldImpl refField = (RefFieldImpl) myManager.getReference(psiField);
            if (refField != null) {
              refField.setFlag(false, CAN_BE_FINAL_MASK);
            }
          }
        }
      }
    } else if (refElement instanceof RefMethod) {
      final RefMethod refMethod = (RefMethod) refElement;
      if (refMethod.isEntry()) {
        ((RefMethodImpl) refMethod).setFlag(false, CAN_BE_FINAL_MASK);
      }
    }
  }
  @Nullable
  public static HighlightInfo checkFinalVariableMightAlreadyHaveBeenAssignedTo(
      @NotNull PsiVariable variable,
      @NotNull PsiReferenceExpression expression,
      @NotNull Map<PsiElement, Collection<ControlFlowUtil.VariableInfo>> finalVarProblems) {
    if (!PsiUtil.isAccessedForWriting(expression)) return null;

    final PsiElement scope =
        variable instanceof PsiField
            ? variable.getParent()
            : variable.getParent() == null ? null : variable.getParent().getParent();
    PsiElement codeBlock = PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope);
    if (codeBlock == null) return null;
    Collection<ControlFlowUtil.VariableInfo> codeBlockProblems =
        getFinalVariableProblemsInBlock(finalVarProblems, codeBlock);

    boolean alreadyAssigned = false;
    for (ControlFlowUtil.VariableInfo variableInfo : codeBlockProblems) {
      if (variableInfo.expression == expression) {
        alreadyAssigned = true;
        break;
      }
    }

    if (!alreadyAssigned) {
      if (!(variable instanceof PsiField)) return null;
      final PsiField field = (PsiField) variable;
      final PsiClass aClass = field.getContainingClass();
      if (aClass == null) return null;
      // field can get assigned in other field initializers
      final PsiField[] fields = aClass.getFields();
      boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC);
      for (PsiField psiField : fields) {
        PsiExpression initializer = psiField.getInitializer();
        if (psiField != field
            && psiField.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic
            && initializer != null
            && initializer != codeBlock
            && !variableDefinitelyNotAssignedIn(field, initializer)) {
          alreadyAssigned = true;
          break;
        }
      }

      if (!alreadyAssigned) {
        // field can get assigned in class initializers
        final PsiMember enclosingConstructorOrInitializer =
            PsiUtil.findEnclosingConstructorOrInitializer(expression);
        if (enclosingConstructorOrInitializer == null
            || !aClass
                .getManager()
                .areElementsEquivalent(
                    enclosingConstructorOrInitializer.getContainingClass(), aClass)) {
          return null;
        }
        final PsiClassInitializer[] initializers = aClass.getInitializers();
        for (PsiClassInitializer initializer : initializers) {
          if (initializer.hasModifierProperty(PsiModifier.STATIC)
              == field.hasModifierProperty(PsiModifier.STATIC)) {
            final PsiCodeBlock body = initializer.getBody();
            if (body == codeBlock) return null;
            try {
              final ControlFlow controlFlow = getControlFlow(body);
              if (!ControlFlowUtil.isVariableDefinitelyNotAssigned(field, controlFlow)) {
                alreadyAssigned = true;
                break;
              }
            } catch (AnalysisCanceledException e) {
              // incomplete code
              return null;
            }
          }
        }
      }

      if (!alreadyAssigned && !field.hasModifierProperty(PsiModifier.STATIC)) {
        // then check if instance field already assigned in other constructor
        final PsiMethod ctr =
            codeBlock.getParent() instanceof PsiMethod ? (PsiMethod) codeBlock.getParent() : null;
        // assignment to final field in several constructors threatens us only if these are linked
        // (there is this() call in the beginning)
        final List<PsiMethod> redirectedConstructors =
            ctr != null && ctr.isConstructor()
                ? JavaHighlightUtil.getChainedConstructors(ctr)
                : null;
        for (int j = 0; redirectedConstructors != null && j < redirectedConstructors.size(); j++) {
          PsiMethod redirectedConstructor = redirectedConstructors.get(j);
          PsiCodeBlock body = redirectedConstructor.getBody();
          if (body != null && variableDefinitelyAssignedIn(variable, body)) {
            alreadyAssigned = true;
            break;
          }
        }
      }
    }

    if (alreadyAssigned) {
      String description =
          JavaErrorMessages.message("variable.already.assigned", variable.getName());
      final HighlightInfo highlightInfo =
          HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
              .range(expression)
              .descriptionAndTooltip(description)
              .create();
      QuickFixAction.registerQuickFixAction(
          highlightInfo,
          QUICK_FIX_FACTORY.createModifierListFix(variable, PsiModifier.FINAL, false, false));
      QuickFixAction.registerQuickFixAction(
          highlightInfo, QUICK_FIX_FACTORY.createDeferFinalAssignmentFix(variable, expression));
      return highlightInfo;
    }

    return null;
  }