예제 #1
0
 @Override
 protected LocalQuickFix createRemoveAssignmentFix(PsiReferenceExpression expression) {
   final PsiElement parent = expression.getParent();
   if (parent instanceof PsiVariable) {
     final PsiVariable variable = (PsiVariable) parent;
     if (variable.hasModifierProperty(PsiModifier.FINAL)) {
       return null;
     }
   }
   return new RemoveSillyAssignmentFix();
 }
  @Nullable
  static HighlightInfo checkVariableMustBeFinal(
      PsiVariable variable,
      PsiJavaCodeReferenceElement context,
      @NotNull LanguageLevel languageLevel) {
    if (variable.hasModifierProperty(PsiModifier.FINAL)) return null;
    final PsiClass innerClass = getInnerClassVariableReferencedFrom(variable, context);
    if (innerClass != null) {
      if (variable instanceof PsiParameter) {
        final PsiElement parent = variable.getParent();
        if (parent instanceof PsiParameterList
            && parent.getParent() instanceof PsiLambdaExpression
            && notAccessedForWriting(
                variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope()))) {
          return null;
        }
      }
      if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)
          && isEffectivelyFinal(variable, innerClass, context)) {
        return null;
      }
      final String description =
          JavaErrorMessages.message("variable.must.be.final", context.getText());

      final HighlightInfo highlightInfo =
          HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
              .range(context)
              .descriptionAndTooltip(description)
              .create();
      QuickFixAction.registerQuickFixAction(
          highlightInfo,
          QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass));
      return highlightInfo;
    }
    final PsiLambdaExpression lambdaExpression =
        PsiTreeUtil.getParentOfType(context, PsiLambdaExpression.class);
    if (lambdaExpression != null && !PsiTreeUtil.isAncestor(lambdaExpression, variable, true)) {
      final PsiElement parent = variable.getParent();
      if (parent instanceof PsiParameterList && parent.getParent() == lambdaExpression) {
        return null;
      }
      if (!isEffectivelyFinal(variable, lambdaExpression, context)) {
        return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
            .range(context)
            .descriptionAndTooltip("Variable used in lambda expression should be effectively final")
            .create();
      }
    }
    return null;
  }
  @Nullable
  static HighlightInfo checkVariableMustBeFinal(
      @NotNull PsiVariable variable,
      @NotNull PsiJavaCodeReferenceElement context,
      @NotNull LanguageLevel languageLevel) {
    if (variable.hasModifierProperty(PsiModifier.FINAL)) return null;
    final PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, context);
    if (innerClass instanceof PsiClass) {
      if (variable instanceof PsiParameter) {
        final PsiElement parent = variable.getParent();
        if (parent instanceof PsiParameterList
            && parent.getParent() instanceof PsiLambdaExpression
            && notAccessedForWriting(
                variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope()))) {
          return null;
        }
      }
      final boolean isToBeEffectivelyFinal = languageLevel.isAtLeast(LanguageLevel.JDK_1_8);
      if (isToBeEffectivelyFinal && isEffectivelyFinal(variable, innerClass, context)) {
        return null;
      }
      final String description =
          JavaErrorMessages.message(
              isToBeEffectivelyFinal
                  ? "variable.must.be.final.or.effectively.final"
                  : "variable.must.be.final",
              context.getText());

      final HighlightInfo highlightInfo =
          HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
              .range(context)
              .descriptionAndTooltip(description)
              .create();
      QuickFixAction.registerQuickFixAction(
          highlightInfo,
          QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass));
      return highlightInfo;
    }
    return checkWriteToFinalInsideLambda(variable, context);
  }
  /**
   * @return true if both expressions resolve to the same variable/class or field in the same
   *     instance of the class
   */
  private static boolean sameInstanceReferences(
      @Nullable PsiJavaCodeReferenceElement lRef,
      @Nullable PsiJavaCodeReferenceElement rRef,
      PsiManager manager) {
    if (lRef == null && rRef == null) return true;
    if (lRef == null || rRef == null) return false;
    PsiElement lResolved = lRef.resolve();
    PsiElement rResolved = rRef.resolve();
    if (!manager.areElementsEquivalent(lResolved, rResolved)) return false;
    if (!(lResolved instanceof PsiVariable)) return false;
    final PsiVariable variable = (PsiVariable) lResolved;
    if (variable.hasModifierProperty(PsiModifier.STATIC)) return true;

    final PsiElement lQualifier = lRef.getQualifier();
    final PsiElement rQualifier = rRef.getQualifier();
    if (lQualifier instanceof PsiJavaCodeReferenceElement
        && rQualifier instanceof PsiJavaCodeReferenceElement) {
      return sameInstanceReferences(
          (PsiJavaCodeReferenceElement) lQualifier,
          (PsiJavaCodeReferenceElement) rQualifier,
          manager);
    }

    if (Comparing.equal(lQualifier, rQualifier)) return true;
    boolean lThis =
        lQualifier == null
            || lQualifier instanceof PsiThisExpression
            || lQualifier instanceof PsiSuperExpression;
    boolean rThis =
        rQualifier == null
            || rQualifier instanceof PsiThisExpression
            || rQualifier instanceof PsiSuperExpression;
    if (lThis && rThis) {
      final PsiJavaCodeReferenceElement llQualifier = getQualifier(lQualifier);
      final PsiJavaCodeReferenceElement rrQualifier = getQualifier(rQualifier);
      return sameInstanceReferences(llQualifier, rrQualifier, manager);
    }
    return false;
  }
 @Nullable
 private String createListIterationText(@NotNull PsiForStatement forStatement) {
   final PsiBinaryExpression condition =
       (PsiBinaryExpression) ParenthesesUtils.stripParentheses(forStatement.getCondition());
   if (condition == null) {
     return null;
   }
   final PsiExpression lhs = ParenthesesUtils.stripParentheses(condition.getLOperand());
   if (lhs == null) {
     return null;
   }
   final PsiExpression rhs = ParenthesesUtils.stripParentheses(condition.getROperand());
   if (rhs == null) {
     return null;
   }
   final IElementType tokenType = condition.getOperationTokenType();
   final String indexName;
   PsiExpression collectionSize;
   if (JavaTokenType.LT.equals(tokenType)) {
     indexName = lhs.getText();
     collectionSize = rhs;
   } else if (JavaTokenType.GT.equals(tokenType)) {
     indexName = rhs.getText();
     collectionSize = lhs;
   } else {
     return null;
   }
   if (collectionSize instanceof PsiReferenceExpression) {
     final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) collectionSize;
     final PsiElement target = referenceExpression.resolve();
     if (target instanceof PsiVariable) {
       final PsiVariable variable = (PsiVariable) target;
       collectionSize = ParenthesesUtils.stripParentheses(variable.getInitializer());
     }
   }
   if (!(collectionSize instanceof PsiMethodCallExpression)) {
     return null;
   }
   final PsiMethodCallExpression methodCallExpression =
       (PsiMethodCallExpression) ParenthesesUtils.stripParentheses(collectionSize);
   if (methodCallExpression == null) {
     return null;
   }
   final PsiReferenceExpression listLengthExpression =
       methodCallExpression.getMethodExpression();
   final PsiExpression qualifier =
       ParenthesesUtils.stripParentheses(listLengthExpression.getQualifierExpression());
   final PsiReferenceExpression listReference;
   if (qualifier instanceof PsiReferenceExpression) {
     listReference = (PsiReferenceExpression) qualifier;
   } else {
     listReference = null;
   }
   PsiType parameterType;
   if (listReference == null) {
     parameterType = extractListTypeFromContainingClass(forStatement);
   } else {
     final PsiType type = listReference.getType();
     if (type == null) {
       return null;
     }
     parameterType = extractContentTypeFromType(type);
   }
   if (parameterType == null) {
     parameterType = TypeUtils.getObjectType(forStatement);
   }
   final String typeString = parameterType.getCanonicalText();
   final PsiVariable listVariable;
   if (listReference == null) {
     listVariable = null;
   } else {
     final PsiElement target = listReference.resolve();
     if (!(target instanceof PsiVariable)) {
       return null;
     }
     listVariable = (PsiVariable) target;
   }
   final PsiStatement body = forStatement.getBody();
   final PsiStatement firstStatement = getFirstStatement(body);
   final boolean isDeclaration =
       isListElementDeclaration(firstStatement, listVariable, indexName, parameterType);
   final String contentVariableName;
   @NonNls final String finalString;
   final PsiStatement statementToSkip;
   if (isDeclaration) {
     final PsiDeclarationStatement declarationStatement =
         (PsiDeclarationStatement) firstStatement;
     assert declarationStatement != null;
     final PsiElement[] declaredElements = declarationStatement.getDeclaredElements();
     final PsiElement declaredElement = declaredElements[0];
     if (!(declaredElement instanceof PsiVariable)) {
       return null;
     }
     final PsiVariable variable = (PsiVariable) declaredElement;
     contentVariableName = variable.getName();
     statementToSkip = declarationStatement;
     if (variable.hasModifierProperty(PsiModifier.FINAL)) {
       finalString = "final ";
     } else {
       finalString = "";
     }
   } else {
     final String collectionName;
     if (listReference == null) {
       collectionName = null;
     } else {
       collectionName = listReference.getReferenceName();
     }
     contentVariableName = createNewVariableName(forStatement, parameterType, collectionName);
     finalString = "";
     statementToSkip = null;
   }
   @NonNls final StringBuilder out = new StringBuilder();
   out.append("for(");
   out.append(finalString);
   out.append(typeString);
   out.append(' ');
   out.append(contentVariableName);
   out.append(": ");
   @NonNls final String listName;
   if (listReference == null) {
     listName = "this";
   } else {
     listName = listReference.getText();
   }
   out.append(listName);
   out.append(')');
   if (body != null) {
     replaceCollectionGetAccess(
         body, contentVariableName, listVariable, indexName, statementToSkip, out);
   }
   return out.toString();
 }
 @Nullable
 private String createArrayIterationText(@NotNull PsiForStatement forStatement) {
   final PsiExpression condition = forStatement.getCondition();
   final PsiBinaryExpression strippedCondition =
       (PsiBinaryExpression) ParenthesesUtils.stripParentheses(condition);
   if (strippedCondition == null) {
     return null;
   }
   final PsiExpression lhs = ParenthesesUtils.stripParentheses(strippedCondition.getLOperand());
   if (lhs == null) {
     return null;
   }
   final PsiExpression rhs = ParenthesesUtils.stripParentheses(strippedCondition.getROperand());
   if (rhs == null) {
     return null;
   }
   final IElementType tokenType = strippedCondition.getOperationTokenType();
   final PsiReferenceExpression arrayLengthExpression;
   final String indexName;
   if (tokenType.equals(JavaTokenType.LT)) {
     arrayLengthExpression = (PsiReferenceExpression) ParenthesesUtils.stripParentheses(rhs);
     indexName = lhs.getText();
   } else if (tokenType.equals(JavaTokenType.GT)) {
     arrayLengthExpression = (PsiReferenceExpression) ParenthesesUtils.stripParentheses(lhs);
     indexName = rhs.getText();
   } else {
     return null;
   }
   if (arrayLengthExpression == null) {
     return null;
   }
   PsiReferenceExpression arrayReference =
       (PsiReferenceExpression) arrayLengthExpression.getQualifierExpression();
   if (arrayReference == null) {
     final PsiElement target = arrayLengthExpression.resolve();
     if (!(target instanceof PsiVariable)) {
       return null;
     }
     final PsiVariable variable = (PsiVariable) target;
     final PsiExpression initializer = variable.getInitializer();
     if (!(initializer instanceof PsiReferenceExpression)) {
       return null;
     }
     final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) initializer;
     arrayReference = (PsiReferenceExpression) referenceExpression.getQualifierExpression();
     if (arrayReference == null) {
       return null;
     }
   }
   final PsiType type = arrayReference.getType();
   if (!(type instanceof PsiArrayType)) {
     return null;
   }
   final PsiArrayType arrayType = (PsiArrayType) type;
   final PsiType componentType = arrayType.getComponentType();
   final String typeText = componentType.getCanonicalText();
   final PsiElement target = arrayReference.resolve();
   if (!(target instanceof PsiVariable)) {
     return null;
   }
   final PsiVariable arrayVariable = (PsiVariable) target;
   final PsiStatement body = forStatement.getBody();
   final PsiStatement firstStatement = getFirstStatement(body);
   final boolean isDeclaration =
       isArrayElementDeclaration(firstStatement, arrayVariable, indexName);
   final String contentVariableName;
   @NonNls final String finalString;
   final PsiStatement statementToSkip;
   if (isDeclaration) {
     final PsiDeclarationStatement declarationStatement =
         (PsiDeclarationStatement) firstStatement;
     assert declarationStatement != null;
     final PsiElement[] declaredElements = declarationStatement.getDeclaredElements();
     final PsiElement declaredElement = declaredElements[0];
     if (!(declaredElement instanceof PsiVariable)) {
       return null;
     }
     final PsiVariable variable = (PsiVariable) declaredElement;
     if (VariableAccessUtils.variableIsAssigned(variable, forStatement)) {
       final String collectionName = arrayReference.getReferenceName();
       contentVariableName = createNewVariableName(forStatement, componentType, collectionName);
       final Project project = forStatement.getProject();
       final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(project);
       if (codeStyleSettings.GENERATE_FINAL_LOCALS) {
         finalString = "final ";
       } else {
         finalString = "";
       }
       statementToSkip = null;
     } else {
       contentVariableName = variable.getName();
       statementToSkip = declarationStatement;
       if (variable.hasModifierProperty(PsiModifier.FINAL)) {
         finalString = "final ";
       } else {
         finalString = "";
       }
     }
   } else {
     final String collectionName = arrayReference.getReferenceName();
     contentVariableName = createNewVariableName(forStatement, componentType, collectionName);
     final Project project = forStatement.getProject();
     final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(project);
     if (codeStyleSettings.GENERATE_FINAL_LOCALS) {
       finalString = "final ";
     } else {
       finalString = "";
     }
     statementToSkip = null;
   }
   @NonNls final StringBuilder out = new StringBuilder();
   out.append("for(");
   out.append(finalString);
   out.append(typeText);
   out.append(' ');
   out.append(contentVariableName);
   out.append(": ");
   final String arrayName = arrayReference.getText();
   out.append(arrayName);
   out.append(')');
   if (body != null) {
     replaceArrayAccess(
         body, contentVariableName, arrayVariable, indexName, statementToSkip, out);
   }
   return out.toString();
 }
 @Nullable
 private String createCollectionIterationText(@NotNull PsiForStatement forStatement)
     throws IncorrectOperationException {
   final PsiStatement body = forStatement.getBody();
   final PsiStatement firstStatement = getFirstStatement(body);
   final PsiStatement initialization = forStatement.getInitialization();
   if (!(initialization instanceof PsiDeclarationStatement)) {
     return null;
   }
   final PsiDeclarationStatement declaration = (PsiDeclarationStatement) initialization;
   final PsiElement declaredIterator = declaration.getDeclaredElements()[0];
   if (!(declaredIterator instanceof PsiVariable)) {
     return null;
   }
   final PsiVariable iteratorVariable = (PsiVariable) declaredIterator;
   final PsiMethodCallExpression initializer =
       (PsiMethodCallExpression) iteratorVariable.getInitializer();
   if (initializer == null) {
     return null;
   }
   final PsiType iteratorType = initializer.getType();
   if (iteratorType == null) {
     return null;
   }
   final PsiType iteratorContentType = extractContentTypeFromType(iteratorType);
   final PsiType iteratorVariableType = iteratorVariable.getType();
   final PsiType contentType;
   final PsiClassType javaLangObject = TypeUtils.getObjectType(forStatement);
   if (iteratorContentType == null) {
     final PsiType iteratorVariableContentType =
         extractContentTypeFromType(iteratorVariableType);
     if (iteratorVariableContentType == null) {
       contentType = javaLangObject;
     } else {
       contentType = iteratorVariableContentType;
     }
   } else {
     contentType = iteratorContentType;
   }
   final PsiReferenceExpression methodExpression = initializer.getMethodExpression();
   final PsiExpression collection = methodExpression.getQualifierExpression();
   final String iteratorName = iteratorVariable.getName();
   final boolean isDeclaration =
       isIteratorNextDeclaration(firstStatement, iteratorName, contentType);
   final PsiStatement statementToSkip;
   @NonNls final String finalString;
   final String contentVariableName;
   if (isDeclaration) {
     final PsiDeclarationStatement declarationStatement =
         (PsiDeclarationStatement) firstStatement;
     assert declarationStatement != null;
     final PsiElement[] declaredElements = declarationStatement.getDeclaredElements();
     final PsiElement declaredElement = declaredElements[0];
     if (!(declaredElement instanceof PsiVariable)) {
       return null;
     }
     final PsiVariable variable = (PsiVariable) declaredElement;
     contentVariableName = variable.getName();
     statementToSkip = declarationStatement;
     if (variable.hasModifierProperty(PsiModifier.FINAL)) {
       finalString = "final ";
     } else {
       finalString = "";
     }
   } else {
     if (collection instanceof PsiReferenceExpression) {
       final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) collection;
       final String collectionName = referenceExpression.getReferenceName();
       contentVariableName = createNewVariableName(forStatement, contentType, collectionName);
     } else {
       contentVariableName = createNewVariableName(forStatement, contentType, null);
     }
     final Project project = forStatement.getProject();
     final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(project);
     if (codeStyleSettings.GENERATE_FINAL_LOCALS) {
       finalString = "final ";
     } else {
       finalString = "";
     }
     statementToSkip = null;
   }
   final String contentTypeString = contentType.getCanonicalText();
   @NonNls final StringBuilder out = new StringBuilder();
   out.append("for(");
   out.append(finalString);
   out.append(contentTypeString);
   out.append(' ');
   out.append(contentVariableName);
   out.append(": ");
   if (!contentType.equals(javaLangObject)) {
     @NonNls final String iterableTypeString = "java.lang.Iterable<" + contentTypeString + '>';
     if (iteratorContentType == null) {
       out.append('(');
       out.append(iterableTypeString);
       out.append(')');
     }
   }
   if (collection == null) {
     out.append("this");
   } else {
     out.append(collection.getText());
   }
   out.append(')');
   replaceIteratorNext(
       body, contentVariableName, iteratorName, statementToSkip, out, contentType);
   return out.toString();
 }
 private boolean checkCondition(
     @Nullable PsiExpression condition,
     @NotNull PsiStatement context,
     List<PsiExpression> notUpdated) {
   if (condition == null) {
     return false;
   }
   if (PsiUtil.isConstantExpression(condition) || PsiKeyword.NULL.equals(condition.getText())) {
     return true;
   }
   if (condition instanceof PsiInstanceOfExpression) {
     final PsiInstanceOfExpression instanceOfExpression = (PsiInstanceOfExpression) condition;
     final PsiExpression operand = instanceOfExpression.getOperand();
     return checkCondition(operand, context, notUpdated);
   } else if (condition instanceof PsiParenthesizedExpression) {
     // catch stuff like "while ((x)) { ... }"
     final PsiExpression expression = ((PsiParenthesizedExpression) condition).getExpression();
     return checkCondition(expression, context, notUpdated);
   } else if (condition instanceof PsiPolyadicExpression) {
     // while (value != x) { ... }
     // while (value != (x + y)) { ... }
     // while (b1 && b2) { ... }
     final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression) condition;
     for (PsiExpression operand : polyadicExpression.getOperands()) {
       if (!checkCondition(operand, context, notUpdated)) {
         return false;
       }
     }
     return true;
   } else if (condition instanceof PsiReferenceExpression) {
     final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) condition;
     final PsiElement element = referenceExpression.resolve();
     if (element instanceof PsiField) {
       final PsiField field = (PsiField) element;
       final PsiType type = field.getType();
       if (field.hasModifierProperty(PsiModifier.FINAL) && type.getArrayDimensions() == 0) {
         if (field.hasModifierProperty(PsiModifier.STATIC)) {
           return true;
         }
         final PsiExpression qualifier = referenceExpression.getQualifierExpression();
         if (qualifier == null) {
           return true;
         } else if (checkCondition(qualifier, context, notUpdated)) {
           return true;
         }
       }
     } else if (element instanceof PsiVariable) {
       final PsiVariable variable = (PsiVariable) element;
       if (variable.hasModifierProperty(PsiModifier.FINAL)) {
         // final variables cannot be updated, don't bother to
         // flag them
         return true;
       } else if (element instanceof PsiLocalVariable || element instanceof PsiParameter) {
         if (!VariableAccessUtils.variableIsAssigned(variable, context)) {
           notUpdated.add(referenceExpression);
           return true;
         }
       }
     }
   } else if (condition instanceof PsiPrefixExpression) {
     final PsiPrefixExpression prefixExpression = (PsiPrefixExpression) condition;
     final IElementType tokenType = prefixExpression.getOperationTokenType();
     if (JavaTokenType.EXCL.equals(tokenType)
         || JavaTokenType.PLUS.equals(tokenType)
         || JavaTokenType.MINUS.equals(tokenType)) {
       final PsiExpression operand = prefixExpression.getOperand();
       return checkCondition(operand, context, notUpdated);
     }
   } else if (condition instanceof PsiArrayAccessExpression) {
     // Actually the contents of the array could change nevertheless
     // if it is accessed through a different reference like this:
     //   int[] local_ints = new int[]{1, 2};
     //   int[] other_ints = local_ints;
     //   while (local_ints[0] > 0) { other_ints[0]--; }
     //
     // Keep this check?
     final PsiArrayAccessExpression accessExpression = (PsiArrayAccessExpression) condition;
     final PsiExpression indexExpression = accessExpression.getIndexExpression();
     return checkCondition(indexExpression, context, notUpdated)
         && checkCondition(accessExpression.getArrayExpression(), context, notUpdated);
   } else if (condition instanceof PsiConditionalExpression) {
     final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression) condition;
     final PsiExpression thenExpression = conditionalExpression.getThenExpression();
     final PsiExpression elseExpression = conditionalExpression.getElseExpression();
     if (thenExpression == null || elseExpression == null) {
       return false;
     }
     return checkCondition(conditionalExpression.getCondition(), context, notUpdated)
         && checkCondition(thenExpression, context, notUpdated)
         && checkCondition(elseExpression, context, notUpdated);
   } else if (condition instanceof PsiThisExpression) {
     return true;
   } else if (condition instanceof PsiMethodCallExpression && !ignoreIterators) {
     final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) condition;
     if (!IteratorUtils.isCallToHasNext(methodCallExpression)) {
       return false;
     }
     final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
     final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
     if (qualifierExpression instanceof PsiReferenceExpression) {
       final PsiReferenceExpression referenceExpression =
           (PsiReferenceExpression) qualifierExpression;
       final PsiElement element = referenceExpression.resolve();
       if (!(element instanceof PsiVariable)) {
         return false;
       }
       final PsiVariable variable = (PsiVariable) element;
       if (!IteratorUtils.containsCallToScannerNext(context, variable, true)) {
         notUpdated.add(qualifierExpression);
         return true;
       }
     } else {
       if (!IteratorUtils.containsCallToScannerNext(context, null, true)) {
         notUpdated.add(methodCallExpression);
         return true;
       }
     }
   }
   return false;
 }
  @Nullable
  static HighlightInfo checkCannotWriteToFinal(
      @NotNull PsiExpression expression, @NotNull PsiFile containingFile) {
    PsiReferenceExpression reference = null;
    boolean readBeforeWrite = false;
    if (expression instanceof PsiAssignmentExpression) {
      final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression) expression;
      final PsiExpression left =
          PsiUtil.skipParenthesizedExprDown(assignmentExpression.getLExpression());
      if (left instanceof PsiReferenceExpression) {
        reference = (PsiReferenceExpression) left;
      }
      readBeforeWrite = assignmentExpression.getOperationTokenType() != JavaTokenType.EQ;
    } else if (expression instanceof PsiPostfixExpression) {
      final PsiExpression operand =
          PsiUtil.skipParenthesizedExprDown(((PsiPostfixExpression) expression).getOperand());
      final IElementType sign = ((PsiPostfixExpression) expression).getOperationTokenType();
      if (operand instanceof PsiReferenceExpression
          && (sign == JavaTokenType.PLUSPLUS || sign == JavaTokenType.MINUSMINUS)) {
        reference = (PsiReferenceExpression) operand;
      }
      readBeforeWrite = true;
    } else if (expression instanceof PsiPrefixExpression) {
      final PsiExpression operand =
          PsiUtil.skipParenthesizedExprDown(((PsiPrefixExpression) expression).getOperand());
      final IElementType sign = ((PsiPrefixExpression) expression).getOperationTokenType();
      if (operand instanceof PsiReferenceExpression
          && (sign == JavaTokenType.PLUSPLUS || sign == JavaTokenType.MINUSMINUS)) {
        reference = (PsiReferenceExpression) operand;
      }
      readBeforeWrite = true;
    }
    final PsiElement resolved = reference == null ? null : reference.resolve();
    PsiVariable variable = resolved instanceof PsiVariable ? (PsiVariable) resolved : null;
    if (variable == null || !variable.hasModifierProperty(PsiModifier.FINAL)) return null;
    final boolean canWrite =
        canWriteToFinal(variable, expression, reference, containingFile)
            && checkWriteToFinalInsideLambda(variable, reference) == null;
    if (readBeforeWrite || !canWrite) {
      final String name = variable.getName();
      String description =
          canWrite
              ? JavaErrorMessages.message("variable.not.initialized", name)
              : JavaErrorMessages.message("assignment.to.final.variable", name);
      final HighlightInfo highlightInfo =
          HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
              .range(reference.getTextRange())
              .descriptionAndTooltip(description)
              .create();
      final PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, expression);
      if (innerClass == null || variable instanceof PsiField) {
        QuickFixAction.registerQuickFixAction(
            highlightInfo,
            QUICK_FIX_FACTORY.createModifierListFix(variable, PsiModifier.FINAL, false, false));
      } else {
        QuickFixAction.registerQuickFixAction(
            highlightInfo,
            QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass));
      }
      return highlightInfo;
    }

    return null;
  }
  @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;
  }
 private static boolean isStaticFinalField(PsiVariable var) {
   return var instanceof PsiField
       && var.hasModifierProperty(PsiModifier.FINAL)
       && var.hasModifierProperty(PsiModifier.STATIC);
 }