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