PathDiagnostic.cpp revision 43f48b0b1bc763dc56db6e01de4fcc44ad389bef
1//===--- PathDiagnostic.cpp - 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#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
15#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
16#include "clang/AST/Expr.h"
17#include "clang/AST/Decl.h"
18#include "clang/AST/DeclObjC.h"
19#include "clang/AST/StmtCXX.h"
20#include "llvm/ADT/SmallString.h"
21
22using namespace clang;
23using namespace ento;
24
25bool PathDiagnosticMacroPiece::containsEvent() const {
26  for (const_iterator I = begin(), E = end(); I!=E; ++I) {
27    if (isa<PathDiagnosticEventPiece>(*I))
28      return true;
29
30    if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I))
31      if (MP->containsEvent())
32        return true;
33  }
34
35  return false;
36}
37
38static StringRef StripTrailingDots(StringRef s) {
39  for (StringRef::size_type i = s.size(); i != 0; --i)
40    if (s[i - 1] != '.')
41      return s.substr(0, i);
42  return "";
43}
44
45PathDiagnosticPiece::PathDiagnosticPiece(StringRef s,
46                                         Kind k, DisplayHint hint)
47  : str(StripTrailingDots(s)), kind(k), Hint(hint) {}
48
49PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)
50  : kind(k), Hint(hint) {}
51
52PathDiagnosticPiece::~PathDiagnosticPiece() {}
53PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {}
54PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {}
55
56PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {
57  for (iterator I = begin(), E = end(); I != E; ++I) delete *I;
58}
59
60PathDiagnostic::PathDiagnostic() : Size(0) {}
61
62PathDiagnostic::~PathDiagnostic() {
63  for (iterator I = begin(), E = end(); I != E; ++I) delete &*I;
64}
65
66void PathDiagnostic::resetPath(bool deletePieces) {
67  Size = 0;
68
69  if (deletePieces)
70    for (iterator I=begin(), E=end(); I!=E; ++I)
71      delete &*I;
72
73  path.clear();
74}
75
76
77PathDiagnostic::PathDiagnostic(StringRef bugtype, StringRef desc,
78                               StringRef category)
79  : Size(0),
80    BugType(StripTrailingDots(bugtype)),
81    Desc(StripTrailingDots(desc)),
82    Category(StripTrailingDots(category)) {}
83
84void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel,
85                                            const DiagnosticInfo &Info) {
86  // Default implementation (Warnings/errors count).
87  DiagnosticClient::HandleDiagnostic(DiagLevel, Info);
88
89  // Create a PathDiagnostic with a single piece.
90
91  PathDiagnostic* D = new PathDiagnostic();
92
93  const char *LevelStr;
94  switch (DiagLevel) {
95  default:
96  case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type");
97  case Diagnostic::Note:    LevelStr = "note: "; break;
98  case Diagnostic::Warning: LevelStr = "warning: "; break;
99  case Diagnostic::Error:   LevelStr = "error: "; break;
100  case Diagnostic::Fatal:   LevelStr = "fatal error: "; break;
101  }
102
103  llvm::SmallString<100> StrC;
104  StrC += LevelStr;
105  Info.FormatDiagnostic(StrC);
106
107  PathDiagnosticPiece *P =
108    new PathDiagnosticEventPiece(FullSourceLoc(Info.getLocation(),
109                                               Info.getSourceManager()),
110                                 StrC.str());
111
112  for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i)
113    P->addRange(Info.getRange(i).getAsRange());
114  for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i)
115    P->addFixItHint(Info.getFixItHint(i));
116  D->push_front(P);
117
118  HandlePathDiagnostic(D);
119}
120
121void PathDiagnosticClient::HandlePathDiagnostic(const PathDiagnostic *D) {
122  // For now this simply forwards to HandlePathDiagnosticImpl.  In the future
123  // we can use this indirection to control for multi-threaded access to
124  // the PathDiagnosticClient from multiple bug reporters.
125  HandlePathDiagnosticImpl(D);
126}
127
128//===----------------------------------------------------------------------===//
129// PathDiagnosticLocation methods.
130//===----------------------------------------------------------------------===//
131
132PathDiagnosticLocation PathDiagnosticLocation::create(const ExplodedNode* N,
133                                                      const SourceManager &SM) {
134  assert(N && "Cannot create a location with a null node.");
135
136  const ExplodedNode *NI = N;
137
138  while (NI) {
139    ProgramPoint P = NI->getLocation();
140
141    if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P)) {
142      return PathDiagnosticLocation(PS->getStmt(), SM);
143    }
144    else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
145      const Stmt *Term = BE->getSrc()->getTerminator();
146      assert(Term);
147      return PathDiagnosticLocation(Term, SM);
148    }
149    NI = NI->succ_empty() ? 0 : *(NI->succ_begin());
150  }
151
152  const Decl &D = N->getCodeDecl();
153  return PathDiagnosticLocation(D.getBodyRBrace(), SM);
154}
155
156FullSourceLoc PathDiagnosticLocation::asLocation() const {
157  assert(isValid());
158  // Note that we want a 'switch' here so that the compiler can warn us in
159  // case we add more cases.
160  switch (K) {
161    case SingleLocK:
162    case RangeK:
163      break;
164    case StmtK:
165      return FullSourceLoc(S->getLocStart(), const_cast<SourceManager&>(*SM));
166    case DeclK:
167      return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
168  }
169
170  return FullSourceLoc(R.getBegin(), const_cast<SourceManager&>(*SM));
171}
172
173PathDiagnosticRange PathDiagnosticLocation::asRange() const {
174  assert(isValid());
175  // Note that we want a 'switch' here so that the compiler can warn us in
176  // case we add more cases.
177  switch (K) {
178    case SingleLocK:
179      return PathDiagnosticRange(R, true);
180    case RangeK:
181      break;
182    case StmtK: {
183      const Stmt *S = asStmt();
184      switch (S->getStmtClass()) {
185        default:
186          break;
187        case Stmt::DeclStmtClass: {
188          const DeclStmt *DS = cast<DeclStmt>(S);
189          if (DS->isSingleDecl()) {
190            // Should always be the case, but we'll be defensive.
191            return SourceRange(DS->getLocStart(),
192                               DS->getSingleDecl()->getLocation());
193          }
194          break;
195        }
196          // FIXME: Provide better range information for different
197          //  terminators.
198        case Stmt::IfStmtClass:
199        case Stmt::WhileStmtClass:
200        case Stmt::DoStmtClass:
201        case Stmt::ForStmtClass:
202        case Stmt::ChooseExprClass:
203        case Stmt::IndirectGotoStmtClass:
204        case Stmt::SwitchStmtClass:
205        case Stmt::BinaryConditionalOperatorClass:
206        case Stmt::ConditionalOperatorClass:
207        case Stmt::ObjCForCollectionStmtClass: {
208          SourceLocation L = S->getLocStart();
209          return SourceRange(L, L);
210        }
211      }
212
213      return S->getSourceRange();
214    }
215    case DeclK:
216      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
217        return MD->getSourceRange();
218      if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
219        if (Stmt *Body = FD->getBody())
220          return Body->getSourceRange();
221      }
222      else {
223        SourceLocation L = D->getLocation();
224        return PathDiagnosticRange(SourceRange(L, L), true);
225      }
226  }
227
228  return R;
229}
230
231void PathDiagnosticLocation::flatten() {
232  if (K == StmtK) {
233    R = asRange();
234    K = RangeK;
235    S = 0;
236    D = 0;
237  }
238  else if (K == DeclK) {
239    SourceLocation L = D->getLocation();
240    R = SourceRange(L, L);
241    K = SingleLocK;
242    S = 0;
243    D = 0;
244  }
245}
246
247//===----------------------------------------------------------------------===//
248// FoldingSet profiling methods.
249//===----------------------------------------------------------------------===//
250
251void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
252  ID.AddInteger((unsigned) K);
253  switch (K) {
254    case RangeK:
255      ID.AddInteger(R.getBegin().getRawEncoding());
256      ID.AddInteger(R.getEnd().getRawEncoding());
257      break;
258    case SingleLocK:
259      ID.AddInteger(R.getBegin().getRawEncoding());
260      break;
261    case StmtK:
262      ID.Add(S);
263      break;
264    case DeclK:
265      ID.Add(D);
266      break;
267  }
268  return;
269}
270
271void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
272  ID.AddInteger((unsigned) getKind());
273  ID.AddString(str);
274  // FIXME: Add profiling support for code hints.
275  ID.AddInteger((unsigned) getDisplayHint());
276  for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) {
277    ID.AddInteger(I->getBegin().getRawEncoding());
278    ID.AddInteger(I->getEnd().getRawEncoding());
279  }
280}
281
282void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
283  PathDiagnosticPiece::Profile(ID);
284  ID.Add(Pos);
285}
286
287void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
288  PathDiagnosticPiece::Profile(ID);
289  for (const_iterator I = begin(), E = end(); I != E; ++I)
290    ID.Add(*I);
291}
292
293void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
294  PathDiagnosticSpotPiece::Profile(ID);
295  for (const_iterator I = begin(), E = end(); I != E; ++I)
296    ID.Add(**I);
297}
298
299void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
300  ID.AddInteger(Size);
301  ID.AddString(BugType);
302  ID.AddString(Desc);
303  ID.AddString(Category);
304  for (const_iterator I = begin(), E = end(); I != E; ++I)
305    ID.Add(*I);
306
307  for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
308    ID.AddString(*I);
309}
310