Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 1 | //===- BugReporter.h - Generate PathDiagnostics -----------------*- C++ -*-===// |
2 | // |
||
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
||
4 | // See https://llvm.org/LICENSE.txt for license information. |
||
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
||
6 | // |
||
7 | //===----------------------------------------------------------------------===// |
||
8 | // |
||
9 | // This file defines BugReporter, a utility class for generating |
||
10 | // PathDiagnostics for analyses based on ProgramState. |
||
11 | // |
||
12 | //===----------------------------------------------------------------------===// |
||
13 | |||
14 | #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H |
||
15 | #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H |
||
16 | |||
17 | #include "clang/Analysis/PathDiagnostic.h" |
||
18 | #include "clang/Basic/LLVM.h" |
||
19 | #include "clang/Basic/SourceLocation.h" |
||
20 | #include "clang/Lex/Preprocessor.h" |
||
21 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" |
||
22 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
||
23 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
||
24 | #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" |
||
25 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" |
||
26 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
||
27 | #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" |
||
28 | #include "llvm/ADT/ArrayRef.h" |
||
29 | #include "llvm/ADT/DenseSet.h" |
||
30 | #include "llvm/ADT/FoldingSet.h" |
||
31 | #include "llvm/ADT/ImmutableSet.h" |
||
32 | #include "llvm/ADT/SmallSet.h" |
||
33 | #include "llvm/ADT/SmallVector.h" |
||
34 | #include "llvm/ADT/StringMap.h" |
||
35 | #include "llvm/ADT/StringRef.h" |
||
36 | #include "llvm/ADT/ilist.h" |
||
37 | #include "llvm/ADT/ilist_node.h" |
||
38 | #include "llvm/ADT/iterator_range.h" |
||
39 | #include <cassert> |
||
40 | #include <memory> |
||
41 | #include <optional> |
||
42 | #include <string> |
||
43 | #include <utility> |
||
44 | #include <vector> |
||
45 | |||
46 | namespace clang { |
||
47 | |||
48 | class AnalyzerOptions; |
||
49 | class ASTContext; |
||
50 | class Decl; |
||
51 | class LocationContext; |
||
52 | class SourceManager; |
||
53 | class Stmt; |
||
54 | |||
55 | namespace ento { |
||
56 | |||
57 | class BugType; |
||
58 | class CheckerBase; |
||
59 | class ExplodedGraph; |
||
60 | class ExplodedNode; |
||
61 | class ExprEngine; |
||
62 | class MemRegion; |
||
63 | |||
64 | //===----------------------------------------------------------------------===// |
||
65 | // Interface for individual bug reports. |
||
66 | //===----------------------------------------------------------------------===// |
||
67 | |||
68 | /// A mapping from diagnostic consumers to the diagnostics they should |
||
69 | /// consume. |
||
70 | using DiagnosticForConsumerMapTy = |
||
71 | llvm::DenseMap<PathDiagnosticConsumer *, std::unique_ptr<PathDiagnostic>>; |
||
72 | |||
73 | /// Interface for classes constructing Stack hints. |
||
74 | /// |
||
75 | /// If a PathDiagnosticEvent occurs in a different frame than the final |
||
76 | /// diagnostic the hints can be used to summarize the effect of the call. |
||
77 | class StackHintGenerator { |
||
78 | public: |
||
79 | virtual ~StackHintGenerator() = 0; |
||
80 | |||
81 | /// Construct the Diagnostic message for the given ExplodedNode. |
||
82 | virtual std::string getMessage(const ExplodedNode *N) = 0; |
||
83 | }; |
||
84 | |||
85 | /// Constructs a Stack hint for the given symbol. |
||
86 | /// |
||
87 | /// The class knows how to construct the stack hint message based on |
||
88 | /// traversing the CallExpr associated with the call and checking if the given |
||
89 | /// symbol is returned or is one of the arguments. |
||
90 | /// The hint can be customized by redefining 'getMessageForX()' methods. |
||
91 | class StackHintGeneratorForSymbol : public StackHintGenerator { |
||
92 | private: |
||
93 | SymbolRef Sym; |
||
94 | std::string Msg; |
||
95 | |||
96 | public: |
||
97 | StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {} |
||
98 | ~StackHintGeneratorForSymbol() override = default; |
||
99 | |||
100 | /// Search the call expression for the symbol Sym and dispatch the |
||
101 | /// 'getMessageForX()' methods to construct a specific message. |
||
102 | std::string getMessage(const ExplodedNode *N) override; |
||
103 | |||
104 | /// Produces the message of the following form: |
||
105 | /// 'Msg via Nth parameter' |
||
106 | virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex); |
||
107 | |||
108 | virtual std::string getMessageForReturn(const CallExpr *CallExpr) { |
||
109 | return Msg; |
||
110 | } |
||
111 | |||
112 | virtual std::string getMessageForSymbolNotFound() { |
||
113 | return Msg; |
||
114 | } |
||
115 | }; |
||
116 | |||
117 | /// This class provides an interface through which checkers can create |
||
118 | /// individual bug reports. |
||
119 | class BugReport { |
||
120 | public: |
||
121 | enum class Kind { Basic, PathSensitive }; |
||
122 | |||
123 | protected: |
||
124 | friend class BugReportEquivClass; |
||
125 | friend class BugReporter; |
||
126 | |||
127 | Kind K; |
||
128 | const BugType& BT; |
||
129 | std::string ShortDescription; |
||
130 | std::string Description; |
||
131 | |||
132 | SmallVector<SourceRange, 4> Ranges; |
||
133 | SmallVector<std::shared_ptr<PathDiagnosticNotePiece>, 4> Notes; |
||
134 | SmallVector<FixItHint, 4> Fixits; |
||
135 | |||
136 | BugReport(Kind kind, const BugType &bt, StringRef desc) |
||
137 | : BugReport(kind, bt, "", desc) {} |
||
138 | |||
139 | BugReport(Kind K, const BugType &BT, StringRef ShortDescription, |
||
140 | StringRef Description) |
||
141 | : K(K), BT(BT), ShortDescription(ShortDescription), |
||
142 | Description(Description) {} |
||
143 | |||
144 | public: |
||
145 | virtual ~BugReport() = default; |
||
146 | |||
147 | Kind getKind() const { return K; } |
||
148 | |||
149 | const BugType& getBugType() const { return BT; } |
||
150 | |||
151 | /// A verbose warning message that is appropriate for displaying next to |
||
152 | /// the source code that introduces the problem. The description should be |
||
153 | /// at least a full sentence starting with a capital letter. The period at |
||
154 | /// the end of the warning is traditionally omitted. If the description |
||
155 | /// consists of multiple sentences, periods between the sentences are |
||
156 | /// encouraged, but the period at the end of the description is still omitted. |
||
157 | StringRef getDescription() const { return Description; } |
||
158 | |||
159 | /// A short general warning message that is appropriate for displaying in |
||
160 | /// the list of all reported bugs. It should describe what kind of bug is found |
||
161 | /// but does not need to try to go into details of that specific bug. |
||
162 | /// Grammatical conventions of getDescription() apply here as well. |
||
163 | StringRef getShortDescription(bool UseFallback = true) const { |
||
164 | if (ShortDescription.empty() && UseFallback) |
||
165 | return Description; |
||
166 | return ShortDescription; |
||
167 | } |
||
168 | |||
169 | /// The primary location of the bug report that points at the undesirable |
||
170 | /// behavior in the code. UIs should attach the warning description to this |
||
171 | /// location. The warning description should describe the bad behavior |
||
172 | /// at this location. |
||
173 | virtual PathDiagnosticLocation getLocation() const = 0; |
||
174 | |||
175 | /// The smallest declaration that contains the bug location. |
||
176 | /// This is purely cosmetic; the declaration can be displayed to the user |
||
177 | /// but it does not affect whether the report is emitted. |
||
178 | virtual const Decl *getDeclWithIssue() const = 0; |
||
179 | |||
180 | /// Get the location on which the report should be uniqued. Two warnings are |
||
181 | /// considered to be equivalent whenever they have the same bug types, |
||
182 | /// descriptions, and uniqueing locations. Out of a class of equivalent |
||
183 | /// warnings only one gets displayed to the user. For most warnings the |
||
184 | /// uniqueing location coincides with their location, but sometimes |
||
185 | /// it makes sense to use different locations. For example, a leak |
||
186 | /// checker can place the warning at the location where the last reference |
||
187 | /// to the leaking resource is dropped but at the same time unique the warning |
||
188 | /// by where that resource is acquired (allocated). |
||
189 | virtual PathDiagnosticLocation getUniqueingLocation() const = 0; |
||
190 | |||
191 | /// Get the declaration that corresponds to (usually contains) the uniqueing |
||
192 | /// location. This is not actively used for uniqueing, i.e. otherwise |
||
193 | /// identical reports that have different uniqueing decls will be considered |
||
194 | /// equivalent. |
||
195 | virtual const Decl *getUniqueingDecl() const = 0; |
||
196 | |||
197 | /// Add new item to the list of additional notes that need to be attached to |
||
198 | /// this report. If the report is path-sensitive, these notes will not be |
||
199 | /// displayed as part of the execution path explanation, but will be displayed |
||
200 | /// separately. Use bug visitors if you need to add an extra path note. |
||
201 | void addNote(StringRef Msg, const PathDiagnosticLocation &Pos, |
||
202 | ArrayRef<SourceRange> Ranges = {}) { |
||
203 | auto P = std::make_shared<PathDiagnosticNotePiece>(Pos, Msg); |
||
204 | |||
205 | for (const auto &R : Ranges) |
||
206 | P->addRange(R); |
||
207 | |||
208 | Notes.push_back(std::move(P)); |
||
209 | } |
||
210 | |||
211 | ArrayRef<std::shared_ptr<PathDiagnosticNotePiece>> getNotes() { |
||
212 | return Notes; |
||
213 | } |
||
214 | |||
215 | /// Add a range to a bug report. |
||
216 | /// |
||
217 | /// Ranges are used to highlight regions of interest in the source code. |
||
218 | /// They should be at the same source code line as the BugReport location. |
||
219 | /// By default, the source range of the statement corresponding to the error |
||
220 | /// node will be used; add a single invalid range to specify absence of |
||
221 | /// ranges. |
||
222 | void addRange(SourceRange R) { |
||
223 | assert((R.isValid() || Ranges.empty()) && "Invalid range can only be used " |
||
224 | "to specify that the report does not have a range."); |
||
225 | Ranges.push_back(R); |
||
226 | } |
||
227 | |||
228 | /// Get the SourceRanges associated with the report. |
||
229 | virtual ArrayRef<SourceRange> getRanges() const { |
||
230 | return Ranges; |
||
231 | } |
||
232 | |||
233 | /// Add a fix-it hint to the bug report. |
||
234 | /// |
||
235 | /// Fix-it hints are the suggested edits to the code that would resolve |
||
236 | /// the problem explained by the bug report. Fix-it hints should be |
||
237 | /// as conservative as possible because it is not uncommon for the user |
||
238 | /// to blindly apply all fixits to their project. Note that it is very hard |
||
239 | /// to produce a good fix-it hint for most path-sensitive warnings. |
||
240 | void addFixItHint(const FixItHint &F) { |
||
241 | Fixits.push_back(F); |
||
242 | } |
||
243 | |||
244 | llvm::ArrayRef<FixItHint> getFixits() const { return Fixits; } |
||
245 | |||
246 | /// Reports are uniqued to ensure that we do not emit multiple diagnostics |
||
247 | /// for each bug. |
||
248 | virtual void Profile(llvm::FoldingSetNodeID& hash) const = 0; |
||
249 | }; |
||
250 | |||
251 | class BasicBugReport : public BugReport { |
||
252 | PathDiagnosticLocation Location; |
||
253 | const Decl *DeclWithIssue = nullptr; |
||
254 | |||
255 | public: |
||
256 | BasicBugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l) |
||
257 | : BugReport(Kind::Basic, bt, desc), Location(l) {} |
||
258 | |||
259 | static bool classof(const BugReport *R) { |
||
260 | return R->getKind() == Kind::Basic; |
||
261 | } |
||
262 | |||
263 | PathDiagnosticLocation getLocation() const override { |
||
264 | assert(Location.isValid()); |
||
265 | return Location; |
||
266 | } |
||
267 | |||
268 | const Decl *getDeclWithIssue() const override { |
||
269 | return DeclWithIssue; |
||
270 | } |
||
271 | |||
272 | PathDiagnosticLocation getUniqueingLocation() const override { |
||
273 | return getLocation(); |
||
274 | } |
||
275 | |||
276 | const Decl *getUniqueingDecl() const override { |
||
277 | return getDeclWithIssue(); |
||
278 | } |
||
279 | |||
280 | /// Specifically set the Decl where an issue occurred. This isn't necessary |
||
281 | /// for BugReports that cover a path as it will be automatically inferred. |
||
282 | void setDeclWithIssue(const Decl *declWithIssue) { |
||
283 | DeclWithIssue = declWithIssue; |
||
284 | } |
||
285 | |||
286 | void Profile(llvm::FoldingSetNodeID& hash) const override; |
||
287 | }; |
||
288 | |||
289 | class PathSensitiveBugReport : public BugReport { |
||
290 | public: |
||
291 | using VisitorList = SmallVector<std::unique_ptr<BugReporterVisitor>, 8>; |
||
292 | using visitor_iterator = VisitorList::iterator; |
||
293 | using visitor_range = llvm::iterator_range<visitor_iterator>; |
||
294 | |||
295 | protected: |
||
296 | /// The ExplodedGraph node against which the report was thrown. It corresponds |
||
297 | /// to the end of the execution path that demonstrates the bug. |
||
298 | const ExplodedNode *ErrorNode = nullptr; |
||
299 | |||
300 | /// The range that corresponds to ErrorNode's program point. It is usually |
||
301 | /// highlighted in the report. |
||
302 | const SourceRange ErrorNodeRange; |
||
303 | |||
304 | /// Profile to identify equivalent bug reports for error report coalescing. |
||
305 | |||
306 | /// A (stack of) a set of symbols that are registered with this |
||
307 | /// report as being "interesting", and thus used to help decide which |
||
308 | /// diagnostics to include when constructing the final path diagnostic. |
||
309 | /// The stack is largely used by BugReporter when generating PathDiagnostics |
||
310 | /// for multiple PathDiagnosticConsumers. |
||
311 | llvm::DenseMap<SymbolRef, bugreporter::TrackingKind> InterestingSymbols; |
||
312 | |||
313 | /// A (stack of) set of regions that are registered with this report as being |
||
314 | /// "interesting", and thus used to help decide which diagnostics |
||
315 | /// to include when constructing the final path diagnostic. |
||
316 | /// The stack is largely used by BugReporter when generating PathDiagnostics |
||
317 | /// for multiple PathDiagnosticConsumers. |
||
318 | llvm::DenseMap<const MemRegion *, bugreporter::TrackingKind> |
||
319 | InterestingRegions; |
||
320 | |||
321 | /// A set of location contexts that correspoind to call sites which should be |
||
322 | /// considered "interesting". |
||
323 | llvm::SmallSet<const LocationContext *, 2> InterestingLocationContexts; |
||
324 | |||
325 | /// A set of custom visitors which generate "event" diagnostics at |
||
326 | /// interesting points in the path. |
||
327 | VisitorList Callbacks; |
||
328 | |||
329 | /// Used for ensuring the visitors are only added once. |
||
330 | llvm::FoldingSet<BugReporterVisitor> CallbacksSet; |
||
331 | |||
332 | /// When set, this flag disables all callstack pruning from a diagnostic |
||
333 | /// path. This is useful for some reports that want maximum fidelty |
||
334 | /// when reporting an issue. |
||
335 | bool DoNotPrunePath = false; |
||
336 | |||
337 | /// Used to track unique reasons why a bug report might be invalid. |
||
338 | /// |
||
339 | /// \sa markInvalid |
||
340 | /// \sa removeInvalidation |
||
341 | using InvalidationRecord = std::pair<const void *, const void *>; |
||
342 | |||
343 | /// If non-empty, this bug report is likely a false positive and should not be |
||
344 | /// shown to the user. |
||
345 | /// |
||
346 | /// \sa markInvalid |
||
347 | /// \sa removeInvalidation |
||
348 | llvm::SmallSet<InvalidationRecord, 4> Invalidations; |
||
349 | |||
350 | /// Conditions we're already tracking. |
||
351 | llvm::SmallSet<const ExplodedNode *, 4> TrackedConditions; |
||
352 | |||
353 | /// Reports with different uniqueing locations are considered to be different |
||
354 | /// for the purposes of deduplication. |
||
355 | PathDiagnosticLocation UniqueingLocation; |
||
356 | const Decl *UniqueingDecl; |
||
357 | |||
358 | const Stmt *getStmt() const; |
||
359 | |||
360 | /// If an event occurs in a different frame than the final diagnostic, |
||
361 | /// supply a message that will be used to construct an extra hint on the |
||
362 | /// returns from all the calls on the stack from this event to the final |
||
363 | /// diagnostic. |
||
364 | // FIXME: Allow shared_ptr keys in DenseMap? |
||
365 | std::map<PathDiagnosticPieceRef, std::unique_ptr<StackHintGenerator>> |
||
366 | StackHints; |
||
367 | |||
368 | public: |
||
369 | PathSensitiveBugReport(const BugType &bt, StringRef desc, |
||
370 | const ExplodedNode *errorNode) |
||
371 | : PathSensitiveBugReport(bt, desc, desc, errorNode) {} |
||
372 | |||
373 | PathSensitiveBugReport(const BugType &bt, StringRef shortDesc, StringRef desc, |
||
374 | const ExplodedNode *errorNode) |
||
375 | : PathSensitiveBugReport(bt, shortDesc, desc, errorNode, |
||
376 | /*LocationToUnique*/ {}, |
||
377 | /*DeclToUnique*/ nullptr) {} |
||
378 | |||
379 | /// Create a PathSensitiveBugReport with a custom uniqueing location. |
||
380 | /// |
||
381 | /// The reports that have the same report location, description, bug type, and |
||
382 | /// ranges are uniqued - only one of the equivalent reports will be presented |
||
383 | /// to the user. This method allows to rest the location which should be used |
||
384 | /// for uniquing reports. For example, memory leaks checker, could set this to |
||
385 | /// the allocation site, rather then the location where the bug is reported. |
||
386 | PathSensitiveBugReport(const BugType &bt, StringRef desc, |
||
387 | const ExplodedNode *errorNode, |
||
388 | PathDiagnosticLocation LocationToUnique, |
||
389 | const Decl *DeclToUnique) |
||
390 | : PathSensitiveBugReport(bt, desc, desc, errorNode, LocationToUnique, |
||
391 | DeclToUnique) {} |
||
392 | |||
393 | PathSensitiveBugReport(const BugType &bt, StringRef shortDesc, StringRef desc, |
||
394 | const ExplodedNode *errorNode, |
||
395 | PathDiagnosticLocation LocationToUnique, |
||
396 | const Decl *DeclToUnique); |
||
397 | |||
398 | static bool classof(const BugReport *R) { |
||
399 | return R->getKind() == Kind::PathSensitive; |
||
400 | } |
||
401 | |||
402 | const ExplodedNode *getErrorNode() const { return ErrorNode; } |
||
403 | |||
404 | /// Indicates whether or not any path pruning should take place |
||
405 | /// when generating a PathDiagnostic from this BugReport. |
||
406 | bool shouldPrunePath() const { return !DoNotPrunePath; } |
||
407 | |||
408 | /// Disable all path pruning when generating a PathDiagnostic. |
||
409 | void disablePathPruning() { DoNotPrunePath = true; } |
||
410 | |||
411 | /// Get the location on which the report should be uniqued. |
||
412 | PathDiagnosticLocation getUniqueingLocation() const override { |
||
413 | return UniqueingLocation; |
||
414 | } |
||
415 | |||
416 | /// Get the declaration containing the uniqueing location. |
||
417 | const Decl *getUniqueingDecl() const override { |
||
418 | return UniqueingDecl; |
||
419 | } |
||
420 | |||
421 | const Decl *getDeclWithIssue() const override; |
||
422 | |||
423 | ArrayRef<SourceRange> getRanges() const override; |
||
424 | |||
425 | PathDiagnosticLocation getLocation() const override; |
||
426 | |||
427 | /// Marks a symbol as interesting. Different kinds of interestingness will |
||
428 | /// be processed differently by visitors (e.g. if the tracking kind is |
||
429 | /// condition, will append "will be used as a condition" to the message). |
||
430 | void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind = |
||
431 | bugreporter::TrackingKind::Thorough); |
||
432 | |||
433 | void markNotInteresting(SymbolRef sym); |
||
434 | |||
435 | /// Marks a region as interesting. Different kinds of interestingness will |
||
436 | /// be processed differently by visitors (e.g. if the tracking kind is |
||
437 | /// condition, will append "will be used as a condition" to the message). |
||
438 | void markInteresting( |
||
439 | const MemRegion *R, |
||
440 | bugreporter::TrackingKind TKind = bugreporter::TrackingKind::Thorough); |
||
441 | |||
442 | void markNotInteresting(const MemRegion *R); |
||
443 | |||
444 | /// Marks a symbolic value as interesting. Different kinds of interestingness |
||
445 | /// will be processed differently by visitors (e.g. if the tracking kind is |
||
446 | /// condition, will append "will be used as a condition" to the message). |
||
447 | void markInteresting(SVal V, bugreporter::TrackingKind TKind = |
||
448 | bugreporter::TrackingKind::Thorough); |
||
449 | void markInteresting(const LocationContext *LC); |
||
450 | |||
451 | bool isInteresting(SymbolRef sym) const; |
||
452 | bool isInteresting(const MemRegion *R) const; |
||
453 | bool isInteresting(SVal V) const; |
||
454 | bool isInteresting(const LocationContext *LC) const; |
||
455 | |||
456 | std::optional<bugreporter::TrackingKind> |
||
457 | getInterestingnessKind(SymbolRef sym) const; |
||
458 | |||
459 | std::optional<bugreporter::TrackingKind> |
||
460 | getInterestingnessKind(const MemRegion *R) const; |
||
461 | |||
462 | std::optional<bugreporter::TrackingKind> getInterestingnessKind(SVal V) const; |
||
463 | |||
464 | /// Returns whether or not this report should be considered valid. |
||
465 | /// |
||
466 | /// Invalid reports are those that have been classified as likely false |
||
467 | /// positives after the fact. |
||
468 | bool isValid() const { |
||
469 | return Invalidations.empty(); |
||
470 | } |
||
471 | |||
472 | /// Marks the current report as invalid, meaning that it is probably a false |
||
473 | /// positive and should not be reported to the user. |
||
474 | /// |
||
475 | /// The \p Tag and \p Data arguments are intended to be opaque identifiers for |
||
476 | /// this particular invalidation, where \p Tag represents the visitor |
||
477 | /// responsible for invalidation, and \p Data represents the reason this |
||
478 | /// visitor decided to invalidate the bug report. |
||
479 | /// |
||
480 | /// \sa removeInvalidation |
||
481 | void markInvalid(const void *Tag, const void *Data) { |
||
482 | Invalidations.insert(std::make_pair(Tag, Data)); |
||
483 | } |
||
484 | |||
485 | /// Profile to identify equivalent bug reports for error report coalescing. |
||
486 | /// Reports are uniqued to ensure that we do not emit multiple diagnostics |
||
487 | /// for each bug. |
||
488 | void Profile(llvm::FoldingSetNodeID &hash) const override; |
||
489 | |||
490 | /// Add custom or predefined bug report visitors to this report. |
||
491 | /// |
||
492 | /// The visitors should be used when the default trace is not sufficient. |
||
493 | /// For example, they allow constructing a more elaborate trace. |
||
494 | /// @{ |
||
495 | void addVisitor(std::unique_ptr<BugReporterVisitor> visitor); |
||
496 | |||
497 | template <class VisitorType, class... Args> |
||
498 | void addVisitor(Args &&... ConstructorArgs) { |
||
499 | addVisitor( |
||
500 | std::make_unique<VisitorType>(std::forward<Args>(ConstructorArgs)...)); |
||
501 | } |
||
502 | /// @} |
||
503 | |||
504 | /// Remove all visitors attached to this bug report. |
||
505 | void clearVisitors(); |
||
506 | |||
507 | /// Iterators through the custom diagnostic visitors. |
||
508 | visitor_iterator visitor_begin() { return Callbacks.begin(); } |
||
509 | visitor_iterator visitor_end() { return Callbacks.end(); } |
||
510 | visitor_range visitors() { return {visitor_begin(), visitor_end()}; } |
||
511 | |||
512 | /// Notes that the condition of the CFGBlock associated with \p Cond is |
||
513 | /// being tracked. |
||
514 | /// \returns false if the condition is already being tracked. |
||
515 | bool addTrackedCondition(const ExplodedNode *Cond) { |
||
516 | return TrackedConditions.insert(Cond).second; |
||
517 | } |
||
518 | |||
519 | void addCallStackHint(PathDiagnosticPieceRef Piece, |
||
520 | std::unique_ptr<StackHintGenerator> StackHint) { |
||
521 | StackHints[Piece] = std::move(StackHint); |
||
522 | } |
||
523 | |||
524 | bool hasCallStackHint(PathDiagnosticPieceRef Piece) const { |
||
525 | return StackHints.count(Piece) > 0; |
||
526 | } |
||
527 | |||
528 | /// Produce the hint for the given node. The node contains |
||
529 | /// information about the call for which the diagnostic can be generated. |
||
530 | std::string |
||
531 | getCallStackMessage(PathDiagnosticPieceRef Piece, |
||
532 | const ExplodedNode *N) const { |
||
533 | auto I = StackHints.find(Piece); |
||
534 | if (I != StackHints.end()) |
||
535 | return I->second->getMessage(N); |
||
536 | return ""; |
||
537 | } |
||
538 | }; |
||
539 | |||
540 | //===----------------------------------------------------------------------===// |
||
541 | // BugTypes (collections of related reports). |
||
542 | //===----------------------------------------------------------------------===// |
||
543 | |||
544 | class BugReportEquivClass : public llvm::FoldingSetNode { |
||
545 | friend class BugReporter; |
||
546 | |||
547 | /// List of *owned* BugReport objects. |
||
548 | llvm::SmallVector<std::unique_ptr<BugReport>, 4> Reports; |
||
549 | |||
550 | void AddReport(std::unique_ptr<BugReport> &&R) { |
||
551 | Reports.push_back(std::move(R)); |
||
552 | } |
||
553 | |||
554 | public: |
||
555 | BugReportEquivClass(std::unique_ptr<BugReport> R) { AddReport(std::move(R)); } |
||
556 | |||
557 | ArrayRef<std::unique_ptr<BugReport>> getReports() const { return Reports; } |
||
558 | |||
559 | void Profile(llvm::FoldingSetNodeID& ID) const { |
||
560 | assert(!Reports.empty()); |
||
561 | Reports.front()->Profile(ID); |
||
562 | } |
||
563 | }; |
||
564 | |||
565 | //===----------------------------------------------------------------------===// |
||
566 | // BugReporter and friends. |
||
567 | //===----------------------------------------------------------------------===// |
||
568 | |||
569 | class BugReporterData { |
||
570 | public: |
||
571 | virtual ~BugReporterData() = default; |
||
572 | |||
573 | virtual ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() = 0; |
||
574 | virtual ASTContext &getASTContext() = 0; |
||
575 | virtual SourceManager &getSourceManager() = 0; |
||
576 | virtual AnalyzerOptions &getAnalyzerOptions() = 0; |
||
577 | virtual Preprocessor &getPreprocessor() = 0; |
||
578 | }; |
||
579 | |||
580 | /// BugReporter is a utility class for generating PathDiagnostics for analysis. |
||
581 | /// It collects the BugReports and BugTypes and knows how to generate |
||
582 | /// and flush the corresponding diagnostics. |
||
583 | /// |
||
584 | /// The base class is used for generating path-insensitive |
||
585 | class BugReporter { |
||
586 | private: |
||
587 | BugReporterData& D; |
||
588 | |||
589 | /// Generate and flush the diagnostics for the given bug report. |
||
590 | void FlushReport(BugReportEquivClass& EQ); |
||
591 | |||
592 | /// The set of bug reports tracked by the BugReporter. |
||
593 | llvm::FoldingSet<BugReportEquivClass> EQClasses; |
||
594 | |||
595 | /// A vector of BugReports for tracking the allocated pointers and cleanup. |
||
596 | std::vector<BugReportEquivClass *> EQClassesVector; |
||
597 | |||
598 | public: |
||
599 | BugReporter(BugReporterData &d); |
||
600 | virtual ~BugReporter(); |
||
601 | |||
602 | /// Generate and flush diagnostics for all bug reports. |
||
603 | void FlushReports(); |
||
604 | |||
605 | ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() { |
||
606 | return D.getPathDiagnosticConsumers(); |
||
607 | } |
||
608 | |||
609 | /// Iterator over the set of BugReports tracked by the BugReporter. |
||
610 | using EQClasses_iterator = llvm::FoldingSet<BugReportEquivClass>::iterator; |
||
611 | EQClasses_iterator EQClasses_begin() { return EQClasses.begin(); } |
||
612 | EQClasses_iterator EQClasses_end() { return EQClasses.end(); } |
||
613 | |||
614 | ASTContext &getContext() { return D.getASTContext(); } |
||
615 | |||
616 | const SourceManager &getSourceManager() { return D.getSourceManager(); } |
||
617 | |||
618 | const AnalyzerOptions &getAnalyzerOptions() { return D.getAnalyzerOptions(); } |
||
619 | |||
620 | Preprocessor &getPreprocessor() { return D.getPreprocessor(); } |
||
621 | |||
622 | /// Add the given report to the set of reports tracked by BugReporter. |
||
623 | /// |
||
624 | /// The reports are usually generated by the checkers. Further, they are |
||
625 | /// folded based on the profile value, which is done to coalesce similar |
||
626 | /// reports. |
||
627 | virtual void emitReport(std::unique_ptr<BugReport> R); |
||
628 | |||
629 | void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, |
||
630 | StringRef BugName, StringRef BugCategory, |
||
631 | StringRef BugStr, PathDiagnosticLocation Loc, |
||
632 | ArrayRef<SourceRange> Ranges = std::nullopt, |
||
633 | ArrayRef<FixItHint> Fixits = std::nullopt); |
||
634 | |||
635 | void EmitBasicReport(const Decl *DeclWithIssue, CheckerNameRef CheckerName, |
||
636 | StringRef BugName, StringRef BugCategory, |
||
637 | StringRef BugStr, PathDiagnosticLocation Loc, |
||
638 | ArrayRef<SourceRange> Ranges = std::nullopt, |
||
639 | ArrayRef<FixItHint> Fixits = std::nullopt); |
||
640 | |||
641 | private: |
||
642 | llvm::StringMap<std::unique_ptr<BugType>> StrBugTypes; |
||
643 | |||
644 | /// Returns a BugType that is associated with the given name and |
||
645 | /// category. |
||
646 | BugType *getBugTypeForName(CheckerNameRef CheckerName, StringRef name, |
||
647 | StringRef category); |
||
648 | |||
649 | virtual BugReport * |
||
650 | findReportInEquivalenceClass(BugReportEquivClass &eqClass, |
||
651 | SmallVectorImpl<BugReport *> &bugReports) { |
||
652 | return eqClass.getReports()[0].get(); |
||
653 | } |
||
654 | |||
655 | protected: |
||
656 | /// Generate the diagnostics for the given bug report. |
||
657 | virtual std::unique_ptr<DiagnosticForConsumerMapTy> |
||
658 | generateDiagnosticForConsumerMap(BugReport *exampleReport, |
||
659 | ArrayRef<PathDiagnosticConsumer *> consumers, |
||
660 | ArrayRef<BugReport *> bugReports); |
||
661 | }; |
||
662 | |||
663 | /// GRBugReporter is used for generating path-sensitive reports. |
||
664 | class PathSensitiveBugReporter final : public BugReporter { |
||
665 | ExprEngine& Eng; |
||
666 | |||
667 | BugReport *findReportInEquivalenceClass( |
||
668 | BugReportEquivClass &eqClass, |
||
669 | SmallVectorImpl<BugReport *> &bugReports) override; |
||
670 | |||
671 | /// Generate the diagnostics for the given bug report. |
||
672 | std::unique_ptr<DiagnosticForConsumerMapTy> |
||
673 | generateDiagnosticForConsumerMap(BugReport *exampleReport, |
||
674 | ArrayRef<PathDiagnosticConsumer *> consumers, |
||
675 | ArrayRef<BugReport *> bugReports) override; |
||
676 | public: |
||
677 | PathSensitiveBugReporter(BugReporterData& d, ExprEngine& eng) |
||
678 | : BugReporter(d), Eng(eng) {} |
||
679 | |||
680 | /// getGraph - Get the exploded graph created by the analysis engine |
||
681 | /// for the analyzed method or function. |
||
682 | const ExplodedGraph &getGraph() const; |
||
683 | |||
684 | /// getStateManager - Return the state manager used by the analysis |
||
685 | /// engine. |
||
686 | ProgramStateManager &getStateManager() const; |
||
687 | |||
688 | /// \p bugReports A set of bug reports within a *single* equivalence class |
||
689 | /// |
||
690 | /// \return A mapping from consumers to the corresponding diagnostics. |
||
691 | /// Iterates through the bug reports within a single equivalence class, |
||
692 | /// stops at a first non-invalidated report. |
||
693 | std::unique_ptr<DiagnosticForConsumerMapTy> generatePathDiagnostics( |
||
694 | ArrayRef<PathDiagnosticConsumer *> consumers, |
||
695 | ArrayRef<PathSensitiveBugReport *> &bugReports); |
||
696 | |||
697 | void emitReport(std::unique_ptr<BugReport> R) override; |
||
698 | }; |
||
699 | |||
700 | |||
701 | class BugReporterContext { |
||
702 | PathSensitiveBugReporter &BR; |
||
703 | |||
704 | virtual void anchor(); |
||
705 | |||
706 | public: |
||
707 | BugReporterContext(PathSensitiveBugReporter &br) : BR(br) {} |
||
708 | |||
709 | virtual ~BugReporterContext() = default; |
||
710 | |||
711 | PathSensitiveBugReporter& getBugReporter() { return BR; } |
||
712 | |||
713 | ProgramStateManager& getStateManager() const { |
||
714 | return BR.getStateManager(); |
||
715 | } |
||
716 | |||
717 | ASTContext &getASTContext() const { |
||
718 | return BR.getContext(); |
||
719 | } |
||
720 | |||
721 | const SourceManager& getSourceManager() const { |
||
722 | return BR.getSourceManager(); |
||
723 | } |
||
724 | |||
725 | const AnalyzerOptions &getAnalyzerOptions() const { |
||
726 | return BR.getAnalyzerOptions(); |
||
727 | } |
||
728 | }; |
||
729 | |||
730 | /// The tag that carries some information with it. |
||
731 | /// |
||
732 | /// It can be valuable to produce tags with some bits of information and later |
||
733 | /// reuse them for a better diagnostic. |
||
734 | /// |
||
735 | /// Please make sure that derived class' constuctor is private and that the user |
||
736 | /// can only create objects using DataTag::Factory. This also means that |
||
737 | /// DataTag::Factory should be friend for every derived class. |
||
738 | class DataTag : public ProgramPointTag { |
||
739 | public: |
||
740 | StringRef getTagDescription() const override { return "Data Tag"; } |
||
741 | |||
742 | // Manage memory for DataTag objects. |
||
743 | class Factory { |
||
744 | std::vector<std::unique_ptr<DataTag>> Tags; |
||
745 | |||
746 | public: |
||
747 | template <class DataTagType, class... Args> |
||
748 | const DataTagType *make(Args &&... ConstructorArgs) { |
||
749 | // We cannot use std::make_unique because we cannot access the private |
||
750 | // constructor from inside it. |
||
751 | Tags.emplace_back( |
||
752 | new DataTagType(std::forward<Args>(ConstructorArgs)...)); |
||
753 | return static_cast<DataTagType *>(Tags.back().get()); |
||
754 | } |
||
755 | }; |
||
756 | |||
757 | protected: |
||
758 | DataTag(void *TagKind) : ProgramPointTag(TagKind) {} |
||
759 | }; |
||
760 | |||
761 | /// The tag upon which the TagVisitor reacts. Add these in order to display |
||
762 | /// additional PathDiagnosticEventPieces along the path. |
||
763 | class NoteTag : public DataTag { |
||
764 | public: |
||
765 | using Callback = std::function<std::string(BugReporterContext &, |
||
766 | PathSensitiveBugReport &)>; |
||
767 | |||
768 | private: |
||
769 | static int Kind; |
||
770 | |||
771 | const Callback Cb; |
||
772 | const bool IsPrunable; |
||
773 | |||
774 | NoteTag(Callback &&Cb, bool IsPrunable) |
||
775 | : DataTag(&Kind), Cb(std::move(Cb)), IsPrunable(IsPrunable) {} |
||
776 | |||
777 | public: |
||
778 | static bool classof(const ProgramPointTag *T) { |
||
779 | return T->getTagKind() == &Kind; |
||
780 | } |
||
781 | |||
782 | std::optional<std::string> generateMessage(BugReporterContext &BRC, |
||
783 | PathSensitiveBugReport &R) const { |
||
784 | std::string Msg = Cb(BRC, R); |
||
785 | if (Msg.empty()) |
||
786 | return std::nullopt; |
||
787 | |||
788 | return std::move(Msg); |
||
789 | } |
||
790 | |||
791 | StringRef getTagDescription() const override { |
||
792 | // TODO: Remember a few examples of generated messages |
||
793 | // and display them in the ExplodedGraph dump by |
||
794 | // returning them from this function. |
||
795 | return "Note Tag"; |
||
796 | } |
||
797 | |||
798 | bool isPrunable() const { return IsPrunable; } |
||
799 | |||
800 | friend class Factory; |
||
801 | friend class TagVisitor; |
||
802 | }; |
||
803 | |||
804 | } // namespace ento |
||
805 | |||
806 | } // namespace clang |
||
807 | |||
808 | #endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H |