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