void submitPasses(
      @NotNull Map<FileEditor, HighlightingPass[]> passesMap,
      @NotNull DaemonProgressIndicator updateProgress) {
    if (isDisposed()) return;

    // null keys are ok
    MultiMap<Document, FileEditor> documentToEditors = MultiMap.createSet();
    MultiMap<FileEditor, TextEditorHighlightingPass> documentBoundPasses = MultiMap.createSmart();
    MultiMap<FileEditor, EditorBoundHighlightingPass> editorBoundPasses = MultiMap.createSmart();
    List<Pair<FileEditor, TextEditorHighlightingPass>> passesWithNoDocuments = new ArrayList<>();
    Set<VirtualFile> vFiles = new HashSet<>();

    for (Map.Entry<FileEditor, HighlightingPass[]> entry : passesMap.entrySet()) {
      FileEditor fileEditor = entry.getKey();
      HighlightingPass[] passes = entry.getValue();
      Document document;
      if (fileEditor instanceof TextEditor) {
        Editor editor = ((TextEditor) fileEditor).getEditor();
        LOG.assertTrue(!(editor instanceof EditorWindow));
        document = editor.getDocument();
      } else {
        VirtualFile virtualFile =
            ((FileEditorManagerEx) FileEditorManager.getInstance(myProject)).getFile(fileEditor);
        document =
            virtualFile == null ? null : FileDocumentManager.getInstance().getDocument(virtualFile);
      }
      if (document != null) {
        vFiles.add(FileDocumentManager.getInstance().getFile(document));
      }

      int prevId = 0;
      for (final HighlightingPass pass : passes) {
        if (pass instanceof EditorBoundHighlightingPass) {
          EditorBoundHighlightingPass editorPass = (EditorBoundHighlightingPass) pass;
          editorPass.setId(
              nextPassId.incrementAndGet()); // have to make ids unique for this document
          editorBoundPasses.putValue(fileEditor, editorPass);
        } else {
          TextEditorHighlightingPass textEditorHighlightingPass =
              convertToTextHighlightingPass(pass, document, nextPassId, prevId);
          document = textEditorHighlightingPass.getDocument();
          documentBoundPasses.putValue(fileEditor, textEditorHighlightingPass);
          if (document == null) {
            passesWithNoDocuments.add(Pair.create(fileEditor, textEditorHighlightingPass));
          } else {
            documentToEditors.putValue(document, fileEditor);
          }
          prevId = textEditorHighlightingPass.getId();
        }
      }
    }

    List<ScheduledPass> freePasses = new ArrayList<>(documentToEditors.size() * 5);
    List<ScheduledPass> dependentPasses = new ArrayList<>(documentToEditors.size() * 10);
    // (fileEditor, passId) -> created pass
    Map<Pair<FileEditor, Integer>, ScheduledPass> toBeSubmitted = new THashMap<>(passesMap.size());

    final AtomicInteger threadsToStartCountdown = new AtomicInteger(0);
    for (Map.Entry<Document, Collection<FileEditor>> entry : documentToEditors.entrySet()) {
      Collection<FileEditor> fileEditors = entry.getValue();
      Document document = entry.getKey();
      FileEditor preferredFileEditor = getPreferredFileEditor(document, fileEditors);
      List<TextEditorHighlightingPass> passes =
          (List<TextEditorHighlightingPass>) documentBoundPasses.get(preferredFileEditor);
      if (passes.isEmpty()) {
        continue;
      }
      sortById(passes);
      for (TextEditorHighlightingPass currentPass : passes) {
        createScheduledPass(
            preferredFileEditor,
            currentPass,
            toBeSubmitted,
            passes,
            freePasses,
            dependentPasses,
            updateProgress,
            threadsToStartCountdown);
      }
    }

    for (Map.Entry<FileEditor, Collection<EditorBoundHighlightingPass>> entry :
        editorBoundPasses.entrySet()) {
      FileEditor fileEditor = entry.getKey();
      Collection<EditorBoundHighlightingPass> createdEditorBoundPasses = entry.getValue();
      List<TextEditorHighlightingPass> createdDocumentBoundPasses =
          (List<TextEditorHighlightingPass>) documentBoundPasses.get(fileEditor);
      List<TextEditorHighlightingPass> allCreatedPasses =
          new ArrayList<>(createdDocumentBoundPasses);
      allCreatedPasses.addAll(createdEditorBoundPasses);

      for (EditorBoundHighlightingPass pass : createdEditorBoundPasses) {
        createScheduledPass(
            fileEditor,
            pass,
            toBeSubmitted,
            allCreatedPasses,
            freePasses,
            dependentPasses,
            updateProgress,
            threadsToStartCountdown);
      }
    }

    for (Pair<FileEditor, TextEditorHighlightingPass> pair : passesWithNoDocuments) {
      FileEditor fileEditor = pair.first;
      TextEditorHighlightingPass pass = pair.second;
      createScheduledPass(
          fileEditor,
          pass,
          toBeSubmitted,
          ContainerUtil.emptyList(),
          freePasses,
          dependentPasses,
          updateProgress,
          threadsToStartCountdown);
    }

    if (CHECK_CONSISTENCY && !ApplicationInfoImpl.isInPerformanceTest()) {
      assertConsistency(freePasses, toBeSubmitted, threadsToStartCountdown);
    }

    log(
        updateProgress,
        null,
        vFiles + " ----- starting " + threadsToStartCountdown.get(),
        freePasses);

    for (ScheduledPass dependentPass : dependentPasses) {
      mySubmittedPasses.put(dependentPass, Job.NULL_JOB);
    }
    for (ScheduledPass freePass : freePasses) {
      submit(freePass);
    }
  }
  private static class RootInfo {
    // getDirectoriesByPackageName used to be in this order, some clients might rely on that
    @NotNull
    final LinkedHashSet<VirtualFile> classAndSourceRoots = ContainerUtil.newLinkedHashSet();

    @NotNull final Set<VirtualFile> libraryOrSdkSources = ContainerUtil.newHashSet();
    @NotNull final Set<VirtualFile> libraryOrSdkClasses = ContainerUtil.newHashSet();
    @NotNull final Map<VirtualFile, Module> contentRootOf = ContainerUtil.newHashMap();
    @NotNull final MultiMap<VirtualFile, Module> sourceRootOf = MultiMap.createSet();
    @NotNull final TObjectIntHashMap<VirtualFile> rootTypeId = new TObjectIntHashMap<VirtualFile>();
    @NotNull final MultiMap<VirtualFile, Library> excludedFromLibraries = MultiMap.createSmart();
    @NotNull final MultiMap<VirtualFile, Library> classOfLibraries = MultiMap.createSmart();
    @NotNull final MultiMap<VirtualFile, Library> sourceOfLibraries = MultiMap.createSmart();
    @NotNull final Set<VirtualFile> excludedFromProject = ContainerUtil.newHashSet();
    @NotNull final Map<VirtualFile, Module> excludedFromModule = ContainerUtil.newHashMap();
    @NotNull final Map<VirtualFile, String> packagePrefix = ContainerUtil.newHashMap();

    @NotNull
    Set<VirtualFile> getAllRoots() {
      LinkedHashSet<VirtualFile> result = ContainerUtil.newLinkedHashSet();
      result.addAll(classAndSourceRoots);
      result.addAll(contentRootOf.keySet());
      result.addAll(excludedFromLibraries.keySet());
      result.addAll(excludedFromModule.keySet());
      result.addAll(excludedFromProject);
      return result;
    }

    @Nullable
    private VirtualFile findModuleRootInfo(@NotNull List<VirtualFile> hierarchy) {
      for (VirtualFile root : hierarchy) {
        Module module = contentRootOf.get(root);
        Module excludedFrom = excludedFromModule.get(root);
        if (module != null && excludedFrom != module) {
          return root;
        }
        if (excludedFrom != null || excludedFromProject.contains(root)) {
          return null;
        }
      }
      return null;
    }

    @Nullable
    private VirtualFile findNearestContentRootForExcluded(@NotNull List<VirtualFile> hierarchy) {
      for (VirtualFile root : hierarchy) {
        if (contentRootOf.containsKey(root)) {
          return root;
        }
      }
      return null;
    }

    @Nullable
    private VirtualFile findLibraryRootInfo(@NotNull List<VirtualFile> hierarchy, boolean source) {
      Set<Library> librariesToIgnore = ContainerUtil.newHashSet();
      for (VirtualFile root : hierarchy) {
        librariesToIgnore.addAll(excludedFromLibraries.get(root));
        if (source
            && libraryOrSdkSources.contains(root)
            && (!sourceOfLibraries.containsKey(root)
                || !librariesToIgnore.containsAll(sourceOfLibraries.get(root)))) {
          return root;
        } else if (!source
            && libraryOrSdkClasses.contains(root)
            && (!classOfLibraries.containsKey(root)
                || !librariesToIgnore.containsAll(classOfLibraries.get(root)))) {
          return root;
        }
      }
      return null;
    }

    private String calcPackagePrefix(
        @NotNull VirtualFile root,
        @NotNull List<VirtualFile> hierarchy,
        VirtualFile moduleContentRoot,
        VirtualFile libraryClassRoot,
        VirtualFile librarySourceRoot) {
      VirtualFile packageRoot =
          findPackageRootInfo(hierarchy, moduleContentRoot, libraryClassRoot, librarySourceRoot);
      String prefix = packagePrefix.get(packageRoot);
      if (prefix != null && !root.equals(packageRoot)) {
        assert packageRoot != null;
        String relative = VfsUtilCore.getRelativePath(root, packageRoot, '.');
        prefix = StringUtil.isEmpty(prefix) ? relative : prefix + '.' + relative;
      }
      return prefix;
    }

    @Nullable
    private VirtualFile findPackageRootInfo(
        @NotNull List<VirtualFile> hierarchy,
        VirtualFile moduleContentRoot,
        VirtualFile libraryClassRoot,
        VirtualFile librarySourceRoot) {
      for (VirtualFile root : hierarchy) {
        if (moduleContentRoot != null
            && sourceRootOf.get(root).contains(contentRootOf.get(moduleContentRoot))
            && librarySourceRoot == null) {
          return root;
        }
        if (root.equals(libraryClassRoot) || root.equals(librarySourceRoot)) {
          return root;
        }
        if (root.equals(moduleContentRoot)
            && !sourceRootOf.containsKey(root)
            && librarySourceRoot == null
            && libraryClassRoot == null) {
          return null;
        }
      }
      return null;
    }

    @NotNull
    private LinkedHashSet<OrderEntry> getLibraryOrderEntries(
        @NotNull List<VirtualFile> hierarchy,
        @Nullable VirtualFile libraryClassRoot,
        @Nullable VirtualFile librarySourceRoot,
        @NotNull MultiMap<VirtualFile, OrderEntry> libClassRootEntries,
        @NotNull MultiMap<VirtualFile, OrderEntry> libSourceRootEntries) {
      LinkedHashSet<OrderEntry> orderEntries = ContainerUtil.newLinkedHashSet();
      for (VirtualFile root : hierarchy) {
        if (root.equals(libraryClassRoot) && !sourceRootOf.containsKey(root)) {
          orderEntries.addAll(libClassRootEntries.get(root));
        }
        if (root.equals(librarySourceRoot) && libraryClassRoot == null) {
          orderEntries.addAll(libSourceRootEntries.get(root));
        }
        if (libClassRootEntries.containsKey(root)
            || sourceRootOf.containsKey(root) && librarySourceRoot == null) {
          break;
        }
      }
      return orderEntries;
    }

    @Nullable
    private ModuleSourceOrderEntry getModuleSourceEntry(
        @NotNull List<VirtualFile> hierarchy,
        @NotNull VirtualFile moduleContentRoot,
        @NotNull MultiMap<VirtualFile, OrderEntry> libClassRootEntries) {
      Module module = contentRootOf.get(moduleContentRoot);
      for (VirtualFile root : hierarchy) {
        if (sourceRootOf.get(root).contains(module)) {
          return ContainerUtil.findInstance(
              ModuleRootManager.getInstance(module).getOrderEntries(),
              ModuleSourceOrderEntry.class);
        }
        if (libClassRootEntries.containsKey(root)) {
          return null;
        }
      }
      return null;
    }
  }
