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