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