PathDiagnostic.cpp revision 21a25167b8279df3d8f889d041a4fd98733d3c27
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 PathDiagnosticConsumer::HandlePathDiagnostic(const PathDiagnostic *D) {
86  // For now this simply forwards to HandlePathDiagnosticImpl.  In the future
87  // we can use this indirection to control for multi-threaded access to
88  // the PathDiagnosticConsumer from multiple bug reporters.
89  HandlePathDiagnosticImpl(D);
90}
91
92//===----------------------------------------------------------------------===//
93// PathDiagnosticLocation methods.
94//===----------------------------------------------------------------------===//
95
96static SourceLocation getValidSourceLocation(const Stmt* S,
97                                             LocationOrAnalysisDeclContext LAC) {
98  SourceLocation L = S->getLocStart();
99  assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should "
100                          "be passed to PathDiagnosticLocation upon creation.");
101
102  // S might be a temporary statement that does not have a location in the
103  // source code, so find an enclosing statement and use it's location.
104  if (!L.isValid()) {
105
106    ParentMap *PM = 0;
107    if (LAC.is<const LocationContext*>())
108      PM = &LAC.get<const LocationContext*>()->getParentMap();
109    else
110      PM = &LAC.get<AnalysisDeclContext*>()->getParentMap();
111
112    while (!L.isValid()) {
113      S = PM->getParent(S);
114      L = S->getLocStart();
115    }
116  }
117
118  return L;
119}
120
121PathDiagnosticLocation
122  PathDiagnosticLocation::createBegin(const Decl *D,
123                                      const SourceManager &SM) {
124  return PathDiagnosticLocation(D->getLocStart(), SM, SingleLocK);
125}
126
127PathDiagnosticLocation
128  PathDiagnosticLocation::createBegin(const Stmt *S,
129                                      const SourceManager &SM,
130                                      LocationOrAnalysisDeclContext LAC) {
131  return PathDiagnosticLocation(getValidSourceLocation(S, LAC),
132                                SM, SingleLocK);
133}
134
135PathDiagnosticLocation
136  PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,
137                                            const SourceManager &SM) {
138  return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);
139}
140
141PathDiagnosticLocation
142  PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
143                                          const SourceManager &SM) {
144  return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);
145}
146
147PathDiagnosticLocation
148  PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS,
149                                           const SourceManager &SM) {
150  SourceLocation L = CS->getLBracLoc();
151  return PathDiagnosticLocation(L, SM, SingleLocK);
152}
153
154PathDiagnosticLocation
155  PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS,
156                                         const SourceManager &SM) {
157  SourceLocation L = CS->getRBracLoc();
158  return PathDiagnosticLocation(L, SM, SingleLocK);
159}
160
161PathDiagnosticLocation
162  PathDiagnosticLocation::createDeclBegin(const LocationContext *LC,
163                                          const SourceManager &SM) {
164  // FIXME: Should handle CXXTryStmt if analyser starts supporting C++.
165  if (const CompoundStmt *CS =
166        dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody()))
167    if (!CS->body_empty()) {
168      SourceLocation Loc = (*CS->body_begin())->getLocStart();
169      return PathDiagnosticLocation(Loc, SM, SingleLocK);
170    }
171
172  return PathDiagnosticLocation();
173}
174
175PathDiagnosticLocation
176  PathDiagnosticLocation::createDeclEnd(const LocationContext *LC,
177                                        const SourceManager &SM) {
178  SourceLocation L = LC->getDecl()->getBodyRBrace();
179  return PathDiagnosticLocation(L, SM, SingleLocK);
180}
181
182PathDiagnosticLocation
183  PathDiagnosticLocation::create(const ProgramPoint& P,
184                                 const SourceManager &SMng) {
185
186  const Stmt* S = 0;
187  if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
188    const CFGBlock *BSrc = BE->getSrc();
189    S = BSrc->getTerminatorCondition();
190  }
191  else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
192    S = PS->getStmt();
193  }
194
195  return PathDiagnosticLocation(S, SMng, P.getLocationContext());
196}
197
198PathDiagnosticLocation
199  PathDiagnosticLocation::createEndOfPath(const ExplodedNode* N,
200                                          const SourceManager &SM) {
201  assert(N && "Cannot create a location with a null node.");
202
203  const ExplodedNode *NI = N;
204
205  while (NI) {
206    ProgramPoint P = NI->getLocation();
207    const LocationContext *LC = P.getLocationContext();
208    if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P))
209      return PathDiagnosticLocation(PS->getStmt(), SM, LC);
210    else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
211      const Stmt *Term = BE->getSrc()->getTerminator();
212      assert(Term);
213      return PathDiagnosticLocation(Term, SM, LC);
214    }
215    NI = NI->succ_empty() ? 0 : *(NI->succ_begin());
216  }
217
218  return createDeclEnd(N->getLocationContext(), SM);
219}
220
221PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(
222                                           const PathDiagnosticLocation &PDL) {
223  FullSourceLoc L = PDL.asLocation();
224  return PathDiagnosticLocation(L, L.getManager(), SingleLocK);
225}
226
227FullSourceLoc
228  PathDiagnosticLocation::genLocation(SourceLocation L,
229                                      LocationOrAnalysisDeclContext LAC) const {
230  assert(isValid());
231  // Note that we want a 'switch' here so that the compiler can warn us in
232  // case we add more cases.
233  switch (K) {
234    case SingleLocK:
235    case RangeK:
236      break;
237    case StmtK:
238      return FullSourceLoc(getValidSourceLocation(S, LAC),
239                           const_cast<SourceManager&>(*SM));
240    case DeclK:
241      return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
242  }
243
244  return FullSourceLoc(L, const_cast<SourceManager&>(*SM));
245}
246
247PathDiagnosticRange
248  PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const {
249  assert(isValid());
250  // Note that we want a 'switch' here so that the compiler can warn us in
251  // case we add more cases.
252  switch (K) {
253    case SingleLocK:
254      return PathDiagnosticRange(SourceRange(Loc,Loc), true);
255    case RangeK:
256      break;
257    case StmtK: {
258      const Stmt *S = asStmt();
259      switch (S->getStmtClass()) {
260        default:
261          break;
262        case Stmt::DeclStmtClass: {
263          const DeclStmt *DS = cast<DeclStmt>(S);
264          if (DS->isSingleDecl()) {
265            // Should always be the case, but we'll be defensive.
266            return SourceRange(DS->getLocStart(),
267                               DS->getSingleDecl()->getLocation());
268          }
269          break;
270        }
271          // FIXME: Provide better range information for different
272          //  terminators.
273        case Stmt::IfStmtClass:
274        case Stmt::WhileStmtClass:
275        case Stmt::DoStmtClass:
276        case Stmt::ForStmtClass:
277        case Stmt::ChooseExprClass:
278        case Stmt::IndirectGotoStmtClass:
279        case Stmt::SwitchStmtClass:
280        case Stmt::BinaryConditionalOperatorClass:
281        case Stmt::ConditionalOperatorClass:
282        case Stmt::ObjCForCollectionStmtClass: {
283          SourceLocation L = getValidSourceLocation(S, LAC);
284          return SourceRange(L, L);
285        }
286      }
287      SourceRange R = S->getSourceRange();
288      if (R.isValid())
289        return R;
290      break;
291    }
292    case DeclK:
293      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
294        return MD->getSourceRange();
295      if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
296        if (Stmt *Body = FD->getBody())
297          return Body->getSourceRange();
298      }
299      else {
300        SourceLocation L = D->getLocation();
301        return PathDiagnosticRange(SourceRange(L, L), true);
302      }
303  }
304
305  return SourceRange(Loc,Loc);
306}
307
308void PathDiagnosticLocation::flatten() {
309  if (K == StmtK) {
310    K = RangeK;
311    S = 0;
312    D = 0;
313  }
314  else if (K == DeclK) {
315    K = SingleLocK;
316    S = 0;
317    D = 0;
318  }
319}
320
321//===----------------------------------------------------------------------===//
322// FoldingSet profiling methods.
323//===----------------------------------------------------------------------===//
324
325void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
326  ID.AddInteger(Range.getBegin().getRawEncoding());
327  ID.AddInteger(Range.getEnd().getRawEncoding());
328  ID.AddInteger(Loc.getRawEncoding());
329  return;
330}
331
332void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
333  ID.AddInteger((unsigned) getKind());
334  ID.AddString(str);
335  // FIXME: Add profiling support for code hints.
336  ID.AddInteger((unsigned) getDisplayHint());
337  for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) {
338    ID.AddInteger(I->getBegin().getRawEncoding());
339    ID.AddInteger(I->getEnd().getRawEncoding());
340  }
341}
342
343void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
344  PathDiagnosticPiece::Profile(ID);
345  ID.Add(Pos);
346}
347
348void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
349  PathDiagnosticPiece::Profile(ID);
350  for (const_iterator I = begin(), E = end(); I != E; ++I)
351    ID.Add(*I);
352}
353
354void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
355  PathDiagnosticSpotPiece::Profile(ID);
356  for (const_iterator I = begin(), E = end(); I != E; ++I)
357    ID.Add(**I);
358}
359
360void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
361  ID.AddInteger(Size);
362  ID.AddString(BugType);
363  ID.AddString(Desc);
364  ID.AddString(Category);
365  for (const_iterator I = begin(), E = end(); I != E; ++I)
366    ID.Add(*I);
367
368  for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
369    ID.AddString(*I);
370}
371