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