private void checkStateForLeakedObligations(
        State state, Map<Obligation, State> leakedObligationMap) throws IllegalStateException {
      if (DEBUG) {
        Path path = state.getPath();
        if (path.getLength() > 0
            && path.getBlockIdAt(path.getLength() - 1) != cfg.getExit().getLabel()) {
          throw new IllegalStateException(
              "path " + path + " at cfg exit has no label for exit block");
        }
      }

      for (int id = 0; id < database.getFactory().getMaxObligationTypes(); ++id) {
        Obligation obligation = database.getFactory().getObligationById(id);
        // If the raw count produced by the analysis
        // for this obligation type is 0,
        // assume everything is ok on this state's path.
        int rawLeakCount = state.getObligationSet().getCount(id);
        if (rawLeakCount == 0) {
          continue;
        }

        // Apply the false-positive suppression heuristics
        int leakCount = getAdjustedLeakCount(state, id);

        if (leakCount > 0) {
          leakedObligationMap.put(obligation, state);
        }
        // TODO: if the leak count is less than 0, then a nonexistent
        // resource was closed
      }
    }
      private State getTransferState(InstructionHandle handle) {
        StateSet stateSet;
        try {
          stateSet = dataflow.getFactAtLocation(new Location(handle, curBlock));
        } catch (DataflowAnalysisException e) {
          bugReporter.logError("Error checking obligation state at " + handle, e);
          return null;
        }

        List<State> prefixes = stateSet.getPrefixStates(state.getPath());
        if (prefixes.size() != 1) {
          // Could this happen?
          if (DEBUG_FP) {
            System.out.println(
                "at "
                    + handle
                    + " in "
                    + xmethod
                    + " found "
                    + prefixes.size()
                    + " states which are prefixes of error state");
          }
          return null;
        }

        return prefixes.get(0);
      }
    /**
     * Get the adjusted leak count for the given State and obligation type. Use heuristics to
     * account for:
     *
     * <ul>
     *   <li>null checks (count the number of times the supposedly leaked obligation is compared to
     *       null, and subtract those from the leak count)
     *   <li>field assignments (count number of times obligation type is assigned to a field, and
     *       subtract those from the leak count)
     *   <li>return statements (if an instance of the obligation type is returned from the method,
     *       subtract one from leak count)
     * </ul>
     *
     * @return the adjusted leak count (positive if leaked obligation, negative if attempt to
     *     release an un-acquired obligation)
     */
    private int getAdjustedLeakCount(State state, int obligationId) {

      final Obligation obligation = database.getFactory().getObligationById(obligationId);
      Path path = state.getPath();
      PostProcessingPathVisitor visitor = new PostProcessingPathVisitor(obligation, state);
      path.acceptVisitor(cfg, visitor);

      if (visitor.couldNotAnalyze()) {
        return 0;
      } else {
        return visitor.getAdjustedLeakCount();
      }
    }
 public PostProcessingPathVisitor(Obligation possiblyLeakedObligation /*
                                                                            * ,
                                                                            * int
                                                                            * initialLeakCount
                                                                            */, State state) {
   this.possiblyLeakedObligation = possiblyLeakedObligation;
   this.state = state;
   this.adjustedLeakCount =
       state.getObligationSet().getCount(possiblyLeakedObligation.getId());
   if (COMPUTE_TRANSFERS) {
     this.transferList = new LinkedList<PossibleObligationTransfer>();
   }
 }
    private void reportWarning(Obligation obligation, State state, StateSet factAtExit) {
      String className = obligation.getClassName();

      if (methodDescriptor.isStatic()
          && methodDescriptor.getName().equals("main")
          && methodDescriptor.getSignature().equals("([Ljava/lang/String;)V")
          && (className.contains("InputStream")
              || className.contains("Reader")
              || factAtExit.isOnExceptionPath())) {
        // Don't report unclosed input streams and readers in main()
        // methods
        return;
      }
      String bugPattern =
          factAtExit.isOnExceptionPath()
              ? "OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE"
              : "OBL_UNSATISFIED_OBLIGATION";
      BugInstance bugInstance =
          new BugInstance(FindUnsatisfiedObligation.this, bugPattern, NORMAL_PRIORITY)
              .addClassAndMethod(methodDescriptor)
              .addClass(className)
              .describe("CLASS_REFTYPE");

      // Report how many instances of the obligation are remaining
      bugInstance
          .addInt(state.getObligationSet().getCount(obligation.getId()))
          .describe(IntAnnotation.INT_OBLIGATIONS_REMAINING);

      // Add source line information
      annotateWarningWithSourceLineInformation(state, obligation, bugInstance);

      if (REPORT_OBLIGATION_SET) {
        bugInstance
            .addString(state.getObligationSet().toString())
            .describe(StringAnnotation.REMAINING_OBLIGATIONS_ROLE);
      }

      bugReporter.reportBug(bugInstance);
    }
      private void applyPossibleObligationTransfers() {
        //
        // See if we recorded any possible obligation transfers
        // that might have created a "wrapper" object.
        // In many cases, it is correct to close either
        // the "wrapped" or "wrapper" object.
        // So, if we see a possible transfer, and we see
        // a +1/-1 obligation count for the pair
        // (consumed and produced obligation types),
        // rather than 0/0,
        // then we will assume that which resource was closed
        // (wrapper or wrapped) was the opposite of what
        // we expected.
        //
        for (PossibleObligationTransfer transfer : transferList) {
          if (DEBUG_FP) {
            System.out.println("Checking possible transfer " + transfer + "...");
          }

          boolean matches = transfer.matches(possiblyLeakedObligation);

          if (DEBUG_FP) {
            System.out.println("  matches: " + possiblyLeakedObligation);
          }

          if (matches) {
            boolean balanced = transfer.balanced(state);
            if (DEBUG_FP) {
              System.out.println("  balanced: " + balanced + " in " + state.getObligationSet());
            }
            if (balanced) {
              if (DEBUG_FP) {
                System.out.println(
                    "  Suppressing path because "
                        + "a transfer appears to result in balanced "
                        + "outstanding obligations");
              }

              adjustedLeakCount = 0;
              break;
            }
          }
        }
      }
    private void reportPath(
        final BugInstance bugInstance, final Obligation obligation, final State state) {

      Path path = state.getPath();

      // This PathVisitor will traverse the Path and add appropriate
      // SourceLineAnnotations to the BugInstance.
      PathVisitor visitor =
          new PathVisitor() {
            boolean sawFirstCreation;

            SourceLineAnnotation lastSourceLine; // = creationSourceLine;

            BasicBlock curBlock;

            @Override
            public void visitBasicBlock(BasicBlock basicBlock) {
              curBlock = basicBlock;

              // See if the initial instance of the leaked resource
              // is in the entry fact due to a @WillClose annotation.
              if (curBlock == cfg.getEntry()) {
                // Get the entry fact - it should have precisely one
                // state
                StateSet entryFact = dataflow.getResultFact(curBlock);
                Iterator<State> i = entryFact.stateIterator();
                if (i.hasNext()) {
                  State entryState = i.next();
                  if (entryState.getObligationSet().getCount(obligation.getId()) > 0) {
                    lastSourceLine = SourceLineAnnotation.forFirstLineOfMethod(methodDescriptor);
                    lastSourceLine.setDescription(
                        SourceLineAnnotation.ROLE_OBLIGATION_CREATED_BY_WILLCLOSE_PARAMETER);
                    bugInstance.add(lastSourceLine);
                    sawFirstCreation = true;

                    if (REPORT_PATH_DEBUG) {
                      System.out.println(
                          "  "
                              + obligation
                              + " created by @WillClose parameter at "
                              + lastSourceLine);
                    }
                  }
                }
              }
            }

            @Override
            public void visitInstructionHandle(InstructionHandle handle) {
              boolean isCreation =
                  (dataflow
                      .getAnalysis()
                      .getActionCache()
                      .addsObligation(curBlock, handle, obligation));

              if (!sawFirstCreation && !isCreation) {
                return;
              }

              SourceLineAnnotation sourceLine =
                  SourceLineAnnotation.fromVisitedInstruction(
                      methodDescriptor, new Location(handle, curBlock));
              sourceLine.setDescription(
                  isCreation
                      ? SourceLineAnnotation.ROLE_OBLIGATION_CREATED
                      : SourceLineAnnotation.ROLE_PATH_CONTINUES);

              boolean isInteresting =
                  (sourceLine.getStartLine() > 0)
                      && (lastSourceLine == null
                          || isCreation
                          || sourceLine.getStartLine() != lastSourceLine.getStartLine());

              if (REPORT_PATH_DEBUG) {
                System.out.println(
                    "  "
                        + handle.getPosition()
                        + " --> "
                        + sourceLine
                        + (isInteresting ? " **" : ""));
              }
              if (isInteresting) {
                bugInstance.add(sourceLine);
                lastSourceLine = sourceLine;
                if (isCreation) {
                  sawFirstCreation = true;
                }
              }
            }

            @Override
            public void visitEdge(Edge edge) {
              if (REPORT_PATH_DEBUG) {
                System.out.println(
                    "Edge of type "
                        + Edge.edgeTypeToString(edge.getType())
                        + " to "
                        + edge.getTarget().getLabel());
                if (edge.getTarget().getFirstInstruction() != null) {
                  System.out.println(
                      "  First instruction in target: " + edge.getTarget().getFirstInstruction());
                }
                if (edge.getTarget().isExceptionThrower()) {
                  System.out.println(
                      "  exception thrower for " + edge.getTarget().getExceptionThrower());
                }
                if (edge.isExceptionEdge()) {
                  System.out.println(
                      "  exceptions thrown: " + typeDataflow.getEdgeExceptionSet(edge));
                }
              }
            }
          };

      // Visit the Path
      path.acceptVisitor(cfg, visitor);
    }
      private void checkForPossibleObligationTransfer(
          InvokeInstruction inv, InstructionHandle handle) throws ClassNotFoundException {
        //
        // We will assume that a method invocation might transfer
        // an obligation from one type to another if
        // 1. either
        // - it's a constructor where the constructed
        // type and exactly one param type
        // are obligation types, or
        // - it's a method where the return type and
        // exactly one param type are obligation types
        // 2. at least one instance of the resource "consumed"
        // by the transfer exists at the point of the transfer.
        // E.g., if we see a transfer of InputStream->Reader,
        // there must be an instance of InputStream at
        // the transfer point.
        //

        if (DEBUG_FP) {
          System.out.println("Checking " + handle + " as possible obligation transfer...:");
        }

        // Find the State which is a prefix of the error state
        // at the location of this (possible) transfer.
        State transferState = getTransferState(handle);
        if (transferState == null) {
          if (DEBUG_FP) {
            System.out.println("No transfer state???");
          }
          return;
        }

        String methodName = inv.getMethodName(cpg);
        Type producedType =
            methodName.equals("<init>") ? inv.getReferenceType(cpg) : inv.getReturnType(cpg);

        if (DEBUG_FP && !(producedType instanceof ObjectType)) {
          System.out.println("Produced type " + producedType + " not an ObjectType");
        }

        if (producedType instanceof ObjectType) {
          Obligation produced =
              database.getFactory().getObligationByType((ObjectType) producedType);

          if (DEBUG_FP && produced == null) {
            System.out.println("Produced type  " + producedType + " not an obligation type");
          }

          if (produced != null) {
            XMethod calledMethod = XFactory.createXMethod(inv, cpg);
            Obligation[] params = database.getFactory().getParameterObligationTypes(calledMethod);

            for (int i = 0; i < params.length; i++) {
              Obligation consumed = params[i];

              if (DEBUG_FP && consumed == null) {
                System.out.println("Param " + i + " not an obligation type");
              }

              if (DEBUG_FP && consumed != null && consumed.equals(produced)) {
                System.out.println("Consumed type is the same as produced type");
              }

              if (consumed != null && !consumed.equals(produced)) {
                // See if an instance of the consumed obligation
                // type
                // exists here.
                if (transferState.getObligationSet().getCount(consumed.getId()) > 0) {
                  transferList.add(new PossibleObligationTransfer(consumed, produced));
                  if (DEBUG_FP) {
                    System.out.println(
                        "===> Possible transfer of "
                            + consumed
                            + " to "
                            + produced
                            + " at "
                            + handle);
                  }
                } else if (DEBUG_FP) {
                  System.out.println(
                      handle
                          + " not a transfer "
                          + "of "
                          + consumed
                          + "->"
                          + produced
                          + " because no instances of "
                          + consumed);
                  System.out.println("I see " + transferState.getObligationSet());
                }
              }
            }
          }
        }
      }
 /**
  * Determine whether the state has "balanced" obligation counts for the consumed and produced
  * Obligation types.
  *
  * @param state a State
  * @return true if the obligation counts are balanced, false otherwise
  */
 private boolean balanced(State state) {
   int consumedCount = state.getObligationSet().getCount(consumed.getId());
   int producedCount = state.getObligationSet().getCount(produced.getId());
   return (consumedCount + producedCount == 0) && (consumedCount == 1 || producedCount == 1);
 }