private void handleBranchingInstruction(
     ProblemsHolder holder,
     StandardInstructionVisitor visitor,
     Set<Instruction> trueSet,
     Set<Instruction> falseSet,
     HashSet<PsiElement> reportedAnchors,
     BranchingInstruction instruction,
     final boolean onTheFly) {
   PsiElement psiAnchor = instruction.getPsiAnchor();
   boolean underBinary = isAtRHSOfBooleanAnd(psiAnchor);
   if (instruction instanceof InstanceofInstruction
       && visitor.isInstanceofRedundant((InstanceofInstruction) instruction)) {
     if (visitor.canBeNull((BinopInstruction) instruction)) {
       holder.registerProblem(
           psiAnchor,
           InspectionsBundle.message("dataflow.message.redundant.instanceof"),
           new RedundantInstanceofFix());
     } else {
       final LocalQuickFix localQuickFix = createSimplifyBooleanExpressionFix(psiAnchor, true);
       holder.registerProblem(
           psiAnchor,
           InspectionsBundle.message(
               underBinary
                   ? "dataflow.message.constant.condition.when.reached"
                   : "dataflow.message.constant.condition",
               Boolean.toString(true)),
           localQuickFix == null ? null : new LocalQuickFix[] {localQuickFix});
     }
   } else if (psiAnchor instanceof PsiSwitchLabelStatement) {
     if (falseSet.contains(instruction)) {
       holder.registerProblem(
           psiAnchor, InspectionsBundle.message("dataflow.message.unreachable.switch.label"));
     }
   } else if (psiAnchor != null
       && !reportedAnchors.contains(psiAnchor)
       && !isFlagCheck(psiAnchor)) {
     boolean evaluatesToTrue = trueSet.contains(instruction);
     final PsiElement parent = psiAnchor.getParent();
     if (parent instanceof PsiAssignmentExpression
         && ((PsiAssignmentExpression) parent).getLExpression() == psiAnchor) {
       holder.registerProblem(
           psiAnchor,
           InspectionsBundle.message(
               "dataflow.message.pointless.assignment.expression",
               Boolean.toString(evaluatesToTrue)),
           createConditionalAssignmentFixes(
               evaluatesToTrue, (PsiAssignmentExpression) parent, onTheFly));
     } else if (!skipReportingConstantCondition(visitor, psiAnchor, evaluatesToTrue)) {
       final LocalQuickFix fix = createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue);
       String message =
           InspectionsBundle.message(
               underBinary
                   ? "dataflow.message.constant.condition.when.reached"
                   : "dataflow.message.constant.condition",
               Boolean.toString(evaluatesToTrue));
       holder.registerProblem(psiAnchor, message, fix == null ? null : new LocalQuickFix[] {fix});
     }
     reportedAnchors.add(psiAnchor);
   }
 }
  private static void reportOptionalOfNullableImprovements(
      ProblemsHolder holder, Set<PsiElement> reportedAnchors, Instruction[] instructions) {
    for (Instruction instruction : instructions) {
      if (instruction instanceof MethodCallInstruction) {
        final PsiExpression[] args = ((MethodCallInstruction) instruction).getArgs();
        if (args.length != 1) continue;

        final PsiExpression expr = args[0];

        if (((MethodCallInstruction) instruction).isOptionalAlwaysNullProblem()) {
          if (!reportedAnchors.add(expr)) continue;
          holder.registerProblem(
              expr,
              "Passing <code>null</code> argument to <code>Optional</code>",
              DfaOptionalSupport.createReplaceOptionalOfNullableWithEmptyFix(expr));
        } else if (((MethodCallInstruction) instruction).isOptionalAlwaysNotNullProblem()) {
          if (!reportedAnchors.add(expr)) continue;
          holder.registerProblem(
              expr,
              "Passing a non-null argument to <code>Optional</code>",
              DfaOptionalSupport.createReplaceOptionalOfNullableWithOfFix());
        }
      }
    }
  }
