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