PathDiagnostic.cpp revision 9b663716449b618ba0390b1dbebc54fa8e971124
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/AST/Expr.h"
16#include "clang/AST/Decl.h"
17#include "clang/AST/DeclObjC.h"
18#include "clang/AST/StmtCXX.h"
19#include "llvm/ADT/SmallString.h"
20#include "llvm/Support/Casting.h"
21
22using namespace clang;
23using namespace ento;
24using llvm::dyn_cast;
25using llvm::isa;
26
27bool PathDiagnosticMacroPiece::containsEvent() const {
28  for (const_iterator I = begin(), E = end(); I!=E; ++I) {
29    if (isa<PathDiagnosticEventPiece>(*I))
30      return true;
31
32    if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I))
33      if (MP->containsEvent())
34        return true;
35  }
36
37  return false;
38}
39
40static llvm::StringRef StripTrailingDots(llvm::StringRef s) {
41  for (llvm::StringRef::size_type i = s.size(); i != 0; --i)
42    if (s[i - 1] != '.')
43      return s.substr(0, i);
44  return "";
45}
46
47PathDiagnosticPiece::PathDiagnosticPiece(llvm::StringRef s,
48                                         Kind k, DisplayHint hint)
49  : str(StripTrailingDots(s)), kind(k), Hint(hint) {}
50
51PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)
52  : kind(k), Hint(hint) {}
53
54PathDiagnosticPiece::~PathDiagnosticPiece() {}
55PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {}
56PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {}
57
58PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {
59  for (iterator I = begin(), E = end(); I != E; ++I) delete *I;
60}
61
62PathDiagnostic::PathDiagnostic() : Size(0) {}
63
64PathDiagnostic::~PathDiagnostic() {
65  for (iterator I = begin(), E = end(); I != E; ++I) delete &*I;
66}
67
68void PathDiagnostic::resetPath(bool deletePieces) {
69  Size = 0;
70
71  if (deletePieces)
72    for (iterator I=begin(), E=end(); I!=E; ++I)
73      delete &*I;
74
75  path.clear();
76}
77
78
79PathDiagnostic::PathDiagnostic(llvm::StringRef bugtype, llvm::StringRef desc,
80                               llvm::StringRef category)
81  : Size(0),
82    BugType(StripTrailingDots(bugtype)),
83    Desc(StripTrailingDots(desc)),
84    Category(StripTrailingDots(category)) {}
85
86void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel,
87                                            const DiagnosticInfo &Info) {
88  // Default implementation (Warnings/errors count).
89  DiagnosticClient::HandleDiagnostic(DiagLevel, Info);
90
91  // Create a PathDiagnostic with a single piece.
92
93  PathDiagnostic* D = new PathDiagnostic();
94
95  const char *LevelStr;
96  switch (DiagLevel) {
97  default:
98  case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type");
99  case Diagnostic::Note:    LevelStr = "note: "; break;
100  case Diagnostic::Warning: LevelStr = "warning: "; break;
101  case Diagnostic::Error:   LevelStr = "error: "; break;
102  case Diagnostic::Fatal:   LevelStr = "fatal error: "; break;
103  }
104
105  llvm::SmallString<100> StrC;
106  StrC += LevelStr;
107  Info.FormatDiagnostic(StrC);
108
109  PathDiagnosticPiece *P =
110    new PathDiagnosticEventPiece(FullSourceLoc(Info.getLocation(),
111                                               Info.getSourceManager()),
112                                 StrC.str());
113
114  for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i)
115    P->addRange(Info.getRange(i).getAsRange());
116  for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i)
117    P->addFixItHint(Info.getFixItHint(i));
118  D->push_front(P);
119
120  HandlePathDiagnostic(D);
121}
122
123//===----------------------------------------------------------------------===//
124// PathDiagnosticLocation methods.
125//===----------------------------------------------------------------------===//
126
127FullSourceLoc PathDiagnosticLocation::asLocation() const {
128  assert(isValid());
129  // Note that we want a 'switch' here so that the compiler can warn us in
130  // case we add more cases.
131  switch (K) {
132    case SingleLocK:
133    case RangeK:
134      break;
135    case StmtK:
136      return FullSourceLoc(S->getLocStart(), const_cast<SourceManager&>(*SM));
137    case DeclK:
138      return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
139  }
140
141  return FullSourceLoc(R.getBegin(), const_cast<SourceManager&>(*SM));
142}
143
144PathDiagnosticRange PathDiagnosticLocation::asRange() const {
145  assert(isValid());
146  // Note that we want a 'switch' here so that the compiler can warn us in
147  // case we add more cases.
148  switch (K) {
149    case SingleLocK:
150      return PathDiagnosticRange(R, true);
151    case RangeK:
152      break;
153    case StmtK: {
154      const Stmt *S = asStmt();
155      switch (S->getStmtClass()) {
156        default:
157          break;
158        case Stmt::DeclStmtClass: {
159          const DeclStmt *DS = cast<DeclStmt>(S);
160          if (DS->isSingleDecl()) {
161            // Should always be the case, but we'll be defensive.
162            return SourceRange(DS->getLocStart(),
163                               DS->getSingleDecl()->getLocation());
164          }
165          break;
166        }
167          // FIXME: Provide better range information for different
168          //  terminators.
169        case Stmt::IfStmtClass:
170        case Stmt::WhileStmtClass:
171        case Stmt::DoStmtClass:
172        case Stmt::ForStmtClass:
173        case Stmt::ChooseExprClass:
174        case Stmt::IndirectGotoStmtClass:
175        case Stmt::SwitchStmtClass:
176        case Stmt::ConditionalOperatorClass:
177        case Stmt::ObjCForCollectionStmtClass: {
178          SourceLocation L = S->getLocStart();
179          return SourceRange(L, L);
180        }
181      }
182
183      return S->getSourceRange();
184    }
185    case DeclK:
186      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
187        return MD->getSourceRange();
188      if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
189        if (Stmt *Body = FD->getBody())
190          return Body->getSourceRange();
191      }
192      else {
193        SourceLocation L = D->getLocation();
194        return PathDiagnosticRange(SourceRange(L, L), true);
195      }
196  }
197
198  return R;
199}
200
201void PathDiagnosticLocation::flatten() {
202  if (K == StmtK) {
203    R = asRange();
204    K = RangeK;
205    S = 0;
206    D = 0;
207  }
208  else if (K == DeclK) {
209    SourceLocation L = D->getLocation();
210    R = SourceRange(L, L);
211    K = SingleLocK;
212    S = 0;
213    D = 0;
214  }
215}
216
217//===----------------------------------------------------------------------===//
218// FoldingSet profiling methods.
219//===----------------------------------------------------------------------===//
220
221void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
222  ID.AddInteger((unsigned) K);
223  switch (K) {
224    case RangeK:
225      ID.AddInteger(R.getBegin().getRawEncoding());
226      ID.AddInteger(R.getEnd().getRawEncoding());
227      break;
228    case SingleLocK:
229      ID.AddInteger(R.getBegin().getRawEncoding());
230      break;
231    case StmtK:
232      ID.Add(S);
233      break;
234    case DeclK:
235      ID.Add(D);
236      break;
237  }
238  return;
239}
240
241void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
242  ID.AddInteger((unsigned) getKind());
243  ID.AddString(str);
244  // FIXME: Add profiling support for code hints.
245  ID.AddInteger((unsigned) getDisplayHint());
246  for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) {
247    ID.AddInteger(I->getBegin().getRawEncoding());
248    ID.AddInteger(I->getEnd().getRawEncoding());
249  }
250}
251
252void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
253  PathDiagnosticPiece::Profile(ID);
254  ID.Add(Pos);
255}
256
257void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
258  PathDiagnosticPiece::Profile(ID);
259  for (const_iterator I = begin(), E = end(); I != E; ++I)
260    ID.Add(*I);
261}
262
263void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
264  PathDiagnosticSpotPiece::Profile(ID);
265  for (const_iterator I = begin(), E = end(); I != E; ++I)
266    ID.Add(**I);
267}
268
269void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
270  ID.AddInteger(Size);
271  ID.AddString(BugType);
272  ID.AddString(Desc);
273  ID.AddString(Category);
274  for (const_iterator I = begin(), E = end(); I != E; ++I)
275    ID.Add(*I);
276
277  for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
278    ID.AddString(*I);
279}
280