/** @author peter */
public class StandardInstructionVisitor extends InstructionVisitor {
  private static final Object ANY_VALUE = new Object();
  private final Set<BinopInstruction> myReachable = new THashSet<BinopInstruction>();
  private final Set<BinopInstruction> myCanBeNullInInstanceof = new THashSet<BinopInstruction>();
  private final MultiMap<PushInstruction, Object> myPossibleVariableValues = MultiMap.createSet();
  private final Set<PsiElement> myNotToReportReachability = new THashSet<PsiElement>();
  private final Set<InstanceofInstruction> myUsefulInstanceofs =
      new THashSet<InstanceofInstruction>();

  @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
  private final FactoryMap<MethodCallInstruction, Nullness> myReturnTypeNullability =
      new FactoryMap<MethodCallInstruction, Nullness>() {
        @Override
        protected Nullness create(MethodCallInstruction key) {
          final PsiCallExpression callExpression = key.getCallExpression();
          if (callExpression instanceof PsiNewExpression) {
            return Nullness.NOT_NULL;
          }

          return callExpression != null
              ? DfaPsiUtil.getElementNullability(
                  key.getResultType(), callExpression.resolveMethod())
              : null;
        }
      };

  @Override
  public DfaInstructionState[] visitAssign(
      AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
    DfaValue dfaSource = memState.pop();
    DfaValue dfaDest = memState.pop();

    if (dfaDest instanceof DfaVariableValue) {
      DfaVariableValue var = (DfaVariableValue) dfaDest;

      DfaValueFactory factory = runner.getFactory();
      if (dfaSource instanceof DfaVariableValue
          && factory.getVarFactory().getAllQualifiedBy(var).contains(dfaSource)) {
        Nullness nullability =
            memState.isNotNull(dfaSource)
                ? Nullness.NOT_NULL
                : ((DfaVariableValue) dfaSource).getInherentNullability();
        dfaSource =
            factory.createTypeValue(((DfaVariableValue) dfaSource).getVariableType(), nullability);
      }

      if (var.getInherentNullability() == Nullness.NOT_NULL) {
        checkNotNullable(
            memState,
            dfaSource,
            NullabilityProblem.assigningToNotNull,
            instruction.getRExpression());
      }
      final PsiModifierListOwner psi = var.getPsiVariable();
      if (!(psi instanceof PsiField) || !psi.hasModifierProperty(PsiModifier.VOLATILE)) {
        memState.setVarValue(var, dfaSource);
      }
    } else if (dfaDest instanceof DfaTypeValue && ((DfaTypeValue) dfaDest).isNotNull()) {
      checkNotNullable(
          memState, dfaSource, NullabilityProblem.assigningToNotNull, instruction.getRExpression());
    }

    memState.push(dfaDest);

    return nextInstruction(instruction, runner, memState);
  }

