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