1 //===--- PathDiagnostic.h - Path-Specific Diagnostic Handling ---*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines the PathDiagnostic-related interfaces.
12 //===----------------------------------------------------------------------===//
14 #ifndef LLVM_CLANG_PATH_DIAGNOSTIC_H
15 #define LLVM_CLANG_PATH_DIAGNOSTIC_H
17 #include "clang/Basic/SourceLocation.h"
18 #include "clang/Analysis/ProgramPoint.h"
19 #include "llvm/ADT/FoldingSet.h"
20 #include "llvm/ADT/IntrusiveRefCntPtr.h"
21 #include "llvm/ADT/PointerUnion.h"
22 #include "llvm/ADT/Optional.h"
30 class AnalysisDeclContext;
34 class LocationContext;
45 typedef const SymExpr* SymbolRef;
47 //===----------------------------------------------------------------------===//
48 // High-level interface for handlers of path-sensitive diagnostics.
49 //===----------------------------------------------------------------------===//
53 class PathDiagnosticConsumer {
55 class PDFileEntry : public llvm::FoldingSetNode {
57 PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {}
59 typedef std::vector<std::pair<StringRef, StringRef> > ConsumerFiles;
61 /// \brief A vector of <consumer,file> pairs.
64 /// \brief A precomputed hash tag used for uniquing PDFileEntry objects.
65 const llvm::FoldingSetNodeID NodeID;
67 /// \brief Used for profiling in the FoldingSet.
68 void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; }
71 struct FilesMade : public llvm::FoldingSet<PDFileEntry> {
72 llvm::BumpPtrAllocator Alloc;
74 void addDiagnostic(const PathDiagnostic &PD,
75 StringRef ConsumerName,
78 PDFileEntry::ConsumerFiles *getFiles(const PathDiagnostic &PD);
82 virtual void anchor();
84 PathDiagnosticConsumer() : flushed(false) {}
85 virtual ~PathDiagnosticConsumer();
87 void FlushDiagnostics(FilesMade *FilesMade);
89 virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
90 FilesMade *filesMade) = 0;
92 virtual StringRef getName() const = 0;
94 void HandlePathDiagnostic(PathDiagnostic *D);
96 enum PathGenerationScheme { None, Minimal, Extensive };
97 virtual PathGenerationScheme getGenerationScheme() const { return Minimal; }
98 virtual bool supportsLogicalOpControlFlow() const { return false; }
99 virtual bool supportsAllBlockEdges() const { return false; }
101 /// Return true if the PathDiagnosticConsumer supports individual
102 /// PathDiagnostics that span multiple files.
103 virtual bool supportsCrossFileDiagnostics() const { return false; }
107 llvm::FoldingSet<PathDiagnostic> Diags;
110 //===----------------------------------------------------------------------===//
111 // Path-sensitive diagnostics.
112 //===----------------------------------------------------------------------===//
114 class PathDiagnosticRange : public SourceRange {
118 PathDiagnosticRange(const SourceRange &R, bool isP = false)
119 : SourceRange(R), isPoint(isP) {}
121 PathDiagnosticRange() : isPoint(false) {}
124 typedef llvm::PointerUnion<const LocationContext*, AnalysisDeclContext*>
125 LocationOrAnalysisDeclContext;
127 class PathDiagnosticLocation {
129 enum Kind { RangeK, SingleLocK, StmtK, DeclK } K;
132 const SourceManager *SM;
134 PathDiagnosticRange Range;
136 PathDiagnosticLocation(SourceLocation L, const SourceManager &sm,
138 : K(kind), S(0), D(0), SM(&sm),
139 Loc(genLocation(L)), Range(genRange()) {
143 genLocation(SourceLocation L = SourceLocation(),
144 LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext*)0) const;
147 genRange(LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext*)0) const;
150 /// Create an invalid location.
151 PathDiagnosticLocation()
152 : K(SingleLocK), S(0), D(0), SM(0) {}
154 /// Create a location corresponding to the given statement.
155 PathDiagnosticLocation(const Stmt *s,
156 const SourceManager &sm,
157 LocationOrAnalysisDeclContext lac)
158 : K(s->getLocStart().isValid() ? StmtK : SingleLocK),
159 S(K == StmtK ? s : 0),
161 Loc(genLocation(SourceLocation(), lac)),
162 Range(genRange(lac)) {
163 assert(K == SingleLocK || S);
164 assert(K == SingleLocK || Loc.isValid());
165 assert(K == SingleLocK || Range.isValid());
168 /// Create a location corresponding to the given declaration.
169 PathDiagnosticLocation(const Decl *d, const SourceManager &sm)
170 : K(DeclK), S(0), D(d), SM(&sm),
171 Loc(genLocation()), Range(genRange()) {
173 assert(Loc.isValid());
174 assert(Range.isValid());
177 /// Create a location at an explicit offset in the source.
179 /// This should only be used if there are no more appropriate constructors.
180 PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm)
181 : K(SingleLocK), S(0), D(0), SM(&sm), Loc(loc, sm), Range(genRange()) {
182 assert(Loc.isValid());
183 assert(Range.isValid());
186 /// Create a location corresponding to the given declaration.
187 static PathDiagnosticLocation create(const Decl *D,
188 const SourceManager &SM) {
189 return PathDiagnosticLocation(D, SM);
192 /// Create a location for the beginning of the declaration.
193 static PathDiagnosticLocation createBegin(const Decl *D,
194 const SourceManager &SM);
196 /// Create a location for the beginning of the statement.
197 static PathDiagnosticLocation createBegin(const Stmt *S,
198 const SourceManager &SM,
199 const LocationOrAnalysisDeclContext LAC);
201 /// Create a location for the end of the statement.
203 /// If the statement is a CompoundStatement, the location will point to the
204 /// closing brace instead of following it.
205 static PathDiagnosticLocation createEnd(const Stmt *S,
206 const SourceManager &SM,
207 const LocationOrAnalysisDeclContext LAC);
209 /// Create the location for the operator of the binary expression.
210 /// Assumes the statement has a valid location.
211 static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO,
212 const SourceManager &SM);
214 /// For member expressions, return the location of the '.' or '->'.
215 /// Assumes the statement has a valid location.
216 static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME,
217 const SourceManager &SM);
219 /// Create a location for the beginning of the compound statement.
220 /// Assumes the statement has a valid location.
221 static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS,
222 const SourceManager &SM);
224 /// Create a location for the end of the compound statement.
225 /// Assumes the statement has a valid location.
226 static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS,
227 const SourceManager &SM);
229 /// Create a location for the beginning of the enclosing declaration body.
230 /// Defaults to the beginning of the first statement in the declaration body.
231 static PathDiagnosticLocation createDeclBegin(const LocationContext *LC,
232 const SourceManager &SM);
234 /// Constructs a location for the end of the enclosing declaration body.
235 /// Defaults to the end of brace.
236 static PathDiagnosticLocation createDeclEnd(const LocationContext *LC,
237 const SourceManager &SM);
239 /// Create a location corresponding to the given valid ExplodedNode.
240 static PathDiagnosticLocation create(const ProgramPoint& P,
241 const SourceManager &SMng);
243 /// Create a location corresponding to the next valid ExplodedNode as end
244 /// of path location.
245 static PathDiagnosticLocation createEndOfPath(const ExplodedNode* N,
246 const SourceManager &SM);
248 /// Convert the given location into a single kind location.
249 static PathDiagnosticLocation createSingleLocation(
250 const PathDiagnosticLocation &PDL);
252 bool operator==(const PathDiagnosticLocation &X) const {
253 return K == X.K && Loc == X.Loc && Range == X.Range;
256 bool operator!=(const PathDiagnosticLocation &X) const {
257 return !(*this == X);
260 bool isValid() const {
264 FullSourceLoc asLocation() const {
268 PathDiagnosticRange asRange() const {
272 const Stmt *asStmt() const { assert(isValid()); return S; }
273 const Decl *asDecl() const { assert(isValid()); return D; }
275 bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; }
278 *this = PathDiagnosticLocation();
283 const SourceManager& getManager() const { assert(isValid()); return *SM; }
285 void Profile(llvm::FoldingSetNodeID &ID) const;
288 class PathDiagnosticLocationPair {
290 PathDiagnosticLocation Start, End;
292 PathDiagnosticLocationPair(const PathDiagnosticLocation &start,
293 const PathDiagnosticLocation &end)
294 : Start(start), End(end) {}
296 const PathDiagnosticLocation &getStart() const { return Start; }
297 const PathDiagnosticLocation &getEnd() const { return End; }
304 void Profile(llvm::FoldingSetNodeID &ID) const {
310 //===----------------------------------------------------------------------===//
311 // Path "pieces" for path-sensitive diagnostics.
312 //===----------------------------------------------------------------------===//
314 class PathDiagnosticPiece : public RefCountedBaseVPTR {
316 enum Kind { ControlFlow, Event, Macro, Call };
317 enum DisplayHint { Above, Below };
320 const std::string str;
322 const DisplayHint Hint;
324 /// A constant string that can be used to tag the PathDiagnosticPiece,
325 /// typically with the identification of the creator. The actual pointer
326 /// value is meant to be an identifier; the string itself is useful for
330 std::vector<SourceRange> ranges;
332 PathDiagnosticPiece() LLVM_DELETED_FUNCTION;
333 PathDiagnosticPiece(const PathDiagnosticPiece &P) LLVM_DELETED_FUNCTION;
334 void operator=(const PathDiagnosticPiece &P) LLVM_DELETED_FUNCTION;
337 PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below);
339 PathDiagnosticPiece(Kind k, DisplayHint hint = Below);
342 virtual ~PathDiagnosticPiece();
344 llvm::StringRef getString() const { return str; }
346 /// Tag this PathDiagnosticPiece with the given C-string.
347 void setTag(const char *tag) { Tag = tag; }
349 /// Return the opaque tag (if any) on the PathDiagnosticPiece.
350 const void *getTag() const { return Tag.data(); }
352 /// Return the string representation of the tag. This is useful
354 StringRef getTagStr() const { return Tag; }
356 /// getDisplayHint - Return a hint indicating where the diagnostic should
357 /// be displayed by the PathDiagnosticConsumer.
358 DisplayHint getDisplayHint() const { return Hint; }
360 virtual PathDiagnosticLocation getLocation() const = 0;
361 virtual void flattenLocations() = 0;
363 Kind getKind() const { return kind; }
365 void addRange(SourceRange R) {
371 void addRange(SourceLocation B, SourceLocation E) {
372 if (!B.isValid() || !E.isValid())
374 ranges.push_back(SourceRange(B,E));
377 /// Return the SourceRanges associated with this PathDiagnosticPiece.
378 ArrayRef<SourceRange> getRanges() const { return ranges; }
380 virtual void Profile(llvm::FoldingSetNodeID &ID) const;
384 class PathPieces : public std::deque<IntrusiveRefCntPtr<PathDiagnosticPiece> > {
385 void flattenTo(PathPieces &Primary, PathPieces &Current,
386 bool ShouldFlattenMacros) const;
390 PathPieces flatten(bool ShouldFlattenMacros) const {
392 flattenTo(Result, Result, ShouldFlattenMacros);
397 class PathDiagnosticSpotPiece : public PathDiagnosticPiece {
399 PathDiagnosticLocation Pos;
401 PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos,
403 PathDiagnosticPiece::Kind k,
404 bool addPosRange = true)
405 : PathDiagnosticPiece(s, k), Pos(pos) {
406 assert(Pos.isValid() && Pos.asLocation().isValid() &&
407 "PathDiagnosticSpotPiece's must have a valid location.");
408 if (addPosRange && Pos.hasRange()) addRange(Pos.asRange());
411 PathDiagnosticLocation getLocation() const { return Pos; }
412 virtual void flattenLocations() { Pos.flatten(); }
414 virtual void Profile(llvm::FoldingSetNodeID &ID) const;
416 static bool classof(const PathDiagnosticPiece *P) {
417 return P->getKind() == Event || P->getKind() == Macro;
421 /// \brief Interface for classes constructing Stack hints.
423 /// If a PathDiagnosticEvent occurs in a different frame than the final
424 /// diagnostic the hints can be used to summarize the effect of the call.
425 class StackHintGenerator {
427 virtual ~StackHintGenerator() = 0;
429 /// \brief Construct the Diagnostic message for the given ExplodedNode.
430 virtual std::string getMessage(const ExplodedNode *N) = 0;
433 /// \brief Constructs a Stack hint for the given symbol.
435 /// The class knows how to construct the stack hint message based on
436 /// traversing the CallExpr associated with the call and checking if the given
437 /// symbol is returned or is one of the arguments.
438 /// The hint can be customized by redefining 'getMessageForX()' methods.
439 class StackHintGeneratorForSymbol : public StackHintGenerator {
445 StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {}
446 virtual ~StackHintGeneratorForSymbol() {}
448 /// \brief Search the call expression for the symbol Sym and dispatch the
449 /// 'getMessageForX()' methods to construct a specific message.
450 virtual std::string getMessage(const ExplodedNode *N);
452 /// Produces the message of the following form:
453 /// 'Msg via Nth parameter'
454 virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex);
455 virtual std::string getMessageForReturn(const CallExpr *CallExpr) {
458 virtual std::string getMessageForSymbolNotFound() {
463 class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece {
464 llvm::Optional<bool> IsPrunable;
466 /// If the event occurs in a different frame than the final diagnostic,
467 /// supply a message that will be used to construct an extra hint on the
468 /// returns from all the calls on the stack from this event to the final
470 llvm::OwningPtr<StackHintGenerator> CallStackHint;
473 PathDiagnosticEventPiece(const PathDiagnosticLocation &pos,
474 StringRef s, bool addPosRange = true,
475 StackHintGenerator *stackHint = 0)
476 : PathDiagnosticSpotPiece(pos, s, Event, addPosRange),
477 CallStackHint(stackHint) {}
479 ~PathDiagnosticEventPiece();
481 /// Mark the diagnostic piece as being potentially prunable. This
482 /// flag may have been previously set, at which point it will not
483 /// be reset unless one specifies to do so.
484 void setPrunable(bool isPrunable, bool override = false) {
485 if (IsPrunable.hasValue() && !override)
487 IsPrunable = isPrunable;
490 /// Return true if the diagnostic piece is prunable.
491 bool isPrunable() const {
492 return IsPrunable.hasValue() ? IsPrunable.getValue() : false;
495 bool hasCallStackHint() {
496 return (CallStackHint != 0);
499 /// Produce the hint for the given node. The node contains
500 /// information about the call for which the diagnostic can be generated.
501 std::string getCallStackMessage(const ExplodedNode *N) {
503 return CallStackHint->getMessage(N);
507 static inline bool classof(const PathDiagnosticPiece *P) {
508 return P->getKind() == Event;
512 class PathDiagnosticCallPiece : public PathDiagnosticPiece {
513 PathDiagnosticCallPiece(const Decl *callerD,
514 const PathDiagnosticLocation &callReturnPos)
515 : PathDiagnosticPiece(Call), Caller(callerD), Callee(0),
516 NoExit(false), callReturn(callReturnPos) {}
518 PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller)
519 : PathDiagnosticPiece(Call), Caller(caller), Callee(0),
520 NoExit(true), path(oldPath) {}
525 // Flag signifying that this diagnostic has only call enter and no matching
529 // The custom string, which should appear after the call Return Diagnostic.
530 // TODO: Should we allow multiple diagnostics?
531 std::string CallStackMessage;
534 PathDiagnosticLocation callEnter;
535 PathDiagnosticLocation callEnterWithin;
536 PathDiagnosticLocation callReturn;
539 virtual ~PathDiagnosticCallPiece();
541 const Decl *getCaller() const { return Caller; }
543 const Decl *getCallee() const { return Callee; }
544 void setCallee(const CallEnter &CE, const SourceManager &SM);
546 bool hasCallStackMessage() { return !CallStackMessage.empty(); }
547 void setCallStackMessage(StringRef st) {
548 CallStackMessage = st;
551 virtual PathDiagnosticLocation getLocation() const {
555 IntrusiveRefCntPtr<PathDiagnosticEventPiece> getCallEnterEvent() const;
556 IntrusiveRefCntPtr<PathDiagnosticEventPiece>
557 getCallEnterWithinCallerEvent() const;
558 IntrusiveRefCntPtr<PathDiagnosticEventPiece> getCallExitEvent() const;
560 virtual void flattenLocations() {
562 callReturn.flatten();
563 for (PathPieces::iterator I = path.begin(),
564 E = path.end(); I != E; ++I) (*I)->flattenLocations();
567 static PathDiagnosticCallPiece *construct(const ExplodedNode *N,
568 const CallExitEnd &CE,
569 const SourceManager &SM);
571 static PathDiagnosticCallPiece *construct(PathPieces &pieces,
574 virtual void Profile(llvm::FoldingSetNodeID &ID) const;
576 static inline bool classof(const PathDiagnosticPiece *P) {
577 return P->getKind() == Call;
581 class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece {
582 std::vector<PathDiagnosticLocationPair> LPairs;
584 PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos,
585 const PathDiagnosticLocation &endPos,
587 : PathDiagnosticPiece(s, ControlFlow) {
588 LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos));
591 PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos,
592 const PathDiagnosticLocation &endPos)
593 : PathDiagnosticPiece(ControlFlow) {
594 LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos));
597 ~PathDiagnosticControlFlowPiece();
599 PathDiagnosticLocation getStartLocation() const {
600 assert(!LPairs.empty() &&
601 "PathDiagnosticControlFlowPiece needs at least one location.");
602 return LPairs[0].getStart();
605 PathDiagnosticLocation getEndLocation() const {
606 assert(!LPairs.empty() &&
607 "PathDiagnosticControlFlowPiece needs at least one location.");
608 return LPairs[0].getEnd();
611 void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); }
613 virtual PathDiagnosticLocation getLocation() const {
614 return getStartLocation();
617 typedef std::vector<PathDiagnosticLocationPair>::iterator iterator;
618 iterator begin() { return LPairs.begin(); }
619 iterator end() { return LPairs.end(); }
621 virtual void flattenLocations() {
622 for (iterator I=begin(), E=end(); I!=E; ++I) I->flatten();
625 typedef std::vector<PathDiagnosticLocationPair>::const_iterator
627 const_iterator begin() const { return LPairs.begin(); }
628 const_iterator end() const { return LPairs.end(); }
630 static inline bool classof(const PathDiagnosticPiece *P) {
631 return P->getKind() == ControlFlow;
634 virtual void Profile(llvm::FoldingSetNodeID &ID) const;
637 class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece {
639 PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos)
640 : PathDiagnosticSpotPiece(pos, "", Macro) {}
642 ~PathDiagnosticMacroPiece();
644 PathPieces subPieces;
646 bool containsEvent() const;
648 virtual void flattenLocations() {
649 PathDiagnosticSpotPiece::flattenLocations();
650 for (PathPieces::iterator I = subPieces.begin(),
651 E = subPieces.end(); I != E; ++I) (*I)->flattenLocations();
654 static inline bool classof(const PathDiagnosticPiece *P) {
655 return P->getKind() == Macro;
658 virtual void Profile(llvm::FoldingSetNodeID &ID) const;
661 /// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive
662 /// diagnostic. It represents an ordered-collection of PathDiagnosticPieces,
663 /// each which represent the pieces of the path.
664 class PathDiagnostic : public llvm::FoldingSetNode {
665 const Decl *DeclWithIssue;
667 std::string VerboseDesc;
668 std::string ShortDesc;
669 std::string Category;
670 std::deque<std::string> OtherDesc;
671 PathDiagnosticLocation Loc;
673 llvm::SmallVector<PathPieces *, 3> pathStack;
675 PathDiagnostic(); // Do not implement.
677 PathDiagnostic(const Decl *DeclWithIssue, StringRef bugtype,
678 StringRef verboseDesc, StringRef shortDesc,
683 const PathPieces &path;
685 /// Return the path currently used by builders for constructing the
687 PathPieces &getActivePath() {
688 if (pathStack.empty())
690 return *pathStack.back();
693 /// Return a mutable version of 'path'.
694 PathPieces &getMutablePieces() {
698 /// Return the unrolled size of the path.
699 unsigned full_size();
701 void pushActivePath(PathPieces *p) { pathStack.push_back(p); }
702 void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); }
704 bool isWithinCall() const { return !pathStack.empty(); }
706 void setEndOfPath(PathDiagnosticPiece *EndPiece) {
707 assert(!Loc.isValid() && "End location already set!");
708 Loc = EndPiece->getLocation();
709 assert(Loc.isValid() && "Invalid location for end-of-path piece");
710 getActivePath().push_back(EndPiece);
716 Loc = PathDiagnosticLocation();
719 StringRef getVerboseDescription() const { return VerboseDesc; }
720 StringRef getShortDescription() const {
721 return ShortDesc.empty() ? VerboseDesc : ShortDesc;
723 StringRef getBugType() const { return BugType; }
724 StringRef getCategory() const { return Category; }
726 /// Return the semantic context where an issue occurred. If the
727 /// issue occurs along a path, this represents the "central" area
728 /// where the bug manifests.
729 const Decl *getDeclWithIssue() const { return DeclWithIssue; }
731 typedef std::deque<std::string>::const_iterator meta_iterator;
732 meta_iterator meta_begin() const { return OtherDesc.begin(); }
733 meta_iterator meta_end() const { return OtherDesc.end(); }
734 void addMeta(StringRef s) { OtherDesc.push_back(s); }
736 PathDiagnosticLocation getLocation() const {
737 assert(Loc.isValid() && "No end-of-path location set yet!");
741 void flattenLocations() {
743 for (PathPieces::iterator I = pathImpl.begin(), E = pathImpl.end();
744 I != E; ++I) (*I)->flattenLocations();
747 /// Profiles the diagnostic, independent of the path it references.
749 /// This can be used to merge diagnostics that refer to the same issue
750 /// along different paths.
751 void Profile(llvm::FoldingSetNodeID &ID) const;
753 /// Profiles the diagnostic, including its path.
755 /// Two diagnostics with the same issue along different paths will generate
756 /// different profiles.
757 void FullProfile(llvm::FoldingSetNodeID &ID) const;
760 } // end GR namespace
762 } //end clang namespace