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