/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/include/clang/Analysis/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_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/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 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 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 | 77 | 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 | 8 | 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 | 633 | 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 | 2.01k | 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 | 222 | virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } |
157 | | |
158 | 19.3k | bool shouldGenerateDiagnostics() const { |
159 | 19.3k | return getGenerationScheme() != None; |
160 | 19.3k | } |
161 | | |
162 | 162k | bool shouldAddPathEdges() const { return getGenerationScheme() >= Extensive; } |
163 | 17.9k | bool shouldAddControlNotes() const { |
164 | 17.9k | return getGenerationScheme() == Minimal || |
165 | 17.9k | getGenerationScheme() == Everything2.25k ; |
166 | 17.9k | } |
167 | | |
168 | 0 | virtual bool supportsLogicalOpControlFlow() const { return false; } |
169 | | |
170 | | /// Return true if the PathDiagnosticConsumer supports individual |
171 | | /// PathDiagnostics that span multiple files. |
172 | 24 | 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 | 132k | : SourceRange(R), isPoint(isP) {} |
189 | 77.0k | 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 | 17.5k | : 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 | 77.0k | 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 | 113k | Loc(genLocation(SourceLocation(), lac)), Range(genRange(lac)) { |
225 | 113k | assert(K == SingleLocK || S); |
226 | 0 | assert(K == SingleLocK || Loc.isValid()); |
227 | 0 | assert(K == SingleLocK || Range.isValid()); |
228 | 113k | } |
229 | | |
230 | | /// Create a location corresponding to the given declaration. |
231 | | PathDiagnosticLocation(const Decl *d, const SourceManager &sm) |
232 | 309 | : K(DeclK), D(d), SM(&sm), Loc(genLocation()), Range(genRange()) { |
233 | 309 | assert(D); |
234 | 0 | assert(Loc.isValid()); |
235 | 0 | assert(Range.isValid()); |
236 | 309 | } |
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 | 303 | : SM(&sm), Loc(loc, sm), Range(genRange()) { |
243 | 303 | assert(Loc.isValid()); |
244 | 0 | assert(Range.isValid()); |
245 | 303 | } |
246 | | |
247 | | /// Create a location corresponding to the given declaration. |
248 | | static PathDiagnosticLocation create(const Decl *D, |
249 | 280 | const SourceManager &SM) { |
250 | 280 | return PathDiagnosticLocation(D, SM); |
251 | 280 | } |
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 | 0 | const LocationOrAnalysisDeclContext LAC) { |
263 | 0 | return createBegin(D, SM); |
264 | 0 | } |
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 | 3.92k | bool operator==(const PathDiagnosticLocation &X) const { |
328 | 3.92k | return K == X.K && Loc == X.Loc3.71k && Range == X.Range1.89k ; |
329 | 3.92k | } |
330 | | |
331 | 2.12k | bool operator!=(const PathDiagnosticLocation &X) const { |
332 | 2.12k | return !(*this == X); |
333 | 2.12k | } |
334 | | |
335 | 899k | bool isValid() const { |
336 | 899k | return SM != nullptr; |
337 | 899k | } |
338 | | |
339 | 594k | FullSourceLoc asLocation() const { |
340 | 594k | return Loc; |
341 | 594k | } |
342 | | |
343 | 36.2k | PathDiagnosticRange asRange() const { |
344 | 36.2k | return Range; |
345 | 36.2k | } |
346 | | |
347 | 307k | const Stmt *asStmt() const { assert(isValid()); return S; } |
348 | 98.8k | const Stmt *getStmtOrNull() const { |
349 | 98.8k | if (!isValid()) |
350 | 0 | return nullptr; |
351 | 98.8k | return asStmt(); |
352 | 98.8k | } |
353 | | |
354 | 0 | const Decl *asDecl() const { assert(isValid()); return D; } |
355 | | |
356 | 34.6k | bool hasRange() const { return K == StmtK || K == RangeK4.74k || K == DeclK3.87k ; } |
357 | | |
358 | 36.8k | bool hasValidLocation() const { return asLocation().isValid(); } |
359 | | |
360 | 256 | void invalidate() { |
361 | 256 | *this = PathDiagnosticLocation(); |
362 | 256 | } |
363 | | |
364 | | void flatten(); |
365 | | |
366 | 646 | 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 | 16.0k | : Start(start), End(end) {} |
381 | | |
382 | 97.5k | const PathDiagnosticLocation &getStart() const { return Start; } |
383 | 68.6k | const PathDiagnosticLocation &getEnd() const { return End; } |
384 | | |
385 | 1.03k | void setStart(const PathDiagnosticLocation &L) { Start = L; } |
386 | 10.5k | void setEnd(const PathDiagnosticLocation &L) { End = L; } |
387 | | |
388 | 3.59k | void flatten() { |
389 | 3.59k | Start.flatten(); |
390 | 3.59k | End.flatten(); |
391 | 3.59k | } |
392 | | |
393 | 0 | void Profile(llvm::FoldingSetNodeID &ID) const { |
394 | 0 | Start.Profile(ID); |
395 | 0 | End.Profile(ID); |
396 | 0 | } |
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 | 13.3k | StringRef getString() const { return str; } |
437 | | |
438 | | /// Tag this PathDiagnosticPiece with the given C-string. |
439 | 7.85k | void setTag(const char *tag) { Tag = tag; } |
440 | | |
441 | | /// Return the opaque tag (if any) on the PathDiagnosticPiece. |
442 | 331 | const void *getTag() const { return Tag.data(); } |
443 | | |
444 | | /// Return the string representation of the tag. This is useful |
445 | | /// for debugging. |
446 | 14 | StringRef getTagStr() const { return Tag; } |
447 | | |
448 | | /// getDisplayHint - Return a hint indicating where the diagnostic should |
449 | | /// be displayed by the PathDiagnosticConsumer. |
450 | 3.54k | DisplayHint getDisplayHint() const { return Hint; } |
451 | | |
452 | | virtual PathDiagnosticLocation getLocation() const = 0; |
453 | | virtual void flattenLocations() = 0; |
454 | | |
455 | 343k | Kind getKind() const { return kind; } |
456 | | |
457 | 51.0k | void addRange(SourceRange R) { |
458 | 51.0k | if (!R.isValid()) |
459 | 6 | return; |
460 | 51.0k | ranges.push_back(R); |
461 | 51.0k | } |
462 | | |
463 | 0 | void addRange(SourceLocation B, SourceLocation E) { |
464 | 0 | if (!B.isValid() || !E.isValid()) |
465 | 0 | return; |
466 | 0 | ranges.push_back(SourceRange(B,E)); |
467 | 0 | } |
468 | | |
469 | 13 | void addFixit(FixItHint F) { |
470 | 13 | fixits.push_back(F); |
471 | 13 | } |
472 | | |
473 | | /// Return the SourceRanges associated with this PathDiagnosticPiece. |
474 | 38.9k | ArrayRef<SourceRange> getRanges() const { return ranges; } |
475 | | |
476 | | /// Return the fix-it hints associated with this PathDiagnosticPiece. |
477 | 29.6k | ArrayRef<FixItHint> getFixits() const { return fixits; } |
478 | | |
479 | | virtual void Profile(llvm::FoldingSetNodeID &ID) const; |
480 | | |
481 | 2 | void setAsLastInMainSourceFile() { |
482 | 2 | LastInMainSourceFile = true; |
483 | 2 | } |
484 | | |
485 | 152 | bool isLastInMainSourceFile() const { |
486 | 152 | return LastInMainSourceFile; |
487 | 152 | } |
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 | 22.2k | PathPieces flatten(bool ShouldFlattenMacros) const { |
500 | 22.2k | PathPieces Result; |
501 | 22.2k | flattenTo(Result, Result, ShouldFlattenMacros); |
502 | 22.2k | return Result; |
503 | 22.2k | } |
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 | 36.7k | : PathDiagnosticPiece(s, k), Pos(pos) { |
518 | 36.7k | assert(Pos.isValid() && Pos.hasValidLocation() && |
519 | 36.7k | "PathDiagnosticSpotPiece's must have a valid location."); |
520 | 36.7k | if (addPosRange && Pos.hasRange()34.6k ) addRange(Pos.asRange())31.1k ; |
521 | 36.7k | } |
522 | | |
523 | 72.5k | PathDiagnosticLocation getLocation() const override { return Pos; } |
524 | 24.4k | void flattenLocations() override { Pos.flatten(); } |
525 | | |
526 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
527 | | |
528 | 0 | static bool classof(const PathDiagnosticPiece *P) { |
529 | 0 | return P->getKind() == Event || P->getKind() == Macro || |
530 | 0 | P->getKind() == Note || P->getKind() == PopUp; |
531 | 0 | } |
532 | | }; |
533 | | |
534 | | class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece { |
535 | | Optional<bool> IsPrunable; |
536 | | |
537 | | public: |
538 | | PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, |
539 | | StringRef s, bool addPosRange = true) |
540 | 35.1k | : 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 | 7.70k | void setPrunable(bool isPrunable, bool override = false) { |
547 | 7.70k | if (IsPrunable.hasValue() && !override469 ) |
548 | 469 | return; |
549 | 7.23k | IsPrunable = isPrunable; |
550 | 7.23k | } |
551 | | |
552 | | /// Return true if the diagnostic piece is prunable. |
553 | 5.55k | bool isPrunable() const { |
554 | 5.55k | return IsPrunable.getValueOr(false); |
555 | 5.55k | } |
556 | | |
557 | | void dump() const override; |
558 | | |
559 | 21.3k | static bool classof(const PathDiagnosticPiece *P) { |
560 | 21.3k | return P->getKind() == Event; |
561 | 21.3k | } |
562 | | }; |
563 | | |
564 | | class PathDiagnosticCallPiece : public PathDiagnosticPiece { |
565 | | const Decl *Caller; |
566 | | const Decl *Callee = nullptr; |
567 | | |
568 | | // Flag signifying that this diagnostic has only call enter and no matching |
569 | | // call exit. |
570 | | bool NoExit; |
571 | | |
572 | | // Flag signifying that the callee function is an Objective-C autosynthesized |
573 | | // property getter or setter. |
574 | | bool IsCalleeAnAutosynthesizedPropertyAccessor = false; |
575 | | |
576 | | // The custom string, which should appear after the call Return Diagnostic. |
577 | | // TODO: Should we allow multiple diagnostics? |
578 | | std::string CallStackMessage; |
579 | | |
580 | | PathDiagnosticCallPiece(const Decl *callerD, |
581 | | const PathDiagnosticLocation &callReturnPos) |
582 | | : PathDiagnosticPiece(Call), Caller(callerD), NoExit(false), |
583 | 6.92k | callReturn(callReturnPos) {} |
584 | | PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller) |
585 | | : PathDiagnosticPiece(Call), Caller(caller), NoExit(true), |
586 | 180 | path(oldPath) {} |
587 | | |
588 | | public: |
589 | | PathDiagnosticLocation callEnter; |
590 | | PathDiagnosticLocation callEnterWithin; |
591 | | PathDiagnosticLocation callReturn; |
592 | | PathPieces path; |
593 | | |
594 | | ~PathDiagnosticCallPiece() override; |
595 | | |
596 | 66 | const Decl *getCaller() const { return Caller; } |
597 | | |
598 | 469 | const Decl *getCallee() const { return Callee; } |
599 | | void setCallee(const CallEnter &CE, const SourceManager &SM); |
600 | | |
601 | 21 | bool hasCallStackMessage() { return !CallStackMessage.empty(); } |
602 | 19 | void setCallStackMessage(StringRef st) { CallStackMessage = std::string(st); } |
603 | | |
604 | 8.32k | PathDiagnosticLocation getLocation() const override { return callEnter; } |
605 | | |
606 | | std::shared_ptr<PathDiagnosticEventPiece> getCallEnterEvent() const; |
607 | | std::shared_ptr<PathDiagnosticEventPiece> |
608 | | getCallEnterWithinCallerEvent() const; |
609 | | std::shared_ptr<PathDiagnosticEventPiece> getCallExitEvent() const; |
610 | | |
611 | 467 | void flattenLocations() override { |
612 | 467 | callEnter.flatten(); |
613 | 467 | callReturn.flatten(); |
614 | 467 | for (const auto &I : path) |
615 | 1.09k | I->flattenLocations(); |
616 | 467 | } |
617 | | |
618 | | static std::shared_ptr<PathDiagnosticCallPiece> |
619 | | construct(const CallExitEnd &CE, |
620 | | const SourceManager &SM); |
621 | | |
622 | | static PathDiagnosticCallPiece *construct(PathPieces &pieces, |
623 | | const Decl *caller); |
624 | | |
625 | | void dump() const override; |
626 | | |
627 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
628 | | |
629 | 82.3k | static bool classof(const PathDiagnosticPiece *P) { |
630 | 82.3k | return P->getKind() == Call; |
631 | 82.3k | } |
632 | | }; |
633 | | |
634 | | class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece { |
635 | | std::vector<PathDiagnosticLocationPair> LPairs; |
636 | | |
637 | | public: |
638 | | PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, |
639 | | const PathDiagnosticLocation &endPos, |
640 | | StringRef s) |
641 | 1.09k | : PathDiagnosticPiece(s, ControlFlow) { |
642 | 1.09k | LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); |
643 | 1.09k | } |
644 | | |
645 | | PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, |
646 | | const PathDiagnosticLocation &endPos) |
647 | 14.9k | : PathDiagnosticPiece(ControlFlow) { |
648 | 14.9k | LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); |
649 | 14.9k | } |
650 | | |
651 | | ~PathDiagnosticControlFlowPiece() override; |
652 | | |
653 | 95.0k | PathDiagnosticLocation getStartLocation() const { |
654 | 95.0k | assert(!LPairs.empty() && |
655 | 95.0k | "PathDiagnosticControlFlowPiece needs at least one location."); |
656 | 0 | return LPairs[0].getStart(); |
657 | 95.0k | } |
658 | | |
659 | 66.1k | PathDiagnosticLocation getEndLocation() const { |
660 | 66.1k | assert(!LPairs.empty() && |
661 | 66.1k | "PathDiagnosticControlFlowPiece needs at least one location."); |
662 | 0 | return LPairs[0].getEnd(); |
663 | 66.1k | } |
664 | | |
665 | 1.03k | void setStartLocation(const PathDiagnosticLocation &L) { |
666 | 1.03k | LPairs[0].setStart(L); |
667 | 1.03k | } |
668 | | |
669 | 10.5k | void setEndLocation(const PathDiagnosticLocation &L) { |
670 | 10.5k | LPairs[0].setEnd(L); |
671 | 10.5k | } |
672 | | |
673 | 0 | void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); } |
674 | | |
675 | 38.9k | PathDiagnosticLocation getLocation() const override { |
676 | 38.9k | return getStartLocation(); |
677 | 38.9k | } |
678 | | |
679 | | using iterator = std::vector<PathDiagnosticLocationPair>::iterator; |
680 | | |
681 | 3.59k | iterator begin() { return LPairs.begin(); } |
682 | 3.59k | iterator end() { return LPairs.end(); } |
683 | | |
684 | 3.59k | void flattenLocations() override { |
685 | 3.59k | for (auto &I : *this) |
686 | 3.59k | I.flatten(); |
687 | 3.59k | } |
688 | | |
689 | | using const_iterator = |
690 | | std::vector<PathDiagnosticLocationPair>::const_iterator; |
691 | | |
692 | 2.53k | const_iterator begin() const { return LPairs.begin(); } |
693 | 2.53k | const_iterator end() const { return LPairs.end(); } |
694 | | |
695 | 82.5k | static bool classof(const PathDiagnosticPiece *P) { |
696 | 82.5k | return P->getKind() == ControlFlow; |
697 | 82.5k | } |
698 | | |
699 | | void dump() const override; |
700 | | |
701 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
702 | | }; |
703 | | |
704 | | class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece { |
705 | | public: |
706 | | PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos) |
707 | 46 | : PathDiagnosticSpotPiece(pos, "", Macro) {} |
708 | | ~PathDiagnosticMacroPiece() override; |
709 | | |
710 | | PathPieces subPieces; |
711 | | |
712 | 46 | void flattenLocations() override { |
713 | 46 | PathDiagnosticSpotPiece::flattenLocations(); |
714 | 46 | for (const auto &I : subPieces) |
715 | 94 | I->flattenLocations(); |
716 | 46 | } |
717 | | |
718 | 38.3k | static bool classof(const PathDiagnosticPiece *P) { |
719 | 38.3k | return P->getKind() == Macro; |
720 | 38.3k | } |
721 | | |
722 | | void dump() const override; |
723 | | |
724 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
725 | | }; |
726 | | |
727 | | class PathDiagnosticNotePiece: public PathDiagnosticSpotPiece { |
728 | | public: |
729 | | PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S, |
730 | | bool AddPosRange = true) |
731 | 227 | : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {} |
732 | | ~PathDiagnosticNotePiece() override; |
733 | | |
734 | 30.7k | static bool classof(const PathDiagnosticPiece *P) { |
735 | 30.7k | return P->getKind() == Note; |
736 | 30.7k | } |
737 | | |
738 | | void dump() const override; |
739 | | |
740 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
741 | | }; |
742 | | |
743 | | class PathDiagnosticPopUpPiece: public PathDiagnosticSpotPiece { |
744 | | public: |
745 | | PathDiagnosticPopUpPiece(const PathDiagnosticLocation &Pos, StringRef S, |
746 | | bool AddPosRange = true) |
747 | 1.29k | : PathDiagnosticSpotPiece(Pos, S, PopUp, AddPosRange) {} |
748 | | ~PathDiagnosticPopUpPiece() override; |
749 | | |
750 | 1.93k | static bool classof(const PathDiagnosticPiece *P) { |
751 | 1.93k | return P->getKind() == PopUp; |
752 | 1.93k | } |
753 | | |
754 | | void dump() const override; |
755 | | |
756 | | void Profile(llvm::FoldingSetNodeID &ID) const override; |
757 | | }; |
758 | | |
759 | | /// File IDs mapped to sets of line numbers. |
760 | | using FilesToLineNumsMap = std::map<FileID, std::set<unsigned>>; |
761 | | |
762 | | /// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive |
763 | | /// diagnostic. It represents an ordered-collection of PathDiagnosticPieces, |
764 | | /// each which represent the pieces of the path. |
765 | | class PathDiagnostic : public llvm::FoldingSetNode { |
766 | | std::string CheckerName; |
767 | | const Decl *DeclWithIssue; |
768 | | std::string BugType; |
769 | | std::string VerboseDesc; |
770 | | std::string ShortDesc; |
771 | | std::string Category; |
772 | | std::deque<std::string> OtherDesc; |
773 | | |
774 | | /// Loc The location of the path diagnostic report. |
775 | | PathDiagnosticLocation Loc; |
776 | | |
777 | | PathPieces pathImpl; |
778 | | SmallVector<PathPieces *, 3> pathStack; |
779 | | |
780 | | /// Important bug uniqueing location. |
781 | | /// The location info is useful to differentiate between bugs. |
782 | | PathDiagnosticLocation UniqueingLoc; |
783 | | const Decl *UniqueingDecl; |
784 | | |
785 | | /// Lines executed in the path. |
786 | | std::unique_ptr<FilesToLineNumsMap> ExecutedLines; |
787 | | |
788 | | public: |
789 | | PathDiagnostic() = delete; |
790 | | PathDiagnostic(StringRef CheckerName, const Decl *DeclWithIssue, |
791 | | StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, |
792 | | StringRef category, PathDiagnosticLocation LocationToUnique, |
793 | | const Decl *DeclToUnique, |
794 | | std::unique_ptr<FilesToLineNumsMap> ExecutedLines); |
795 | | ~PathDiagnostic(); |
796 | | |
797 | | const PathPieces &path; |
798 | | |
799 | | /// Return the path currently used by builders for constructing the |
800 | | /// PathDiagnostic. |
801 | 303k | PathPieces &getActivePath() { |
802 | 303k | if (pathStack.empty()) |
803 | 163k | return pathImpl; |
804 | 140k | return *pathStack.back(); |
805 | 303k | } |
806 | | |
807 | | /// Return a mutable version of 'path'. |
808 | 33.7k | PathPieces &getMutablePieces() { |
809 | 33.7k | return pathImpl; |
810 | 33.7k | } |
811 | | |
812 | | /// Return the unrolled size of the path. |
813 | | unsigned full_size(); |
814 | | |
815 | 6.92k | void pushActivePath(PathPieces *p) { pathStack.push_back(p); } |
816 | 7.10k | void popActivePath() { if (!pathStack.empty()) pathStack.pop_back()6.92k ; } |
817 | | |
818 | 7.10k | bool isWithinCall() const { return !pathStack.empty(); } |
819 | | |
820 | 20.6k | void setEndOfPath(PathDiagnosticPieceRef EndPiece) { |
821 | 20.6k | assert(!Loc.isValid() && "End location already set!"); |
822 | 0 | Loc = EndPiece->getLocation(); |
823 | 20.6k | assert(Loc.isValid() && "Invalid location for end-of-path piece"); |
824 | 0 | getActivePath().push_back(std::move(EndPiece)); |
825 | 20.6k | } |
826 | | |
827 | 2 | void appendToDesc(StringRef S) { |
828 | 2 | if (!ShortDesc.empty()) |
829 | 2 | ShortDesc += S; |
830 | 2 | VerboseDesc += S; |
831 | 2 | } |
832 | | |
833 | 1.95k | StringRef getVerboseDescription() const { return VerboseDesc; } |
834 | | |
835 | 20.5k | StringRef getShortDescription() const { |
836 | 20.5k | return ShortDesc.empty() ? VerboseDesc1.85k : ShortDesc18.6k ; |
837 | 20.5k | } |
838 | | |
839 | 19.1k | StringRef getCheckerName() const { return CheckerName; } |
840 | 2.47k | StringRef getBugType() const { return BugType; } |
841 | 1.62k | StringRef getCategory() const { return Category; } |
842 | | |
843 | | using meta_iterator = std::deque<std::string>::const_iterator; |
844 | | |
845 | 132 | meta_iterator meta_begin() const { return OtherDesc.begin(); } |
846 | 132 | meta_iterator meta_end() const { return OtherDesc.end(); } |
847 | 0 | void addMeta(StringRef s) { OtherDesc.push_back(std::string(s)); } |
848 | | |
849 | 737 | const FilesToLineNumsMap &getExecutedLines() const { |
850 | 737 | return *ExecutedLines; |
851 | 737 | } |
852 | | |
853 | 20.6k | FilesToLineNumsMap &getExecutedLines() { |
854 | 20.6k | return *ExecutedLines; |
855 | 20.6k | } |
856 | | |
857 | | /// Return the semantic context where an issue occurred. If the |
858 | | /// issue occurs along a path, this represents the "central" area |
859 | | /// where the bug manifests. |
860 | 1.60k | const Decl *getDeclWithIssue() const { return DeclWithIssue; } |
861 | | |
862 | 2 | void setDeclWithIssue(const Decl *D) { |
863 | 2 | DeclWithIssue = D; |
864 | 2 | } |
865 | | |
866 | 493k | PathDiagnosticLocation getLocation() const { |
867 | 493k | return Loc; |
868 | 493k | } |
869 | | |
870 | 2 | void setLocation(PathDiagnosticLocation NewLoc) { |
871 | 2 | Loc = NewLoc; |
872 | 2 | } |
873 | | |
874 | | /// Get the location on which the report should be uniqued. |
875 | 80.8k | PathDiagnosticLocation getUniqueingLoc() const { |
876 | 80.8k | return UniqueingLoc; |
877 | 80.8k | } |
878 | | |
879 | | /// Get the declaration containing the uniqueing location. |
880 | 209 | const Decl *getUniqueingDecl() const { |
881 | 209 | return UniqueingDecl; |
882 | 209 | } |
883 | | |
884 | 20.6k | void flattenLocations() { |
885 | 20.6k | Loc.flatten(); |
886 | 20.6k | for (const auto &I : pathImpl) |
887 | 27.2k | I->flattenLocations(); |
888 | 20.6k | } |
889 | | |
890 | | /// Profiles the diagnostic, independent of the path it references. |
891 | | /// |
892 | | /// This can be used to merge diagnostics that refer to the same issue |
893 | | /// along different paths. |
894 | | void Profile(llvm::FoldingSetNodeID &ID) const; |
895 | | |
896 | | /// Profiles the diagnostic, including its path. |
897 | | /// |
898 | | /// Two diagnostics with the same issue along different paths will generate |
899 | | /// different profiles. |
900 | | void FullProfile(llvm::FoldingSetNodeID &ID) const; |
901 | | }; |
902 | | |
903 | | } // namespace ento |
904 | | } // namespace clang |
905 | | |
906 | | #endif // LLVM_CLANG_ANALYSIS_PATHDIAGNOSTIC_H |