Ejemplo n.º 3
0
  private void handleStepOutOfLoop(
      @NotNull final Instruction prevInstruction,
      @NotNull Instruction nextInstruction,
      @NotNull final int[] loopNumber,
      @NotNull MultiMap<BranchingInstruction, DfaMemoryState> processedStates,
      @NotNull MultiMap<BranchingInstruction, DfaMemoryState> incomingStates,
      @NotNull List<DfaInstructionState> inFlightStates,
      @NotNull DfaInstructionState[] afterStates,
      @NotNull StateQueue queue) {
    if (loopNumber[prevInstruction.getIndex()] == 0
        || inSameLoop(prevInstruction, nextInstruction, loopNumber)) {
      return;
    }
    // stepped out of loop. destroy all memory states from the loop, we don't need them anymore

    // but do not touch yet states being handled right now
    for (DfaInstructionState state : inFlightStates) {
      Instruction instruction = state.getInstruction();
      if (inSameLoop(prevInstruction, instruction, loopNumber)) {
        return;
      }
    }
    for (DfaInstructionState state : afterStates) {
      Instruction instruction = state.getInstruction();
      if (inSameLoop(prevInstruction, instruction, loopNumber)) {
        return;
      }
    }
    // and still in queue
    if (!queue.processAll(
        new Processor<DfaInstructionState>() {
          @Override
          public boolean process(DfaInstructionState state) {
            Instruction instruction = state.getInstruction();
            return !inSameLoop(prevInstruction, instruction, loopNumber);
          }
        })) return;

    // now remove obsolete memory states
    final Set<BranchingInstruction> mayRemoveStatesFor = new THashSet<BranchingInstruction>();
    for (Instruction instruction : myInstructions) {
      if (inSameLoop(prevInstruction, instruction, loopNumber)
          && instruction instanceof BranchingInstruction) {
        mayRemoveStatesFor.add((BranchingInstruction) instruction);
      }
    }

    for (Instruction instruction : mayRemoveStatesFor) {
      processedStates.remove((BranchingInstruction) instruction);
      incomingStates.remove((BranchingInstruction) instruction);
    }
  }
 private static void reportUnboxedNullables(
     DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
   for (PsiElement expr : visitor.getProblems(NullabilityProblem.unboxingNullable)) {
     if (!reportedAnchors.add(expr)) continue;
     holder.registerProblem(expr, InspectionsBundle.message("dataflow.message.unboxing"));
   }
 }
  private void reportNullableReturns(
      DataFlowInstructionVisitor visitor,
      ProblemsHolder holder,
      Set<PsiElement> reportedAnchors,
      @NotNull PsiElement block) {
    final PsiMethod method = getScopeMethod(block);
    if (method == null || NullableStuffInspectionBase.isNullableNotInferred(method, true)) return;

    boolean notNullRequired = NullableNotNullManager.isNotNull(method);
    if (!notNullRequired && !SUGGEST_NULLABLE_ANNOTATIONS) return;

    PsiType returnType = method.getReturnType();
    // no warnings in void lambdas, where the expression is not returned anyway
    if (block instanceof PsiExpression
        && block.getParent() instanceof PsiLambdaExpression
        && returnType == PsiType.VOID) return;

    // no warnings for Void methods, where only null can be possibly returned
    if (returnType == null || returnType.equalsToText(CommonClassNames.JAVA_LANG_VOID)) return;

    for (PsiElement statement : visitor.getProblems(NullabilityProblem.nullableReturn)) {
      assert statement instanceof PsiExpression;
      final PsiExpression expr = (PsiExpression) statement;
      if (!reportedAnchors.add(expr)) continue;

      if (notNullRequired) {
        final String text =
            isNullLiteralExpression(expr)
                ? InspectionsBundle.message("dataflow.message.return.null.from.notnull")
                : InspectionsBundle.message("dataflow.message.return.nullable.from.notnull");
        holder.registerProblem(expr, text);
      } else if (AnnotationUtil.isAnnotatingApplicable(statement)) {
        final NullableNotNullManager manager =
            NullableNotNullManager.getInstance(expr.getProject());
        final String defaultNullable = manager.getDefaultNullable();
        final String presentableNullable = StringUtil.getShortName(defaultNullable);
        final String text =
            isNullLiteralExpression(expr)
                ? InspectionsBundle.message(
                    "dataflow.message.return.null.from.notnullable", presentableNullable)
                : InspectionsBundle.message(
                    "dataflow.message.return.nullable.from.notnullable", presentableNullable);
        final LocalQuickFix[] fixes =
            PsiTreeUtil.getParentOfType(expr, PsiMethod.class, PsiLambdaExpression.class)
                    instanceof PsiLambdaExpression
                ? LocalQuickFix.EMPTY_ARRAY
                : new LocalQuickFix[] {
                  new AnnotateMethodFix(
                      defaultNullable, ArrayUtil.toStringArray(manager.getNotNulls())) {
                    @Override
                    public int shouldAnnotateBaseMethod(
                        PsiMethod method, PsiMethod superMethod, Project project) {
                      return 1;
                    }
                  }
                };
        holder.registerProblem(expr, text, fixes);
      }
    }
  }
  private static void reportNullableAssignments(
      DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
    for (PsiElement expr : visitor.getProblems(NullabilityProblem.assigningToNotNull)) {
      if (!reportedAnchors.add(expr)) continue;

      final String text =
          isNullLiteralExpression(expr)
              ? InspectionsBundle.message("dataflow.message.assigning.null")
              : InspectionsBundle.message("dataflow.message.assigning.nullable");
      holder.registerProblem(expr, text);
    }
  }
