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