10b57cec5SDimitry Andric //===- BugReporter.h - Generate PathDiagnostics -----------------*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file defines BugReporter, a utility class for generating 100b57cec5SDimitry Andric // PathDiagnostics for analyses based on ProgramState. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H 150b57cec5SDimitry Andric #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H 160b57cec5SDimitry Andric 17a7dea167SDimitry Andric #include "clang/Analysis/PathDiagnostic.h" 180b57cec5SDimitry Andric #include "clang/Basic/LLVM.h" 190b57cec5SDimitry Andric #include "clang/Basic/SourceLocation.h" 205ffd83dbSDimitry Andric #include "clang/Lex/Preprocessor.h" 210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" 225f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugSuppression.h" 235ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 255ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 260b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 270b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 280b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" 290b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h" 300b57cec5SDimitry Andric #include "llvm/ADT/FoldingSet.h" 310b57cec5SDimitry Andric #include "llvm/ADT/ImmutableSet.h" 320b57cec5SDimitry Andric #include "llvm/ADT/SmallSet.h" 330b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h" 340b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h" 350b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 360b57cec5SDimitry Andric #include "llvm/ADT/ilist.h" 370b57cec5SDimitry Andric #include "llvm/ADT/ilist_node.h" 380b57cec5SDimitry Andric #include "llvm/ADT/iterator_range.h" 390b57cec5SDimitry Andric #include <cassert> 400b57cec5SDimitry Andric #include <memory> 41bdd1243dSDimitry Andric #include <optional> 420b57cec5SDimitry Andric #include <string> 430b57cec5SDimitry Andric #include <utility> 440b57cec5SDimitry Andric #include <vector> 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric namespace clang { 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric class AnalyzerOptions; 490b57cec5SDimitry Andric class ASTContext; 500b57cec5SDimitry Andric class Decl; 510b57cec5SDimitry Andric class LocationContext; 520b57cec5SDimitry Andric class SourceManager; 530b57cec5SDimitry Andric class Stmt; 540b57cec5SDimitry Andric 550b57cec5SDimitry Andric namespace ento { 560b57cec5SDimitry Andric 570b57cec5SDimitry Andric class BugType; 580b57cec5SDimitry Andric class CheckerBase; 590b57cec5SDimitry Andric class ExplodedGraph; 600b57cec5SDimitry Andric class ExplodedNode; 610b57cec5SDimitry Andric class ExprEngine; 620b57cec5SDimitry Andric class MemRegion; 630b57cec5SDimitry Andric 640b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 650b57cec5SDimitry Andric // Interface for individual bug reports. 660b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric /// A mapping from diagnostic consumers to the diagnostics they should 690b57cec5SDimitry Andric /// consume. 700b57cec5SDimitry Andric using DiagnosticForConsumerMapTy = 710b57cec5SDimitry Andric llvm::DenseMap<PathDiagnosticConsumer *, std::unique_ptr<PathDiagnostic>>; 720b57cec5SDimitry Andric 73a7dea167SDimitry Andric /// Interface for classes constructing Stack hints. 74a7dea167SDimitry Andric /// 75a7dea167SDimitry Andric /// If a PathDiagnosticEvent occurs in a different frame than the final 76a7dea167SDimitry Andric /// diagnostic the hints can be used to summarize the effect of the call. 77a7dea167SDimitry Andric class StackHintGenerator { 780b57cec5SDimitry Andric public: 79a7dea167SDimitry Andric virtual ~StackHintGenerator() = 0; 800b57cec5SDimitry Andric 81a7dea167SDimitry Andric /// Construct the Diagnostic message for the given ExplodedNode. 82a7dea167SDimitry Andric virtual std::string getMessage(const ExplodedNode *N) = 0; 830b57cec5SDimitry Andric }; 840b57cec5SDimitry Andric 85a7dea167SDimitry Andric /// Constructs a Stack hint for the given symbol. 86a7dea167SDimitry Andric /// 87a7dea167SDimitry Andric /// The class knows how to construct the stack hint message based on 88a7dea167SDimitry Andric /// traversing the CallExpr associated with the call and checking if the given 89a7dea167SDimitry Andric /// symbol is returned or is one of the arguments. 90a7dea167SDimitry Andric /// The hint can be customized by redefining 'getMessageForX()' methods. 91a7dea167SDimitry Andric class StackHintGeneratorForSymbol : public StackHintGenerator { 92a7dea167SDimitry Andric private: 93a7dea167SDimitry Andric SymbolRef Sym; 94a7dea167SDimitry Andric std::string Msg; 95a7dea167SDimitry Andric 96a7dea167SDimitry Andric public: StackHintGeneratorForSymbol(SymbolRef S,StringRef M)97a7dea167SDimitry Andric StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {} 98a7dea167SDimitry Andric ~StackHintGeneratorForSymbol() override = default; 99a7dea167SDimitry Andric 100a7dea167SDimitry Andric /// Search the call expression for the symbol Sym and dispatch the 101a7dea167SDimitry Andric /// 'getMessageForX()' methods to construct a specific message. 102a7dea167SDimitry Andric std::string getMessage(const ExplodedNode *N) override; 103a7dea167SDimitry Andric 104a7dea167SDimitry Andric /// Produces the message of the following form: 105a7dea167SDimitry Andric /// 'Msg via Nth parameter' 106a7dea167SDimitry Andric virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex); 107a7dea167SDimitry Andric getMessageForReturn(const CallExpr * CallExpr)108a7dea167SDimitry Andric virtual std::string getMessageForReturn(const CallExpr *CallExpr) { 109a7dea167SDimitry Andric return Msg; 110a7dea167SDimitry Andric } 111a7dea167SDimitry Andric getMessageForSymbolNotFound()112a7dea167SDimitry Andric virtual std::string getMessageForSymbolNotFound() { 113a7dea167SDimitry Andric return Msg; 114a7dea167SDimitry Andric } 115a7dea167SDimitry Andric }; 116a7dea167SDimitry Andric 117a7dea167SDimitry Andric /// This class provides an interface through which checkers can create 118a7dea167SDimitry Andric /// individual bug reports. 119a7dea167SDimitry Andric class BugReport { 120a7dea167SDimitry Andric public: 121a7dea167SDimitry Andric enum class Kind { Basic, PathSensitive }; 1220b57cec5SDimitry Andric 1230b57cec5SDimitry Andric protected: 1240b57cec5SDimitry Andric friend class BugReportEquivClass; 1250b57cec5SDimitry Andric friend class BugReporter; 1260b57cec5SDimitry Andric 127a7dea167SDimitry Andric Kind K; 1280b57cec5SDimitry Andric const BugType& BT; 1290b57cec5SDimitry Andric std::string ShortDescription; 1300b57cec5SDimitry Andric std::string Description; 1310b57cec5SDimitry Andric 1320b57cec5SDimitry Andric SmallVector<SourceRange, 4> Ranges; 133a7dea167SDimitry Andric SmallVector<std::shared_ptr<PathDiagnosticNotePiece>, 4> Notes; 134a7dea167SDimitry Andric SmallVector<FixItHint, 4> Fixits; 1350b57cec5SDimitry Andric BugReport(Kind kind,const BugType & bt,StringRef desc)136a7dea167SDimitry Andric BugReport(Kind kind, const BugType &bt, StringRef desc) 1375ffd83dbSDimitry Andric : BugReport(kind, bt, "", desc) {} 138a7dea167SDimitry Andric BugReport(Kind K,const BugType & BT,StringRef ShortDescription,StringRef Description)139a7dea167SDimitry Andric BugReport(Kind K, const BugType &BT, StringRef ShortDescription, 140a7dea167SDimitry Andric StringRef Description) 141a7dea167SDimitry Andric : K(K), BT(BT), ShortDescription(ShortDescription), 142a7dea167SDimitry Andric Description(Description) {} 143a7dea167SDimitry Andric 144a7dea167SDimitry Andric public: 145a7dea167SDimitry Andric virtual ~BugReport() = default; 146a7dea167SDimitry Andric getKind()147a7dea167SDimitry Andric Kind getKind() const { return K; } 148a7dea167SDimitry Andric getBugType()149a7dea167SDimitry Andric const BugType& getBugType() const { return BT; } 150a7dea167SDimitry Andric 151a7dea167SDimitry Andric /// A verbose warning message that is appropriate for displaying next to 152a7dea167SDimitry Andric /// the source code that introduces the problem. The description should be 153a7dea167SDimitry Andric /// at least a full sentence starting with a capital letter. The period at 154a7dea167SDimitry Andric /// the end of the warning is traditionally omitted. If the description 155a7dea167SDimitry Andric /// consists of multiple sentences, periods between the sentences are 156a7dea167SDimitry Andric /// encouraged, but the period at the end of the description is still omitted. getDescription()157a7dea167SDimitry Andric StringRef getDescription() const { return Description; } 158a7dea167SDimitry Andric 159a7dea167SDimitry Andric /// A short general warning message that is appropriate for displaying in 160a7dea167SDimitry Andric /// the list of all reported bugs. It should describe what kind of bug is found 161a7dea167SDimitry Andric /// but does not need to try to go into details of that specific bug. 162a7dea167SDimitry Andric /// Grammatical conventions of getDescription() apply here as well. 163a7dea167SDimitry Andric StringRef getShortDescription(bool UseFallback = true) const { 164a7dea167SDimitry Andric if (ShortDescription.empty() && UseFallback) 165a7dea167SDimitry Andric return Description; 166a7dea167SDimitry Andric return ShortDescription; 167a7dea167SDimitry Andric } 168a7dea167SDimitry Andric 169a7dea167SDimitry Andric /// The primary location of the bug report that points at the undesirable 170a7dea167SDimitry Andric /// behavior in the code. UIs should attach the warning description to this 171a7dea167SDimitry Andric /// location. The warning description should describe the bad behavior 172a7dea167SDimitry Andric /// at this location. 173a7dea167SDimitry Andric virtual PathDiagnosticLocation getLocation() const = 0; 174a7dea167SDimitry Andric 175a7dea167SDimitry Andric /// The smallest declaration that contains the bug location. 176a7dea167SDimitry Andric /// This is purely cosmetic; the declaration can be displayed to the user 177a7dea167SDimitry Andric /// but it does not affect whether the report is emitted. 178a7dea167SDimitry Andric virtual const Decl *getDeclWithIssue() const = 0; 179a7dea167SDimitry Andric 180a7dea167SDimitry Andric /// Get the location on which the report should be uniqued. Two warnings are 181a7dea167SDimitry Andric /// considered to be equivalent whenever they have the same bug types, 182a7dea167SDimitry Andric /// descriptions, and uniqueing locations. Out of a class of equivalent 183a7dea167SDimitry Andric /// warnings only one gets displayed to the user. For most warnings the 184a7dea167SDimitry Andric /// uniqueing location coincides with their location, but sometimes 185a7dea167SDimitry Andric /// it makes sense to use different locations. For example, a leak 186a7dea167SDimitry Andric /// checker can place the warning at the location where the last reference 187a7dea167SDimitry Andric /// to the leaking resource is dropped but at the same time unique the warning 188a7dea167SDimitry Andric /// by where that resource is acquired (allocated). 189a7dea167SDimitry Andric virtual PathDiagnosticLocation getUniqueingLocation() const = 0; 190a7dea167SDimitry Andric 191a7dea167SDimitry Andric /// Get the declaration that corresponds to (usually contains) the uniqueing 192a7dea167SDimitry Andric /// location. This is not actively used for uniqueing, i.e. otherwise 193a7dea167SDimitry Andric /// identical reports that have different uniqueing decls will be considered 194a7dea167SDimitry Andric /// equivalent. 195a7dea167SDimitry Andric virtual const Decl *getUniqueingDecl() const = 0; 196a7dea167SDimitry Andric 197a7dea167SDimitry Andric /// Add new item to the list of additional notes that need to be attached to 198a7dea167SDimitry Andric /// this report. If the report is path-sensitive, these notes will not be 199a7dea167SDimitry Andric /// displayed as part of the execution path explanation, but will be displayed 200a7dea167SDimitry Andric /// separately. Use bug visitors if you need to add an extra path note. 201a7dea167SDimitry Andric void addNote(StringRef Msg, const PathDiagnosticLocation &Pos, 202a7dea167SDimitry Andric ArrayRef<SourceRange> Ranges = {}) { 203a7dea167SDimitry Andric auto P = std::make_shared<PathDiagnosticNotePiece>(Pos, Msg); 204a7dea167SDimitry Andric 205a7dea167SDimitry Andric for (const auto &R : Ranges) 206a7dea167SDimitry Andric P->addRange(R); 207a7dea167SDimitry Andric 208a7dea167SDimitry Andric Notes.push_back(std::move(P)); 209a7dea167SDimitry Andric } 210a7dea167SDimitry Andric getNotes()211a7dea167SDimitry Andric ArrayRef<std::shared_ptr<PathDiagnosticNotePiece>> getNotes() { 212a7dea167SDimitry Andric return Notes; 213a7dea167SDimitry Andric } 214a7dea167SDimitry Andric 215a7dea167SDimitry Andric /// Add a range to a bug report. 216a7dea167SDimitry Andric /// 217a7dea167SDimitry Andric /// Ranges are used to highlight regions of interest in the source code. 218a7dea167SDimitry Andric /// They should be at the same source code line as the BugReport location. 219a7dea167SDimitry Andric /// By default, the source range of the statement corresponding to the error 220a7dea167SDimitry Andric /// node will be used; add a single invalid range to specify absence of 221a7dea167SDimitry Andric /// ranges. addRange(SourceRange R)222a7dea167SDimitry Andric void addRange(SourceRange R) { 223a7dea167SDimitry Andric assert((R.isValid() || Ranges.empty()) && "Invalid range can only be used " 224a7dea167SDimitry Andric "to specify that the report does not have a range."); 225a7dea167SDimitry Andric Ranges.push_back(R); 226a7dea167SDimitry Andric } 227a7dea167SDimitry Andric 228a7dea167SDimitry Andric /// Get the SourceRanges associated with the report. getRanges()229a7dea167SDimitry Andric virtual ArrayRef<SourceRange> getRanges() const { 230a7dea167SDimitry Andric return Ranges; 231a7dea167SDimitry Andric } 232a7dea167SDimitry Andric 233a7dea167SDimitry Andric /// Add a fix-it hint to the bug report. 234a7dea167SDimitry Andric /// 235a7dea167SDimitry Andric /// Fix-it hints are the suggested edits to the code that would resolve 236a7dea167SDimitry Andric /// the problem explained by the bug report. Fix-it hints should be 237a7dea167SDimitry Andric /// as conservative as possible because it is not uncommon for the user 238a7dea167SDimitry Andric /// to blindly apply all fixits to their project. Note that it is very hard 239a7dea167SDimitry Andric /// to produce a good fix-it hint for most path-sensitive warnings. addFixItHint(const FixItHint & F)240a7dea167SDimitry Andric void addFixItHint(const FixItHint &F) { 241a7dea167SDimitry Andric Fixits.push_back(F); 242a7dea167SDimitry Andric } 243a7dea167SDimitry Andric getFixits()244a7dea167SDimitry Andric llvm::ArrayRef<FixItHint> getFixits() const { return Fixits; } 245a7dea167SDimitry Andric 246a7dea167SDimitry Andric /// Reports are uniqued to ensure that we do not emit multiple diagnostics 247a7dea167SDimitry Andric /// for each bug. 248a7dea167SDimitry Andric virtual void Profile(llvm::FoldingSetNodeID& hash) const = 0; 249a7dea167SDimitry Andric }; 250a7dea167SDimitry Andric 251a7dea167SDimitry Andric class BasicBugReport : public BugReport { 252a7dea167SDimitry Andric PathDiagnosticLocation Location; 253a7dea167SDimitry Andric const Decl *DeclWithIssue = nullptr; 254a7dea167SDimitry Andric 255a7dea167SDimitry Andric public: BasicBugReport(const BugType & bt,StringRef desc,PathDiagnosticLocation l)256a7dea167SDimitry Andric BasicBugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l) 257a7dea167SDimitry Andric : BugReport(Kind::Basic, bt, desc), Location(l) {} 258a7dea167SDimitry Andric classof(const BugReport * R)259a7dea167SDimitry Andric static bool classof(const BugReport *R) { 260a7dea167SDimitry Andric return R->getKind() == Kind::Basic; 261a7dea167SDimitry Andric } 262a7dea167SDimitry Andric getLocation()263a7dea167SDimitry Andric PathDiagnosticLocation getLocation() const override { 264a7dea167SDimitry Andric assert(Location.isValid()); 265a7dea167SDimitry Andric return Location; 266a7dea167SDimitry Andric } 267a7dea167SDimitry Andric getDeclWithIssue()268a7dea167SDimitry Andric const Decl *getDeclWithIssue() const override { 269a7dea167SDimitry Andric return DeclWithIssue; 270a7dea167SDimitry Andric } 271a7dea167SDimitry Andric getUniqueingLocation()272a7dea167SDimitry Andric PathDiagnosticLocation getUniqueingLocation() const override { 273a7dea167SDimitry Andric return getLocation(); 274a7dea167SDimitry Andric } 275a7dea167SDimitry Andric getUniqueingDecl()276a7dea167SDimitry Andric const Decl *getUniqueingDecl() const override { 277a7dea167SDimitry Andric return getDeclWithIssue(); 278a7dea167SDimitry Andric } 279a7dea167SDimitry Andric 280a7dea167SDimitry Andric /// Specifically set the Decl where an issue occurred. This isn't necessary 281a7dea167SDimitry Andric /// for BugReports that cover a path as it will be automatically inferred. setDeclWithIssue(const Decl * declWithIssue)282a7dea167SDimitry Andric void setDeclWithIssue(const Decl *declWithIssue) { 283a7dea167SDimitry Andric DeclWithIssue = declWithIssue; 284a7dea167SDimitry Andric } 285a7dea167SDimitry Andric 286a7dea167SDimitry Andric void Profile(llvm::FoldingSetNodeID& hash) const override; 287a7dea167SDimitry Andric }; 288a7dea167SDimitry Andric 289a7dea167SDimitry Andric class PathSensitiveBugReport : public BugReport { 290a7dea167SDimitry Andric public: 291a7dea167SDimitry Andric using VisitorList = SmallVector<std::unique_ptr<BugReporterVisitor>, 8>; 292a7dea167SDimitry Andric using visitor_iterator = VisitorList::iterator; 293a7dea167SDimitry Andric using visitor_range = llvm::iterator_range<visitor_iterator>; 294a7dea167SDimitry Andric 295a7dea167SDimitry Andric protected: 296a7dea167SDimitry Andric /// The ExplodedGraph node against which the report was thrown. It corresponds 297a7dea167SDimitry Andric /// to the end of the execution path that demonstrates the bug. 298a7dea167SDimitry Andric const ExplodedNode *ErrorNode = nullptr; 299a7dea167SDimitry Andric 300a7dea167SDimitry Andric /// The range that corresponds to ErrorNode's program point. It is usually 301a7dea167SDimitry Andric /// highlighted in the report. 302a7dea167SDimitry Andric const SourceRange ErrorNodeRange; 303a7dea167SDimitry Andric 304a7dea167SDimitry Andric /// Profile to identify equivalent bug reports for error report coalescing. 3050b57cec5SDimitry Andric 3060b57cec5SDimitry Andric /// A (stack of) a set of symbols that are registered with this 3070b57cec5SDimitry Andric /// report as being "interesting", and thus used to help decide which 3080b57cec5SDimitry Andric /// diagnostics to include when constructing the final path diagnostic. 3090b57cec5SDimitry Andric /// The stack is largely used by BugReporter when generating PathDiagnostics 3100b57cec5SDimitry Andric /// for multiple PathDiagnosticConsumers. 311a7dea167SDimitry Andric llvm::DenseMap<SymbolRef, bugreporter::TrackingKind> InterestingSymbols; 3120b57cec5SDimitry Andric 3130b57cec5SDimitry Andric /// A (stack of) set of regions that are registered with this report as being 3140b57cec5SDimitry Andric /// "interesting", and thus used to help decide which diagnostics 3150b57cec5SDimitry Andric /// to include when constructing the final path diagnostic. 3160b57cec5SDimitry Andric /// The stack is largely used by BugReporter when generating PathDiagnostics 3170b57cec5SDimitry Andric /// for multiple PathDiagnosticConsumers. 318a7dea167SDimitry Andric llvm::DenseMap<const MemRegion *, bugreporter::TrackingKind> 319a7dea167SDimitry Andric InterestingRegions; 3200b57cec5SDimitry Andric 3210b57cec5SDimitry Andric /// A set of location contexts that correspoind to call sites which should be 3220b57cec5SDimitry Andric /// considered "interesting". 3230b57cec5SDimitry Andric llvm::SmallSet<const LocationContext *, 2> InterestingLocationContexts; 3240b57cec5SDimitry Andric 3250b57cec5SDimitry Andric /// A set of custom visitors which generate "event" diagnostics at 3260b57cec5SDimitry Andric /// interesting points in the path. 3270b57cec5SDimitry Andric VisitorList Callbacks; 3280b57cec5SDimitry Andric 3290b57cec5SDimitry Andric /// Used for ensuring the visitors are only added once. 3300b57cec5SDimitry Andric llvm::FoldingSet<BugReporterVisitor> CallbacksSet; 3310b57cec5SDimitry Andric 3320b57cec5SDimitry Andric /// When set, this flag disables all callstack pruning from a diagnostic 3330b57cec5SDimitry Andric /// path. This is useful for some reports that want maximum fidelty 3340b57cec5SDimitry Andric /// when reporting an issue. 3350b57cec5SDimitry Andric bool DoNotPrunePath = false; 3360b57cec5SDimitry Andric 3370b57cec5SDimitry Andric /// Used to track unique reasons why a bug report might be invalid. 3380b57cec5SDimitry Andric /// 3390b57cec5SDimitry Andric /// \sa markInvalid 3400b57cec5SDimitry Andric /// \sa removeInvalidation 3410b57cec5SDimitry Andric using InvalidationRecord = std::pair<const void *, const void *>; 3420b57cec5SDimitry Andric 3430b57cec5SDimitry Andric /// If non-empty, this bug report is likely a false positive and should not be 3440b57cec5SDimitry Andric /// shown to the user. 3450b57cec5SDimitry Andric /// 3460b57cec5SDimitry Andric /// \sa markInvalid 3470b57cec5SDimitry Andric /// \sa removeInvalidation 3480b57cec5SDimitry Andric llvm::SmallSet<InvalidationRecord, 4> Invalidations; 3490b57cec5SDimitry Andric 3500b57cec5SDimitry Andric /// Conditions we're already tracking. 3510b57cec5SDimitry Andric llvm::SmallSet<const ExplodedNode *, 4> TrackedConditions; 3520b57cec5SDimitry Andric 353a7dea167SDimitry Andric /// Reports with different uniqueing locations are considered to be different 354a7dea167SDimitry Andric /// for the purposes of deduplication. 355a7dea167SDimitry Andric PathDiagnosticLocation UniqueingLocation; 356a7dea167SDimitry Andric const Decl *UniqueingDecl; 3570b57cec5SDimitry Andric 358a7dea167SDimitry Andric const Stmt *getStmt() const; 359a7dea167SDimitry Andric 360a7dea167SDimitry Andric /// If an event occurs in a different frame than the final diagnostic, 361a7dea167SDimitry Andric /// supply a message that will be used to construct an extra hint on the 362a7dea167SDimitry Andric /// returns from all the calls on the stack from this event to the final 363a7dea167SDimitry Andric /// diagnostic. 364a7dea167SDimitry Andric // FIXME: Allow shared_ptr keys in DenseMap? 365a7dea167SDimitry Andric std::map<PathDiagnosticPieceRef, std::unique_ptr<StackHintGenerator>> 366a7dea167SDimitry Andric StackHints; 3670b57cec5SDimitry Andric 3680b57cec5SDimitry Andric public: PathSensitiveBugReport(const BugType & bt,StringRef desc,const ExplodedNode * errorNode)369a7dea167SDimitry Andric PathSensitiveBugReport(const BugType &bt, StringRef desc, 370a7dea167SDimitry Andric const ExplodedNode *errorNode) 3715ffd83dbSDimitry Andric : PathSensitiveBugReport(bt, desc, desc, errorNode) {} 3720b57cec5SDimitry Andric PathSensitiveBugReport(const BugType & bt,StringRef shortDesc,StringRef desc,const ExplodedNode * errorNode)373a7dea167SDimitry Andric PathSensitiveBugReport(const BugType &bt, StringRef shortDesc, StringRef desc, 374a7dea167SDimitry Andric const ExplodedNode *errorNode) 3755ffd83dbSDimitry Andric : PathSensitiveBugReport(bt, shortDesc, desc, errorNode, 3765ffd83dbSDimitry Andric /*LocationToUnique*/ {}, 3775ffd83dbSDimitry Andric /*DeclToUnique*/ nullptr) {} 3780b57cec5SDimitry Andric 379a7dea167SDimitry Andric /// Create a PathSensitiveBugReport with a custom uniqueing location. 3800b57cec5SDimitry Andric /// 3810b57cec5SDimitry Andric /// The reports that have the same report location, description, bug type, and 3820b57cec5SDimitry Andric /// ranges are uniqued - only one of the equivalent reports will be presented 3830b57cec5SDimitry Andric /// to the user. This method allows to rest the location which should be used 3840b57cec5SDimitry Andric /// for uniquing reports. For example, memory leaks checker, could set this to 3850b57cec5SDimitry Andric /// the allocation site, rather then the location where the bug is reported. PathSensitiveBugReport(const BugType & bt,StringRef desc,const ExplodedNode * errorNode,PathDiagnosticLocation LocationToUnique,const Decl * DeclToUnique)386480093f4SDimitry Andric PathSensitiveBugReport(const BugType &bt, StringRef desc, 387a7dea167SDimitry Andric const ExplodedNode *errorNode, 388a7dea167SDimitry Andric PathDiagnosticLocation LocationToUnique, 389a7dea167SDimitry Andric const Decl *DeclToUnique) 3905ffd83dbSDimitry Andric : PathSensitiveBugReport(bt, desc, desc, errorNode, LocationToUnique, 3915ffd83dbSDimitry Andric DeclToUnique) {} 3925ffd83dbSDimitry Andric 3935ffd83dbSDimitry Andric PathSensitiveBugReport(const BugType &bt, StringRef shortDesc, StringRef desc, 3945ffd83dbSDimitry Andric const ExplodedNode *errorNode, 3955ffd83dbSDimitry Andric PathDiagnosticLocation LocationToUnique, 3965ffd83dbSDimitry Andric const Decl *DeclToUnique); 3970b57cec5SDimitry Andric classof(const BugReport * R)398a7dea167SDimitry Andric static bool classof(const BugReport *R) { 399a7dea167SDimitry Andric return R->getKind() == Kind::PathSensitive; 400a7dea167SDimitry Andric } 4010b57cec5SDimitry Andric getErrorNode()4020b57cec5SDimitry Andric const ExplodedNode *getErrorNode() const { return ErrorNode; } 4030b57cec5SDimitry Andric 4040b57cec5SDimitry Andric /// Indicates whether or not any path pruning should take place 4050b57cec5SDimitry Andric /// when generating a PathDiagnostic from this BugReport. shouldPrunePath()4060b57cec5SDimitry Andric bool shouldPrunePath() const { return !DoNotPrunePath; } 4070b57cec5SDimitry Andric 4080b57cec5SDimitry Andric /// Disable all path pruning when generating a PathDiagnostic. disablePathPruning()4090b57cec5SDimitry Andric void disablePathPruning() { DoNotPrunePath = true; } 4100b57cec5SDimitry Andric 411a7dea167SDimitry Andric /// Get the location on which the report should be uniqued. getUniqueingLocation()412a7dea167SDimitry Andric PathDiagnosticLocation getUniqueingLocation() const override { 413a7dea167SDimitry Andric return UniqueingLocation; 414a7dea167SDimitry Andric } 415a7dea167SDimitry Andric 416a7dea167SDimitry Andric /// Get the declaration containing the uniqueing location. getUniqueingDecl()417a7dea167SDimitry Andric const Decl *getUniqueingDecl() const override { 418a7dea167SDimitry Andric return UniqueingDecl; 419a7dea167SDimitry Andric } 420a7dea167SDimitry Andric 421a7dea167SDimitry Andric const Decl *getDeclWithIssue() const override; 422a7dea167SDimitry Andric 423a7dea167SDimitry Andric ArrayRef<SourceRange> getRanges() const override; 424a7dea167SDimitry Andric 425a7dea167SDimitry Andric PathDiagnosticLocation getLocation() const override; 426a7dea167SDimitry Andric 427a7dea167SDimitry Andric /// Marks a symbol as interesting. Different kinds of interestingness will 428a7dea167SDimitry Andric /// be processed differently by visitors (e.g. if the tracking kind is 429a7dea167SDimitry Andric /// condition, will append "will be used as a condition" to the message). 430a7dea167SDimitry Andric void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind = 431a7dea167SDimitry Andric bugreporter::TrackingKind::Thorough); 432a7dea167SDimitry Andric 433fe6060f1SDimitry Andric void markNotInteresting(SymbolRef sym); 434fe6060f1SDimitry Andric 435a7dea167SDimitry Andric /// Marks a region as interesting. Different kinds of interestingness will 436a7dea167SDimitry Andric /// be processed differently by visitors (e.g. if the tracking kind is 437a7dea167SDimitry Andric /// condition, will append "will be used as a condition" to the message). 438a7dea167SDimitry Andric void markInteresting( 439a7dea167SDimitry Andric const MemRegion *R, 440a7dea167SDimitry Andric bugreporter::TrackingKind TKind = bugreporter::TrackingKind::Thorough); 441a7dea167SDimitry Andric 442fe6060f1SDimitry Andric void markNotInteresting(const MemRegion *R); 443fe6060f1SDimitry Andric 444a7dea167SDimitry Andric /// Marks a symbolic value as interesting. Different kinds of interestingness 445a7dea167SDimitry Andric /// will be processed differently by visitors (e.g. if the tracking kind is 446a7dea167SDimitry Andric /// condition, will append "will be used as a condition" to the message). 447a7dea167SDimitry Andric void markInteresting(SVal V, bugreporter::TrackingKind TKind = 448a7dea167SDimitry Andric bugreporter::TrackingKind::Thorough); 4490b57cec5SDimitry Andric void markInteresting(const LocationContext *LC); 4500b57cec5SDimitry Andric 451a7dea167SDimitry Andric bool isInteresting(SymbolRef sym) const; 452a7dea167SDimitry Andric bool isInteresting(const MemRegion *R) const; 453a7dea167SDimitry Andric bool isInteresting(SVal V) const; 454a7dea167SDimitry Andric bool isInteresting(const LocationContext *LC) const; 455a7dea167SDimitry Andric 456bdd1243dSDimitry Andric std::optional<bugreporter::TrackingKind> 457a7dea167SDimitry Andric getInterestingnessKind(SymbolRef sym) const; 458a7dea167SDimitry Andric 459bdd1243dSDimitry Andric std::optional<bugreporter::TrackingKind> 460a7dea167SDimitry Andric getInterestingnessKind(const MemRegion *R) const; 461a7dea167SDimitry Andric 462bdd1243dSDimitry Andric std::optional<bugreporter::TrackingKind> getInterestingnessKind(SVal V) const; 4630b57cec5SDimitry Andric 4640b57cec5SDimitry Andric /// Returns whether or not this report should be considered valid. 4650b57cec5SDimitry Andric /// 4660b57cec5SDimitry Andric /// Invalid reports are those that have been classified as likely false 4670b57cec5SDimitry Andric /// positives after the fact. isValid()4680b57cec5SDimitry Andric bool isValid() const { 4690b57cec5SDimitry Andric return Invalidations.empty(); 4700b57cec5SDimitry Andric } 4710b57cec5SDimitry Andric 4720b57cec5SDimitry Andric /// Marks the current report as invalid, meaning that it is probably a false 4730b57cec5SDimitry Andric /// positive and should not be reported to the user. 4740b57cec5SDimitry Andric /// 4750b57cec5SDimitry Andric /// The \p Tag and \p Data arguments are intended to be opaque identifiers for 4760b57cec5SDimitry Andric /// this particular invalidation, where \p Tag represents the visitor 4770b57cec5SDimitry Andric /// responsible for invalidation, and \p Data represents the reason this 4780b57cec5SDimitry Andric /// visitor decided to invalidate the bug report. 4790b57cec5SDimitry Andric /// 4800b57cec5SDimitry Andric /// \sa removeInvalidation markInvalid(const void * Tag,const void * Data)4810b57cec5SDimitry Andric void markInvalid(const void *Tag, const void *Data) { 4820b57cec5SDimitry Andric Invalidations.insert(std::make_pair(Tag, Data)); 4830b57cec5SDimitry Andric } 4840b57cec5SDimitry Andric 485a7dea167SDimitry Andric /// Profile to identify equivalent bug reports for error report coalescing. 486a7dea167SDimitry Andric /// Reports are uniqued to ensure that we do not emit multiple diagnostics 487a7dea167SDimitry Andric /// for each bug. 488a7dea167SDimitry Andric void Profile(llvm::FoldingSetNodeID &hash) const override; 4890b57cec5SDimitry Andric 4900b57cec5SDimitry Andric /// Add custom or predefined bug report visitors to this report. 4910b57cec5SDimitry Andric /// 4920b57cec5SDimitry Andric /// The visitors should be used when the default trace is not sufficient. 4930b57cec5SDimitry Andric /// For example, they allow constructing a more elaborate trace. 494fe6060f1SDimitry Andric /// @{ 4950b57cec5SDimitry Andric void addVisitor(std::unique_ptr<BugReporterVisitor> visitor); 4960b57cec5SDimitry Andric 497fe6060f1SDimitry Andric template <class VisitorType, class... Args> addVisitor(Args &&...ConstructorArgs)498fe6060f1SDimitry Andric void addVisitor(Args &&... ConstructorArgs) { 499fe6060f1SDimitry Andric addVisitor( 500fe6060f1SDimitry Andric std::make_unique<VisitorType>(std::forward<Args>(ConstructorArgs)...)); 501fe6060f1SDimitry Andric } 502fe6060f1SDimitry Andric /// @} 503fe6060f1SDimitry Andric 5040b57cec5SDimitry Andric /// Remove all visitors attached to this bug report. 5050b57cec5SDimitry Andric void clearVisitors(); 5060b57cec5SDimitry Andric 5070b57cec5SDimitry Andric /// Iterators through the custom diagnostic visitors. visitor_begin()5080b57cec5SDimitry Andric visitor_iterator visitor_begin() { return Callbacks.begin(); } visitor_end()5090b57cec5SDimitry Andric visitor_iterator visitor_end() { return Callbacks.end(); } visitors()510a7dea167SDimitry Andric visitor_range visitors() { return {visitor_begin(), visitor_end()}; } 5110b57cec5SDimitry Andric 5120b57cec5SDimitry Andric /// Notes that the condition of the CFGBlock associated with \p Cond is 5130b57cec5SDimitry Andric /// being tracked. 5140b57cec5SDimitry Andric /// \returns false if the condition is already being tracked. addTrackedCondition(const ExplodedNode * Cond)5150b57cec5SDimitry Andric bool addTrackedCondition(const ExplodedNode *Cond) { 5160b57cec5SDimitry Andric return TrackedConditions.insert(Cond).second; 5170b57cec5SDimitry Andric } 5180b57cec5SDimitry Andric addCallStackHint(PathDiagnosticPieceRef Piece,std::unique_ptr<StackHintGenerator> StackHint)519a7dea167SDimitry Andric void addCallStackHint(PathDiagnosticPieceRef Piece, 520a7dea167SDimitry Andric std::unique_ptr<StackHintGenerator> StackHint) { 521a7dea167SDimitry Andric StackHints[Piece] = std::move(StackHint); 522a7dea167SDimitry Andric } 523a7dea167SDimitry Andric hasCallStackHint(PathDiagnosticPieceRef Piece)524a7dea167SDimitry Andric bool hasCallStackHint(PathDiagnosticPieceRef Piece) const { 525a7dea167SDimitry Andric return StackHints.count(Piece) > 0; 526a7dea167SDimitry Andric } 527a7dea167SDimitry Andric 528a7dea167SDimitry Andric /// Produce the hint for the given node. The node contains 529a7dea167SDimitry Andric /// information about the call for which the diagnostic can be generated. 530a7dea167SDimitry Andric std::string getCallStackMessage(PathDiagnosticPieceRef Piece,const ExplodedNode * N)531a7dea167SDimitry Andric getCallStackMessage(PathDiagnosticPieceRef Piece, 532a7dea167SDimitry Andric const ExplodedNode *N) const { 533a7dea167SDimitry Andric auto I = StackHints.find(Piece); 534a7dea167SDimitry Andric if (I != StackHints.end()) 535a7dea167SDimitry Andric return I->second->getMessage(N); 536a7dea167SDimitry Andric return ""; 537a7dea167SDimitry Andric } 5380b57cec5SDimitry Andric }; 5390b57cec5SDimitry Andric 5400b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 5410b57cec5SDimitry Andric // BugTypes (collections of related reports). 5420b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 5430b57cec5SDimitry Andric 5440b57cec5SDimitry Andric class BugReportEquivClass : public llvm::FoldingSetNode { 5450b57cec5SDimitry Andric friend class BugReporter; 5460b57cec5SDimitry Andric 5470b57cec5SDimitry Andric /// List of *owned* BugReport objects. 548a7dea167SDimitry Andric llvm::SmallVector<std::unique_ptr<BugReport>, 4> Reports; 5490b57cec5SDimitry Andric AddReport(std::unique_ptr<BugReport> && R)550a7dea167SDimitry Andric void AddReport(std::unique_ptr<BugReport> &&R) { 551a7dea167SDimitry Andric Reports.push_back(std::move(R)); 5520b57cec5SDimitry Andric } 5530b57cec5SDimitry Andric 5540b57cec5SDimitry Andric public: BugReportEquivClass(std::unique_ptr<BugReport> R)5550b57cec5SDimitry Andric BugReportEquivClass(std::unique_ptr<BugReport> R) { AddReport(std::move(R)); } 556a7dea167SDimitry Andric getReports()557a7dea167SDimitry Andric ArrayRef<std::unique_ptr<BugReport>> getReports() const { return Reports; } 5580b57cec5SDimitry Andric Profile(llvm::FoldingSetNodeID & ID)5590b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID& ID) const { 5600b57cec5SDimitry Andric assert(!Reports.empty()); 561a7dea167SDimitry Andric Reports.front()->Profile(ID); 5620b57cec5SDimitry Andric } 5630b57cec5SDimitry Andric }; 5640b57cec5SDimitry Andric 5650b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 5660b57cec5SDimitry Andric // BugReporter and friends. 5670b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 5680b57cec5SDimitry Andric 5690b57cec5SDimitry Andric class BugReporterData { 5700b57cec5SDimitry Andric public: 571a7dea167SDimitry Andric virtual ~BugReporterData() = default; 5720b57cec5SDimitry Andric 5730b57cec5SDimitry Andric virtual ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() = 0; 5740b57cec5SDimitry Andric virtual ASTContext &getASTContext() = 0; 5750b57cec5SDimitry Andric virtual SourceManager &getSourceManager() = 0; 5760b57cec5SDimitry Andric virtual AnalyzerOptions &getAnalyzerOptions() = 0; 5775ffd83dbSDimitry Andric virtual Preprocessor &getPreprocessor() = 0; 5780b57cec5SDimitry Andric }; 5790b57cec5SDimitry Andric 5800b57cec5SDimitry Andric /// BugReporter is a utility class for generating PathDiagnostics for analysis. 5810b57cec5SDimitry Andric /// It collects the BugReports and BugTypes and knows how to generate 5820b57cec5SDimitry Andric /// and flush the corresponding diagnostics. 5830b57cec5SDimitry Andric /// 5840b57cec5SDimitry Andric /// The base class is used for generating path-insensitive 5850b57cec5SDimitry Andric class BugReporter { 5860b57cec5SDimitry Andric private: 5870b57cec5SDimitry Andric BugReporterData& D; 5880b57cec5SDimitry Andric 5890b57cec5SDimitry Andric /// Generate and flush the diagnostics for the given bug report. 5900b57cec5SDimitry Andric void FlushReport(BugReportEquivClass& EQ); 5910b57cec5SDimitry Andric 5920b57cec5SDimitry Andric /// The set of bug reports tracked by the BugReporter. 5930b57cec5SDimitry Andric llvm::FoldingSet<BugReportEquivClass> EQClasses; 5940b57cec5SDimitry Andric 5950b57cec5SDimitry Andric /// A vector of BugReports for tracking the allocated pointers and cleanup. 5960b57cec5SDimitry Andric std::vector<BugReportEquivClass *> EQClassesVector; 5970b57cec5SDimitry Andric 5985f757f3fSDimitry Andric /// User-provided in-code suppressions. 5995f757f3fSDimitry Andric BugSuppression UserSuppressions; 6005f757f3fSDimitry Andric 6010b57cec5SDimitry Andric public: 6025ffd83dbSDimitry Andric BugReporter(BugReporterData &d); 6030b57cec5SDimitry Andric virtual ~BugReporter(); 6040b57cec5SDimitry Andric 6050b57cec5SDimitry Andric /// Generate and flush diagnostics for all bug reports. 6060b57cec5SDimitry Andric void FlushReports(); 6070b57cec5SDimitry Andric getPathDiagnosticConsumers()6080b57cec5SDimitry Andric ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() { 6090b57cec5SDimitry Andric return D.getPathDiagnosticConsumers(); 6100b57cec5SDimitry Andric } 6110b57cec5SDimitry Andric 6120b57cec5SDimitry Andric /// Iterator over the set of BugReports tracked by the BugReporter. 6130b57cec5SDimitry Andric using EQClasses_iterator = llvm::FoldingSet<BugReportEquivClass>::iterator; equivalenceClasses()61406c3fb27SDimitry Andric llvm::iterator_range<EQClasses_iterator> equivalenceClasses() { 61506c3fb27SDimitry Andric return EQClasses; 61606c3fb27SDimitry Andric } 6170b57cec5SDimitry Andric getContext()6180b57cec5SDimitry Andric ASTContext &getContext() { return D.getASTContext(); } 6190b57cec5SDimitry Andric getSourceManager()620a7dea167SDimitry Andric const SourceManager &getSourceManager() { return D.getSourceManager(); } 6210b57cec5SDimitry Andric getAnalyzerOptions()622a7dea167SDimitry Andric const AnalyzerOptions &getAnalyzerOptions() { return D.getAnalyzerOptions(); } 6230b57cec5SDimitry Andric getPreprocessor()6245ffd83dbSDimitry Andric Preprocessor &getPreprocessor() { return D.getPreprocessor(); } 6255ffd83dbSDimitry Andric 6260b57cec5SDimitry Andric /// Add the given report to the set of reports tracked by BugReporter. 6270b57cec5SDimitry Andric /// 6280b57cec5SDimitry Andric /// The reports are usually generated by the checkers. Further, they are 6290b57cec5SDimitry Andric /// folded based on the profile value, which is done to coalesce similar 6300b57cec5SDimitry Andric /// reports. 631a7dea167SDimitry Andric virtual void emitReport(std::unique_ptr<BugReport> R); 6320b57cec5SDimitry Andric 6330b57cec5SDimitry Andric void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, 6340b57cec5SDimitry Andric StringRef BugName, StringRef BugCategory, 6350b57cec5SDimitry Andric StringRef BugStr, PathDiagnosticLocation Loc, 636bdd1243dSDimitry Andric ArrayRef<SourceRange> Ranges = std::nullopt, 637bdd1243dSDimitry Andric ArrayRef<FixItHint> Fixits = std::nullopt); 6380b57cec5SDimitry Andric 639a7dea167SDimitry Andric void EmitBasicReport(const Decl *DeclWithIssue, CheckerNameRef CheckerName, 6400b57cec5SDimitry Andric StringRef BugName, StringRef BugCategory, 6410b57cec5SDimitry Andric StringRef BugStr, PathDiagnosticLocation Loc, 642bdd1243dSDimitry Andric ArrayRef<SourceRange> Ranges = std::nullopt, 643bdd1243dSDimitry Andric ArrayRef<FixItHint> Fixits = std::nullopt); 6440b57cec5SDimitry Andric 6450b57cec5SDimitry Andric private: 6465ffd83dbSDimitry Andric llvm::StringMap<std::unique_ptr<BugType>> StrBugTypes; 6470b57cec5SDimitry Andric 6480b57cec5SDimitry Andric /// Returns a BugType that is associated with the given name and 6490b57cec5SDimitry Andric /// category. 650a7dea167SDimitry Andric BugType *getBugTypeForName(CheckerNameRef CheckerName, StringRef name, 6510b57cec5SDimitry Andric StringRef category); 652a7dea167SDimitry Andric 653a7dea167SDimitry Andric virtual BugReport * findReportInEquivalenceClass(BugReportEquivClass & eqClass,SmallVectorImpl<BugReport * > & bugReports)654a7dea167SDimitry Andric findReportInEquivalenceClass(BugReportEquivClass &eqClass, 655a7dea167SDimitry Andric SmallVectorImpl<BugReport *> &bugReports) { 656a7dea167SDimitry Andric return eqClass.getReports()[0].get(); 657a7dea167SDimitry Andric } 658a7dea167SDimitry Andric 659a7dea167SDimitry Andric protected: 660a7dea167SDimitry Andric /// Generate the diagnostics for the given bug report. 661a7dea167SDimitry Andric virtual std::unique_ptr<DiagnosticForConsumerMapTy> 662a7dea167SDimitry Andric generateDiagnosticForConsumerMap(BugReport *exampleReport, 663a7dea167SDimitry Andric ArrayRef<PathDiagnosticConsumer *> consumers, 664a7dea167SDimitry Andric ArrayRef<BugReport *> bugReports); 6650b57cec5SDimitry Andric }; 6660b57cec5SDimitry Andric 6670b57cec5SDimitry Andric /// GRBugReporter is used for generating path-sensitive reports. 668a7dea167SDimitry Andric class PathSensitiveBugReporter final : public BugReporter { 6690b57cec5SDimitry Andric ExprEngine& Eng; 6700b57cec5SDimitry Andric 671a7dea167SDimitry Andric BugReport *findReportInEquivalenceClass( 672a7dea167SDimitry Andric BugReportEquivClass &eqClass, 673a7dea167SDimitry Andric SmallVectorImpl<BugReport *> &bugReports) override; 6740b57cec5SDimitry Andric 675a7dea167SDimitry Andric /// Generate the diagnostics for the given bug report. 676a7dea167SDimitry Andric std::unique_ptr<DiagnosticForConsumerMapTy> 677a7dea167SDimitry Andric generateDiagnosticForConsumerMap(BugReport *exampleReport, 678a7dea167SDimitry Andric ArrayRef<PathDiagnosticConsumer *> consumers, 679a7dea167SDimitry Andric ArrayRef<BugReport *> bugReports) override; 680a7dea167SDimitry Andric public: PathSensitiveBugReporter(BugReporterData & d,ExprEngine & eng)681a7dea167SDimitry Andric PathSensitiveBugReporter(BugReporterData& d, ExprEngine& eng) 682a7dea167SDimitry Andric : BugReporter(d), Eng(eng) {} 6830b57cec5SDimitry Andric 6840b57cec5SDimitry Andric /// getGraph - Get the exploded graph created by the analysis engine 6850b57cec5SDimitry Andric /// for the analyzed method or function. 686a7dea167SDimitry Andric const ExplodedGraph &getGraph() const; 6870b57cec5SDimitry Andric 6880b57cec5SDimitry Andric /// getStateManager - Return the state manager used by the analysis 6890b57cec5SDimitry Andric /// engine. 690a7dea167SDimitry Andric ProgramStateManager &getStateManager() const; 6910b57cec5SDimitry Andric 6920b57cec5SDimitry Andric /// \p bugReports A set of bug reports within a *single* equivalence class 6930b57cec5SDimitry Andric /// 6940b57cec5SDimitry Andric /// \return A mapping from consumers to the corresponding diagnostics. 6950b57cec5SDimitry Andric /// Iterates through the bug reports within a single equivalence class, 6960b57cec5SDimitry Andric /// stops at a first non-invalidated report. 697a7dea167SDimitry Andric std::unique_ptr<DiagnosticForConsumerMapTy> generatePathDiagnostics( 698a7dea167SDimitry Andric ArrayRef<PathDiagnosticConsumer *> consumers, 699a7dea167SDimitry Andric ArrayRef<PathSensitiveBugReport *> &bugReports); 7000b57cec5SDimitry Andric 701a7dea167SDimitry Andric void emitReport(std::unique_ptr<BugReport> R) override; 7020b57cec5SDimitry Andric }; 7030b57cec5SDimitry Andric 7040b57cec5SDimitry Andric 7050b57cec5SDimitry Andric class BugReporterContext { 706a7dea167SDimitry Andric PathSensitiveBugReporter &BR; 7070b57cec5SDimitry Andric 7080b57cec5SDimitry Andric virtual void anchor(); 7090b57cec5SDimitry Andric 7100b57cec5SDimitry Andric public: BugReporterContext(PathSensitiveBugReporter & br)711a7dea167SDimitry Andric BugReporterContext(PathSensitiveBugReporter &br) : BR(br) {} 7120b57cec5SDimitry Andric 7130b57cec5SDimitry Andric virtual ~BugReporterContext() = default; 7140b57cec5SDimitry Andric getBugReporter()715a7dea167SDimitry Andric PathSensitiveBugReporter& getBugReporter() { return BR; } 7160b57cec5SDimitry Andric getStateManager()717a7dea167SDimitry Andric ProgramStateManager& getStateManager() const { 7180b57cec5SDimitry Andric return BR.getStateManager(); 7190b57cec5SDimitry Andric } 7200b57cec5SDimitry Andric getASTContext()721a7dea167SDimitry Andric ASTContext &getASTContext() const { 7220b57cec5SDimitry Andric return BR.getContext(); 7230b57cec5SDimitry Andric } 7240b57cec5SDimitry Andric getSourceManager()725a7dea167SDimitry Andric const SourceManager& getSourceManager() const { 7260b57cec5SDimitry Andric return BR.getSourceManager(); 7270b57cec5SDimitry Andric } 7280b57cec5SDimitry Andric getAnalyzerOptions()729a7dea167SDimitry Andric const AnalyzerOptions &getAnalyzerOptions() const { 7300b57cec5SDimitry Andric return BR.getAnalyzerOptions(); 7310b57cec5SDimitry Andric } 7320b57cec5SDimitry Andric }; 7330b57cec5SDimitry Andric 734fe6060f1SDimitry Andric /// The tag that carries some information with it. 735fe6060f1SDimitry Andric /// 736fe6060f1SDimitry Andric /// It can be valuable to produce tags with some bits of information and later 737fe6060f1SDimitry Andric /// reuse them for a better diagnostic. 738fe6060f1SDimitry Andric /// 739fe6060f1SDimitry Andric /// Please make sure that derived class' constuctor is private and that the user 740fe6060f1SDimitry Andric /// can only create objects using DataTag::Factory. This also means that 741fe6060f1SDimitry Andric /// DataTag::Factory should be friend for every derived class. 742fe6060f1SDimitry Andric class DataTag : public ProgramPointTag { 743fe6060f1SDimitry Andric public: getTagDescription()744fe6060f1SDimitry Andric StringRef getTagDescription() const override { return "Data Tag"; } 745fe6060f1SDimitry Andric 746fe6060f1SDimitry Andric // Manage memory for DataTag objects. 747fe6060f1SDimitry Andric class Factory { 748fe6060f1SDimitry Andric std::vector<std::unique_ptr<DataTag>> Tags; 749fe6060f1SDimitry Andric 750fe6060f1SDimitry Andric public: 751fe6060f1SDimitry Andric template <class DataTagType, class... Args> make(Args &&...ConstructorArgs)752fe6060f1SDimitry Andric const DataTagType *make(Args &&... ConstructorArgs) { 753fe6060f1SDimitry Andric // We cannot use std::make_unique because we cannot access the private 754fe6060f1SDimitry Andric // constructor from inside it. 755fe6060f1SDimitry Andric Tags.emplace_back( 756fe6060f1SDimitry Andric new DataTagType(std::forward<Args>(ConstructorArgs)...)); 757fe6060f1SDimitry Andric return static_cast<DataTagType *>(Tags.back().get()); 758fe6060f1SDimitry Andric } 759fe6060f1SDimitry Andric }; 760fe6060f1SDimitry Andric 761fe6060f1SDimitry Andric protected: DataTag(void * TagKind)762fe6060f1SDimitry Andric DataTag(void *TagKind) : ProgramPointTag(TagKind) {} 763fe6060f1SDimitry Andric }; 7640b57cec5SDimitry Andric 7650b57cec5SDimitry Andric /// The tag upon which the TagVisitor reacts. Add these in order to display 7660b57cec5SDimitry Andric /// additional PathDiagnosticEventPieces along the path. 767fe6060f1SDimitry Andric class NoteTag : public DataTag { 7680b57cec5SDimitry Andric public: 769fe6060f1SDimitry Andric using Callback = std::function<std::string(BugReporterContext &, 7705ffd83dbSDimitry Andric PathSensitiveBugReport &)>; 7710b57cec5SDimitry Andric 7720b57cec5SDimitry Andric private: 7730b57cec5SDimitry Andric static int Kind; 7740b57cec5SDimitry Andric 7750b57cec5SDimitry Andric const Callback Cb; 7760b57cec5SDimitry Andric const bool IsPrunable; 7770b57cec5SDimitry Andric NoteTag(Callback && Cb,bool IsPrunable)7780b57cec5SDimitry Andric NoteTag(Callback &&Cb, bool IsPrunable) 779fe6060f1SDimitry Andric : DataTag(&Kind), Cb(std::move(Cb)), IsPrunable(IsPrunable) {} 7800b57cec5SDimitry Andric 7810b57cec5SDimitry Andric public: classof(const ProgramPointTag * T)7820b57cec5SDimitry Andric static bool classof(const ProgramPointTag *T) { 7830b57cec5SDimitry Andric return T->getTagKind() == &Kind; 7840b57cec5SDimitry Andric } 7850b57cec5SDimitry Andric generateMessage(BugReporterContext & BRC,PathSensitiveBugReport & R)786bdd1243dSDimitry Andric std::optional<std::string> generateMessage(BugReporterContext &BRC, 7875ffd83dbSDimitry Andric PathSensitiveBugReport &R) const { 7880b57cec5SDimitry Andric std::string Msg = Cb(BRC, R); 7890b57cec5SDimitry Andric if (Msg.empty()) 790bdd1243dSDimitry Andric return std::nullopt; 7910b57cec5SDimitry Andric 7920b57cec5SDimitry Andric return std::move(Msg); 7930b57cec5SDimitry Andric } 7940b57cec5SDimitry Andric getTagDescription()7950b57cec5SDimitry Andric StringRef getTagDescription() const override { 7960b57cec5SDimitry Andric // TODO: Remember a few examples of generated messages 7970b57cec5SDimitry Andric // and display them in the ExplodedGraph dump by 7980b57cec5SDimitry Andric // returning them from this function. 7990b57cec5SDimitry Andric return "Note Tag"; 8000b57cec5SDimitry Andric } 8010b57cec5SDimitry Andric isPrunable()8020b57cec5SDimitry Andric bool isPrunable() const { return IsPrunable; } 8030b57cec5SDimitry Andric 804fe6060f1SDimitry Andric friend class Factory; 8050b57cec5SDimitry Andric friend class TagVisitor; 8060b57cec5SDimitry Andric }; 8070b57cec5SDimitry Andric 8080b57cec5SDimitry Andric } // namespace ento 8090b57cec5SDimitry Andric 8100b57cec5SDimitry Andric } // namespace clang 8110b57cec5SDimitry Andric 8120b57cec5SDimitry Andric #endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H 813