Ejemplo n.º 7
0
  @NotNull
  public Pair<Set<Instruction>, Set<Instruction>> getConstConditionalExpressions() {
    Set<Instruction> trueSet = new HashSet<Instruction>();
    Set<Instruction> falseSet = new HashSet<Instruction>();

    for (Instruction instruction : myInstructions) {
      if (instruction instanceof BranchingInstruction) {
        BranchingInstruction branchingInstruction = (BranchingInstruction) instruction;
        if (branchingInstruction.getPsiAnchor() != null
            && branchingInstruction.isConditionConst()) {
          if (!branchingInstruction.isTrueReachable()) {
            falseSet.add(branchingInstruction);
          }

          if (!branchingInstruction.isFalseReachable()) {
            trueSet.add(branchingInstruction);
          }
        }
      }
    }

    for (Instruction instruction : myInstructions) {
      if (instruction instanceof BranchingInstruction) {
        BranchingInstruction branchingInstruction = (BranchingInstruction) instruction;
        if (branchingInstruction.isTrueReachable()) {
          falseSet.remove(branchingInstruction);
        }
        if (branchingInstruction.isFalseReachable()) {
          trueSet.remove(branchingInstruction);
        }
      }
    }

    return Pair.create(trueSet, falseSet);
  }
  private void reportNullableArgumentsPassedToNonAnnotated(
      DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
    for (PsiElement expr :
        visitor.getProblems(NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter)) {
      if (reportedAnchors.contains(expr)) continue;

      final String text =
          isNullLiteralExpression(expr)
              ? "Passing <code>null</code> argument to non annotated parameter"
              : "Argument <code>#ref</code> #loc might be null but passed to non annotated parameter";
      LocalQuickFix[] fixes =
          createNPEFixes((PsiExpression) expr, (PsiExpression) expr, holder.isOnTheFly());
      final PsiElement parent = expr.getParent();
      if (parent instanceof PsiExpressionList) {
        final int idx = ArrayUtilRt.find(((PsiExpressionList) parent).getExpressions(), expr);
        if (idx > -1) {
          final PsiElement gParent = parent.getParent();
          if (gParent instanceof PsiCallExpression) {
            final PsiMethod psiMethod = ((PsiCallExpression) gParent).resolveMethod();
            if (psiMethod != null
                && psiMethod.getManager().isInProject(psiMethod)
                && AnnotationUtil.isAnnotatingApplicable(psiMethod)) {
              final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
              if (idx < parameters.length) {
                final AddNullableAnnotationFix addNullableAnnotationFix =
                    new AddNullableAnnotationFix(parameters[idx]);
                fixes =
                    fixes == null
                        ? new LocalQuickFix[] {addNullableAnnotationFix}
                        : ArrayUtil.append(fixes, addNullableAnnotationFix);
                holder.registerProblem(expr, text, fixes);
                reportedAnchors.add(expr);
              }
            }
          }
        }
      }
    }
  }
  private void reportNullableArguments(
      DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
    for (PsiElement expr :
        visitor.getProblems(NullabilityProblem.passingNullableToNotNullParameter)) {
      if (!reportedAnchors.add(expr)) continue;

      final String text =
          isNullLiteralExpression(expr)
              ? InspectionsBundle.message("dataflow.message.passing.null.argument")
              : InspectionsBundle.message("dataflow.message.passing.nullable.argument");
      LocalQuickFix[] fixes =
          createNPEFixes((PsiExpression) expr, (PsiExpression) expr, holder.isOnTheFly());
      holder.registerProblem(expr, text, fixes);
    }
  }
  private static void reportNullableReturns(
      StandardDataFlowRunner runner,
      DataFlowInstructionVisitor visitor,
      ProblemsHolder holder,
      Set<PsiElement> reportedAnchors) {
    for (PsiElement statement : visitor.getProblems(NullabilityProblem.nullableReturn)) {
      assert statement instanceof PsiExpression;
      final PsiExpression expr = (PsiExpression) statement;
      if (!reportedAnchors.add(expr)) continue;

      if (runner.isInNotNullMethod()) {
        final String text =
            isNullLiteralExpression(expr)
                ? InspectionsBundle.message("dataflow.message.return.null.from.notnull")
                : InspectionsBundle.message("dataflow.message.return.nullable.from.notnull");
        holder.registerProblem(expr, text);
      } else if (AnnotationUtil.isAnnotatingApplicable(statement)) {
        final NullableNotNullManager manager =
            NullableNotNullManager.getInstance(expr.getProject());
        final String defaultNullable = manager.getDefaultNullable();
        final String presentableNullable = StringUtil.getShortName(defaultNullable);
        final String text =
            isNullLiteralExpression(expr)
                ? InspectionsBundle.message(
                    "dataflow.message.return.null.from.notnullable", presentableNullable)
                : InspectionsBundle.message(
                    "dataflow.message.return.nullable.from.notnullable", presentableNullable);
        holder.registerProblem(
            expr,
            text,
            new AnnotateMethodFix(defaultNullable, ArrayUtil.toStringArray(manager.getNotNulls())) {
              @Override
              public int shouldAnnotateBaseMethod(
                  PsiMethod method, PsiMethod superMethod, Project project) {
                return 1;
              }
            });
      }
    }
  }
  private static void reportConstantReferenceValues(
      ProblemsHolder holder, StandardInstructionVisitor visitor, Set<PsiElement> reportedAnchors) {
    for (Pair<PsiReferenceExpression, DfaConstValue> pair : visitor.getConstantReferenceValues()) {
      PsiReferenceExpression ref = pair.first;
      if (!reportedAnchors.add(ref)) {
        continue;
      }

      final Object value = pair.second.getValue();
      PsiVariable constant = pair.second.getConstant();
      final String presentableName = constant != null ? constant.getName() : String.valueOf(value);
      final String exprText = getConstantValueText(value, constant);
      if (presentableName == null || exprText == null) {
        continue;
      }

      holder.registerProblem(
          ref,
          "Value <code>#ref</code> #loc is always '" + presentableName + "'",
          new LocalQuickFix() {
            @NotNull
            @Override
            public String getName() {
              return "Replace with '" + presentableName + "'";
            }

            @NotNull
            @Override
            public String getFamilyName() {
              return "Replace with constant value";
            }

            @Override
            public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
              if (!FileModificationService.getInstance()
                  .preparePsiElementsForWrite(descriptor.getPsiElement())) {
                return;
              }

              JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
              PsiElement newElement =
                  descriptor
                      .getPsiElement()
                      .replace(facade.getElementFactory().createExpressionFromText(exprText, null));
              newElement =
                  JavaCodeStyleManager.getInstance(project).shortenClassReferences(newElement);
              if (newElement instanceof PsiJavaCodeReferenceElement) {
                PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement) newElement;
                PsiElement target = ref.resolve();
                String shortName = ref.getReferenceName();
                if (target != null
                    && shortName != null
                    && ref.isQualified()
                    && facade.getResolveHelper().resolveReferencedVariable(shortName, newElement)
                        == target) {
                  newElement.replace(
                      facade.getElementFactory().createExpressionFromText(shortName, null));
                }
              }
            }
          });
    }
  }
  private static void reportConstantReferenceValues(
      ProblemsHolder holder, StandardInstructionVisitor visitor, Set<PsiElement> reportedAnchors) {
    for (Pair<PsiReferenceExpression, DfaConstValue> pair : visitor.getConstantReferenceValues()) {
      PsiReferenceExpression ref = pair.first;
      if (ref.getParent() instanceof PsiReferenceExpression || !reportedAnchors.add(ref)) {
        continue;
      }

      final Object value = pair.second.getValue();
      PsiVariable constant = pair.second.getConstant();
      final String presentableName = constant != null ? constant.getName() : String.valueOf(value);
      final String exprText = String.valueOf(value);
      if (presentableName == null || exprText == null) {
        continue;
      }

      holder.registerProblem(
          ref,
          "Value <code>#ref</code> #loc is always '" + presentableName + "'",
          new LocalQuickFix() {
            @NotNull
            @Override
            public String getName() {
              return "Replace with '" + presentableName + "'";
            }

            @NotNull
            @Override
            public String getFamilyName() {
              return "Replace with constant value";
            }

            @Override
            public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
              if (!FileModificationService.getInstance()
                  .preparePsiElementsForWrite(descriptor.getPsiElement())) {
                return;
              }

              PsiElement problemElement = descriptor.getPsiElement();
              if (problemElement == null) return;

              PsiMethodCallExpression call =
                  problemElement.getParent() instanceof PsiExpressionList
                          && problemElement.getParent().getParent()
                              instanceof PsiMethodCallExpression
                      ? (PsiMethodCallExpression) problemElement.getParent().getParent()
                      : null;
              PsiMethod targetMethod = call == null ? null : call.resolveMethod();

              JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
              problemElement.replace(
                  facade.getElementFactory().createExpressionFromText(exprText, null));

              if (targetMethod != null) {
                ExtractMethodUtil.addCastsToEnsureResolveTarget(targetMethod, call);
              }
            }
          });
    }
  }
