PathDiagnostic.cpp revision 4d353eb8af7324c0ee3736c736668f6c9b162ee0
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::create(const ExplodedNode* N,
134                                                      const SourceManager &SM) {
135  assert(N && "Cannot create a location with a null node.");
136
137  const ExplodedNode *NI = N;
138
139  while (NI) {
140    ProgramPoint P = NI->getLocation();
141    const LocationContext *LC = P.getLocationContext();
142    if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P)) {
143      return PathDiagnosticLocation(PS->getStmt(), SM, LC);
144    }
145    else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
146      const Stmt *Term = BE->getSrc()->getTerminator();
147      assert(Term);
148      return PathDiagnosticLocation(Term, SM, LC);
149    }
150    NI = NI->succ_empty() ? 0 : *(NI->succ_begin());
151  }
152
153  const Decl &D = N->getCodeDecl();
154  return PathDiagnosticLocation(D.getBodyRBrace(), SM);
155}
156
157static SourceLocation getValidSourceLocation(const Stmt* S,
158                                             const LocationContext *LC) {
159  SourceLocation L = S->getLocStart();
160
161  // S might be a temporary statement that does not have a location in the
162  // source code, so find an enclosing statement and use it's location.
163  if (!L.isValid() && LC) {
164    assert(LC);
165    ParentMap & PM = LC->getParentMap();
166
167    const Stmt *PS = S;
168    while (!L.isValid()) {
169      PS = PM.getParent(PS);
170      L = PS->getLocStart();
171    }
172  }
173
174  // TODO: either change the name or uncomment the assert.
175  //assert(L.isValid());
176  return L;
177}
178
179FullSourceLoc PathDiagnosticLocation::asLocation() const {
180  assert(isValid());
181  // Note that we want a 'switch' here so that the compiler can warn us in
182  // case we add more cases.
183  switch (K) {
184    case SingleLocK:
185    case RangeK:
186      break;
187    case StmtK:
188      return FullSourceLoc(getValidSourceLocation(S, LC),
189                           const_cast<SourceManager&>(*SM));
190    case DeclK:
191      return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
192  }
193
194  return FullSourceLoc(R.getBegin(), const_cast<SourceManager&>(*SM));
195}
196
197PathDiagnosticRange PathDiagnosticLocation::asRange() const {
198  assert(isValid());
199  // Note that we want a 'switch' here so that the compiler can warn us in
200  // case we add more cases.
201  switch (K) {
202    case SingleLocK:
203      return PathDiagnosticRange(R, true);
204    case RangeK:
205      break;
206    case StmtK: {
207      const Stmt *S = asStmt();
208      switch (S->getStmtClass()) {
209        default:
210          break;
211        case Stmt::DeclStmtClass: {
212          const DeclStmt *DS = cast<DeclStmt>(S);
213          if (DS->isSingleDecl()) {
214            // Should always be the case, but we'll be defensive.
215            return SourceRange(DS->getLocStart(),
216                               DS->getSingleDecl()->getLocation());
217          }
218          break;
219        }
220          // FIXME: Provide better range information for different
221          //  terminators.
222        case Stmt::IfStmtClass:
223        case Stmt::WhileStmtClass:
224        case Stmt::DoStmtClass:
225        case Stmt::ForStmtClass:
226        case Stmt::ChooseExprClass:
227        case Stmt::IndirectGotoStmtClass:
228        case Stmt::SwitchStmtClass:
229        case Stmt::BinaryConditionalOperatorClass:
230        case Stmt::ConditionalOperatorClass:
231        case Stmt::ObjCForCollectionStmtClass: {
232          SourceLocation L = getValidSourceLocation(S, LC);
233          return SourceRange(L, L);
234        }
235      }
236
237      return S->getSourceRange();
238    }
239    case DeclK:
240      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
241        return MD->getSourceRange();
242      if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
243        if (Stmt *Body = FD->getBody())
244          return Body->getSourceRange();
245      }
246      else {
247        SourceLocation L = D->getLocation();
248        return PathDiagnosticRange(SourceRange(L, L), true);
249      }
250  }
251
252  return R;
253}
254
255void PathDiagnosticLocation::flatten() {
256  if (K == StmtK) {
257    R = asRange();
258    K = RangeK;
259    S = 0;
260    D = 0;
261  }
262  else if (K == DeclK) {
263    SourceLocation L = D->getLocation();
264    R = SourceRange(L, L);
265    K = SingleLocK;
266    S = 0;
267    D = 0;
268  }
269}
270
271//===----------------------------------------------------------------------===//
272// FoldingSet profiling methods.
273//===----------------------------------------------------------------------===//
274
275void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
276  ID.AddInteger((unsigned) K);
277  switch (K) {
278    case RangeK:
279      ID.AddInteger(R.getBegin().getRawEncoding());
280      ID.AddInteger(R.getEnd().getRawEncoding());
281      break;
282    case SingleLocK:
283      ID.AddInteger(R.getBegin().getRawEncoding());
284      break;
285    case StmtK:
286      ID.Add(S);
287      break;
288    case DeclK:
289      ID.Add(D);
290      break;
291  }
292  return;
293}
294
295void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
296  ID.AddInteger((unsigned) getKind());
297  ID.AddString(str);
298  // FIXME: Add profiling support for code hints.
299  ID.AddInteger((unsigned) getDisplayHint());
300  for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) {
301    ID.AddInteger(I->getBegin().getRawEncoding());
302    ID.AddInteger(I->getEnd().getRawEncoding());
303  }
304}
305
306void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
307  PathDiagnosticPiece::Profile(ID);
308  ID.Add(Pos);
309}
310
311void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
312  PathDiagnosticPiece::Profile(ID);
313  for (const_iterator I = begin(), E = end(); I != E; ++I)
314    ID.Add(*I);
315}
316
317void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
318  PathDiagnosticSpotPiece::Profile(ID);
319  for (const_iterator I = begin(), E = end(); I != E; ++I)
320    ID.Add(**I);
321}
322
323void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
324  ID.AddInteger(Size);
325  ID.AddString(BugType);
326  ID.AddString(Desc);
327  ID.AddString(Category);
328  for (const_iterator I = begin(), E = end(); I != E; ++I)
329    ID.Add(*I);
330
331  for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
332    ID.AddString(*I);
333}
334