Add EvalEndPath interface to Checker. Now we can check memory leaked at the
end of the path. Need to unify interfaces.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89063 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Analysis/PathSensitive/Checker.h b/include/clang/Analysis/PathSensitive/Checker.h
index 26cfc8a..b7ed20f 100644
--- a/include/clang/Analysis/PathSensitive/Checker.h
+++ b/include/clang/Analysis/PathSensitive/Checker.h
@@ -170,6 +170,8 @@
                             const Stmt *StoreE, SVal location, SVal val) {}
   virtual void EvalDeadSymbols(CheckerContext &C, const Stmt *S,
                                SymbolReaper &SymReaper) {}
+  virtual void EvalEndPath(GREndPathNodeBuilder &B, void *tag,
+                           GRExprEngine &Eng) {}
 };
 } // end clang namespace
 
diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp
index 0b7afe7..2633177 100644
--- a/lib/Analysis/GRExprEngine.cpp
+++ b/lib/Analysis/GRExprEngine.cpp
@@ -902,6 +902,11 @@
 void GRExprEngine::ProcessEndPath(GREndPathNodeBuilder& builder) {
   getTF().EvalEndPath(*this, builder);
   StateMgr.EndPath(builder.getState());
+  for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){
+    void *tag = I->first;
+    Checker *checker = I->second;
+    checker->EvalEndPath(builder, tag, *this);
+  }
 }
 
 /// ProcessSwitch - Called by GRCoreEngine.  Used to generate successor
diff --git a/lib/Analysis/MallocChecker.cpp b/lib/Analysis/MallocChecker.cpp
index 6129358..fdd6a3d 100644
--- a/lib/Analysis/MallocChecker.cpp
+++ b/lib/Analysis/MallocChecker.cpp
@@ -22,8 +22,28 @@
 
 namespace {
 
-enum RefState {
-  Allocated, Released, Escaped
+struct RefState {
+  enum Kind { Allocated, Released, Escaped } K;
+  const Stmt *S;
+
+  RefState(Kind k, const Stmt *s) : K(k), S(s) {}
+
+  bool isAllocated() const { return K == Allocated; }
+  bool isReleased() const { return K == Released; }
+  bool isEscaped() const { return K == Escaped; }
+
+  bool operator==(const RefState &X) const {
+    return K == X.K && S == X.S;
+  }
+
+  static RefState getAllocated(const Stmt *s) { return RefState(Allocated, s); }
+  static RefState getReleased(const Stmt *s) { return RefState(Released, s); }
+  static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const {
+    ID.AddInteger(K);
+    ID.AddPointer(S);
+  }
 };
 
 class VISIBILITY_HIDDEN RegionState {};
@@ -39,25 +59,15 @@
   static void *getTag();
   void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE);
   void EvalDeadSymbols(CheckerContext &C,const Stmt *S,SymbolReaper &SymReaper);
+  void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng);
 private:
   void MallocMem(CheckerContext &C, const CallExpr *CE);
   void FreeMem(CheckerContext &C, const CallExpr *CE);
 };
 }
 
