1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
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/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" |
20 | #include "clang/StaticAnalyzer/Core/IssueHash.h" |
21 | #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" |
22 | #include "llvm/ADT/SmallVector.h" |
23 | #include "llvm/Support/Casting.h" |
24 | using namespace clang; |
25 | using namespace ento; |
26 | using namespace markup; |
27 | |
28 | namespace { |
29 | class PlistDiagnostics : public PathDiagnosticConsumer { |
30 | const std::string OutputFile; |
31 | const LangOptions &LangOpts; |
32 | const bool SupportsCrossFileDiagnostics; |
33 | public: |
34 | PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, |
35 | const std::string& prefix, |
36 | const LangOptions &LangOpts, |
37 | bool supportsMultipleFiles); |
38 | |
39 | ~PlistDiagnostics() override {} |
40 | |
41 | void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, |
42 | FilesMade *filesMade) override; |
43 | |
44 | StringRef getName() const override { |
45 | return "PlistDiagnostics"; |
46 | } |
47 | |
48 | PathGenerationScheme getGenerationScheme() const override { |
49 | return Extensive; |
50 | } |
51 | bool supportsLogicalOpControlFlow() const override { return true; } |
52 | bool supportsCrossFileDiagnostics() const override { |
53 | return SupportsCrossFileDiagnostics; |
54 | } |
55 | }; |
56 | } |
57 | |
58 | PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, |
59 | const std::string& output, |
60 | const LangOptions &LO, |
61 | bool supportsMultipleFiles) |
62 | : OutputFile(output), |
63 | LangOpts(LO), |
64 | SupportsCrossFileDiagnostics(supportsMultipleFiles) {} |
65 | |
66 | void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, |
67 | PathDiagnosticConsumers &C, |
68 | const std::string& s, |
69 | const Preprocessor &PP) { |
70 | C.push_back(new PlistDiagnostics(AnalyzerOpts, s, |
71 | PP.getLangOpts(), false)); |
72 | } |
73 | |
74 | void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, |
75 | PathDiagnosticConsumers &C, |
76 | const std::string &s, |
77 | const Preprocessor &PP) { |
78 | C.push_back(new PlistDiagnostics(AnalyzerOpts, s, |
79 | PP.getLangOpts(), true)); |
80 | } |
81 | |
82 | static void ReportControlFlow(raw_ostream &o, |
83 | const PathDiagnosticControlFlowPiece& P, |
84 | const FIDMap& FM, |
85 | const SourceManager &SM, |
86 | const LangOptions &LangOpts, |
87 | unsigned indent) { |
88 | |
89 | Indent(o, indent) << "<dict>\n"; |
90 | ++indent; |
91 | |
92 | Indent(o, indent) << "<key>kind</key><string>control</string>\n"; |
93 | |
94 | |
95 | Indent(o, indent) << "<key>edges</key>\n"; |
96 | ++indent; |
97 | Indent(o, indent) << "<array>\n"; |
98 | ++indent; |
99 | for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); |
100 | I!=E; ++I) { |
101 | Indent(o, indent) << "<dict>\n"; |
102 | ++indent; |
103 | |
104 | |
105 | |
106 | |
107 | Indent(o, indent) << "<key>start</key>\n"; |
108 | SourceRange StartEdge( |
109 | SM.getExpansionLoc(I->getStart().asRange().getBegin())); |
110 | EmitRange(o, SM, Lexer::getAsCharRange(StartEdge, SM, LangOpts), FM, |
111 | indent + 1); |
112 | |
113 | Indent(o, indent) << "<key>end</key>\n"; |
114 | SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin())); |
115 | EmitRange(o, SM, Lexer::getAsCharRange(EndEdge, SM, LangOpts), FM, |
116 | indent + 1); |
117 | |
118 | --indent; |
119 | Indent(o, indent) << "</dict>\n"; |
120 | } |
121 | --indent; |
122 | Indent(o, indent) << "</array>\n"; |
123 | --indent; |
124 | |
125 | |
126 | const auto &s = P.getString(); |
127 | if (!s.empty()) { |
128 | Indent(o, indent) << "<key>alternate</key>"; |
129 | EmitString(o, s) << '\n'; |
130 | } |
131 | |
132 | --indent; |
133 | Indent(o, indent) << "</dict>\n"; |
134 | } |
135 | |
136 | static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, |
137 | const FIDMap& FM, |
138 | const SourceManager &SM, |
139 | const LangOptions &LangOpts, |
140 | unsigned indent, |
141 | unsigned depth, |
142 | bool isKeyEvent = false) { |
143 | |
144 | Indent(o, indent) << "<dict>\n"; |
145 | ++indent; |
146 | |
147 | Indent(o, indent) << "<key>kind</key><string>event</string>\n"; |
148 | |
149 | if (isKeyEvent) { |
150 | Indent(o, indent) << "<key>key_event</key><true/>\n"; |
151 | } |
152 | |
153 | |
154 | FullSourceLoc L = P.getLocation().asLocation(); |
155 | |
156 | Indent(o, indent) << "<key>location</key>\n"; |
157 | EmitLocation(o, SM, L, FM, indent); |
158 | |
159 | |
160 | ArrayRef<SourceRange> Ranges = P.getRanges(); |
161 | |
162 | if (!Ranges.empty()) { |
163 | Indent(o, indent) << "<key>ranges</key>\n"; |
164 | Indent(o, indent) << "<array>\n"; |
165 | ++indent; |
166 | for (auto &R : Ranges) |
167 | EmitRange(o, SM, |
168 | Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts), |
169 | FM, indent + 1); |
170 | --indent; |
171 | Indent(o, indent) << "</array>\n"; |
172 | } |
173 | |
174 | |
175 | Indent(o, indent) << "<key>depth</key>"; |
176 | EmitInteger(o, depth) << '\n'; |
177 | |
178 | |
179 | assert(!P.getString().empty())((!P.getString().empty()) ? static_cast<void> (0) : __assert_fail ("!P.getString().empty()", "/tmp/buildd/llvm-toolchain-snapshot-4.0~svn290870/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp" , 179, __PRETTY_FUNCTION__)); |
180 | Indent(o, indent) << "<key>extended_message</key>\n"; |
181 | Indent(o, indent); |
182 | EmitString(o, P.getString()) << '\n'; |
183 | |
184 | |
185 | |
186 | Indent(o, indent) << "<key>message</key>\n"; |
187 | Indent(o, indent); |
188 | EmitString(o, P.getString()) << '\n'; |
189 | |
190 | |
191 | --indent; |
192 | Indent(o, indent); o << "</dict>\n"; |
193 | } |
194 | |
195 | static void ReportPiece(raw_ostream &o, |
196 | const PathDiagnosticPiece &P, |
197 | const FIDMap& FM, const SourceManager &SM, |
198 | const LangOptions &LangOpts, |
199 | unsigned indent, |
200 | unsigned depth, |
201 | bool includeControlFlow, |
202 | bool isKeyEvent = false); |
203 | |
204 | static void ReportCall(raw_ostream &o, |
205 | const PathDiagnosticCallPiece &P, |
206 | const FIDMap& FM, const SourceManager &SM, |
207 | const LangOptions &LangOpts, |
208 | unsigned indent, |
209 | unsigned depth) { |
210 | |
211 | IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = |
212 | P.getCallEnterEvent(); |
213 | |
214 | if (callEnter) |
215 | ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true, |
216 | P.isLastInMainSourceFile()); |
217 | |
218 | IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller = |
219 | P.getCallEnterWithinCallerEvent(); |
220 | |
221 | ++depth; |
222 | |
223 | if (callEnterWithinCaller) |
224 | ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, |
225 | indent, depth, true); |
226 | |
227 | for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) |
228 | ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); |
229 | |
230 | --depth; |
231 | |
232 | IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = |
233 | P.getCallExitEvent(); |
234 | |
235 | if (callExit) |
236 | ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true); |
237 | } |
238 | |
239 | static void ReportMacro(raw_ostream &o, |
240 | const PathDiagnosticMacroPiece& P, |
241 | const FIDMap& FM, const SourceManager &SM, |
242 | const LangOptions &LangOpts, |
243 | unsigned indent, |
244 | unsigned depth) { |
245 | |
246 | for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); |
247 | I!=E; ++I) { |
248 | ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false); |
249 | } |
250 | } |
251 | |
252 | static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, |
253 | const FIDMap& FM, const SourceManager &SM, |
254 | const LangOptions &LangOpts) { |
255 | ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true); |
256 | } |
257 | |
258 | static void ReportPiece(raw_ostream &o, |
259 | const PathDiagnosticPiece &P, |
260 | const FIDMap& FM, const SourceManager &SM, |
261 | const LangOptions &LangOpts, |
262 | unsigned indent, |
263 | unsigned depth, |
264 | bool includeControlFlow, |
265 | bool isKeyEvent) { |
266 | switch (P.getKind()) { |
267 | case PathDiagnosticPiece::ControlFlow: |
268 | if (includeControlFlow) |
269 | ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, |
270 | LangOpts, indent); |
271 | break; |
272 | case PathDiagnosticPiece::Call: |
273 | ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts, |
274 | indent, depth); |
275 | break; |
276 | case PathDiagnosticPiece::Event: |
277 | ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts, |
278 | indent, depth, isKeyEvent); |
279 | break; |
280 | case PathDiagnosticPiece::Macro: |
281 | ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, |
282 | indent, depth); |
283 | break; |
284 | case PathDiagnosticPiece::Note: |
285 | |
286 | break; |
287 | } |
288 | } |
289 | |
290 | void PlistDiagnostics::FlushDiagnosticsImpl( |
291 | std::vector<const PathDiagnostic *> &Diags, |
292 | FilesMade *filesMade) { |
293 | |
294 | |
295 | FIDMap FM; |
296 | SmallVector<FileID, 10> Fids; |
297 | const SourceManager* SM = nullptr; |
| 1 | 'SM' initialized to a null pointer value | |
|
298 | |
299 | if (!Diags.empty()) |
| 2 | | Assuming the condition is false | |
|
| |
300 | SM = &Diags.front()->path.front()->getLocation().getManager(); |
301 | |
302 | |
303 | auto AddPieceFID = [&FM, &Fids, SM](const PathDiagnosticPiece *Piece)->void { |
304 | AddFID(FM, Fids, *SM, Piece->getLocation().asLocation()); |
305 | ArrayRef<SourceRange> Ranges = Piece->getRanges(); |
306 | for (const SourceRange &Range : Ranges) { |
307 | AddFID(FM, Fids, *SM, Range.getBegin()); |
308 | AddFID(FM, Fids, *SM, Range.getEnd()); |
309 | } |
310 | }; |
311 | |
312 | for (const PathDiagnostic *D : Diags) { |
313 | |
314 | SmallVector<const PathPieces *, 5> WorkList; |
315 | WorkList.push_back(&D->path); |
316 | |
317 | while (!WorkList.empty()) { |
318 | const PathPieces &Path = *WorkList.pop_back_val(); |
319 | |
320 | for (const auto &Iter : Path) { |
321 | const PathDiagnosticPiece *Piece = Iter.get(); |
322 | AddPieceFID(Piece); |
323 | |
324 | if (const PathDiagnosticCallPiece *Call = |
325 | dyn_cast<PathDiagnosticCallPiece>(Piece)) { |
326 | if (IntrusiveRefCntPtr<PathDiagnosticEventPiece> |
327 | CallEnterWithin = Call->getCallEnterWithinCallerEvent()) |
328 | AddPieceFID(CallEnterWithin.get()); |
329 | |
330 | if (IntrusiveRefCntPtr<PathDiagnosticEventPiece> |
331 | CallEnterEvent = Call->getCallEnterEvent()) |
332 | AddPieceFID(CallEnterEvent.get()); |
333 | |
334 | WorkList.push_back(&Call->path); |
335 | } |
336 | else if (const PathDiagnosticMacroPiece *Macro = |
337 | dyn_cast<PathDiagnosticMacroPiece>(Piece)) { |
338 | WorkList.push_back(&Macro->subPieces); |
339 | } |
340 | } |
341 | } |
342 | } |
343 | |
344 | |
345 | std::error_code EC; |
346 | llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text); |
347 | if (EC) { |
| |
348 | llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; |
349 | return; |
350 | } |
351 | |
352 | EmitPlistHeader(o); |
353 | |
354 | |
355 | |
356 | |
357 | |
358 | o << "<dict>\n" << |
359 | " <key>clang_version</key>\n"; |
360 | EmitString(o, getClangFullVersion()) << '\n'; |
361 | o << " <key>files</key>\n" |
362 | " <array>\n"; |
363 | |
364 | for (FileID FID : Fids) |
| 5 | | Assuming '__begin' is equal to '__end' | |
|
365 | EmitString(o << " ", SM->getFileEntryForID(FID)->getName()) << '\n'; |
366 | |
367 | o << " </array>\n" |
368 | " <key>diagnostics</key>\n" |
369 | " <array>\n"; |
370 | |
371 | for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), |
| 6 | | Loop condition is true. Entering loop body | |
|
372 | DE = Diags.end(); DI!=DE; ++DI) { |
373 | |
374 | o << " <dict>\n" |
375 | " <key>path</key>\n"; |
376 | |
377 | const PathDiagnostic *D = *DI; |
378 | |
379 | o << " <array>\n"; |
380 | |
381 | for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); |
| 7 | | Loop condition is false. Execution continues on line 385 | |
|
382 | I != E; ++I) |
383 | ReportDiag(o, **I, FM, *SM, LangOpts); |
384 | |
385 | o << " </array>\n"; |
386 | |
387 | |
388 | o << " <key>description</key>"; |
389 | EmitString(o, D->getShortDescription()) << '\n'; |
390 | o << " <key>category</key>"; |
391 | EmitString(o, D->getCategory()) << '\n'; |
392 | o << " <key>type</key>"; |
393 | EmitString(o, D->getBugType()) << '\n'; |
394 | o << " <key>check_name</key>"; |
395 | EmitString(o, D->getCheckName()) << '\n'; |
396 | |
397 | o << " <!-- This hash is experimental and going to change! -->\n"; |
398 | o << " <key>issue_hash_content_of_line_in_context</key>"; |
399 | PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); |
400 | FullSourceLoc L(SM->getExpansionLoc(UPDLoc.isValid() |
| |
| 9 | | Called C++ object pointer is null |
|
401 | ? UPDLoc.asLocation() |
402 | : D->getLocation().asLocation()), |
403 | *SM); |
404 | const Decl *DeclWithIssue = D->getDeclWithIssue(); |
405 | EmitString(o, GetIssueHash(*SM, L, D->getCheckName(), D->getBugType(), |
406 | DeclWithIssue, LangOpts)) |
407 | << '\n'; |
408 | |
409 | |
410 | |
411 | if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { |
412 | |
413 | if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { |
414 | StringRef declKind; |
415 | switch (ND->getKind()) { |
416 | case Decl::CXXRecord: |
417 | declKind = "C++ class"; |
418 | break; |
419 | case Decl::CXXMethod: |
420 | declKind = "C++ method"; |
421 | break; |
422 | case Decl::ObjCMethod: |
423 | declKind = "Objective-C method"; |
424 | break; |
425 | case Decl::Function: |
426 | declKind = "function"; |
427 | break; |
428 | default: |
429 | break; |
430 | } |
431 | if (!declKind.empty()) { |
432 | const std::string &declName = ND->getDeclName().getAsString(); |
433 | o << " <key>issue_context_kind</key>"; |
434 | EmitString(o, declKind) << '\n'; |
435 | o << " <key>issue_context</key>"; |
436 | EmitString(o, declName) << '\n'; |
437 | } |
438 | |
439 | |
440 | |
441 | if (const Stmt *Body = DeclWithIssue->getBody()) { |
442 | |
443 | |
444 | |
445 | |
446 | |
447 | |
448 | if (UPDLoc.isValid()) { |
449 | FullSourceLoc UFunL(SM->getExpansionLoc( |
450 | D->getUniqueingDecl()->getBody()->getLocStart()), *SM); |
451 | o << " <key>issue_hash_function_offset</key><string>" |
452 | << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber() |
453 | << "</string>\n"; |
454 | |
455 | |
456 | } else { |
457 | FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); |
458 | o << " <key>issue_hash_function_offset</key><string>" |
459 | << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() |
460 | << "</string>\n"; |
461 | } |
462 | |
463 | } |
464 | } |
465 | } |
466 | |
467 | |
468 | o << " <key>location</key>\n"; |
469 | EmitLocation(o, *SM, D->getLocation().asLocation(), FM, 2); |
470 | |
471 | |
472 | if (!filesMade->empty()) { |
473 | StringRef lastName; |
474 | PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); |
475 | if (files) { |
476 | for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), |
477 | CE = files->end(); CI != CE; ++CI) { |
478 | StringRef newName = CI->first; |
479 | if (newName != lastName) { |
480 | if (!lastName.empty()) { |
481 | o << " </array>\n"; |
482 | } |
483 | lastName = newName; |
484 | o << " <key>" << lastName << "_files</key>\n"; |
485 | o << " <array>\n"; |
486 | } |
487 | o << " <string>" << CI->second << "</string>\n"; |
488 | } |
489 | o << " </array>\n"; |
490 | } |
491 | } |
492 | |
493 | |
494 | o << " </dict>\n"; |
495 | } |
496 | |
497 | o << " </array>\n"; |
498 | |
499 | |
500 | o << "</dict>\n</plist>"; |
501 | } |