1 //===--- PathDiagnostic.h - Path-Specific Diagnostic Handling ---*- 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 the PathDiagnostic-related interfaces. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_CLANG_PATH_DIAGNOSTIC_H 15 #define LLVM_CLANG_PATH_DIAGNOSTIC_H 16 17 #include "clang/Basic/SourceLocation.h" 18 #include "clang/Analysis/ProgramPoint.h" 19 #include "llvm/ADT/FoldingSet.h" 20 #include "llvm/ADT/IntrusiveRefCntPtr.h" 21 #include "llvm/ADT/PointerUnion.h" 22 #include "llvm/ADT/Optional.h" 23 #include <deque> 24 #include <iterator> 25 #include <string> 26 #include <vector> 27 28 namespace clang { 29 30 class AnalysisDeclContext; 31 class BinaryOperator; 32 class CompoundStmt; 33 class Decl; 34 class LocationContext; 35 class MemberExpr; 36 class ParentMap; 37 class ProgramPoint; 38 class SourceManager; 39 class Stmt; 40 41 namespace ento { 42 43 class ExplodedNode; 44 class SymExpr; 45 typedef const SymExpr* SymbolRef; 46 47 //===----------------------------------------------------------------------===// 48 // High-level interface for handlers of path-sensitive diagnostics. 49 //===----------------------------------------------------------------------===// 50 51 class PathDiagnostic; 52 53 class PathDiagnosticConsumer { 54 virtual void anchor(); 55 public: 56 PathDiagnosticConsumer() : flushed(false) {} 57 virtual ~PathDiagnosticConsumer(); 58 59 void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade); 60 61 virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, 62 SmallVectorImpl<std::string> *FilesMade) 63 = 0; 64 65 virtual StringRef getName() const = 0; 66 67 void HandlePathDiagnostic(PathDiagnostic *D); 68 69 enum PathGenerationScheme { Minimal, Extensive }; 70 virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } 71 virtual bool supportsLogicalOpControlFlow() const { return false; } 72 virtual bool supportsAllBlockEdges() const { return false; } 73 virtual bool useVerboseDescription() const { return true; } 74 75 /// Return true if the PathDiagnosticConsumer supports individual 76 /// PathDiagnostics that span multiple files. 77 virtual bool supportsCrossFileDiagnostics() const { return false; } 78 79 protected: 80 bool flushed; 81 llvm::FoldingSet<PathDiagnostic> Diags; 82 }; 83 84 //===----------------------------------------------------------------------===// 85 // Path-sensitive diagnostics. 86 //===----------------------------------------------------------------------===// 87 88 class PathDiagnosticRange : public SourceRange { 89 public: 90 bool isPoint; 91 92 PathDiagnosticRange(const SourceRange &R, bool isP = false) 93 : SourceRange(R), isPoint(isP) {} 94 95 PathDiagnosticRange() : isPoint(false) {} 96 }; 97 98 typedef llvm::PointerUnion<const LocationContext*, AnalysisDeclContext*> 99 LocationOrAnalysisDeclContext; 100 101 class PathDiagnosticLocation { 102 private: 103 enum Kind { RangeK, SingleLocK, StmtK, DeclK } K; 104 const Stmt *S; 105 const Decl *D; 106 const SourceManager *SM; 107 FullSourceLoc Loc; 108 PathDiagnosticRange Range; 109 110 PathDiagnosticLocation(SourceLocation L, const SourceManager &sm, 111 Kind kind) 112 : K(kind), S(0), D(0), SM(&sm), 113 Loc(genLocation(L)), Range(genRange()) { 114 assert(Loc.isValid()); 115 assert(Range.isValid()); 116 } 117 118 FullSourceLoc 119 genLocation(SourceLocation L = SourceLocation(), 120 LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext*)0) const; 121 122 PathDiagnosticRange 123 genRange(LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext*)0) const; 124 125 public: 126 /// Create an invalid location. 127 PathDiagnosticLocation() 128 : K(SingleLocK), S(0), D(0), SM(0) {} 129 130 /// Create a location corresponding to the given statement. 131 PathDiagnosticLocation(const Stmt *s, 132 const SourceManager &sm, 133 LocationOrAnalysisDeclContext lac) 134 : K(StmtK), S(s), D(0), SM(&sm), 135 Loc(genLocation(SourceLocation(), lac)), 136 Range(genRange(lac)) { 137 assert(S); 138 assert(Loc.isValid()); 139 assert(Range.isValid()); 140 } 141 142 /// Create a location corresponding to the given declaration. 143 PathDiagnosticLocation(const Decl *d, const SourceManager &sm) 144 : K(DeclK), S(0), D(d), SM(&sm), 145 Loc(genLocation()), Range(genRange()) { 146 assert(D); 147 assert(Loc.isValid()); 148 assert(Range.isValid()); 149 } 150 151 /// Create a location corresponding to the given declaration. 152 static PathDiagnosticLocation create(const Decl *D, 153 const SourceManager &SM) { 154 return PathDiagnosticLocation(D, SM); 155 } 156 157 /// Create a location for the beginning of the declaration. 158 static PathDiagnosticLocation createBegin(const Decl *D, 159 const SourceManager &SM); 160 161 /// Create a location for the beginning of the statement. 162 static PathDiagnosticLocation createBegin(const Stmt *S, 163 const SourceManager &SM, 164 const LocationOrAnalysisDeclContext LAC); 165 166 /// Create the location for the operator of the binary expression. 167 /// Assumes the statement has a valid location. 168 static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO, 169 const SourceManager &SM); 170 171 /// For member expressions, return the location of the '.' or '->'. 172 /// Assumes the statement has a valid location. 173 static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME, 174 const SourceManager &SM); 175 176 /// Create a location for the beginning of the compound statement. 177 /// Assumes the statement has a valid location. 178 static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS, 179 const SourceManager &SM); 180 181 /// Create a location for the end of the compound statement. 182 /// Assumes the statement has a valid location. 183 static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS, 184 const SourceManager &SM); 185 186 /// Create a location for the beginning of the enclosing declaration body. 187 /// Defaults to the beginning of the first statement in the declaration body. 188 static PathDiagnosticLocation createDeclBegin(const LocationContext *LC, 189 const SourceManager &SM); 190 191 /// Constructs a location for the end of the enclosing declaration body. 192 /// Defaults to the end of brace. 193 static PathDiagnosticLocation createDeclEnd(const LocationContext *LC, 194 const SourceManager &SM); 195 196 /// Create a location corresponding to the given valid ExplodedNode. 197 static PathDiagnosticLocation create(const ProgramPoint& P, 198 const SourceManager &SMng); 199 200 /// Create a location corresponding to the next valid ExplodedNode as end 201 /// of path location. 202 static PathDiagnosticLocation createEndOfPath(const ExplodedNode* N, 203 const SourceManager &SM); 204 205 /// Convert the given location into a single kind location. 206 static PathDiagnosticLocation createSingleLocation( 207 const PathDiagnosticLocation &PDL); 208 209 bool operator==(const PathDiagnosticLocation &X) const { 210 return K == X.K && Loc == X.Loc && Range == X.Range; 211 } 212 213 bool operator!=(const PathDiagnosticLocation &X) const { 214 return !(*this == X); 215 } 216 217 bool isValid() const { 218 return SM != 0; 219 } 220 221 FullSourceLoc asLocation() const { 222 return Loc; 223 } 224 225 PathDiagnosticRange asRange() const { 226 return Range; 227 } 228 229 const Stmt *asStmt() const { assert(isValid()); return S; } 230 const Decl *asDecl() const { assert(isValid()); return D; } 231 232 bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; } 233 234 void invalidate() { 235 *this = PathDiagnosticLocation(); 236 } 237 238 void flatten(); 239 240 const SourceManager& getManager() const { assert(isValid()); return *SM; } 241 242 void Profile(llvm::FoldingSetNodeID &ID) const; 243 }; 244 245 class PathDiagnosticLocationPair { 246 private: 247 PathDiagnosticLocation Start, End; 248 public: 249 PathDiagnosticLocationPair(const PathDiagnosticLocation &start, 250 const PathDiagnosticLocation &end) 251 : Start(start), End(end) {} 252 253 const PathDiagnosticLocation &getStart() const { return Start; } 254 const PathDiagnosticLocation &getEnd() const { return End; } 255 256 void flatten() { 257 Start.flatten(); 258 End.flatten(); 259 } 260 261 void Profile(llvm::FoldingSetNodeID &ID) const { 262 Start.Profile(ID); 263 End.Profile(ID); 264 } 265 }; 266 267 //===----------------------------------------------------------------------===// 268 // Path "pieces" for path-sensitive diagnostics. 269 //===----------------------------------------------------------------------===// 270 271 class PathDiagnosticPiece : public RefCountedBaseVPTR { 272 public: 273 enum Kind { ControlFlow, Event, Macro, Call }; 274 enum DisplayHint { Above, Below }; 275 276 private: 277 const std::string str; 278 const Kind kind; 279 const DisplayHint Hint; 280 std::vector<SourceRange> ranges; 281 282 // Do not implement: 283 PathDiagnosticPiece(); 284 PathDiagnosticPiece(const PathDiagnosticPiece &P); 285 PathDiagnosticPiece& operator=(const PathDiagnosticPiece &P); 286 287 protected: 288 PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below); 289 290 PathDiagnosticPiece(Kind k, DisplayHint hint = Below); 291 292 public: 293 virtual ~PathDiagnosticPiece(); 294 295 const std::string& getString() const { return str; } 296 297 /// getDisplayHint - Return a hint indicating where the diagnostic should 298 /// be displayed by the PathDiagnosticConsumer. 299 DisplayHint getDisplayHint() const { return Hint; } 300 301 virtual PathDiagnosticLocation getLocation() const = 0; 302 virtual void flattenLocations() = 0; 303 304 Kind getKind() const { return kind; } 305 306 void addRange(SourceRange R) { 307 if (!R.isValid()) 308 return; 309 ranges.push_back(R); 310 } 311 312 void addRange(SourceLocation B, SourceLocation E) { 313 if (!B.isValid() || !E.isValid()) 314 return; 315 ranges.push_back(SourceRange(B,E)); 316 } 317 318 typedef const SourceRange* range_iterator; 319 320 range_iterator ranges_begin() const { 321 return ranges.empty() ? NULL : &ranges[0]; 322 } 323 324 range_iterator ranges_end() const { 325 return ranges_begin() + ranges.size(); 326 } 327 328 static inline bool classof(const PathDiagnosticPiece *P) { 329 return true; 330 } 331 332 virtual void Profile(llvm::FoldingSetNodeID &ID) const; 333 }; 334 335 336 class PathPieces : 337 public std::deque<IntrusiveRefCntPtr<PathDiagnosticPiece> > { 338 public: 339 ~PathPieces(); 340 }; 341 342 class PathDiagnosticSpotPiece : public PathDiagnosticPiece { 343 private: 344 PathDiagnosticLocation Pos; 345 public: 346 PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos, 347 StringRef s, 348 PathDiagnosticPiece::Kind k, 349 bool addPosRange = true) 350 : PathDiagnosticPiece(s, k), Pos(pos) { 351 assert(Pos.isValid() && Pos.asLocation().isValid() && 352 "PathDiagnosticSpotPiece's must have a valid location."); 353 if (addPosRange && Pos.hasRange()) addRange(Pos.asRange()); 354 } 355 356 PathDiagnosticLocation getLocation() const { return Pos; } 357 virtual void flattenLocations() { Pos.flatten(); } 358 359 virtual void Profile(llvm::FoldingSetNodeID &ID) const; 360 }; 361 362 /// \brief Interface for classes constructing Stack hints. 363 /// 364 /// If a PathDiagnosticEvent occurs in a different frame than the final 365 /// diagnostic the hints can be used to summarise the effect of the call. 366 class StackHintGenerator { 367 public: 368 virtual ~StackHintGenerator() = 0; 369 370 /// \brief Construct the Diagnostic message for the given ExplodedNode. 371 virtual std::string getMessage(const ExplodedNode *N) = 0; 372 }; 373 374 /// \brief Constructs a Stack hint for the given symbol. 375 /// 376 /// The class knows how to construct the stack hint message based on 377 /// traversing the CallExpr associated with the call and checking if the given 378 /// symbol is returned or is one of the arguments. 379 /// The hint can be customized by redefining 'getMessageForX()' methods. 380 class StackHintGeneratorForSymbol : public StackHintGenerator { 381 private: 382 SymbolRef Sym; 383 std::string Msg; 384 385 public: 386 StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {} 387 virtual ~StackHintGeneratorForSymbol() {} 388 389 /// \brief Search the call expression for the symbol Sym and dispatch the 390 /// 'getMessageForX()' methods to construct a specific message. 391 virtual std::string getMessage(const ExplodedNode *N); 392 393 /// Prints the ordinal form of the given integer, 394 /// only valid for ValNo : ValNo > 0. 395 void printOrdinal(unsigned ValNo, llvm::raw_svector_ostream &Out); 396 397 /// Produces the message of the following form: 398 /// 'Msg via Nth parameter' 399 virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex); 400 virtual std::string getMessageForReturn(const CallExpr *CallExpr) { 401 return Msg; 402 } 403 virtual std::string getMessageForSymbolNotFound() { 404 return Msg; 405 } 406 }; 407 408 class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece { 409 llvm::Optional<bool> IsPrunable; 410 411 /// If the event occurs in a different frame than the final diagnostic, 412 /// supply a message that will be used to construct an extra hint on the 413 /// returns from all the calls on the stack from this event to the final 414 /// diagnostic. 415 llvm::OwningPtr<StackHintGenerator> CallStackHint; 416 417 public: 418 PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, 419 StringRef s, bool addPosRange = true, 420 StackHintGenerator *stackHint = 0) 421 : PathDiagnosticSpotPiece(pos, s, Event, addPosRange), 422 CallStackHint(stackHint) {} 423 424 ~PathDiagnosticEventPiece(); 425 426 /// Mark the diagnostic piece as being potentially prunable. This 427 /// flag may have been previously set, at which point it will not 428 /// be reset unless one specifies to do so. 429 void setPrunable(bool isPrunable, bool override = false) { 430 if (IsPrunable.hasValue() && !override) 431 return; 432 IsPrunable = isPrunable; 433 } 434 435 /// Return true if the diagnostic piece is prunable. 436 bool isPrunable() const { 437 return IsPrunable.hasValue() ? IsPrunable.getValue() : false; 438 } 439 440 bool hasCallStackHint() { 441 return (CallStackHint != 0); 442 } 443 444 /// Produce the hint for the given node. The node contains 445 /// information about the call for which the diagnostic can be generated. 446 std::string getCallStackMessage(const ExplodedNode *N) { 447 if (CallStackHint) 448 return CallStackHint->getMessage(N); 449 return ""; 450 } 451 452 static inline bool classof(const PathDiagnosticPiece *P) { 453 return P->getKind() == Event; 454 } 455 }; 456 457 class PathDiagnosticCallPiece : public PathDiagnosticPiece { 458 PathDiagnosticCallPiece(const Decl *callerD, 459 const PathDiagnosticLocation &callReturnPos) 460 : PathDiagnosticPiece(Call), Caller(callerD), Callee(0), 461 NoExit(false), callReturn(callReturnPos) {} 462 463 PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller) 464 : PathDiagnosticPiece(Call), Caller(caller), Callee(0), 465 NoExit(true), path(oldPath) {} 466 467 const Decl *Caller; 468 const Decl *Callee; 469 470 // Flag signifying that this diagnostic has only call enter and no matching 471 // call exit. 472 bool NoExit; 473 474 // The custom string, which should appear after the call Return Diagnostic. 475 // TODO: Should we allow multiple diagnostics? 476 std::string CallStackMessage; 477 478 public: 479 PathDiagnosticLocation callEnter; 480 PathDiagnosticLocation callEnterWithin; 481 PathDiagnosticLocation callReturn; 482 PathPieces path; 483 484 virtual ~PathDiagnosticCallPiece(); 485 486 const Decl *getCaller() const { return Caller; } 487 488 const Decl *getCallee() const { return Callee; } 489 void setCallee(const CallEnter &CE, const SourceManager &SM); 490 491 bool hasCallStackMessage() { return !CallStackMessage.empty(); } 492 void setCallStackMessage(StringRef st) { 493 CallStackMessage = st; 494 } 495 496 virtual PathDiagnosticLocation getLocation() const { 497 return callEnter; 498 } 499 500 IntrusiveRefCntPtr<PathDiagnosticEventPiece> getCallEnterEvent() const; 501 IntrusiveRefCntPtr<PathDiagnosticEventPiece> 502 getCallEnterWithinCallerEvent() const; 503 IntrusiveRefCntPtr<PathDiagnosticEventPiece> getCallExitEvent() const; 504 505 virtual void flattenLocations() { 506 callEnter.flatten(); 507 callReturn.flatten(); 508 for (PathPieces::iterator I = path.begin(), 509 E = path.end(); I != E; ++I) (*I)->flattenLocations(); 510 } 511 512 static PathDiagnosticCallPiece *construct(const ExplodedNode *N, 513 const CallExit &CE, 514 const SourceManager &SM); 515 516 static PathDiagnosticCallPiece *construct(PathPieces &pieces, 517 const Decl *caller); 518 519 virtual void Profile(llvm::FoldingSetNodeID &ID) const; 520 521 static inline bool classof(const PathDiagnosticPiece *P) { 522 return P->getKind() == Call; 523 } 524 }; 525 526 class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece { 527 std::vector<PathDiagnosticLocationPair> LPairs; 528 public: 529 PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, 530 const PathDiagnosticLocation &endPos, 531 StringRef s) 532 : PathDiagnosticPiece(s, ControlFlow) { 533 LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); 534 } 535 536 PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, 537 const PathDiagnosticLocation &endPos) 538 : PathDiagnosticPiece(ControlFlow) { 539 LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); 540 } 541 542 ~PathDiagnosticControlFlowPiece(); 543 544 PathDiagnosticLocation getStartLocation() const { 545 assert(!LPairs.empty() && 546 "PathDiagnosticControlFlowPiece needs at least one location."); 547 return LPairs[0].getStart(); 548 } 549 550 PathDiagnosticLocation getEndLocation() const { 551 assert(!LPairs.empty() && 552 "PathDiagnosticControlFlowPiece needs at least one location."); 553 return LPairs[0].getEnd(); 554 } 555 556 void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); } 557 558 virtual PathDiagnosticLocation getLocation() const { 559 return getStartLocation(); 560 } 561 562 typedef std::vector<PathDiagnosticLocationPair>::iterator iterator; 563 iterator begin() { return LPairs.begin(); } 564 iterator end() { return LPairs.end(); } 565 566 virtual void flattenLocations() { 567 for (iterator I=begin(), E=end(); I!=E; ++I) I->flatten(); 568 } 569 570 typedef std::vector<PathDiagnosticLocationPair>::const_iterator 571 const_iterator; 572 const_iterator begin() const { return LPairs.begin(); } 573 const_iterator end() const { return LPairs.end(); } 574 575 static inline bool classof(const PathDiagnosticPiece *P) { 576 return P->getKind() == ControlFlow; 577 } 578 579 virtual void Profile(llvm::FoldingSetNodeID &ID) const; 580 }; 581 582 class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece { 583 public: 584 PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos) 585 : PathDiagnosticSpotPiece(pos, "", Macro) {} 586 587 ~PathDiagnosticMacroPiece(); 588 589 PathPieces subPieces; 590 591 bool containsEvent() const; 592 593 virtual void flattenLocations() { 594 PathDiagnosticSpotPiece::flattenLocations(); 595 for (PathPieces::iterator I = subPieces.begin(), 596 E = subPieces.end(); I != E; ++I) (*I)->flattenLocations(); 597 } 598 599 static inline bool classof(const PathDiagnosticPiece *P) { 600 return P->getKind() == Macro; 601 } 602 603 virtual void Profile(llvm::FoldingSetNodeID &ID) const; 604 }; 605 606 /// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive 607 /// diagnostic. It represents an ordered-collection of PathDiagnosticPieces, 608 /// each which represent the pieces of the path. 609 class PathDiagnostic : public llvm::FoldingSetNode { 610 const Decl *DeclWithIssue; 611 std::string BugType; 612 std::string Desc; 613 std::string Category; 614 std::deque<std::string> OtherDesc; 615 PathPieces pathImpl; 616 llvm::SmallVector<PathPieces *, 3> pathStack; 617 618 PathDiagnostic(); // Do not implement. 619 public: 620 const PathPieces &path; 621 622 /// Return the path currently used by builders for constructing the 623 /// PathDiagnostic. 624 PathPieces &getActivePath() { 625 if (pathStack.empty()) 626 return pathImpl; 627 return *pathStack.back(); 628 } 629 630 /// Return a mutable version of 'path'. 631 PathPieces &getMutablePieces() { 632 return pathImpl; 633 } 634 635 /// Return the unrolled size of the path. 636 unsigned full_size(); 637 638 void pushActivePath(PathPieces *p) { pathStack.push_back(p); } 639 void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); } 640 641 // PathDiagnostic(); 642 PathDiagnostic(const Decl *DeclWithIssue, 643 StringRef bugtype, 644 StringRef desc, 645 StringRef category); 646 647 ~PathDiagnostic(); 648 649 StringRef getDescription() const { return Desc; } 650 StringRef getBugType() const { return BugType; } 651 StringRef getCategory() const { return Category; } 652 653 /// Return the semantic context where an issue occurred. If the 654 /// issue occurs along a path, this represents the "central" area 655 /// where the bug manifests. 656 const Decl *getDeclWithIssue() const { return DeclWithIssue; } 657 658 typedef std::deque<std::string>::const_iterator meta_iterator; 659 meta_iterator meta_begin() const { return OtherDesc.begin(); } 660 meta_iterator meta_end() const { return OtherDesc.end(); } 661 void addMeta(StringRef s) { OtherDesc.push_back(s); } 662 663 PathDiagnosticLocation getLocation() const; 664 665 void flattenLocations() { 666 for (PathPieces::iterator I = pathImpl.begin(), E = pathImpl.end(); 667 I != E; ++I) (*I)->flattenLocations(); 668 } 669 670 void Profile(llvm::FoldingSetNodeID &ID) const; 671 672 void FullProfile(llvm::FoldingSetNodeID &ID) const; 673 }; 674 675 } // end GR namespace 676 677 } //end clang namespace 678 679 #endif 680