Ejemplo n.º 13
0
  @NotNull
  final RunnerResult analyzeMethod(
      @NotNull PsiElement psiBlock,
      @NotNull InstructionVisitor visitor,
      boolean ignoreAssertions,
      @NotNull Collection<DfaMemoryState> initialStates) {
    if (PsiTreeUtil.findChildOfType(psiBlock, OuterLanguageElement.class) != null)
      return RunnerResult.NOT_APPLICABLE;

    try {
      final ControlFlow flow =
          new ControlFlowAnalyzer(myValueFactory, psiBlock, ignoreAssertions).buildControlFlow();
      if (flow == null) return RunnerResult.NOT_APPLICABLE;
      int[] loopNumber = LoopAnalyzer.calcInLoop(flow);

      int endOffset = flow.getInstructionCount();
      myInstructions = flow.getInstructions();
      myNestedClosures.clear();

      Set<Instruction> joinInstructions = ContainerUtil.newHashSet();
      for (int index = 0; index < myInstructions.length; index++) {
        Instruction instruction = myInstructions[index];
        if (instruction instanceof GotoInstruction) {
          joinInstructions.add(myInstructions[((GotoInstruction) instruction).getOffset()]);
        } else if (instruction instanceof ConditionalGotoInstruction) {
          joinInstructions.add(
              myInstructions[((ConditionalGotoInstruction) instruction).getOffset()]);
        } else if (instruction instanceof MethodCallInstruction
            && !((MethodCallInstruction) instruction).getContracts().isEmpty()) {
          joinInstructions.add(myInstructions[index + 1]);
        }
      }

      if (LOG.isDebugEnabled()) {
        LOG.debug("Analyzing code block: " + psiBlock.getText());
        for (int i = 0; i < myInstructions.length; i++) {
          LOG.debug(i + ": " + myInstructions[i]);
        }
      }
      // for (int i = 0; i < myInstructions.length; i++) System.out.println(i + ": " +
      // myInstructions[i].toString());

      Integer tooExpensiveHash = psiBlock.getUserData(TOO_EXPENSIVE_HASH);
      if (tooExpensiveHash != null && tooExpensiveHash == psiBlock.getText().hashCode()) {
        LOG.debug("Too complex because hasn't changed since being too complex already");
        return RunnerResult.TOO_COMPLEX;
      }

      final StateQueue queue = new StateQueue();
      for (final DfaMemoryState initialState : initialStates) {
        queue.offer(new DfaInstructionState(myInstructions[0], initialState));
      }

      MultiMap<BranchingInstruction, DfaMemoryState> processedStates = MultiMap.createSet();
      MultiMap<BranchingInstruction, DfaMemoryState> incomingStates = MultiMap.createSet();

      long msLimit =
          shouldCheckTimeLimit()
              ? Registry.intValue("ide.dfa.time.limit.online")
              : Registry.intValue("ide.dfa.time.limit.offline");
      WorkingTimeMeasurer measurer = new WorkingTimeMeasurer(msLimit * 1000 * 1000);
      int count = 0;
      while (!queue.isEmpty()) {
        List<DfaInstructionState> states = queue.getNextInstructionStates(joinInstructions);
        for (DfaInstructionState instructionState : states) {
          if (count++ % 1024 == 0 && measurer.isTimeOver()) {
            LOG.debug("Too complex because the analysis took too long");
            psiBlock.putUserData(TOO_EXPENSIVE_HASH, psiBlock.getText().hashCode());
            return RunnerResult.TOO_COMPLEX;
          }
          ProgressManager.checkCanceled();

          if (LOG.isDebugEnabled()) {
            LOG.debug(instructionState.toString());
          }
          // System.out.println(instructionState.toString());

          Instruction instruction = instructionState.getInstruction();

          if (instruction instanceof BranchingInstruction) {
            BranchingInstruction branching = (BranchingInstruction) instruction;
            Collection<DfaMemoryState> processed = processedStates.get(branching);
            if (processed.contains(instructionState.getMemoryState())) {
              continue;
            }
            if (processed.size() > MAX_STATES_PER_BRANCH) {
              LOG.debug("Too complex because too many different possible states");
              return RunnerResult.TOO_COMPLEX; // Too complex :(
            }
            if (loopNumber[branching.getIndex()] != 0) {
              processedStates.putValue(branching, instructionState.getMemoryState().createCopy());
            }
          }

          DfaInstructionState[] after = acceptInstruction(visitor, instructionState);
          for (DfaInstructionState state : after) {
            Instruction nextInstruction = state.getInstruction();
            if (nextInstruction.getIndex() >= endOffset) {
              continue;
            }
            handleStepOutOfLoop(
                instruction,
                nextInstruction,
                loopNumber,
                processedStates,
                incomingStates,
                states,
                after,
                queue);
            if (nextInstruction instanceof BranchingInstruction) {
              BranchingInstruction branching = (BranchingInstruction) nextInstruction;
              if (processedStates.get(branching).contains(state.getMemoryState())
                  || incomingStates.get(branching).contains(state.getMemoryState())) {
                continue;
              }
              if (loopNumber[branching.getIndex()] != 0) {
                incomingStates.putValue(branching, state.getMemoryState().createCopy());
              }
            }
            queue.offer(state);
          }
        }
      }

      psiBlock.putUserData(TOO_EXPENSIVE_HASH, null);
      LOG.debug("Analysis ok");
      return RunnerResult.OK;
    } catch (ArrayIndexOutOfBoundsException e) {
      LOG.error(psiBlock.getText(), e);
      return RunnerResult.ABORTED;
    } catch (EmptyStackException e) {
      LOG.error(psiBlock.getText(), e);
      return RunnerResult.ABORTED;
    }
  }