  @Override
  public DfaInstructionState[] visitCheckReturnValue(
      CheckReturnValueInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
    final DfaValue retValue = memState.pop();
    checkNotNullable(
        memState, retValue, NullabilityProblem.nullableReturn, instruction.getReturn());
    return nextInstruction(instruction, runner, memState);
  }

  @Override
  public DfaInstructionState[] visitFieldReference(
      FieldReferenceInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
    final DfaValue qualifier = memState.pop();
    if (!checkNotNullable(
        memState, qualifier, NullabilityProblem.fieldAccessNPE, instruction.getElementToAssert())) {
      forceNotNull(runner, memState, qualifier);
    }

    return nextInstruction(instruction, runner, memState);
  }

  @Override
  public DfaInstructionState[] visitPush(
      PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
    if (instruction.isReferenceRead()) {
      DfaValue dfaValue = instruction.getValue();
      if (dfaValue instanceof DfaVariableValue) {
        DfaConstValue constValue = memState.getConstantValue((DfaVariableValue) dfaValue);
        myPossibleVariableValues.putValue(
            instruction,
            constValue != null
                    && (constValue.getValue() == null || constValue.getValue() instanceof Boolean)
                ? constValue
                : ANY_VALUE);
      }
    }
    return super.visitPush(instruction, runner, memState);
  }

