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