PathDiagnostic.h revision 0b3ade86a1c60cf0c7b56aa238aff458eb7f5974
1//===--- PathDiagnostic.h - Path-Specific Diagnostic Handling ---*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10//  This file defines the PathDiagnostic-related interfaces.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_CLANG_PATH_DIAGNOSTIC_H
15#define LLVM_CLANG_PATH_DIAGNOSTIC_H
16
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"
23#include <deque>
24#include <iterator>
25#include <string>
26#include <vector>
27
28namespace clang {
29
30class AnalysisDeclContext;
31class BinaryOperator;
32class CompoundStmt;
33class Decl;
34class LocationContext;
35class MemberExpr;
36class ParentMap;
37class ProgramPoint;
38class SourceManager;
39class Stmt;
40
41namespace ento {
42
43class ExplodedNode;
44class SymExpr;
45typedef const SymExpr* SymbolRef;
46
47//===----------------------------------------------------------------------===//
48// High-level interface for handlers of path-sensitive diagnostics.
49//===----------------------------------------------------------------------===//
50
51class PathDiagnostic;
52
53class PathDiagnosticConsumer {
54  virtual void anchor();
55public:
56  PathDiagnosticConsumer() : flushed(false) {}
57  virtual ~PathDiagnosticConsumer();
58
59  void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade);
60
61  virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
62                                    SmallVectorImpl<std::string> *FilesMade)
63                                    = 0;
64
65  virtual StringRef getName() const = 0;
66
67  void HandlePathDiagnostic(PathDiagnostic *D);
68
69  enum PathGenerationScheme { Minimal, Extensive };
70  virtual PathGenerationScheme getGenerationScheme() const { return Minimal; }
71  virtual bool supportsLogicalOpControlFlow() const { return false; }
72  virtual bool supportsAllBlockEdges() const { return false; }
73  virtual bool useVerboseDescription() const { return true; }
74
75  /// Return true if the PathDiagnosticConsumer supports individual
76  /// PathDiagnostics that span multiple files.
77  virtual bool supportsCrossFileDiagnostics() const { return false; }
78
79protected:
80  bool flushed;
81  llvm::FoldingSet<PathDiagnostic> Diags;
82};
83
84//===----------------------------------------------------------------------===//
85// Path-sensitive diagnostics.
86//===----------------------------------------------------------------------===//
87
88class PathDiagnosticRange : public SourceRange {
89public:
90  bool isPoint;
91
92  PathDiagnosticRange(const SourceRange &R, bool isP = false)
93    : SourceRange(R), isPoint(isP) {}
94
95  PathDiagnosticRange() : isPoint(false) {}
96};
97
98typedef llvm::PointerUnion<const LocationContext*, AnalysisDeclContext*>
99                                                   LocationOrAnalysisDeclContext;
100
101class PathDiagnosticLocation {
102private:
103  enum Kind { RangeK, SingleLocK, StmtK, DeclK } K;
104  const Stmt *S;
105  const Decl *D;
106  const SourceManager *SM;
107  FullSourceLoc Loc;
108  PathDiagnosticRange Range;
109
110  PathDiagnosticLocation(SourceLocation L, const SourceManager &sm,
111                         Kind kind)
112    : K(kind), S(0), D(0), SM(&sm),
113      Loc(genLocation(L)), Range(genRange()) {
114    assert(Loc.isValid());
115    assert(Range.isValid());
116  }
117
118  FullSourceLoc
119    genLocation(SourceLocation L = SourceLocation(),
120                LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext*)0) const;
121
122  PathDiagnosticRange
123    genRange(LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext*)0) const;
124
125public:
126  /// Create an invalid location.
127  PathDiagnosticLocation()
128    : K(SingleLocK), S(0), D(0), SM(0) {}
129
130  /// Create a location corresponding to the given statement.
131  PathDiagnosticLocation(const Stmt *s,
132                         const SourceManager &sm,
133                         LocationOrAnalysisDeclContext lac)
134    : K(StmtK), S(s), D(0), SM(&sm),
135      Loc(genLocation(SourceLocation(), lac)),
136      Range(genRange(lac)) {
137    assert(S);
138    assert(Loc.isValid());
139    assert(Range.isValid());
140  }
141
142  /// Create a location corresponding to the given declaration.
143  PathDiagnosticLocation(const Decl *d, const SourceManager &sm)
144    : K(DeclK), S(0), D(d), SM(&sm),
145      Loc(genLocation()), Range(genRange()) {
146    assert(D);
147    assert(Loc.isValid());
148    assert(Range.isValid());
149  }
150
151  /// Create a location corresponding to the given declaration.
152  static PathDiagnosticLocation create(const Decl *D,
153                                       const SourceManager &SM) {
154    return PathDiagnosticLocation(D, SM);
155  }
156
157  /// Create a location for the beginning of the declaration.
158  static PathDiagnosticLocation createBegin(const Decl *D,
159                                            const SourceManager &SM);
160
161  /// Create a location for the beginning of the statement.
162  static PathDiagnosticLocation createBegin(const Stmt *S,
163                                            const SourceManager &SM,
164                                            const LocationOrAnalysisDeclContext LAC);
165
166  /// Create the location for the operator of the binary expression.
167  /// Assumes the statement has a valid location.
168  static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO,
169                                                  const SourceManager &SM);
170
171  /// For member expressions, return the location of the '.' or '->'.
172  /// Assumes the statement has a valid location.
173  static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME,
174                                                const SourceManager &SM);
175
176  /// Create a location for the beginning of the compound statement.
177  /// Assumes the statement has a valid location.
178  static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS,
179                                                 const SourceManager &SM);
180
181  /// Create a location for the end of the compound statement.
182  /// Assumes the statement has a valid location.
183  static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS,
184                                               const SourceManager &SM);
185
186  /// Create a location for the beginning of the enclosing declaration body.
187  /// Defaults to the beginning of the first statement in the declaration body.
188  static PathDiagnosticLocation createDeclBegin(const LocationContext *LC,
189                                                const SourceManager &SM);
190
191  /// Constructs a location for the end of the enclosing declaration body.
192  /// Defaults to the end of brace.
193  static PathDiagnosticLocation createDeclEnd(const LocationContext *LC,
194                                                   const SourceManager &SM);
195
196  /// Create a location corresponding to the given valid ExplodedNode.
197  static PathDiagnosticLocation create(const ProgramPoint& P,
198                                       const SourceManager &SMng);
199
200  /// Create a location corresponding to the next valid ExplodedNode as end
201  /// of path location.
202  static PathDiagnosticLocation createEndOfPath(const ExplodedNode* N,
203                                                const SourceManager &SM);
204
205  /// Convert the given location into a single kind location.
206  static PathDiagnosticLocation createSingleLocation(
207                                             const PathDiagnosticLocation &PDL);
208
209  bool operator==(const PathDiagnosticLocation &X) const {
210    return K == X.K && Loc == X.Loc && Range == X.Range;
211  }
212
213  bool operator!=(const PathDiagnosticLocation &X) const {
214    return !(*this == X);
215  }
216
217  bool isValid() const {
218    return SM != 0;
219  }
220
221  FullSourceLoc asLocation() const {
222    return Loc;
223  }
224
225  PathDiagnosticRange asRange() const {
226    return Range;
227  }
228
229  const Stmt *asStmt() const { assert(isValid()); return S; }
230  const Decl *asDecl() const { assert(isValid()); return D; }
231
232  bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; }
233
234  void invalidate() {
235    *this = PathDiagnosticLocation();
236  }
237
238  void flatten();
239
240  const SourceManager& getManager() const { assert(isValid()); return *SM; }
241
242  void Profile(llvm::FoldingSetNodeID &ID) const;
243};
244
245class PathDiagnosticLocationPair {
246private:
247  PathDiagnosticLocation Start, End;
248public:
249  PathDiagnosticLocationPair(const PathDiagnosticLocation &start,
250                             const PathDiagnosticLocation &end)
251    : Start(start), End(end) {}
252
253  const PathDiagnosticLocation &getStart() const { return Start; }
254  const PathDiagnosticLocation &getEnd() const { return End; }
255
256  void flatten() {
257    Start.flatten();
258    End.flatten();
259  }
260
261  void Profile(llvm::FoldingSetNodeID &ID) const {
262    Start.Profile(ID);
263    End.Profile(ID);
264  }
265};
266
267//===----------------------------------------------------------------------===//
268// Path "pieces" for path-sensitive diagnostics.
269//===----------------------------------------------------------------------===//
270
271class PathDiagnosticPiece : public RefCountedBaseVPTR {
272public:
273  enum Kind { ControlFlow, Event, Macro, Call };
274  enum DisplayHint { Above, Below };
275
276private:
277  const std::string str;
278  const Kind kind;
279  const DisplayHint Hint;
280  std::vector<SourceRange> ranges;
281
282  // Do not implement:
283  PathDiagnosticPiece();
284  PathDiagnosticPiece(const PathDiagnosticPiece &P);
285  PathDiagnosticPiece& operator=(const PathDiagnosticPiece &P);
286
287protected:
288  PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below);
289
290  PathDiagnosticPiece(Kind k, DisplayHint hint = Below);
291
292public:
293  virtual ~PathDiagnosticPiece();
294
295  const std::string& getString() const { return str; }
296
297  /// getDisplayHint - Return a hint indicating where the diagnostic should
298  ///  be displayed by the PathDiagnosticConsumer.
299  DisplayHint getDisplayHint() const { return Hint; }
300
301  virtual PathDiagnosticLocation getLocation() const = 0;
302  virtual void flattenLocations() = 0;
303
304  Kind getKind() const { return kind; }
305
306  void addRange(SourceRange R) {
307    if (!R.isValid())
308      return;
309    ranges.push_back(R);
310  }
311
312  void addRange(SourceLocation B, SourceLocation E) {
313    if (!B.isValid() || !E.isValid())
314      return;
315    ranges.push_back(SourceRange(B,E));
316  }
317
318  typedef const SourceRange* range_iterator;
319
320  range_iterator ranges_begin() const {
321    return ranges.empty() ? NULL : &ranges[0];
322  }
323
324  range_iterator ranges_end() const {
325    return ranges_begin() + ranges.size();
326  }
327
328  static inline bool classof(const PathDiagnosticPiece *P) {
329    return true;
330  }
331
332  virtual void Profile(llvm::FoldingSetNodeID &ID) const;
333};
334
335
336class PathPieces :
337  public std::deque<IntrusiveRefCntPtr<PathDiagnosticPiece> > {
338public:
339  ~PathPieces();
340};
341
342class PathDiagnosticSpotPiece : public PathDiagnosticPiece {
343private:
344  PathDiagnosticLocation Pos;
345public:
346  PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos,
347                          StringRef s,
348                          PathDiagnosticPiece::Kind k,
349                          bool addPosRange = true)
350  : PathDiagnosticPiece(s, k), Pos(pos) {
351    assert(Pos.isValid() && Pos.asLocation().isValid() &&
352           "PathDiagnosticSpotPiece's must have a valid location.");
353    if (addPosRange && Pos.hasRange()) addRange(Pos.asRange());
354  }
355
356  PathDiagnosticLocation getLocation() const { return Pos; }
357  virtual void flattenLocations() { Pos.flatten(); }
358
359  virtual void Profile(llvm::FoldingSetNodeID &ID) const;
360};
361
362/// \brief Interface for classes constructing Stack hints.
363///
364/// If a PathDiagnosticEvent occurs in a different frame than the final
365/// diagnostic the hints can be used to summarize the effect of the call.
366class StackHintGenerator {
367public:
368  virtual ~StackHintGenerator() = 0;
369
370  /// \brief Construct the Diagnostic message for the given ExplodedNode.
371  virtual std::string getMessage(const ExplodedNode *N) = 0;
372};
373
374/// \brief Constructs a Stack hint for the given symbol.
375///
376/// The class knows how to construct the stack hint message based on
377/// traversing the CallExpr associated with the call and checking if the given
378/// symbol is returned or is one of the arguments.
379/// The hint can be customized by redefining 'getMessageForX()' methods.
380class StackHintGeneratorForSymbol : public StackHintGenerator {
381private:
382  SymbolRef Sym;
383  std::string Msg;
384
385public:
386  StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {}
387  virtual ~StackHintGeneratorForSymbol() {}
388
389  /// \brief Search the call expression for the symbol Sym and dispatch the
390  /// 'getMessageForX()' methods to construct a specific message.
391  virtual std::string getMessage(const ExplodedNode *N);
392
393  /// Prints the ordinal form of the given integer,
394  /// only valid for ValNo : ValNo > 0.
395  void printOrdinal(unsigned ValNo, llvm::raw_svector_ostream &Out);
396
397  /// Produces the message of the following form:
398  ///   'Msg via Nth parameter'
399  virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex);
400  virtual std::string getMessageForReturn(const CallExpr *CallExpr) {
401    return Msg;
402  }
403  virtual std::string getMessageForSymbolNotFound() {
404    return Msg;
405  }
406};
407
408class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece {
409  llvm::Optional<bool> IsPrunable;
410
411  /// If the event occurs in a different frame than the final diagnostic,
412  /// supply a message that will be used to construct an extra hint on the
413  /// returns from all the calls on the stack from this event to the final
414  /// diagnostic.
415  llvm::OwningPtr<StackHintGenerator> CallStackHint;
416
417public:
418  PathDiagnosticEventPiece(const PathDiagnosticLocation &pos,
419                           StringRef s, bool addPosRange = true,
420                           StackHintGenerator *stackHint = 0)
421    : PathDiagnosticSpotPiece(pos, s, Event, addPosRange),
422      CallStackHint(stackHint) {}
423
424  ~PathDiagnosticEventPiece();
425
426  /// Mark the diagnostic piece as being potentially prunable.  This
427  /// flag may have been previously set, at which point it will not
428  /// be reset unless one specifies to do so.
429  void setPrunable(bool isPrunable, bool override = false) {
430    if (IsPrunable.hasValue() && !override)
431     return;
432    IsPrunable = isPrunable;
433  }
434
435  /// Return true if the diagnostic piece is prunable.
436  bool isPrunable() const {
437    return IsPrunable.hasValue() ? IsPrunable.getValue() : false;
438  }
439
440  bool hasCallStackHint() {
441    return (CallStackHint != 0);
442  }
443
444  /// Produce the hint for the given node. The node contains
445  /// information about the call for which the diagnostic can be generated.
446  std::string getCallStackMessage(const ExplodedNode *N) {
447    if (CallStackHint)
448      return CallStackHint->getMessage(N);
449    return "";
450  }
451
452  static inline bool classof(const PathDiagnosticPiece *P) {
453    return P->getKind() == Event;
454  }
455};
456
457class PathDiagnosticCallPiece : public PathDiagnosticPiece {
458  PathDiagnosticCallPiece(const Decl *callerD,
459                          const PathDiagnosticLocation &callReturnPos)
460    : PathDiagnosticPiece(Call), Caller(callerD), Callee(0),
461      NoExit(false), callReturn(callReturnPos) {}
462
463  PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller)
464    : PathDiagnosticPiece(Call), Caller(caller), Callee(0),
465      NoExit(true), path(oldPath) {}
466
467  const Decl *Caller;
468  const Decl *Callee;
469
470  // Flag signifying that this diagnostic has only call enter and no matching
471  // call exit.
472  bool NoExit;
473
474  // The custom string, which should appear after the call Return Diagnostic.
475  // TODO: Should we allow multiple diagnostics?
476  std::string CallStackMessage;
477
478public:
479  PathDiagnosticLocation callEnter;
480  PathDiagnosticLocation callEnterWithin;
481  PathDiagnosticLocation callReturn;
482  PathPieces path;
483
484  virtual ~PathDiagnosticCallPiece();
485
486  const Decl *getCaller() const { return Caller; }
487
488  const Decl *getCallee() const { return Callee; }
489  void setCallee(const CallEnter &CE, const SourceManager &SM);
490
491  bool hasCallStackMessage() { return !CallStackMessage.empty(); }
492  void setCallStackMessage(StringRef st) {
493    CallStackMessage = st;
494  }
495
496  virtual PathDiagnosticLocation getLocation() const {
497    return callEnter;
498  }
499
500  IntrusiveRefCntPtr<PathDiagnosticEventPiece> getCallEnterEvent() const;
501  IntrusiveRefCntPtr<PathDiagnosticEventPiece>
502    getCallEnterWithinCallerEvent() const;
503  IntrusiveRefCntPtr<PathDiagnosticEventPiece> getCallExitEvent() const;
504
505  virtual void flattenLocations() {
506    callEnter.flatten();
507    callReturn.flatten();
508    for (PathPieces::iterator I = path.begin(),
509         E = path.end(); I != E; ++I) (*I)->flattenLocations();
510  }
511
512  static PathDiagnosticCallPiece *construct(const ExplodedNode *N,
513                                            const CallExitEnd &CE,
514                                            const SourceManager &SM);
515
516  static PathDiagnosticCallPiece *construct(PathPieces &pieces,
517                                            const Decl *caller);
518
519  virtual void Profile(llvm::FoldingSetNodeID &ID) const;
520
521  static inline bool classof(const PathDiagnosticPiece *P) {
522    return P->getKind() == Call;
523  }
524};
525
526class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece {
527  std::vector<PathDiagnosticLocationPair> LPairs;
528public:
529  PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos,
530                                 const PathDiagnosticLocation &endPos,
531                                 StringRef s)
532    : PathDiagnosticPiece(s, ControlFlow) {
533      LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos));
534    }
535
536  PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos,
537                                 const PathDiagnosticLocation &endPos)
538    : PathDiagnosticPiece(ControlFlow) {
539      LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos));
540    }
541
542  ~PathDiagnosticControlFlowPiece();
543
544  PathDiagnosticLocation getStartLocation() const {
545    assert(!LPairs.empty() &&
546           "PathDiagnosticControlFlowPiece needs at least one location.");
547    return LPairs[0].getStart();
548  }
549
550  PathDiagnosticLocation getEndLocation() const {
551    assert(!LPairs.empty() &&
552           "PathDiagnosticControlFlowPiece needs at least one location.");
553    return LPairs[0].getEnd();
554  }
555
556  void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); }
557
558  virtual PathDiagnosticLocation getLocation() const {
559    return getStartLocation();
560  }
561
562  typedef std::vector<PathDiagnosticLocationPair>::iterator iterator;
563  iterator begin() { return LPairs.begin(); }
564  iterator end()   { return LPairs.end(); }
565
566  virtual void flattenLocations() {
567    for (iterator I=begin(), E=end(); I!=E; ++I) I->flatten();
568  }
569
570  typedef std::vector<PathDiagnosticLocationPair>::const_iterator
571          const_iterator;
572  const_iterator begin() const { return LPairs.begin(); }
573  const_iterator end() const   { return LPairs.end(); }
574
575  static inline bool classof(const PathDiagnosticPiece *P) {
576    return P->getKind() == ControlFlow;
577  }
578
579  virtual void Profile(llvm::FoldingSetNodeID &ID) const;
580};
581
582class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece {
583public:
584  PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos)
585    : PathDiagnosticSpotPiece(pos, "", Macro) {}
586
587  ~PathDiagnosticMacroPiece();
588
589  PathPieces subPieces;
590
591  bool containsEvent() const;
592
593  virtual void flattenLocations() {
594    PathDiagnosticSpotPiece::flattenLocations();
595    for (PathPieces::iterator I = subPieces.begin(),
596         E = subPieces.end(); I != E; ++I) (*I)->flattenLocations();
597  }
598
599  static inline bool classof(const PathDiagnosticPiece *P) {
600    return P->getKind() == Macro;
601  }
602
603  virtual void Profile(llvm::FoldingSetNodeID &ID) const;
604};
605
606/// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive
607///  diagnostic.  It represents an ordered-collection of PathDiagnosticPieces,
608///  each which represent the pieces of the path.
609class PathDiagnostic : public llvm::FoldingSetNode {
610  const Decl *DeclWithIssue;
611  std::string BugType;
612  std::string Desc;
613  std::string Category;
614  std::deque<std::string> OtherDesc;
615  PathPieces pathImpl;
616  llvm::SmallVector<PathPieces *, 3> pathStack;
617
618  PathDiagnostic(); // Do not implement.
619public:
620  const PathPieces &path;
621
622  /// Return the path currently used by builders for constructing the
623  /// PathDiagnostic.
624  PathPieces &getActivePath() {
625    if (pathStack.empty())
626      return pathImpl;
627    return *pathStack.back();
628  }
629
630  /// Return a mutable version of 'path'.
631  PathPieces &getMutablePieces() {
632    return pathImpl;
633  }
634
635  /// Return the unrolled size of the path.
636  unsigned full_size();
637
638  void pushActivePath(PathPieces *p) { pathStack.push_back(p); }
639  void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); }
640
641  //  PathDiagnostic();
642  PathDiagnostic(const Decl *DeclWithIssue,
643                 StringRef bugtype,
644                 StringRef desc,
645                 StringRef category);
646
647  ~PathDiagnostic();
648
649  StringRef getDescription() const { return Desc; }
650  StringRef getBugType() const { return BugType; }
651  StringRef getCategory() const { return Category; }
652
653  /// Return the semantic context where an issue occurred.  If the
654  /// issue occurs along a path, this represents the "central" area
655  /// where the bug manifests.
656  const Decl *getDeclWithIssue() const { return DeclWithIssue; }
657
658  typedef std::deque<std::string>::const_iterator meta_iterator;
659  meta_iterator meta_begin() const { return OtherDesc.begin(); }
660  meta_iterator meta_end() const { return OtherDesc.end(); }
661  void addMeta(StringRef s) { OtherDesc.push_back(s); }
662
663  PathDiagnosticLocation getLocation() const;
664
665  void flattenLocations() {
666    for (PathPieces::iterator I = pathImpl.begin(), E = pathImpl.end();
667         I != E; ++I) (*I)->flattenLocations();
668  }
669
670  void Profile(llvm::FoldingSetNodeID &ID) const;
671
672  void FullProfile(llvm::FoldingSetNodeID &ID) const;
673};
674
675} // end GR namespace
676
677} //end clang namespace
678
679#endif
680