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
22 using namespace clang;
23 using namespace ento;
24 using llvm::dyn_cast;
25 using llvm::isa;
26
containsEvent() const27 bool 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
StripTrailingDots(llvm::StringRef s)40 static 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
PathDiagnosticPiece(llvm::StringRef s,Kind k,DisplayHint hint)47 PathDiagnosticPiece::PathDiagnosticPiece(llvm::StringRef s,
48 Kind k, DisplayHint hint)
49 : str(StripTrailingDots(s)), kind(k), Hint(hint) {}
50
PathDiagnosticPiece(Kind k,DisplayHint hint)51 PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)
52 : kind(k), Hint(hint) {}
53
~PathDiagnosticPiece()54 PathDiagnosticPiece::~PathDiagnosticPiece() {}
~PathDiagnosticEventPiece()55 PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {}
~PathDiagnosticControlFlowPiece()56 PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {}
57
~PathDiagnosticMacroPiece()58 PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {
59 for (iterator I = begin(), E = end(); I != E; ++I) delete *I;
60 }
61
PathDiagnostic()62 PathDiagnostic::PathDiagnostic() : Size(0) {}
63
~PathDiagnostic()64 PathDiagnostic::~PathDiagnostic() {
65 for (iterator I = begin(), E = end(); I != E; ++I) delete &*I;
66 }
67
resetPath(bool deletePieces)68 void 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
PathDiagnostic(llvm::StringRef bugtype,llvm::StringRef desc,llvm::StringRef category)79 PathDiagnostic::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
HandleDiagnostic(Diagnostic::Level DiagLevel,const DiagnosticInfo & Info)86 void 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
asLocation() const127 FullSourceLoc 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
asRange() const144 PathDiagnosticRange 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::BinaryConditionalOperatorClass:
177 case Stmt::ConditionalOperatorClass:
178 case Stmt::ObjCForCollectionStmtClass: {
179 SourceLocation L = S->getLocStart();
180 return SourceRange(L, L);
181 }
182 }
183
184 return S->getSourceRange();
185 }
186 case DeclK:
187 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
188 return MD->getSourceRange();
189 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
190 if (Stmt *Body = FD->getBody())
191 return Body->getSourceRange();
192 }
193 else {
194 SourceLocation L = D->getLocation();
195 return PathDiagnosticRange(SourceRange(L, L), true);
196 }
197 }
198
199 return R;
200 }
201
flatten()202 void PathDiagnosticLocation::flatten() {
203 if (K == StmtK) {
204 R = asRange();
205 K = RangeK;
206 S = 0;
207 D = 0;
208 }
209 else if (K == DeclK) {
210 SourceLocation L = D->getLocation();
211 R = SourceRange(L, L);
212 K = SingleLocK;
213 S = 0;
214 D = 0;
215 }
216 }
217
218 //===----------------------------------------------------------------------===//
219 // FoldingSet profiling methods.
220 //===----------------------------------------------------------------------===//
221
Profile(llvm::FoldingSetNodeID & ID) const222 void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
223 ID.AddInteger((unsigned) K);
224 switch (K) {
225 case RangeK:
226 ID.AddInteger(R.getBegin().getRawEncoding());
227 ID.AddInteger(R.getEnd().getRawEncoding());
228 break;
229 case SingleLocK:
230 ID.AddInteger(R.getBegin().getRawEncoding());
231 break;
232 case StmtK:
233 ID.Add(S);
234 break;
235 case DeclK:
236 ID.Add(D);
237 break;
238 }
239 return;
240 }
241
Profile(llvm::FoldingSetNodeID & ID) const242 void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
243 ID.AddInteger((unsigned) getKind());
244 ID.AddString(str);
245 // FIXME: Add profiling support for code hints.
246 ID.AddInteger((unsigned) getDisplayHint());
247 for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) {
248 ID.AddInteger(I->getBegin().getRawEncoding());
249 ID.AddInteger(I->getEnd().getRawEncoding());
250 }
251 }
252
Profile(llvm::FoldingSetNodeID & ID) const253 void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
254 PathDiagnosticPiece::Profile(ID);
255 ID.Add(Pos);
256 }
257
Profile(llvm::FoldingSetNodeID & ID) const258 void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
259 PathDiagnosticPiece::Profile(ID);
260 for (const_iterator I = begin(), E = end(); I != E; ++I)
261 ID.Add(*I);
262 }
263
Profile(llvm::FoldingSetNodeID & ID) const264 void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
265 PathDiagnosticSpotPiece::Profile(ID);
266 for (const_iterator I = begin(), E = end(); I != E; ++I)
267 ID.Add(**I);
268 }
269
Profile(llvm::FoldingSetNodeID & ID) const270 void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
271 ID.AddInteger(Size);
272 ID.AddString(BugType);
273 ID.AddString(Desc);
274 ID.AddString(Category);
275 for (const_iterator I = begin(), E = end(); I != E; ++I)
276 ID.Add(*I);
277
278 for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
279 ID.AddString(*I);
280 }
281