  public List<Pair<PsiReferenceExpression, DfaConstValue>> getConstantReferenceValues() {
    List<Pair<PsiReferenceExpression, DfaConstValue>> result = ContainerUtil.newArrayList();
    for (PushInstruction instruction : myPossibleVariableValues.keySet()) {
      Collection<Object> values = myPossibleVariableValues.get(instruction);
      if (values.size() == 1) {
        Object singleValue = values.iterator().next();
        if (singleValue != ANY_VALUE) {
          result.add(
              Pair.create(
                  (PsiReferenceExpression) instruction.getPlace(), (DfaConstValue) singleValue));
        }
      }
    }
    return result;
  }

  @Override
  public DfaInstructionState[] visitTypeCast(
      TypeCastInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
    final DfaValueFactory factory = runner.getFactory();
    DfaValue dfaExpr = factory.createValue(instruction.getCasted());
    if (dfaExpr != null) {
      DfaTypeValue dfaType =
          (DfaTypeValue) factory.createTypeValue(instruction.getCastTo(), Nullness.UNKNOWN);
      DfaRelationValue dfaInstanceof =
          factory.getRelationFactory().createRelation(dfaExpr, dfaType, INSTANCEOF_KEYWORD, false);
      if (dfaInstanceof != null && !memState.applyInstanceofOrNull(dfaInstanceof)) {
        onInstructionProducesCCE(instruction);
      }
    }

    if (instruction.getCastTo() instanceof PsiPrimitiveType) {
      memState.push(runner.getFactory().getBoxedFactory().createUnboxed(memState.pop()));
    }

    return nextInstruction(instruction, runner, memState);
  }

  protected void onInstructionProducesCCE(TypeCastInstruction instruction) {}

