1 //===--- BugReporter.h - Generate PathDiagnostics --------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines BugReporter, a utility class for generating
11 // PathDiagnostics for analyses based on ProgramState.
13 //===----------------------------------------------------------------------===//
15 #ifndef LLVM_CLANG_GR_BUGREPORTER
16 #define LLVM_CLANG_GR_BUGREPORTER
18 #include "clang/Basic/SourceLocation.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h"
20 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
22 #include "llvm/ADT/FoldingSet.h"
23 #include "llvm/ADT/ImmutableList.h"
24 #include "llvm/ADT/ImmutableSet.h"
25 #include "llvm/ADT/SmallSet.h"
31 class DiagnosticsEngine;
42 class BugReporterContext;
46 //===----------------------------------------------------------------------===//
47 // Interface for individual bug reports.
48 //===----------------------------------------------------------------------===//
50 /// This class provides an interface through which checkers can create
51 /// individual bug reports.
56 virtual ~NodeResolver() {}
57 virtual const ExplodedNode*
58 getOriginalNode(const ExplodedNode *N) = 0;
61 typedef const SourceRange *ranges_iterator;
62 typedef llvm::ImmutableList<BugReporterVisitor*>::iterator visitor_iterator;
63 typedef SmallVector<StringRef, 2> ExtraTextList;
66 friend class BugReporter;
67 friend class BugReportEquivClass;
70 std::string ShortDescription;
71 std::string Description;
72 PathDiagnosticLocation Location;
73 const ExplodedNode *ErrorNode;
74 SmallVector<SourceRange, 4> Ranges;
75 ExtraTextList ExtraText;
77 // Not the most efficient data structure, but we use an ImmutableList for the
78 // Callbacks because it is safe to make additions to list during iteration.
79 llvm::ImmutableList<BugReporterVisitor*>::Factory F;
80 llvm::ImmutableList<BugReporterVisitor*> Callbacks;
81 llvm::FoldingSet<BugReporterVisitor> CallbacksSet;
84 BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode)
85 : BT(bt), Description(desc), ErrorNode(errornode),
86 Callbacks(F.getEmptyList()) {}
88 BugReport(BugType& bt, StringRef shortDesc, StringRef desc,
89 const ExplodedNode *errornode)
90 : BT(bt), ShortDescription(shortDesc), Description(desc),
91 ErrorNode(errornode), Callbacks(F.getEmptyList()) {}
93 BugReport(BugType& bt, StringRef desc, PathDiagnosticLocation l)
94 : BT(bt), Description(desc), Location(l), ErrorNode(0),
95 Callbacks(F.getEmptyList()) {}
99 const BugType& getBugType() const { return BT; }
100 BugType& getBugType() { return BT; }
102 const ExplodedNode *getErrorNode() const { return ErrorNode; }
104 const StringRef getDescription() const { return Description; }
106 const StringRef getShortDescription() const {
107 return ShortDescription.empty() ? Description : ShortDescription;
110 /// \brief This allows for addition of meta data to the diagnostic.
112 /// Currently, only the HTMLDiagnosticClient knows how to display it.
113 void addExtraText(StringRef S) {
114 ExtraText.push_back(S);
117 virtual const ExtraTextList &getExtraText() {
121 /// \brief Return the "definitive" location of the reported bug.
123 /// While a bug can span an entire path, usually there is a specific
124 /// location that can be used to identify where the key issue occurred.
125 /// This location is used by clients rendering diagnostics.
126 virtual PathDiagnosticLocation getLocation(const SourceManager &SM) const;
128 const Stmt *getStmt() const;
130 /// \brief Add a range to a bug report.
132 /// Ranges are used to highlight regions of interest in the source code.
133 /// They should be at the same source code line as the BugReport location.
134 /// By default, the source range of the statement corresponding to the error
135 /// node will be used; add a single invalid range to specify absence of
137 void addRange(SourceRange R) {
138 assert((R.isValid() || Ranges.empty()) && "Invalid range can only be used "
139 "to specify that the report does not have a range.");
143 /// \brief Get the SourceRanges associated with the report.
144 virtual std::pair<ranges_iterator, ranges_iterator> getRanges();
146 /// \brief Add custom or predefined bug report visitors to this report.
148 /// The visitors should be used when the default trace is not sufficient.
149 /// For example, they allow constructing a more elaborate trace.
150 /// \sa registerConditionVisitor(), registerTrackNullOrUndefValue(),
151 /// registerFindLastStore(), registerNilReceiverVisitor(), and
152 /// registerVarDeclsLastStore().
153 void addVisitor(BugReporterVisitor *visitor);
155 /// Iterators through the custom diagnostic visitors.
156 visitor_iterator visitor_begin() { return Callbacks.begin(); }
157 visitor_iterator visitor_end() { return Callbacks.end(); }
159 /// Profile to identify equivalent bug reports for error report coalescing.
160 /// Reports are uniqued to ensure that we do not emit multiple diagnostics
162 virtual void Profile(llvm::FoldingSetNodeID& hash) const;
165 //===----------------------------------------------------------------------===//
166 // BugTypes (collections of related reports).
167 //===----------------------------------------------------------------------===//
169 class BugReportEquivClass : public llvm::FoldingSetNode {
170 /// List of *owned* BugReport objects.
171 std::list<BugReport*> Reports;
173 friend class BugReporter;
174 void AddReport(BugReport* R) { Reports.push_back(R); }
176 BugReportEquivClass(BugReport* R) { Reports.push_back(R); }
177 ~BugReportEquivClass();
179 void Profile(llvm::FoldingSetNodeID& ID) const {
180 assert(!Reports.empty());
181 (*Reports.begin())->Profile(ID);
185 std::list<BugReport*>::iterator impl;
187 iterator(std::list<BugReport*>::iterator i) : impl(i) {}
188 iterator &operator++() { ++impl; return *this; }
189 bool operator==(const iterator &I) const { return I.impl == impl; }
190 bool operator!=(const iterator &I) const { return I.impl != impl; }
191 BugReport* operator*() const { return *impl; }
192 BugReport* operator->() const { return *impl; }
195 class const_iterator {
196 std::list<BugReport*>::const_iterator impl;
198 const_iterator(std::list<BugReport*>::const_iterator i) : impl(i) {}
199 const_iterator &operator++() { ++impl; return *this; }
200 bool operator==(const const_iterator &I) const { return I.impl == impl; }
201 bool operator!=(const const_iterator &I) const { return I.impl != impl; }
202 const BugReport* operator*() const { return *impl; }
203 const BugReport* operator->() const { return *impl; }
206 iterator begin() { return iterator(Reports.begin()); }
207 iterator end() { return iterator(Reports.end()); }
209 const_iterator begin() const { return const_iterator(Reports.begin()); }
210 const_iterator end() const { return const_iterator(Reports.end()); }
213 //===----------------------------------------------------------------------===//
214 // BugReporter and friends.
215 //===----------------------------------------------------------------------===//
217 class BugReporterData {
219 virtual ~BugReporterData();
220 virtual DiagnosticsEngine& getDiagnostic() = 0;
221 virtual PathDiagnosticConsumer* getPathDiagnosticConsumer() = 0;
222 virtual ASTContext &getASTContext() = 0;
223 virtual SourceManager& getSourceManager() = 0;
226 /// BugReporter is a utility class for generating PathDiagnostics for analysis.
227 /// It collects the BugReports and BugTypes and knows how to generate
228 /// and flush the corresponding diagnostics.
231 enum Kind { BaseBRKind, GRBugReporterKind };
234 typedef llvm::ImmutableSet<BugType*> BugTypesTy;
235 BugTypesTy::Factory F;
241 /// Generate and flush the diagnostics for the given bug report.
242 void FlushReport(BugReportEquivClass& EQ);
244 /// The set of bug reports tracked by the BugReporter.
245 llvm::FoldingSet<BugReportEquivClass> EQClasses;
246 /// A vector of BugReports for tracking the allocated pointers and cleanup.
247 std::vector<BugReportEquivClass *> EQClassesVector;
250 BugReporter(BugReporterData& d, Kind k) : BugTypes(F.getEmptySet()), kind(k),
254 BugReporter(BugReporterData& d) : BugTypes(F.getEmptySet()), kind(BaseBRKind),
256 virtual ~BugReporter();
258 /// \brief Generate and flush diagnostics for all bug reports.
261 Kind getKind() const { return kind; }
263 DiagnosticsEngine& getDiagnostic() {
264 return D.getDiagnostic();
267 PathDiagnosticConsumer* getPathDiagnosticConsumer() {
268 return D.getPathDiagnosticConsumer();
271 /// \brief Iterator over the set of BugTypes tracked by the BugReporter.
272 typedef BugTypesTy::iterator iterator;
273 iterator begin() { return BugTypes.begin(); }
274 iterator end() { return BugTypes.end(); }
276 /// \brief Iterator over the set of BugReports tracked by the BugReporter.
277 typedef llvm::FoldingSet<BugReportEquivClass>::iterator EQClasses_iterator;
278 EQClasses_iterator EQClasses_begin() { return EQClasses.begin(); }
279 EQClasses_iterator EQClasses_end() { return EQClasses.end(); }
281 ASTContext &getContext() { return D.getASTContext(); }
283 SourceManager& getSourceManager() { return D.getSourceManager(); }
285 virtual void GeneratePathDiagnostic(PathDiagnostic& pathDiagnostic,
286 SmallVectorImpl<BugReport *> &bugReports) {}
288 void Register(BugType *BT);
290 /// \brief Add the given report to the set of reports tracked by BugReporter.
292 /// The reports are usually generated by the checkers. Further, they are
293 /// folded based on the profile value, which is done to coalesce similar
295 void EmitReport(BugReport *R);
297 void EmitBasicReport(StringRef BugName, StringRef BugStr,
298 PathDiagnosticLocation Loc,
299 SourceRange* RangeBeg, unsigned NumRanges);
301 void EmitBasicReport(StringRef BugName, StringRef BugCategory,
302 StringRef BugStr, PathDiagnosticLocation Loc,
303 SourceRange* RangeBeg, unsigned NumRanges);
306 void EmitBasicReport(StringRef BugName, StringRef BugStr,
307 PathDiagnosticLocation Loc) {
308 EmitBasicReport(BugName, BugStr, Loc, 0, 0);
311 void EmitBasicReport(StringRef BugName, StringRef BugCategory,
312 StringRef BugStr, PathDiagnosticLocation Loc) {
313 EmitBasicReport(BugName, BugCategory, BugStr, Loc, 0, 0);
316 void EmitBasicReport(StringRef BugName, StringRef BugStr,
317 PathDiagnosticLocation Loc, SourceRange R) {
318 EmitBasicReport(BugName, BugStr, Loc, &R, 1);
321 void EmitBasicReport(StringRef BugName, StringRef Category,
322 StringRef BugStr, PathDiagnosticLocation Loc,
324 EmitBasicReport(BugName, Category, BugStr, Loc, &R, 1);
327 static bool classof(const BugReporter* R) { return true; }
330 llvm::StringMap<BugType *> StrBugTypes;
332 /// \brief Returns a BugType that is associated with the given name and
334 BugType *getBugTypeForName(StringRef name, StringRef category);
337 // FIXME: Get rid of GRBugReporter. It's the wrong abstraction.
338 class GRBugReporter : public BugReporter {
340 llvm::SmallSet<SymbolRef, 10> NotableSymbols;
342 GRBugReporter(BugReporterData& d, ExprEngine& eng)
343 : BugReporter(d, GRBugReporterKind), Eng(eng) {}
345 virtual ~GRBugReporter();
347 /// getEngine - Return the analysis engine used to analyze a given
348 /// function or method.
349 ExprEngine &getEngine() { return Eng; }
351 /// getGraph - Get the exploded graph created by the analysis engine
352 /// for the analyzed method or function.
353 ExplodedGraph &getGraph();
355 /// getStateManager - Return the state manager used by the analysis
357 ProgramStateManager &getStateManager();
359 virtual void GeneratePathDiagnostic(PathDiagnostic &pathDiagnostic,
360 SmallVectorImpl<BugReport*> &bugReports);
362 void addNotableSymbol(SymbolRef Sym) {
363 NotableSymbols.insert(Sym);
366 bool isNotable(SymbolRef Sym) const {
367 return (bool) NotableSymbols.count(Sym);
370 /// classof - Used by isa<>, cast<>, and dyn_cast<>.
371 static bool classof(const BugReporter* R) {
372 return R->getKind() == GRBugReporterKind;
376 class BugReporterContext {
379 BugReporterContext(GRBugReporter& br) : BR(br) {}
381 virtual ~BugReporterContext() {}
383 GRBugReporter& getBugReporter() { return BR; }
385 ExplodedGraph &getGraph() { return BR.getGraph(); }
387 void addNotableSymbol(SymbolRef Sym) {
388 // FIXME: For now forward to GRBugReporter.
389 BR.addNotableSymbol(Sym);
392 bool isNotable(SymbolRef Sym) const {
393 // FIXME: For now forward to GRBugReporter.
394 return BR.isNotable(Sym);
397 ProgramStateManager& getStateManager() {
398 return BR.getStateManager();
401 SValBuilder& getSValBuilder() {
402 return getStateManager().getSValBuilder();
405 ASTContext &getASTContext() {
406 return BR.getContext();
409 SourceManager& getSourceManager() {
410 return BR.getSourceManager();
413 virtual BugReport::NodeResolver& getNodeResolver() = 0;
416 } // end GR namespace
418 } // end clang namespace