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