  @Override
  public DfaInstructionState[] visitMethodCall(
      final MethodCallInstruction instruction,
      final DataFlowRunner runner,
      final DfaMemoryState memState) {
    DfaValue[] argValues = popCallArguments(instruction, runner, memState);
    final DfaValue qualifier = popQualifier(instruction, runner, memState);

    List<DfaMemoryState> currentStates = ContainerUtil.newArrayList(memState);
    Set<DfaMemoryState> finalStates = ContainerUtil.newLinkedHashSet();
    if (argValues != null) {
      for (MethodContract contract : instruction.getContracts()) {
        currentStates =
            addContractResults(
                argValues, contract, currentStates, instruction, runner.getFactory(), finalStates);
      }
    }
    for (DfaMemoryState state : currentStates) {
      state.push(getMethodResultValue(instruction, qualifier, runner.getFactory()));
      finalStates.add(state);
    }

    return ContainerUtil.map2Array(
        finalStates,
        DfaInstructionState.class,
        new Function<DfaMemoryState, DfaInstructionState>() {
          @Override
          public DfaInstructionState fun(DfaMemoryState state) {
            if (instruction.shouldFlushFields()) {
              state.flushFields();
            }
            return new DfaInstructionState(
                runner.getInstruction(instruction.getIndex() + 1), state);
          }
        });
  }

  @Nullable
  private DfaValue[] popCallArguments(
      MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
    final PsiExpression[] args = instruction.getArgs();

    PsiMethod method = instruction.getTargetMethod();
    boolean varargCall = instruction.isVarArgCall();
    DfaValue[] argValues;
    if (method == null || instruction.getContracts().isEmpty()) {
      argValues = null;
    } else {
      int paramCount = method.getParameterList().getParametersCount();
      if (paramCount == args.length || method.isVarArgs() && args.length >= paramCount - 1) {
        argValues = new DfaValue[paramCount];
        if (varargCall) {
          argValues[paramCount - 1] = DfaUnknownValue.getInstance();
        }
      } else {
        argValues = null;
      }
    }

    for (int i = 0; i < args.length; i++) {
      final DfaValue arg = memState.pop();
      int paramIndex = args.length - i - 1;
      if (argValues != null && (paramIndex < argValues.length - 1 || !varargCall)) {
        argValues[paramIndex] = arg;
      }

      PsiExpression expr = args[paramIndex];
      Nullness requiredNullability = instruction.getArgRequiredNullability(expr);
      if (requiredNullability == Nullness.NOT_NULL) {
        if (!checkNotNullable(
            memState, arg, NullabilityProblem.passingNullableToNotNullParameter, expr)) {
          forceNotNull(runner, memState, arg);
        }
      } else if (requiredNullability == Nullness.UNKNOWN) {
        checkNotNullable(
            memState, arg, NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter, expr);
      }
    }
    return argValues;
  }

  private DfaValue popQualifier(
      MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
    @NotNull final DfaValue qualifier = memState.pop();
    boolean unboxing = instruction.getMethodType() == MethodCallInstruction.MethodType.UNBOXING;
    NullabilityProblem problem =
        unboxing ? NullabilityProblem.unboxingNullable : NullabilityProblem.callNPE;
    PsiExpression anchor = unboxing ? instruction.getContext() : instruction.getCallExpression();
    if (!checkNotNullable(memState, qualifier, problem, anchor)) {
      forceNotNull(runner, memState, qualifier);
    }
    return qualifier;
  }

  private List<DfaMemoryState> addContractResults(
      DfaValue[] argValues,
      MethodContract contract,
      List<DfaMemoryState> states,
      MethodCallInstruction instruction,
      DfaValueFactory factory,
      Set<DfaMemoryState> finalStates) {
    DfaConstValue.Factory constFactory = factory.getConstFactory();
    List<DfaMemoryState> falseStates = ContainerUtil.newArrayList();
    for (int i = 0; i < argValues.length; i++) {
      DfaValue argValue = argValues[i];
      MethodContract.ValueConstraint constraint = contract.arguments[i];
      DfaConstValue expectedValue = constraint.getComparisonValue(factory);
      if (expectedValue == null) continue;

      boolean invertCondition = constraint.shouldUseNonEqComparison();
      DfaValue condition =
          factory
              .getRelationFactory()
              .createRelation(argValue, expectedValue, EQEQ, invertCondition);
      if (condition == null) {
        if (!(argValue instanceof DfaConstValue)) {
          for (DfaMemoryState state : states) {
            falseStates.add(state.createCopy());
          }
          continue;
        }
        condition =
            constFactory.createFromValue(
                (argValue == expectedValue) != invertCondition, PsiType.BOOLEAN, null);
      }

      List<DfaMemoryState> nextStates = ContainerUtil.newArrayList();
      for (DfaMemoryState state : states) {
        boolean unknownVsNull =
            expectedValue == constFactory.getNull()
                && argValue instanceof DfaVariableValue
                && ((DfaMemoryStateImpl) state)
                        .getVariableState((DfaVariableValue) argValue)
                        .getNullability()
                    == Nullness.UNKNOWN;
        DfaMemoryState falseCopy = state.createCopy();
        if (state.applyCondition(condition)) {
          if (unknownVsNull && !invertCondition) {
            state.markEphemeral();
          }
          nextStates.add(state);
        }
        if (falseCopy.applyCondition(condition.createNegated())) {
          if (unknownVsNull && invertCondition) {
            falseCopy.markEphemeral();
          }
          falseStates.add(falseCopy);
        }
      }
      states = nextStates;
    }

    for (DfaMemoryState state : states) {
      state.push(getDfaContractReturnValue(contract, instruction, factory));
      finalStates.add(state);
    }

    return falseStates;
  }

