1 //===- BugReporter.h - Generate PathDiagnostics -----------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines BugReporter, a utility class for generating
10 // PathDiagnostics for analyses based on ProgramState.
12 //===----------------------------------------------------------------------===//
14 #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H
15 #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H
17 #include "clang/Basic/LLVM.h"
18 #include "clang/Basic/SourceLocation.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
20 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
21 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
26 #include "llvm/ADT/ArrayRef.h"
27 #include "llvm/ADT/DenseSet.h"
28 #include "llvm/ADT/FoldingSet.h"
29 #include "llvm/ADT/ImmutableSet.h"
30 #include "llvm/ADT/None.h"
31 #include "llvm/ADT/SmallSet.h"
32 #include "llvm/ADT/SmallVector.h"
33 #include "llvm/ADT/StringMap.h"
34 #include "llvm/ADT/StringRef.h"
35 #include "llvm/ADT/ilist.h"
36 #include "llvm/ADT/ilist_node.h"
37 #include "llvm/ADT/iterator_range.h"
46 class AnalyzerOptions;
49 class DiagnosticsEngine;
50 class LocationContext;
64 //===----------------------------------------------------------------------===//
65 // Interface for individual bug reports.
66 //===----------------------------------------------------------------------===//
68 /// A mapping from diagnostic consumers to the diagnostics they should
70 using DiagnosticForConsumerMapTy =
71 llvm::DenseMap<PathDiagnosticConsumer *, std::unique_ptr<PathDiagnostic>>;
73 /// This class provides an interface through which checkers can create
74 /// individual bug reports.
75 class BugReport : public llvm::ilist_node<BugReport> {
78 virtual void anchor();
81 virtual ~NodeResolver() = default;
83 virtual const ExplodedNode*
84 getOriginalNode(const ExplodedNode *N) = 0;
87 using ranges_iterator = const SourceRange *;
88 using VisitorList = SmallVector<std::unique_ptr<BugReporterVisitor>, 8>;
89 using visitor_iterator = VisitorList::iterator;
90 using ExtraTextList = SmallVector<StringRef, 2>;
91 using NoteList = SmallVector<std::shared_ptr<PathDiagnosticNotePiece>, 4>;
94 friend class BugReportEquivClass;
95 friend class BugReporter;
98 const Decl *DeclWithIssue = nullptr;
99 std::string ShortDescription;
100 std::string Description;
101 PathDiagnosticLocation Location;
102 PathDiagnosticLocation UniqueingLocation;
103 const Decl *UniqueingDecl;
105 const ExplodedNode *ErrorNode = nullptr;
106 SmallVector<SourceRange, 4> Ranges;
107 ExtraTextList ExtraText;
110 using Symbols = llvm::DenseSet<SymbolRef>;
111 using Regions = llvm::DenseSet<const MemRegion *>;
113 /// A (stack of) a set of symbols that are registered with this
114 /// report as being "interesting", and thus used to help decide which
115 /// diagnostics to include when constructing the final path diagnostic.
116 /// The stack is largely used by BugReporter when generating PathDiagnostics
117 /// for multiple PathDiagnosticConsumers.
118 SmallVector<Symbols *, 2> interestingSymbols;
120 /// A (stack of) set of regions that are registered with this report as being
121 /// "interesting", and thus used to help decide which diagnostics
122 /// to include when constructing the final path diagnostic.
123 /// The stack is largely used by BugReporter when generating PathDiagnostics
124 /// for multiple PathDiagnosticConsumers.
125 SmallVector<Regions *, 2> interestingRegions;
127 /// A set of location contexts that correspoind to call sites which should be
128 /// considered "interesting".
129 llvm::SmallSet<const LocationContext *, 2> InterestingLocationContexts;
131 /// A set of custom visitors which generate "event" diagnostics at
132 /// interesting points in the path.
133 VisitorList Callbacks;
135 /// Used for ensuring the visitors are only added once.
136 llvm::FoldingSet<BugReporterVisitor> CallbacksSet;
138 /// When set, this flag disables all callstack pruning from a diagnostic
139 /// path. This is useful for some reports that want maximum fidelty
140 /// when reporting an issue.
141 bool DoNotPrunePath = false;
143 /// Used to track unique reasons why a bug report might be invalid.
146 /// \sa removeInvalidation
147 using InvalidationRecord = std::pair<const void *, const void *>;
149 /// If non-empty, this bug report is likely a false positive and should not be
150 /// shown to the user.
153 /// \sa removeInvalidation
154 llvm::SmallSet<InvalidationRecord, 4> Invalidations;
156 /// Conditions we're already tracking.
157 llvm::SmallSet<const ExplodedNode *, 4> TrackedConditions;
160 // Used internally by BugReporter.
161 Symbols &getInterestingSymbols();
162 Regions &getInterestingRegions();
164 void lazyInitializeInterestingSets();
165 void pushInterestingSymbolsAndRegions();
166 void popInterestingSymbolsAndRegions();
169 BugReport(const BugType& bt, StringRef desc, const ExplodedNode *errornode)
170 : BT(bt), Description(desc), ErrorNode(errornode) {}
172 BugReport(const BugType& bt, StringRef shortDesc, StringRef desc,
173 const ExplodedNode *errornode)
174 : BT(bt), ShortDescription(shortDesc), Description(desc),
175 ErrorNode(errornode) {}
177 BugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l)
178 : BT(bt), Description(desc), Location(l) {}
180 /// Create a BugReport with a custom uniqueing location.
182 /// The reports that have the same report location, description, bug type, and
183 /// ranges are uniqued - only one of the equivalent reports will be presented
184 /// to the user. This method allows to rest the location which should be used
185 /// for uniquing reports. For example, memory leaks checker, could set this to
186 /// the allocation site, rather then the location where the bug is reported.
187 BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode,
188 PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique)
189 : BT(bt), Description(desc), UniqueingLocation(LocationToUnique),
190 UniqueingDecl(DeclToUnique), ErrorNode(errornode) {}
192 virtual ~BugReport();
194 const BugType& getBugType() const { return BT; }
195 //BugType& getBugType() { return BT; }
197 /// True when the report has an execution path associated with it.
199 /// A report is said to be path-sensitive if it was thrown against a
200 /// particular exploded node in the path-sensitive analysis graph.
201 /// Path-sensitive reports have their intermediate path diagnostics
202 /// auto-generated, perhaps with the help of checker-defined visitors,
203 /// and may contain extra notes.
204 /// Path-insensitive reports consist only of a single warning message
205 /// in a specific location, and perhaps extra notes.
206 /// Path-sensitive checkers are allowed to throw path-insensitive reports.
207 bool isPathSensitive() const { return ErrorNode != nullptr; }
209 const ExplodedNode *getErrorNode() const { return ErrorNode; }
211 StringRef getDescription() const { return Description; }
213 StringRef getShortDescription(bool UseFallback = true) const {
214 if (ShortDescription.empty() && UseFallback)
216 return ShortDescription;
219 /// Indicates whether or not any path pruning should take place
220 /// when generating a PathDiagnostic from this BugReport.
221 bool shouldPrunePath() const { return !DoNotPrunePath; }
223 /// Disable all path pruning when generating a PathDiagnostic.
224 void disablePathPruning() { DoNotPrunePath = true; }
226 void markInteresting(SymbolRef sym);
227 void markInteresting(const MemRegion *R);
228 void markInteresting(SVal V);
229 void markInteresting(const LocationContext *LC);
231 bool isInteresting(SymbolRef sym);
232 bool isInteresting(const MemRegion *R);
233 bool isInteresting(SVal V);
234 bool isInteresting(const LocationContext *LC);
236 /// Returns whether or not this report should be considered valid.
238 /// Invalid reports are those that have been classified as likely false
239 /// positives after the fact.
240 bool isValid() const {
241 return Invalidations.empty();
244 /// Marks the current report as invalid, meaning that it is probably a false
245 /// positive and should not be reported to the user.
247 /// The \p Tag and \p Data arguments are intended to be opaque identifiers for
248 /// this particular invalidation, where \p Tag represents the visitor
249 /// responsible for invalidation, and \p Data represents the reason this
250 /// visitor decided to invalidate the bug report.
252 /// \sa removeInvalidation
253 void markInvalid(const void *Tag, const void *Data) {
254 Invalidations.insert(std::make_pair(Tag, Data));
257 /// Return the canonical declaration, be it a method or class, where
258 /// this issue semantically occurred.
259 const Decl *getDeclWithIssue() const;
261 /// Specifically set the Decl where an issue occurred. This isn't necessary
262 /// for BugReports that cover a path as it will be automatically inferred.
263 void setDeclWithIssue(const Decl *declWithIssue) {
264 DeclWithIssue = declWithIssue;
267 /// Add new item to the list of additional notes that need to be attached to
268 /// this path-insensitive report. If you want to add extra notes to a
269 /// path-sensitive report, you need to use a BugReporterVisitor because it
270 /// allows you to specify where exactly in the auto-generated path diagnostic
271 /// the extra note should appear.
272 void addNote(StringRef Msg, const PathDiagnosticLocation &Pos,
273 ArrayRef<SourceRange> Ranges) {
274 auto P = std::make_shared<PathDiagnosticNotePiece>(Pos, Msg);
276 for (const auto &R : Ranges)
279 Notes.push_back(std::move(P));
282 // FIXME: Instead of making an override, we could have default-initialized
283 // Ranges with {}, however it crashes the MSVC 2013 compiler.
284 void addNote(StringRef Msg, const PathDiagnosticLocation &Pos) {
285 std::vector<SourceRange> Ranges;
286 addNote(Msg, Pos, Ranges);
289 virtual const NoteList &getNotes() {
293 /// This allows for addition of meta data to the diagnostic.
295 /// Currently, only the HTMLDiagnosticClient knows how to display it.
296 void addExtraText(StringRef S) {
297 ExtraText.push_back(S);
300 virtual const ExtraTextList &getExtraText() {
304 /// Return the "definitive" location of the reported bug.
306 /// While a bug can span an entire path, usually there is a specific
307 /// location that can be used to identify where the key issue occurred.
308 /// This location is used by clients rendering diagnostics.
309 virtual PathDiagnosticLocation getLocation(const SourceManager &SM) const;
311 /// Get the location on which the report should be uniqued.
312 PathDiagnosticLocation getUniqueingLocation() const {
313 return UniqueingLocation;
316 /// Get the declaration containing the uniqueing location.
317 const Decl *getUniqueingDecl() const {
318 return UniqueingDecl;
321 const Stmt *getStmt() const;
323 /// Add a range to a bug report.
325 /// Ranges are used to highlight regions of interest in the source code.
326 /// They should be at the same source code line as the BugReport location.
327 /// By default, the source range of the statement corresponding to the error
328 /// node will be used; add a single invalid range to specify absence of
330 void addRange(SourceRange R) {
331 assert((R.isValid() || Ranges.empty()) && "Invalid range can only be used "
332 "to specify that the report does not have a range.");
336 /// Get the SourceRanges associated with the report.
337 virtual llvm::iterator_range<ranges_iterator> getRanges();
339 /// Add custom or predefined bug report visitors to this report.
341 /// The visitors should be used when the default trace is not sufficient.
342 /// For example, they allow constructing a more elaborate trace.
343 /// \sa registerConditionVisitor(), registerTrackNullOrUndefValue(),
344 /// registerFindLastStore(), registerNilReceiverVisitor(), and
345 /// registerVarDeclsLastStore().
346 void addVisitor(std::unique_ptr<BugReporterVisitor> visitor);
348 /// Remove all visitors attached to this bug report.
349 void clearVisitors();
351 /// Iterators through the custom diagnostic visitors.
352 visitor_iterator visitor_begin() { return Callbacks.begin(); }
353 visitor_iterator visitor_end() { return Callbacks.end(); }
355 /// Notes that the condition of the CFGBlock associated with \p Cond is
357 /// \returns false if the condition is already being tracked.
358 bool addTrackedCondition(const ExplodedNode *Cond) {
359 return TrackedConditions.insert(Cond).second;
362 /// Profile to identify equivalent bug reports for error report coalescing.
363 /// Reports are uniqued to ensure that we do not emit multiple diagnostics
365 virtual void Profile(llvm::FoldingSetNodeID& hash) const;
368 //===----------------------------------------------------------------------===//
369 // BugTypes (collections of related reports).
370 //===----------------------------------------------------------------------===//
372 class BugReportEquivClass : public llvm::FoldingSetNode {
373 friend class BugReporter;
375 /// List of *owned* BugReport objects.
376 llvm::ilist<BugReport> Reports;
378 void AddReport(std::unique_ptr<BugReport> R) {
379 Reports.push_back(R.release());
383 BugReportEquivClass(std::unique_ptr<BugReport> R) { AddReport(std::move(R)); }
384 ~BugReportEquivClass();
386 void Profile(llvm::FoldingSetNodeID& ID) const {
387 assert(!Reports.empty());
388 Reports.front().Profile(ID);
391 using iterator = llvm::ilist<BugReport>::iterator;
392 using const_iterator = llvm::ilist<BugReport>::const_iterator;
394 iterator begin() { return Reports.begin(); }
395 iterator end() { return Reports.end(); }
397 const_iterator begin() const { return Reports.begin(); }
398 const_iterator end() const { return Reports.end(); }
401 //===----------------------------------------------------------------------===//
402 // BugReporter and friends.
403 //===----------------------------------------------------------------------===//
405 class BugReporterData {
407 virtual ~BugReporterData();
409 virtual DiagnosticsEngine& getDiagnostic() = 0;
410 virtual ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() = 0;
411 virtual ASTContext &getASTContext() = 0;
412 virtual SourceManager &getSourceManager() = 0;
413 virtual AnalyzerOptions &getAnalyzerOptions() = 0;
416 /// BugReporter is a utility class for generating PathDiagnostics for analysis.
417 /// It collects the BugReports and BugTypes and knows how to generate
418 /// and flush the corresponding diagnostics.
420 /// The base class is used for generating path-insensitive
423 enum Kind { BaseBRKind, GRBugReporterKind };
426 using BugTypesTy = llvm::ImmutableSet<BugType *>;
428 BugTypesTy::Factory F;
434 /// Generate and flush the diagnostics for the given bug report.
435 void FlushReport(BugReportEquivClass& EQ);
437 /// Generate the diagnostics for the given bug report.
438 std::unique_ptr<DiagnosticForConsumerMapTy>
439 generateDiagnosticForConsumerMap(BugReport *exampleReport,
440 ArrayRef<PathDiagnosticConsumer *> consumers,
441 ArrayRef<BugReport *> bugReports);
443 /// The set of bug reports tracked by the BugReporter.
444 llvm::FoldingSet<BugReportEquivClass> EQClasses;
446 /// A vector of BugReports for tracking the allocated pointers and cleanup.
447 std::vector<BugReportEquivClass *> EQClassesVector;
450 BugReporter(BugReporterData& d, Kind k)
451 : BugTypes(F.getEmptySet()), kind(k), D(d) {}
454 BugReporter(BugReporterData& d)
455 : BugTypes(F.getEmptySet()), kind(BaseBRKind), D(d) {}
456 virtual ~BugReporter();
458 /// Generate and flush diagnostics for all bug reports.
461 Kind getKind() const { return kind; }
463 DiagnosticsEngine& getDiagnostic() {
464 return D.getDiagnostic();
467 ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() {
468 return D.getPathDiagnosticConsumers();
471 /// Iterator over the set of BugTypes tracked by the BugReporter.
472 using iterator = BugTypesTy::iterator;
473 iterator begin() { return BugTypes.begin(); }
474 iterator end() { return BugTypes.end(); }
476 /// Iterator over the set of BugReports tracked by the BugReporter.
477 using EQClasses_iterator = llvm::FoldingSet<BugReportEquivClass>::iterator;
478 EQClasses_iterator EQClasses_begin() { return EQClasses.begin(); }
479 EQClasses_iterator EQClasses_end() { return EQClasses.end(); }
481 ASTContext &getContext() { return D.getASTContext(); }
483 SourceManager &getSourceManager() { return D.getSourceManager(); }
485 AnalyzerOptions &getAnalyzerOptions() { return D.getAnalyzerOptions(); }
487 virtual std::unique_ptr<DiagnosticForConsumerMapTy>
488 generatePathDiagnostics(ArrayRef<PathDiagnosticConsumer *> consumers,
489 ArrayRef<BugReport *> &bugReports) {
493 void Register(const BugType *BT);
495 /// Add the given report to the set of reports tracked by BugReporter.
497 /// The reports are usually generated by the checkers. Further, they are
498 /// folded based on the profile value, which is done to coalesce similar
500 void emitReport(std::unique_ptr<BugReport> R);
502 void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker,
503 StringRef BugName, StringRef BugCategory,
504 StringRef BugStr, PathDiagnosticLocation Loc,
505 ArrayRef<SourceRange> Ranges = None);
507 void EmitBasicReport(const Decl *DeclWithIssue, CheckName CheckName,
508 StringRef BugName, StringRef BugCategory,
509 StringRef BugStr, PathDiagnosticLocation Loc,
510 ArrayRef<SourceRange> Ranges = None);
513 llvm::StringMap<BugType *> StrBugTypes;
515 /// Returns a BugType that is associated with the given name and
517 BugType *getBugTypeForName(CheckName CheckName, StringRef name,
521 /// GRBugReporter is used for generating path-sensitive reports.
522 class GRBugReporter : public BugReporter {
526 GRBugReporter(BugReporterData& d, ExprEngine& eng)
527 : BugReporter(d, GRBugReporterKind), Eng(eng) {}
529 ~GRBugReporter() override;
531 /// getGraph - Get the exploded graph created by the analysis engine
532 /// for the analyzed method or function.
533 ExplodedGraph &getGraph();
535 /// getStateManager - Return the state manager used by the analysis
537 ProgramStateManager &getStateManager();
539 /// \p bugReports A set of bug reports within a *single* equivalence class
541 /// \return A mapping from consumers to the corresponding diagnostics.
542 /// Iterates through the bug reports within a single equivalence class,
543 /// stops at a first non-invalidated report.
544 std::unique_ptr<DiagnosticForConsumerMapTy>
545 generatePathDiagnostics(ArrayRef<PathDiagnosticConsumer *> consumers,
546 ArrayRef<BugReport *> &bugReports) override;
548 /// classof - Used by isa<>, cast<>, and dyn_cast<>.
549 static bool classof(const BugReporter* R) {
550 return R->getKind() == GRBugReporterKind;
555 class NodeMapClosure : public BugReport::NodeResolver {
556 InterExplodedGraphMap &M;
559 NodeMapClosure(InterExplodedGraphMap &m) : M(m) {}
561 const ExplodedNode *getOriginalNode(const ExplodedNode *N) override {
566 class BugReporterContext {
570 virtual void anchor();
573 BugReporterContext(GRBugReporter &br, InterExplodedGraphMap &Backmap)
574 : BR(br), NMC(Backmap) {}
576 virtual ~BugReporterContext() = default;
578 GRBugReporter& getBugReporter() { return BR; }
580 ExplodedGraph &getGraph() { return BR.getGraph(); }
582 ProgramStateManager& getStateManager() {
583 return BR.getStateManager();
586 SValBuilder &getSValBuilder() {
587 return getStateManager().getSValBuilder();
590 ASTContext &getASTContext() {
591 return BR.getContext();
594 SourceManager& getSourceManager() {
595 return BR.getSourceManager();
598 AnalyzerOptions &getAnalyzerOptions() {
599 return BR.getAnalyzerOptions();
602 NodeMapClosure& getNodeResolver() { return NMC; }
606 /// The tag upon which the TagVisitor reacts. Add these in order to display
607 /// additional PathDiagnosticEventPieces along the path.
608 class NoteTag : public ProgramPointTag {
611 std::function<std::string(BugReporterContext &, BugReport &)>;
617 const bool IsPrunable;
619 NoteTag(Callback &&Cb, bool IsPrunable)
620 : ProgramPointTag(&Kind), Cb(std::move(Cb)), IsPrunable(IsPrunable) {}
623 static bool classof(const ProgramPointTag *T) {
624 return T->getTagKind() == &Kind;
627 Optional<std::string> generateMessage(BugReporterContext &BRC,
628 BugReport &R) const {
629 std::string Msg = Cb(BRC, R);
633 return std::move(Msg);
636 StringRef getTagDescription() const override {
637 // TODO: Remember a few examples of generated messages
638 // and display them in the ExplodedGraph dump by
639 // returning them from this function.
643 bool isPrunable() const { return IsPrunable; }
645 // Manage memory for NoteTag objects.
647 std::vector<std::unique_ptr<NoteTag>> Tags;
650 const NoteTag *makeNoteTag(Callback &&Cb, bool IsPrunable = false) {
651 // We cannot use make_unique because we cannot access the private
652 // constructor from inside it.
653 std::unique_ptr<NoteTag> T(new NoteTag(std::move(Cb), IsPrunable));
654 Tags.push_back(std::move(T));
655 return Tags.back().get();
659 friend class TagVisitor;
666 #endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H