1353942Sdim//===- PathDiagnostic.h - Path-Specific Diagnostic Handling -----*- C++ -*-===// 2353942Sdim// 3353942Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353942Sdim// See https://llvm.org/LICENSE.txt for license information. 5353942Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6353942Sdim// 7353942Sdim//===----------------------------------------------------------------------===// 8353942Sdim// 9353942Sdim// This file defines the PathDiagnostic-related interfaces. 10353942Sdim// 11353942Sdim//===----------------------------------------------------------------------===// 12353942Sdim 13353942Sdim#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H 14353942Sdim#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H 15353942Sdim 16353942Sdim#include "clang/AST/Stmt.h" 17353942Sdim#include "clang/Analysis/AnalysisDeclContext.h" 18353942Sdim#include "clang/Basic/LLVM.h" 19353942Sdim#include "clang/Basic/SourceLocation.h" 20353942Sdim#include "llvm/ADT/ArrayRef.h" 21353942Sdim#include "llvm/ADT/FoldingSet.h" 22353942Sdim#include "llvm/ADT/Optional.h" 23353942Sdim#include "llvm/ADT/PointerUnion.h" 24353942Sdim#include "llvm/ADT/SmallVector.h" 25353942Sdim#include "llvm/ADT/StringRef.h" 26353942Sdim#include "llvm/Support/Allocator.h" 27353942Sdim#include <cassert> 28353942Sdim#include <deque> 29353942Sdim#include <iterator> 30353942Sdim#include <list> 31353942Sdim#include <map> 32353942Sdim#include <memory> 33353942Sdim#include <set> 34353942Sdim#include <string> 35353942Sdim#include <utility> 36353942Sdim#include <vector> 37353942Sdim 38353942Sdimnamespace clang { 39353942Sdim 40353942Sdimclass AnalysisDeclContext; 41353942Sdimclass BinaryOperator; 42353942Sdimclass CallEnter; 43353942Sdimclass CallExitEnd; 44353942Sdimclass CallExpr; 45353942Sdimclass ConditionalOperator; 46353942Sdimclass Decl; 47353942Sdimclass Expr; 48353942Sdimclass LocationContext; 49353942Sdimclass MemberExpr; 50353942Sdimclass ProgramPoint; 51353942Sdimclass SourceManager; 52353942Sdim 53353942Sdimnamespace ento { 54353942Sdim 55353942Sdim//===----------------------------------------------------------------------===// 56353942Sdim// High-level interface for handlers of path-sensitive diagnostics. 57353942Sdim//===----------------------------------------------------------------------===// 58353942Sdim 59353942Sdimclass PathDiagnostic; 60353942Sdim 61353942Sdimclass PathDiagnosticConsumer { 62353942Sdimpublic: 63353942Sdim class PDFileEntry : public llvm::FoldingSetNode { 64353942Sdim public: 65353942Sdim PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {} 66353942Sdim 67353942Sdim using ConsumerFiles = std::vector<std::pair<StringRef, StringRef>>; 68353942Sdim 69353942Sdim /// A vector of <consumer,file> pairs. 70353942Sdim ConsumerFiles files; 71353942Sdim 72353942Sdim /// A precomputed hash tag used for uniquing PDFileEntry objects. 73353942Sdim const llvm::FoldingSetNodeID NodeID; 74353942Sdim 75353942Sdim /// Used for profiling in the FoldingSet. 76353942Sdim void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; } 77353942Sdim }; 78353942Sdim 79353942Sdim class FilesMade { 80353942Sdim llvm::BumpPtrAllocator Alloc; 81353942Sdim llvm::FoldingSet<PDFileEntry> Set; 82353942Sdim 83353942Sdim public: 84353942Sdim ~FilesMade(); 85353942Sdim 86353942Sdim bool empty() const { return Set.empty(); } 87353942Sdim 88353942Sdim void addDiagnostic(const PathDiagnostic &PD, 89353942Sdim StringRef ConsumerName, 90353942Sdim StringRef fileName); 91353942Sdim 92353942Sdim PDFileEntry::ConsumerFiles *getFiles(const PathDiagnostic &PD); 93353942Sdim }; 94353942Sdim 95353942Sdimprivate: 96353942Sdim virtual void anchor(); 97353942Sdim 98353942Sdimpublic: 99353942Sdim PathDiagnosticConsumer() = default; 100353942Sdim virtual ~PathDiagnosticConsumer(); 101353942Sdim 102353942Sdim void FlushDiagnostics(FilesMade *FilesMade); 103353942Sdim 104353942Sdim virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, 105353942Sdim FilesMade *filesMade) = 0; 106353942Sdim 107353942Sdim virtual StringRef getName() const = 0; 108353942Sdim 109353942Sdim void HandlePathDiagnostic(std::unique_ptr<PathDiagnostic> D); 110353942Sdim 111353942Sdim enum PathGenerationScheme { 112353942Sdim /// Only runs visitors, no output generated. 113353942Sdim None, 114353942Sdim 115353942Sdim /// Used for HTML, SARIF, and text output. 116353942Sdim Minimal, 117353942Sdim 118353942Sdim /// Used for plist output, used for "arrows" generation. 119353942Sdim Extensive, 120353942Sdim }; 121353942Sdim 122353942Sdim virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } 123353942Sdim 124353942Sdim bool shouldGenerateDiagnostics() const { 125353942Sdim return getGenerationScheme() != None; 126353942Sdim } 127353942Sdim 128353942Sdim bool shouldAddPathEdges() const { return getGenerationScheme() == Extensive; } 129353942Sdim 130353942Sdim virtual bool supportsLogicalOpControlFlow() const { return false; } 131353942Sdim 132353942Sdim /// Return true if the PathDiagnosticConsumer supports individual 133353942Sdim /// PathDiagnostics that span multiple files. 134353942Sdim virtual bool supportsCrossFileDiagnostics() const { return false; } 135353942Sdim 136353942Sdimprotected: 137353942Sdim bool flushed = false; 138353942Sdim llvm::FoldingSet<PathDiagnostic> Diags; 139353942Sdim}; 140353942Sdim 141353942Sdim//===----------------------------------------------------------------------===// 142353942Sdim// Path-sensitive diagnostics. 143353942Sdim//===----------------------------------------------------------------------===// 144353942Sdim 145353942Sdimclass PathDiagnosticRange : public SourceRange { 146353942Sdimpublic: 147353942Sdim bool isPoint = false; 148353942Sdim 149353942Sdim PathDiagnosticRange(SourceRange R, bool isP = false) 150353942Sdim : SourceRange(R), isPoint(isP) {} 151353942Sdim PathDiagnosticRange() = default; 152353942Sdim}; 153353942Sdim 154353942Sdimusing LocationOrAnalysisDeclContext = 155353942Sdim llvm::PointerUnion<const LocationContext *, AnalysisDeclContext *>; 156353942Sdim 157353942Sdimclass PathDiagnosticLocation { 158353942Sdimprivate: 159353942Sdim enum Kind { RangeK, SingleLocK, StmtK, DeclK } K = SingleLocK; 160353942Sdim 161353942Sdim const Stmt *S = nullptr; 162353942Sdim const Decl *D = nullptr; 163353942Sdim const SourceManager *SM = nullptr; 164353942Sdim FullSourceLoc Loc; 165353942Sdim PathDiagnosticRange Range; 166353942Sdim 167353942Sdim PathDiagnosticLocation(SourceLocation L, const SourceManager &sm, Kind kind) 168353942Sdim : K(kind), SM(&sm), Loc(genLocation(L)), Range(genRange()) {} 169353942Sdim 170353942Sdim FullSourceLoc genLocation( 171353942Sdim SourceLocation L = SourceLocation(), 172353942Sdim LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; 173353942Sdim 174353942Sdim PathDiagnosticRange genRange( 175353942Sdim LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; 176353942Sdim 177353942Sdimpublic: 178353942Sdim /// Create an invalid location. 179353942Sdim PathDiagnosticLocation() = default; 180353942Sdim 181353942Sdim /// Create a location corresponding to the given statement. 182353942Sdim PathDiagnosticLocation(const Stmt *s, const SourceManager &sm, 183353942Sdim LocationOrAnalysisDeclContext lac) 184353942Sdim : K(s->getBeginLoc().isValid() ? StmtK : SingleLocK), 185353942Sdim S(K == StmtK ? s : nullptr), SM(&sm), 186353942Sdim Loc(genLocation(SourceLocation(), lac)), Range(genRange(lac)) { 187353942Sdim assert(K == SingleLocK || S); 188353942Sdim assert(K == SingleLocK || Loc.isValid()); 189353942Sdim assert(K == SingleLocK || Range.isValid()); 190353942Sdim } 191353942Sdim 192353942Sdim /// Create a location corresponding to the given declaration. 193353942Sdim PathDiagnosticLocation(const Decl *d, const SourceManager &sm) 194353942Sdim : K(DeclK), D(d), SM(&sm), Loc(genLocation()), Range(genRange()) { 195353942Sdim assert(D); 196353942Sdim assert(Loc.isValid()); 197353942Sdim assert(Range.isValid()); 198353942Sdim } 199353942Sdim 200353942Sdim /// Create a location at an explicit offset in the source. 201353942Sdim /// 202353942Sdim /// This should only be used if there are no more appropriate constructors. 203353942Sdim PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm) 204353942Sdim : SM(&sm), Loc(loc, sm), Range(genRange()) { 205353942Sdim assert(Loc.isValid()); 206353942Sdim assert(Range.isValid()); 207353942Sdim } 208353942Sdim 209353942Sdim /// Create a location corresponding to the given declaration. 210353942Sdim static PathDiagnosticLocation create(const Decl *D, 211353942Sdim const SourceManager &SM) { 212353942Sdim return PathDiagnosticLocation(D, SM); 213353942Sdim } 214353942Sdim 215353942Sdim /// Create a location for the beginning of the declaration. 216353942Sdim static PathDiagnosticLocation createBegin(const Decl *D, 217353942Sdim const SourceManager &SM); 218353942Sdim 219353942Sdim /// Create a location for the beginning of the declaration. 220353942Sdim /// The third argument is ignored, useful for generic treatment 221353942Sdim /// of statements and declarations. 222353942Sdim static PathDiagnosticLocation 223353942Sdim createBegin(const Decl *D, const SourceManager &SM, 224353942Sdim const LocationOrAnalysisDeclContext LAC) { 225353942Sdim return createBegin(D, SM); 226353942Sdim } 227353942Sdim 228353942Sdim /// Create a location for the beginning of the statement. 229353942Sdim static PathDiagnosticLocation createBegin(const Stmt *S, 230353942Sdim const SourceManager &SM, 231353942Sdim const LocationOrAnalysisDeclContext LAC); 232353942Sdim 233353942Sdim /// Create a location for the end of the statement. 234353942Sdim /// 235353942Sdim /// If the statement is a CompoundStatement, the location will point to the 236353942Sdim /// closing brace instead of following it. 237353942Sdim static PathDiagnosticLocation createEnd(const Stmt *S, 238353942Sdim const SourceManager &SM, 239353942Sdim const LocationOrAnalysisDeclContext LAC); 240353942Sdim 241353942Sdim /// Create the location for the operator of the binary expression. 242353942Sdim /// Assumes the statement has a valid location. 243353942Sdim static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO, 244353942Sdim const SourceManager &SM); 245353942Sdim static PathDiagnosticLocation createConditionalColonLoc( 246353942Sdim const ConditionalOperator *CO, 247353942Sdim const SourceManager &SM); 248353942Sdim 249353942Sdim /// For member expressions, return the location of the '.' or '->'. 250353942Sdim /// Assumes the statement has a valid location. 251353942Sdim static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME, 252353942Sdim const SourceManager &SM); 253353942Sdim 254353942Sdim /// Create a location for the beginning of the compound statement. 255353942Sdim /// Assumes the statement has a valid location. 256353942Sdim static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS, 257353942Sdim const SourceManager &SM); 258353942Sdim 259353942Sdim /// Create a location for the end of the compound statement. 260353942Sdim /// Assumes the statement has a valid location. 261353942Sdim static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS, 262353942Sdim const SourceManager &SM); 263353942Sdim 264353942Sdim /// Create a location for the beginning of the enclosing declaration body. 265353942Sdim /// Defaults to the beginning of the first statement in the declaration body. 266353942Sdim static PathDiagnosticLocation createDeclBegin(const LocationContext *LC, 267353942Sdim const SourceManager &SM); 268353942Sdim 269353942Sdim /// Constructs a location for the end of the enclosing declaration body. 270353942Sdim /// Defaults to the end of brace. 271353942Sdim static PathDiagnosticLocation createDeclEnd(const LocationContext *LC, 272353942Sdim const SourceManager &SM); 273353942Sdim 274353942Sdim /// Create a location corresponding to the given valid ProgramPoint. 275353942Sdim static PathDiagnosticLocation create(const ProgramPoint &P, 276353942Sdim const SourceManager &SMng); 277353942Sdim 278353942Sdim /// Convert the given location into a single kind location. 279353942Sdim static PathDiagnosticLocation createSingleLocation( 280353942Sdim const PathDiagnosticLocation &PDL); 281353942Sdim 282353942Sdim /// Construct a source location that corresponds to either the beginning 283353942Sdim /// or the end of the given statement, or a nearby valid source location 284353942Sdim /// if the statement does not have a valid source location of its own. 285353942Sdim static SourceLocation 286353942Sdim getValidSourceLocation(const Stmt *S, LocationOrAnalysisDeclContext LAC, 287353942Sdim bool UseEndOfStatement = false); 288353942Sdim 289353942Sdim bool operator==(const PathDiagnosticLocation &X) const { 290353942Sdim return K == X.K && Loc == X.Loc && Range == X.Range; 291353942Sdim } 292353942Sdim 293353942Sdim bool operator!=(const PathDiagnosticLocation &X) const { 294353942Sdim return !(*this == X); 295353942Sdim } 296353942Sdim 297353942Sdim bool isValid() const { 298353942Sdim return SM != nullptr; 299353942Sdim } 300353942Sdim 301353942Sdim FullSourceLoc asLocation() const { 302353942Sdim return Loc; 303353942Sdim } 304353942Sdim 305353942Sdim PathDiagnosticRange asRange() const { 306353942Sdim return Range; 307353942Sdim } 308353942Sdim 309353942Sdim const Stmt *asStmt() const { assert(isValid()); return S; } 310353942Sdim const Stmt *getStmtOrNull() const { 311353942Sdim if (!isValid()) 312353942Sdim return nullptr; 313353942Sdim return asStmt(); 314353942Sdim } 315353942Sdim 316353942Sdim const Decl *asDecl() const { assert(isValid()); return D; } 317353942Sdim 318353942Sdim bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; } 319353942Sdim 320353942Sdim bool hasValidLocation() const { return asLocation().isValid(); } 321353942Sdim 322353942Sdim void invalidate() { 323353942Sdim *this = PathDiagnosticLocation(); 324353942Sdim } 325353942Sdim 326353942Sdim void flatten(); 327353942Sdim 328353942Sdim const SourceManager& getManager() const { assert(isValid()); return *SM; } 329353942Sdim 330353942Sdim void Profile(llvm::FoldingSetNodeID &ID) const; 331353942Sdim 332353942Sdim void dump() const; 333353942Sdim}; 334353942Sdim 335353942Sdimclass PathDiagnosticLocationPair { 336353942Sdimprivate: 337353942Sdim PathDiagnosticLocation Start, End; 338353942Sdim 339353942Sdimpublic: 340353942Sdim PathDiagnosticLocationPair(const PathDiagnosticLocation &start, 341353942Sdim const PathDiagnosticLocation &end) 342353942Sdim : Start(start), End(end) {} 343353942Sdim 344353942Sdim const PathDiagnosticLocation &getStart() const { return Start; } 345353942Sdim const PathDiagnosticLocation &getEnd() const { return End; } 346353942Sdim 347353942Sdim void setStart(const PathDiagnosticLocation &L) { Start = L; } 348353942Sdim void setEnd(const PathDiagnosticLocation &L) { End = L; } 349353942Sdim 350353942Sdim void flatten() { 351353942Sdim Start.flatten(); 352353942Sdim End.flatten(); 353353942Sdim } 354353942Sdim 355353942Sdim void Profile(llvm::FoldingSetNodeID &ID) const { 356353942Sdim Start.Profile(ID); 357353942Sdim End.Profile(ID); 358353942Sdim } 359353942Sdim}; 360353942Sdim 361353942Sdim//===----------------------------------------------------------------------===// 362353942Sdim// Path "pieces" for path-sensitive diagnostics. 363353942Sdim//===----------------------------------------------------------------------===// 364353942Sdim 365353942Sdimclass PathDiagnosticPiece: public llvm::FoldingSetNode { 366353942Sdimpublic: 367353942Sdim enum Kind { ControlFlow, Event, Macro, Call, Note, PopUp }; 368353942Sdim enum DisplayHint { Above, Below }; 369353942Sdim 370353942Sdimprivate: 371353942Sdim const std::string str; 372353942Sdim const Kind kind; 373353942Sdim const DisplayHint Hint; 374353942Sdim 375353942Sdim /// In the containing bug report, this piece is the last piece from 376353942Sdim /// the main source file. 377353942Sdim bool LastInMainSourceFile = false; 378353942Sdim 379353942Sdim /// A constant string that can be used to tag the PathDiagnosticPiece, 380353942Sdim /// typically with the identification of the creator. The actual pointer 381353942Sdim /// value is meant to be an identifier; the string itself is useful for 382353942Sdim /// debugging. 383353942Sdim StringRef Tag; 384353942Sdim 385353942Sdim std::vector<SourceRange> ranges; 386353942Sdim std::vector<FixItHint> fixits; 387353942Sdim 388353942Sdimprotected: 389353942Sdim PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below); 390353942Sdim PathDiagnosticPiece(Kind k, DisplayHint hint = Below); 391353942Sdim 392353942Sdimpublic: 393353942Sdim PathDiagnosticPiece() = delete; 394353942Sdim PathDiagnosticPiece(const PathDiagnosticPiece &) = delete; 395353942Sdim PathDiagnosticPiece &operator=(const PathDiagnosticPiece &) = delete; 396353942Sdim virtual ~PathDiagnosticPiece(); 397353942Sdim 398353942Sdim StringRef getString() const { return str; } 399353942Sdim 400353942Sdim /// Tag this PathDiagnosticPiece with the given C-string. 401353942Sdim void setTag(const char *tag) { Tag = tag; } 402353942Sdim 403353942Sdim /// Return the opaque tag (if any) on the PathDiagnosticPiece. 404353942Sdim const void *getTag() const { return Tag.data(); } 405353942Sdim 406353942Sdim /// Return the string representation of the tag. This is useful 407353942Sdim /// for debugging. 408353942Sdim StringRef getTagStr() const { return Tag; } 409353942Sdim 410353942Sdim /// getDisplayHint - Return a hint indicating where the diagnostic should 411353942Sdim /// be displayed by the PathDiagnosticConsumer. 412353942Sdim DisplayHint getDisplayHint() const { return Hint; } 413353942Sdim 414353942Sdim virtual PathDiagnosticLocation getLocation() const = 0; 415353942Sdim virtual void flattenLocations() = 0; 416353942Sdim 417353942Sdim Kind getKind() const { return kind; } 418353942Sdim 419353942Sdim void addRange(SourceRange R) { 420353942Sdim if (!R.isValid()) 421353942Sdim return; 422353942Sdim ranges.push_back(R); 423353942Sdim } 424353942Sdim 425353942Sdim void addRange(SourceLocation B, SourceLocation E) { 426353942Sdim if (!B.isValid() || !E.isValid()) 427353942Sdim return; 428353942Sdim ranges.push_back(SourceRange(B,E)); 429353942Sdim } 430353942Sdim 431353942Sdim void addFixit(FixItHint F) { 432353942Sdim fixits.push_back(F); 433353942Sdim } 434353942Sdim 435353942Sdim /// Return the SourceRanges associated with this PathDiagnosticPiece. 436353942Sdim ArrayRef<SourceRange> getRanges() const { return ranges; } 437353942Sdim 438353942Sdim /// Return the fix-it hints associated with this PathDiagnosticPiece. 439353942Sdim ArrayRef<FixItHint> getFixits() const { return fixits; } 440353942Sdim 441353942Sdim virtual void Profile(llvm::FoldingSetNodeID &ID) const; 442353942Sdim 443353942Sdim void setAsLastInMainSourceFile() { 444353942Sdim LastInMainSourceFile = true; 445353942Sdim } 446353942Sdim 447353942Sdim bool isLastInMainSourceFile() const { 448353942Sdim return LastInMainSourceFile; 449353942Sdim } 450353942Sdim 451353942Sdim virtual void dump() const = 0; 452353942Sdim}; 453353942Sdim 454353942Sdimusing PathDiagnosticPieceRef = std::shared_ptr<PathDiagnosticPiece>; 455353942Sdim 456353942Sdimclass PathPieces : public std::list<PathDiagnosticPieceRef> { 457353942Sdim void flattenTo(PathPieces &Primary, PathPieces &Current, 458353942Sdim bool ShouldFlattenMacros) const; 459353942Sdim 460353942Sdimpublic: 461353942Sdim PathPieces flatten(bool ShouldFlattenMacros) const { 462353942Sdim PathPieces Result; 463353942Sdim flattenTo(Result, Result, ShouldFlattenMacros); 464353942Sdim return Result; 465353942Sdim } 466353942Sdim 467353942Sdim void dump() const; 468353942Sdim}; 469353942Sdim 470353942Sdimclass PathDiagnosticSpotPiece : public PathDiagnosticPiece { 471353942Sdimprivate: 472353942Sdim PathDiagnosticLocation Pos; 473353942Sdim 474353942Sdimpublic: 475353942Sdim PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos, 476353942Sdim StringRef s, 477353942Sdim PathDiagnosticPiece::Kind k, 478353942Sdim bool addPosRange = true) 479353942Sdim : PathDiagnosticPiece(s, k), Pos(pos) { 480353942Sdim assert(Pos.isValid() && Pos.hasValidLocation() && 481353942Sdim "PathDiagnosticSpotPiece's must have a valid location."); 482353942Sdim if (addPosRange && Pos.hasRange()) addRange(Pos.asRange()); 483353942Sdim } 484353942Sdim 485353942Sdim PathDiagnosticLocation getLocation() const override { return Pos; } 486353942Sdim void flattenLocations() override { Pos.flatten(); } 487353942Sdim 488353942Sdim void Profile(llvm::FoldingSetNodeID &ID) const override; 489353942Sdim 490353942Sdim static bool classof(const PathDiagnosticPiece *P) { 491353942Sdim return P->getKind() == Event || P->getKind() == Macro || 492353942Sdim P->getKind() == Note || P->getKind() == PopUp; 493353942Sdim } 494353942Sdim}; 495353942Sdim 496353942Sdimclass PathDiagnosticEventPiece : public PathDiagnosticSpotPiece { 497353942Sdim Optional<bool> IsPrunable; 498353942Sdim 499353942Sdimpublic: 500353942Sdim PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, 501353942Sdim StringRef s, bool addPosRange = true) 502353942Sdim : PathDiagnosticSpotPiece(pos, s, Event, addPosRange) {} 503353942Sdim ~PathDiagnosticEventPiece() override; 504353942Sdim 505353942Sdim /// Mark the diagnostic piece as being potentially prunable. This 506353942Sdim /// flag may have been previously set, at which point it will not 507353942Sdim /// be reset unless one specifies to do so. 508353942Sdim void setPrunable(bool isPrunable, bool override = false) { 509353942Sdim if (IsPrunable.hasValue() && !override) 510353942Sdim return; 511353942Sdim IsPrunable = isPrunable; 512353942Sdim } 513353942Sdim 514353942Sdim /// Return true if the diagnostic piece is prunable. 515353942Sdim bool isPrunable() const { 516353942Sdim return IsPrunable.hasValue() ? IsPrunable.getValue() : false; 517353942Sdim } 518353942Sdim 519353942Sdim void dump() const override; 520353942Sdim 521353942Sdim static bool classof(const PathDiagnosticPiece *P) { 522353942Sdim return P->getKind() == Event; 523353942Sdim } 524353942Sdim}; 525353942Sdim 526353942Sdimclass PathDiagnosticCallPiece : public PathDiagnosticPiece { 527353942Sdim const Decl *Caller; 528353942Sdim const Decl *Callee = nullptr; 529353942Sdim 530353942Sdim // Flag signifying that this diagnostic has only call enter and no matching 531353942Sdim // call exit. 532353942Sdim bool NoExit; 533353942Sdim 534353942Sdim // Flag signifying that the callee function is an Objective-C autosynthesized 535353942Sdim // property getter or setter. 536353942Sdim bool IsCalleeAnAutosynthesizedPropertyAccessor = false; 537353942Sdim 538353942Sdim // The custom string, which should appear after the call Return Diagnostic. 539353942Sdim // TODO: Should we allow multiple diagnostics? 540353942Sdim std::string CallStackMessage; 541353942Sdim 542353942Sdim PathDiagnosticCallPiece(const Decl *callerD, 543353942Sdim const PathDiagnosticLocation &callReturnPos) 544353942Sdim : PathDiagnosticPiece(Call), Caller(callerD), NoExit(false), 545353942Sdim callReturn(callReturnPos) {} 546353942Sdim PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller) 547353942Sdim : PathDiagnosticPiece(Call), Caller(caller), NoExit(true), 548353942Sdim path(oldPath) {} 549353942Sdim 550353942Sdimpublic: 551353942Sdim PathDiagnosticLocation callEnter; 552353942Sdim PathDiagnosticLocation callEnterWithin; 553353942Sdim PathDiagnosticLocation callReturn; 554353942Sdim PathPieces path; 555353942Sdim 556353942Sdim ~PathDiagnosticCallPiece() override; 557353942Sdim 558353942Sdim const Decl *getCaller() const { return Caller; } 559353942Sdim 560353942Sdim const Decl *getCallee() const { return Callee; } 561353942Sdim void setCallee(const CallEnter &CE, const SourceManager &SM); 562353942Sdim 563353942Sdim bool hasCallStackMessage() { return !CallStackMessage.empty(); } 564353942Sdim void setCallStackMessage(StringRef st) { CallStackMessage = st; } 565353942Sdim 566353942Sdim PathDiagnosticLocation getLocation() const override { return callEnter; } 567353942Sdim 568353942Sdim std::shared_ptr<PathDiagnosticEventPiece> getCallEnterEvent() const; 569353942Sdim std::shared_ptr<PathDiagnosticEventPiece> 570353942Sdim getCallEnterWithinCallerEvent() const; 571353942Sdim std::shared_ptr<PathDiagnosticEventPiece> getCallExitEvent() const; 572353942Sdim 573353942Sdim void flattenLocations() override { 574353942Sdim callEnter.flatten(); 575353942Sdim callReturn.flatten(); 576353942Sdim for (const auto &I : path) 577353942Sdim I->flattenLocations(); 578353942Sdim } 579353942Sdim 580353942Sdim static std::shared_ptr<PathDiagnosticCallPiece> 581353942Sdim construct(const CallExitEnd &CE, 582353942Sdim const SourceManager &SM); 583353942Sdim 584353942Sdim static PathDiagnosticCallPiece *construct(PathPieces &pieces, 585353942Sdim const Decl *caller); 586353942Sdim 587353942Sdim void dump() const override; 588353942Sdim 589353942Sdim void Profile(llvm::FoldingSetNodeID &ID) const override; 590353942Sdim 591353942Sdim static bool classof(const PathDiagnosticPiece *P) { 592353942Sdim return P->getKind() == Call; 593353942Sdim } 594353942Sdim}; 595353942Sdim 596353942Sdimclass PathDiagnosticControlFlowPiece : public PathDiagnosticPiece { 597353942Sdim std::vector<PathDiagnosticLocationPair> LPairs; 598353942Sdim 599353942Sdimpublic: 600353942Sdim PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, 601353942Sdim const PathDiagnosticLocation &endPos, 602353942Sdim StringRef s) 603353942Sdim : PathDiagnosticPiece(s, ControlFlow) { 604353942Sdim LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); 605353942Sdim } 606353942Sdim 607353942Sdim PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, 608353942Sdim const PathDiagnosticLocation &endPos) 609353942Sdim : PathDiagnosticPiece(ControlFlow) { 610353942Sdim LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); 611353942Sdim } 612353942Sdim 613353942Sdim ~PathDiagnosticControlFlowPiece() override; 614353942Sdim 615353942Sdim PathDiagnosticLocation getStartLocation() const { 616353942Sdim assert(!LPairs.empty() && 617353942Sdim "PathDiagnosticControlFlowPiece needs at least one location."); 618353942Sdim return LPairs[0].getStart(); 619353942Sdim } 620353942Sdim 621353942Sdim PathDiagnosticLocation getEndLocation() const { 622353942Sdim assert(!LPairs.empty() && 623353942Sdim "PathDiagnosticControlFlowPiece needs at least one location."); 624353942Sdim return LPairs[0].getEnd(); 625353942Sdim } 626353942Sdim 627353942Sdim void setStartLocation(const PathDiagnosticLocation &L) { 628353942Sdim LPairs[0].setStart(L); 629353942Sdim } 630353942Sdim 631353942Sdim void setEndLocation(const PathDiagnosticLocation &L) { 632353942Sdim LPairs[0].setEnd(L); 633353942Sdim } 634353942Sdim 635353942Sdim void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); } 636353942Sdim 637353942Sdim PathDiagnosticLocation getLocation() const override { 638353942Sdim return getStartLocation(); 639353942Sdim } 640353942Sdim 641353942Sdim using iterator = std::vector<PathDiagnosticLocationPair>::iterator; 642353942Sdim 643353942Sdim iterator begin() { return LPairs.begin(); } 644353942Sdim iterator end() { return LPairs.end(); } 645353942Sdim 646353942Sdim void flattenLocations() override { 647353942Sdim for (auto &I : *this) 648353942Sdim I.flatten(); 649353942Sdim } 650353942Sdim 651353942Sdim using const_iterator = 652353942Sdim std::vector<PathDiagnosticLocationPair>::const_iterator; 653353942Sdim 654353942Sdim const_iterator begin() const { return LPairs.begin(); } 655353942Sdim const_iterator end() const { return LPairs.end(); } 656353942Sdim 657353942Sdim static bool classof(const PathDiagnosticPiece *P) { 658353942Sdim return P->getKind() == ControlFlow; 659353942Sdim } 660353942Sdim 661353942Sdim void dump() const override; 662353942Sdim 663353942Sdim void Profile(llvm::FoldingSetNodeID &ID) const override; 664353942Sdim}; 665353942Sdim 666353942Sdimclass PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece { 667353942Sdimpublic: 668353942Sdim PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos) 669353942Sdim : PathDiagnosticSpotPiece(pos, "", Macro) {} 670353942Sdim ~PathDiagnosticMacroPiece() override; 671353942Sdim 672353942Sdim PathPieces subPieces; 673353942Sdim 674353942Sdim void flattenLocations() override { 675353942Sdim PathDiagnosticSpotPiece::flattenLocations(); 676353942Sdim for (const auto &I : subPieces) 677353942Sdim I->flattenLocations(); 678353942Sdim } 679353942Sdim 680353942Sdim static bool classof(const PathDiagnosticPiece *P) { 681353942Sdim return P->getKind() == Macro; 682353942Sdim } 683353942Sdim 684353942Sdim void dump() const override; 685353942Sdim 686353942Sdim void Profile(llvm::FoldingSetNodeID &ID) const override; 687353942Sdim}; 688353942Sdim 689353942Sdimclass PathDiagnosticNotePiece: public PathDiagnosticSpotPiece { 690353942Sdimpublic: 691353942Sdim PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S, 692353942Sdim bool AddPosRange = true) 693353942Sdim : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {} 694353942Sdim ~PathDiagnosticNotePiece() override; 695353942Sdim 696353942Sdim static bool classof(const PathDiagnosticPiece *P) { 697353942Sdim return P->getKind() == Note; 698353942Sdim } 699353942Sdim 700353942Sdim void dump() const override; 701353942Sdim 702353942Sdim void Profile(llvm::FoldingSetNodeID &ID) const override; 703353942Sdim}; 704353942Sdim 705353942Sdimclass PathDiagnosticPopUpPiece: public PathDiagnosticSpotPiece { 706353942Sdimpublic: 707353942Sdim PathDiagnosticPopUpPiece(const PathDiagnosticLocation &Pos, StringRef S, 708353942Sdim bool AddPosRange = true) 709353942Sdim : PathDiagnosticSpotPiece(Pos, S, PopUp, AddPosRange) {} 710353942Sdim ~PathDiagnosticPopUpPiece() override; 711353942Sdim 712353942Sdim static bool classof(const PathDiagnosticPiece *P) { 713353942Sdim return P->getKind() == PopUp; 714353942Sdim } 715353942Sdim 716353942Sdim void dump() const override; 717353942Sdim 718353942Sdim void Profile(llvm::FoldingSetNodeID &ID) const override; 719353942Sdim}; 720353942Sdim 721353942Sdim/// File IDs mapped to sets of line numbers. 722353942Sdimusing FilesToLineNumsMap = std::map<FileID, std::set<unsigned>>; 723353942Sdim 724353942Sdim/// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive 725353942Sdim/// diagnostic. It represents an ordered-collection of PathDiagnosticPieces, 726353942Sdim/// each which represent the pieces of the path. 727353942Sdimclass PathDiagnostic : public llvm::FoldingSetNode { 728353942Sdim std::string CheckerName; 729353942Sdim const Decl *DeclWithIssue; 730353942Sdim std::string BugType; 731353942Sdim std::string VerboseDesc; 732353942Sdim std::string ShortDesc; 733353942Sdim std::string Category; 734353942Sdim std::deque<std::string> OtherDesc; 735353942Sdim 736353942Sdim /// Loc The location of the path diagnostic report. 737353942Sdim PathDiagnosticLocation Loc; 738353942Sdim 739353942Sdim PathPieces pathImpl; 740353942Sdim SmallVector<PathPieces *, 3> pathStack; 741353942Sdim 742353942Sdim /// Important bug uniqueing location. 743353942Sdim /// The location info is useful to differentiate between bugs. 744353942Sdim PathDiagnosticLocation UniqueingLoc; 745353942Sdim const Decl *UniqueingDecl; 746353942Sdim 747353942Sdim /// Lines executed in the path. 748353942Sdim std::unique_ptr<FilesToLineNumsMap> ExecutedLines; 749353942Sdim 750353942Sdimpublic: 751353942Sdim PathDiagnostic() = delete; 752353942Sdim PathDiagnostic(StringRef CheckerName, const Decl *DeclWithIssue, 753353942Sdim StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, 754353942Sdim StringRef category, PathDiagnosticLocation LocationToUnique, 755353942Sdim const Decl *DeclToUnique, 756353942Sdim std::unique_ptr<FilesToLineNumsMap> ExecutedLines); 757353942Sdim ~PathDiagnostic(); 758353942Sdim 759353942Sdim const PathPieces &path; 760353942Sdim 761353942Sdim /// Return the path currently used by builders for constructing the 762353942Sdim /// PathDiagnostic. 763353942Sdim PathPieces &getActivePath() { 764353942Sdim if (pathStack.empty()) 765353942Sdim return pathImpl; 766353942Sdim return *pathStack.back(); 767353942Sdim } 768353942Sdim 769353942Sdim /// Return a mutable version of 'path'. 770353942Sdim PathPieces &getMutablePieces() { 771353942Sdim return pathImpl; 772353942Sdim } 773353942Sdim 774353942Sdim /// Return the unrolled size of the path. 775353942Sdim unsigned full_size(); 776353942Sdim 777353942Sdim void pushActivePath(PathPieces *p) { pathStack.push_back(p); } 778353942Sdim void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); } 779353942Sdim 780353942Sdim bool isWithinCall() const { return !pathStack.empty(); } 781353942Sdim 782353942Sdim void setEndOfPath(PathDiagnosticPieceRef EndPiece) { 783353942Sdim assert(!Loc.isValid() && "End location already set!"); 784353942Sdim Loc = EndPiece->getLocation(); 785353942Sdim assert(Loc.isValid() && "Invalid location for end-of-path piece"); 786353942Sdim getActivePath().push_back(std::move(EndPiece)); 787353942Sdim } 788353942Sdim 789353942Sdim void appendToDesc(StringRef S) { 790353942Sdim if (!ShortDesc.empty()) 791353942Sdim ShortDesc += S; 792353942Sdim VerboseDesc += S; 793353942Sdim } 794353942Sdim 795353942Sdim StringRef getVerboseDescription() const { return VerboseDesc; } 796353942Sdim 797353942Sdim StringRef getShortDescription() const { 798353942Sdim return ShortDesc.empty() ? VerboseDesc : ShortDesc; 799353942Sdim } 800353942Sdim 801353942Sdim StringRef getCheckerName() const { return CheckerName; } 802353942Sdim StringRef getBugType() const { return BugType; } 803353942Sdim StringRef getCategory() const { return Category; } 804353942Sdim 805353942Sdim using meta_iterator = std::deque<std::string>::const_iterator; 806353942Sdim 807353942Sdim meta_iterator meta_begin() const { return OtherDesc.begin(); } 808353942Sdim meta_iterator meta_end() const { return OtherDesc.end(); } 809353942Sdim void addMeta(StringRef s) { OtherDesc.push_back(s); } 810353942Sdim 811353942Sdim const FilesToLineNumsMap &getExecutedLines() const { 812353942Sdim return *ExecutedLines; 813353942Sdim } 814353942Sdim 815353942Sdim FilesToLineNumsMap &getExecutedLines() { 816353942Sdim return *ExecutedLines; 817353942Sdim } 818353942Sdim 819353942Sdim /// Return the semantic context where an issue occurred. If the 820353942Sdim /// issue occurs along a path, this represents the "central" area 821353942Sdim /// where the bug manifests. 822353942Sdim const Decl *getDeclWithIssue() const { return DeclWithIssue; } 823353942Sdim 824353942Sdim void setDeclWithIssue(const Decl *D) { 825353942Sdim DeclWithIssue = D; 826353942Sdim } 827353942Sdim 828353942Sdim PathDiagnosticLocation getLocation() const { 829353942Sdim return Loc; 830353942Sdim } 831353942Sdim 832353942Sdim void setLocation(PathDiagnosticLocation NewLoc) { 833353942Sdim Loc = NewLoc; 834353942Sdim } 835353942Sdim 836353942Sdim /// Get the location on which the report should be uniqued. 837353942Sdim PathDiagnosticLocation getUniqueingLoc() const { 838353942Sdim return UniqueingLoc; 839353942Sdim } 840353942Sdim 841353942Sdim /// Get the declaration containing the uniqueing location. 842353942Sdim const Decl *getUniqueingDecl() const { 843353942Sdim return UniqueingDecl; 844353942Sdim } 845353942Sdim 846353942Sdim void flattenLocations() { 847353942Sdim Loc.flatten(); 848353942Sdim for (const auto &I : pathImpl) 849353942Sdim I->flattenLocations(); 850353942Sdim } 851353942Sdim 852353942Sdim /// Profiles the diagnostic, independent of the path it references. 853353942Sdim /// 854353942Sdim /// This can be used to merge diagnostics that refer to the same issue 855353942Sdim /// along different paths. 856353942Sdim void Profile(llvm::FoldingSetNodeID &ID) const; 857353942Sdim 858353942Sdim /// Profiles the diagnostic, including its path. 859353942Sdim /// 860353942Sdim /// Two diagnostics with the same issue along different paths will generate 861353942Sdim /// different profiles. 862353942Sdim void FullProfile(llvm::FoldingSetNodeID &ID) const; 863353942Sdim}; 864353942Sdim 865353942Sdim} // namespace ento 866353942Sdim} // namespace clang 867353942Sdim 868353942Sdim#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H 869