 |
|
 |
|
| Files: |
1 |
|
Branches Taken: |
51.6% |
49 / 95 |
| Generated: |
2010-02-10 01:31 |
|
Branches Executed: |
77.9% |
74 / 95 |
| |
|
Line Coverage: |
77.5% |
172 / 222 |
| |
 |
|
 |
1 : //===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- 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 PlistDiagnostics object.
11 : //
12 : //===----------------------------------------------------------------------===//
13 :
14 : #include "clang/Frontend/PathDiagnosticClients.h"
15 : #include "clang/Checker/BugReporter/PathDiagnostic.h"
16 : #include "clang/Basic/SourceManager.h"
17 : #include "clang/Basic/FileManager.h"
18 : #include "clang/Lex/Preprocessor.h"
19 : #include "llvm/Support/raw_ostream.h"
20 : #include "llvm/Support/Casting.h"
21 : #include "llvm/ADT/DenseMap.h"
22 : #include "llvm/ADT/SmallVector.h"
23 : using namespace clang;
24 : using llvm::cast;
25 :
26 : typedef llvm::DenseMap<FileID, unsigned> FIDMap;
27 :
28 : namespace clang {
29 : class Preprocessor;
30 : }
31 :
32 : namespace {
33 : struct CompareDiagnostics {
34 : // Compare if 'X' is "<" than 'Y'.
35 10: bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const {
36 : // First compare by location
37 10: const FullSourceLoc &XLoc = X->getLocation().asLocation();
38 10: const FullSourceLoc &YLoc = Y->getLocation().asLocation();
0: branch 1 not taken
10: branch 2 taken
39 10: if (XLoc < YLoc)
40 0: return true;
10: branch 1 taken
0: branch 2 not taken
41 10: if (XLoc != YLoc)
42 10: return false;
43 :
44 : // Next, compare by bug type.
45 0: llvm::StringRef XBugType = X->getBugType();
46 0: llvm::StringRef YBugType = Y->getBugType();
0: branch 1 not taken
0: branch 2 not taken
47 0: if (XBugType < YBugType)
48 0: return true;
0: branch 1 not taken
0: branch 2 not taken
49 0: if (XBugType != YBugType)
50 0: return false;
51 :
52 : // Next, compare by bug description.
53 0: llvm::StringRef XDesc = X->getDescription();
54 0: llvm::StringRef YDesc = Y->getDescription();
0: branch 1 not taken
0: branch 2 not taken
55 0: if (XDesc < YDesc)
56 0: return true;
0: branch 1 not taken
0: branch 2 not taken
57 0: if (XDesc != YDesc)
58 0: return false;
59 :
60 : // FIXME: Further refine by comparing PathDiagnosticPieces?
61 0: return false;
62 : }
63 : };
64 : }
65 :
66 : namespace {
67 : class PlistDiagnostics : public PathDiagnosticClient {
68 : std::vector<const PathDiagnostic*> BatchedDiags;
69 : const std::string OutputFile;
70 : const LangOptions &LangOpts;
71 : llvm::OwningPtr<PathDiagnosticClient> SubPD;
72 : bool flushed;
73 : public:
74 : PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts,
75 : PathDiagnosticClient *subPD);
76 :
1: branch 5 taken
0: branch 6 not taken
0: branch 13 not taken
0: branch 14 not taken
77 1: ~PlistDiagnostics() { FlushDiagnostics(NULL); }
78 :
79 : void FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade);
80 :
81 : void HandlePathDiagnostic(const PathDiagnostic* D);
82 :
83 0: virtual llvm::StringRef getName() const {
84 0: return "PlistDiagnostics";
85 : }
86 :
87 : PathGenerationScheme getGenerationScheme() const;
88 0: bool supportsLogicalOpControlFlow() const { return true; }
89 0: bool supportsAllBlockEdges() const { return true; }
90 6: virtual bool useVerboseDescription() const { return false; }
91 : };
92 : } // end anonymous namespace
93 :
94 : PlistDiagnostics::PlistDiagnostics(const std::string& output,
95 : const LangOptions &LO,
96 1: PathDiagnosticClient *subPD)
97 1: : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false) {}
98 :
99 : PathDiagnosticClient*
100 : clang::CreatePlistDiagnosticClient(const std::string& s, const Preprocessor &PP,
101 1: PathDiagnosticClient *subPD) {
102 1: return new PlistDiagnostics(s, PP.getLangOptions(), subPD);
103 : }
104 :
105 : PathDiagnosticClient::PathGenerationScheme
106 6: PlistDiagnostics::getGenerationScheme() const {
0: branch 1 not taken
6: branch 2 taken
107 6: if (const PathDiagnosticClient *PD = SubPD.get())
108 0: return PD->getGenerationScheme();
109 :
110 6: return Extensive;
111 : }
112 :
113 : static void AddFID(FIDMap &FIDs, llvm::SmallVectorImpl<FileID> &V,
114 45: const SourceManager* SM, SourceLocation L) {
115 :
116 45: FileID FID = SM->getFileID(SM->getInstantiationLoc(L));
117 45: FIDMap::iterator I = FIDs.find(FID);
1: branch 3 taken
44: branch 4 taken
118 45: if (I != FIDs.end()) return;
119 1: FIDs[FID] = V.size();
120 1: V.push_back(FID);
121 : }
122 :
123 : static unsigned GetFID(const FIDMap& FIDs, const SourceManager &SM,
124 87: SourceLocation L) {
125 87: FileID FID = SM.getFileID(SM.getInstantiationLoc(L));
126 87: FIDMap::const_iterator I = FIDs.find(FID);
0: branch 2 not taken
87: branch 3 taken
127 87: assert(I != FIDs.end());
128 87: return I->second;
129 : }
130 :
131 735: static llvm::raw_ostream& Indent(llvm::raw_ostream& o, const unsigned indent) {
5449: branch 1 taken
735: branch 2 taken
132 735: for (unsigned i = 0; i < indent; ++i) o << ' ';
133 735: return o;
134 : }
135 :
136 : static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM,
137 : const LangOptions &LangOpts,
138 : SourceLocation L, const FIDMap &FM,
139 87: unsigned indent, bool extend = false) {
140 :
141 87: FullSourceLoc Loc(SM.getInstantiationLoc(L), const_cast<SourceManager&>(SM));
142 :
143 : // Add in the length of the token, so that we cover multi-char tokens.
144 : unsigned offset =
27: branch 0 taken
60: branch 1 taken
145 87: extend ? Lexer::MeasureTokenLength(Loc, SM, LangOpts) - 1 : 0;
146 :
147 87: Indent(o, indent) << "<dict>\n";
148 : Indent(o, indent) << " <key>line</key><integer>"
149 87: << Loc.getInstantiationLineNumber() << "</integer>\n";
150 : Indent(o, indent) << " <key>col</key><integer>"
151 87: << Loc.getInstantiationColumnNumber() + offset << "</integer>\n";
152 : Indent(o, indent) << " <key>file</key><integer>"
153 87: << GetFID(FM, SM, Loc) << "</integer>\n";
154 87: Indent(o, indent) << "</dict>\n";
155 87: }
156 :
157 : static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM,
158 : const LangOptions &LangOpts,
159 : const PathDiagnosticLocation &L, const FIDMap& FM,
160 6: unsigned indent, bool extend = false) {
161 6: EmitLocation(o, SM, LangOpts, L.asLocation(), FM, indent, extend);
162 6: }
163 :
164 : static void EmitRange(llvm::raw_ostream& o, const SourceManager &SM,
165 : const LangOptions &LangOpts,
166 : PathDiagnosticRange R, const FIDMap &FM,
167 35: unsigned indent) {
168 35: Indent(o, indent) << "<array>\n";
169 35: EmitLocation(o, SM, LangOpts, R.getBegin(), FM, indent+1);
170 35: EmitLocation(o, SM, LangOpts, R.getEnd(), FM, indent+1, !R.isPoint);
171 35: Indent(o, indent) << "</array>\n";
172 35: }
173 :
174 : static llvm::raw_ostream& EmitString(llvm::raw_ostream& o,
175 41: const std::string& s) {
176 41: o << "<string>";
1531: branch 4 taken
41: branch 5 taken
177 1572: for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) {
178 1531: char c = *I;
1489: branch 0 taken
0: branch 1 not taken
0: branch 2 not taken
0: branch 3 not taken
42: branch 4 taken
0: branch 5 not taken
179 1531: switch (c) {
180 1489: default: o << c; break;
181 0: case '&': o << "&"; break;
182 0: case '<': o << "<"; break;
183 0: case '>': o << ">"; break;
184 42: case '\'': o << "'"; break;
185 0: case '\"': o << """; break;
186 : }
187 : }
188 41: o << "</string>";
189 41: return o;
190 : }
191 :
192 : static void ReportControlFlow(llvm::raw_ostream& o,
193 : const PathDiagnosticControlFlowPiece& P,
194 : const FIDMap& FM,
195 : const SourceManager &SM,
196 : const LangOptions &LangOpts,
197 12: unsigned indent) {
198 :
199 12: Indent(o, indent) << "<dict>\n";
200 12: ++indent;
201 :
202 12: Indent(o, indent) << "<key>kind</key><string>control</string>\n";
203 :
204 : // Emit edges.
205 12: Indent(o, indent) << "<key>edges</key>\n";
206 12: ++indent;
207 12: Indent(o, indent) << "<array>\n";
208 12: ++indent;
12: branch 4 taken
12: branch 5 taken
209 24: for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end();
210 : I!=E; ++I) {
211 12: Indent(o, indent) << "<dict>\n";
212 12: ++indent;
213 12: Indent(o, indent) << "<key>start</key>\n";
214 12: EmitRange(o, SM, LangOpts, I->getStart().asRange(), FM, indent+1);
215 12: Indent(o, indent) << "<key>end</key>\n";
216 12: EmitRange(o, SM, LangOpts, I->getEnd().asRange(), FM, indent+1);
217 12: --indent;
218 12: Indent(o, indent) << "</dict>\n";
219 : }
220 12: --indent;
221 12: Indent(o, indent) << "</array>\n";
222 12: --indent;
223 :
224 : // Output any helper text.
225 12: const std::string& s = P.getString();
0: branch 1 not taken
12: branch 2 taken
226 12: if (!s.empty()) {
227 0: Indent(o, indent) << "<key>alternate</key>";
228 0: EmitString(o, s) << '\n';
229 : }
230 :
231 12: --indent;
232 12: Indent(o, indent) << "</dict>\n";
233 12: }
234 :
235 : static void ReportEvent(llvm::raw_ostream& o, const PathDiagnosticPiece& P,
236 : const FIDMap& FM,
237 : const SourceManager &SM,
238 : const LangOptions &LangOpts,
239 11: unsigned indent) {
240 :
241 11: Indent(o, indent) << "<dict>\n";
242 11: ++indent;
243 :
244 11: Indent(o, indent) << "<key>kind</key><string>event</string>\n";
245 :
246 : // Output the location.
247 11: FullSourceLoc L = P.getLocation().asLocation();
248 :
249 11: Indent(o, indent) << "<key>location</key>\n";
250 11: EmitLocation(o, SM, LangOpts, L, FM, indent);
251 :
252 : // Output the ranges (if any).
253 11: PathDiagnosticPiece::range_iterator RI = P.ranges_begin(),
254 11: RE = P.ranges_end();
255 :
11: branch 0 taken
0: branch 1 not taken
256 11: if (RI != RE) {
257 11: Indent(o, indent) << "<key>ranges</key>\n";
258 11: Indent(o, indent) << "<array>\n";
259 11: ++indent;
11: branch 0 taken
11: branch 1 taken
260 22: for (; RI != RE; ++RI)
261 11: EmitRange(o, SM, LangOpts, *RI, FM, indent+1);
262 11: --indent;
263 11: Indent(o, indent) << "</array>\n";
264 : }
265 :
266 : // Output the text.
0: branch 2 not taken
11: branch 3 taken
267 11: assert(!P.getString().empty());
268 11: Indent(o, indent) << "<key>extended_message</key>\n";
269 11: Indent(o, indent);
270 11: EmitString(o, P.getString()) << '\n';
271 :
272 : // Output the short text.
273 : // FIXME: Really use a short string.
274 11: Indent(o, indent) << "<key>message</key>\n";
275 11: EmitString(o, P.getString()) << '\n';
276 :
277 : // Finish up.
278 11: --indent;
279 11: Indent(o, indent); o << "</dict>\n";
280 11: }
281 :
282 : static void ReportMacro(llvm::raw_ostream& o,
283 : const PathDiagnosticMacroPiece& P,
284 : const FIDMap& FM, const SourceManager &SM,
285 : const LangOptions &LangOpts,
286 0: unsigned indent) {
287 :
0: branch 4 not taken
0: branch 5 not taken
288 0: for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end();
289 : I!=E; ++I) {
290 :
0: branch 2 not taken
0: branch 3 not taken
0: branch 4 not taken
291 0: switch ((*I)->getKind()) {
292 : default:
293 0: break;
294 : case PathDiagnosticPiece::Event:
295 : ReportEvent(o, cast<PathDiagnosticEventPiece>(**I), FM, SM, LangOpts,
296 0: indent);
297 0: break;
298 : case PathDiagnosticPiece::Macro:
299 : ReportMacro(o, cast<PathDiagnosticMacroPiece>(**I), FM, SM, LangOpts,
300 0: indent);
301 : break;
302 : }
303 : }
304 0: }
305 :
306 : static void ReportDiag(llvm::raw_ostream& o, const PathDiagnosticPiece& P,
307 : const FIDMap& FM, const SourceManager &SM,
308 23: const LangOptions &LangOpts) {
309 :
310 23: unsigned indent = 4;
311 :
12: branch 1 taken
11: branch 2 taken
0: branch 3 not taken
0: branch 4 not taken
312 23: switch (P.getKind()) {
313 : case PathDiagnosticPiece::ControlFlow:
314 : ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM,
315 12: LangOpts, indent);
316 12: break;
317 : case PathDiagnosticPiece::Event:
318 : ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts,
319 11: indent);
320 11: break;
321 : case PathDiagnosticPiece::Macro:
322 : ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts,
323 0: indent);
324 : break;
325 : }
326 23: }
327 :
328 6: void PlistDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) {
0: branch 0 not taken
6: branch 1 taken
329 6: if (!D)
330 0: return;
331 :
0: branch 1 not taken
6: branch 2 taken
332 6: if (D->empty()) {
0: branch 0 not taken
0: branch 1 not taken
333 0: delete D;
334 0: return;
335 : }
336 :
337 : // We need to flatten the locations (convert Stmt* to locations) because
338 : // the referenced statements may be freed by the time the diagnostics
339 : // are emitted.
340 6: const_cast<PathDiagnostic*>(D)->flattenLocations();
341 6: BatchedDiags.push_back(D);
342 : }
343 :
344 : void PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string>
345 2: *FilesMade) {
346 :
1: branch 0 taken
1: branch 1 taken
347 2: if (flushed)
348 1: return;
349 :
350 1: flushed = true;
351 :
352 : // Sort the diagnostics so that they are always emitted in a deterministic
353 : // order.
1: branch 1 taken
0: branch 2 not taken
354 1: if (!BatchedDiags.empty())
355 1: std::sort(BatchedDiags.begin(), BatchedDiags.end(), CompareDiagnostics());
356 :
357 : // Build up a set of FIDs that we use by scanning the locations and
358 : // ranges of the diagnostics.
359 1: FIDMap FM;
360 1: llvm::SmallVector<FileID, 10> Fids;
361 1: const SourceManager* SM = 0;
362 :
1: branch 1 taken
0: branch 2 not taken
363 1: if (!BatchedDiags.empty())
364 1: SM = &(*BatchedDiags.begin())->begin()->getLocation().getManager();
365 :
6: branch 3 taken
1: branch 4 taken
366 8: for (std::vector<const PathDiagnostic*>::iterator DI = BatchedDiags.begin(),
367 1: DE = BatchedDiags.end(); DI != DE; ++DI) {
368 :
369 6: const PathDiagnostic *D = *DI;
370 :
23: branch 4 taken
6: branch 5 taken
371 29: for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I!=E; ++I) {
372 23: AddFID(FM, Fids, SM, I->getLocation().asLocation());
373 :
11: branch 2 taken
23: branch 3 taken
374 57: for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(),
375 23: RE=I->ranges_end(); RI!=RE; ++RI) {
376 11: AddFID(FM, Fids, SM, RI->getBegin());
377 11: AddFID(FM, Fids, SM, RI->getEnd());
378 : }
379 : }
380 : }
381 :
382 : // Open the file.
383 1: std::string ErrMsg;
384 1: llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg);
0: branch 1 not taken
1: branch 2 taken
385 1: if (!ErrMsg.empty()) {
386 0: llvm::errs() << "warning: could not creat file: " << OutputFile << '\n';
387 0: return;
388 : }
389 :
390 : // Write the plist header.
391 : o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
392 : "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
393 : "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
394 1: "<plist version=\"1.0\">\n";
395 :
396 : // Write the root object: a <dict> containing...
397 : // - "files", an <array> mapping from FIDs to file names
398 : // - "diagnostics", an <array> containing the path diagnostics
399 : o << "<dict>\n"
400 : " <key>files</key>\n"
401 1: " <array>\n";
402 :
1: branch 2 taken
1: branch 3 taken
403 2: for (llvm::SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end();
404 : I!=E; ++I) {
405 1: o << " ";
406 1: EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n';
407 : }
408 :
409 : o << " </array>\n"
410 : " <key>diagnostics</key>\n"
411 1: " <array>\n";
412 :
6: branch 4 taken
1: branch 5 taken
413 8: for (std::vector<const PathDiagnostic*>::iterator DI=BatchedDiags.begin(),
414 1: DE = BatchedDiags.end(); DI!=DE; ++DI) {
415 :
416 : o << " <dict>\n"
417 6: " <key>path</key>\n";
418 :
419 6: const PathDiagnostic *D = *DI;
420 : // Create an owning smart pointer for 'D' just so that we auto-free it
421 : // when we exit this method.
422 6: llvm::OwningPtr<PathDiagnostic> OwnedD(const_cast<PathDiagnostic*>(D));
423 :
424 6: o << " <array>\n";
425 :
23: branch 4 taken
6: branch 5 taken
426 29: for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I)
427 23: ReportDiag(o, *I, FM, *SM, LangOpts);
428 :
429 6: o << " </array>\n";
430 :
431 : // Output the bug type and bug category.
432 6: o << " <key>description</key>";
433 6: EmitString(o, D->getDescription()) << '\n';
434 6: o << " <key>category</key>";
435 6: EmitString(o, D->getCategory()) << '\n';
436 6: o << " <key>type</key>";
437 6: EmitString(o, D->getBugType()) << '\n';
438 :
439 : // Output the location of the bug.
440 6: o << " <key>location</key>\n";
441 6: EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2);
442 :
443 : // Output the diagnostic to the sub-diagnostic client, if any.
0: branch 1 not taken
6: branch 2 taken
444 6: if (SubPD) {
445 0: SubPD->HandlePathDiagnostic(OwnedD.take());
446 0: llvm::SmallVector<std::string, 1> SubFilesMade;
447 0: SubPD->FlushDiagnostics(SubFilesMade);
448 :
0: branch 1 not taken
0: branch 2 not taken
449 0: if (!SubFilesMade.empty()) {
450 0: o << " <key>" << SubPD->getName() << "_files</key>\n";
451 0: o << " <array>\n";
0: branch 1 not taken
0: branch 2 not taken
452 0: for (size_t i = 0, n = SubFilesMade.size(); i < n ; ++i)
453 0: o << " <string>" << SubFilesMade[i] << "</string>\n";
454 0: o << " </array>\n";
455 0: }
456 : }
457 :
458 : // Close up the entry.
459 6: o << " </dict>\n";
460 : }
461 :
462 1: o << " </array>\n";
463 :
464 : // Finish.
465 1: o << "</dict>\n</plist>";
466 :
0: branch 0 not taken
1: branch 1 taken
467 1: if (FilesMade)
468 0: FilesMade->push_back(OutputFile);
469 :
1: branch 2 taken
0: branch 3 not taken
1: branch 5 taken
0: branch 6 not taken
1: branch 8 taken
0: branch 9 not taken
1: branch 11 taken
0: branch 12 not taken
470 1: BatchedDiags.clear();
471 : }
Generated: 2010-02-10 01:31 by zcov