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