File: | tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp |
Warning: | line 344, column 5 Forming reference to null pointer |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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/Basic/FileManager.h" | |||
15 | #include "clang/Basic/PlistSupport.h" | |||
16 | #include "clang/Basic/SourceManager.h" | |||
17 | #include "clang/Basic/Version.h" | |||
18 | #include "clang/Lex/Preprocessor.h" | |||
19 | #include "clang/Rewrite/Core/HTMLRewrite.h" | |||
20 | #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" | |||
21 | #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" | |||
22 | #include "clang/StaticAnalyzer/Core/IssueHash.h" | |||
23 | #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" | |||
24 | #include "llvm/ADT/Statistic.h" | |||
25 | #include "llvm/ADT/SmallVector.h" | |||
26 | #include "llvm/Support/Casting.h" | |||
27 | using namespace clang; | |||
28 | using namespace ento; | |||
29 | using namespace markup; | |||
30 | ||||
31 | namespace { | |||
32 | class PlistDiagnostics : public PathDiagnosticConsumer { | |||
33 | const std::string OutputFile; | |||
34 | const LangOptions &LangOpts; | |||
35 | const bool SupportsCrossFileDiagnostics; | |||
36 | const bool SerializeStatistics; | |||
37 | public: | |||
38 | PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, | |||
39 | const std::string& prefix, | |||
40 | const LangOptions &LangOpts, | |||
41 | bool supportsMultipleFiles); | |||
42 | ||||
43 | ~PlistDiagnostics() override {} | |||
44 | ||||
45 | void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, | |||
46 | FilesMade *filesMade) override; | |||
47 | ||||
48 | StringRef getName() const override { | |||
49 | return "PlistDiagnostics"; | |||
50 | } | |||
51 | ||||
52 | PathGenerationScheme getGenerationScheme() const override { | |||
53 | return Extensive; | |||
54 | } | |||
55 | bool supportsLogicalOpControlFlow() const override { return true; } | |||
56 | bool supportsCrossFileDiagnostics() const override { | |||
57 | return SupportsCrossFileDiagnostics; | |||
58 | } | |||
59 | }; | |||
60 | } // end anonymous namespace | |||
61 | ||||
62 | PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, | |||
63 | const std::string& output, | |||
64 | const LangOptions &LO, | |||
65 | bool supportsMultipleFiles) | |||
66 | : OutputFile(output), | |||
67 | LangOpts(LO), | |||
68 | SupportsCrossFileDiagnostics(supportsMultipleFiles), | |||
69 | SerializeStatistics(AnalyzerOpts.shouldSerializeStats()) {} | |||
70 | ||||
71 | void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, | |||
72 | PathDiagnosticConsumers &C, | |||
73 | const std::string& s, | |||
74 | const Preprocessor &PP) { | |||
75 | C.push_back(new PlistDiagnostics(AnalyzerOpts, s, | |||
76 | PP.getLangOpts(), false)); | |||
77 | } | |||
78 | ||||
79 | void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, | |||
80 | PathDiagnosticConsumers &C, | |||
81 | const std::string &s, | |||
82 | const Preprocessor &PP) { | |||
83 | C.push_back(new PlistDiagnostics(AnalyzerOpts, s, | |||
84 | PP.getLangOpts(), true)); | |||
85 | } | |||
86 | ||||
87 | static void EmitRanges(raw_ostream &o, | |||
88 | const ArrayRef<SourceRange> Ranges, | |||
89 | const FIDMap& FM, | |||
90 | const SourceManager &SM, | |||
91 | const LangOptions &LangOpts, | |||
92 | unsigned indent) { | |||
93 | ||||
94 | if (Ranges.empty()) | |||
95 | return; | |||
96 | ||||
97 | Indent(o, indent) << "<key>ranges</key>\n"; | |||
98 | Indent(o, indent) << "<array>\n"; | |||
99 | ++indent; | |||
100 | for (auto &R : Ranges) | |||
101 | EmitRange(o, SM, | |||
102 | Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts), | |||
103 | FM, indent + 1); | |||
104 | --indent; | |||
105 | Indent(o, indent) << "</array>\n"; | |||
106 | } | |||
107 | ||||
108 | static void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent) { | |||
109 | // Output the text. | |||
110 | assert(!Message.empty())(static_cast <bool> (!Message.empty()) ? void (0) : __assert_fail ("!Message.empty()", "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp" , 110, __extension__ __PRETTY_FUNCTION__)); | |||
111 | Indent(o, indent) << "<key>extended_message</key>\n"; | |||
112 | Indent(o, indent); | |||
113 | EmitString(o, Message) << '\n'; | |||
114 | ||||
115 | // Output the short text. | |||
116 | // FIXME: Really use a short string. | |||
117 | Indent(o, indent) << "<key>message</key>\n"; | |||
118 | Indent(o, indent); | |||
119 | EmitString(o, Message) << '\n'; | |||
120 | } | |||
121 | ||||
122 | static void ReportControlFlow(raw_ostream &o, | |||
123 | const PathDiagnosticControlFlowPiece& P, | |||
124 | const FIDMap& FM, | |||
125 | const SourceManager &SM, | |||
126 | const LangOptions &LangOpts, | |||
127 | unsigned indent) { | |||
128 | ||||
129 | Indent(o, indent) << "<dict>\n"; | |||
130 | ++indent; | |||
131 | ||||
132 | Indent(o, indent) << "<key>kind</key><string>control</string>\n"; | |||
133 | ||||
134 | // Emit edges. | |||
135 | Indent(o, indent) << "<key>edges</key>\n"; | |||
136 | ++indent; | |||
137 | Indent(o, indent) << "<array>\n"; | |||
138 | ++indent; | |||
139 | for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); | |||
140 | I!=E; ++I) { | |||
141 | Indent(o, indent) << "<dict>\n"; | |||
142 | ++indent; | |||
143 | ||||
144 | // Make the ranges of the start and end point self-consistent with adjacent edges | |||
145 | // by forcing to use only the beginning of the range. This simplifies the layout | |||
146 | // logic for clients. | |||
147 | Indent(o, indent) << "<key>start</key>\n"; | |||
148 | SourceRange StartEdge( | |||
149 | SM.getExpansionLoc(I->getStart().asRange().getBegin())); | |||
150 | EmitRange(o, SM, Lexer::getAsCharRange(StartEdge, SM, LangOpts), FM, | |||
151 | indent + 1); | |||
152 | ||||
153 | Indent(o, indent) << "<key>end</key>\n"; | |||
154 | SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin())); | |||
155 | EmitRange(o, SM, Lexer::getAsCharRange(EndEdge, SM, LangOpts), FM, | |||
156 | indent + 1); | |||
157 | ||||
158 | --indent; | |||
159 | Indent(o, indent) << "</dict>\n"; | |||
160 | } | |||
161 | --indent; | |||
162 | Indent(o, indent) << "</array>\n"; | |||
163 | --indent; | |||
164 | ||||
165 | // Output any helper text. | |||
166 | const auto &s = P.getString(); | |||
167 | if (!s.empty()) { | |||
168 | Indent(o, indent) << "<key>alternate</key>"; | |||
169 | EmitString(o, s) << '\n'; | |||
170 | } | |||
171 | ||||
172 | --indent; | |||
173 | Indent(o, indent) << "</dict>\n"; | |||
174 | } | |||
175 | ||||
176 | static void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, | |||
177 | const FIDMap& FM, | |||
178 | const SourceManager &SM, | |||
179 | const LangOptions &LangOpts, | |||
180 | unsigned indent, | |||
181 | unsigned depth, | |||
182 | bool isKeyEvent = false) { | |||
183 | ||||
184 | Indent(o, indent) << "<dict>\n"; | |||
185 | ++indent; | |||
186 | ||||
187 | Indent(o, indent) << "<key>kind</key><string>event</string>\n"; | |||
188 | ||||
189 | if (isKeyEvent) { | |||
190 | Indent(o, indent) << "<key>key_event</key><true/>\n"; | |||
191 | } | |||
192 | ||||
193 | // Output the location. | |||
194 | FullSourceLoc L = P.getLocation().asLocation(); | |||
195 | ||||
196 | Indent(o, indent) << "<key>location</key>\n"; | |||
197 | EmitLocation(o, SM, L, FM, indent); | |||
198 | ||||
199 | // Output the ranges (if any). | |||
200 | ArrayRef<SourceRange> Ranges = P.getRanges(); | |||
201 | EmitRanges(o, Ranges, FM, SM, LangOpts, indent); | |||
202 | ||||
203 | // Output the call depth. | |||
204 | Indent(o, indent) << "<key>depth</key>"; | |||
205 | EmitInteger(o, depth) << '\n'; | |||
206 | ||||
207 | // Output the text. | |||
208 | EmitMessage(o, P.getString(), indent); | |||
209 | ||||
210 | // Finish up. | |||
211 | --indent; | |||
212 | Indent(o, indent); o << "</dict>\n"; | |||
213 | } | |||
214 | ||||
215 | static void ReportPiece(raw_ostream &o, | |||
216 | const PathDiagnosticPiece &P, | |||
217 | const FIDMap& FM, const SourceManager &SM, | |||
218 | const LangOptions &LangOpts, | |||
219 | unsigned indent, | |||
220 | unsigned depth, | |||
221 | bool includeControlFlow, | |||
222 | bool isKeyEvent = false); | |||
223 | ||||
224 | static void ReportCall(raw_ostream &o, | |||
225 | const PathDiagnosticCallPiece &P, | |||
226 | const FIDMap& FM, const SourceManager &SM, | |||
227 | const LangOptions &LangOpts, | |||
228 | unsigned indent, | |||
229 | unsigned depth) { | |||
230 | ||||
231 | if (auto callEnter = P.getCallEnterEvent()) | |||
232 | ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true, | |||
233 | P.isLastInMainSourceFile()); | |||
234 | ||||
235 | ||||
236 | ++depth; | |||
237 | ||||
238 | if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent()) | |||
239 | ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, | |||
240 | indent, depth, true); | |||
241 | ||||
242 | for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) | |||
243 | ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); | |||
244 | ||||
245 | --depth; | |||
246 | ||||
247 | if (auto callExit = P.getCallExitEvent()) | |||
248 | ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true); | |||
249 | } | |||
250 | ||||
251 | static void ReportMacro(raw_ostream &o, | |||
252 | const PathDiagnosticMacroPiece& P, | |||
253 | const FIDMap& FM, const SourceManager &SM, | |||
254 | const LangOptions &LangOpts, | |||
255 | unsigned indent, | |||
256 | unsigned depth) { | |||
257 | ||||
258 | for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); | |||
259 | I!=E; ++I) { | |||
260 | ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false); | |||
261 | } | |||
262 | } | |||
263 | ||||
264 | static void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, | |||
265 | const FIDMap& FM, | |||
266 | const SourceManager &SM, | |||
267 | const LangOptions &LangOpts, | |||
268 | unsigned indent, | |||
269 | unsigned depth) { | |||
270 | ||||
271 | Indent(o, indent) << "<dict>\n"; | |||
272 | ++indent; | |||
273 | ||||
274 | // Output the location. | |||
275 | FullSourceLoc L = P.getLocation().asLocation(); | |||
276 | ||||
277 | Indent(o, indent) << "<key>location</key>\n"; | |||
278 | EmitLocation(o, SM, L, FM, indent); | |||
279 | ||||
280 | // Output the ranges (if any). | |||
281 | ArrayRef<SourceRange> Ranges = P.getRanges(); | |||
282 | EmitRanges(o, Ranges, FM, SM, LangOpts, indent); | |||
283 | ||||
284 | // Output the text. | |||
285 | EmitMessage(o, P.getString(), indent); | |||
286 | ||||
287 | // Finish up. | |||
288 | --indent; | |||
289 | Indent(o, indent); o << "</dict>\n"; | |||
290 | } | |||
291 | ||||
292 | static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, | |||
293 | const FIDMap& FM, const SourceManager &SM, | |||
294 | const LangOptions &LangOpts) { | |||
295 | ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true); | |||
296 | } | |||
297 | ||||
298 | static void ReportPiece(raw_ostream &o, | |||
299 | const PathDiagnosticPiece &P, | |||
300 | const FIDMap& FM, const SourceManager &SM, | |||
301 | const LangOptions &LangOpts, | |||
302 | unsigned indent, | |||
303 | unsigned depth, | |||
304 | bool includeControlFlow, | |||
305 | bool isKeyEvent) { | |||
306 | switch (P.getKind()) { | |||
307 | case PathDiagnosticPiece::ControlFlow: | |||
308 | if (includeControlFlow) | |||
309 | ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, | |||
310 | LangOpts, indent); | |||
311 | break; | |||
312 | case PathDiagnosticPiece::Call: | |||
313 | ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts, | |||
314 | indent, depth); | |||
315 | break; | |||
316 | case PathDiagnosticPiece::Event: | |||
317 | ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts, | |||
318 | indent, depth, isKeyEvent); | |||
319 | break; | |||
320 | case PathDiagnosticPiece::Macro: | |||
321 | ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, | |||
322 | indent, depth); | |||
323 | break; | |||
324 | case PathDiagnosticPiece::Note: | |||
325 | ReportNote(o, cast<PathDiagnosticNotePiece>(P), FM, SM, LangOpts, | |||
326 | indent, depth); | |||
327 | break; | |||
328 | } | |||
329 | } | |||
330 | ||||
331 | void PlistDiagnostics::FlushDiagnosticsImpl( | |||
332 | std::vector<const PathDiagnostic *> &Diags, | |||
333 | FilesMade *filesMade) { | |||
334 | // Build up a set of FIDs that we use by scanning the locations and | |||
335 | // ranges of the diagnostics. | |||
336 | FIDMap FM; | |||
337 | SmallVector<FileID, 10> Fids; | |||
338 | const SourceManager* SM = nullptr; | |||
339 | ||||
340 | if (!Diags.empty()) | |||
| ||||
341 | SM = &Diags.front()->path.front()->getLocation().getManager(); | |||
342 | ||||
343 | auto AddPieceFID = [&FM, &Fids, SM](const PathDiagnosticPiece &Piece) { | |||
344 | AddFID(FM, Fids, *SM, Piece.getLocation().asLocation()); | |||
| ||||
345 | ArrayRef<SourceRange> Ranges = Piece.getRanges(); | |||
346 | for (const SourceRange &Range : Ranges) { | |||
347 | AddFID(FM, Fids, *SM, Range.getBegin()); | |||
348 | AddFID(FM, Fids, *SM, Range.getEnd()); | |||
349 | } | |||
350 | }; | |||
351 | ||||
352 | for (const PathDiagnostic *D : Diags) { | |||
353 | ||||
354 | SmallVector<const PathPieces *, 5> WorkList; | |||
355 | WorkList.push_back(&D->path); | |||
356 | ||||
357 | while (!WorkList.empty()) { | |||
358 | const PathPieces &Path = *WorkList.pop_back_val(); | |||
359 | ||||
360 | for (const auto &Iter : Path) { | |||
361 | const PathDiagnosticPiece &Piece = *Iter; | |||
362 | AddPieceFID(Piece); | |||
363 | ||||
364 | if (const PathDiagnosticCallPiece *Call = | |||
365 | dyn_cast<PathDiagnosticCallPiece>(&Piece)) { | |||
366 | if (auto CallEnterWithin = Call->getCallEnterWithinCallerEvent()) | |||
367 | AddPieceFID(*CallEnterWithin); | |||
368 | ||||
369 | if (auto CallEnterEvent = Call->getCallEnterEvent()) | |||
370 | AddPieceFID(*CallEnterEvent); | |||
371 | ||||
372 | WorkList.push_back(&Call->path); | |||
373 | } else if (const PathDiagnosticMacroPiece *Macro = | |||
374 | dyn_cast<PathDiagnosticMacroPiece>(&Piece)) { | |||
375 | WorkList.push_back(&Macro->subPieces); | |||
376 | } | |||
377 | } | |||
378 | } | |||
379 | } | |||
380 | ||||
381 | // Open the file. | |||
382 | std::error_code EC; | |||
383 | llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text); | |||
384 | if (EC) { | |||
385 | llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; | |||
386 | return; | |||
387 | } | |||
388 | ||||
389 | EmitPlistHeader(o); | |||
390 | ||||
391 | // Write the root object: a <dict> containing... | |||
392 | // - "clang_version", the string representation of clang version | |||
393 | // - "files", an <array> mapping from FIDs to file names | |||
394 | // - "diagnostics", an <array> containing the path diagnostics | |||
395 | o << "<dict>\n" << | |||
396 | " <key>clang_version</key>\n"; | |||
397 | EmitString(o, getClangFullVersion()) << '\n'; | |||
398 | o << " <key>files</key>\n" | |||
399 | " <array>\n"; | |||
400 | ||||
401 | for (FileID FID : Fids) | |||
402 | EmitString(o << " ", SM->getFileEntryForID(FID)->getName()) << '\n'; | |||
403 | ||||
404 | o << " </array>\n" | |||
405 | " <key>diagnostics</key>\n" | |||
406 | " <array>\n"; | |||
407 | ||||
408 | for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), | |||
409 | DE = Diags.end(); DI!=DE; ++DI) { | |||
410 | ||||
411 | o << " <dict>\n"; | |||
412 | ||||
413 | const PathDiagnostic *D = *DI; | |||
414 | const PathPieces &PP = D->path; | |||
415 | ||||
416 | assert(std::is_partitioned((static_cast <bool> (std::is_partitioned( PP.begin(), PP .end(), [](const std::shared_ptr<PathDiagnosticPiece> & E) { return E->getKind() == PathDiagnosticPiece::Note; }) && "PathDiagnostic is not partitioned so that notes precede the rest" ) ? void (0) : __assert_fail ("std::is_partitioned( PP.begin(), PP.end(), [](const std::shared_ptr<PathDiagnosticPiece> &E) { return E->getKind() == PathDiagnosticPiece::Note; }) && \"PathDiagnostic is not partitioned so that notes precede the rest\"" , "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp" , 420, __extension__ __PRETTY_FUNCTION__)) | |||
417 | PP.begin(), PP.end(),(static_cast <bool> (std::is_partitioned( PP.begin(), PP .end(), [](const std::shared_ptr<PathDiagnosticPiece> & E) { return E->getKind() == PathDiagnosticPiece::Note; }) && "PathDiagnostic is not partitioned so that notes precede the rest" ) ? void (0) : __assert_fail ("std::is_partitioned( PP.begin(), PP.end(), [](const std::shared_ptr<PathDiagnosticPiece> &E) { return E->getKind() == PathDiagnosticPiece::Note; }) && \"PathDiagnostic is not partitioned so that notes precede the rest\"" , "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp" , 420, __extension__ __PRETTY_FUNCTION__)) | |||
418 | [](const std::shared_ptr<PathDiagnosticPiece> &E)(static_cast <bool> (std::is_partitioned( PP.begin(), PP .end(), [](const std::shared_ptr<PathDiagnosticPiece> & E) { return E->getKind() == PathDiagnosticPiece::Note; }) && "PathDiagnostic is not partitioned so that notes precede the rest" ) ? void (0) : __assert_fail ("std::is_partitioned( PP.begin(), PP.end(), [](const std::shared_ptr<PathDiagnosticPiece> &E) { return E->getKind() == PathDiagnosticPiece::Note; }) && \"PathDiagnostic is not partitioned so that notes precede the rest\"" , "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp" , 420, __extension__ __PRETTY_FUNCTION__)) | |||
419 | { return E->getKind() == PathDiagnosticPiece::Note; }) &&(static_cast <bool> (std::is_partitioned( PP.begin(), PP .end(), [](const std::shared_ptr<PathDiagnosticPiece> & E) { return E->getKind() == PathDiagnosticPiece::Note; }) && "PathDiagnostic is not partitioned so that notes precede the rest" ) ? void (0) : __assert_fail ("std::is_partitioned( PP.begin(), PP.end(), [](const std::shared_ptr<PathDiagnosticPiece> &E) { return E->getKind() == PathDiagnosticPiece::Note; }) && \"PathDiagnostic is not partitioned so that notes precede the rest\"" , "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp" , 420, __extension__ __PRETTY_FUNCTION__)) | |||
420 | "PathDiagnostic is not partitioned so that notes precede the rest")(static_cast <bool> (std::is_partitioned( PP.begin(), PP .end(), [](const std::shared_ptr<PathDiagnosticPiece> & E) { return E->getKind() == PathDiagnosticPiece::Note; }) && "PathDiagnostic is not partitioned so that notes precede the rest" ) ? void (0) : __assert_fail ("std::is_partitioned( PP.begin(), PP.end(), [](const std::shared_ptr<PathDiagnosticPiece> &E) { return E->getKind() == PathDiagnosticPiece::Note; }) && \"PathDiagnostic is not partitioned so that notes precede the rest\"" , "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp" , 420, __extension__ __PRETTY_FUNCTION__)); | |||
421 | ||||
422 | PathPieces::const_iterator FirstNonNote = std::partition_point( | |||
423 | PP.begin(), PP.end(), | |||
424 | [](const std::shared_ptr<PathDiagnosticPiece> &E) | |||
425 | { return E->getKind() == PathDiagnosticPiece::Note; }); | |||
426 | ||||
427 | PathPieces::const_iterator I = PP.begin(); | |||
428 | ||||
429 | if (FirstNonNote != PP.begin()) { | |||
430 | o << " <key>notes</key>\n" | |||
431 | " <array>\n"; | |||
432 | ||||
433 | for (; I != FirstNonNote; ++I) | |||
434 | ReportDiag(o, **I, FM, *SM, LangOpts); | |||
435 | ||||
436 | o << " </array>\n"; | |||
437 | } | |||
438 | ||||
439 | o << " <key>path</key>\n"; | |||
440 | ||||
441 | o << " <array>\n"; | |||
442 | ||||
443 | for (PathPieces::const_iterator E = PP.end(); I != E; ++I) | |||
444 | ReportDiag(o, **I, FM, *SM, LangOpts); | |||
445 | ||||
446 | o << " </array>\n"; | |||
447 | ||||
448 | // Output the bug type and bug category. | |||
449 | o << " <key>description</key>"; | |||
450 | EmitString(o, D->getShortDescription()) << '\n'; | |||
451 | o << " <key>category</key>"; | |||
452 | EmitString(o, D->getCategory()) << '\n'; | |||
453 | o << " <key>type</key>"; | |||
454 | EmitString(o, D->getBugType()) << '\n'; | |||
455 | o << " <key>check_name</key>"; | |||
456 | EmitString(o, D->getCheckName()) << '\n'; | |||
457 | ||||
458 | o << " <!-- This hash is experimental and going to change! -->\n"; | |||
459 | o << " <key>issue_hash_content_of_line_in_context</key>"; | |||
460 | PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); | |||
461 | FullSourceLoc L(SM->getExpansionLoc(UPDLoc.isValid() | |||
462 | ? UPDLoc.asLocation() | |||
463 | : D->getLocation().asLocation()), | |||
464 | *SM); | |||
465 | const Decl *DeclWithIssue = D->getDeclWithIssue(); | |||
466 | EmitString(o, GetIssueHash(*SM, L, D->getCheckName(), D->getBugType(), | |||
467 | DeclWithIssue, LangOpts)) | |||
468 | << '\n'; | |||
469 | ||||
470 | // Output information about the semantic context where | |||
471 | // the issue occurred. | |||
472 | if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { | |||
473 | // FIXME: handle blocks, which have no name. | |||
474 | if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { | |||
475 | StringRef declKind; | |||
476 | switch (ND->getKind()) { | |||
477 | case Decl::CXXRecord: | |||
478 | declKind = "C++ class"; | |||
479 | break; | |||
480 | case Decl::CXXMethod: | |||
481 | declKind = "C++ method"; | |||
482 | break; | |||
483 | case Decl::ObjCMethod: | |||
484 | declKind = "Objective-C method"; | |||
485 | break; | |||
486 | case Decl::Function: | |||
487 | declKind = "function"; | |||
488 | break; | |||
489 | default: | |||
490 | break; | |||
491 | } | |||
492 | if (!declKind.empty()) { | |||
493 | const std::string &declName = ND->getDeclName().getAsString(); | |||
494 | o << " <key>issue_context_kind</key>"; | |||
495 | EmitString(o, declKind) << '\n'; | |||
496 | o << " <key>issue_context</key>"; | |||
497 | EmitString(o, declName) << '\n'; | |||
498 | } | |||
499 | ||||
500 | // Output the bug hash for issue unique-ing. Currently, it's just an | |||
501 | // offset from the beginning of the function. | |||
502 | if (const Stmt *Body = DeclWithIssue->getBody()) { | |||
503 | ||||
504 | // If the bug uniqueing location exists, use it for the hash. | |||
505 | // For example, this ensures that two leaks reported on the same line | |||
506 | // will have different issue_hashes and that the hash will identify | |||
507 | // the leak location even after code is added between the allocation | |||
508 | // site and the end of scope (leak report location). | |||
509 | if (UPDLoc.isValid()) { | |||
510 | FullSourceLoc UFunL(SM->getExpansionLoc( | |||
511 | D->getUniqueingDecl()->getBody()->getLocStart()), *SM); | |||
512 | o << " <key>issue_hash_function_offset</key><string>" | |||
513 | << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber() | |||
514 | << "</string>\n"; | |||
515 | ||||
516 | // Otherwise, use the location on which the bug is reported. | |||
517 | } else { | |||
518 | FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); | |||
519 | o << " <key>issue_hash_function_offset</key><string>" | |||
520 | << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() | |||
521 | << "</string>\n"; | |||
522 | } | |||
523 | ||||
524 | } | |||
525 | } | |||
526 | } | |||
527 | ||||
528 | // Output the location of the bug. | |||
529 | o << " <key>location</key>\n"; | |||
530 | EmitLocation(o, *SM, D->getLocation().asLocation(), FM, 2); | |||
531 | ||||
532 | // Output the diagnostic to the sub-diagnostic client, if any. | |||
533 | if (!filesMade->empty()) { | |||
534 | StringRef lastName; | |||
535 | PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); | |||
536 | if (files) { | |||
537 | for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), | |||
538 | CE = files->end(); CI != CE; ++CI) { | |||
539 | StringRef newName = CI->first; | |||
540 | if (newName != lastName) { | |||
541 | if (!lastName.empty()) { | |||
542 | o << " </array>\n"; | |||
543 | } | |||
544 | lastName = newName; | |||
545 | o << " <key>" << lastName << "_files</key>\n"; | |||
546 | o << " <array>\n"; | |||
547 | } | |||
548 | o << " <string>" << CI->second << "</string>\n"; | |||
549 | } | |||
550 | o << " </array>\n"; | |||
551 | } | |||
552 | } | |||
553 | ||||
554 | // Close up the entry. | |||
555 | o << " </dict>\n"; | |||
556 | } | |||
557 | ||||
558 | o << " </array>\n"; | |||
559 | ||||
560 | if (llvm::AreStatisticsEnabled() && SerializeStatistics) { | |||
561 | o << " <key>statistics</key>\n"; | |||
562 | std::string stats; | |||
563 | llvm::raw_string_ostream os(stats); | |||
564 | llvm::PrintStatisticsJSON(os); | |||
565 | os.flush(); | |||
566 | EmitString(o, html::EscapeText(stats)) << '\n'; | |||
567 | } | |||
568 | ||||
569 | // Finish. | |||
570 | o << "</dict>\n</plist>"; | |||
571 | } |