aboutsummaryrefslogtreecommitdiff
path: root/lib/Analysis/BugReporter.cpp
diff options
context:
space:
mode:
authorTed Kremenek <kremenek@apple.com>2009-09-18 05:37:41 +0000
committerTed Kremenek <kremenek@apple.com>2009-09-18 05:37:41 +0000
commit6a19832d08f00ac78c0a69c4fbe38b04a55b75cc (patch)
treeae55168e28cbde260c4cc1958cce3f66c8ffb2ea /lib/Analysis/BugReporter.cpp
parentf1affe6129442b230d3d09b1938e07a7b341a102 (diff)
Introduce caching of diagnostics in BugReporter. This provides extra
pruning of diagnostics that may be emitted multiple times. This is accomplished by adding FoldingSet profiling support to PathDiagnostic, and then having BugReporter record what diagnostics have been issued. This was motived to a serious bug introduced by moving the 'divide-by-zero' checking outside of GRExprEngine into a separate 'Checker' class. When analyzing code using the '-fobjc-gc' option, a given function would be analyzed twice, but the second time various "internal checks" would be disabled to avoid emitting multiple diagnostics (e.g., "null dereference") for the same issue. The problem is that such checks also effect path pruning and don't just emit diagnostics. This resulted in an assertion failure involving a real divide-by-zero in some analyzed code where we would get an assertion failure in APInt because the 'DivZero' check was disabled and didn't prune the logic that resulted in the divide-by-zero in the analyzer. The implemented solution is somewhat of a hack, and may not perform extremely well. This will need to be cleaned up over time. As a regression test, 'misc-ps.m' has been modified so that its tests are run using -fobjc-gc to test this diagnostic pruning behavior. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82198 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Analysis/BugReporter.cpp')
-rw-r--r--lib/Analysis/BugReporter.cpp47
1 files changed, 47 insertions, 0 deletions
diff --git a/lib/Analysis/BugReporter.cpp b/lib/Analysis/BugReporter.cpp
index 38e982888e..064fff47f4 100644
--- a/lib/Analysis/BugReporter.cpp
+++ b/lib/Analysis/BugReporter.cpp
@@ -1732,6 +1732,50 @@ static BugReport *FindReportInEquivalenceClass(BugReportEquivClass& EQ) {
return NULL;
}
+
+//===----------------------------------------------------------------------===//
+// DiagnosticCache. This is a hack to cache analyzer diagnostics. It
+// uses global state, which eventually should go elsewhere.
+//===----------------------------------------------------------------------===//
+namespace {
+class VISIBILITY_HIDDEN DiagCacheItem : public llvm::FoldingSetNode {
+ llvm::FoldingSetNodeID ID;
+public:
+ DiagCacheItem(BugReport *R, PathDiagnostic *PD) {
+ ID.AddString(R->getBugType().getName());
+ ID.AddString(R->getBugType().getCategory());
+ ID.AddString(R->getDescription());
+ ID.AddInteger(R->getLocation().getRawEncoding());
+ PD->Profile(ID);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &id) {
+ id = ID;
+ }
+
+ llvm::FoldingSetNodeID &getID() { return ID; }
+};
+}
+
+static bool IsCachedDiagnostic(BugReport *R, PathDiagnostic *PD) {
+ // FIXME: Eventually this diagnostic cache should reside in something
+ // like AnalysisManager instead of being a static variable. This is
+ // really unsafe in the long term.
+ typedef llvm::FoldingSet<DiagCacheItem> DiagnosticCache;
+ static DiagnosticCache DC;
+
+ void *InsertPos;
+ DiagCacheItem *Item = new DiagCacheItem(R, PD);
+
+ if (DC.FindNodeOrInsertPos(Item->getID(), InsertPos)) {
+ delete Item;
+ return true;
+ }
+
+ DC.InsertNode(Item, InsertPos);
+ return false;
+}
+
void BugReporter::FlushReport(BugReportEquivClass& EQ) {
BugReport *R = FindReportInEquivalenceClass(EQ);
@@ -1752,6 +1796,9 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
GeneratePathDiagnostic(*D.get(), EQ);
+ if (IsCachedDiagnostic(R, D.get()))
+ return;
+
// Get the meta data.
std::pair<const char**, const char**> Meta = R->getExtraDescriptiveText();
for (const char** s = Meta.first; s != Meta.second; ++s)