BugReporter.h revision e172e8b9e7fc67d7d03589af7e92fe777afcf33a
1//===--- BugReporter.h - Generate PathDiagnostics --------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file defines BugReporter, a utility class for generating 11// PathDiagnostics for analyses based on ProgramState. 12// 13//===----------------------------------------------------------------------===// 14 15#ifndef LLVM_CLANG_GR_BUGREPORTER 16#define LLVM_CLANG_GR_BUGREPORTER 17 18#include "clang/Basic/SourceLocation.h" 19#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 20#include "llvm/ADT/FoldingSet.h" 21#include "llvm/ADT/ImmutableList.h" 22#include "llvm/ADT/ImmutableSet.h" 23#include "llvm/ADT/SmallSet.h" 24#include <list> 25 26namespace clang { 27 28class ASTContext; 29class Diagnostic; 30class Stmt; 31class ParentMap; 32 33namespace ento { 34 35class PathDiagnostic; 36class PathDiagnosticPiece; 37class PathDiagnosticClient; 38class ExplodedNode; 39class ExplodedGraph; 40class BugReporter; 41class BugReporterContext; 42class ExprEngine; 43class ProgramState; 44class BugType; 45 46//===----------------------------------------------------------------------===// 47// Interface for individual bug reports. 48//===----------------------------------------------------------------------===// 49 50class BugReporterVisitor : public llvm::FoldingSetNode { 51public: 52 virtual ~BugReporterVisitor(); 53 virtual PathDiagnosticPiece *VisitNode(const ExplodedNode *N, 54 const ExplodedNode *PrevN, 55 BugReporterContext &BRC) = 0; 56 57 virtual bool isOwnedByReporterContext() { return true; } 58 virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0; 59}; 60 61class BugReport : public BugReporterVisitor { 62public: 63 class NodeResolver { 64 public: 65 virtual ~NodeResolver() {} 66 virtual const ExplodedNode* 67 getOriginalNode(const ExplodedNode *N) = 0; 68 }; 69 70 typedef void (*VisitorCreator)(BugReporterContext &BRcC, const void *data, 71 const ExplodedNode *N); 72 typedef const SourceRange *ranges_iterator; 73 74protected: 75 friend class BugReporter; 76 friend class BugReportEquivClass; 77 typedef SmallVector<std::pair<VisitorCreator, const void*>, 2> Creators; 78 79 BugType& BT; 80 std::string ShortDescription; 81 std::string Description; 82 const ExplodedNode *ErrorNode; 83 SmallVector<SourceRange, 4> Ranges; 84 Creators creators; 85 86 /// Profile to identify equivalent bug reports for error report coalescing. 87 virtual void Profile(llvm::FoldingSetNodeID& hash) const; 88 89 const Stmt *getStmt() const; 90 91public: 92 BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode) 93 : BT(bt), Description(desc), ErrorNode(errornode) {} 94 95 BugReport(BugType& bt, StringRef shortDesc, StringRef desc, 96 const ExplodedNode *errornode) 97 : BT(bt), ShortDescription(shortDesc), Description(desc), 98 ErrorNode(errornode) {} 99 100 virtual ~BugReport(); 101 102 bool isOwnedByReporterContext() { return false; } 103 104 const BugType& getBugType() const { return BT; } 105 BugType& getBugType() { return BT; } 106 107 const ExplodedNode *getErrorNode() const { return ErrorNode; } 108 109 const StringRef getDescription() const { return Description; } 110 111 const StringRef getShortDescription() const { 112 return ShortDescription.empty() ? Description : ShortDescription; 113 } 114 115 /// \brief This allows for addition of meta data to the diagnostic. 116 /// 117 /// Currently, only the HTMLDiagnosticClient knows how to display it. 118 virtual std::pair<const char**,const char**> getExtraDescriptiveText() { 119 return std::make_pair((const char**)0,(const char**)0); 120 } 121 122 /// Provide custom definition for the last diagnostic piece on the path. 123 virtual PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, 124 const ExplodedNode *N); 125 126 /// \brief Return the "definitive" location of the reported bug. 127 /// 128 /// While a bug can span an entire path, usually there is a specific 129 /// location that can be used to identify where the key issue occurred. 130 /// This location is used by clients rendering diagnostics. 131 virtual SourceLocation getLocation() const; 132 133 /// \brief Add a range to a bug report. 134 /// 135 /// Ranges are used to highlight regions of interest in the source code. 136 /// They should be at the same source code line as the BugReport location. 137 void addRange(SourceRange R) { 138 assert(R.isValid()); 139 Ranges.push_back(R); 140 } 141 142 /// \brief Get the SourceRanges associated with the report. 143 virtual std::pair<ranges_iterator, ranges_iterator> getRanges(); 144 145 /// \brief Add custom or predefined bug report visitors to this report. 146 /// 147 /// The visitors should be used when the default trace is not sufficient. 148 /// For example, they allow constructing a more elaborate trace. 149 /// \sa registerConditionVisitor(), registerTrackNullOrUndefValue(), 150 /// registerFindLastStore(), registerNilReceiverVisitor(), and 151 /// registerVarDeclsLastStore(). 152 void addVisitorCreator(VisitorCreator creator, const void *data) { 153 creators.push_back(std::make_pair(creator, data)); 154 } 155 156 virtual PathDiagnosticPiece *VisitNode(const ExplodedNode *N, 157 const ExplodedNode *PrevN, 158 BugReporterContext &BR); 159 160 virtual void registerInitialVisitors(BugReporterContext &BRC, 161 const ExplodedNode *N) {} 162}; 163 164//===----------------------------------------------------------------------===// 165// BugTypes (collections of related reports). 166//===----------------------------------------------------------------------===// 167 168class BugReportEquivClass : public llvm::FoldingSetNode { 169 /// List of *owned* BugReport objects. 170 std::list<BugReport*> Reports; 171 172 friend class BugReporter; 173 void AddReport(BugReport* R) { Reports.push_back(R); } 174public: 175 BugReportEquivClass(BugReport* R) { Reports.push_back(R); } 176 ~BugReportEquivClass(); 177 178 void Profile(llvm::FoldingSetNodeID& ID) const { 179 assert(!Reports.empty()); 180 (*Reports.begin())->Profile(ID); 181 } 182 183 class iterator { 184 std::list<BugReport*>::iterator impl; 185 public: 186 iterator(std::list<BugReport*>::iterator i) : impl(i) {} 187 iterator &operator++() { ++impl; return *this; } 188 bool operator==(const iterator &I) const { return I.impl == impl; } 189 bool operator!=(const iterator &I) const { return I.impl != impl; } 190 BugReport* operator*() const { return *impl; } 191 BugReport* operator->() const { return *impl; } 192 }; 193 194 class const_iterator { 195 std::list<BugReport*>::const_iterator impl; 196 public: 197 const_iterator(std::list<BugReport*>::const_iterator i) : impl(i) {} 198 const_iterator &operator++() { ++impl; return *this; } 199 bool operator==(const const_iterator &I) const { return I.impl == impl; } 200 bool operator!=(const const_iterator &I) const { return I.impl != impl; } 201 const BugReport* operator*() const { return *impl; } 202 const BugReport* operator->() const { return *impl; } 203 }; 204 205 iterator begin() { return iterator(Reports.begin()); } 206 iterator end() { return iterator(Reports.end()); } 207 208 const_iterator begin() const { return const_iterator(Reports.begin()); } 209 const_iterator end() const { return const_iterator(Reports.end()); } 210}; 211 212//===----------------------------------------------------------------------===// 213// BugReporter and friends. 214//===----------------------------------------------------------------------===// 215 216class BugReporterData { 217public: 218 virtual ~BugReporterData(); 219 virtual Diagnostic& getDiagnostic() = 0; 220 virtual PathDiagnosticClient* getPathDiagnosticClient() = 0; 221 virtual ASTContext &getASTContext() = 0; 222 virtual SourceManager& getSourceManager() = 0; 223}; 224 225/// BugReporter is a utility class for generating PathDiagnostics for analysis. 226/// It collects the BugReports and BugTypes and knows how to generate 227/// and flush the corresponding diagnostics. 228class BugReporter { 229public: 230 enum Kind { BaseBRKind, GRBugReporterKind }; 231 232private: 233 typedef llvm::ImmutableSet<BugType*> BugTypesTy; 234 BugTypesTy::Factory F; 235 BugTypesTy BugTypes; 236 237 const Kind kind; 238 BugReporterData& D; 239 240 /// Generate and flush the diagnostics for the given bug report. 241 void FlushReport(BugReportEquivClass& EQ); 242 243 /// The set of bug reports tracked by the BugReporter. 244 llvm::FoldingSet<BugReportEquivClass> EQClasses; 245 246protected: 247 BugReporter(BugReporterData& d, Kind k) : BugTypes(F.getEmptySet()), kind(k), 248 D(d) {} 249 250public: 251 BugReporter(BugReporterData& d) : BugTypes(F.getEmptySet()), kind(BaseBRKind), 252 D(d) {} 253 virtual ~BugReporter(); 254 255 /// \brief Generate and flush diagnostics for all bug reports. 256 void FlushReports(); 257 258 Kind getKind() const { return kind; } 259 260 Diagnostic& getDiagnostic() { 261 return D.getDiagnostic(); 262 } 263 264 PathDiagnosticClient* getPathDiagnosticClient() { 265 return D.getPathDiagnosticClient(); 266 } 267 268 /// \brief Iterator over the set of BugTypes tracked by the BugReporter. 269 typedef BugTypesTy::iterator iterator; 270 iterator begin() { return BugTypes.begin(); } 271 iterator end() { return BugTypes.end(); } 272 273 /// \brief Iterator over the set of BugReports tracked by the BugReporter. 274 typedef llvm::FoldingSet<BugReportEquivClass>::iterator EQClasses_iterator; 275 EQClasses_iterator EQClasses_begin() { return EQClasses.begin(); } 276 EQClasses_iterator EQClasses_end() { return EQClasses.end(); } 277 278 ASTContext &getContext() { return D.getASTContext(); } 279 280 SourceManager& getSourceManager() { return D.getSourceManager(); } 281 282 virtual void GeneratePathDiagnostic(PathDiagnostic& pathDiagnostic, 283 SmallVectorImpl<BugReport *> &bugReports) {} 284 285 void Register(BugType *BT); 286 287 /// \brief Add the given report to the set of reports tracked by BugReporter. 288 /// 289 /// The reports are usually generated by the checkers. Further, they are 290 /// folded based on the profile value, which is done to coalesce similar 291 /// reports. 292 void EmitReport(BugReport *R); 293 294 void EmitBasicReport(StringRef BugName, StringRef BugStr, 295 SourceLocation Loc, 296 SourceRange* RangeBeg, unsigned NumRanges); 297 298 void EmitBasicReport(StringRef BugName, StringRef BugCategory, 299 StringRef BugStr, SourceLocation Loc, 300 SourceRange* RangeBeg, unsigned NumRanges); 301 302 303 void EmitBasicReport(StringRef BugName, StringRef BugStr, 304 SourceLocation Loc) { 305 EmitBasicReport(BugName, BugStr, Loc, 0, 0); 306 } 307 308 void EmitBasicReport(StringRef BugName, StringRef BugCategory, 309 StringRef BugStr, SourceLocation Loc) { 310 EmitBasicReport(BugName, BugCategory, BugStr, Loc, 0, 0); 311 } 312 313 void EmitBasicReport(StringRef BugName, StringRef BugStr, 314 SourceLocation Loc, SourceRange R) { 315 EmitBasicReport(BugName, BugStr, Loc, &R, 1); 316 } 317 318 void EmitBasicReport(StringRef BugName, StringRef Category, 319 StringRef BugStr, SourceLocation Loc, 320 SourceRange R) { 321 EmitBasicReport(BugName, Category, BugStr, Loc, &R, 1); 322 } 323 324 static bool classof(const BugReporter* R) { return true; } 325 326private: 327 llvm::StringMap<BugType *> StrBugTypes; 328 329 /// \brief Returns a BugType that is associated with the given name and 330 /// category. 331 BugType *getBugTypeForName(StringRef name, StringRef category); 332}; 333 334// FIXME: Get rid of GRBugReporter. It's the wrong abstraction. 335class GRBugReporter : public BugReporter { 336 ExprEngine& Eng; 337 llvm::SmallSet<SymbolRef, 10> NotableSymbols; 338public: 339 GRBugReporter(BugReporterData& d, ExprEngine& eng) 340 : BugReporter(d, GRBugReporterKind), Eng(eng) {} 341 342 virtual ~GRBugReporter(); 343 344 /// getEngine - Return the analysis engine used to analyze a given 345 /// function or method. 346 ExprEngine &getEngine() { return Eng; } 347 348 /// getGraph - Get the exploded graph created by the analysis engine 349 /// for the analyzed method or function. 350 ExplodedGraph &getGraph(); 351 352 /// getStateManager - Return the state manager used by the analysis 353 /// engine. 354 ProgramStateManager &getStateManager(); 355 356 virtual void GeneratePathDiagnostic(PathDiagnostic &pathDiagnostic, 357 SmallVectorImpl<BugReport*> &bugReports); 358 359 void addNotableSymbol(SymbolRef Sym) { 360 NotableSymbols.insert(Sym); 361 } 362 363 bool isNotable(SymbolRef Sym) const { 364 return (bool) NotableSymbols.count(Sym); 365 } 366 367 /// classof - Used by isa<>, cast<>, and dyn_cast<>. 368 static bool classof(const BugReporter* R) { 369 return R->getKind() == GRBugReporterKind; 370 } 371}; 372 373class BugReporterContext { 374 GRBugReporter &BR; 375 // Not the most efficient data structure, but we use an ImmutableList for the 376 // Callbacks because it is safe to make additions to list during iteration. 377 llvm::ImmutableList<BugReporterVisitor*>::Factory F; 378 llvm::ImmutableList<BugReporterVisitor*> Callbacks; 379 llvm::FoldingSet<BugReporterVisitor> CallbacksSet; 380public: 381 BugReporterContext(GRBugReporter& br) : BR(br), Callbacks(F.getEmptyList()) {} 382 virtual ~BugReporterContext(); 383 384 void addVisitor(BugReporterVisitor* visitor); 385 386 typedef llvm::ImmutableList<BugReporterVisitor*>::iterator visitor_iterator; 387 visitor_iterator visitor_begin() { return Callbacks.begin(); } 388 visitor_iterator visitor_end() { return Callbacks.end(); } 389 390 GRBugReporter& getBugReporter() { return BR; } 391 392 ExplodedGraph &getGraph() { return BR.getGraph(); } 393 394 void addNotableSymbol(SymbolRef Sym) { 395 // FIXME: For now forward to GRBugReporter. 396 BR.addNotableSymbol(Sym); 397 } 398 399 bool isNotable(SymbolRef Sym) const { 400 // FIXME: For now forward to GRBugReporter. 401 return BR.isNotable(Sym); 402 } 403 404 ProgramStateManager& getStateManager() { 405 return BR.getStateManager(); 406 } 407 408 SValBuilder& getSValBuilder() { 409 return getStateManager().getSValBuilder(); 410 } 411 412 ASTContext &getASTContext() { 413 return BR.getContext(); 414 } 415 416 SourceManager& getSourceManager() { 417 return BR.getSourceManager(); 418 } 419 420 virtual BugReport::NodeResolver& getNodeResolver() = 0; 421}; 422 423class DiagBugReport : public BugReport { 424 std::list<std::string> Strs; 425 FullSourceLoc L; 426public: 427 DiagBugReport(BugType& D, StringRef desc, FullSourceLoc l) : 428 BugReport(D, desc, 0), L(l) {} 429 430 virtual ~DiagBugReport() {} 431 432 // FIXME: Move out-of-line (virtual function). 433 SourceLocation getLocation() const { return L; } 434 435 void addString(StringRef s) { Strs.push_back(s); } 436 437 typedef std::list<std::string>::const_iterator str_iterator; 438 str_iterator str_begin() const { return Strs.begin(); } 439 str_iterator str_end() const { return Strs.end(); } 440}; 441 442//===----------------------------------------------------------------------===// 443//===----------------------------------------------------------------------===// 444 445namespace bugreporter { 446 447const Stmt *GetDerefExpr(const ExplodedNode *N); 448const Stmt *GetDenomExpr(const ExplodedNode *N); 449const Stmt *GetCalleeExpr(const ExplodedNode *N); 450const Stmt *GetRetValExpr(const ExplodedNode *N); 451 452void registerConditionVisitor(BugReporterContext &BRC); 453 454void registerTrackNullOrUndefValue(BugReporterContext &BRC, const void *stmt, 455 const ExplodedNode *N); 456 457void registerFindLastStore(BugReporterContext &BRC, const void *memregion, 458 const ExplodedNode *N); 459 460void registerNilReceiverVisitor(BugReporterContext &BRC); 461 462void registerVarDeclsLastStore(BugReporterContext &BRC, const void *stmt, 463 const ExplodedNode *N); 464 465} // end namespace clang::bugreporter 466 467//===----------------------------------------------------------------------===// 468 469} // end GR namespace 470 471} // end clang namespace 472 473#endif 474