[analyzer] Create symbol-aware stack hints (building upon r152837).

The symbol-aware stack hint combines the checker-provided message
with the information about how the symbol was passed to the callee: as
a parameter or a return value.

For malloc, the generated messages look like this :
"Returning from 'foo'; released memory via 1st parameter"
"Returning from 'foo'; allocated memory via 1st parameter"
"Returning from 'foo'; allocated memory returned"
"Returning from 'foo'; reallocation of 1st parameter failed"


(We are yet to handle cases when the symbol is a field in a struct or
an array element.)

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152962 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index e071626..2926fd5 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -243,6 +243,29 @@
                                    const ExplodedNode *PrevN,
                                    BugReporterContext &BRC,
                                    BugReport &BR);
+  private:
+    class StackHintGeneratorForReallocationFailed
+        : public StackHintGeneratorForSymbol {
+    public:
+      StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M)
+        : StackHintGeneratorForSymbol(S, M) {}
+
+      virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex) {
+        SmallString<200> buf;
+        llvm::raw_svector_ostream os(buf);
+
+        os << "; reallocation of ";
+        // Printed parameters start at 1, not 0.
+        printOrdinal(++ArgIndex, os);
+        os << " parameter failed";
+
+        return os.str();
+      }
+
+      virtual std::string getMessageForReturn(const CallExpr *CallExpr) {
+        return "; reallocation of returned value failed";
+      }
+    };
   };
 };
 } // end anonymous namespace
@@ -1249,7 +1272,7 @@
 
   const Stmt *S = 0;
   const char *Msg = 0;
-  const char *StackMsg = 0;
+  StackHintGeneratorForSymbol *StackHint = 0;
 
   // Retrieve the associated statement.
   ProgramPoint ProgLoc = N->getLocation();
@@ -1269,14 +1292,15 @@
   if (Mode == Normal) {
     if (isAllocated(RS, RSPrev, S)) {
       Msg = "Memory is allocated";
-      StackMsg = ", which allocated memory";
+      StackHint = new StackHintGeneratorForSymbol(Sym, "; allocated memory");
     } else if (isReleased(RS, RSPrev, S)) {
       Msg = "Memory is released";
-      StackMsg = ", which released memory";
+      StackHint = new StackHintGeneratorForSymbol(Sym, "; released memory");
     } else if (isReallocFailedCheck(RS, RSPrev, S)) {
       Mode = ReallocationFailed;
       Msg = "Reallocation failed";
-      StackMsg = ", where reallocation failed";
+      StackHint = new StackHintGeneratorForReallocationFailed(Sym,
+                                                   "; reallocation failed");
     }
 
   // We are in a special mode if a reallocation failed later in the path.
@@ -1296,18 +1320,18 @@
     if (!(FunName.equals("realloc") || FunName.equals("reallocf")))
       return 0;
     Msg = "Attempt to reallocate memory";
-    StackMsg = ", which attempted to reallocate memory";
+    StackHint = new StackHintGeneratorForSymbol(Sym, "; reallocated memory");
     Mode = Normal;
   }
 
   if (!Msg)
     return 0;
-  assert(StackMsg);
+  assert(StackHint);
 
   // Generate the extra diagnostic.
   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
                              N->getLocationContext());
-  return new PathDiagnosticEventPiece(Pos, Msg, true, StackMsg);
+  return new PathDiagnosticEventPiece(Pos, Msg, true, StackHint);
 }
 
 
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp
index b59405b..363a6df 100644
--- a/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -380,22 +380,29 @@
 //===----------------------------------------------------------------------===//
 // "Minimal" path diagnostic generation algorithm.
 //===----------------------------------------------------------------------===//
+typedef std::pair<PathDiagnosticCallPiece*, const ExplodedNode*> StackDiagPair;
+typedef SmallVector<StackDiagPair, 6> StackDiagVector;
+
 static void updateStackPiecesWithMessage(PathDiagnosticPiece *P,
-                   llvm::SmallVector<PathDiagnosticCallPiece*, 6> &CallStack) {
+                                         StackDiagVector &CallStack) {
   // If the piece contains a special message, add it to all the call
   // pieces on the active stack.
   if (PathDiagnosticEventPiece *ep =
         dyn_cast<PathDiagnosticEventPiece>(P)) {
-    StringRef stackMsg = ep->getCallStackMessage();
 
-    if (!stackMsg.empty())
-      for (llvm::SmallVector<PathDiagnosticCallPiece*, 6>::iterator
-             I = CallStack.begin(), E = CallStack.end(); I != E; ++I)
+    if (ep->hasCallStackHint())
+      for (StackDiagVector::iterator I = CallStack.begin(),
+                                     E = CallStack.end(); I != E; ++I) {
+        PathDiagnosticCallPiece *CP = I->first;
+        const ExplodedNode *N = I->second;
+        StringRef stackMsg = ep->getCallStackMessage(N);
+
         // The last message on the path to final bug is the most important
         // one. Since we traverse the path backwards, do not add the message
         // if one has been previously added.
-        if  (!(*I)->hasCallStackMessage())
-          (*I)->setCallStackMessage(stackMsg);
+        if  (!CP->hasCallStackMessage())
+          CP->setCallStackMessage(stackMsg);
+      }
   }
 }
 