  private DfaValue getDfaContractReturnValue(
      MethodContract contract, MethodCallInstruction instruction, DfaValueFactory factory) {
    switch (contract.returnValue) {
      case NULL_VALUE:
        return factory.getConstFactory().getNull();
      case NOT_NULL_VALUE:
        return factory.createTypeValue(instruction.getResultType(), Nullness.NOT_NULL);
      case TRUE_VALUE:
        return factory.getConstFactory().getTrue();
      case FALSE_VALUE:
        return factory.getConstFactory().getFalse();
      case THROW_EXCEPTION:
        return factory.getConstFactory().getContractFail();
      default:
        return getMethodResultValue(instruction, null, factory);
    }
  }

  private static void forceNotNull(DataFlowRunner runner, DfaMemoryState memState, DfaValue arg) {
    if (arg instanceof DfaVariableValue) {
      DfaVariableValue var = (DfaVariableValue) arg;
      memState.setVarValue(
          var, runner.getFactory().createTypeValue(var.getVariableType(), Nullness.NOT_NULL));
    }
  }

  @NotNull
  private DfaValue getMethodResultValue(
      MethodCallInstruction instruction,
      @Nullable DfaValue qualifierValue,
      DfaValueFactory factory) {
    DfaValue precalculated = instruction.getPrecalculatedReturnValue();
    if (precalculated != null) {
      return precalculated;
    }

    final PsiType type = instruction.getResultType();
    final MethodCallInstruction.MethodType methodType = instruction.getMethodType();

    if (methodType == MethodCallInstruction.MethodType.UNBOXING) {
      return factory.getBoxedFactory().createUnboxed(qualifierValue);
    }

    if (methodType == MethodCallInstruction.MethodType.BOXING) {
      DfaValue boxed = factory.getBoxedFactory().createBoxed(qualifierValue);
      return boxed == null ? factory.createTypeValue(type, Nullness.NOT_NULL) : boxed;
    }

    if (methodType == MethodCallInstruction.MethodType.CAST) {
      assert qualifierValue != null;
      if (qualifierValue instanceof DfaConstValue) {
        Object casted =
            TypeConversionUtil.computeCastTo(((DfaConstValue) qualifierValue).getValue(), type);
        return factory
            .getConstFactory()
            .createFromValue(casted, type, ((DfaConstValue) qualifierValue).getConstant());
      }
      return qualifierValue;
    }

    if (type != null && (type instanceof PsiClassType || type.getArrayDimensions() > 0)) {
      Nullness nullability = myReturnTypeNullability.get(instruction);
      if (nullability == Nullness.UNKNOWN && factory.isUnknownMembersAreNullable()) {
        nullability = Nullness.NULLABLE;
      }
      return factory.createTypeValue(type, nullability);
    }
    return DfaUnknownValue.getInstance();
  }

  protected boolean checkNotNullable(
      DfaMemoryState state, DfaValue value, NullabilityProblem problem, PsiElement anchor) {
    boolean notNullable = state.checkNotNullable(value);
    if (notNullable
        && problem != NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter) {
      DfaValueFactory factory = ((DfaMemoryStateImpl) state).getFactory();
      state.applyCondition(
          factory
              .getRelationFactory()
              .createRelation(value, factory.getConstFactory().getNull(), NE, false));
    }
    return notNullable;
  }

