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