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