  @Override
  public DfaInstructionState[] visitBinop(
      BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
    myReachable.add(instruction);

    DfaValue dfaRight = memState.pop();
    DfaValue dfaLeft = memState.pop();

    final IElementType opSign = instruction.getOperationSign();
    if (opSign != null) {
      DfaInstructionState[] states =
          handleConstantComparison(instruction, runner, memState, dfaRight, dfaLeft, opSign);
      if (states == null) {
        states = handleRelationBinop(instruction, runner, memState, dfaRight, dfaLeft);
      }
      if (states != null) {
        return states;
      }

      if (PLUS == opSign) {
        memState.push(instruction.getNonNullStringValue(runner.getFactory()));
      } else {
        if (instruction instanceof InstanceofInstruction) {
          handleInstanceof((InstanceofInstruction) instruction, dfaRight, dfaLeft);
        }
        memState.push(DfaUnknownValue.getInstance());
      }
    } else {
      memState.push(DfaUnknownValue.getInstance());
    }

    instruction.setTrueReachable(); // Not a branching instruction actually.
    instruction.setFalseReachable();

    return nextInstruction(instruction, runner, memState);
  }

  @Nullable
  private DfaInstructionState[] handleRelationBinop(
      BinopInstruction instruction,
      DataFlowRunner runner,
      DfaMemoryState memState,
      DfaValue dfaRight,
      DfaValue dfaLeft) {
    DfaValueFactory factory = runner.getFactory();
    final Instruction next = runner.getInstruction(instruction.getIndex() + 1);
    DfaRelationValue dfaRelation =
        factory
            .getRelationFactory()
            .createRelation(dfaLeft, dfaRight, instruction.getOperationSign(), false);
    if (dfaRelation == null) {
      return null;
    }

    myCanBeNullInInstanceof.add(instruction);

    ArrayList<DfaInstructionState> states = new ArrayList<DfaInstructionState>();

    final DfaMemoryState trueCopy = memState.createCopy();
    if (trueCopy.applyCondition(dfaRelation)) {
      trueCopy.push(factory.getConstFactory().getTrue());
      instruction.setTrueReachable();
      states.add(new DfaInstructionState(next, trueCopy));
    }

    //noinspection UnnecessaryLocalVariable
    DfaMemoryState falseCopy = memState;
    if (falseCopy.applyCondition(dfaRelation.createNegated())) {
      falseCopy.push(factory.getConstFactory().getFalse());
      instruction.setFalseReachable();
      states.add(new DfaInstructionState(next, falseCopy));
      if (instruction instanceof InstanceofInstruction && !falseCopy.isNull(dfaLeft)) {
        myUsefulInstanceofs.add((InstanceofInstruction) instruction);
      }
    }

    return states.toArray(new DfaInstructionState[states.size()]);
  }

  public void skipConstantConditionReporting(@Nullable PsiElement anchor) {
    ContainerUtil.addIfNotNull(myNotToReportReachability, anchor);
  }

  private void handleInstanceof(
      InstanceofInstruction instruction, DfaValue dfaRight, DfaValue dfaLeft) {
    if (dfaLeft instanceof DfaTypeValue && dfaRight instanceof DfaTypeValue) {
      if (!((DfaTypeValue) dfaLeft).isNotNull()) {
        myCanBeNullInInstanceof.add(instruction);
      }

      if (((DfaTypeValue) dfaRight)
          .getDfaType()
          .isAssignableFrom(((DfaTypeValue) dfaLeft).getDfaType())) {
        return;
      }
    }
    myUsefulInstanceofs.add(instruction);
  }

  @Nullable
  private static DfaInstructionState[] handleConstantComparison(
      BinopInstruction instruction,
      DataFlowRunner runner,
      DfaMemoryState memState,
      DfaValue dfaRight,
      DfaValue dfaLeft,
      IElementType opSign) {
    if (dfaRight instanceof DfaConstValue && dfaLeft instanceof DfaVariableValue) {
      Object value = ((DfaConstValue) dfaRight).getValue();
      if (value instanceof Number) {
        DfaInstructionState[] result =
            checkComparingWithConstant(
                instruction,
                runner,
                memState,
                (DfaVariableValue) dfaLeft,
                opSign,
                ((Number) value).doubleValue());
        if (result != null) {
          return result;
        }
      }
    }
    if (dfaRight instanceof DfaVariableValue && dfaLeft instanceof DfaConstValue) {
      return handleConstantComparison(
          instruction,
          runner,
          memState,
          dfaLeft,
          dfaRight,
          DfaRelationValue.getSymmetricOperation(opSign));
    }

    if (EQEQ != opSign && NE != opSign) {
      return null;
    }

    if (dfaLeft instanceof DfaConstValue && dfaRight instanceof DfaConstValue
        || dfaLeft == runner.getFactory().getConstFactory().getContractFail()
        || dfaRight == runner.getFactory().getConstFactory().getContractFail()) {
      boolean negated =
          (NE == opSign)
              ^ (DfaMemoryStateImpl.isNaN(dfaLeft) || DfaMemoryStateImpl.isNaN(dfaRight));
      if (dfaLeft == dfaRight ^ negated) {
        return alwaysTrue(instruction, runner, memState);
      }
      return alwaysFalse(instruction, runner, memState);
    }

    return null;
  }