@@ -410,7 +417,7 @@
   const ExplodedNode *NextNode = N->pred_empty()
                                         ? NULL : *(N->pred_begin());
 
-  llvm::SmallVector<PathDiagnosticCallPiece*, 6> CallStack;
+  StackDiagVector CallStack;
 
   while (NextNode) {
     N = NextNode;
@@ -424,7 +431,7 @@
         PathDiagnosticCallPiece::construct(N, *CE, SMgr);
       PD.getActivePath().push_front(C);
       PD.pushActivePath(&C->path);
-      CallStack.push_back(C);
+      CallStack.push_back(StackDiagPair(C, N));
       continue;      
     }
     
@@ -446,7 +453,7 @@
       }
       C->setCallee(*CE, SMgr);
       if (!CallStack.empty()) {
-        assert(CallStack.back() == C);
+        assert(CallStack.back().first == C);
         CallStack.pop_back();
       }
       continue;
@@ -1047,7 +1054,7 @@
                                             const ExplodedNode *N) {
   EdgeBuilder EB(PD, PDB);
   const SourceManager& SM = PDB.getSourceManager();
-  llvm::SmallVector<PathDiagnosticCallPiece*, 6> CallStack;
+  StackDiagVector CallStack;
 
   const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin());
   while (NextNode) {
@@ -1068,7 +1075,7 @@
           PathDiagnosticCallPiece::construct(N, *CE, SM);
         PD.getActivePath().push_front(C);
         PD.pushActivePath(&C->path);
-        CallStack.push_back(C);
+        CallStack.push_back(StackDiagPair(C, N));
         break;
       }
       
@@ -1104,7 +1111,7 @@
         EB.addContext(CE->getCallExpr());
 
         if (!CallStack.empty()) {
-          assert(CallStack.back() == C);
+          assert(CallStack.back().first == C);
           CallStack.pop_back();
         }
         break;
diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
index cd08f9c..2a2b9c6 100644
--- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -659,3 +659,94 @@
   for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
     ID.AddString(*I);
 }
+
+StackHintGenerator::~StackHintGenerator() {}
+
+std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
+  ProgramPoint P = N->getLocation();
+  const CallExit *CExit = dyn_cast<CallExit>(&P);
+  assert(CExit && "Stack Hints should be constructed at CallExit points.");
+
+  const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt());
+  if (!CE)
+    return "";
+
+  // Get the successor node to make sure the return statement is evaluated and
+  // CE is set to the result value.
+  N = *N->succ_begin();
+  if (!N)
+    return getMessageForSymbolNotFound();
+
+  // Check if one of the parameters are set to the interesting symbol.
+  ProgramStateRef State = N->getState();
+  const LocationContext *LCtx = N->getLocationContext();
+  unsigned ArgIndex = 0;
+  for (CallExpr::const_arg_iterator I = CE->arg_begin(),
+                                    E = CE->arg_end(); I != E; ++I, ++ArgIndex){
+    SVal SV = State->getSVal(*I, LCtx);
+
+    // Check if the variable corresponding to the symbol is passed by value.
+    SymbolRef AS = SV.getAsLocSymbol();
+    if (AS == Sym) {
+      return getMessageForArg(*I, ArgIndex);
+    }
+
+    // Check if the parameter is a pointer to the symbol.
+    if (const loc::MemRegionVal *Reg = dyn_cast<loc::MemRegionVal>(&SV)) {
+      SVal PSV = State->getSVal(Reg->getRegion());
+      SymbolRef AS = PSV.getAsLocSymbol();
+      if (AS == Sym) {
+        return getMessageForArg(*I, ArgIndex);
+      }
+    }
+  }
+
+  // Check if we are returning the interesting symbol.
+  SVal SV = State->getSVal(CE, LCtx);
+  SymbolRef RetSym = SV.getAsLocSymbol();
+  if (RetSym == Sym) {
+    return getMessageForReturn(CE);
+  }
+
+  return getMessageForSymbolNotFound();
+}
+
+/// TODO: This is copied from clang diagnostics. Maybe we could just move it to
+/// some common place. (Same as HandleOrdinalModifier.)
+void StackHintGeneratorForSymbol::printOrdinal(unsigned ValNo,
+                                               llvm::raw_svector_ostream &Out) {
+  assert(ValNo != 0 && "ValNo must be strictly positive!");
+
+  // We could use text forms for the first N ordinals, but the numeric
+  // forms are actually nicer in diagnostics because they stand out.
+  Out << ValNo;
+
+  // It is critically important that we do this perfectly for
+  // user-written sequences with over 100 elements.
+  switch (ValNo % 100) {
+  case 11:
+  case 12:
+  case 13:
+    Out << "th"; return;
+  default:
+    switch (ValNo % 10) {
+    case 1: Out << "st"; return;
+    case 2: Out << "nd"; return;
+    case 3: Out << "rd"; return;
+    default: Out << "th"; return;
+    }
+  }
+}
+
+std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE,
+                                                        unsigned ArgIndex) {
+  SmallString<200> buf;
+  llvm::raw_svector_ostream os(buf);
+
+  os << Msg << " via ";
+  // Printed parameters start at 1, not 0.
+  printOrdinal(++ArgIndex, os);
+  os << " parameter";
+
+  return os.str();
+}