コード例 #1
0
 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();
   }
 }
コード例 #2
0
    private PsiElement addDeclarationWithFieldInitializerAndRetargetReferences(
        final PsiElementFactory elementFactory,
        final String localName,
        final PsiCodeBlock anchorBlock,
        final PsiElement anchor,
        final Set<PsiReference> refs)
        throws IncorrectOperationException {
      final PsiDeclarationStatement decl =
          elementFactory.createVariableDeclarationStatement(
              localName, myField.getType(), myField.getInitializer());
      final PsiElement newDeclaration = anchorBlock.addBefore(decl, anchor);

      retargetReferences(elementFactory, localName, refs);
      return newDeclaration;
    }
コード例 #3
0
  @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);
      }
    }
  }
コード例 #4
0
  @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;
  }