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