Пример #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();
   }
 }
  private static boolean isCollectCall(PsiStatement body, final PsiParameter parameter) {
    PsiIfStatement ifStatement = extractIfStatement(body);
    final PsiMethodCallExpression methodCallExpression = extractAddCall(body, ifStatement);
    if (methodCallExpression != null) {
      final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
      final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
      PsiClass qualifierClass = null;
      if (qualifierExpression instanceof PsiReferenceExpression) {
        if (ReferencesSearch.search(parameter, new LocalSearchScope(qualifierExpression))
                .findFirst()
            != null) {
          return false;
        }
        final PsiElement resolve = ((PsiReferenceExpression) qualifierExpression).resolve();
        if (resolve instanceof PsiVariable) {
          if (ReferencesSearch.search(
                      resolve, new LocalSearchScope(methodCallExpression.getArgumentList()))
                  .findFirst()
              != null) {
            return false;
          }
        }
        qualifierClass = PsiUtil.resolveClassInType(qualifierExpression.getType());
      } else if (qualifierExpression == null) {
        final PsiClass enclosingClass = PsiTreeUtil.getParentOfType(body, PsiClass.class);
        if (PsiUtil.getEnclosingStaticElement(body, enclosingClass) == null) {
          qualifierClass = enclosingClass;
        }
      }

      if (qualifierClass != null
          && InheritanceUtil.isInheritor(
              qualifierClass, false, CommonClassNames.JAVA_UTIL_COLLECTION)) {

        while (ifStatement != null && PsiTreeUtil.isAncestor(body, ifStatement, false)) {
          final PsiExpression condition = ifStatement.getCondition();
          if (condition != null
              && isConditionDependsOnUpdatedCollections(condition, qualifierExpression))
            return false;
          ifStatement = PsiTreeUtil.getParentOfType(ifStatement, PsiIfStatement.class);
        }

        final PsiElement resolve = methodExpression.resolve();
        if (resolve instanceof PsiMethod
            && "add".equals(((PsiMethod) resolve).getName())
            && ((PsiMethod) resolve).getParameterList().getParametersCount() == 1) {
          final PsiExpression[] args = methodCallExpression.getArgumentList().getExpressions();
          if (args.length == 1) {
            if (args[0] instanceof PsiCallExpression) {
              final PsiMethod method = ((PsiCallExpression) args[0]).resolveMethod();
              return method != null && !method.hasTypeParameters() && !isThrowsCompatible(method);
            }
            return true;
          }
        }
      }
    }
    return false;
  }
Пример #3
0
 private static void retargetReferences(
     final PsiElementFactory elementFactory,
     final String localName,
     final Set<PsiReference> refs)
     throws IncorrectOperationException {
   final PsiReferenceExpression refExpr =
       (PsiReferenceExpression) elementFactory.createExpressionFromText(localName, null);
   for (PsiReference ref : refs) {
     if (ref instanceof PsiReferenceExpression) {
       ((PsiReferenceExpression) ref).replace(refExpr);
     }
   }
 }
  private static boolean isSameField(
      @NotNull PsiMember enclosingCtrOrInitializer,
      @NotNull PsiField field,
      @NotNull PsiReferenceExpression reference,
      @NotNull PsiFile containingFile) {

    if (!containingFile
        .getManager()
        .areElementsEquivalent(
            enclosingCtrOrInitializer.getContainingClass(), field.getContainingClass()))
      return false;
    PsiExpression qualifierExpression = reference.getQualifierExpression();
    return qualifierExpression == null || qualifierExpression instanceof PsiThisExpression;
  }
  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;
  }
  @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;
  }