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 = std::string(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(std::string(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