/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
Line | Count | Source (jump to first uncovered line) |
1 | | //===- PathDiagnostic.h - Path-Specific Diagnostic Handling -----*- C++ -*-===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This file defines the PathDiagnostic-related interfaces. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H |
14 | | #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H |
15 | | |
16 | | #include "clang/AST/Stmt.h" |
17 | | #include "clang/Analysis/AnalysisDeclContext.h" |
18 | | #include "clang/Basic/LLVM.h" |
19 | | #include "clang/Basic/SourceLocation.h" |
20 | | #include "llvm/ADT/ArrayRef.h" |
21 | | #include "llvm/ADT/FoldingSet.h" |
22 | | #include "llvm/ADT/Optional.h" |
23 | | #include "llvm/ADT/PointerUnion.h" |
24 | | #include "llvm/ADT/SmallVector.h" |
25 | | #include "llvm/ADT/StringRef.h" |
26 | | #include "llvm/Support/Allocator.h" |
27 | | #include <cassert> |
28 | | #include <deque> |
29 | | #include <iterator> |
30 | | #include <list> |
31 | | #include <map> |
32 | | #include <memory> |
33 | | #include <set> |
34 | | #include <string> |
35 | | #include <utility> |
36 | | #include <vector> |
37 | | |
38 | | namespace clang { |
39 | | |
40 | | class AnalysisDeclContext; |
41 | | class BinaryOperator; |
42 | | class CallEnter; |
43 | | class CallExitEnd; |
44 | | class CallExpr; |
45 | | class ConditionalOperator; |
46 | | class Decl; |
47 | | class Expr; |
48 | | class LocationContext; |
49 | | class MemberExpr; |
50 | | class ProgramPoint; |
51 | | class SourceManager; |
52 | | |
53 | | namespace ento { |
54 | | |
55 | | class ExplodedNode; |
56 | | class SymExpr; |
57 | | |
58 | | using SymbolRef = const SymExpr *; |
59 | | |
60 | | //===----------------------------------------------------------------------===// |
61 | | // High-level interface for handlers of path-sensitive diagnostics. |
62 | | //===----------------------------------------------------------------------===// |
63 | | |
64 | | class PathDiagnostic; |
65 | | |
66 | | class PathDiagnosticConsumer { |
67 | | public: |
68 | | class PDFileEntry : public llvm::FoldingSetNode { |
69 | | public: |
70 | 78 | PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {} |
71 | | |
72 | | using ConsumerFiles = std::vector<std::pair<StringRef, StringRef>>; |
73 | | |
74 | | /// A vector of <consumer,file> pairs. |
75 | | ConsumerFiles files; |
76 | | |
77 | | /// A precomputed hash tag used for uniquing PDFileEntry objects. |
78 | | const llvm::FoldingSetNodeID NodeID; |
79 | | |
80 | | /// Used for profiling in the FoldingSet. |
81 | 10 | void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; } |
82 | | }; |
83 | | |
84 | | class FilesMade { |
85 | | llvm::BumpPtrAllocator Alloc; |
86 | | llvm::FoldingSet<PDFileEntry> Set; |
87 | | |
88 | | public: |
89 | | ~FilesMade(); |
90 | | |
91 | 590 | bool empty() const { return Set.empty(); } |
92 | | |
93 | | void addDiagnostic(const PathDiagnostic &PD, |
94 | | StringRef ConsumerName, |
95 | | StringRef fileName); |
96 | | |
97 | | PDFileEntry::ConsumerFiles *getFiles(const PathDiagnostic &PD); |
98 | | }; |
99 | | |
100 | | private: |
101 | | virtual void anchor(); |
102 | | |
103 | | public: |
104 | 997 | PathDiagnosticConsumer() = default; |
105 | | virtual ~PathDiagnosticConsumer(); |
106 | | |
107 | | void FlushDiagnostics(FilesMade *FilesMade); |
108 | | |
109 | | virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, |
110 | | FilesMade *filesMade) = 0; |
111 | | |
112 | | virtual StringRef getName() const = 0; |
113 | | |
114 | | void HandlePathDiagnostic(std::unique_ptr<PathDiagnostic> D); |
115 | | |
116 | | enum PathGenerationScheme { |
117 | | /// Only runs visitors, no output generated. |
118 | | None, |
119 | | |
120 | | /// Used for HTML, SARIF, and text output. |
121 | | Minimal, |
122 | | |
123 | | /// Used for plist output, used for "arrows" generation. |
124 | | Extensive, |
125 | | }; |
126 | | |
127 | 82 | virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } |
128 | 0 | virtual bool supportsLogicalOpControlFlow() const { return false; } |
129 | | |
130 | | /// Return true if the PathDiagnosticConsumer supports individual |
131 | | /// PathDiagnostics that span multiple files. |
132 | 1 | virtual bool supportsCrossFileDiagnostics() const { return false; } |
133 | | |
134 | | protected: |
135 | | bool flushed = false; |
136 | | llvm::FoldingSet<PathDiagnostic> Diags; |
137 | | }; |
138 | | |
139 | | //===----------------------------------------------------------------------===// |
140 | | // Path-sensitive diagnostics. |
141 | | //===----------------------------------------------------------------------===// |
142 | | |
143 | | class PathDiagnosticRange : public SourceRange { |
144 | | public: |
145 | | bool isPoint = false; |
146 | | |
147 | | PathDiagnosticRange(SourceRange R, bool isP = false) |
148 | 91.1k | : SourceRange(R), isPoint(isP) {} |
149 | 50.4k | PathDiagnosticRange() = default; |
150 | | }; |
151 | | |
152 | | using LocationOrAnalysisDeclContext = |
153 | | llvm::PointerUnion<const LocationContext *, AnalysisDeclContext *>; |
154 | | |
155 | | class PathDiagnosticLocation { |
156 | | private: |
157 | | enum Kind { RangeK, SingleLocK, StmtK, DeclK } K = SingleLocK; |
158 | | |
159 | | const Stmt *S = nullptr; |
160 | | const Decl *D = nullptr; |
161 | | const SourceManager *SM = nullptr; |
162 | | FullSourceLoc Loc; |
163 | | PathDiagnosticRange Range; |
164 | | |
165 | | PathDiagnosticLocation(SourceLocation L, const SourceManager &sm, Kind kind) |
166 | 15.1k | : K(kind), SM(&sm), Loc(genLocation(L)), Range(genRange()) {} |
167 | | |
168 | | FullSourceLoc genLocation( |
169 | | SourceLocation L = SourceLocation(), |
170 | | LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; |
171 | | |
172 | | PathDiagnosticRange genRange( |
173 | | LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; |
174 | | |
175 | | public: |
176 | | /// Create an invalid location. |
177 | 50.4k | PathDiagnosticLocation() = default; |
178 | | |
179 | | /// Create a location corresponding to the given statement. |
180 | | PathDiagnosticLocation(const Stmt *s, const SourceManager &sm, |
181 | | LocationOrAnalysisDeclContext lac) |
182 | | : K(s->getBeginLoc().isValid() ? StmtK : SingleLocK), |
183 | | S(K == StmtK ? s : nullptr), SM(&sm), |
184 | 75.5k | Loc(genLocation(SourceLocation(), lac)), Range(genRange(lac)) { |
185 | 75.5k | assert(K == SingleLocK || S); |
186 | 75.5k | assert(K == SingleLocK || Loc.isValid()); |
187 | 75.5k | assert(K == SingleLocK || Range.isValid()); |
188 | 75.5k | } |
189 | | |
190 | | /// Create a location corresponding to the given declaration. |
191 | | PathDiagnosticLocation(const Decl *d, const SourceManager &sm) |
192 | 294 | : K(DeclK), D(d), SM(&sm), Loc(genLocation()), Range(genRange()) { |
193 | 294 | assert(D); |
194 | 294 | assert(Loc.isValid()); |
195 | 294 | assert(Range.isValid()); |
196 | 294 | } |
197 | | |
198 | | /// Create a location at an explicit offset in the source. |
199 | | /// |
200 | | /// This should only be used if there are no more appropriate constructors. |
201 | | PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm) |
202 | 120 | : SM(&sm), Loc(loc, sm), Range(genRange()) { |
203 | 120 | assert(Loc.isValid()); |
204 | 120 | assert(Range.isValid()); |
205 | 120 | } |
206 | | |
207 | | /// Create a location corresponding to the given declaration. |
208 | | static PathDiagnosticLocation create(const Decl *D, |
209 | 276 | const SourceManager &SM) { |
210 | 276 | return PathDiagnosticLocation(D, SM); |
211 | 276 | } |
212 | | |
213 | | /// Create a location for the beginning of the declaration. |
214 | | static PathDiagnosticLocation createBegin(const Decl *D, |
215 | | const SourceManager &SM); |
216 | | |
217 | | /// Create a location for the beginning of the declaration. |
218 | | /// The third argument is ignored, useful for generic treatment |
219 | | /// of statements and declarations. |
220 | | static PathDiagnosticLocation |
221 | | createBegin(const Decl *D, const SourceManager &SM, |
222 | 0 | const LocationOrAnalysisDeclContext LAC) { |
223 | 0 | return createBegin(D, SM); |
224 | 0 | } |
225 | | |
226 | | /// Create a location for the beginning of the statement. |
227 | | static PathDiagnosticLocation createBegin(const Stmt *S, |
228 | | const SourceManager &SM, |
229 | | const LocationOrAnalysisDeclContext LAC); |
230 | | |
231 | | /// Create a location for the end of the statement. |
232 | | /// |
233 | | /// If the statement is a CompoundStatement, the location will point to the |
234 | | /// closing brace instead of following it. |
235 | | static PathDiagnosticLocation createEnd(const Stmt *S, |
236 | | const SourceManager &SM, |
237 | | const LocationOrAnalysisDeclContext LAC); |
238 | | |
239 | | /// Create the location for the operator of the binary expression. |
240 | | /// Assumes the statement has a valid location. |
241 | | static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO, |
242 | | const SourceManager &SM); |
243 | | static PathDiagnosticLocation createConditionalColonLoc( |
244 | | const ConditionalOperator *CO, |
245 | | const SourceManager &SM); |
246 | | |
247 | | /// For member expressions, return the location of the '.' or '->'. |
248 | | /// Assumes the statement has a valid location. |
249 | | static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME, |
250 | | const SourceManager &SM); |
251 | | |
252 | | /// Create a location for the beginning of the compound statement. |
253 | | /// Assumes the statement has a valid location. |
254 | | static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS, |
255 | | const SourceManager &SM); |
256 | | |
257 | | /// Create a location for the end of the compound statement. |
258 | | /// Assumes the statement has a valid location. |
259 | | static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS, |
260 | | const SourceManager &SM); |
261 | | |
262 | | /// Create a location for the beginning of the enclosing declaration body. |
263 | | /// Defaults to the beginning of the first statement in the declaration body. |
264 | | static PathDiagnosticLocation createDeclBegin(const LocationContext *LC, |
265 | | const SourceManager &SM); |
266 | | |
267 | | /// Constructs a location for the end of the enclosing declaration body. |
268 | | /// Defaults to the end of brace. |
269 | | static PathDiagnosticLocation createDeclEnd(const LocationContext *LC, |
270 | | const SourceManager &SM); |
271 | | |
272 | | /// Create a location corresponding to the given valid ExplodedNode. |
273 | | static PathDiagnosticLocation create(const ProgramPoint &P, |
274 | | const SourceManager &SMng); |
275 | | |
276 | | /// Create a location corresponding to the next valid ExplodedNode as end |
277 | | /// of path location. |
278 | | static PathDiagnosticLocation createEndOfPath(const ExplodedNode* N, |
279 | | const SourceManager &SM); |
280 | | |
281 | | /// Convert the given location into a single kind location. |
282 | | static PathDiagnosticLocation createSingleLocation( |
283 | | const PathDiagnosticLocation &PDL); |
284 | | |
285 | 2.93k | bool operator==(const PathDiagnosticLocation &X) const { |
286 | 2.93k | return K == X.K && Loc == X.Loc2.77k && Range == X.Range1.52k ; |
287 | 2.93k | } |
288 | | |
289 | 1.48k | bool operator!=(const PathDiagnosticLocation &X) const { |
290 | 1.48k | return !(*this == X); |
291 | 1.48k | } |
292 | | |
293 | 192k | bool isValid() const { |
294 | 192k | return SM != nullptr; |
295 | 192k | } |
296 | | |
297 | 232k | FullSourceLoc asLocation() const { |
298 | 232k | return Loc; |
299 | 232k | } |
300 | | |
301 | 22.2k | PathDiagnosticRange asRange() const { |
302 | 22.2k | return Range; |
303 | 22.2k | } |
304 | | |
305 | 236k | const Stmt *asStmt() const { assert(isValid()); return S; } |
306 | 81.6k | const Stmt *getStmtOrNull() const { |
307 | 81.6k | if (!isValid()) |
308 | 0 | return nullptr; |
309 | 81.6k | return asStmt(); |
310 | 81.6k | } |
311 | | |
312 | 0 | const Decl *asDecl() const { assert(isValid()); return D; } |
313 | | |
314 | 21.0k | bool hasRange() const { return K == StmtK || K == RangeK4.03k || K == DeclK3.30k ; } |
315 | | |
316 | 76 | bool hasValidLocation() const { return asLocation().isValid(); } |
317 | | |
318 | 115 | void invalidate() { |
319 | 115 | *this = PathDiagnosticLocation(); |
320 | 115 | } |
321 | | |
322 | | void flatten(); |
323 | | |
324 | 565 | const SourceManager& getManager() const { assert(isValid()); return *SM; } |
325 | | |
326 | | void Profile(llvm::FoldingSetNodeID &ID) const; |
327 | | |
328 | | void dump() const; |
329 | | |
330 | | /// Given an exploded node, retrieve the statement that should be used |
331 | | /// for the diagnostic location. |
332 | | static const Stmt *getStmt(const ExplodedNode *N); |
333 | | |
334 | | /// Retrieve the statement corresponding to the successor node. |
335 | | static const Stmt *getNextStmt(const ExplodedNode *N); |
336 | | }; |
337 | | |
338 | | class PathDiagnosticLocationPair { |
339 | | private: |
340 | | PathDiagnosticLocation Start, End; |
341 | | |
342 | | public: |
343 | | PathDiagnosticLocationPair(const PathDiagnosticLocation &start, |
344 | | const PathDiagnosticLocation &end) |
345 | 12.9k | : Start(start), End(end) {} |
346 | | |
347 | 79.9k | const PathDiagnosticLocation &getStart() const { return Start; } |
348 | 56.6k | const PathDiagnosticLocation &getEnd() const { return End; } |
349 | | |
350 | 863 | void setStart(const PathDiagnosticLocation &L) { Start = L; } |
351 | 8.78k | void setEnd(const PathDiagnosticLocation &L) { End = L; } |
352 | | |
353 | 2.73k | void flatten() { |
354 | 2.73k | Start.flatten(); |
355 | 2.73k | End.flatten(); |
356 | 2.73k | } |
357 | | |
358 | 0 | void Profile(llvm::FoldingSetNodeID &ID) const { |
359 | 0 | Start.Profile(ID); |
360 | 0 | End.Profile(ID); |
361 | 0 | } |
362 | | }; |
363 | | |
364 | | //===----------------------------------------------------------------------===// |
365 | | // Path "pieces" for path-sensitive diagnostics. |
366 | | //===----------------------------------------------------------------------===// |
367 | | |
368 | | class PathDiagnosticPiece: public llvm::FoldingSetNode { |
369 | | public: |
370 | | enum Kind { ControlFlow, Event, Macro, Call, Note, PopUp }; |
371 | | enum DisplayHint { Above, Below }; |
372 | | |
373 | | private: |
374 | | const std::string str; |
375 | | const Kind kind; |
376 | | const DisplayHint Hint; |
377 | | |
378 | | /// In the containing bug report, this piece is the last piece from |
379 | | /// the main source file. |
380 | | bool LastInMainSourceFile = false; |
381 | | |
382 | | /// A constant string that can be used to tag the PathDiagnosticPiece, |
383 | | /// typically with the identification of the creator. The actual pointer |
384 | | /// value is meant to be an identifier; the string itself is useful for |
385 | | /// debugging. |
386 | | StringRef Tag; |
387 | | |
388 | | std::vector<SourceRange> ranges; |
389 | | |
390 | | protected: |
391 | | PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below); |
392 | | PathDiagnosticPiece(Kind k, DisplayHint hint = Below); |
393 | | |
394 | | public: |
395 | | PathDiagnosticPiece() = delete; |
396 | | PathDiagnosticPiece(const PathDiagnosticPiece &) = delete; |
397 | | PathDiagnosticPiece &operator=(const PathDiagnosticPiece &) = delete; |
398 | | virtual ~PathDiagnosticPiece(); |
399 | | |
400 | 9.07k | StringRef getString() const { return str; } |
401 | | |
402 | | /// Tag this PathDiagnosticPiece with the given C-string. |
403 | 4.41k | void setTag(const char *tag) { Tag = tag; } |
404 | | |
405 | | /// Return the opaque tag (if any) on the PathDiagnosticPiece. |
406 | 265 | const void *getTag() const { return Tag.data(); } |
407 | | |
408 | | /// Return the string representation of the tag. This is useful |
409 | | /// for debugging. |
410 | 9 | StringRef getTagStr() const { return Tag; } |
411 | | |
412 | | /// getDisplayHint - Return a hint indicating where the diagnostic should |
413 | | /// be displayed by the PathDiagnosticConsumer. |
414 | 2.43k | DisplayHint getDisplayHint() const { return Hint; } |
415 | | |
416 | | virtual PathDiagnosticLocation getLocation() const = 0; |
417 | | virtual void flattenLocations() = 0; |
418 | | |
419 | 256k | Kind getKind() const { return kind; } |
420 | | |
421 | 29.0k | void addRange(SourceRange R) { |
422 | 29.0k | if (!R.isValid()) |
423 | 0 | return; |
424 | 29.0k | ranges.push_back(R); |
425 | 29.0k | } |
426 | | |
427 | 0 | void addRange(SourceLocation B, SourceLocation E) { |
428 | 0 | if (!B.isValid() || !E.isValid()) |
429 | 0 | return; |
430 | 0 | ranges.push_back(SourceRange(B,E)); |
431 | 0 | } |
432 | | |
433 | | /// Return the SourceRanges associated with this PathDiagnosticPiece. |
434 | 26.1k | ArrayRef<SourceRange> getRanges() const { return ranges; } |
435 | | |
436 | | virtual void Profile(llvm::FoldingSetNodeID &ID) const; |
437 | | |
438 | 2 | void setAsLastInMainSourceFile() { |
439 | 2 | LastInMainSourceFile = true; |
440 | 2 | } |
441 | | |
442 | 141 | bool isLastInMainSourceFile() const { |
443 | 141 | return LastInMainSourceFile; |
444 | 141 | } |
445 | | |
446 | | virtual void dump() const = 0; |
447 | | }; |
448 | | |
449 | | class PathPieces : public std::list<std::shared_ptr<PathDiagnosticPiece>> { |
450 | | void flattenTo(PathPieces &Primary, PathPieces &Current, |
451 | | bool ShouldFlattenMacros) const; |
452 | | |
453 | | public: |
454 | 12.6k | PathPieces flatten(bool ShouldFlattenMacros) const { |
455 | 12.6k | PathPieces Result; |
456 | 12.6k | flattenTo(Result, Result, ShouldFlattenMacros); |
457 | 12.6k | return Result; |
458 | 12.6k | } |
459 | | |
460 | | void dump() const; |
461 | | }; |
462 | | |
463 | | class PathDiagnosticSpotPiece : public PathDiagnosticPiece { |
464 | | private: |
465 | | PathDiagnosticLocation Pos; |
466 | | |
467 | | public: |
468 | | PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos, |
469 | | StringRef s, |
470 | | PathDiagnosticPiece::Kind k, |
471 | | bool addPosRange = true) |
472 | 22.5k | : PathDiagnosticPiece(s, k), Pos(pos) { |
473 | 22.5k | assert(Pos.isValid() && Pos.hasValidLocation() && |
474 | 22.5k | "PathDiagnosticSpotPiece's must have a valid location."); |
475 | 22.5k | if (addPosRange && Pos.hasRange()21.0k ) addRange(Pos.asRange())18.0k ; |
476 | 22.5k | } |
477 | | |
478 | 46.4k | PathDiagnosticLocation getLocation() const override { return Pos; } |
479 | 14.2k | void flattenLocations() override { Pos.flatten(); } |
480 | | |
481 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
482 | | |
483 | 0 | static bool classof(const PathDiagnosticPiece *P) { |
484 | 0 | return P->getKind() == Event || P->getKind() == Macro || |
485 | 0 | P->getKind() == Note || P->getKind() == PopUp; |
486 | 0 | } |
487 | | }; |
488 | | |
489 | | /// Interface for classes constructing Stack hints. |
490 | | /// |
491 | | /// If a PathDiagnosticEvent occurs in a different frame than the final |
492 | | /// diagnostic the hints can be used to summarize the effect of the call. |
493 | | class StackHintGenerator { |
494 | | public: |
495 | | virtual ~StackHintGenerator() = 0; |
496 | | |
497 | | /// Construct the Diagnostic message for the given ExplodedNode. |
498 | | virtual std::string getMessage(const ExplodedNode *N) = 0; |
499 | | }; |
500 | | |
501 | | /// Constructs a Stack hint for the given symbol. |
502 | | /// |
503 | | /// The class knows how to construct the stack hint message based on |
504 | | /// traversing the CallExpr associated with the call and checking if the given |
505 | | /// symbol is returned or is one of the arguments. |
506 | | /// The hint can be customized by redefining 'getMessageForX()' methods. |
507 | | class StackHintGeneratorForSymbol : public StackHintGenerator { |
508 | | private: |
509 | | SymbolRef Sym; |
510 | | std::string Msg; |
511 | | |
512 | | public: |
513 | 681 | StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {} |
514 | 681 | ~StackHintGeneratorForSymbol() override = default; |
515 | | |
516 | | /// Search the call expression for the symbol Sym and dispatch the |
517 | | /// 'getMessageForX()' methods to construct a specific message. |
518 | | std::string getMessage(const ExplodedNode *N) override; |
519 | | |
520 | | /// Produces the message of the following form: |
521 | | /// 'Msg via Nth parameter' |
522 | | virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex); |
523 | | |
524 | 7 | virtual std::string getMessageForReturn(const CallExpr *CallExpr) { |
525 | 7 | return Msg; |
526 | 7 | } |
527 | | |
528 | 6 | virtual std::string getMessageForSymbolNotFound() { |
529 | 6 | return Msg; |
530 | 6 | } |
531 | | }; |
532 | | |
533 | | class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece { |
534 | | Optional<bool> IsPrunable; |
535 | | |
536 | | /// If the event occurs in a different frame than the final diagnostic, |
537 | | /// supply a message that will be used to construct an extra hint on the |
538 | | /// returns from all the calls on the stack from this event to the final |
539 | | /// diagnostic. |
540 | | std::unique_ptr<StackHintGenerator> CallStackHint; |
541 | | |
542 | | public: |
543 | | PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, |
544 | | StringRef s, bool addPosRange = true, |
545 | | StackHintGenerator *stackHint = nullptr) |
546 | | : PathDiagnosticSpotPiece(pos, s, Event, addPosRange), |
547 | 21.5k | CallStackHint(stackHint) {} |
548 | | ~PathDiagnosticEventPiece() override; |
549 | | |
550 | | /// Mark the diagnostic piece as being potentially prunable. This |
551 | | /// flag may have been previously set, at which point it will not |
552 | | /// be reset unless one specifies to do so. |
553 | 3.85k | void setPrunable(bool isPrunable, bool override = false) { |
554 | 3.85k | if (IsPrunable.hasValue() && !override289 ) |
555 | 289 | return; |
556 | 3.56k | IsPrunable = isPrunable; |
557 | 3.56k | } |
558 | | |
559 | | /// Return true if the diagnostic piece is prunable. |
560 | 3.86k | bool isPrunable() const { |
561 | 3.86k | return IsPrunable.hasValue() ? IsPrunable.getValue()650 : false3.21k ; |
562 | 3.86k | } |
563 | | |
564 | 2.24k | bool hasCallStackHint() { return (bool)CallStackHint; } |
565 | | |
566 | | /// Produce the hint for the given node. The node contains |
567 | | /// information about the call for which the diagnostic can be generated. |
568 | 20 | std::string getCallStackMessage(const ExplodedNode *N) { |
569 | 20 | if (CallStackHint) |
570 | 20 | return CallStackHint->getMessage(N); |
571 | 0 | return {}; |
572 | 0 | } |
573 | | |
574 | | void dump() const override; |
575 | | |
576 | 17.6k | static bool classof(const PathDiagnosticPiece *P) { |
577 | 17.6k | return P->getKind() == Event; |
578 | 17.6k | } |
579 | | }; |
580 | | |
581 | | class PathDiagnosticCallPiece : public PathDiagnosticPiece { |
582 | | const Decl *Caller; |
583 | | const Decl *Callee = nullptr; |
584 | | |
585 | | // Flag signifying that this diagnostic has only call enter and no matching |
586 | | // call exit. |
587 | | bool NoExit; |
588 | | |
589 | | // Flag signifying that the callee function is an Objective-C autosynthesized |
590 | | // property getter or setter. |
591 | | bool IsCalleeAnAutosynthesizedPropertyAccessor = false; |
592 | | |
593 | | // The custom string, which should appear after the call Return Diagnostic. |
594 | | // TODO: Should we allow multiple diagnostics? |
595 | | std::string CallStackMessage; |
596 | | |
597 | | PathDiagnosticCallPiece(const Decl *callerD, |
598 | | const PathDiagnosticLocation &callReturnPos) |
599 | | : PathDiagnosticPiece(Call), Caller(callerD), NoExit(false), |
600 | 6.70k | callReturn(callReturnPos) {} |
601 | | PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller) |
602 | | : PathDiagnosticPiece(Call), Caller(caller), NoExit(true), |
603 | 193 | path(oldPath) {} |
604 | | |
605 | | public: |
606 | | PathDiagnosticLocation callEnter; |
607 | | PathDiagnosticLocation callEnterWithin; |
608 | | PathDiagnosticLocation callReturn; |
609 | | PathPieces path; |
610 | | |
611 | | ~PathDiagnosticCallPiece() override; |
612 | | |
613 | 83 | const Decl *getCaller() const { return Caller; } |
614 | | |
615 | 419 | const Decl *getCallee() const { return Callee; } |
616 | | void setCallee(const CallEnter &CE, const SourceManager &SM); |
617 | | |
618 | 20 | bool hasCallStackMessage() { return !CallStackMessage.empty(); } |
619 | 18 | void setCallStackMessage(StringRef st) { CallStackMessage = st; } |
620 | | |
621 | 7.99k | PathDiagnosticLocation getLocation() const override { return callEnter; } |
622 | | |
623 | | std::shared_ptr<PathDiagnosticEventPiece> getCallEnterEvent() const; |
624 | | std::shared_ptr<PathDiagnosticEventPiece> |
625 | | getCallEnterWithinCallerEvent() const; |
626 | | std::shared_ptr<PathDiagnosticEventPiece> getCallExitEvent() const; |
627 | | |
628 | 417 | void flattenLocations() override { |
629 | 417 | callEnter.flatten(); |
630 | 417 | callReturn.flatten(); |
631 | 417 | for (const auto &I : path) |
632 | 922 | I->flattenLocations(); |
633 | 417 | } |
634 | | |
635 | | static std::shared_ptr<PathDiagnosticCallPiece> |
636 | | construct(const CallExitEnd &CE, |
637 | | const SourceManager &SM); |
638 | | |
639 | | static PathDiagnosticCallPiece *construct(PathPieces &pieces, |
640 | | const Decl *caller); |
641 | | |
642 | | void dump() const override; |
643 | | |
644 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
645 | | |
646 | 66.2k | static bool classof(const PathDiagnosticPiece *P) { |
647 | 66.2k | return P->getKind() == Call; |
648 | 66.2k | } |
649 | | }; |
650 | | |
651 | | class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece { |
652 | | std::vector<PathDiagnosticLocationPair> LPairs; |
653 | | |
654 | | public: |
655 | | PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, |
656 | | const PathDiagnosticLocation &endPos, |
657 | | StringRef s) |
658 | 651 | : PathDiagnosticPiece(s, ControlFlow) { |
659 | 651 | LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); |
660 | 651 | } |
661 | | |
662 | | PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, |
663 | | const PathDiagnosticLocation &endPos) |
664 | 12.3k | : PathDiagnosticPiece(ControlFlow) { |
665 | 12.3k | LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); |
666 | 12.3k | } |
667 | | |
668 | | ~PathDiagnosticControlFlowPiece() override; |
669 | | |
670 | 77.8k | PathDiagnosticLocation getStartLocation() const { |
671 | 77.8k | assert(!LPairs.empty() && |
672 | 77.8k | "PathDiagnosticControlFlowPiece needs at least one location."); |
673 | 77.8k | return LPairs[0].getStart(); |
674 | 77.8k | } |
675 | | |
676 | 54.5k | PathDiagnosticLocation getEndLocation() const { |
677 | 54.5k | assert(!LPairs.empty() && |
678 | 54.5k | "PathDiagnosticControlFlowPiece needs at least one location."); |
679 | 54.5k | return LPairs[0].getEnd(); |
680 | 54.5k | } |
681 | | |
682 | 863 | void setStartLocation(const PathDiagnosticLocation &L) { |
683 | 863 | LPairs[0].setStart(L); |
684 | 863 | } |
685 | | |
686 | 8.78k | void setEndLocation(const PathDiagnosticLocation &L) { |
687 | 8.78k | LPairs[0].setEnd(L); |
688 | 8.78k | } |
689 | | |
690 | 0 | void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); } |
691 | | |
692 | 31.6k | PathDiagnosticLocation getLocation() const override { |
693 | 31.6k | return getStartLocation(); |
694 | 31.6k | } |
695 | | |
696 | | using iterator = std::vector<PathDiagnosticLocationPair>::iterator; |
697 | | |
698 | 2.73k | iterator begin() { return LPairs.begin(); } |
699 | 2.73k | iterator end() { return LPairs.end(); } |
700 | | |
701 | 2.73k | void flattenLocations() override { |
702 | 2.73k | for (auto &I : *this) |
703 | 2.73k | I.flatten(); |
704 | 2.73k | } |
705 | | |
706 | | using const_iterator = |
707 | | std::vector<PathDiagnosticLocationPair>::const_iterator; |
708 | | |
709 | 2.11k | const_iterator begin() const { return LPairs.begin(); } |
710 | 2.11k | const_iterator end() const { return LPairs.end(); } |
711 | | |
712 | 65.3k | static bool classof(const PathDiagnosticPiece *P) { |
713 | 65.3k | return P->getKind() == ControlFlow; |
714 | 65.3k | } |
715 | | |
716 | | void dump() const override; |
717 | | |
718 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
719 | | }; |
720 | | |
721 | | class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece { |
722 | | public: |
723 | | PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos) |
724 | 31 | : PathDiagnosticSpotPiece(pos, "", Macro) {} |
725 | | ~PathDiagnosticMacroPiece() override; |
726 | | |
727 | | PathPieces subPieces; |
728 | | |
729 | | bool containsEvent() const; |
730 | | |
731 | 31 | void flattenLocations() override { |
732 | 31 | PathDiagnosticSpotPiece::flattenLocations(); |
733 | 31 | for (const auto &I : subPieces) |
734 | 71 | I->flattenLocations(); |
735 | 31 | } |
736 | | |
737 | 30.6k | static bool classof(const PathDiagnosticPiece *P) { |
738 | 30.6k | return P->getKind() == Macro; |
739 | 30.6k | } |
740 | | |
741 | | void dump() const override; |
742 | | |
743 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
744 | | }; |
745 | | |
746 | | class PathDiagnosticNotePiece: public PathDiagnosticSpotPiece { |
747 | | public: |
748 | | PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S, |
749 | | bool AddPosRange = true) |
750 | 166 | : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {} |
751 | | ~PathDiagnosticNotePiece() override; |
752 | | |
753 | 16.7k | static bool classof(const PathDiagnosticPiece *P) { |
754 | 16.7k | return P->getKind() == Note; |
755 | 16.7k | } |
756 | | |
757 | | void dump() const override; |
758 | | |
759 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
760 | | }; |
761 | | |
762 | | class PathDiagnosticPopUpPiece: public PathDiagnosticSpotPiece { |
763 | | public: |
764 | | PathDiagnosticPopUpPiece(const PathDiagnosticLocation &Pos, StringRef S, |
765 | | bool AddPosRange = true) |
766 | 836 | : PathDiagnosticSpotPiece(Pos, S, PopUp, AddPosRange) {} |
767 | | ~PathDiagnosticPopUpPiece() override; |
768 | | |
769 | 689 | static bool classof(const PathDiagnosticPiece *P) { |
770 | 689 | return P->getKind() == PopUp; |
771 | 689 | } |
772 | | |
773 | | void dump() const override; |
774 | | |
775 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
776 | | }; |
777 | | |
778 | | /// File IDs mapped to sets of line numbers. |
779 | | using FilesToLineNumsMap = std::map<FileID, std::set<unsigned>>; |
780 | | |
781 | | /// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive |
782 | | /// diagnostic. It represents an ordered-collection of PathDiagnosticPieces, |
783 | | /// each which represent the pieces of the path. |
784 | | class PathDiagnostic : public llvm::FoldingSetNode { |
785 | | std::string CheckName; |
786 | | const Decl *DeclWithIssue; |
787 | | std::string BugType; |
788 | | std::string VerboseDesc; |
789 | | std::string ShortDesc; |
790 | | std::string Category; |
791 | | std::deque<std::string> OtherDesc; |
792 | | |
793 | | /// Loc The location of the path diagnostic report. |
794 | | PathDiagnosticLocation Loc; |
795 | | |
796 | | PathPieces pathImpl; |
797 | | SmallVector<PathPieces *, 3> pathStack; |
798 | | |
799 | | /// Important bug uniqueing location. |
800 | | /// The location info is useful to differentiate between bugs. |
801 | | PathDiagnosticLocation UniqueingLoc; |
802 | | const Decl *UniqueingDecl; |
803 | | |
804 | | /// Lines executed in the path. |
805 | | std::unique_ptr<FilesToLineNumsMap> ExecutedLines; |
806 | | |
807 | | public: |
808 | | PathDiagnostic() = delete; |
809 | | PathDiagnostic(StringRef CheckName, const Decl *DeclWithIssue, |
810 | | StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, |
811 | | StringRef category, PathDiagnosticLocation LocationToUnique, |
812 | | const Decl *DeclToUnique, |
813 | | std::unique_ptr<FilesToLineNumsMap> ExecutedLines); |
814 | | ~PathDiagnostic(); |
815 | | |
816 | | const PathPieces &path; |
817 | | |
818 | | /// Return the path currently used by builders for constructing the |
819 | | /// PathDiagnostic. |
820 | 75.7k | PathPieces &getActivePath() { |
821 | 75.7k | if (pathStack.empty()) |
822 | 62.5k | return pathImpl; |
823 | 13.2k | return *pathStack.back(); |
824 | 13.2k | } |
825 | | |
826 | | /// Return a mutable version of 'path'. |
827 | 21.1k | PathPieces &getMutablePieces() { |
828 | 21.1k | return pathImpl; |
829 | 21.1k | } |
830 | | |
831 | | /// Return the unrolled size of the path. |
832 | | unsigned full_size(); |
833 | | |
834 | 6.70k | void pushActivePath(PathPieces *p) { pathStack.push_back(p); } |
835 | 6.89k | void popActivePath() { if (!pathStack.empty()) pathStack.pop_back()6.70k ; } |
836 | | |
837 | 6.89k | bool isWithinCall() const { return !pathStack.empty(); } |
838 | | |
839 | 11.6k | void setEndOfPath(std::shared_ptr<PathDiagnosticPiece> EndPiece) { |
840 | 11.6k | assert(!Loc.isValid() && "End location already set!"); |
841 | 11.6k | Loc = EndPiece->getLocation(); |
842 | 11.6k | assert(Loc.isValid() && "Invalid location for end-of-path piece"); |
843 | 11.6k | getActivePath().push_back(std::move(EndPiece)); |
844 | 11.6k | } |
845 | | |
846 | 2 | void appendToDesc(StringRef S) { |
847 | 2 | if (!ShortDesc.empty()) |
848 | 0 | ShortDesc += S; |
849 | 2 | VerboseDesc += S; |
850 | 2 | } |
851 | | |
852 | | /// If the last piece of the report point to the header file, resets |
853 | | /// the location of the report to be the last location in the main source |
854 | | /// file. |
855 | | void resetDiagnosticLocationToMainFile(); |
856 | | |
857 | 538 | StringRef getVerboseDescription() const { return VerboseDesc; } |
858 | | |
859 | 11.5k | StringRef getShortDescription() const { |
860 | 11.5k | return ShortDesc.empty() ? VerboseDesc11.5k : ShortDesc48 ; |
861 | 11.5k | } |
862 | | |
863 | 1.27k | StringRef getCheckName() const { return CheckName; } |
864 | 1.64k | StringRef getBugType() const { return BugType; } |
865 | 863 | StringRef getCategory() const { return Category; } |
866 | | |
867 | | /// Return the semantic context where an issue occurred. If the |
868 | | /// issue occurs along a path, this represents the "central" area |
869 | | /// where the bug manifests. |
870 | 1.35k | const Decl *getDeclWithIssue() const { return DeclWithIssue; } |
871 | | |
872 | | using meta_iterator = std::deque<std::string>::const_iterator; |
873 | | |
874 | 93 | meta_iterator meta_begin() const { return OtherDesc.begin(); } |
875 | 93 | meta_iterator meta_end() const { return OtherDesc.end(); } |
876 | 0 | void addMeta(StringRef s) { OtherDesc.push_back(s); } |
877 | | |
878 | 671 | const FilesToLineNumsMap &getExecutedLines() const { |
879 | 671 | return *ExecutedLines; |
880 | 671 | } |
881 | | |
882 | 11.6k | FilesToLineNumsMap &getExecutedLines() { |
883 | 11.6k | return *ExecutedLines; |
884 | 11.6k | } |
885 | | |
886 | 175k | PathDiagnosticLocation getLocation() const { |
887 | 175k | return Loc; |
888 | 175k | } |
889 | | |
890 | | /// Get the location on which the report should be uniqued. |
891 | 671 | PathDiagnosticLocation getUniqueingLoc() const { |
892 | 671 | return UniqueingLoc; |
893 | 671 | } |
894 | | |
895 | | /// Get the declaration containing the uniqueing location. |
896 | 191 | const Decl *getUniqueingDecl() const { |
897 | 191 | return UniqueingDecl; |
898 | 191 | } |
899 | | |
900 | 11.6k | void flattenLocations() { |
901 | 11.6k | Loc.flatten(); |
902 | 11.6k | for (const auto &I : pathImpl) |
903 | 16.4k | I->flattenLocations(); |
904 | 11.6k | } |
905 | | |
906 | | /// Profiles the diagnostic, independent of the path it references. |
907 | | /// |
908 | | /// This can be used to merge diagnostics that refer to the same issue |
909 | | /// along different paths. |
910 | | void Profile(llvm::FoldingSetNodeID &ID) const; |
911 | | |
912 | | /// Profiles the diagnostic, including its path. |
913 | | /// |
914 | | /// Two diagnostics with the same issue along different paths will generate |
915 | | /// different profiles. |
916 | | void FullProfile(llvm::FoldingSetNodeID &ID) const; |
917 | | }; |
918 | | |
919 | | } // namespace ento |
920 | | |
921 | | } // namespace clang |
922 | | |
923 | | #endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H |