  @Nullable
  private static DfaInstructionState[] checkComparingWithConstant(
      BinopInstruction instruction,
      DataFlowRunner runner,
      DfaMemoryState memState,
      DfaVariableValue var,
      IElementType opSign,
      double comparedWith) {
    DfaConstValue knownConstantValue = memState.getConstantValue(var);
    Object knownValue = knownConstantValue == null ? null : knownConstantValue.getValue();
    if (knownValue instanceof Number) {
      double knownDouble = ((Number) knownValue).doubleValue();
      return checkComparisonWithKnownRange(
          instruction, runner, memState, opSign, comparedWith, knownDouble, knownDouble);
    }

    PsiType varType = var.getVariableType();
    if (!(varType instanceof PsiPrimitiveType)) return null;

    if (varType == PsiType.FLOAT || varType == PsiType.DOUBLE) return null;

    double minValue =
        varType == PsiType.BYTE
            ? Byte.MIN_VALUE
            : varType == PsiType.SHORT
                ? Short.MIN_VALUE
                : varType == PsiType.INT
                    ? Integer.MIN_VALUE
                    : varType == PsiType.CHAR ? Character.MIN_VALUE : Long.MIN_VALUE;
    double maxValue =
        varType == PsiType.BYTE
            ? Byte.MAX_VALUE
            : varType == PsiType.SHORT
                ? Short.MAX_VALUE
                : varType == PsiType.INT
                    ? Integer.MAX_VALUE
                    : varType == PsiType.CHAR ? Character.MAX_VALUE : Long.MAX_VALUE;

    return checkComparisonWithKnownRange(
        instruction, runner, memState, opSign, comparedWith, minValue, maxValue);
  }

  @Nullable
  private static DfaInstructionState[] checkComparisonWithKnownRange(
      BinopInstruction instruction,
      DataFlowRunner runner,
      DfaMemoryState memState,
      IElementType opSign,
      double comparedWith,
      double rangeMin,
      double rangeMax) {
    if (comparedWith < rangeMin || comparedWith > rangeMax) {
      if (opSign == EQEQ) return alwaysFalse(instruction, runner, memState);
      if (opSign == NE) return alwaysTrue(instruction, runner, memState);
    }

    if (opSign == LT && comparedWith <= rangeMin) return alwaysFalse(instruction, runner, memState);
    if (opSign == LT && comparedWith > rangeMax) return alwaysTrue(instruction, runner, memState);
    if (opSign == LE && comparedWith >= rangeMax) return alwaysTrue(instruction, runner, memState);

    if (opSign == GT && comparedWith >= rangeMax) return alwaysFalse(instruction, runner, memState);
    if (opSign == GT && comparedWith < rangeMin) return alwaysTrue(instruction, runner, memState);
    if (opSign == GE && comparedWith <= rangeMin) return alwaysTrue(instruction, runner, memState);

    return null;
  }

  private static DfaInstructionState[] alwaysFalse(
      BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
    memState.push(runner.getFactory().getConstFactory().getFalse());
    instruction.setFalseReachable();
    return nextInstruction(instruction, runner, memState);
  }

  private static DfaInstructionState[] alwaysTrue(
      BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
    memState.push(runner.getFactory().getConstFactory().getTrue());
    instruction.setTrueReachable();
    return nextInstruction(instruction, runner, memState);
  }

  public boolean isInstanceofRedundant(InstanceofInstruction instruction) {
    return !myUsefulInstanceofs.contains(instruction)
        && !instruction.isConditionConst()
        && myReachable.contains(instruction);
  }

  public boolean canBeNull(BinopInstruction instruction) {
    return myCanBeNullInInstanceof.contains(instruction);
  }

  public boolean silenceConstantCondition(@Nullable PsiElement element) {
    for (PsiElement skipped : myNotToReportReachability) {
      if (PsiTreeUtil.isAncestor(element, skipped, false)) {
        return true;
      }
    }
    if (PsiTreeUtil.findChildOfType(element, PsiAssignmentExpression.class) != null) {
      return true;
    }
    return false;
  }
}
예제 #4
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;
    }
  }