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 typedef std::vector<std::pair<StringRef, std::string> > FilesMade;
58 virtual void anchor();
60 PathDiagnosticConsumer() : flushed(false) {}
61 virtual ~PathDiagnosticConsumer();
63 void FlushDiagnostics(FilesMade *FilesMade);
65 virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
66 FilesMade *filesMade) = 0;
68 virtual StringRef getName() const = 0;
70 void HandlePathDiagnostic(PathDiagnostic *D);
72 enum PathGenerationScheme { None, Minimal, Extensive };
73 virtual PathGenerationScheme getGenerationScheme() const { return Minimal; }
74 virtual bool supportsLogicalOpControlFlow() const { return false; }
75 virtual bool supportsAllBlockEdges() const { return false; }
76 virtual bool useVerboseDescription() const { return true; }
78 /// Return true if the PathDiagnosticConsumer supports individual
79 /// PathDiagnostics that span multiple files.
80 virtual bool supportsCrossFileDiagnostics() const { return false; }
84 llvm::FoldingSet<PathDiagnostic> Diags;
87 //===----------------------------------------------------------------------===//
88 // Path-sensitive diagnostics.
89 //===----------------------------------------------------------------------===//
91 class PathDiagnosticRange : public SourceRange {
95 PathDiagnosticRange(const SourceRange &R, bool isP = false)
96 : SourceRange(R), isPoint(isP) {}
98 PathDiagnosticRange() : isPoint(false) {}
101 typedef llvm::PointerUnion<const LocationContext*, AnalysisDeclContext*>
102 LocationOrAnalysisDeclContext;
104 class PathDiagnosticLocation {
106 enum Kind { RangeK, SingleLocK, StmtK, DeclK } K;
109 const SourceManager *SM;
111 PathDiagnosticRange Range;
113 PathDiagnosticLocation(SourceLocation L, const SourceManager &sm,
115 : K(kind), S(0), D(0), SM(&sm),
116 Loc(genLocation(L)), Range(genRange()) {
117 assert(Loc.isValid());
118 assert(Range.isValid());
122 genLocation(SourceLocation L = SourceLocation(),
123 LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext*)0) const;
126 genRange(LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext*)0) const;
129 /// Create an invalid location.
130 PathDiagnosticLocation()
131 : K(SingleLocK), S(0), D(0), SM(0) {}
133 /// Create a location corresponding to the given statement.
134 PathDiagnosticLocation(const Stmt *s,
135 const SourceManager &sm,
136 LocationOrAnalysisDeclContext lac)
137 : K(StmtK), S(s), D(0), SM(&sm),
138 Loc(genLocation(SourceLocation(), lac)),
139 Range(genRange(lac)) {
141 assert(Loc.isValid());
142 assert(Range.isValid());
145 /// Create a location corresponding to the given declaration.
146 PathDiagnosticLocation(const Decl *d, const SourceManager &sm)
147 : K(DeclK), S(0), D(d), SM(&sm),
148 Loc(genLocation()), Range(genRange()) {
150 assert(Loc.isValid());
151 assert(Range.isValid());
154 /// Create a location at an explicit offset in the source.
156 /// This should only be used if there are no more appropriate constructors.
157 PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm)
158 : K(SingleLocK), S(0), D(0), SM(&sm), Loc(loc, sm), Range(genRange()) {
159 assert(Loc.isValid());
160 assert(Range.isValid());
163 /// Create a location corresponding to the given declaration.
164 static PathDiagnosticLocation create(const Decl *D,
165 const SourceManager &SM) {
166 return PathDiagnosticLocation(D, SM);
169 /// Create a location for the beginning of the declaration.
170 static PathDiagnosticLocation createBegin(const Decl *D,
171 const SourceManager &SM);
173 /// Create a location for the beginning of the statement.
174 static PathDiagnosticLocation createBegin(const Stmt *S,
175 const SourceManager &SM,
176 const LocationOrAnalysisDeclContext LAC);
178 /// Create a location for the end of the statement.
180 /// If the statement is a CompoundStatement, the location will point to the
181 /// closing brace instead of following it.
182 static PathDiagnosticLocation createEnd(const Stmt *S,
183 const SourceManager &SM,
184 const LocationOrAnalysisDeclContext LAC);
186 /// Create the location for the operator of the binary expression.
187 /// Assumes the statement has a valid location.
188 static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO,
189 const SourceManager &SM);
191 /// For member expressions, return the location of the '.' or '->'.
192 /// Assumes the statement has a valid location.
193 static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME,
194 const SourceManager &SM);
196 /// Create a location for the beginning of the compound statement.
197 /// Assumes the statement has a valid location.
198 static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS,
199 const SourceManager &SM);
201 /// Create a location for the end of the compound statement.
202 /// Assumes the statement has a valid location.
203 static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS,
204 const SourceManager &SM);
206 /// Create a location for the beginning of the enclosing declaration body.
207 /// Defaults to the beginning of the first statement in the declaration body.
208 static PathDiagnosticLocation createDeclBegin(const LocationContext *LC,
209 const SourceManager &SM);
211 /// Constructs a location for the end of the enclosing declaration body.
212 /// Defaults to the end of brace.
213 static PathDiagnosticLocation createDeclEnd(const LocationContext *LC,
214 const SourceManager &SM);
216 /// Create a location corresponding to the given valid ExplodedNode.
217 static PathDiagnosticLocation create(const ProgramPoint& P,
218 const SourceManager &SMng);
220 /// Create a location corresponding to the next valid ExplodedNode as end
221 /// of path location.
222 static PathDiagnosticLocation createEndOfPath(const ExplodedNode* N,
223 const SourceManager &SM);
225 /// Convert the given location into a single kind location.
226 static PathDiagnosticLocation createSingleLocation(
227 const PathDiagnosticLocation &PDL);
229 bool operator==(const PathDiagnosticLocation &X) const {
230 return K == X.K && Loc == X.Loc && Range == X.Range;
233 bool operator!=(const PathDiagnosticLocation &X) const {
234 return !(*this == X);
237 bool isValid() const {
241 FullSourceLoc asLocation() const {
245 PathDiagnosticRange asRange() const {
249 const Stmt *asStmt() const { assert(isValid()); return S; }
250 const Decl *asDecl() const { assert(isValid()); return D; }
252 bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; }
255 *this = PathDiagnosticLocation();
260 const SourceManager& getManager() const { assert(isValid()); return *SM; }
262 void Profile(llvm::FoldingSetNodeID &ID) const;
265 class PathDiagnosticLocationPair {
267 PathDiagnosticLocation Start, End;
269 PathDiagnosticLocationPair(const PathDiagnosticLocation &start,
270 const PathDiagnosticLocation &end)
271 : Start(start), End(end) {}
273 const PathDiagnosticLocation &getStart() const { return Start; }
274 const PathDiagnosticLocation &getEnd() const { return End; }
281 void Profile(llvm::FoldingSetNodeID &ID) const {
287 //===----------------------------------------------------------------------===//
288 // Path "pieces" for path-sensitive diagnostics.
289 //===----------------------------------------------------------------------===//
291 class PathDiagnosticPiece : public RefCountedBaseVPTR {
293 enum Kind { ControlFlow, Event, Macro, Call };
294 enum DisplayHint { Above, Below };
297 const std::string str;
299 const DisplayHint Hint;
300 std::vector<SourceRange> ranges;
303 PathDiagnosticPiece();
304 PathDiagnosticPiece(const PathDiagnosticPiece &P);
305 PathDiagnosticPiece& operator=(const PathDiagnosticPiece &P);
308 PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below);
310 PathDiagnosticPiece(Kind k, DisplayHint hint = Below);
313 virtual ~PathDiagnosticPiece();
315 const std::string& getString() const { return str; }
317 /// getDisplayHint - Return a hint indicating where the diagnostic should
318 /// be displayed by the PathDiagnosticConsumer.
319 DisplayHint getDisplayHint() const { return Hint; }
321 virtual PathDiagnosticLocation getLocation() const = 0;
322 virtual void flattenLocations() = 0;
324 Kind getKind() const { return kind; }
326 void addRange(SourceRange R) {
332 void addRange(SourceLocation B, SourceLocation E) {
333 if (!B.isValid() || !E.isValid())
335 ranges.push_back(SourceRange(B,E));
338 /// Return the SourceRanges associated with this PathDiagnosticPiece.
339 ArrayRef<SourceRange> getRanges() const { return ranges; }
341 static inline bool classof(const PathDiagnosticPiece *P) {
345 virtual void Profile(llvm::FoldingSetNodeID &ID) const;
349 class PathPieces : public std::deque<IntrusiveRefCntPtr<PathDiagnosticPiece> > {
350 void flattenTo(PathPieces &Primary, PathPieces &Current,
351 bool ShouldFlattenMacros) const;
355 PathPieces flatten(bool ShouldFlattenMacros) const {
357 flattenTo(Result, Result, ShouldFlattenMacros);
362 class PathDiagnosticSpotPiece : public PathDiagnosticPiece {
364 PathDiagnosticLocation Pos;
366 PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos,
368 PathDiagnosticPiece::Kind k,
369 bool addPosRange = true)
370 : PathDiagnosticPiece(s, k), Pos(pos) {
371 assert(Pos.isValid() && Pos.asLocation().isValid() &&
372 "PathDiagnosticSpotPiece's must have a valid location.");
373 if (addPosRange && Pos.hasRange()) addRange(Pos.asRange());
376 PathDiagnosticLocation getLocation() const { return Pos; }
377 virtual void flattenLocations() { Pos.flatten(); }
379 virtual void Profile(llvm::FoldingSetNodeID &ID) const;
382 /// \brief Interface for classes constructing Stack hints.
384 /// If a PathDiagnosticEvent occurs in a different frame than the final
385 /// diagnostic the hints can be used to summarize the effect of the call.
386 class StackHintGenerator {
388 virtual ~StackHintGenerator() = 0;
390 /// \brief Construct the Diagnostic message for the given ExplodedNode.
391 virtual std::string getMessage(const ExplodedNode *N) = 0;
394 /// \brief Constructs a Stack hint for the given symbol.
396 /// The class knows how to construct the stack hint message based on
397 /// traversing the CallExpr associated with the call and checking if the given
398 /// symbol is returned or is one of the arguments.
399 /// The hint can be customized by redefining 'getMessageForX()' methods.
400 class StackHintGeneratorForSymbol : public StackHintGenerator {
406 StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {}
407 virtual ~StackHintGeneratorForSymbol() {}
409 /// \brief Search the call expression for the symbol Sym and dispatch the
410 /// 'getMessageForX()' methods to construct a specific message.
411 virtual std::string getMessage(const ExplodedNode *N);
413 /// Prints the ordinal form of the given integer,
414 /// only valid for ValNo : ValNo > 0.
415 void printOrdinal(unsigned ValNo, llvm::raw_svector_ostream &Out);
417 /// Produces the message of the following form:
418 /// 'Msg via Nth parameter'
419 virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex);
420 virtual std::string getMessageForReturn(const CallExpr *CallExpr) {
423 virtual std::string getMessageForSymbolNotFound() {
428 class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece {
429 llvm::Optional<bool> IsPrunable;
431 /// If the event occurs in a different frame than the final diagnostic,
432 /// supply a message that will be used to construct an extra hint on the
433 /// returns from all the calls on the stack from this event to the final
435 llvm::OwningPtr<StackHintGenerator> CallStackHint;
438 PathDiagnosticEventPiece(const PathDiagnosticLocation &pos,
439 StringRef s, bool addPosRange = true,
440 StackHintGenerator *stackHint = 0)
441 : PathDiagnosticSpotPiece(pos, s, Event, addPosRange),
442 CallStackHint(stackHint) {}
444 ~PathDiagnosticEventPiece();
446 /// Mark the diagnostic piece as being potentially prunable. This
447 /// flag may have been previously set, at which point it will not
448 /// be reset unless one specifies to do so.
449 void setPrunable(bool isPrunable, bool override = false) {
450 if (IsPrunable.hasValue() && !override)
452 IsPrunable = isPrunable;
455 /// Return true if the diagnostic piece is prunable.
456 bool isPrunable() const {
457 return IsPrunable.hasValue() ? IsPrunable.getValue() : false;
460 bool hasCallStackHint() {
461 return (CallStackHint != 0);
464 /// Produce the hint for the given node. The node contains
465 /// information about the call for which the diagnostic can be generated.
466 std::string getCallStackMessage(const ExplodedNode *N) {
468 return CallStackHint->getMessage(N);
472 static inline bool classof(const PathDiagnosticPiece *P) {
473 return P->getKind() == Event;
477 class PathDiagnosticCallPiece : public PathDiagnosticPiece {
478 PathDiagnosticCallPiece(const Decl *callerD,
479 const PathDiagnosticLocation &callReturnPos)
480 : PathDiagnosticPiece(Call), Caller(callerD), Callee(0),
481 NoExit(false), callReturn(callReturnPos) {}
483 PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller)
484 : PathDiagnosticPiece(Call), Caller(caller), Callee(0),
485 NoExit(true), path(oldPath) {}
490 // Flag signifying that this diagnostic has only call enter and no matching
494 // The custom string, which should appear after the call Return Diagnostic.
495 // TODO: Should we allow multiple diagnostics?
496 std::string CallStackMessage;
499 PathDiagnosticLocation callEnter;
500 PathDiagnosticLocation callEnterWithin;
501 PathDiagnosticLocation callReturn;
504 virtual ~PathDiagnosticCallPiece();
506 const Decl *getCaller() const { return Caller; }
508 const Decl *getCallee() const { return Callee; }
509 void setCallee(const CallEnter &CE, const SourceManager &SM);
511 bool hasCallStackMessage() { return !CallStackMessage.empty(); }
512 void setCallStackMessage(StringRef st) {
513 CallStackMessage = st;
516 virtual PathDiagnosticLocation getLocation() const {
520 IntrusiveRefCntPtr<PathDiagnosticEventPiece> getCallEnterEvent() const;
521 IntrusiveRefCntPtr<PathDiagnosticEventPiece>
522 getCallEnterWithinCallerEvent() const;
523 IntrusiveRefCntPtr<PathDiagnosticEventPiece> getCallExitEvent() const;
525 virtual void flattenLocations() {
527 callReturn.flatten();
528 for (PathPieces::iterator I = path.begin(),
529 E = path.end(); I != E; ++I) (*I)->flattenLocations();
532 static PathDiagnosticCallPiece *construct(const ExplodedNode *N,
533 const CallExitEnd &CE,
534 const SourceManager &SM);
536 static PathDiagnosticCallPiece *construct(PathPieces &pieces,
539 virtual void Profile(llvm::FoldingSetNodeID &ID) const;
541 static inline bool classof(const PathDiagnosticPiece *P) {
542 return P->getKind() == Call;
546 class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece {
547 std::vector<PathDiagnosticLocationPair> LPairs;
549 PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos,
550 const PathDiagnosticLocation &endPos,
552 : PathDiagnosticPiece(s, ControlFlow) {
553 LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos));
556 PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos,
557 const PathDiagnosticLocation &endPos)
558 : PathDiagnosticPiece(ControlFlow) {
559 LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos));
562 ~PathDiagnosticControlFlowPiece();
564 PathDiagnosticLocation getStartLocation() const {
565 assert(!LPairs.empty() &&
566 "PathDiagnosticControlFlowPiece needs at least one location.");
567 return LPairs[0].getStart();
570 PathDiagnosticLocation getEndLocation() const {
571 assert(!LPairs.empty() &&
572 "PathDiagnosticControlFlowPiece needs at least one location.");
573 return LPairs[0].getEnd();
576 void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); }
578 virtual PathDiagnosticLocation getLocation() const {
579 return getStartLocation();
582 typedef std::vector<PathDiagnosticLocationPair>::iterator iterator;
583 iterator begin() { return LPairs.begin(); }
584 iterator end() { return LPairs.end(); }
586 virtual void flattenLocations() {
587 for (iterator I=begin(), E=end(); I!=E; ++I) I->flatten();
590 typedef std::vector<PathDiagnosticLocationPair>::const_iterator
592 const_iterator begin() const { return LPairs.begin(); }
593 const_iterator end() const { return LPairs.end(); }
595 static inline bool classof(const PathDiagnosticPiece *P) {
596 return P->getKind() == ControlFlow;
599 virtual void Profile(llvm::FoldingSetNodeID &ID) const;
602 class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece {
604 PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos)
605 : PathDiagnosticSpotPiece(pos, "", Macro) {}
607 ~PathDiagnosticMacroPiece();
609 PathPieces subPieces;
611 bool containsEvent() const;
613 virtual void flattenLocations() {
614 PathDiagnosticSpotPiece::flattenLocations();
615 for (PathPieces::iterator I = subPieces.begin(),
616 E = subPieces.end(); I != E; ++I) (*I)->flattenLocations();
619 static inline bool classof(const PathDiagnosticPiece *P) {
620 return P->getKind() == Macro;
623 virtual void Profile(llvm::FoldingSetNodeID &ID) const;
626 /// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive
627 /// diagnostic. It represents an ordered-collection of PathDiagnosticPieces,
628 /// each which represent the pieces of the path.
629 class PathDiagnostic : public llvm::FoldingSetNode {
630 const Decl *DeclWithIssue;
633 std::string Category;
634 std::deque<std::string> OtherDesc;
636 llvm::SmallVector<PathPieces *, 3> pathStack;
638 PathDiagnostic(); // Do not implement.
640 const PathPieces &path;
642 /// Return the path currently used by builders for constructing the
644 PathPieces &getActivePath() {
645 if (pathStack.empty())
647 return *pathStack.back();
650 /// Return a mutable version of 'path'.
651 PathPieces &getMutablePieces() {
655 /// Return the unrolled size of the path.
656 unsigned full_size();
658 void pushActivePath(PathPieces *p) { pathStack.push_back(p); }
659 void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); }
661 bool isWithinCall() const { return !pathStack.empty(); }
664 PathDiagnostic(const Decl *DeclWithIssue,
671 StringRef getDescription() const { return Desc; }
672 StringRef getBugType() const { return BugType; }
673 StringRef getCategory() const { return Category; }
675 /// Return the semantic context where an issue occurred. If the
676 /// issue occurs along a path, this represents the "central" area
677 /// where the bug manifests.
678 const Decl *getDeclWithIssue() const { return DeclWithIssue; }
680 typedef std::deque<std::string>::const_iterator meta_iterator;
681 meta_iterator meta_begin() const { return OtherDesc.begin(); }
682 meta_iterator meta_end() const { return OtherDesc.end(); }
683 void addMeta(StringRef s) { OtherDesc.push_back(s); }
685 PathDiagnosticLocation getLocation() const;
687 void flattenLocations() {
688 for (PathPieces::iterator I = pathImpl.begin(), E = pathImpl.end();
689 I != E; ++I) (*I)->flattenLocations();
692 void Profile(llvm::FoldingSetNodeID &ID) const;
694 void FullProfile(llvm::FoldingSetNodeID &ID) const;
697 } // end GR namespace
699 } //end clang namespace