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 
38 namespace clang {
39 
40 class AnalysisDeclContext;
41 class BinaryOperator;
42 class CallEnter;
43 class CallExitEnd;
44 class CallExpr;
45 class ConditionalOperator;
46 class Decl;
47 class Expr;
48 class LocationContext;
49 class MemberExpr;
50 class ProgramPoint;
51 class SourceManager;
52 
53 namespace ento {
54 
55 //===----------------------------------------------------------------------===//
56 // High-level interface for handlers of path-sensitive diagnostics.
57 //===----------------------------------------------------------------------===//
58 
59 class PathDiagnostic;
60 
61 /// These options tweak the behavior of path diangostic consumers.
62 /// Most of these options are currently supported by very few consumers.
63 struct 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 
100 class PathDiagnosticConsumer {
101 public:
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 
134 private:
135   virtual void anchor();
136 
137 public:
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 
175 protected:
176   bool flushed = false;
177   llvm::FoldingSet<PathDiagnostic> Diags;
178 };
179 
180 //===----------------------------------------------------------------------===//
181 // Path-sensitive diagnostics.
182 //===----------------------------------------------------------------------===//
183 
184 class PathDiagnosticRange : public SourceRange {
185 public:
186   bool isPoint = false;
187 
188   PathDiagnosticRange(SourceRange R, bool isP = false)
189       : SourceRange(R), isPoint(isP) {}
190   PathDiagnosticRange() = default;
191 };
192 
193 using LocationOrAnalysisDeclContext =
194     llvm::PointerUnion<const LocationContext *, AnalysisDeclContext *>;
195 
196 class PathDiagnosticLocation {
197 private:
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 
216 public:
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 
374 class PathDiagnosticLocationPair {
375 private:
376   PathDiagnosticLocation Start, End;
377 
378 public:
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 
404 class PathDiagnosticPiece: public llvm::FoldingSetNode {
405 public:
406   enum Kind { ControlFlow, Event, Macro, Call, Note, PopUp };
407   enum DisplayHint { Above, Below };
408 
409 private:
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 
427 protected:
428   PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below);
429   PathDiagnosticPiece(Kind k, DisplayHint hint = Below);
430 
431 public:
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 
493 using PathDiagnosticPieceRef = std::shared_ptr<PathDiagnosticPiece>;
494 
495 class PathPieces : public std::list<PathDiagnosticPieceRef> {
496   void flattenTo(PathPieces &Primary, PathPieces &Current,
497                  bool ShouldFlattenMacros) const;
498 
499 public:
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 
509 class PathDiagnosticSpotPiece : public PathDiagnosticPiece {
510 private:
511   PathDiagnosticLocation Pos;
512 
513 public:
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 
535 class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece {
536   Optional<bool> IsPrunable;
537 
538 public:
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 
565 class 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 
589 public:
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 
635 class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece {
636   std::vector<PathDiagnosticLocationPair> LPairs;
637 
638 public:
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 
705 class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece {
706 public:
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 
728 class PathDiagnosticNotePiece: public PathDiagnosticSpotPiece {
729 public:
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 
744 class PathDiagnosticPopUpPiece: public PathDiagnosticSpotPiece {
745 public:
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.
761 using 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.
766 class 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 
789 public:
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