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(L: SourceLocation(), LAC: lac)), Range(genRange(LAC: 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, }; |
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(x: R); |
461 | } |
462 | |
463 | void addRange(SourceLocation B, SourceLocation E) { |
464 | if (!B.isValid() || !E.isValid()) |
465 | return; |
466 | ranges.push_back(x: SourceRange(B,E)); |
467 | } |
468 | |
469 | void addFixit(FixItHint F) { |
470 | fixits.push_back(x: 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(Primary&: Result, Current&: 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(R: 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(u: 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(x: PathDiagnosticLocationPair(startPos, endPos)); |
641 | } |
642 | |
643 | PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, |
644 | const PathDiagnosticLocation &endPos) |
645 | : PathDiagnosticPiece(ControlFlow) { |
646 | LPairs.push_back(x: 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: 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 : public PathDiagnosticSpotPiece { |
742 | public: |
743 | (const PathDiagnosticLocation &Pos, StringRef S, |
744 | bool AddPosRange = true) |
745 | : PathDiagnosticSpotPiece(Pos, S, PopUp, AddPosRange) {} |
746 | () override; |
747 | |
748 | static bool (const PathDiagnosticPiece *P) { |
749 | return P->getKind() == PopUp; |
750 | } |
751 | |
752 | void () const override; |
753 | |
754 | void (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(Elt: 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(x: 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(x: 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 |
905 | |