@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;
  }
Пример #2
0
    public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
      if (!myField.isValid()) return; // weird. should not get here when field becomes invalid

      final Collection<PsiReference> refs = ReferencesSearch.search(myField).findAll();
      if (refs.isEmpty()) return;
      Set<PsiReference> refsSet = new HashSet<PsiReference>(refs);
      PsiCodeBlock anchorBlock = findAnchorBlock(refs);
      if (anchorBlock == null)
        return; // was assert, but need to fix the case when obsolete inspection highlighting is
                // left
      if (!CodeInsightUtil.preparePsiElementsForWrite(anchorBlock)) return;
      final PsiElementFactory elementFactory =
          JavaPsiFacade.getInstance(project).getElementFactory();
      final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
      final String propertyName =
          styleManager.variableNameToPropertyName(myField.getName(), VariableKind.FIELD);
      String localName =
          styleManager.propertyNameToVariableName(propertyName, VariableKind.LOCAL_VARIABLE);
      localName = RefactoringUtil.suggestUniqueVariableName(localName, anchorBlock, myField);
      PsiElement firstElement = getFirstElement(refs);
      boolean mayBeFinal = mayBeFinal(refsSet, firstElement);
      PsiElement newDeclaration = null;
      try {
        final PsiElement anchor = getAnchorElement(anchorBlock, firstElement);
        if (anchor instanceof PsiExpressionStatement
            && ((PsiExpressionStatement) anchor).getExpression()
                instanceof PsiAssignmentExpression) {
          final PsiAssignmentExpression expression =
              (PsiAssignmentExpression) ((PsiExpressionStatement) anchor).getExpression();
          if (expression.getOperationTokenType() == JavaTokenType.EQ
              && expression.getLExpression() instanceof PsiReferenceExpression
              && ((PsiReference) expression.getLExpression()).isReferenceTo(myField)) {
            final PsiExpression initializer = expression.getRExpression();
            final PsiDeclarationStatement decl =
                elementFactory.createVariableDeclarationStatement(
                    localName, myField.getType(), initializer);
            if (!mayBeFinal) {
              PsiUtil.setModifierProperty(
                  ((PsiModifierListOwner) decl.getDeclaredElements()[0]), PsiModifier.FINAL, false);
            }
            newDeclaration = anchor.replace(decl);
            refsSet.remove(expression.getLExpression());
            retargetReferences(elementFactory, localName, refsSet);
          } else {
            newDeclaration =
                addDeclarationWithFieldInitializerAndRetargetReferences(
                    elementFactory, localName, anchorBlock, anchor, refsSet);
          }
        } else {
          newDeclaration =
              addDeclarationWithFieldInitializerAndRetargetReferences(
                  elementFactory, localName, anchorBlock, anchor, refsSet);
        }
      } catch (IncorrectOperationException e) {
        LOG.error(e);
      }

      if (newDeclaration != null) {
        final PsiFile psiFile = myField.getContainingFile();
        final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
        if (editor != null && IJSwingUtilities.hasFocus(editor.getComponent())) {
          final PsiFile file =
              PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
          if (file == psiFile) {
            editor.getCaretModel().moveToOffset(newDeclaration.getTextOffset());
            editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
          }
        }
      }

      try {
        myField.normalizeDeclaration();
        myField.delete();
      } catch (IncorrectOperationException e) {
        LOG.error(e);
      }
    }