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