[analyzer diagnostics] start prototyping stripping PathDiagnostics of unnecessary cruft caused by path inlining.

This introduces a concept of a "prunable" PathDiagnosticEvent.  Currently this is a flag, but
we may evolve the concept to make this more dynamically inferred.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151663 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp
index de3c8ed..8d9b614 100644
--- a/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -111,6 +111,51 @@
 }
 
 //===----------------------------------------------------------------------===//
+// Diagnostic cleanup.
+//===----------------------------------------------------------------------===//
+
+/// Recursively scan through a path and prune out calls and macros pieces
+/// that aren't needed.  Return true if afterwards the path contains
+/// "interesting stuff" which means it should be pruned from the parent path.
+static bool RemoveUneededCalls(PathPieces &pieces) {
+  bool containsSomethingInteresting = false;
+  const unsigned N = pieces.size();
+  
+  for (unsigned i = 0 ; i < N ; ++i) {
+    // Remove the front piece from the path.  If it is still something we
+    // want to keep once we are done, we will push it back on the end.
+    IntrusiveRefCntPtr<PathDiagnosticPiece> piece(pieces.front());
+    pieces.pop_front();
+    
+    if (PathDiagnosticCallPiece *call =
+        dyn_cast<PathDiagnosticCallPiece>(piece)) {      
+      // Recursively clean out the subclass.  Keep this call around if
+      // it contains any informative diagnostics.
+      if (!RemoveUneededCalls(call->path))
+        continue;
+      containsSomethingInteresting = true;
+    }
+    else if (PathDiagnosticMacroPiece *macro =
+             dyn_cast<PathDiagnosticMacroPiece>(piece)) {
+      if (!RemoveUneededCalls(macro->subPieces))
+        continue;
+      containsSomethingInteresting = true;
+    }
+    else if (PathDiagnosticEventPiece *event =
+             dyn_cast<PathDiagnosticEventPiece>(piece)) {
+      // We never throw away an event, but we do throw it away wholesale
+      // as part of a path if we throw the entire path away.
+      if (!event->isPrunable())
+        containsSomethingInteresting = true;
+    }
+    
+    pieces.push_back(piece);
+  }
+  
+  return containsSomethingInteresting;
+}
+
+//===----------------------------------------------------------------------===//
 // PathDiagnosticBuilder and its associated routines and helper objects.
 //===----------------------------------------------------------------------===//
 
@@ -1749,6 +1794,11 @@
       GenerateMinimalPathDiagnostic(PD, PDB, N);
       break;
   }
+  
+  // Finally, prune the diagnostic path of uninteresting stuff.
+  bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces());
+  assert(hasSomethingInteresting);
+  (void) hasSomethingInteresting;
 }
 
 void BugReporter::Register(BugType *BT) {