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_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H 15 #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H 16 17 #include "clang/AST/Stmt.h" 18 #include "clang/Analysis/AnalysisDeclContext.h" 19 #include "clang/Basic/LLVM.h" 20 #include "clang/Basic/SourceLocation.h" 21 #include "llvm/ADT/ArrayRef.h" 22 #include "llvm/ADT/FoldingSet.h" 23 #include "llvm/ADT/Optional.h" 24 #include "llvm/ADT/PointerUnion.h" 25 #include "llvm/ADT/SmallVector.h" 26 #include "llvm/ADT/StringRef.h" 27 #include "llvm/Support/Allocator.h" 28 #include <cassert> 29 #include <deque> 30 #include <iterator> 31 #include <list> 32 #include <map> 33 #include <memory> 34 #include <set> 35 #include <string> 36 #include <utility> 37 #include <vector> 38 39 namespace clang { 40 41 class AnalysisDeclContext; 42 class BinaryOperator; 43 class CallEnter; 44 class CallExitEnd; 45 class CallExpr; 46 class ConditionalOperator; 47 class Decl; 48 class Expr; 49 class LocationContext; 50 class MemberExpr; 51 class ProgramPoint; 52 class SourceManager; 53 54 namespace ento { 55 56 class ExplodedNode; 57 class SymExpr; 58 59 using SymbolRef = const SymExpr *; 60 61 //===----------------------------------------------------------------------===// 62 // High-level interface for handlers of path-sensitive diagnostics. 63 //===----------------------------------------------------------------------===// 64 65 class PathDiagnostic; 66 67 class PathDiagnosticConsumer { 68 public: 69 class PDFileEntry : public llvm::FoldingSetNode { 70 public: PDFileEntry(llvm::FoldingSetNodeID & NodeID)71 PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {} 72 73 using ConsumerFiles = std::vector<std::pair<StringRef, StringRef>>; 74 75 /// A vector of <consumer,file> pairs. 76 ConsumerFiles files; 77 78 /// A precomputed hash tag used for uniquing PDFileEntry objects. 79 const llvm::FoldingSetNodeID NodeID; 80 81 /// Used for profiling in the FoldingSet. Profile(llvm::FoldingSetNodeID & ID)82 void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; } 83 }; 84 85 class FilesMade { 86 llvm::BumpPtrAllocator Alloc; 87 llvm::FoldingSet<PDFileEntry> Set; 88 89 public: 90 ~FilesMade(); 91 empty()92 bool empty() const { return Set.empty(); } 93 94 void addDiagnostic(const PathDiagnostic &PD, 95 StringRef ConsumerName, 96 StringRef fileName); 97 98 PDFileEntry::ConsumerFiles *getFiles(const PathDiagnostic &PD); 99 }; 100 101 private: 102 virtual void anchor(); 103 104 public: 105 PathDiagnosticConsumer() = default; 106 virtual ~PathDiagnosticConsumer(); 107 108 void FlushDiagnostics(FilesMade *FilesMade); 109 110 virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, 111 FilesMade *filesMade) = 0; 112 113 virtual StringRef getName() const = 0; 114 115 void HandlePathDiagnostic(std::unique_ptr<PathDiagnostic> D); 116 117 enum PathGenerationScheme { 118 /// Only runs visitors, no output generated. 119 None, 120 121 /// Used for HTML, SARIF, and text output. 122 Minimal, 123 124 /// Used for plist output, used for "arrows" generation. 125 Extensive, 126 }; 127 getGenerationScheme()128 virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } supportsLogicalOpControlFlow()129 virtual bool supportsLogicalOpControlFlow() const { return false; } 130 131 /// Return true if the PathDiagnosticConsumer supports individual 132 /// PathDiagnostics that span multiple files. supportsCrossFileDiagnostics()133 virtual bool supportsCrossFileDiagnostics() const { return false; } 134 135 protected: 136 bool flushed = false; 137 llvm::FoldingSet<PathDiagnostic> Diags; 138 }; 139 140 //===----------------------------------------------------------------------===// 141 // Path-sensitive diagnostics. 142 //===----------------------------------------------------------------------===// 143 144 class PathDiagnosticRange : public SourceRange { 145 public: 146 bool isPoint = false; 147 148 PathDiagnosticRange(SourceRange R, bool isP = false) SourceRange(R)149 : SourceRange(R), isPoint(isP) {} 150 PathDiagnosticRange() = default; 151 }; 152 153 using LocationOrAnalysisDeclContext = 154 llvm::PointerUnion<const LocationContext *, AnalysisDeclContext *>; 155 156 class PathDiagnosticLocation { 157 private: 158 enum Kind { RangeK, SingleLocK, StmtK, DeclK } K = SingleLocK; 159 160 const Stmt *S = nullptr; 161 const Decl *D = nullptr; 162 const SourceManager *SM = nullptr; 163 FullSourceLoc Loc; 164 PathDiagnosticRange Range; 165 PathDiagnosticLocation(SourceLocation L,const SourceManager & sm,Kind kind)166 PathDiagnosticLocation(SourceLocation L, const SourceManager &sm, Kind kind) 167 : K(kind), SM(&sm), Loc(genLocation(L)), Range(genRange()) {} 168 169 FullSourceLoc genLocation( 170 SourceLocation L = SourceLocation(), 171 LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; 172 173 PathDiagnosticRange genRange( 174 LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; 175 176 public: 177 /// Create an invalid location. 178 PathDiagnosticLocation() = default; 179 180 /// Create a location corresponding to the given statement. PathDiagnosticLocation(const Stmt * s,const SourceManager & sm,LocationOrAnalysisDeclContext lac)181 PathDiagnosticLocation(const Stmt *s, const SourceManager &sm, 182 LocationOrAnalysisDeclContext lac) 183 : K(s->getBeginLoc().isValid() ? StmtK : SingleLocK), 184 S(K == StmtK ? s : nullptr), SM(&sm), 185 Loc(genLocation(SourceLocation(), lac)), Range(genRange(lac)) { 186 assert(K == SingleLocK || S); 187 assert(K == SingleLocK || Loc.isValid()); 188 assert(K == SingleLocK || Range.isValid()); 189 } 190 191 /// Create a location corresponding to the given declaration. PathDiagnosticLocation(const Decl * d,const SourceManager & sm)192 PathDiagnosticLocation(const Decl *d, const SourceManager &sm) 193 : K(DeclK), D(d), SM(&sm), Loc(genLocation()), Range(genRange()) { 194 assert(D); 195 assert(Loc.isValid()); 196 assert(Range.isValid()); 197 } 198 199 /// Create a location at an explicit offset in the source. 200 /// 201 /// This should only be used if there are no more appropriate constructors. PathDiagnosticLocation(SourceLocation loc,const SourceManager & sm)202 PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm) 203 : SM(&sm), Loc(loc, sm), Range(genRange()) { 204 assert(Loc.isValid()); 205 assert(Range.isValid()); 206 } 207 208 /// Create a location corresponding to the given declaration. create(const Decl * D,const SourceManager & SM)209 static PathDiagnosticLocation create(const Decl *D, 210 const SourceManager &SM) { 211 return PathDiagnosticLocation(D, SM); 212 } 213 214 /// Create a location for the beginning of the declaration. 215 static PathDiagnosticLocation createBegin(const Decl *D, 216 const SourceManager &SM); 217 218 /// Create a location for the beginning of the declaration. 219 /// The third argument is ignored, useful for generic treatment 220 /// of statements and declarations. 221 static PathDiagnosticLocation createBegin(const Decl * D,const SourceManager & SM,const LocationOrAnalysisDeclContext LAC)222 createBegin(const Decl *D, const SourceManager &SM, 223 const LocationOrAnalysisDeclContext LAC) { 224 return createBegin(D, SM); 225 } 226 227 /// Create a location for the beginning of the statement. 228 static PathDiagnosticLocation createBegin(const Stmt *S, 229 const SourceManager &SM, 230 const LocationOrAnalysisDeclContext LAC); 231 232 /// Create a location for the end of the statement. 233 /// 234 /// If the statement is a CompoundStatement, the location will point to the 235 /// closing brace instead of following it. 236 static PathDiagnosticLocation createEnd(const Stmt *S, 237 const SourceManager &SM, 238 const LocationOrAnalysisDeclContext LAC); 239 240 /// Create the location for the operator of the binary expression. 241 /// Assumes the statement has a valid location. 242 static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO, 243 const SourceManager &SM); 244 static PathDiagnosticLocation createConditionalColonLoc( 245 const ConditionalOperator *CO, 246 const SourceManager &SM); 247 248 /// For member expressions, return the location of the '.' or '->'. 249 /// Assumes the statement has a valid location. 250 static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME, 251 const SourceManager &SM); 252 253 /// Create a location for the beginning of the compound statement. 254 /// Assumes the statement has a valid location. 255 static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS, 256 const SourceManager &SM); 257 258 /// Create a location for the end of the compound statement. 259 /// Assumes the statement has a valid location. 260 static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS, 261 const SourceManager &SM); 262 263 /// Create a location for the beginning of the enclosing declaration body. 264 /// Defaults to the beginning of the first statement in the declaration body. 265 static PathDiagnosticLocation createDeclBegin(const LocationContext *LC, 266 const SourceManager &SM); 267 268 /// Constructs a location for the end of the enclosing declaration body. 269 /// Defaults to the end of brace. 270 static PathDiagnosticLocation createDeclEnd(const LocationContext *LC, 271 const SourceManager &SM); 272 273 /// Create a location corresponding to the given valid ExplodedNode. 274 static PathDiagnosticLocation create(const ProgramPoint &P, 275 const SourceManager &SMng); 276 277 /// Create a location corresponding to the next valid ExplodedNode as end 278 /// of path location. 279 static PathDiagnosticLocation createEndOfPath(const ExplodedNode* N, 280 const SourceManager &SM); 281 282 /// Convert the given location into a single kind location. 283 static PathDiagnosticLocation createSingleLocation( 284 const PathDiagnosticLocation &PDL); 285 286 bool operator==(const PathDiagnosticLocation &X) const { 287 return K == X.K && Loc == X.Loc && Range == X.Range; 288 } 289 290 bool operator!=(const PathDiagnosticLocation &X) const { 291 return !(*this == X); 292 } 293 isValid()294 bool isValid() const { 295 return SM != nullptr; 296 } 297 asLocation()298 FullSourceLoc asLocation() const { 299 return Loc; 300 } 301 asRange()302 PathDiagnosticRange asRange() const { 303 return Range; 304 } 305 asStmt()306 const Stmt *asStmt() const { assert(isValid()); return S; } getStmtOrNull()307 const Stmt *getStmtOrNull() const { 308 if (!isValid()) 309 return nullptr; 310 return asStmt(); 311 } 312 asDecl()313 const Decl *asDecl() const { assert(isValid()); return D; } 314 hasRange()315 bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; } 316 invalidate()317 void invalidate() { 318 *this = PathDiagnosticLocation(); 319 } 320 321 void flatten(); 322 getManager()323 const SourceManager& getManager() const { assert(isValid()); return *SM; } 324 325 void Profile(llvm::FoldingSetNodeID &ID) const; 326 327 void dump() const; 328 329 /// Given an exploded node, retrieve the statement that should be used 330 /// for the diagnostic location. 331 static const Stmt *getStmt(const ExplodedNode *N); 332 333 /// Retrieve the statement corresponding to the successor node. 334 static const Stmt *getNextStmt(const ExplodedNode *N); 335 }; 336 337 class PathDiagnosticLocationPair { 338 private: 339 PathDiagnosticLocation Start, End; 340 341 public: PathDiagnosticLocationPair(const PathDiagnosticLocation & start,const PathDiagnosticLocation & end)342 PathDiagnosticLocationPair(const PathDiagnosticLocation &start, 343 const PathDiagnosticLocation &end) 344 : Start(start), End(end) {} 345 getStart()346 const PathDiagnosticLocation &getStart() const { return Start; } getEnd()347 const PathDiagnosticLocation &getEnd() const { return End; } 348 setStart(const PathDiagnosticLocation & L)349 void setStart(const PathDiagnosticLocation &L) { Start = L; } setEnd(const PathDiagnosticLocation & L)350 void setEnd(const PathDiagnosticLocation &L) { End = L; } 351 flatten()352 void flatten() { 353 Start.flatten(); 354 End.flatten(); 355 } 356 Profile(llvm::FoldingSetNodeID & ID)357 void Profile(llvm::FoldingSetNodeID &ID) const { 358 Start.Profile(ID); 359 End.Profile(ID); 360 } 361 }; 362 363 //===----------------------------------------------------------------------===// 364 // Path "pieces" for path-sensitive diagnostics. 365 //===----------------------------------------------------------------------===// 366 367 class PathDiagnosticPiece: public llvm::FoldingSetNode { 368 public: 369 enum Kind { ControlFlow, Event, Macro, Call, Note }; 370 enum DisplayHint { Above, Below }; 371 372 private: 373 const std::string str; 374 const Kind kind; 375 const DisplayHint Hint; 376 377 /// In the containing bug report, this piece is the last piece from 378 /// the main source file. 379 bool LastInMainSourceFile = false; 380 381 /// A constant string that can be used to tag the PathDiagnosticPiece, 382 /// typically with the identification of the creator. The actual pointer 383 /// value is meant to be an identifier; the string itself is useful for 384 /// debugging. 385 StringRef Tag; 386 387 std::vector<SourceRange> ranges; 388 389 protected: 390 PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below); 391 PathDiagnosticPiece(Kind k, DisplayHint hint = Below); 392 393 public: 394 PathDiagnosticPiece() = delete; 395 PathDiagnosticPiece(const PathDiagnosticPiece &) = delete; 396 PathDiagnosticPiece &operator=(const PathDiagnosticPiece &) = delete; 397 virtual ~PathDiagnosticPiece(); 398 getString()399 StringRef getString() const { return str; } 400 401 /// Tag this PathDiagnosticPiece with the given C-string. setTag(const char * tag)402 void setTag(const char *tag) { Tag = tag; } 403 404 /// Return the opaque tag (if any) on the PathDiagnosticPiece. getTag()405 const void *getTag() const { return Tag.data(); } 406 407 /// Return the string representation of the tag. This is useful 408 /// for debugging. getTagStr()409 StringRef getTagStr() const { return Tag; } 410 411 /// getDisplayHint - Return a hint indicating where the diagnostic should 412 /// be displayed by the PathDiagnosticConsumer. getDisplayHint()413 DisplayHint getDisplayHint() const { return Hint; } 414 415 virtual PathDiagnosticLocation getLocation() const = 0; 416 virtual void flattenLocations() = 0; 417 getKind()418 Kind getKind() const { return kind; } 419 addRange(SourceRange R)420 void addRange(SourceRange R) { 421 if (!R.isValid()) 422 return; 423 ranges.push_back(R); 424 } 425 addRange(SourceLocation B,SourceLocation E)426 void addRange(SourceLocation B, SourceLocation E) { 427 if (!B.isValid() || !E.isValid()) 428 return; 429 ranges.push_back(SourceRange(B,E)); 430 } 431 432 /// Return the SourceRanges associated with this PathDiagnosticPiece. getRanges()433 ArrayRef<SourceRange> getRanges() const { return ranges; } 434 435 virtual void Profile(llvm::FoldingSetNodeID &ID) const; 436 setAsLastInMainSourceFile()437 void setAsLastInMainSourceFile() { 438 LastInMainSourceFile = true; 439 } 440 isLastInMainSourceFile()441 bool isLastInMainSourceFile() const { 442 return LastInMainSourceFile; 443 } 444 445 virtual void dump() const = 0; 446 }; 447 448 class PathPieces : public std::list<std::shared_ptr<PathDiagnosticPiece>> { 449 void flattenTo(PathPieces &Primary, PathPieces &Current, 450 bool ShouldFlattenMacros) const; 451 452 public: flatten(bool ShouldFlattenMacros)453 PathPieces flatten(bool ShouldFlattenMacros) const { 454 PathPieces Result; 455 flattenTo(Result, Result, ShouldFlattenMacros); 456 return Result; 457 } 458 459 void dump() const; 460 }; 461 462 class PathDiagnosticSpotPiece : public PathDiagnosticPiece { 463 private: 464 PathDiagnosticLocation Pos; 465 466 public: 467 PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos, 468 StringRef s, 469 PathDiagnosticPiece::Kind k, 470 bool addPosRange = true) PathDiagnosticPiece(s,k)471 : PathDiagnosticPiece(s, k), Pos(pos) { 472 assert(Pos.isValid() && Pos.asLocation().isValid() && 473 "PathDiagnosticSpotPiece's must have a valid location."); 474 if (addPosRange && Pos.hasRange()) addRange(Pos.asRange()); 475 } 476 getLocation()477 PathDiagnosticLocation getLocation() const override { return Pos; } flattenLocations()478 void flattenLocations() override { Pos.flatten(); } 479 480 void Profile(llvm::FoldingSetNodeID &ID) const override; 481 classof(const PathDiagnosticPiece * P)482 static bool classof(const PathDiagnosticPiece *P) { 483 return P->getKind() == Event || P->getKind() == Macro || 484 P->getKind() == Note; 485 } 486 }; 487 488 /// Interface for classes constructing Stack hints. 489 /// 490 /// If a PathDiagnosticEvent occurs in a different frame than the final 491 /// diagnostic the hints can be used to summarize the effect of the call. 492 class StackHintGenerator { 493 public: 494 virtual ~StackHintGenerator() = 0; 495 496 /// Construct the Diagnostic message for the given ExplodedNode. 497 virtual std::string getMessage(const ExplodedNode *N) = 0; 498 }; 499 500 /// Constructs a Stack hint for the given symbol. 501 /// 502 /// The class knows how to construct the stack hint message based on 503 /// traversing the CallExpr associated with the call and checking if the given 504 /// symbol is returned or is one of the arguments. 505 /// The hint can be customized by redefining 'getMessageForX()' methods. 506 class StackHintGeneratorForSymbol : public StackHintGenerator { 507 private: 508 SymbolRef Sym; 509 std::string Msg; 510 511 public: StackHintGeneratorForSymbol(SymbolRef S,StringRef M)512 StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {} 513 ~StackHintGeneratorForSymbol() override = default; 514 515 /// Search the call expression for the symbol Sym and dispatch the 516 /// 'getMessageForX()' methods to construct a specific message. 517 std::string getMessage(const ExplodedNode *N) override; 518 519 /// Produces the message of the following form: 520 /// 'Msg via Nth parameter' 521 virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex); 522 getMessageForReturn(const CallExpr * CallExpr)523 virtual std::string getMessageForReturn(const CallExpr *CallExpr) { 524 return Msg; 525 } 526 getMessageForSymbolNotFound()527 virtual std::string getMessageForSymbolNotFound() { 528 return Msg; 529 } 530 }; 531 532 class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece { 533 Optional<bool> IsPrunable; 534 535 /// If the event occurs in a different frame than the final diagnostic, 536 /// supply a message that will be used to construct an extra hint on the 537 /// returns from all the calls on the stack from this event to the final 538 /// diagnostic. 539 std::unique_ptr<StackHintGenerator> CallStackHint; 540 541 public: 542 PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, 543 StringRef s, bool addPosRange = true, 544 StackHintGenerator *stackHint = nullptr) PathDiagnosticSpotPiece(pos,s,Event,addPosRange)545 : PathDiagnosticSpotPiece(pos, s, Event, addPosRange), 546 CallStackHint(stackHint) {} 547 ~PathDiagnosticEventPiece() override; 548 549 /// Mark the diagnostic piece as being potentially prunable. This 550 /// flag may have been previously set, at which point it will not 551 /// be reset unless one specifies to do so. 552 void setPrunable(bool isPrunable, bool override = false) { 553 if (IsPrunable.hasValue() && !override) 554 return; 555 IsPrunable = isPrunable; 556 } 557 558 /// Return true if the diagnostic piece is prunable. isPrunable()559 bool isPrunable() const { 560 return IsPrunable.hasValue() ? IsPrunable.getValue() : false; 561 } 562 hasCallStackHint()563 bool hasCallStackHint() { return (bool)CallStackHint; } 564 565 /// Produce the hint for the given node. The node contains 566 /// information about the call for which the diagnostic can be generated. getCallStackMessage(const ExplodedNode * N)567 std::string getCallStackMessage(const ExplodedNode *N) { 568 if (CallStackHint) 569 return CallStackHint->getMessage(N); 570 return {}; 571 } 572 573 void dump() const override; 574 classof(const PathDiagnosticPiece * P)575 static bool classof(const PathDiagnosticPiece *P) { 576 return P->getKind() == Event; 577 } 578 }; 579 580 class PathDiagnosticCallPiece : public PathDiagnosticPiece { 581 const Decl *Caller; 582 const Decl *Callee = nullptr; 583 584 // Flag signifying that this diagnostic has only call enter and no matching 585 // call exit. 586 bool NoExit; 587 588 // Flag signifying that the callee function is an Objective-C autosynthesized 589 // property getter or setter. 590 bool IsCalleeAnAutosynthesizedPropertyAccessor = false; 591 592 // The custom string, which should appear after the call Return Diagnostic. 593 // TODO: Should we allow multiple diagnostics? 594 std::string CallStackMessage; 595 PathDiagnosticCallPiece(const Decl * callerD,const PathDiagnosticLocation & callReturnPos)596 PathDiagnosticCallPiece(const Decl *callerD, 597 const PathDiagnosticLocation &callReturnPos) 598 : PathDiagnosticPiece(Call), Caller(callerD), NoExit(false), 599 callReturn(callReturnPos) {} PathDiagnosticCallPiece(PathPieces & oldPath,const Decl * caller)600 PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller) 601 : PathDiagnosticPiece(Call), Caller(caller), NoExit(true), 602 path(oldPath) {} 603 604 public: 605 PathDiagnosticLocation callEnter; 606 PathDiagnosticLocation callEnterWithin; 607 PathDiagnosticLocation callReturn; 608 PathPieces path; 609 610 ~PathDiagnosticCallPiece() override; 611 getCaller()612 const Decl *getCaller() const { return Caller; } 613 getCallee()614 const Decl *getCallee() const { return Callee; } 615 void setCallee(const CallEnter &CE, const SourceManager &SM); 616 hasCallStackMessage()617 bool hasCallStackMessage() { return !CallStackMessage.empty(); } setCallStackMessage(StringRef st)618 void setCallStackMessage(StringRef st) { CallStackMessage = st; } 619 getLocation()620 PathDiagnosticLocation getLocation() const override { return callEnter; } 621 622 std::shared_ptr<PathDiagnosticEventPiece> getCallEnterEvent() const; 623 std::shared_ptr<PathDiagnosticEventPiece> 624 getCallEnterWithinCallerEvent() const; 625 std::shared_ptr<PathDiagnosticEventPiece> getCallExitEvent() const; 626 flattenLocations()627 void flattenLocations() override { 628 callEnter.flatten(); 629 callReturn.flatten(); 630 for (const auto &I : path) 631 I->flattenLocations(); 632 } 633 634 static std::shared_ptr<PathDiagnosticCallPiece> 635 construct(const CallExitEnd &CE, 636 const SourceManager &SM); 637 638 static PathDiagnosticCallPiece *construct(PathPieces &pieces, 639 const Decl *caller); 640 641 void dump() const override; 642 643 void Profile(llvm::FoldingSetNodeID &ID) const override; 644 classof(const PathDiagnosticPiece * P)645 static bool classof(const PathDiagnosticPiece *P) { 646 return P->getKind() == Call; 647 } 648 }; 649 650 class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece { 651 std::vector<PathDiagnosticLocationPair> LPairs; 652 653 public: PathDiagnosticControlFlowPiece(const PathDiagnosticLocation & startPos,const PathDiagnosticLocation & endPos,StringRef s)654 PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, 655 const PathDiagnosticLocation &endPos, 656 StringRef s) 657 : PathDiagnosticPiece(s, ControlFlow) { 658 LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); 659 } 660 PathDiagnosticControlFlowPiece(const PathDiagnosticLocation & startPos,const PathDiagnosticLocation & endPos)661 PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, 662 const PathDiagnosticLocation &endPos) 663 : PathDiagnosticPiece(ControlFlow) { 664 LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); 665 } 666 667 ~PathDiagnosticControlFlowPiece() override; 668 getStartLocation()669 PathDiagnosticLocation getStartLocation() const { 670 assert(!LPairs.empty() && 671 "PathDiagnosticControlFlowPiece needs at least one location."); 672 return LPairs[0].getStart(); 673 } 674 getEndLocation()675 PathDiagnosticLocation getEndLocation() const { 676 assert(!LPairs.empty() && 677 "PathDiagnosticControlFlowPiece needs at least one location."); 678 return LPairs[0].getEnd(); 679 } 680 setStartLocation(const PathDiagnosticLocation & L)681 void setStartLocation(const PathDiagnosticLocation &L) { 682 LPairs[0].setStart(L); 683 } 684 setEndLocation(const PathDiagnosticLocation & L)685 void setEndLocation(const PathDiagnosticLocation &L) { 686 LPairs[0].setEnd(L); 687 } 688 push_back(const PathDiagnosticLocationPair & X)689 void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); } 690 getLocation()691 PathDiagnosticLocation getLocation() const override { 692 return getStartLocation(); 693 } 694 695 using iterator = std::vector<PathDiagnosticLocationPair>::iterator; 696 begin()697 iterator begin() { return LPairs.begin(); } end()698 iterator end() { return LPairs.end(); } 699 flattenLocations()700 void flattenLocations() override { 701 for (auto &I : *this) 702 I.flatten(); 703 } 704 705 using const_iterator = 706 std::vector<PathDiagnosticLocationPair>::const_iterator; 707 begin()708 const_iterator begin() const { return LPairs.begin(); } end()709 const_iterator end() const { return LPairs.end(); } 710 classof(const PathDiagnosticPiece * P)711 static bool classof(const PathDiagnosticPiece *P) { 712 return P->getKind() == ControlFlow; 713 } 714 715 void dump() const override; 716 717 void Profile(llvm::FoldingSetNodeID &ID) const override; 718 }; 719 720 class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece { 721 public: PathDiagnosticMacroPiece(const PathDiagnosticLocation & pos)722 PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos) 723 : PathDiagnosticSpotPiece(pos, "", Macro) {} 724 ~PathDiagnosticMacroPiece() override; 725 726 PathPieces subPieces; 727 728 bool containsEvent() const; 729 flattenLocations()730 void flattenLocations() override { 731 PathDiagnosticSpotPiece::flattenLocations(); 732 for (const auto &I : subPieces) 733 I->flattenLocations(); 734 } 735 classof(const PathDiagnosticPiece * P)736 static bool classof(const PathDiagnosticPiece *P) { 737 return P->getKind() == Macro; 738 } 739 740 void dump() const override; 741 742 void Profile(llvm::FoldingSetNodeID &ID) const override; 743 }; 744 745 class PathDiagnosticNotePiece: public PathDiagnosticSpotPiece { 746 public: 747 PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S, 748 bool AddPosRange = true) PathDiagnosticSpotPiece(Pos,S,Note,AddPosRange)749 : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {} 750 ~PathDiagnosticNotePiece() override; 751 classof(const PathDiagnosticPiece * P)752 static bool classof(const PathDiagnosticPiece *P) { 753 return P->getKind() == Note; 754 } 755 756 void dump() const override; 757 758 void Profile(llvm::FoldingSetNodeID &ID) const override; 759 }; 760 761 /// File IDs mapped to sets of line numbers. 762 using FilesToLineNumsMap = std::map<FileID, std::set<unsigned>>; 763 764 /// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive 765 /// diagnostic. It represents an ordered-collection of PathDiagnosticPieces, 766 /// each which represent the pieces of the path. 767 class PathDiagnostic : public llvm::FoldingSetNode { 768 std::string CheckName; 769 const Decl *DeclWithIssue; 770 std::string BugType; 771 std::string VerboseDesc; 772 std::string ShortDesc; 773 std::string Category; 774 std::deque<std::string> OtherDesc; 775 776 /// Loc The location of the path diagnostic report. 777 PathDiagnosticLocation Loc; 778 779 PathPieces pathImpl; 780 SmallVector<PathPieces *, 3> pathStack; 781 782 /// Important bug uniqueing location. 783 /// The location info is useful to differentiate between bugs. 784 PathDiagnosticLocation UniqueingLoc; 785 const Decl *UniqueingDecl; 786 787 /// Lines executed in the path. 788 std::unique_ptr<FilesToLineNumsMap> ExecutedLines; 789 790 public: 791 PathDiagnostic() = delete; 792 PathDiagnostic(StringRef CheckName, const Decl *DeclWithIssue, 793 StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, 794 StringRef category, PathDiagnosticLocation LocationToUnique, 795 const Decl *DeclToUnique, 796 std::unique_ptr<FilesToLineNumsMap> ExecutedLines); 797 ~PathDiagnostic(); 798 799 const PathPieces &path; 800 801 /// Return the path currently used by builders for constructing the 802 /// PathDiagnostic. getActivePath()803 PathPieces &getActivePath() { 804 if (pathStack.empty()) 805 return pathImpl; 806 return *pathStack.back(); 807 } 808 809 /// Return a mutable version of 'path'. getMutablePieces()810 PathPieces &getMutablePieces() { 811 return pathImpl; 812 } 813 814 /// Return the unrolled size of the path. 815 unsigned full_size(); 816 pushActivePath(PathPieces * p)817 void pushActivePath(PathPieces *p) { pathStack.push_back(p); } popActivePath()818 void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); } 819 isWithinCall()820 bool isWithinCall() const { return !pathStack.empty(); } 821 setEndOfPath(std::shared_ptr<PathDiagnosticPiece> EndPiece)822 void setEndOfPath(std::shared_ptr<PathDiagnosticPiece> EndPiece) { 823 assert(!Loc.isValid() && "End location already set!"); 824 Loc = EndPiece->getLocation(); 825 assert(Loc.isValid() && "Invalid location for end-of-path piece"); 826 getActivePath().push_back(std::move(EndPiece)); 827 } 828 appendToDesc(StringRef S)829 void appendToDesc(StringRef S) { 830 if (!ShortDesc.empty()) 831 ShortDesc += S; 832 VerboseDesc += S; 833 } 834 835 /// If the last piece of the report point to the header file, resets 836 /// the location of the report to be the last location in the main source 837 /// file. 838 void resetDiagnosticLocationToMainFile(); 839 getVerboseDescription()840 StringRef getVerboseDescription() const { return VerboseDesc; } 841 getShortDescription()842 StringRef getShortDescription() const { 843 return ShortDesc.empty() ? VerboseDesc : ShortDesc; 844 } 845 getCheckName()846 StringRef getCheckName() const { return CheckName; } getBugType()847 StringRef getBugType() const { return BugType; } getCategory()848 StringRef getCategory() const { return Category; } 849 850 /// Return the semantic context where an issue occurred. If the 851 /// issue occurs along a path, this represents the "central" area 852 /// where the bug manifests. getDeclWithIssue()853 const Decl *getDeclWithIssue() const { return DeclWithIssue; } 854 855 using meta_iterator = std::deque<std::string>::const_iterator; 856 meta_begin()857 meta_iterator meta_begin() const { return OtherDesc.begin(); } meta_end()858 meta_iterator meta_end() const { return OtherDesc.end(); } addMeta(StringRef s)859 void addMeta(StringRef s) { OtherDesc.push_back(s); } 860 getExecutedLines()861 const FilesToLineNumsMap &getExecutedLines() const { 862 return *ExecutedLines; 863 } 864 getExecutedLines()865 FilesToLineNumsMap &getExecutedLines() { 866 return *ExecutedLines; 867 } 868 getLocation()869 PathDiagnosticLocation getLocation() const { 870 return Loc; 871 } 872 873 /// Get the location on which the report should be uniqued. getUniqueingLoc()874 PathDiagnosticLocation getUniqueingLoc() const { 875 return UniqueingLoc; 876 } 877 878 /// Get the declaration containing the uniqueing location. getUniqueingDecl()879 const Decl *getUniqueingDecl() const { 880 return UniqueingDecl; 881 } 882 flattenLocations()883 void flattenLocations() { 884 Loc.flatten(); 885 for (const auto &I : pathImpl) 886 I->flattenLocations(); 887 } 888 889 /// Profiles the diagnostic, independent of the path it references. 890 /// 891 /// This can be used to merge diagnostics that refer to the same issue 892 /// along different paths. 893 void Profile(llvm::FoldingSetNodeID &ID) const; 894 895 /// Profiles the diagnostic, including its path. 896 /// 897 /// Two diagnostics with the same issue along different paths will generate 898 /// different profiles. 899 void FullProfile(llvm::FoldingSetNodeID &ID) const; 900 }; 901 902 } // namespace ento 903 904 } // namespace clang 905 906 #endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H 907