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