1353942Sdim//===- PathDiagnostic.h - Path-Specific Diagnostic Handling -----*- C++ -*-===//
2353942Sdim//
3353942Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353942Sdim// See https://llvm.org/LICENSE.txt for license information.
5353942Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353942Sdim//
7353942Sdim//===----------------------------------------------------------------------===//
8353942Sdim//
9353942Sdim//  This file defines the PathDiagnostic-related interfaces.
10353942Sdim//
11353942Sdim//===----------------------------------------------------------------------===//
12353942Sdim
13353942Sdim#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H
14353942Sdim#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H
15353942Sdim
16353942Sdim#include "clang/AST/Stmt.h"
17353942Sdim#include "clang/Analysis/AnalysisDeclContext.h"
18353942Sdim#include "clang/Basic/LLVM.h"
19353942Sdim#include "clang/Basic/SourceLocation.h"
20353942Sdim#include "llvm/ADT/ArrayRef.h"
21353942Sdim#include "llvm/ADT/FoldingSet.h"
22353942Sdim#include "llvm/ADT/Optional.h"
23353942Sdim#include "llvm/ADT/PointerUnion.h"
24353942Sdim#include "llvm/ADT/SmallVector.h"
25353942Sdim#include "llvm/ADT/StringRef.h"
26353942Sdim#include "llvm/Support/Allocator.h"
27353942Sdim#include <cassert>
28353942Sdim#include <deque>
29353942Sdim#include <iterator>
30353942Sdim#include <list>
31353942Sdim#include <map>
32353942Sdim#include <memory>
33353942Sdim#include <set>
34353942Sdim#include <string>
35353942Sdim#include <utility>
36353942Sdim#include <vector>
37353942Sdim
38353942Sdimnamespace clang {
39353942Sdim
40353942Sdimclass AnalysisDeclContext;
41353942Sdimclass BinaryOperator;
42353942Sdimclass CallEnter;
43353942Sdimclass CallExitEnd;
44353942Sdimclass CallExpr;
45353942Sdimclass ConditionalOperator;
46353942Sdimclass Decl;
47353942Sdimclass Expr;
48353942Sdimclass LocationContext;
49353942Sdimclass MemberExpr;
50353942Sdimclass ProgramPoint;
51353942Sdimclass SourceManager;
52353942Sdim
53353942Sdimnamespace ento {
54353942Sdim
55353942Sdim//===----------------------------------------------------------------------===//
56353942Sdim// High-level interface for handlers of path-sensitive diagnostics.
57353942Sdim//===----------------------------------------------------------------------===//
58353942Sdim
59353942Sdimclass PathDiagnostic;
60353942Sdim
61353942Sdimclass PathDiagnosticConsumer {
62353942Sdimpublic:
63353942Sdim  class PDFileEntry : public llvm::FoldingSetNode {
64353942Sdim  public:
65353942Sdim    PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {}
66353942Sdim
67353942Sdim    using ConsumerFiles = std::vector<std::pair<StringRef, StringRef>>;
68353942Sdim
69353942Sdim    /// A vector of <consumer,file> pairs.
70353942Sdim    ConsumerFiles files;
71353942Sdim
72353942Sdim    /// A precomputed hash tag used for uniquing PDFileEntry objects.
73353942Sdim    const llvm::FoldingSetNodeID NodeID;
74353942Sdim
75353942Sdim    /// Used for profiling in the FoldingSet.
76353942Sdim    void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; }
77353942Sdim  };
78353942Sdim
79353942Sdim  class FilesMade {
80353942Sdim    llvm::BumpPtrAllocator Alloc;
81353942Sdim    llvm::FoldingSet<PDFileEntry> Set;
82353942Sdim
83353942Sdim  public:
84353942Sdim    ~FilesMade();
85353942Sdim
86353942Sdim    bool empty() const { return Set.empty(); }
87353942Sdim
88353942Sdim    void addDiagnostic(const PathDiagnostic &PD,
89353942Sdim                       StringRef ConsumerName,
90353942Sdim                       StringRef fileName);
91353942Sdim
92353942Sdim    PDFileEntry::ConsumerFiles *getFiles(const PathDiagnostic &PD);
93353942Sdim  };
94353942Sdim
95353942Sdimprivate:
96353942Sdim  virtual void anchor();
97353942Sdim
98353942Sdimpublic:
99353942Sdim  PathDiagnosticConsumer() = default;
100353942Sdim  virtual ~PathDiagnosticConsumer();
101353942Sdim
102353942Sdim  void FlushDiagnostics(FilesMade *FilesMade);
103353942Sdim
104353942Sdim  virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
105353942Sdim                                    FilesMade *filesMade) = 0;
106353942Sdim
107353942Sdim  virtual StringRef getName() const = 0;
108353942Sdim
109353942Sdim  void HandlePathDiagnostic(std::unique_ptr<PathDiagnostic> D);
110353942Sdim
111353942Sdim  enum PathGenerationScheme {
112353942Sdim    /// Only runs visitors, no output generated.
113353942Sdim    None,
114353942Sdim
115353942Sdim    /// Used for HTML, SARIF, and text output.
116353942Sdim    Minimal,
117353942Sdim
118353942Sdim    /// Used for plist output, used for "arrows" generation.
119353942Sdim    Extensive,
120353942Sdim  };
121353942Sdim
122353942Sdim  virtual PathGenerationScheme getGenerationScheme() const { return Minimal; }
123353942Sdim
124353942Sdim  bool shouldGenerateDiagnostics() const {
125353942Sdim    return getGenerationScheme() != None;
126353942Sdim  }
127353942Sdim
128353942Sdim  bool shouldAddPathEdges() const { return getGenerationScheme() == Extensive; }
129353942Sdim
130353942Sdim  virtual bool supportsLogicalOpControlFlow() const { return false; }
131353942Sdim
132353942Sdim  /// Return true if the PathDiagnosticConsumer supports individual
133353942Sdim  /// PathDiagnostics that span multiple files.
134353942Sdim  virtual bool supportsCrossFileDiagnostics() const { return false; }
135353942Sdim
136353942Sdimprotected:
137353942Sdim  bool flushed = false;
138353942Sdim  llvm::FoldingSet<PathDiagnostic> Diags;
139353942Sdim};
140353942Sdim
141353942Sdim//===----------------------------------------------------------------------===//
142353942Sdim// Path-sensitive diagnostics.
143353942Sdim//===----------------------------------------------------------------------===//
144353942Sdim
145353942Sdimclass PathDiagnosticRange : public SourceRange {
146353942Sdimpublic:
147353942Sdim  bool isPoint = false;
148353942Sdim
149353942Sdim  PathDiagnosticRange(SourceRange R, bool isP = false)
150353942Sdim      : SourceRange(R), isPoint(isP) {}
151353942Sdim  PathDiagnosticRange() = default;
152353942Sdim};
153353942Sdim
154353942Sdimusing LocationOrAnalysisDeclContext =
155353942Sdim    llvm::PointerUnion<const LocationContext *, AnalysisDeclContext *>;
156353942Sdim
157353942Sdimclass PathDiagnosticLocation {
158353942Sdimprivate:
159353942Sdim  enum Kind { RangeK, SingleLocK, StmtK, DeclK } K = SingleLocK;
160353942Sdim
161353942Sdim  const Stmt *S = nullptr;
162353942Sdim  const Decl *D = nullptr;
163353942Sdim  const SourceManager *SM = nullptr;
164353942Sdim  FullSourceLoc Loc;
165353942Sdim  PathDiagnosticRange Range;
166353942Sdim
167353942Sdim  PathDiagnosticLocation(SourceLocation L, const SourceManager &sm, Kind kind)
168353942Sdim      : K(kind), SM(&sm), Loc(genLocation(L)), Range(genRange()) {}
169353942Sdim
170353942Sdim  FullSourceLoc genLocation(
171353942Sdim      SourceLocation L = SourceLocation(),
172353942Sdim      LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const;
173353942Sdim
174353942Sdim  PathDiagnosticRange genRange(
175353942Sdim      LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const;
176353942Sdim
177353942Sdimpublic:
178353942Sdim  /// Create an invalid location.
179353942Sdim  PathDiagnosticLocation() = default;
180353942Sdim
181353942Sdim  /// Create a location corresponding to the given statement.
182353942Sdim  PathDiagnosticLocation(const Stmt *s, const SourceManager &sm,
183353942Sdim                         LocationOrAnalysisDeclContext lac)
184353942Sdim      : K(s->getBeginLoc().isValid() ? StmtK : SingleLocK),
185353942Sdim        S(K == StmtK ? s : nullptr), SM(&sm),
186353942Sdim        Loc(genLocation(SourceLocation(), lac)), Range(genRange(lac)) {
187353942Sdim    assert(K == SingleLocK || S);
188353942Sdim    assert(K == SingleLocK || Loc.isValid());
189353942Sdim    assert(K == SingleLocK || Range.isValid());
190353942Sdim  }
191353942Sdim
192353942Sdim  /// Create a location corresponding to the given declaration.
193353942Sdim  PathDiagnosticLocation(const Decl *d, const SourceManager &sm)
194353942Sdim      : K(DeclK), D(d), SM(&sm), Loc(genLocation()), Range(genRange()) {
195353942Sdim    assert(D);
196353942Sdim    assert(Loc.isValid());
197353942Sdim    assert(Range.isValid());
198353942Sdim  }
199353942Sdim
200353942Sdim  /// Create a location at an explicit offset in the source.
201353942Sdim  ///
202353942Sdim  /// This should only be used if there are no more appropriate constructors.
203353942Sdim  PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm)
204353942Sdim      : SM(&sm), Loc(loc, sm), Range(genRange()) {
205353942Sdim    assert(Loc.isValid());
206353942Sdim    assert(Range.isValid());
207353942Sdim  }
208353942Sdim
209353942Sdim  /// Create a location corresponding to the given declaration.
210353942Sdim  static PathDiagnosticLocation create(const Decl *D,
211353942Sdim                                       const SourceManager &SM) {
212353942Sdim    return PathDiagnosticLocation(D, SM);
213353942Sdim  }
214353942Sdim
215353942Sdim  /// Create a location for the beginning of the declaration.
216353942Sdim  static PathDiagnosticLocation createBegin(const Decl *D,
217353942Sdim                                            const SourceManager &SM);
218353942Sdim
219353942Sdim  /// Create a location for the beginning of the declaration.
220353942Sdim  /// The third argument is ignored, useful for generic treatment
221353942Sdim  /// of statements and declarations.
222353942Sdim  static PathDiagnosticLocation
223353942Sdim  createBegin(const Decl *D, const SourceManager &SM,
224353942Sdim              const LocationOrAnalysisDeclContext LAC) {
225353942Sdim    return createBegin(D, SM);
226353942Sdim  }
227353942Sdim
228353942Sdim  /// Create a location for the beginning of the statement.
229353942Sdim  static PathDiagnosticLocation createBegin(const Stmt *S,
230353942Sdim                                            const SourceManager &SM,
231353942Sdim                                            const LocationOrAnalysisDeclContext LAC);
232353942Sdim
233353942Sdim  /// Create a location for the end of the statement.
234353942Sdim  ///
235353942Sdim  /// If the statement is a CompoundStatement, the location will point to the
236353942Sdim  /// closing brace instead of following it.
237353942Sdim  static PathDiagnosticLocation createEnd(const Stmt *S,
238353942Sdim                                          const SourceManager &SM,
239353942Sdim                                       const LocationOrAnalysisDeclContext LAC);
240353942Sdim
241353942Sdim  /// Create the location for the operator of the binary expression.
242353942Sdim  /// Assumes the statement has a valid location.
243353942Sdim  static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO,
244353942Sdim                                                  const SourceManager &SM);
245353942Sdim  static PathDiagnosticLocation createConditionalColonLoc(
246353942Sdim                                                  const ConditionalOperator *CO,
247353942Sdim                                                  const SourceManager &SM);
248353942Sdim
249353942Sdim  /// For member expressions, return the location of the '.' or '->'.
250353942Sdim  /// Assumes the statement has a valid location.
251353942Sdim  static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME,
252353942Sdim                                                const SourceManager &SM);
253353942Sdim
254353942Sdim  /// Create a location for the beginning of the compound statement.
255353942Sdim  /// Assumes the statement has a valid location.
256353942Sdim  static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS,
257353942Sdim                                                 const SourceManager &SM);
258353942Sdim
259353942Sdim  /// Create a location for the end of the compound statement.
260353942Sdim  /// Assumes the statement has a valid location.
261353942Sdim  static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS,
262353942Sdim                                               const SourceManager &SM);
263353942Sdim
264353942Sdim  /// Create a location for the beginning of the enclosing declaration body.
265353942Sdim  /// Defaults to the beginning of the first statement in the declaration body.
266353942Sdim  static PathDiagnosticLocation createDeclBegin(const LocationContext *LC,
267353942Sdim                                                const SourceManager &SM);
268353942Sdim
269353942Sdim  /// Constructs a location for the end of the enclosing declaration body.
270353942Sdim  /// Defaults to the end of brace.
271353942Sdim  static PathDiagnosticLocation createDeclEnd(const LocationContext *LC,
272353942Sdim                                                   const SourceManager &SM);
273353942Sdim
274353942Sdim  /// Create a location corresponding to the given valid ProgramPoint.
275353942Sdim  static PathDiagnosticLocation create(const ProgramPoint &P,
276353942Sdim                                       const SourceManager &SMng);
277353942Sdim
278353942Sdim  /// Convert the given location into a single kind location.
279353942Sdim  static PathDiagnosticLocation createSingleLocation(
280353942Sdim                                             const PathDiagnosticLocation &PDL);
281353942Sdim
282353942Sdim  /// Construct a source location that corresponds to either the beginning
283353942Sdim  /// or the end of the given statement, or a nearby valid source location
284353942Sdim  /// if the statement does not have a valid source location of its own.
285353942Sdim  static SourceLocation
286353942Sdim  getValidSourceLocation(const Stmt *S, LocationOrAnalysisDeclContext LAC,
287353942Sdim                         bool UseEndOfStatement = false);
288353942Sdim
289353942Sdim  bool operator==(const PathDiagnosticLocation &X) const {
290353942Sdim    return K == X.K && Loc == X.Loc && Range == X.Range;
291353942Sdim  }
292353942Sdim
293353942Sdim  bool operator!=(const PathDiagnosticLocation &X) const {
294353942Sdim    return !(*this == X);
295353942Sdim  }
296353942Sdim
297353942Sdim  bool isValid() const {
298353942Sdim    return SM != nullptr;
299353942Sdim  }
300353942Sdim
301353942Sdim  FullSourceLoc asLocation() const {
302353942Sdim    return Loc;
303353942Sdim  }
304353942Sdim
305353942Sdim  PathDiagnosticRange asRange() const {
306353942Sdim    return Range;
307353942Sdim  }
308353942Sdim
309353942Sdim  const Stmt *asStmt() const { assert(isValid()); return S; }
310353942Sdim  const Stmt *getStmtOrNull() const {
311353942Sdim    if (!isValid())
312353942Sdim      return nullptr;
313353942Sdim    return asStmt();
314353942Sdim  }
315353942Sdim
316353942Sdim  const Decl *asDecl() const { assert(isValid()); return D; }
317353942Sdim
318353942Sdim  bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; }
319353942Sdim
320353942Sdim  bool hasValidLocation() const { return asLocation().isValid(); }
321353942Sdim
322353942Sdim  void invalidate() {
323353942Sdim    *this = PathDiagnosticLocation();
324353942Sdim  }
325353942Sdim
326353942Sdim  void flatten();
327353942Sdim
328353942Sdim  const SourceManager& getManager() const { assert(isValid()); return *SM; }
329353942Sdim
330353942Sdim  void Profile(llvm::FoldingSetNodeID &ID) const;
331353942Sdim
332353942Sdim  void dump() const;
333353942Sdim};
334353942Sdim
335353942Sdimclass PathDiagnosticLocationPair {
336353942Sdimprivate:
337353942Sdim  PathDiagnosticLocation Start, End;
338353942Sdim
339353942Sdimpublic:
340353942Sdim  PathDiagnosticLocationPair(const PathDiagnosticLocation &start,
341353942Sdim                             const PathDiagnosticLocation &end)
342353942Sdim      : Start(start), End(end) {}
343353942Sdim
344353942Sdim  const PathDiagnosticLocation &getStart() const { return Start; }
345353942Sdim  const PathDiagnosticLocation &getEnd() const { return End; }
346353942Sdim
347353942Sdim  void setStart(const PathDiagnosticLocation &L) { Start = L; }
348353942Sdim  void setEnd(const PathDiagnosticLocation &L) { End = L; }
349353942Sdim
350353942Sdim  void flatten() {
351353942Sdim    Start.flatten();
352353942Sdim    End.flatten();
353353942Sdim  }
354353942Sdim
355353942Sdim  void Profile(llvm::FoldingSetNodeID &ID) const {
356353942Sdim    Start.Profile(ID);
357353942Sdim    End.Profile(ID);
358353942Sdim  }
359353942Sdim};
360353942Sdim
361353942Sdim//===----------------------------------------------------------------------===//
362353942Sdim// Path "pieces" for path-sensitive diagnostics.
363353942Sdim//===----------------------------------------------------------------------===//
364353942Sdim
365353942Sdimclass PathDiagnosticPiece: public llvm::FoldingSetNode {
366353942Sdimpublic:
367353942Sdim  enum Kind { ControlFlow, Event, Macro, Call, Note, PopUp };
368353942Sdim  enum DisplayHint { Above, Below };
369353942Sdim
370353942Sdimprivate:
371353942Sdim  const std::string str;
372353942Sdim  const Kind kind;
373353942Sdim  const DisplayHint Hint;
374353942Sdim
375353942Sdim  /// In the containing bug report, this piece is the last piece from
376353942Sdim  /// the main source file.
377353942Sdim  bool LastInMainSourceFile = false;
378353942Sdim
379353942Sdim  /// A constant string that can be used to tag the PathDiagnosticPiece,
380353942Sdim  /// typically with the identification of the creator.  The actual pointer
381353942Sdim  /// value is meant to be an identifier; the string itself is useful for
382353942Sdim  /// debugging.
383353942Sdim  StringRef Tag;
384353942Sdim
385353942Sdim  std::vector<SourceRange> ranges;
386353942Sdim  std::vector<FixItHint> fixits;
387353942Sdim
388353942Sdimprotected:
389353942Sdim  PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below);
390353942Sdim  PathDiagnosticPiece(Kind k, DisplayHint hint = Below);
391353942Sdim
392353942Sdimpublic:
393353942Sdim  PathDiagnosticPiece() = delete;
394353942Sdim  PathDiagnosticPiece(const PathDiagnosticPiece &) = delete;
395353942Sdim  PathDiagnosticPiece &operator=(const PathDiagnosticPiece &) = delete;
396353942Sdim  virtual ~PathDiagnosticPiece();
397353942Sdim
398353942Sdim  StringRef getString() const { return str; }
399353942Sdim
400353942Sdim  /// Tag this PathDiagnosticPiece with the given C-string.
401353942Sdim  void setTag(const char *tag) { Tag = tag; }
402353942Sdim
403353942Sdim  /// Return the opaque tag (if any) on the PathDiagnosticPiece.
404353942Sdim  const void *getTag() const { return Tag.data(); }
405353942Sdim
406353942Sdim  /// Return the string representation of the tag.  This is useful
407353942Sdim  /// for debugging.
408353942Sdim  StringRef getTagStr() const { return Tag; }
409353942Sdim
410353942Sdim  /// getDisplayHint - Return a hint indicating where the diagnostic should
411353942Sdim  ///  be displayed by the PathDiagnosticConsumer.
412353942Sdim  DisplayHint getDisplayHint() const { return Hint; }
413353942Sdim
414353942Sdim  virtual PathDiagnosticLocation getLocation() const = 0;
415353942Sdim  virtual void flattenLocations() = 0;
416353942Sdim
417353942Sdim  Kind getKind() const { return kind; }
418353942Sdim
419353942Sdim  void addRange(SourceRange R) {
420353942Sdim    if (!R.isValid())
421353942Sdim      return;
422353942Sdim    ranges.push_back(R);
423353942Sdim  }
424353942Sdim
425353942Sdim  void addRange(SourceLocation B, SourceLocation E) {
426353942Sdim    if (!B.isValid() || !E.isValid())
427353942Sdim      return;
428353942Sdim    ranges.push_back(SourceRange(B,E));
429353942Sdim  }
430353942Sdim
431353942Sdim  void addFixit(FixItHint F) {
432353942Sdim    fixits.push_back(F);
433353942Sdim  }
434353942Sdim
435353942Sdim  /// Return the SourceRanges associated with this PathDiagnosticPiece.
436353942Sdim  ArrayRef<SourceRange> getRanges() const { return ranges; }
437353942Sdim
438353942Sdim  /// Return the fix-it hints associated with this PathDiagnosticPiece.
439353942Sdim  ArrayRef<FixItHint> getFixits() const { return fixits; }
440353942Sdim
441353942Sdim  virtual void Profile(llvm::FoldingSetNodeID &ID) const;
442353942Sdim
443353942Sdim  void setAsLastInMainSourceFile() {
444353942Sdim    LastInMainSourceFile = true;
445353942Sdim  }
446353942Sdim
447353942Sdim  bool isLastInMainSourceFile() const {
448353942Sdim    return LastInMainSourceFile;
449353942Sdim  }
450353942Sdim
451353942Sdim  virtual void dump() const = 0;
452353942Sdim};
453353942Sdim
454353942Sdimusing PathDiagnosticPieceRef = std::shared_ptr<PathDiagnosticPiece>;
455353942Sdim
456353942Sdimclass PathPieces : public std::list<PathDiagnosticPieceRef> {
457353942Sdim  void flattenTo(PathPieces &Primary, PathPieces &Current,
458353942Sdim                 bool ShouldFlattenMacros) const;
459353942Sdim
460353942Sdimpublic:
461353942Sdim  PathPieces flatten(bool ShouldFlattenMacros) const {
462353942Sdim    PathPieces Result;
463353942Sdim    flattenTo(Result, Result, ShouldFlattenMacros);
464353942Sdim    return Result;
465353942Sdim  }
466353942Sdim
467353942Sdim  void dump() const;
468353942Sdim};
469353942Sdim
470353942Sdimclass PathDiagnosticSpotPiece : public PathDiagnosticPiece {
471353942Sdimprivate:
472353942Sdim  PathDiagnosticLocation Pos;
473353942Sdim
474353942Sdimpublic:
475353942Sdim  PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos,
476353942Sdim                          StringRef s,
477353942Sdim                          PathDiagnosticPiece::Kind k,
478353942Sdim                          bool addPosRange = true)
479353942Sdim      : PathDiagnosticPiece(s, k), Pos(pos) {
480353942Sdim    assert(Pos.isValid() && Pos.hasValidLocation() &&
481353942Sdim           "PathDiagnosticSpotPiece's must have a valid location.");
482353942Sdim    if (addPosRange && Pos.hasRange()) addRange(Pos.asRange());
483353942Sdim  }
484353942Sdim
485353942Sdim  PathDiagnosticLocation getLocation() const override { return Pos; }
486353942Sdim  void flattenLocations() override { Pos.flatten(); }
487353942Sdim
488353942Sdim  void Profile(llvm::FoldingSetNodeID &ID) const override;
489353942Sdim
490353942Sdim  static bool classof(const PathDiagnosticPiece *P) {
491353942Sdim    return P->getKind() == Event || P->getKind() == Macro ||
492353942Sdim           P->getKind() == Note || P->getKind() == PopUp;
493353942Sdim  }
494353942Sdim};
495353942Sdim
496353942Sdimclass PathDiagnosticEventPiece : public PathDiagnosticSpotPiece {
497353942Sdim  Optional<bool> IsPrunable;
498353942Sdim
499353942Sdimpublic:
500353942Sdim  PathDiagnosticEventPiece(const PathDiagnosticLocation &pos,
501353942Sdim                           StringRef s, bool addPosRange = true)
502353942Sdim      : PathDiagnosticSpotPiece(pos, s, Event, addPosRange) {}
503353942Sdim  ~PathDiagnosticEventPiece() override;
504353942Sdim
505353942Sdim  /// Mark the diagnostic piece as being potentially prunable.  This
506353942Sdim  /// flag may have been previously set, at which point it will not
507353942Sdim  /// be reset unless one specifies to do so.
508353942Sdim  void setPrunable(bool isPrunable, bool override = false) {
509353942Sdim    if (IsPrunable.hasValue() && !override)
510353942Sdim     return;
511353942Sdim    IsPrunable = isPrunable;
512353942Sdim  }
513353942Sdim
514353942Sdim  /// Return true if the diagnostic piece is prunable.
515353942Sdim  bool isPrunable() const {
516353942Sdim    return IsPrunable.hasValue() ? IsPrunable.getValue() : false;
517353942Sdim  }
518353942Sdim
519353942Sdim  void dump() const override;
520353942Sdim
521353942Sdim  static bool classof(const PathDiagnosticPiece *P) {
522353942Sdim    return P->getKind() == Event;
523353942Sdim  }
524353942Sdim};
525353942Sdim
526353942Sdimclass PathDiagnosticCallPiece : public PathDiagnosticPiece {
527353942Sdim  const Decl *Caller;
528353942Sdim  const Decl *Callee = nullptr;
529353942Sdim
530353942Sdim  // Flag signifying that this diagnostic has only call enter and no matching
531353942Sdim  // call exit.
532353942Sdim  bool NoExit;
533353942Sdim
534353942Sdim  // Flag signifying that the callee function is an Objective-C autosynthesized
535353942Sdim  // property getter or setter.
536353942Sdim  bool IsCalleeAnAutosynthesizedPropertyAccessor = false;
537353942Sdim
538353942Sdim  // The custom string, which should appear after the call Return Diagnostic.
539353942Sdim  // TODO: Should we allow multiple diagnostics?
540353942Sdim  std::string CallStackMessage;
541353942Sdim
542353942Sdim  PathDiagnosticCallPiece(const Decl *callerD,
543353942Sdim                          const PathDiagnosticLocation &callReturnPos)
544353942Sdim      : PathDiagnosticPiece(Call), Caller(callerD), NoExit(false),
545353942Sdim        callReturn(callReturnPos) {}
546353942Sdim  PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller)
547353942Sdim      : PathDiagnosticPiece(Call), Caller(caller), NoExit(true),
548353942Sdim        path(oldPath) {}
549353942Sdim
550353942Sdimpublic:
551353942Sdim  PathDiagnosticLocation callEnter;
552353942Sdim  PathDiagnosticLocation callEnterWithin;
553353942Sdim  PathDiagnosticLocation callReturn;
554353942Sdim  PathPieces path;
555353942Sdim
556353942Sdim  ~PathDiagnosticCallPiece() override;
557353942Sdim
558353942Sdim  const Decl *getCaller() const { return Caller; }
559353942Sdim
560353942Sdim  const Decl *getCallee() const { return Callee; }
561353942Sdim  void setCallee(const CallEnter &CE, const SourceManager &SM);
562353942Sdim
563353942Sdim  bool hasCallStackMessage() { return !CallStackMessage.empty(); }
564353942Sdim  void setCallStackMessage(StringRef st) { CallStackMessage = st; }
565353942Sdim
566353942Sdim  PathDiagnosticLocation getLocation() const override { return callEnter; }
567353942Sdim
568353942Sdim  std::shared_ptr<PathDiagnosticEventPiece> getCallEnterEvent() const;
569353942Sdim  std::shared_ptr<PathDiagnosticEventPiece>
570353942Sdim  getCallEnterWithinCallerEvent() const;
571353942Sdim  std::shared_ptr<PathDiagnosticEventPiece> getCallExitEvent() const;
572353942Sdim
573353942Sdim  void flattenLocations() override {
574353942Sdim    callEnter.flatten();
575353942Sdim    callReturn.flatten();
576353942Sdim    for (const auto &I : path)
577353942Sdim      I->flattenLocations();
578353942Sdim  }
579353942Sdim
580353942Sdim  static std::shared_ptr<PathDiagnosticCallPiece>
581353942Sdim  construct(const CallExitEnd &CE,
582353942Sdim            const SourceManager &SM);
583353942Sdim
584353942Sdim  static PathDiagnosticCallPiece *construct(PathPieces &pieces,
585353942Sdim                                            const Decl *caller);
586353942Sdim
587353942Sdim  void dump() const override;
588353942Sdim
589353942Sdim  void Profile(llvm::FoldingSetNodeID &ID) const override;
590353942Sdim
591353942Sdim  static bool classof(const PathDiagnosticPiece *P) {
592353942Sdim    return P->getKind() == Call;
593353942Sdim  }
594353942Sdim};
595353942Sdim
596353942Sdimclass PathDiagnosticControlFlowPiece : public PathDiagnosticPiece {
597353942Sdim  std::vector<PathDiagnosticLocationPair> LPairs;
598353942Sdim
599353942Sdimpublic:
600353942Sdim  PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos,
601353942Sdim                                 const PathDiagnosticLocation &endPos,
602353942Sdim                                 StringRef s)
603353942Sdim      : PathDiagnosticPiece(s, ControlFlow) {
604353942Sdim    LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos));
605353942Sdim  }
606353942Sdim
607353942Sdim  PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos,
608353942Sdim                                 const PathDiagnosticLocation &endPos)
609353942Sdim      : PathDiagnosticPiece(ControlFlow) {
610353942Sdim    LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos));
611353942Sdim  }
612353942Sdim
613353942Sdim  ~PathDiagnosticControlFlowPiece() override;
614353942Sdim
615353942Sdim  PathDiagnosticLocation getStartLocation() const {
616353942Sdim    assert(!LPairs.empty() &&
617353942Sdim           "PathDiagnosticControlFlowPiece needs at least one location.");
618353942Sdim    return LPairs[0].getStart();
619353942Sdim  }
620353942Sdim
621353942Sdim  PathDiagnosticLocation getEndLocation() const {
622353942Sdim    assert(!LPairs.empty() &&
623353942Sdim           "PathDiagnosticControlFlowPiece needs at least one location.");
624353942Sdim    return LPairs[0].getEnd();
625353942Sdim  }
626353942Sdim
627353942Sdim  void setStartLocation(const PathDiagnosticLocation &L) {
628353942Sdim    LPairs[0].setStart(L);
629353942Sdim  }
630353942Sdim
631353942Sdim  void setEndLocation(const PathDiagnosticLocation &L) {
632353942Sdim    LPairs[0].setEnd(L);
633353942Sdim  }
634353942Sdim
635353942Sdim  void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); }
636353942Sdim
637353942Sdim  PathDiagnosticLocation getLocation() const override {
638353942Sdim    return getStartLocation();
639353942Sdim  }
640353942Sdim
641353942Sdim  using iterator = std::vector<PathDiagnosticLocationPair>::iterator;
642353942Sdim
643353942Sdim  iterator begin() { return LPairs.begin(); }
644353942Sdim  iterator end() { return LPairs.end(); }
645353942Sdim
646353942Sdim  void flattenLocations() override {
647353942Sdim    for (auto &I : *this)
648353942Sdim      I.flatten();
649353942Sdim  }
650353942Sdim
651353942Sdim  using const_iterator =
652353942Sdim      std::vector<PathDiagnosticLocationPair>::const_iterator;
653353942Sdim
654353942Sdim  const_iterator begin() const { return LPairs.begin(); }
655353942Sdim  const_iterator end() const { return LPairs.end(); }
656353942Sdim
657353942Sdim  static bool classof(const PathDiagnosticPiece *P) {
658353942Sdim    return P->getKind() == ControlFlow;
659353942Sdim  }
660353942Sdim
661353942Sdim  void dump() const override;
662353942Sdim
663353942Sdim  void Profile(llvm::FoldingSetNodeID &ID) const override;
664353942Sdim};
665353942Sdim
666353942Sdimclass PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece {
667353942Sdimpublic:
668353942Sdim  PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos)
669353942Sdim      : PathDiagnosticSpotPiece(pos, "", Macro) {}
670353942Sdim  ~PathDiagnosticMacroPiece() override;
671353942Sdim
672353942Sdim  PathPieces subPieces;
673353942Sdim
674353942Sdim  void flattenLocations() override {
675353942Sdim    PathDiagnosticSpotPiece::flattenLocations();
676353942Sdim    for (const auto &I : subPieces)
677353942Sdim      I->flattenLocations();
678353942Sdim  }
679353942Sdim
680353942Sdim  static bool classof(const PathDiagnosticPiece *P) {
681353942Sdim    return P->getKind() == Macro;
682353942Sdim  }
683353942Sdim
684353942Sdim  void dump() const override;
685353942Sdim
686353942Sdim  void Profile(llvm::FoldingSetNodeID &ID) const override;
687353942Sdim};
688353942Sdim
689353942Sdimclass PathDiagnosticNotePiece: public PathDiagnosticSpotPiece {
690353942Sdimpublic:
691353942Sdim  PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S,
692353942Sdim                          bool AddPosRange = true)
693353942Sdim      : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {}
694353942Sdim  ~PathDiagnosticNotePiece() override;
695353942Sdim
696353942Sdim  static bool classof(const PathDiagnosticPiece *P) {
697353942Sdim    return P->getKind() == Note;
698353942Sdim  }
699353942Sdim
700353942Sdim  void dump() const override;
701353942Sdim
702353942Sdim  void Profile(llvm::FoldingSetNodeID &ID) const override;
703353942Sdim};
704353942Sdim
705353942Sdimclass PathDiagnosticPopUpPiece: public PathDiagnosticSpotPiece {
706353942Sdimpublic:
707353942Sdim  PathDiagnosticPopUpPiece(const PathDiagnosticLocation &Pos, StringRef S,
708353942Sdim                           bool AddPosRange = true)
709353942Sdim      : PathDiagnosticSpotPiece(Pos, S, PopUp, AddPosRange) {}
710353942Sdim  ~PathDiagnosticPopUpPiece() override;
711353942Sdim
712353942Sdim  static bool classof(const PathDiagnosticPiece *P) {
713353942Sdim    return P->getKind() == PopUp;
714353942Sdim  }
715353942Sdim
716353942Sdim  void dump() const override;
717353942Sdim
718353942Sdim  void Profile(llvm::FoldingSetNodeID &ID) const override;
719353942Sdim};
720353942Sdim
721353942Sdim/// File IDs mapped to sets of line numbers.
722353942Sdimusing FilesToLineNumsMap = std::map<FileID, std::set<unsigned>>;
723353942Sdim
724353942Sdim/// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive
725353942Sdim///  diagnostic.  It represents an ordered-collection of PathDiagnosticPieces,
726353942Sdim///  each which represent the pieces of the path.
727353942Sdimclass PathDiagnostic : public llvm::FoldingSetNode {
728353942Sdim  std::string CheckerName;
729353942Sdim  const Decl *DeclWithIssue;
730353942Sdim  std::string BugType;
731353942Sdim  std::string VerboseDesc;
732353942Sdim  std::string ShortDesc;
733353942Sdim  std::string Category;
734353942Sdim  std::deque<std::string> OtherDesc;
735353942Sdim
736353942Sdim  /// Loc The location of the path diagnostic report.
737353942Sdim  PathDiagnosticLocation Loc;
738353942Sdim
739353942Sdim  PathPieces pathImpl;
740353942Sdim  SmallVector<PathPieces *, 3> pathStack;
741353942Sdim
742353942Sdim  /// Important bug uniqueing location.
743353942Sdim  /// The location info is useful to differentiate between bugs.
744353942Sdim  PathDiagnosticLocation UniqueingLoc;
745353942Sdim  const Decl *UniqueingDecl;
746353942Sdim
747353942Sdim  /// Lines executed in the path.
748353942Sdim  std::unique_ptr<FilesToLineNumsMap> ExecutedLines;
749353942Sdim
750353942Sdimpublic:
751353942Sdim  PathDiagnostic() = delete;
752353942Sdim  PathDiagnostic(StringRef CheckerName, const Decl *DeclWithIssue,
753353942Sdim                 StringRef bugtype, StringRef verboseDesc, StringRef shortDesc,
754353942Sdim                 StringRef category, PathDiagnosticLocation LocationToUnique,
755353942Sdim                 const Decl *DeclToUnique,
756353942Sdim                 std::unique_ptr<FilesToLineNumsMap> ExecutedLines);
757353942Sdim  ~PathDiagnostic();
758353942Sdim
759353942Sdim  const PathPieces &path;
760353942Sdim
761353942Sdim  /// Return the path currently used by builders for constructing the
762353942Sdim  /// PathDiagnostic.
763353942Sdim  PathPieces &getActivePath() {
764353942Sdim    if (pathStack.empty())
765353942Sdim      return pathImpl;
766353942Sdim    return *pathStack.back();
767353942Sdim  }
768353942Sdim
769353942Sdim  /// Return a mutable version of 'path'.
770353942Sdim  PathPieces &getMutablePieces() {
771353942Sdim    return pathImpl;
772353942Sdim  }
773353942Sdim
774353942Sdim  /// Return the unrolled size of the path.
775353942Sdim  unsigned full_size();
776353942Sdim
777353942Sdim  void pushActivePath(PathPieces *p) { pathStack.push_back(p); }
778353942Sdim  void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); }
779353942Sdim
780353942Sdim  bool isWithinCall() const { return !pathStack.empty(); }
781353942Sdim
782353942Sdim  void setEndOfPath(PathDiagnosticPieceRef EndPiece) {
783353942Sdim    assert(!Loc.isValid() && "End location already set!");
784353942Sdim    Loc = EndPiece->getLocation();
785353942Sdim    assert(Loc.isValid() && "Invalid location for end-of-path piece");
786353942Sdim    getActivePath().push_back(std::move(EndPiece));
787353942Sdim  }
788353942Sdim
789353942Sdim  void appendToDesc(StringRef S) {
790353942Sdim    if (!ShortDesc.empty())
791353942Sdim      ShortDesc += S;
792353942Sdim    VerboseDesc += S;
793353942Sdim  }
794353942Sdim
795353942Sdim  StringRef getVerboseDescription() const { return VerboseDesc; }
796353942Sdim
797353942Sdim  StringRef getShortDescription() const {
798353942Sdim    return ShortDesc.empty() ? VerboseDesc : ShortDesc;
799353942Sdim  }
800353942Sdim
801353942Sdim  StringRef getCheckerName() const { return CheckerName; }
802353942Sdim  StringRef getBugType() const { return BugType; }
803353942Sdim  StringRef getCategory() const { return Category; }
804353942Sdim
805353942Sdim  using meta_iterator = std::deque<std::string>::const_iterator;
806353942Sdim
807353942Sdim  meta_iterator meta_begin() const { return OtherDesc.begin(); }
808353942Sdim  meta_iterator meta_end() const { return OtherDesc.end(); }
809353942Sdim  void addMeta(StringRef s) { OtherDesc.push_back(s); }
810353942Sdim
811353942Sdim  const FilesToLineNumsMap &getExecutedLines() const {
812353942Sdim    return *ExecutedLines;
813353942Sdim  }
814353942Sdim
815353942Sdim  FilesToLineNumsMap &getExecutedLines() {
816353942Sdim    return *ExecutedLines;
817353942Sdim  }
818353942Sdim
819353942Sdim  /// Return the semantic context where an issue occurred.  If the
820353942Sdim  /// issue occurs along a path, this represents the "central" area
821353942Sdim  /// where the bug manifests.
822353942Sdim  const Decl *getDeclWithIssue() const { return DeclWithIssue; }
823353942Sdim
824353942Sdim  void setDeclWithIssue(const Decl *D) {
825353942Sdim    DeclWithIssue = D;
826353942Sdim  }
827353942Sdim
828353942Sdim  PathDiagnosticLocation getLocation() const {
829353942Sdim    return Loc;
830353942Sdim  }
831353942Sdim
832353942Sdim  void setLocation(PathDiagnosticLocation NewLoc) {
833353942Sdim    Loc = NewLoc;
834353942Sdim  }
835353942Sdim
836353942Sdim  /// Get the location on which the report should be uniqued.
837353942Sdim  PathDiagnosticLocation getUniqueingLoc() const {
838353942Sdim    return UniqueingLoc;
839353942Sdim  }
840353942Sdim
841353942Sdim  /// Get the declaration containing the uniqueing location.
842353942Sdim  const Decl *getUniqueingDecl() const {
843353942Sdim    return UniqueingDecl;
844353942Sdim  }
845353942Sdim
846353942Sdim  void flattenLocations() {
847353942Sdim    Loc.flatten();
848353942Sdim    for (const auto &I : pathImpl)
849353942Sdim      I->flattenLocations();
850353942Sdim  }
851353942Sdim
852353942Sdim  /// Profiles the diagnostic, independent of the path it references.
853353942Sdim  ///
854353942Sdim  /// This can be used to merge diagnostics that refer to the same issue
855353942Sdim  /// along different paths.
856353942Sdim  void Profile(llvm::FoldingSetNodeID &ID) const;
857353942Sdim
858353942Sdim  /// Profiles the diagnostic, including its path.
859353942Sdim  ///
860353942Sdim  /// Two diagnostics with the same issue along different paths will generate
861353942Sdim  /// different profiles.
862353942Sdim  void FullProfile(llvm::FoldingSetNodeID &ID) const;
863353942Sdim};
864353942Sdim
865353942Sdim} // namespace ento
866353942Sdim} // namespace clang
867353942Sdim
868353942Sdim#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H
869