-namespace llvm {
-  template<> struct FoldingSetTrait<RefState> {
-    static void Profile(const RefState &X, FoldingSetNodeID &ID) { 
-      ID.AddInteger(X);
-    }
-    static void Profile(RefState &X, FoldingSetNodeID &ID) { 
-      ID.AddInteger(X);
-    }
-  };
-}
-
 namespace clang {
-  template<>
+  template <>
   struct GRStateTrait<RegionState> 
     : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, RefState> > {
     static void *GDMIndex() { return MallocChecker::getTag(); }
@@ -101,7 +111,8 @@
   SymbolRef Sym = CallVal.getAsLocSymbol();
   assert(Sym);
   // Set the symbol's state to Allocated.
-  const GRState *AllocState = state->set<RegionState>(Sym, Allocated);
+  const GRState *AllocState 
+    = state->set<RegionState>(Sym, RefState::getAllocated(CE));
   C.addTransition(C.GenerateNode(CE, AllocState));
 }
 
@@ -115,7 +126,7 @@
   assert(RS);
 
   // Check double free.
-  if (*RS == Released) {
+  if (RS->isReleased()) {
     ExplodedNode *N = C.GenerateNode(CE, true);
     if (N) {
       if (!BT_DoubleFree)
@@ -130,7 +141,8 @@
   }
 
   // Normal free.
-  const GRState *FreedState = state->set<RegionState>(Sym, Released);
+  const GRState *FreedState 
+    = state->set<RegionState>(Sym, RefState::getReleased(CE));
   C.addTransition(C.GenerateNode(CE, FreedState));
 }
 
@@ -144,17 +156,37 @@
     if (!RS)
       return;
 
-    if (*RS == Allocated) {
+    if (RS->isAllocated()) {
       ExplodedNode *N = C.GenerateNode(S, true);
       if (N) {
         if (!BT_Leak)
           BT_Leak = new BuiltinBug("Memory leak",
                      "Allocated memory never released. Potential memory leak.");
         // FIXME: where it is allocated.
-        BugReport *R = new BugReport(*BT_Leak,
-                                     BT_Leak->getDescription(), N);
+        BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N);
         C.EmitReport(R);
       }
     }
   }
 }
+
+void MallocChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag,
+                                GRExprEngine &Eng) {
+  const GRState *state = B.getState();
+  typedef llvm::ImmutableMap<SymbolRef, RefState> SymMap;
+  SymMap M = state->get<RegionState>();
+
+  for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) {
+    RefState RS = I->second;
+    if (RS.isAllocated()) {
+      ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor());
+      if (N) {
+        if (!BT_Leak)
+          BT_Leak = new BuiltinBug("Memory leak",
+                     "Allocated memory never released. Potential memory leak.");
+        BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N);
+        Eng.getBugReporter().EmitReport(R);
+      }
+    }
+  }
+}
diff --git a/test/Analysis/malloc.c b/test/Analysis/malloc.c
index fb9674a..3dc5843 100644
--- a/test/Analysis/malloc.c
+++ b/test/Analysis/malloc.c
@@ -8,9 +8,8 @@
   return; // expected-warning{{Allocated memory never released. Potential memory leak.}}
 }
 
-// THIS TEST CURRENTLY FAILS.
 void f1_b() {
-  int *p = malloc(10);
+  int *p = malloc(10); // expected-warning{{Allocated memory never released. Potential memory leak.}}
 }
 
 void f2() {
@@ -19,20 +18,20 @@
   free(p); // expected-warning{{Try to free a memory block that has been released}}
 }
 
-// This case tests that storing malloc'ed memory to a static variable which is then returned
-// is not leaked.  In the absence of known contracts for functions or inter-procedural analysis,
-// this is a conservative answer.
+// This case tests that storing malloc'ed memory to a static variable which is
+// then returned is not leaked.  In the absence of known contracts for functions
+// or inter-procedural analysis, this is a conservative answer.
 int *f3() {
   static int *p = 0;
-  p = malloc(10); // no-warning
-  return p;
+  p = malloc(10); // will be fixed.
+  return p; // expected-warning{{Allocated memory never released. Potential memory leak.}}
 }
 
-// This case tests that storing malloc'ed memory to a static global variable which is then returned
-// is not leaked.  In the absence of known contracts for functions or inter-procedural analysis,
-// this is a conservative answer.
+// This case tests that storing malloc'ed memory to a static global variable
+// which is then returned is not leaked.  In the absence of known contracts for
+// functions or inter-procedural analysis, this is a conservative answer.
 static int *p_f4 = 0;
 int *f4() {
-  p_f4 = malloc(10); // no-warning
-  return p_f4;
+  p_f4 = malloc(10); // will be fixed.
+  return p_f4; // expected-warning{{Allocated memory never released. Potential memory leak.}}
 }