@NotNull
  public static FragmentVariableInfos obtainVariableFlowInformation(@NotNull final GrStatement first,
                                                                    @NotNull final GrStatement last,
                                                                    @NotNull final GrControlFlowOwner flowOwner,
                                                                    @NotNull GroovyControlFlow flow) {

    final Int2ObjectMap<int[]> dfaResult = inferDfaResult(flow.getFlow());

    final LinkedHashSet<Integer> fragmentInstructions = getFragmentInstructions(first, last, flow.getFlow());
    final int[] postorder = reversedPostOrder(flow.getFlow());
    LinkedHashSet<Integer> reachableFromFragmentReads = getReachable(fragmentInstructions, flow.getFlow(), dfaResult, postorder);
    LinkedHashSet<Integer> fragmentReads = filterReads(fragmentInstructions, flow.getFlow());

    final Map<String, VariableInfo> imap = new LinkedHashMap<>();
    final Map<String, VariableInfo> omap = new LinkedHashMap<>();

    final PsiManager manager = first.getManager();

    for (final Integer ref : fragmentReads) {
      ReadWriteVariableInstruction rwInstruction = (ReadWriteVariableInstruction)flow.getFlow()[ref];
      final int[] defs = dfaResult.get((int)ref);
      assert defs != null;
      int descriptorId = rwInstruction.getDescriptor();
      VariableDescriptor descriptor = flow.getVarIndices()[descriptorId];
      if (!allDefsInFragment(defs, fragmentInstructions) || isLocallyDeclaredOutOf(rwInstruction.getElement(), descriptor, flowOwner)) {
        addVariable(descriptor.getName(), imap, manager, getType(rwInstruction.getElement()));
      }
    }

    Set<Integer> outerBound = getFragmentOuterBound(fragmentInstructions, flow.getFlow());
    for (final Integer ref : reachableFromFragmentReads) {
      ReadWriteVariableInstruction rwInstruction = (ReadWriteVariableInstruction)flow.getFlow()[ref];
      int descriptorId = rwInstruction.getDescriptor();
      VariableDescriptor descriptor = flow.getVarIndices()[descriptorId];
      final int[] defs = dfaResult.get((int)ref);
      assert defs != null;
      if (anyDefInFragment(defs, fragmentInstructions)) {
        for (int insnNum : outerBound) {
          addVariable(descriptor.getName(), omap, manager, getVariableTypeAt(flowOwner, flow.getFlow()[insnNum], descriptorId, descriptor));
        }

        if (!allProperDefsInFragment(defs, ref, fragmentInstructions, postorder)) {
          PsiType inputType = getType(rwInstruction.getElement());
          addVariable(descriptor.getName(), imap, manager, inputType);
        }
      }
    }

    addClosureUsages(imap, omap, first, last, flowOwner);

    return new FragmentVariableInfos() {
      @Override
      public VariableInfo[] getInputVariableNames() {
        return imap.values().toArray(VariableInfo.EMPTY_ARRAY);
      }

      @Override
      public VariableInfo[] getOutputVariableNames() {
        return omap.values().toArray(VariableInfo.EMPTY_ARRAY);
      }
    };
  }
@NotNull
  public static FragmentVariableInfos obtainVariableFlowInformation(@NotNull final GrStatement first,
                                                                    @NotNull final GrStatement last,
                                                                    @NotNull final GrControlFlowOwner flowOwner,
                                                                    @NotNull GroovyControlFlow flow)