1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | #include "clang/Basic/FileManager.h" |
14 | #include "clang/Basic/PlistSupport.h" |
15 | #include "clang/Basic/SourceManager.h" |
16 | #include "clang/Basic/Version.h" |
17 | #include "clang/Lex/Preprocessor.h" |
18 | #include "clang/Lex/TokenConcatenation.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/SmallPtrSet.h" |
26 | #include "llvm/ADT/SmallVector.h" |
27 | #include "llvm/Support/Casting.h" |
28 | |
29 | using namespace clang; |
30 | using namespace ento; |
31 | using namespace markup; |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | namespace { |
39 | class PlistDiagnostics : public PathDiagnosticConsumer { |
40 | const std::string OutputFile; |
41 | const Preprocessor &PP; |
42 | AnalyzerOptions &AnOpts; |
43 | const bool SupportsCrossFileDiagnostics; |
44 | public: |
45 | PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, |
46 | const std::string& prefix, |
47 | const Preprocessor &PP, |
48 | bool supportsMultipleFiles); |
49 | |
50 | ~PlistDiagnostics() override {} |
51 | |
52 | void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, |
53 | FilesMade *filesMade) override; |
54 | |
55 | StringRef getName() const override { |
56 | return "PlistDiagnostics"; |
57 | } |
58 | |
59 | PathGenerationScheme getGenerationScheme() const override { |
60 | return Extensive; |
61 | } |
62 | bool supportsLogicalOpControlFlow() const override { return true; } |
63 | bool supportsCrossFileDiagnostics() const override { |
64 | return SupportsCrossFileDiagnostics; |
65 | } |
66 | }; |
67 | } |
68 | |
69 | namespace { |
70 | |
71 | |
72 | class PlistPrinter { |
73 | const FIDMap& FM; |
74 | AnalyzerOptions &AnOpts; |
75 | const Preprocessor &PP; |
76 | llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces; |
77 | |
78 | public: |
79 | PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts, |
80 | const Preprocessor &PP) |
81 | : FM(FM), AnOpts(AnOpts), PP(PP) { |
82 | } |
83 | |
84 | void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) { |
85 | ReportPiece(o, P, 4, 0, true); |
86 | |
87 | |
88 | (void)AnOpts; |
89 | } |
90 | |
91 | |
92 | |
93 | |
94 | |
95 | |
96 | |
97 | void ReportMacroExpansions(raw_ostream &o, unsigned indent); |
98 | |
99 | private: |
100 | void ReportPiece(raw_ostream &o, const PathDiagnosticPiece &P, |
101 | unsigned indent, unsigned depth, bool includeControlFlow, |
102 | bool isKeyEvent = false) { |
103 | switch (P.getKind()) { |
104 | case PathDiagnosticPiece::ControlFlow: |
105 | if (includeControlFlow) |
106 | ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), indent); |
107 | break; |
108 | case PathDiagnosticPiece::Call: |
109 | ReportCall(o, cast<PathDiagnosticCallPiece>(P), indent, |
110 | depth); |
111 | break; |
112 | case PathDiagnosticPiece::Event: |
113 | ReportEvent(o, cast<PathDiagnosticEventPiece>(P), indent, depth, |
114 | isKeyEvent); |
115 | break; |
116 | case PathDiagnosticPiece::Macro: |
117 | ReportMacroSubPieces(o, cast<PathDiagnosticMacroPiece>(P), indent, |
118 | depth); |
119 | break; |
120 | case PathDiagnosticPiece::Note: |
121 | ReportNote(o, cast<PathDiagnosticNotePiece>(P), indent); |
122 | break; |
123 | } |
124 | } |
125 | |
126 | void EmitRanges(raw_ostream &o, const ArrayRef<SourceRange> Ranges, |
127 | unsigned indent); |
128 | void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent); |
129 | |
130 | void ReportControlFlow(raw_ostream &o, |
131 | const PathDiagnosticControlFlowPiece& P, |
132 | unsigned indent); |
133 | void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, |
134 | unsigned indent, unsigned depth, bool isKeyEvent = false); |
135 | void ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, |
136 | unsigned indent, unsigned depth); |
137 | void ReportMacroSubPieces(raw_ostream &o, const PathDiagnosticMacroPiece& P, |
138 | unsigned indent, unsigned depth); |
139 | void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, |
140 | unsigned indent); |
141 | }; |
142 | |
143 | } |
144 | |
145 | namespace { |
146 | |
147 | struct ExpansionInfo { |
148 | std::string MacroName; |
149 | std::string Expansion; |
150 | ExpansionInfo(std::string N, std::string E) |
151 | : MacroName(std::move(N)), Expansion(std::move(E)) {} |
152 | }; |
153 | |
154 | } |
155 | |
156 | static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, |
157 | AnalyzerOptions &AnOpts, |
158 | const Preprocessor &PP, |
159 | const PathPieces &Path); |
160 | |
161 | |
162 | |
163 | void printCoverage(const PathDiagnostic *D, |
164 | unsigned InputIndentLevel, |
165 | SmallVectorImpl<FileID> &Fids, |
166 | FIDMap &FM, |
167 | llvm::raw_fd_ostream &o); |
168 | |
169 | static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, |
170 | const Preprocessor &PP); |
171 | |
172 | |
173 | |
174 | |
175 | |
176 | void PlistPrinter::EmitRanges(raw_ostream &o, |
177 | const ArrayRef<SourceRange> Ranges, |
178 | unsigned indent) { |
179 | |
180 | if (Ranges.empty()) |
181 | return; |
182 | |
183 | Indent(o, indent) << "<key>ranges</key>\n"; |
184 | Indent(o, indent) << "<array>\n"; |
185 | ++indent; |
186 | |
187 | const SourceManager &SM = PP.getSourceManager(); |
188 | const LangOptions &LangOpts = PP.getLangOpts(); |
189 | |
190 | for (auto &R : Ranges) |
191 | EmitRange(o, SM, |
192 | Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts), |
193 | FM, indent + 1); |
194 | --indent; |
195 | Indent(o, indent) << "</array>\n"; |
196 | } |
197 | |
198 | void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message, |
199 | unsigned indent) { |
200 | |
201 | assert(!Message.empty()); |
202 | Indent(o, indent) << "<key>extended_message</key>\n"; |
203 | Indent(o, indent); |
204 | EmitString(o, Message) << '\n'; |
205 | |
206 | |
207 | |
208 | Indent(o, indent) << "<key>message</key>\n"; |
209 | Indent(o, indent); |
210 | EmitString(o, Message) << '\n'; |
211 | } |
212 | |
213 | void PlistPrinter::ReportControlFlow(raw_ostream &o, |
214 | const PathDiagnosticControlFlowPiece& P, |
215 | unsigned indent) { |
216 | |
217 | const SourceManager &SM = PP.getSourceManager(); |
218 | const LangOptions &LangOpts = PP.getLangOpts(); |
219 | |
220 | Indent(o, indent) << "<dict>\n"; |
221 | ++indent; |
222 | |
223 | Indent(o, indent) << "<key>kind</key><string>control</string>\n"; |
224 | |
225 | |
226 | Indent(o, indent) << "<key>edges</key>\n"; |
227 | ++indent; |
228 | Indent(o, indent) << "<array>\n"; |
229 | ++indent; |
230 | for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); |
231 | I!=E; ++I) { |
232 | Indent(o, indent) << "<dict>\n"; |
233 | ++indent; |
234 | |
235 | |
236 | |
237 | |
238 | Indent(o, indent) << "<key>start</key>\n"; |
239 | SourceRange StartEdge( |
240 | SM.getExpansionLoc(I->getStart().asRange().getBegin())); |
241 | EmitRange(o, SM, Lexer::getAsCharRange(StartEdge, SM, LangOpts), FM, |
242 | indent + 1); |
243 | |
244 | Indent(o, indent) << "<key>end</key>\n"; |
245 | SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin())); |
246 | EmitRange(o, SM, Lexer::getAsCharRange(EndEdge, SM, LangOpts), FM, |
247 | indent + 1); |
248 | |
249 | --indent; |
250 | Indent(o, indent) << "</dict>\n"; |
251 | } |
252 | --indent; |
253 | Indent(o, indent) << "</array>\n"; |
254 | --indent; |
255 | |
256 | |
257 | const auto &s = P.getString(); |
258 | if (!s.empty()) { |
259 | Indent(o, indent) << "<key>alternate</key>"; |
260 | EmitString(o, s) << '\n'; |
261 | } |
262 | |
263 | --indent; |
264 | Indent(o, indent) << "</dict>\n"; |
265 | } |
266 | |
267 | void PlistPrinter::ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, |
268 | unsigned indent, unsigned depth, |
269 | bool isKeyEvent) { |
270 | |
271 | const SourceManager &SM = PP.getSourceManager(); |
272 | |
273 | Indent(o, indent) << "<dict>\n"; |
274 | ++indent; |
275 | |
276 | Indent(o, indent) << "<key>kind</key><string>event</string>\n"; |
277 | |
278 | if (isKeyEvent) { |
279 | Indent(o, indent) << "<key>key_event</key><true/>\n"; |
280 | } |
281 | |
282 | |
283 | FullSourceLoc L = P.getLocation().asLocation(); |
284 | |
285 | Indent(o, indent) << "<key>location</key>\n"; |
286 | EmitLocation(o, SM, L, FM, indent); |
287 | |
288 | |
289 | ArrayRef<SourceRange> Ranges = P.getRanges(); |
290 | EmitRanges(o, Ranges, indent); |
291 | |
292 | |
293 | Indent(o, indent) << "<key>depth</key>"; |
294 | EmitInteger(o, depth) << '\n'; |
295 | |
296 | |
297 | EmitMessage(o, P.getString(), indent); |
298 | |
299 | |
300 | --indent; |
301 | Indent(o, indent); o << "</dict>\n"; |
302 | } |
303 | |
304 | void PlistPrinter::ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, |
305 | unsigned indent, |
306 | unsigned depth) { |
307 | |
308 | if (auto callEnter = P.getCallEnterEvent()) |
309 | ReportPiece(o, *callEnter, indent, depth, true, |
310 | P.isLastInMainSourceFile()); |
311 | |
312 | |
313 | ++depth; |
314 | |
315 | if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent()) |
316 | ReportPiece(o, *callEnterWithinCaller, indent, depth, |
317 | true); |
318 | |
319 | for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) |
320 | ReportPiece(o, **I, indent, depth, true); |
321 | |
322 | --depth; |
323 | |
324 | if (auto callExit = P.getCallExitEvent()) |
325 | ReportPiece(o, *callExit, indent, depth, true); |
326 | } |
327 | |
328 | void PlistPrinter::ReportMacroSubPieces(raw_ostream &o, |
329 | const PathDiagnosticMacroPiece& P, |
330 | unsigned indent, unsigned depth) { |
331 | MacroPieces.push_back(&P); |
332 | |
333 | for (PathPieces::const_iterator I = P.subPieces.begin(), |
334 | E = P.subPieces.end(); |
335 | I != E; ++I) { |
336 | ReportPiece(o, **I, indent, depth, false); |
337 | } |
338 | } |
339 | |
340 | void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) { |
341 | |
342 | for (const PathDiagnosticMacroPiece *P : MacroPieces) { |
343 | const SourceManager &SM = PP.getSourceManager(); |
344 | ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP); |
345 | |
346 | Indent(o, indent) << "<dict>\n"; |
347 | ++indent; |
348 | |
349 | |
350 | FullSourceLoc L = P->getLocation().asLocation(); |
351 | |
352 | Indent(o, indent) << "<key>location</key>\n"; |
353 | EmitLocation(o, SM, L, FM, indent); |
354 | |
355 | |
356 | ArrayRef<SourceRange> Ranges = P->getRanges(); |
357 | EmitRanges(o, Ranges, indent); |
358 | |
359 | |
360 | Indent(o, indent) << "<key>name</key>"; |
361 | EmitString(o, EI.MacroName) << '\n'; |
362 | |
363 | |
364 | Indent(o, indent) << "<key>expansion</key>"; |
365 | EmitString(o, EI.Expansion) << '\n'; |
366 | |
367 | |
368 | --indent; |
369 | Indent(o, indent); |
370 | o << "</dict>\n"; |
371 | } |
372 | } |
373 | |
374 | void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, |
375 | unsigned indent) { |
376 | |
377 | const SourceManager &SM = PP.getSourceManager(); |
378 | |
379 | Indent(o, indent) << "<dict>\n"; |
380 | ++indent; |
381 | |
382 | |
383 | FullSourceLoc L = P.getLocation().asLocation(); |
384 | |
385 | Indent(o, indent) << "<key>location</key>\n"; |
386 | EmitLocation(o, SM, L, FM, indent); |
387 | |
388 | |
389 | ArrayRef<SourceRange> Ranges = P.getRanges(); |
390 | EmitRanges(o, Ranges, indent); |
391 | |
392 | |
393 | EmitMessage(o, P.getString(), indent); |
394 | |
395 | |
396 | --indent; |
397 | Indent(o, indent); o << "</dict>\n"; |
398 | } |
399 | |
400 | |
401 | |
402 | |
403 | |
404 | |
405 | |
406 | static void printCoverage(const PathDiagnostic *D, |
407 | unsigned InputIndentLevel, |
408 | SmallVectorImpl<FileID> &Fids, |
409 | FIDMap &FM, |
410 | llvm::raw_fd_ostream &o) { |
411 | unsigned IndentLevel = InputIndentLevel; |
412 | |
413 | Indent(o, IndentLevel) << "<key>ExecutedLines</key>\n"; |
414 | Indent(o, IndentLevel) << "<dict>\n"; |
415 | IndentLevel++; |
416 | |
417 | |
418 | const FilesToLineNumsMap &ExecutedLines = D->getExecutedLines(); |
419 | for (auto I = ExecutedLines.begin(), E = ExecutedLines.end(); I != E; ++I) { |
420 | unsigned FileKey = AddFID(FM, Fids, I->first); |
421 | Indent(o, IndentLevel) << "<key>" << FileKey << "</key>\n"; |
422 | Indent(o, IndentLevel) << "<array>\n"; |
423 | IndentLevel++; |
424 | for (unsigned LineNo : I->second) { |
425 | Indent(o, IndentLevel); |
426 | EmitInteger(o, LineNo) << "\n"; |
427 | } |
428 | IndentLevel--; |
429 | Indent(o, IndentLevel) << "</array>\n"; |
430 | } |
431 | IndentLevel--; |
432 | Indent(o, IndentLevel) << "</dict>\n"; |
433 | |
434 | assert(IndentLevel == InputIndentLevel); |
435 | } |
436 | |
437 | static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, |
438 | AnalyzerOptions &AnOpts, |
439 | const Preprocessor &PP, |
440 | const PathPieces &Path) { |
441 | PlistPrinter Printer(FM, AnOpts, PP); |
442 | (0) . __assert_fail ("std..is_partitioned( Path.begin(), Path.end(), [](const std..shared_ptr &E) { return E->getKind() == PathDiagnosticPiece..Note; }) && \"PathDiagnostic is not partitioned so that notes precede the rest\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 446, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(std::is_partitioned( |
443 | (0) . __assert_fail ("std..is_partitioned( Path.begin(), Path.end(), [](const std..shared_ptr &E) { return E->getKind() == PathDiagnosticPiece..Note; }) && \"PathDiagnostic is not partitioned so that notes precede the rest\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 446, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true"> Path.begin(), Path.end(), |
444 | (0) . __assert_fail ("std..is_partitioned( Path.begin(), Path.end(), [](const std..shared_ptr &E) { return E->getKind() == PathDiagnosticPiece..Note; }) && \"PathDiagnostic is not partitioned so that notes precede the rest\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 446, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true"> [](const std::shared_ptr<PathDiagnosticPiece> &E) |
445 | (0) . __assert_fail ("std..is_partitioned( Path.begin(), Path.end(), [](const std..shared_ptr &E) { return E->getKind() == PathDiagnosticPiece..Note; }) && \"PathDiagnostic is not partitioned so that notes precede the rest\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 446, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true"> { return E->getKind() == PathDiagnosticPiece::Note; }) && |
446 | (0) . __assert_fail ("std..is_partitioned( Path.begin(), Path.end(), [](const std..shared_ptr &E) { return E->getKind() == PathDiagnosticPiece..Note; }) && \"PathDiagnostic is not partitioned so that notes precede the rest\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 446, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true"> "PathDiagnostic is not partitioned so that notes precede the rest"); |
447 | |
448 | PathPieces::const_iterator FirstNonNote = std::partition_point( |
449 | Path.begin(), Path.end(), |
450 | [](const std::shared_ptr<PathDiagnosticPiece> &E) |
451 | { return E->getKind() == PathDiagnosticPiece::Note; }); |
452 | |
453 | PathPieces::const_iterator I = Path.begin(); |
454 | |
455 | if (FirstNonNote != Path.begin()) { |
456 | o << " <key>notes</key>\n" |
457 | " <array>\n"; |
458 | |
459 | for (; I != FirstNonNote; ++I) |
460 | Printer.ReportDiag(o, **I); |
461 | |
462 | o << " </array>\n"; |
463 | } |
464 | |
465 | o << " <key>path</key>\n"; |
466 | |
467 | o << " <array>\n"; |
468 | |
469 | for (PathPieces::const_iterator E = Path.end(); I != E; ++I) |
470 | Printer.ReportDiag(o, **I); |
471 | |
472 | o << " </array>\n"; |
473 | |
474 | if (!AnOpts.ShouldDisplayMacroExpansions) |
475 | return; |
476 | |
477 | o << " <key>macro_expansions</key>\n" |
478 | " <array>\n"; |
479 | Printer.ReportMacroExpansions(o, 4); |
480 | o << " </array>\n"; |
481 | } |
482 | |
483 | |
484 | |
485 | |
486 | |
487 | PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, |
488 | const std::string& output, |
489 | const Preprocessor &PP, |
490 | bool supportsMultipleFiles) |
491 | : OutputFile(output), PP(PP), AnOpts(AnalyzerOpts), |
492 | SupportsCrossFileDiagnostics(supportsMultipleFiles) {} |
493 | |
494 | void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, |
495 | PathDiagnosticConsumers &C, |
496 | const std::string& s, |
497 | const Preprocessor &PP) { |
498 | C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, |
499 | false)); |
500 | } |
501 | |
502 | void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, |
503 | PathDiagnosticConsumers &C, |
504 | const std::string &s, |
505 | const Preprocessor &PP) { |
506 | C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, |
507 | true)); |
508 | } |
509 | void PlistDiagnostics::FlushDiagnosticsImpl( |
510 | std::vector<const PathDiagnostic *> &Diags, |
511 | FilesMade *filesMade) { |
512 | |
513 | |
514 | FIDMap FM; |
515 | SmallVector<FileID, 10> Fids; |
516 | const SourceManager& SM = PP.getSourceManager(); |
517 | const LangOptions &LangOpts = PP.getLangOpts(); |
518 | |
519 | auto AddPieceFID = [&FM, &Fids, &SM](const PathDiagnosticPiece &Piece) { |
520 | AddFID(FM, Fids, SM, Piece.getLocation().asLocation()); |
521 | ArrayRef<SourceRange> Ranges = Piece.getRanges(); |
522 | for (const SourceRange &Range : Ranges) { |
523 | AddFID(FM, Fids, SM, Range.getBegin()); |
524 | AddFID(FM, Fids, SM, Range.getEnd()); |
525 | } |
526 | }; |
527 | |
528 | for (const PathDiagnostic *D : Diags) { |
529 | |
530 | SmallVector<const PathPieces *, 5> WorkList; |
531 | WorkList.push_back(&D->path); |
532 | |
533 | while (!WorkList.empty()) { |
534 | const PathPieces &Path = *WorkList.pop_back_val(); |
535 | |
536 | for (const auto &Iter : Path) { |
537 | const PathDiagnosticPiece &Piece = *Iter; |
538 | AddPieceFID(Piece); |
539 | |
540 | if (const PathDiagnosticCallPiece *Call = |
541 | dyn_cast<PathDiagnosticCallPiece>(&Piece)) { |
542 | if (auto CallEnterWithin = Call->getCallEnterWithinCallerEvent()) |
543 | AddPieceFID(*CallEnterWithin); |
544 | |
545 | if (auto CallEnterEvent = Call->getCallEnterEvent()) |
546 | AddPieceFID(*CallEnterEvent); |
547 | |
548 | WorkList.push_back(&Call->path); |
549 | } else if (const PathDiagnosticMacroPiece *Macro = |
550 | dyn_cast<PathDiagnosticMacroPiece>(&Piece)) { |
551 | WorkList.push_back(&Macro->subPieces); |
552 | } |
553 | } |
554 | } |
555 | } |
556 | |
557 | |
558 | std::error_code EC; |
559 | llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text); |
560 | if (EC) { |
561 | llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; |
562 | return; |
563 | } |
564 | |
565 | EmitPlistHeader(o); |
566 | |
567 | |
568 | |
569 | |
570 | |
571 | o << "<dict>\n" << |
572 | " <key>clang_version</key>\n"; |
573 | EmitString(o, getClangFullVersion()) << '\n'; |
574 | o << " <key>diagnostics</key>\n" |
575 | " <array>\n"; |
576 | |
577 | for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), |
578 | DE = Diags.end(); DI!=DE; ++DI) { |
579 | |
580 | o << " <dict>\n"; |
581 | |
582 | const PathDiagnostic *D = *DI; |
583 | printBugPath(o, FM, AnOpts, PP, D->path); |
584 | |
585 | |
586 | o << " <key>description</key>"; |
587 | EmitString(o, D->getShortDescription()) << '\n'; |
588 | o << " <key>category</key>"; |
589 | EmitString(o, D->getCategory()) << '\n'; |
590 | o << " <key>type</key>"; |
591 | EmitString(o, D->getBugType()) << '\n'; |
592 | o << " <key>check_name</key>"; |
593 | EmitString(o, D->getCheckName()) << '\n'; |
594 | |
595 | o << " <!-- This hash is experimental and going to change! -->\n"; |
596 | o << " <key>issue_hash_content_of_line_in_context</key>"; |
597 | PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); |
598 | FullSourceLoc L(SM.getExpansionLoc(UPDLoc.isValid() |
599 | ? UPDLoc.asLocation() |
600 | : D->getLocation().asLocation()), |
601 | SM); |
602 | const Decl *DeclWithIssue = D->getDeclWithIssue(); |
603 | EmitString(o, GetIssueHash(SM, L, D->getCheckName(), D->getBugType(), |
604 | DeclWithIssue, LangOpts)) |
605 | << '\n'; |
606 | |
607 | |
608 | |
609 | if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { |
610 | |
611 | if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { |
612 | StringRef declKind; |
613 | switch (ND->getKind()) { |
614 | case Decl::CXXRecord: |
615 | declKind = "C++ class"; |
616 | break; |
617 | case Decl::CXXMethod: |
618 | declKind = "C++ method"; |
619 | break; |
620 | case Decl::ObjCMethod: |
621 | declKind = "Objective-C method"; |
622 | break; |
623 | case Decl::Function: |
624 | declKind = "function"; |
625 | break; |
626 | default: |
627 | break; |
628 | } |
629 | if (!declKind.empty()) { |
630 | const std::string &declName = ND->getDeclName().getAsString(); |
631 | o << " <key>issue_context_kind</key>"; |
632 | EmitString(o, declKind) << '\n'; |
633 | o << " <key>issue_context</key>"; |
634 | EmitString(o, declName) << '\n'; |
635 | } |
636 | |
637 | |
638 | |
639 | if (const Stmt *Body = DeclWithIssue->getBody()) { |
640 | |
641 | |
642 | |
643 | |
644 | |
645 | |
646 | if (UPDLoc.isValid()) { |
647 | FullSourceLoc UFunL( |
648 | SM.getExpansionLoc( |
649 | D->getUniqueingDecl()->getBody()->getBeginLoc()), |
650 | SM); |
651 | o << " <key>issue_hash_function_offset</key><string>" |
652 | << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber() |
653 | << "</string>\n"; |
654 | |
655 | |
656 | } else { |
657 | FullSourceLoc FunL(SM.getExpansionLoc(Body->getBeginLoc()), SM); |
658 | o << " <key>issue_hash_function_offset</key><string>" |
659 | << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() |
660 | << "</string>\n"; |
661 | } |
662 | |
663 | } |
664 | } |
665 | } |
666 | |
667 | |
668 | o << " <key>location</key>\n"; |
669 | EmitLocation(o, SM, D->getLocation().asLocation(), FM, 2); |
670 | |
671 | |
672 | if (!filesMade->empty()) { |
673 | StringRef lastName; |
674 | PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); |
675 | if (files) { |
676 | for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), |
677 | CE = files->end(); CI != CE; ++CI) { |
678 | StringRef newName = CI->first; |
679 | if (newName != lastName) { |
680 | if (!lastName.empty()) { |
681 | o << " </array>\n"; |
682 | } |
683 | lastName = newName; |
684 | o << " <key>" << lastName << "_files</key>\n"; |
685 | o << " <array>\n"; |
686 | } |
687 | o << " <string>" << CI->second << "</string>\n"; |
688 | } |
689 | o << " </array>\n"; |
690 | } |
691 | } |
692 | |
693 | printCoverage(D, , Fids, FM, o); |
694 | |
695 | |
696 | o << " </dict>\n"; |
697 | } |
698 | |
699 | o << " </array>\n"; |
700 | |
701 | o << " <key>files</key>\n" |
702 | " <array>\n"; |
703 | for (FileID FID : Fids) |
704 | EmitString(o << " ", SM.getFileEntryForID(FID)->getName()) << '\n'; |
705 | o << " </array>\n"; |
706 | |
707 | if (llvm::AreStatisticsEnabled() && AnOpts.ShouldSerializeStats) { |
708 | o << " <key>statistics</key>\n"; |
709 | std::string stats; |
710 | llvm::raw_string_ostream os(stats); |
711 | llvm::PrintStatisticsJSON(os); |
712 | os.flush(); |
713 | EmitString(o, html::EscapeText(stats)) << '\n'; |
714 | } |
715 | |
716 | |
717 | o << "</dict>\n</plist>"; |
718 | } |
719 | |
720 | |
721 | |
722 | |
723 | |
724 | namespace { |
725 | |
726 | using ExpArgTokens = llvm::SmallVector<Token, 2>; |
727 | |
728 | |
729 | |
730 | class MacroArgMap : public std::map<const IdentifierInfo *, ExpArgTokens> { |
731 | public: |
732 | void expandFromPrevMacro(const MacroArgMap &Super); |
733 | }; |
734 | |
735 | struct MacroNameAndArgs { |
736 | std::string Name; |
737 | const MacroInfo *MI = nullptr; |
738 | MacroArgMap Args; |
739 | |
740 | MacroNameAndArgs(std::string N, const MacroInfo *MI, MacroArgMap M) |
741 | : Name(std::move(N)), MI(MI), Args(std::move(M)) {} |
742 | }; |
743 | |
744 | class TokenPrinter { |
745 | llvm::raw_ostream &OS; |
746 | const Preprocessor &PP; |
747 | |
748 | Token PrevTok, PrevPrevTok; |
749 | TokenConcatenation ConcatInfo; |
750 | |
751 | public: |
752 | TokenPrinter(llvm::raw_ostream &OS, const Preprocessor &PP) |
753 | : OS(OS), PP(PP), ConcatInfo(PP) { |
754 | PrevTok.setKind(tok::unknown); |
755 | PrevPrevTok.setKind(tok::unknown); |
756 | } |
757 | |
758 | void printToken(const Token &Tok); |
759 | }; |
760 | |
761 | } |
762 | |
763 | |
764 | |
765 | |
766 | |
767 | |
768 | |
769 | |
770 | |
771 | |
772 | |
773 | |
774 | |
775 | |
776 | |
777 | |
778 | |
779 | |
780 | |
781 | |
782 | |
783 | |
784 | |
785 | |
786 | |
787 | |
788 | static std::string getMacroNameAndPrintExpansion( |
789 | TokenPrinter &Printer, |
790 | SourceLocation MacroLoc, |
791 | const Preprocessor &PP, |
792 | const MacroArgMap &PrevArgs, |
793 | llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens); |
794 | |
795 | |
796 | |
797 | |
798 | |
799 | |
800 | |
801 | |
802 | |
803 | |
804 | |
805 | |
806 | |
807 | |
808 | |
809 | |
810 | |
811 | |
812 | |
813 | |
814 | |
815 | |
816 | |
817 | static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, |
818 | const Preprocessor &PP); |
819 | |
820 | |
821 | static MacroInfo::tokens_iterator getMatchingRParen( |
822 | MacroInfo::tokens_iterator It, |
823 | MacroInfo::tokens_iterator End); |
824 | |
825 | |
826 | |
827 | static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP, |
828 | const SourceManager &SM, |
829 | const IdentifierInfo *II, |
830 | SourceLocation Loc); |
831 | |
832 | |
833 | |
834 | |
835 | |
836 | static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, |
837 | const Preprocessor &PP) { |
838 | |
839 | llvm::SmallString<200> ExpansionBuf; |
840 | llvm::raw_svector_ostream OS(ExpansionBuf); |
841 | TokenPrinter Printer(OS, PP); |
842 | llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens; |
843 | |
844 | std::string MacroName = |
845 | getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}, |
846 | AlreadyProcessedTokens); |
847 | return { MacroName, OS.str() }; |
848 | } |
849 | |
850 | static std::string getMacroNameAndPrintExpansion( |
851 | TokenPrinter &Printer, |
852 | SourceLocation MacroLoc, |
853 | const Preprocessor &PP, |
854 | const MacroArgMap &PrevArgs, |
855 | llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens) { |
856 | |
857 | const SourceManager &SM = PP.getSourceManager(); |
858 | |
859 | MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP); |
860 | IdentifierInfo* IDInfo = PP.getIdentifierInfo(Info.Name); |
861 | |
862 | |
863 | |
864 | |
865 | |
866 | |
867 | |
868 | if (AlreadyProcessedTokens.find(IDInfo) != AlreadyProcessedTokens.end()) |
869 | return Info.Name; |
870 | AlreadyProcessedTokens.insert(IDInfo); |
871 | |
872 | if (!Info.MI) |
873 | return Info.Name; |
874 | |
875 | |
876 | Info.Args.expandFromPrevMacro(PrevArgs); |
877 | |
878 | |
879 | for (auto It = Info.MI->tokens_begin(), E = Info.MI->tokens_end(); It != E; |
880 | ++It) { |
881 | Token T = *It; |
882 | |
883 | |
884 | if (T.isNot(tok::identifier)) { |
885 | Printer.printToken(T); |
886 | continue; |
887 | } |
888 | |
889 | const auto *II = T.getIdentifierInfo(); |
890 | (0) . __assert_fail ("II && \"This token is an identifier but has no IdentifierInfo!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 891, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(II && |
891 | (0) . __assert_fail ("II && \"This token is an identifier but has no IdentifierInfo!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 891, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true"> "This token is an identifier but has no IdentifierInfo!"); |
892 | |
893 | |
894 | |
895 | if (getMacroInfoForLocation(PP, SM, II, T.getLocation())) { |
896 | getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args, |
897 | AlreadyProcessedTokens); |
898 | |
899 | |
900 | |
901 | |
902 | auto N = std::next(It); |
903 | if (N != E && N->is(tok::l_paren)) |
904 | It = getMatchingRParen(++It, E); |
905 | continue; |
906 | } |
907 | |
908 | |
909 | auto ArgMapIt = Info.Args.find(II); |
910 | if (ArgMapIt != Info.Args.end()) { |
911 | for (MacroInfo::tokens_iterator ArgIt = ArgMapIt->second.begin(), |
912 | ArgEnd = ArgMapIt->second.end(); |
913 | ArgIt != ArgEnd; ++ArgIt) { |
914 | |
915 | |
916 | |
917 | const auto *ArgII = ArgIt->getIdentifierInfo(); |
918 | if (!ArgII) { |
919 | Printer.printToken(*ArgIt); |
920 | continue; |
921 | } |
922 | |
923 | const auto *MI = PP.getMacroInfo(ArgII); |
924 | if (!MI) { |
925 | Printer.printToken(*ArgIt); |
926 | continue; |
927 | } |
928 | |
929 | getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP, |
930 | Info.Args, AlreadyProcessedTokens); |
931 | |
932 | |
933 | |
934 | |
935 | |
936 | |
937 | |
938 | |
939 | auto N = std::next(ArgIt); |
940 | if (N != ArgEnd && N->is(tok::l_paren)) |
941 | ArgIt = getMatchingRParen(++ArgIt, ArgEnd); |
942 | } |
943 | continue; |
944 | } |
945 | |
946 | |
947 | |
948 | Printer.printToken(T); |
949 | } |
950 | |
951 | AlreadyProcessedTokens.erase(IDInfo); |
952 | |
953 | return Info.Name; |
954 | } |
955 | |
956 | static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, |
957 | const Preprocessor &PP) { |
958 | |
959 | const SourceManager &SM = PP.getSourceManager(); |
960 | const LangOptions &LangOpts = PP.getLangOpts(); |
961 | |
962 | |
963 | |
964 | std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ExpanLoc); |
965 | const llvm::MemoryBuffer *MB = SM.getBuffer(LocInfo.first); |
966 | const char *MacroNameTokenPos = MB->getBufferStart() + LocInfo.second; |
967 | |
968 | Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts, |
969 | MB->getBufferStart(), MacroNameTokenPos, MB->getBufferEnd()); |
970 | |
971 | |
972 | Token TheTok; |
973 | RawLexer.LexFromRawLexer(TheTok); |
974 | |
975 | std::string MacroName = PP.getSpelling(TheTok); |
976 | |
977 | const auto *II = PP.getIdentifierInfo(MacroName); |
978 | (0) . __assert_fail ("II && \"Failed to acquire the IndetifierInfo for the macro!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 978, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(II && "Failed to acquire the IndetifierInfo for the macro!"); |
979 | |
980 | const MacroInfo *MI = getMacroInfoForLocation(PP, SM, II, ExpanLoc); |
981 | |
982 | |
983 | |
984 | |
985 | |
986 | |
987 | if (!MI) |
988 | return { MacroName, MI, {} }; |
989 | |
990 | |
991 | |
992 | |
993 | |
994 | |
995 | |
996 | |
997 | ArrayRef<const IdentifierInfo *> MacroArgs = MI->params(); |
998 | if (MacroArgs.empty()) |
999 | return { MacroName, MI, {} }; |
1000 | |
1001 | RawLexer.LexFromRawLexer(TheTok); |
1002 | |
1003 | |
1004 | |
1005 | |
1006 | |
1007 | |
1008 | |
1009 | |
1010 | if (TheTok.isNot(tok::l_paren)) |
1011 | return { MacroName, MI, {} }; |
1012 | |
1013 | MacroArgMap Args; |
1014 | |
1015 | |
1016 | |
1017 | |
1018 | |
1019 | |
1020 | |
1021 | |
1022 | |
1023 | |
1024 | int ParenthesesDepth = 1; |
1025 | |
1026 | |
1027 | |
1028 | const IdentifierInfo *__VA_ARGS__II = PP.getIdentifierInfo("__VA_ARGS__"); |
1029 | |
1030 | for (const IdentifierInfo *UnexpArgII : MacroArgs) { |
1031 | MacroArgMap::mapped_type ExpandedArgTokens; |
1032 | |
1033 | |
1034 | |
1035 | |
1036 | |
1037 | |
1038 | |
1039 | |
1040 | |
1041 | |
1042 | if (ParenthesesDepth != 0) { |
1043 | |
1044 | |
1045 | RawLexer.LexFromRawLexer(TheTok); |
1046 | |
1047 | while (!(ParenthesesDepth == 1 && |
1048 | (UnexpArgII == __VA_ARGS__II ? false : TheTok.is(tok::comma)))) { |
1049 | (0) . __assert_fail ("TheTok.isNot(tok..eof) && \"EOF encountered while looking for expanded macro args!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 1050, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(TheTok.isNot(tok::eof) && |
1050 | (0) . __assert_fail ("TheTok.isNot(tok..eof) && \"EOF encountered while looking for expanded macro args!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 1050, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true"> "EOF encountered while looking for expanded macro args!"); |
1051 | |
1052 | if (TheTok.is(tok::l_paren)) |
1053 | ++ParenthesesDepth; |
1054 | |
1055 | if (TheTok.is(tok::r_paren)) |
1056 | --ParenthesesDepth; |
1057 | |
1058 | if (ParenthesesDepth == 0) |
1059 | break; |
1060 | |
1061 | if (TheTok.is(tok::raw_identifier)) |
1062 | PP.LookUpIdentifierInfo(TheTok); |
1063 | |
1064 | ExpandedArgTokens.push_back(TheTok); |
1065 | RawLexer.LexFromRawLexer(TheTok); |
1066 | } |
1067 | } else { |
1068 | assert(UnexpArgII == __VA_ARGS__II); |
1069 | } |
1070 | |
1071 | Args.emplace(UnexpArgII, std::move(ExpandedArgTokens)); |
1072 | } |
1073 | |
1074 | (0) . __assert_fail ("TheTok.is(tok..r_paren) && \"Expanded macro argument acquisition failed! After the end of the loop\" \" this token should be ')'!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 1076, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(TheTok.is(tok::r_paren) && |
1075 | (0) . __assert_fail ("TheTok.is(tok..r_paren) && \"Expanded macro argument acquisition failed! After the end of the loop\" \" this token should be ')'!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 1076, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true"> "Expanded macro argument acquisition failed! After the end of the loop" |
1076 | (0) . __assert_fail ("TheTok.is(tok..r_paren) && \"Expanded macro argument acquisition failed! After the end of the loop\" \" this token should be ')'!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 1076, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true"> " this token should be ')'!"); |
1077 | |
1078 | return { MacroName, MI, Args }; |
1079 | } |
1080 | |
1081 | static MacroInfo::tokens_iterator getMatchingRParen( |
1082 | MacroInfo::tokens_iterator It, |
1083 | MacroInfo::tokens_iterator End) { |
1084 | |
1085 | (0) . __assert_fail ("It->is(tok..l_paren) && \"This token should be '('!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 1085, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(It->is(tok::l_paren) && "This token should be '('!"); |
1086 | |
1087 | |
1088 | int ParenthesesDepth = 1; |
1089 | while (ParenthesesDepth != 0) { |
1090 | ++It; |
1091 | |
1092 | (0) . __assert_fail ("It->isNot(tok..eof) && \"Encountered EOF while attempting to skip macro arguments!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 1093, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(It->isNot(tok::eof) && |
1093 | (0) . __assert_fail ("It->isNot(tok..eof) && \"Encountered EOF while attempting to skip macro arguments!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 1093, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true"> "Encountered EOF while attempting to skip macro arguments!"); |
1094 | (0) . __assert_fail ("It != End && \"End of the macro definition reached before finding ')'!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 1095, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(It != End && |
1095 | (0) . __assert_fail ("It != End && \"End of the macro definition reached before finding ')'!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp", 1095, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true"> "End of the macro definition reached before finding ')'!"); |
1096 | |
1097 | if (It->is(tok::l_paren)) |
1098 | ++ParenthesesDepth; |
1099 | |
1100 | if (It->is(tok::r_paren)) |
1101 | --ParenthesesDepth; |
1102 | } |
1103 | return It; |
1104 | } |
1105 | |
1106 | static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP, |
1107 | const SourceManager &SM, |
1108 | const IdentifierInfo *II, |
1109 | SourceLocation Loc) { |
1110 | |
1111 | const MacroDirective *MD = PP.getLocalMacroDirectiveHistory(II); |
1112 | if (!MD) |
1113 | return nullptr; |
1114 | |
1115 | return MD->findDirectiveAtLoc(Loc, SM).getMacroInfo(); |
1116 | } |
1117 | |
1118 | void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) { |
1119 | |
1120 | for (value_type &Pair : *this) { |
1121 | ExpArgTokens &CurrExpArgTokens = Pair.second; |
1122 | |
1123 | |
1124 | auto It = CurrExpArgTokens.begin(); |
1125 | while (It != CurrExpArgTokens.end()) { |
1126 | if (It->isNot(tok::identifier)) { |
1127 | ++It; |
1128 | continue; |
1129 | } |
1130 | |
1131 | const auto *II = It->getIdentifierInfo(); |
1132 | assert(II); |
1133 | |
1134 | |
1135 | if (!Super.count(II)) { |
1136 | ++It; |
1137 | continue; |
1138 | } |
1139 | |
1140 | const ExpArgTokens &SuperExpArgTokens = Super.at(II); |
1141 | |
1142 | It = CurrExpArgTokens.insert( |
1143 | It, SuperExpArgTokens.begin(), SuperExpArgTokens.end()); |
1144 | std::advance(It, SuperExpArgTokens.size()); |
1145 | It = CurrExpArgTokens.erase(It); |
1146 | } |
1147 | } |
1148 | } |
1149 | |
1150 | void TokenPrinter::printToken(const Token &Tok) { |
1151 | |
1152 | if (PrevTok.isNot(tok::unknown)) { |
1153 | |
1154 | |
1155 | if(Tok.hasLeadingSpace() || ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, |
1156 | Tok)) { |
1157 | |
1158 | if (PrevTok.isNot(tok::hashhash) && Tok.isNot(tok::hashhash)) { |
1159 | OS << ' '; |
1160 | } |
1161 | } |
1162 | } |
1163 | |
1164 | if (!Tok.isOneOf(tok::hash, tok::hashhash)) { |
1165 | if (PrevTok.is(tok::hash)) |
1166 | OS << '\"' << PP.getSpelling(Tok) << '\"'; |
1167 | else |
1168 | OS << PP.getSpelling(Tok); |
1169 | } |
1170 | |
1171 | PrevPrevTok = PrevTok; |
1172 | PrevTok = Tok; |
1173 | } |
1174 | |