/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Index/CommentToXML.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- CommentToXML.cpp - Convert comments to XML representation --------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | |
9 | | #include "clang/Index/CommentToXML.h" |
10 | | #include "clang/AST/ASTContext.h" |
11 | | #include "clang/AST/Attr.h" |
12 | | #include "clang/AST/Comment.h" |
13 | | #include "clang/AST/CommentVisitor.h" |
14 | | #include "clang/Basic/FileManager.h" |
15 | | #include "clang/Basic/SourceManager.h" |
16 | | #include "clang/Format/Format.h" |
17 | | #include "clang/Index/USRGeneration.h" |
18 | | #include "llvm/ADT/StringExtras.h" |
19 | | #include "llvm/ADT/TinyPtrVector.h" |
20 | | #include "llvm/Support/raw_ostream.h" |
21 | | |
22 | | using namespace clang; |
23 | | using namespace clang::comments; |
24 | | using namespace clang::index; |
25 | | |
26 | | namespace { |
27 | | |
28 | | /// This comparison will sort parameters with valid index by index, then vararg |
29 | | /// parameters, and invalid (unresolved) parameters last. |
30 | | class ParamCommandCommentCompareIndex { |
31 | | public: |
32 | | bool operator()(const ParamCommandComment *LHS, |
33 | 124 | const ParamCommandComment *RHS) const { |
34 | 124 | unsigned LHSIndex = UINT_MAX; |
35 | 124 | unsigned RHSIndex = UINT_MAX; |
36 | | |
37 | 124 | if (LHS->isParamIndexValid()) { |
38 | 66 | if (LHS->isVarArgParam()) |
39 | 14 | LHSIndex = UINT_MAX - 1; |
40 | 52 | else |
41 | 52 | LHSIndex = LHS->getParamIndex(); |
42 | 66 | } |
43 | 124 | if (RHS->isParamIndexValid()) { |
44 | 114 | if (RHS->isVarArgParam()) |
45 | 0 | RHSIndex = UINT_MAX - 1; |
46 | 114 | else |
47 | 114 | RHSIndex = RHS->getParamIndex(); |
48 | 114 | } |
49 | 124 | return LHSIndex < RHSIndex; |
50 | 124 | } |
51 | | }; |
52 | | |
53 | | /// This comparison will sort template parameters in the following order: |
54 | | /// \li real template parameters (depth = 1) in index order; |
55 | | /// \li all other names (depth > 1); |
56 | | /// \li unresolved names. |
57 | | class TParamCommandCommentComparePosition { |
58 | | public: |
59 | | bool operator()(const TParamCommandComment *LHS, |
60 | 118 | const TParamCommandComment *RHS) const { |
61 | | // Sort unresolved names last. |
62 | 118 | if (!LHS->isPositionValid()) |
63 | 8 | return false; |
64 | 110 | if (!RHS->isPositionValid()) |
65 | 18 | return true; |
66 | | |
67 | 92 | if (LHS->getDepth() > 1) |
68 | 42 | return false; |
69 | 50 | if (RHS->getDepth() > 1) |
70 | 6 | return true; |
71 | | |
72 | | // Sort template parameters in index order. |
73 | 44 | if (LHS->getDepth() == 1 && RHS->getDepth() == 1) |
74 | 44 | return LHS->getIndex(0) < RHS->getIndex(0); |
75 | | |
76 | | // Leave all other names in source order. |
77 | 0 | return true; |
78 | 44 | } |
79 | | }; |
80 | | |
81 | | /// Separate parts of a FullComment. |
82 | | struct FullCommentParts { |
83 | | /// Take a full comment apart and initialize members accordingly. |
84 | | FullCommentParts(const FullComment *C, |
85 | | const CommandTraits &Traits); |
86 | | |
87 | | const BlockContentComment *Brief; |
88 | | const BlockContentComment *Headerfile; |
89 | | const ParagraphComment *FirstParagraph; |
90 | | SmallVector<const BlockCommandComment *, 4> Returns; |
91 | | SmallVector<const ParamCommandComment *, 8> Params; |
92 | | SmallVector<const TParamCommandComment *, 4> TParams; |
93 | | llvm::TinyPtrVector<const BlockCommandComment *> Exceptions; |
94 | | SmallVector<const BlockContentComment *, 8> MiscBlocks; |
95 | | }; |
96 | | |
97 | | FullCommentParts::FullCommentParts(const FullComment *C, |
98 | | const CommandTraits &Traits) : |
99 | 2.00k | Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) { |
100 | 2.00k | for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); |
101 | 6.02k | I != E; ++I4.02k ) { |
102 | 4.02k | const Comment *Child = *I; |
103 | 4.02k | if (!Child) |
104 | 0 | continue; |
105 | 4.02k | switch (Child->getCommentKind()) { |
106 | 0 | case Comment::NoCommentKind: |
107 | 0 | continue; |
108 | | |
109 | 2.25k | case Comment::ParagraphCommentKind: { |
110 | 2.25k | const ParagraphComment *PC = cast<ParagraphComment>(Child); |
111 | 2.25k | if (PC->isWhitespace()) |
112 | 1.12k | break; |
113 | 1.12k | if (!FirstParagraph) |
114 | 1.05k | FirstParagraph = PC; |
115 | | |
116 | 1.12k | MiscBlocks.push_back(PC); |
117 | 1.12k | break; |
118 | 2.25k | } |
119 | | |
120 | 1.02k | case Comment::BlockCommandCommentKind: { |
121 | 1.02k | const BlockCommandComment *BCC = cast<BlockCommandComment>(Child); |
122 | 1.02k | const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID()); |
123 | 1.02k | if (!Brief && Info->IsBriefCommand870 ) { |
124 | 592 | Brief = BCC; |
125 | 592 | break; |
126 | 592 | } |
127 | 432 | if (!Headerfile && Info->IsHeaderfileCommand430 ) { |
128 | 12 | Headerfile = BCC; |
129 | 12 | break; |
130 | 12 | } |
131 | 420 | if (Info->IsReturnsCommand) { |
132 | 174 | Returns.push_back(BCC); |
133 | 174 | break; |
134 | 174 | } |
135 | 246 | if (Info->IsThrowsCommand) { |
136 | 32 | Exceptions.push_back(BCC); |
137 | 32 | break; |
138 | 32 | } |
139 | 214 | MiscBlocks.push_back(BCC); |
140 | 214 | break; |
141 | 246 | } |
142 | | |
143 | 410 | case Comment::ParamCommandCommentKind: { |
144 | 410 | const ParamCommandComment *PCC = cast<ParamCommandComment>(Child); |
145 | 410 | if (!PCC->hasParamName()) |
146 | 8 | break; |
147 | | |
148 | 402 | if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph()368 ) |
149 | 24 | break; |
150 | | |
151 | 378 | Params.push_back(PCC); |
152 | 378 | break; |
153 | 402 | } |
154 | | |
155 | 240 | case Comment::TParamCommandCommentKind: { |
156 | 240 | const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child); |
157 | 240 | if (!TPCC->hasParamName()) |
158 | 12 | break; |
159 | | |
160 | 228 | if (!TPCC->hasNonWhitespaceParagraph()) |
161 | 4 | break; |
162 | | |
163 | 224 | TParams.push_back(TPCC); |
164 | 224 | break; |
165 | 228 | } |
166 | | |
167 | 18 | case Comment::VerbatimBlockCommentKind: |
168 | 18 | MiscBlocks.push_back(cast<BlockCommandComment>(Child)); |
169 | 18 | break; |
170 | | |
171 | 84 | case Comment::VerbatimLineCommentKind: { |
172 | 84 | const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child); |
173 | 84 | const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID()); |
174 | 84 | if (!Info->IsDeclarationCommand) |
175 | 8 | MiscBlocks.push_back(VLC); |
176 | 84 | break; |
177 | 228 | } |
178 | | |
179 | 0 | case Comment::TextCommentKind: |
180 | 0 | case Comment::InlineCommandCommentKind: |
181 | 0 | case Comment::HTMLStartTagCommentKind: |
182 | 0 | case Comment::HTMLEndTagCommentKind: |
183 | 0 | case Comment::VerbatimBlockLineCommentKind: |
184 | 0 | case Comment::FullCommentKind: |
185 | 0 | llvm_unreachable("AST node of this kind can't be a child of " |
186 | 4.02k | "a FullComment"); |
187 | 4.02k | } |
188 | 4.02k | } |
189 | | |
190 | | // Sort params in order they are declared in the function prototype. |
191 | | // Unresolved parameters are put at the end of the list in the same order |
192 | | // they were seen in the comment. |
193 | 2.00k | llvm::stable_sort(Params, ParamCommandCommentCompareIndex()); |
194 | 2.00k | llvm::stable_sort(TParams, TParamCommandCommentComparePosition()); |
195 | 2.00k | } |
196 | | |
197 | | void printHTMLStartTagComment(const HTMLStartTagComment *C, |
198 | 128 | llvm::raw_svector_ostream &Result) { |
199 | 128 | Result << "<" << C->getTagName(); |
200 | | |
201 | 128 | if (C->getNumAttrs() != 0) { |
202 | 122 | for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++64 ) { |
203 | 64 | Result << " "; |
204 | 64 | const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); |
205 | 64 | Result << Attr.Name; |
206 | 64 | if (!Attr.Value.empty()) |
207 | 34 | Result << "=\"" << Attr.Value << "\""; |
208 | 64 | } |
209 | 58 | } |
210 | | |
211 | 128 | if (!C->isSelfClosing()) |
212 | 102 | Result << ">"; |
213 | 26 | else |
214 | 26 | Result << "/>"; |
215 | 128 | } |
216 | | |
217 | | class CommentASTToHTMLConverter : |
218 | | public ConstCommentVisitor<CommentASTToHTMLConverter> { |
219 | | public: |
220 | | /// \param Str accumulator for HTML. |
221 | | CommentASTToHTMLConverter(const FullComment *FC, |
222 | | SmallVectorImpl<char> &Str, |
223 | | const CommandTraits &Traits) : |
224 | 1.00k | FC(FC), Result(Str), Traits(Traits) |
225 | 1.00k | { } |
226 | | |
227 | | // Inline content. |
228 | | void visitTextComment(const TextComment *C); |
229 | | void visitInlineCommandComment(const InlineCommandComment *C); |
230 | | void visitHTMLStartTagComment(const HTMLStartTagComment *C); |
231 | | void visitHTMLEndTagComment(const HTMLEndTagComment *C); |
232 | | |
233 | | // Block content. |
234 | | void visitParagraphComment(const ParagraphComment *C); |
235 | | void visitBlockCommandComment(const BlockCommandComment *C); |
236 | | void visitParamCommandComment(const ParamCommandComment *C); |
237 | | void visitTParamCommandComment(const TParamCommandComment *C); |
238 | | void visitVerbatimBlockComment(const VerbatimBlockComment *C); |
239 | | void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); |
240 | | void visitVerbatimLineComment(const VerbatimLineComment *C); |
241 | | |
242 | | void visitFullComment(const FullComment *C); |
243 | | |
244 | | // Helpers. |
245 | | |
246 | | /// Convert a paragraph that is not a block by itself (an argument to some |
247 | | /// command). |
248 | | void visitNonStandaloneParagraphComment(const ParagraphComment *C); |
249 | | |
250 | | void appendToResultWithHTMLEscaping(StringRef S); |
251 | | |
252 | | private: |
253 | | const FullComment *FC; |
254 | | /// Output stream for HTML. |
255 | | llvm::raw_svector_ostream Result; |
256 | | |
257 | | const CommandTraits &Traits; |
258 | | }; |
259 | | } // end unnamed namespace |
260 | | |
261 | 1.75k | void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { |
262 | 1.75k | appendToResultWithHTMLEscaping(C->getText()); |
263 | 1.75k | } |
264 | | |
265 | | void CommentASTToHTMLConverter::visitInlineCommandComment( |
266 | 185 | const InlineCommandComment *C) { |
267 | | // Nothing to render if no arguments supplied. |
268 | 185 | if (C->getNumArgs() == 0) |
269 | 154 | return; |
270 | | |
271 | | // Nothing to render if argument is empty. |
272 | 31 | StringRef Arg0 = C->getArgText(0); |
273 | 31 | if (Arg0.empty()) |
274 | 0 | return; |
275 | | |
276 | 31 | switch (C->getRenderKind()) { |
277 | 1 | case InlineCommandComment::RenderNormal: |
278 | 2 | for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i1 ) { |
279 | 1 | appendToResultWithHTMLEscaping(C->getArgText(i)); |
280 | 1 | Result << " "; |
281 | 1 | } |
282 | 1 | return; |
283 | | |
284 | 3 | case InlineCommandComment::RenderBold: |
285 | 3 | assert(C->getNumArgs() == 1); |
286 | 3 | Result << "<b>"; |
287 | 3 | appendToResultWithHTMLEscaping(Arg0); |
288 | 3 | Result << "</b>"; |
289 | 3 | return; |
290 | 9 | case InlineCommandComment::RenderMonospaced: |
291 | 9 | assert(C->getNumArgs() == 1); |
292 | 9 | Result << "<tt>"; |
293 | 9 | appendToResultWithHTMLEscaping(Arg0); |
294 | 9 | Result<< "</tt>"; |
295 | 9 | return; |
296 | 15 | case InlineCommandComment::RenderEmphasized: |
297 | 15 | assert(C->getNumArgs() == 1); |
298 | 15 | Result << "<em>"; |
299 | 15 | appendToResultWithHTMLEscaping(Arg0); |
300 | 15 | Result << "</em>"; |
301 | 15 | return; |
302 | 3 | case InlineCommandComment::RenderAnchor: |
303 | 3 | assert(C->getNumArgs() == 1); |
304 | 3 | Result << "<span id=\"" << Arg0 << "\"></span>"; |
305 | 3 | return; |
306 | 31 | } |
307 | 31 | } |
308 | | |
309 | | void CommentASTToHTMLConverter::visitHTMLStartTagComment( |
310 | 64 | const HTMLStartTagComment *C) { |
311 | 64 | printHTMLStartTagComment(C, Result); |
312 | 64 | } |
313 | | |
314 | | void CommentASTToHTMLConverter::visitHTMLEndTagComment( |
315 | 31 | const HTMLEndTagComment *C) { |
316 | 31 | Result << "</" << C->getTagName() << ">"; |
317 | 31 | } |
318 | | |
319 | | void CommentASTToHTMLConverter::visitParagraphComment( |
320 | 164 | const ParagraphComment *C) { |
321 | 164 | if (C->isWhitespace()) |
322 | 15 | return; |
323 | | |
324 | 149 | Result << "<p>"; |
325 | 149 | for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); |
326 | 361 | I != E; ++I212 ) { |
327 | 212 | visit(*I); |
328 | 212 | } |
329 | 149 | Result << "</p>"; |
330 | 149 | } |
331 | | |
332 | | void CommentASTToHTMLConverter::visitBlockCommandComment( |
333 | 496 | const BlockCommandComment *C) { |
334 | 496 | const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID()); |
335 | 496 | if (Info->IsBriefCommand) { |
336 | 304 | Result << "<p class=\"para-brief\">"; |
337 | 304 | visitNonStandaloneParagraphComment(C->getParagraph()); |
338 | 304 | Result << "</p>"; |
339 | 304 | return; |
340 | 304 | } |
341 | 192 | if (Info->IsReturnsCommand) { |
342 | 87 | Result << "<p class=\"para-returns\">" |
343 | 87 | "<span class=\"word-returns\">Returns</span> "; |
344 | 87 | visitNonStandaloneParagraphComment(C->getParagraph()); |
345 | 87 | Result << "</p>"; |
346 | 87 | return; |
347 | 87 | } |
348 | | // We don't know anything about this command. Just render the paragraph. |
349 | 105 | visit(C->getParagraph()); |
350 | 105 | } |
351 | | |
352 | | void CommentASTToHTMLConverter::visitParamCommandComment( |
353 | 189 | const ParamCommandComment *C) { |
354 | 189 | if (C->isParamIndexValid()) { |
355 | 130 | if (C->isVarArgParam()) { |
356 | 9 | Result << "<dt class=\"param-name-index-vararg\">"; |
357 | 9 | appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); |
358 | 121 | } else { |
359 | 121 | Result << "<dt class=\"param-name-index-" |
360 | 121 | << C->getParamIndex() |
361 | 121 | << "\">"; |
362 | 121 | appendToResultWithHTMLEscaping(C->getParamName(FC)); |
363 | 121 | } |
364 | 130 | } else { |
365 | 59 | Result << "<dt class=\"param-name-index-invalid\">"; |
366 | 59 | appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); |
367 | 59 | } |
368 | 189 | Result << "</dt>"; |
369 | | |
370 | 189 | if (C->isParamIndexValid()) { |
371 | 130 | if (C->isVarArgParam()) |
372 | 9 | Result << "<dd class=\"param-descr-index-vararg\">"; |
373 | 121 | else |
374 | 121 | Result << "<dd class=\"param-descr-index-" |
375 | 121 | << C->getParamIndex() |
376 | 121 | << "\">"; |
377 | 130 | } else |
378 | 59 | Result << "<dd class=\"param-descr-index-invalid\">"; |
379 | | |
380 | 189 | visitNonStandaloneParagraphComment(C->getParagraph()); |
381 | 189 | Result << "</dd>"; |
382 | 189 | } |
383 | | |
384 | | void CommentASTToHTMLConverter::visitTParamCommandComment( |
385 | 112 | const TParamCommandComment *C) { |
386 | 112 | if (C->isPositionValid()) { |
387 | 81 | if (C->getDepth() == 1) |
388 | 58 | Result << "<dt class=\"tparam-name-index-" |
389 | 58 | << C->getIndex(0) |
390 | 58 | << "\">"; |
391 | 23 | else |
392 | 23 | Result << "<dt class=\"tparam-name-index-other\">"; |
393 | 81 | appendToResultWithHTMLEscaping(C->getParamName(FC)); |
394 | 81 | } else { |
395 | 31 | Result << "<dt class=\"tparam-name-index-invalid\">"; |
396 | 31 | appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); |
397 | 31 | } |
398 | | |
399 | 112 | Result << "</dt>"; |
400 | | |
401 | 112 | if (C->isPositionValid()) { |
402 | 81 | if (C->getDepth() == 1) |
403 | 58 | Result << "<dd class=\"tparam-descr-index-" |
404 | 58 | << C->getIndex(0) |
405 | 58 | << "\">"; |
406 | 23 | else |
407 | 23 | Result << "<dd class=\"tparam-descr-index-other\">"; |
408 | 81 | } else |
409 | 31 | Result << "<dd class=\"tparam-descr-index-invalid\">"; |
410 | | |
411 | 112 | visitNonStandaloneParagraphComment(C->getParagraph()); |
412 | 112 | Result << "</dd>"; |
413 | 112 | } |
414 | | |
415 | | void CommentASTToHTMLConverter::visitVerbatimBlockComment( |
416 | 9 | const VerbatimBlockComment *C) { |
417 | 9 | unsigned NumLines = C->getNumLines(); |
418 | 9 | if (NumLines == 0) |
419 | 0 | return; |
420 | | |
421 | 9 | Result << "<pre>"; |
422 | 26 | for (unsigned i = 0; i != NumLines; ++i17 ) { |
423 | 17 | appendToResultWithHTMLEscaping(C->getText(i)); |
424 | 17 | if (i + 1 != NumLines) |
425 | 8 | Result << '\n'; |
426 | 17 | } |
427 | 9 | Result << "</pre>"; |
428 | 9 | } |
429 | | |
430 | | void CommentASTToHTMLConverter::visitVerbatimBlockLineComment( |
431 | 0 | const VerbatimBlockLineComment *C) { |
432 | 0 | llvm_unreachable("should not see this AST node"); |
433 | 0 | } |
434 | | |
435 | | void CommentASTToHTMLConverter::visitVerbatimLineComment( |
436 | 4 | const VerbatimLineComment *C) { |
437 | 4 | Result << "<pre>"; |
438 | 4 | appendToResultWithHTMLEscaping(C->getText()); |
439 | 4 | Result << "</pre>"; |
440 | 4 | } |
441 | | |
442 | 1.00k | void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) { |
443 | 1.00k | FullCommentParts Parts(C, Traits); |
444 | | |
445 | 1.00k | bool FirstParagraphIsBrief = false; |
446 | 1.00k | if (Parts.Headerfile) |
447 | 6 | visit(Parts.Headerfile); |
448 | 1.00k | if (Parts.Brief) |
449 | 296 | visit(Parts.Brief); |
450 | 704 | else if (Parts.FirstParagraph) { |
451 | 505 | Result << "<p class=\"para-brief\">"; |
452 | 505 | visitNonStandaloneParagraphComment(Parts.FirstParagraph); |
453 | 505 | Result << "</p>"; |
454 | 505 | FirstParagraphIsBrief = true; |
455 | 505 | } |
456 | | |
457 | 1.68k | for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i684 ) { |
458 | 684 | const Comment *C = Parts.MiscBlocks[i]; |
459 | 684 | if (FirstParagraphIsBrief && C == Parts.FirstParagraph566 ) |
460 | 505 | continue; |
461 | 179 | visit(C); |
462 | 179 | } |
463 | | |
464 | 1.00k | if (Parts.TParams.size() != 0) { |
465 | 66 | Result << "<dl>"; |
466 | 178 | for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i112 ) |
467 | 112 | visit(Parts.TParams[i]); |
468 | 66 | Result << "</dl>"; |
469 | 66 | } |
470 | | |
471 | 1.00k | if (Parts.Params.size() != 0) { |
472 | 133 | Result << "<dl>"; |
473 | 322 | for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i189 ) |
474 | 189 | visit(Parts.Params[i]); |
475 | 133 | Result << "</dl>"; |
476 | 133 | } |
477 | | |
478 | 1.00k | if (Parts.Returns.size() != 0) { |
479 | 81 | Result << "<div class=\"result-discussion\">"; |
480 | 168 | for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i87 ) |
481 | 87 | visit(Parts.Returns[i]); |
482 | 81 | Result << "</div>"; |
483 | 81 | } |
484 | | |
485 | 1.00k | } |
486 | | |
487 | | void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( |
488 | 1.19k | const ParagraphComment *C) { |
489 | 1.19k | if (!C) |
490 | 0 | return; |
491 | | |
492 | 1.19k | for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); |
493 | 3.01k | I != E; ++I1.81k ) { |
494 | 1.81k | visit(*I); |
495 | 1.81k | } |
496 | 1.19k | } |
497 | | |
498 | 2.10k | void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { |
499 | 25.4k | for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I23.3k ) { |
500 | 23.3k | const char C = *I; |
501 | 23.3k | switch (C) { |
502 | 4 | case '&': |
503 | 4 | Result << "&"; |
504 | 4 | break; |
505 | 28 | case '<': |
506 | 28 | Result << "<"; |
507 | 28 | break; |
508 | 18 | case '>': |
509 | 18 | Result << ">"; |
510 | 18 | break; |
511 | 22 | case '"': |
512 | 22 | Result << """; |
513 | 22 | break; |
514 | 74 | case '\'': |
515 | 74 | Result << "'"; |
516 | 74 | break; |
517 | 36 | case '/': |
518 | 36 | Result << "/"; |
519 | 36 | break; |
520 | 23.1k | default: |
521 | 23.1k | Result << C; |
522 | 23.1k | break; |
523 | 23.3k | } |
524 | 23.3k | } |
525 | 2.10k | } |
526 | | |
527 | | namespace { |
528 | | class CommentASTToXMLConverter : |
529 | | public ConstCommentVisitor<CommentASTToXMLConverter> { |
530 | | public: |
531 | | /// \param Str accumulator for XML. |
532 | | CommentASTToXMLConverter(const FullComment *FC, |
533 | | SmallVectorImpl<char> &Str, |
534 | | const CommandTraits &Traits, |
535 | | const SourceManager &SM) : |
536 | 1.00k | FC(FC), Result(Str), Traits(Traits), SM(SM) { } |
537 | | |
538 | | // Inline content. |
539 | | void visitTextComment(const TextComment *C); |
540 | | void visitInlineCommandComment(const InlineCommandComment *C); |
541 | | void visitHTMLStartTagComment(const HTMLStartTagComment *C); |
542 | | void visitHTMLEndTagComment(const HTMLEndTagComment *C); |
543 | | |
544 | | // Block content. |
545 | | void visitParagraphComment(const ParagraphComment *C); |
546 | | |
547 | | void appendParagraphCommentWithKind(const ParagraphComment *C, |
548 | | StringRef Kind); |
549 | | |
550 | | void visitBlockCommandComment(const BlockCommandComment *C); |
551 | | void visitParamCommandComment(const ParamCommandComment *C); |
552 | | void visitTParamCommandComment(const TParamCommandComment *C); |
553 | | void visitVerbatimBlockComment(const VerbatimBlockComment *C); |
554 | | void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); |
555 | | void visitVerbatimLineComment(const VerbatimLineComment *C); |
556 | | |
557 | | void visitFullComment(const FullComment *C); |
558 | | |
559 | | // Helpers. |
560 | | void appendToResultWithXMLEscaping(StringRef S); |
561 | | void appendToResultWithCDATAEscaping(StringRef S); |
562 | | |
563 | | void formatTextOfDeclaration(const DeclInfo *DI, |
564 | | SmallString<128> &Declaration); |
565 | | |
566 | | private: |
567 | | const FullComment *FC; |
568 | | |
569 | | /// Output stream for XML. |
570 | | llvm::raw_svector_ostream Result; |
571 | | |
572 | | const CommandTraits &Traits; |
573 | | const SourceManager &SM; |
574 | | }; |
575 | | |
576 | | void getSourceTextOfDeclaration(const DeclInfo *ThisDecl, |
577 | 1.00k | SmallVectorImpl<char> &Str) { |
578 | 1.00k | ASTContext &Context = ThisDecl->CurrentDecl->getASTContext(); |
579 | 1.00k | const LangOptions &LangOpts = Context.getLangOpts(); |
580 | 1.00k | llvm::raw_svector_ostream OS(Str); |
581 | 1.00k | PrintingPolicy PPolicy(LangOpts); |
582 | 1.00k | PPolicy.PolishForDeclaration = true; |
583 | 1.00k | PPolicy.TerseOutput = true; |
584 | 1.00k | PPolicy.ConstantsAsWritten = true; |
585 | 1.00k | ThisDecl->CurrentDecl->print(OS, PPolicy, |
586 | 1.00k | /*Indentation*/0, /*PrintInstantiation*/false); |
587 | 1.00k | } |
588 | | |
589 | | void CommentASTToXMLConverter::formatTextOfDeclaration( |
590 | 1.00k | const DeclInfo *DI, SmallString<128> &Declaration) { |
591 | | // Formatting API expects null terminated input string. |
592 | 1.00k | StringRef StringDecl(Declaration.c_str(), Declaration.size()); |
593 | | |
594 | | // Formatter specific code. |
595 | 1.00k | unsigned Offset = 0; |
596 | 1.00k | unsigned Length = Declaration.size(); |
597 | | |
598 | 1.00k | format::FormatStyle Style = format::getLLVMStyle(); |
599 | 1.00k | Style.FixNamespaceComments = false; |
600 | 1.00k | tooling::Replacements Replaces = |
601 | 1.00k | reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd"); |
602 | 1.00k | auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces); |
603 | 1.00k | if (static_cast<bool>(FormattedStringDecl)) { |
604 | 1.00k | Declaration = *FormattedStringDecl; |
605 | 1.00k | } |
606 | 1.00k | } |
607 | | |
608 | | } // end unnamed namespace |
609 | | |
610 | 1.76k | void CommentASTToXMLConverter::visitTextComment(const TextComment *C) { |
611 | 1.76k | appendToResultWithXMLEscaping(C->getText()); |
612 | 1.76k | } |
613 | | |
614 | | void CommentASTToXMLConverter::visitInlineCommandComment( |
615 | 185 | const InlineCommandComment *C) { |
616 | | // Nothing to render if no arguments supplied. |
617 | 185 | if (C->getNumArgs() == 0) |
618 | 154 | return; |
619 | | |
620 | | // Nothing to render if argument is empty. |
621 | 31 | StringRef Arg0 = C->getArgText(0); |
622 | 31 | if (Arg0.empty()) |
623 | 0 | return; |
624 | | |
625 | 31 | switch (C->getRenderKind()) { |
626 | 1 | case InlineCommandComment::RenderNormal: |
627 | 2 | for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i1 ) { |
628 | 1 | appendToResultWithXMLEscaping(C->getArgText(i)); |
629 | 1 | Result << " "; |
630 | 1 | } |
631 | 1 | return; |
632 | 3 | case InlineCommandComment::RenderBold: |
633 | 3 | assert(C->getNumArgs() == 1); |
634 | 3 | Result << "<bold>"; |
635 | 3 | appendToResultWithXMLEscaping(Arg0); |
636 | 3 | Result << "</bold>"; |
637 | 3 | return; |
638 | 9 | case InlineCommandComment::RenderMonospaced: |
639 | 9 | assert(C->getNumArgs() == 1); |
640 | 9 | Result << "<monospaced>"; |
641 | 9 | appendToResultWithXMLEscaping(Arg0); |
642 | 9 | Result << "</monospaced>"; |
643 | 9 | return; |
644 | 15 | case InlineCommandComment::RenderEmphasized: |
645 | 15 | assert(C->getNumArgs() == 1); |
646 | 15 | Result << "<emphasized>"; |
647 | 15 | appendToResultWithXMLEscaping(Arg0); |
648 | 15 | Result << "</emphasized>"; |
649 | 15 | return; |
650 | 3 | case InlineCommandComment::RenderAnchor: |
651 | 3 | assert(C->getNumArgs() == 1); |
652 | 3 | Result << "<anchor id=\"" << Arg0 << "\"></anchor>"; |
653 | 3 | return; |
654 | 31 | } |
655 | 31 | } |
656 | | |
657 | | void CommentASTToXMLConverter::visitHTMLStartTagComment( |
658 | 64 | const HTMLStartTagComment *C) { |
659 | 64 | Result << "<rawHTML"; |
660 | 64 | if (C->isMalformed()) |
661 | 19 | Result << " isMalformed=\"1\""; |
662 | 64 | Result << ">"; |
663 | 64 | { |
664 | 64 | SmallString<32> Tag; |
665 | 64 | { |
666 | 64 | llvm::raw_svector_ostream TagOS(Tag); |
667 | 64 | printHTMLStartTagComment(C, TagOS); |
668 | 64 | } |
669 | 64 | appendToResultWithCDATAEscaping(Tag); |
670 | 64 | } |
671 | 64 | Result << "</rawHTML>"; |
672 | 64 | } |
673 | | |
674 | | void |
675 | 31 | CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { |
676 | 31 | Result << "<rawHTML"; |
677 | 31 | if (C->isMalformed()) |
678 | 10 | Result << " isMalformed=\"1\""; |
679 | 31 | Result << "></" << C->getTagName() << "></rawHTML>"; |
680 | 31 | } |
681 | | |
682 | | void |
683 | 865 | CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { |
684 | 865 | appendParagraphCommentWithKind(C, StringRef()); |
685 | 865 | } |
686 | | |
687 | | void CommentASTToXMLConverter::appendParagraphCommentWithKind( |
688 | | const ParagraphComment *C, |
689 | 1.37k | StringRef ParagraphKind) { |
690 | 1.37k | if (C->isWhitespace()) |
691 | 81 | return; |
692 | | |
693 | 1.29k | if (ParagraphKind.empty()) |
694 | 1.22k | Result << "<Para>"; |
695 | 69 | else |
696 | 69 | Result << "<Para kind=\"" << ParagraphKind << "\">"; |
697 | | |
698 | 1.29k | for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); |
699 | 3.34k | I != E; ++I2.04k ) { |
700 | 2.04k | visit(*I); |
701 | 2.04k | } |
702 | 1.29k | Result << "</Para>"; |
703 | 1.29k | } |
704 | | |
705 | | void CommentASTToXMLConverter::visitBlockCommandComment( |
706 | 512 | const BlockCommandComment *C) { |
707 | 512 | StringRef ParagraphKind; |
708 | | |
709 | 512 | switch (C->getCommandID()) { |
710 | 0 | case CommandTraits::KCI_attention: |
711 | 54 | case CommandTraits::KCI_author: |
712 | 54 | case CommandTraits::KCI_authors: |
713 | 54 | case CommandTraits::KCI_bug: |
714 | 54 | case CommandTraits::KCI_copyright: |
715 | 54 | case CommandTraits::KCI_date: |
716 | 56 | case CommandTraits::KCI_invariant: |
717 | 58 | case CommandTraits::KCI_note: |
718 | 58 | case CommandTraits::KCI_post: |
719 | 58 | case CommandTraits::KCI_pre: |
720 | 58 | case CommandTraits::KCI_remark: |
721 | 58 | case CommandTraits::KCI_remarks: |
722 | 58 | case CommandTraits::KCI_sa: |
723 | 59 | case CommandTraits::KCI_see: |
724 | 59 | case CommandTraits::KCI_since: |
725 | 71 | case CommandTraits::KCI_todo: |
726 | 71 | case CommandTraits::KCI_version: |
727 | 71 | case CommandTraits::KCI_warning: |
728 | 71 | ParagraphKind = C->getCommandName(Traits); |
729 | 71 | break; |
730 | 441 | default: |
731 | 441 | break; |
732 | 512 | } |
733 | | |
734 | 512 | appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); |
735 | 512 | } |
736 | | |
737 | | void CommentASTToXMLConverter::visitParamCommandComment( |
738 | 189 | const ParamCommandComment *C) { |
739 | 189 | Result << "<Parameter><Name>"; |
740 | 189 | appendToResultWithXMLEscaping(C->isParamIndexValid() |
741 | 189 | ? C->getParamName(FC)130 |
742 | 189 | : C->getParamNameAsWritten()59 ); |
743 | 189 | Result << "</Name>"; |
744 | | |
745 | 189 | if (C->isParamIndexValid()) { |
746 | 130 | if (C->isVarArgParam()) |
747 | 9 | Result << "<IsVarArg />"; |
748 | 121 | else |
749 | 121 | Result << "<Index>" << C->getParamIndex() << "</Index>"; |
750 | 130 | } |
751 | | |
752 | 189 | Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; |
753 | 189 | switch (C->getDirection()) { |
754 | 184 | case ParamCommandComment::In: |
755 | 184 | Result << "in"; |
756 | 184 | break; |
757 | 3 | case ParamCommandComment::Out: |
758 | 3 | Result << "out"; |
759 | 3 | break; |
760 | 2 | case ParamCommandComment::InOut: |
761 | 2 | Result << "in,out"; |
762 | 2 | break; |
763 | 189 | } |
764 | 189 | Result << "</Direction><Discussion>"; |
765 | 189 | visit(C->getParagraph()); |
766 | 189 | Result << "</Discussion></Parameter>"; |
767 | 189 | } |
768 | | |
769 | | void CommentASTToXMLConverter::visitTParamCommandComment( |
770 | 112 | const TParamCommandComment *C) { |
771 | 112 | Result << "<Parameter><Name>"; |
772 | 112 | appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)81 |
773 | 112 | : C->getParamNameAsWritten()31 ); |
774 | 112 | Result << "</Name>"; |
775 | | |
776 | 112 | if (C->isPositionValid() && C->getDepth() == 181 ) { |
777 | 58 | Result << "<Index>" << C->getIndex(0) << "</Index>"; |
778 | 58 | } |
779 | | |
780 | 112 | Result << "<Discussion>"; |
781 | 112 | visit(C->getParagraph()); |
782 | 112 | Result << "</Discussion></Parameter>"; |
783 | 112 | } |
784 | | |
785 | | void CommentASTToXMLConverter::visitVerbatimBlockComment( |
786 | 9 | const VerbatimBlockComment *C) { |
787 | 9 | unsigned NumLines = C->getNumLines(); |
788 | 9 | if (NumLines == 0) |
789 | 0 | return; |
790 | | |
791 | 9 | switch (C->getCommandID()) { |
792 | 1 | case CommandTraits::KCI_code: |
793 | 1 | Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; |
794 | 1 | break; |
795 | 8 | default: |
796 | 8 | Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; |
797 | 8 | break; |
798 | 9 | } |
799 | 26 | for (unsigned i = 0; 9 i != NumLines; ++i17 ) { |
800 | 17 | appendToResultWithXMLEscaping(C->getText(i)); |
801 | 17 | if (i + 1 != NumLines) |
802 | 8 | Result << '\n'; |
803 | 17 | } |
804 | 9 | Result << "</Verbatim>"; |
805 | 9 | } |
806 | | |
807 | | void CommentASTToXMLConverter::visitVerbatimBlockLineComment( |
808 | 0 | const VerbatimBlockLineComment *C) { |
809 | 0 | llvm_unreachable("should not see this AST node"); |
810 | 0 | } |
811 | | |
812 | | void CommentASTToXMLConverter::visitVerbatimLineComment( |
813 | 4 | const VerbatimLineComment *C) { |
814 | 4 | Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; |
815 | 4 | appendToResultWithXMLEscaping(C->getText()); |
816 | 4 | Result << "</Verbatim>"; |
817 | 4 | } |
818 | | |
819 | 1.00k | void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { |
820 | 1.00k | FullCommentParts Parts(C, Traits); |
821 | | |
822 | 1.00k | const DeclInfo *DI = C->getDeclInfo(); |
823 | 1.00k | StringRef RootEndTag; |
824 | 1.00k | if (DI) { |
825 | 1.00k | switch (DI->getKind()) { |
826 | 61 | case DeclInfo::OtherKind: |
827 | 61 | RootEndTag = "</Other>"; |
828 | 61 | Result << "<Other"; |
829 | 61 | break; |
830 | 566 | case DeclInfo::FunctionKind: |
831 | 566 | RootEndTag = "</Function>"; |
832 | 566 | Result << "<Function"; |
833 | 566 | switch (DI->TemplateKind) { |
834 | 499 | case DeclInfo::NotTemplate: |
835 | 499 | break; |
836 | 57 | case DeclInfo::Template: |
837 | 57 | Result << " templateKind=\"template\""; |
838 | 57 | break; |
839 | 10 | case DeclInfo::TemplateSpecialization: |
840 | 10 | Result << " templateKind=\"specialization\""; |
841 | 10 | break; |
842 | 0 | case DeclInfo::TemplatePartialSpecialization: |
843 | 0 | llvm_unreachable("partial specializations of functions " |
844 | 566 | "are not allowed in C++"); |
845 | 566 | } |
846 | 566 | if (DI->IsInstanceMethod) |
847 | 122 | Result << " isInstanceMethod=\"1\""; |
848 | 566 | if (DI->IsClassMethod) |
849 | 8 | Result << " isClassMethod=\"1\""; |
850 | 566 | break; |
851 | 95 | case DeclInfo::ClassKind: |
852 | 95 | RootEndTag = "</Class>"; |
853 | 95 | Result << "<Class"; |
854 | 95 | switch (DI->TemplateKind) { |
855 | 59 | case DeclInfo::NotTemplate: |
856 | 59 | break; |
857 | 25 | case DeclInfo::Template: |
858 | 25 | Result << " templateKind=\"template\""; |
859 | 25 | break; |
860 | 5 | case DeclInfo::TemplateSpecialization: |
861 | 5 | Result << " templateKind=\"specialization\""; |
862 | 5 | break; |
863 | 6 | case DeclInfo::TemplatePartialSpecialization: |
864 | 6 | Result << " templateKind=\"partialSpecialization\""; |
865 | 6 | break; |
866 | 95 | } |
867 | 95 | break; |
868 | 170 | case DeclInfo::VariableKind: |
869 | 170 | RootEndTag = "</Variable>"; |
870 | 170 | Result << "<Variable"; |
871 | 170 | break; |
872 | 9 | case DeclInfo::NamespaceKind: |
873 | 9 | RootEndTag = "</Namespace>"; |
874 | 9 | Result << "<Namespace"; |
875 | 9 | break; |
876 | 74 | case DeclInfo::TypedefKind: |
877 | 74 | RootEndTag = "</Typedef>"; |
878 | 74 | Result << "<Typedef"; |
879 | 74 | break; |
880 | 25 | case DeclInfo::EnumKind: |
881 | 25 | RootEndTag = "</Enum>"; |
882 | 25 | Result << "<Enum"; |
883 | 25 | break; |
884 | 1.00k | } |
885 | | |
886 | 1.00k | { |
887 | | // Print line and column number. |
888 | 1.00k | SourceLocation Loc = DI->CurrentDecl->getLocation(); |
889 | 1.00k | std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); |
890 | 1.00k | FileID FID = LocInfo.first; |
891 | 1.00k | unsigned FileOffset = LocInfo.second; |
892 | | |
893 | 1.00k | if (FID.isValid()) { |
894 | 1.00k | if (OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID)) { |
895 | 979 | Result << " file=\""; |
896 | 979 | appendToResultWithXMLEscaping(FE->getName()); |
897 | 979 | Result << "\""; |
898 | 979 | } |
899 | 1.00k | Result << " line=\"" << SM.getLineNumber(FID, FileOffset) |
900 | 1.00k | << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) |
901 | 1.00k | << "\""; |
902 | 1.00k | } |
903 | 1.00k | } |
904 | | |
905 | | // Finish the root tag. |
906 | 1.00k | Result << ">"; |
907 | | |
908 | 1.00k | bool FoundName = false; |
909 | 1.00k | if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { |
910 | 960 | if (DeclarationName DeclName = ND->getDeclName()) { |
911 | 955 | Result << "<Name>"; |
912 | 955 | std::string Name = DeclName.getAsString(); |
913 | 955 | appendToResultWithXMLEscaping(Name); |
914 | 955 | FoundName = true; |
915 | 955 | Result << "</Name>"; |
916 | 955 | } |
917 | 960 | } |
918 | 1.00k | if (!FoundName) |
919 | 45 | Result << "<Name><anonymous></Name>"; |
920 | | |
921 | 1.00k | { |
922 | | // Print USR. |
923 | 1.00k | SmallString<128> USR; |
924 | 1.00k | generateUSRForDecl(DI->CommentDecl, USR); |
925 | 1.00k | if (!USR.empty()) { |
926 | 1.00k | Result << "<USR>"; |
927 | 1.00k | appendToResultWithXMLEscaping(USR); |
928 | 1.00k | Result << "</USR>"; |
929 | 1.00k | } |
930 | 1.00k | } |
931 | 1.00k | } else { |
932 | | // No DeclInfo -- just emit some root tag and name tag. |
933 | 0 | RootEndTag = "</Other>"; |
934 | 0 | Result << "<Other><Name>unknown</Name>"; |
935 | 0 | } |
936 | | |
937 | 1.00k | if (Parts.Headerfile) { |
938 | 6 | Result << "<Headerfile>"; |
939 | 6 | visit(Parts.Headerfile); |
940 | 6 | Result << "</Headerfile>"; |
941 | 6 | } |
942 | | |
943 | 1.00k | { |
944 | | // Pretty-print the declaration. |
945 | 1.00k | Result << "<Declaration>"; |
946 | 1.00k | SmallString<128> Declaration; |
947 | 1.00k | getSourceTextOfDeclaration(DI, Declaration); |
948 | 1.00k | formatTextOfDeclaration(DI, Declaration); |
949 | 1.00k | appendToResultWithXMLEscaping(Declaration); |
950 | 1.00k | Result << "</Declaration>"; |
951 | 1.00k | } |
952 | | |
953 | 1.00k | bool FirstParagraphIsBrief = false; |
954 | 1.00k | if (Parts.Brief) { |
955 | 296 | Result << "<Abstract>"; |
956 | 296 | visit(Parts.Brief); |
957 | 296 | Result << "</Abstract>"; |
958 | 704 | } else if (Parts.FirstParagraph) { |
959 | 505 | Result << "<Abstract>"; |
960 | 505 | visit(Parts.FirstParagraph); |
961 | 505 | Result << "</Abstract>"; |
962 | 505 | FirstParagraphIsBrief = true; |
963 | 505 | } |
964 | | |
965 | 1.00k | if (Parts.TParams.size() != 0) { |
966 | 66 | Result << "<TemplateParameters>"; |
967 | 178 | for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i112 ) |
968 | 112 | visit(Parts.TParams[i]); |
969 | 66 | Result << "</TemplateParameters>"; |
970 | 66 | } |
971 | | |
972 | 1.00k | if (Parts.Params.size() != 0) { |
973 | 133 | Result << "<Parameters>"; |
974 | 322 | for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i189 ) |
975 | 189 | visit(Parts.Params[i]); |
976 | 133 | Result << "</Parameters>"; |
977 | 133 | } |
978 | | |
979 | 1.00k | if (Parts.Exceptions.size() != 0) { |
980 | 10 | Result << "<Exceptions>"; |
981 | 26 | for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i16 ) |
982 | 16 | visit(Parts.Exceptions[i]); |
983 | 10 | Result << "</Exceptions>"; |
984 | 10 | } |
985 | | |
986 | 1.00k | if (Parts.Returns.size() != 0) { |
987 | 81 | Result << "<ResultDiscussion>"; |
988 | 168 | for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i87 ) |
989 | 87 | visit(Parts.Returns[i]); |
990 | 81 | Result << "</ResultDiscussion>"; |
991 | 81 | } |
992 | | |
993 | 1.00k | if (DI->CommentDecl->hasAttrs()) { |
994 | 11 | const AttrVec &Attrs = DI->CommentDecl->getAttrs(); |
995 | 23 | for (unsigned i = 0, e = Attrs.size(); i != e; i++12 ) { |
996 | 12 | const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); |
997 | 12 | if (!AA) { |
998 | 8 | if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { |
999 | 5 | if (DA->getMessage().empty()) |
1000 | 4 | Result << "<Deprecated/>"; |
1001 | 1 | else { |
1002 | 1 | Result << "<Deprecated>"; |
1003 | 1 | appendToResultWithXMLEscaping(DA->getMessage()); |
1004 | 1 | Result << "</Deprecated>"; |
1005 | 1 | } |
1006 | 5 | } |
1007 | 3 | else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { |
1008 | 3 | if (UA->getMessage().empty()) |
1009 | 2 | Result << "<Unavailable/>"; |
1010 | 1 | else { |
1011 | 1 | Result << "<Unavailable>"; |
1012 | 1 | appendToResultWithXMLEscaping(UA->getMessage()); |
1013 | 1 | Result << "</Unavailable>"; |
1014 | 1 | } |
1015 | 3 | } |
1016 | 8 | continue; |
1017 | 8 | } |
1018 | | |
1019 | | // 'availability' attribute. |
1020 | 4 | Result << "<Availability"; |
1021 | 4 | StringRef Distribution; |
1022 | 4 | if (AA->getPlatform()) { |
1023 | 4 | Distribution = AvailabilityAttr::getPrettyPlatformName( |
1024 | 4 | AA->getPlatform()->getName()); |
1025 | 4 | if (Distribution.empty()) |
1026 | 0 | Distribution = AA->getPlatform()->getName(); |
1027 | 4 | } |
1028 | 4 | Result << " distribution=\"" << Distribution << "\">"; |
1029 | 4 | VersionTuple IntroducedInVersion = AA->getIntroduced(); |
1030 | 4 | if (!IntroducedInVersion.empty()) { |
1031 | 3 | Result << "<IntroducedInVersion>" |
1032 | 3 | << IntroducedInVersion.getAsString() |
1033 | 3 | << "</IntroducedInVersion>"; |
1034 | 3 | } |
1035 | 4 | VersionTuple DeprecatedInVersion = AA->getDeprecated(); |
1036 | 4 | if (!DeprecatedInVersion.empty()) { |
1037 | 2 | Result << "<DeprecatedInVersion>" |
1038 | 2 | << DeprecatedInVersion.getAsString() |
1039 | 2 | << "</DeprecatedInVersion>"; |
1040 | 2 | } |
1041 | 4 | VersionTuple RemovedAfterVersion = AA->getObsoleted(); |
1042 | 4 | if (!RemovedAfterVersion.empty()) { |
1043 | 2 | Result << "<RemovedAfterVersion>" |
1044 | 2 | << RemovedAfterVersion.getAsString() |
1045 | 2 | << "</RemovedAfterVersion>"; |
1046 | 2 | } |
1047 | 4 | StringRef DeprecationSummary = AA->getMessage(); |
1048 | 4 | if (!DeprecationSummary.empty()) { |
1049 | 2 | Result << "<DeprecationSummary>"; |
1050 | 2 | appendToResultWithXMLEscaping(DeprecationSummary); |
1051 | 2 | Result << "</DeprecationSummary>"; |
1052 | 2 | } |
1053 | 4 | if (AA->getUnavailable()) |
1054 | 1 | Result << "<Unavailable/>"; |
1055 | 4 | Result << "</Availability>"; |
1056 | 4 | } |
1057 | 11 | } |
1058 | | |
1059 | 1.00k | { |
1060 | 1.00k | bool StartTagEmitted = false; |
1061 | 1.68k | for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i684 ) { |
1062 | 684 | const Comment *C = Parts.MiscBlocks[i]; |
1063 | 684 | if (FirstParagraphIsBrief && C == Parts.FirstParagraph566 ) |
1064 | 505 | continue; |
1065 | 179 | if (!StartTagEmitted) { |
1066 | 138 | Result << "<Discussion>"; |
1067 | 138 | StartTagEmitted = true; |
1068 | 138 | } |
1069 | 179 | visit(C); |
1070 | 179 | } |
1071 | 1.00k | if (StartTagEmitted) |
1072 | 138 | Result << "</Discussion>"; |
1073 | 1.00k | } |
1074 | | |
1075 | 1.00k | Result << RootEndTag; |
1076 | 1.00k | } |
1077 | | |
1078 | 6.05k | void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { |
1079 | 205k | for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I199k ) { |
1080 | 199k | const char C = *I; |
1081 | 199k | switch (C) { |
1082 | 42 | case '&': |
1083 | 42 | Result << "&"; |
1084 | 42 | break; |
1085 | 245 | case '<': |
1086 | 245 | Result << "<"; |
1087 | 245 | break; |
1088 | 376 | case '>': |
1089 | 376 | Result << ">"; |
1090 | 376 | break; |
1091 | 22 | case '"': |
1092 | 22 | Result << """; |
1093 | 22 | break; |
1094 | 74 | case '\'': |
1095 | 74 | Result << "'"; |
1096 | 74 | break; |
1097 | 198k | default: |
1098 | 198k | Result << C; |
1099 | 198k | break; |
1100 | 199k | } |
1101 | 199k | } |
1102 | 6.05k | } |
1103 | | |
1104 | 64 | void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) { |
1105 | 64 | if (S.empty()) |
1106 | 0 | return; |
1107 | | |
1108 | 64 | Result << "<![CDATA["; |
1109 | 132 | while (!S.empty()) { |
1110 | 68 | size_t Pos = S.find("]]>"); |
1111 | 68 | if (Pos == 0) { |
1112 | 2 | Result << "]]]]><![CDATA[>"; |
1113 | 2 | S = S.drop_front(3); |
1114 | 2 | continue; |
1115 | 2 | } |
1116 | 66 | if (Pos == StringRef::npos) |
1117 | 64 | Pos = S.size(); |
1118 | | |
1119 | 66 | Result << S.substr(0, Pos); |
1120 | | |
1121 | 66 | S = S.drop_front(Pos); |
1122 | 66 | } |
1123 | 64 | Result << "]]>"; |
1124 | 64 | } |
1125 | | |
1126 | 42 | CommentToXMLConverter::CommentToXMLConverter() {} |
1127 | 42 | CommentToXMLConverter::~CommentToXMLConverter() {} |
1128 | | |
1129 | | void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC, |
1130 | | SmallVectorImpl<char> &HTML, |
1131 | 1.00k | const ASTContext &Context) { |
1132 | 1.00k | CommentASTToHTMLConverter Converter(FC, HTML, |
1133 | 1.00k | Context.getCommentCommandTraits()); |
1134 | 1.00k | Converter.visit(FC); |
1135 | 1.00k | } |
1136 | | |
1137 | | void CommentToXMLConverter::convertHTMLTagNodeToText( |
1138 | | const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text, |
1139 | 0 | const ASTContext &Context) { |
1140 | 0 | CommentASTToHTMLConverter Converter(nullptr, Text, |
1141 | 0 | Context.getCommentCommandTraits()); |
1142 | 0 | Converter.visit(HTC); |
1143 | 0 | } |
1144 | | |
1145 | | void CommentToXMLConverter::convertCommentToXML(const FullComment *FC, |
1146 | | SmallVectorImpl<char> &XML, |
1147 | 1.00k | const ASTContext &Context) { |
1148 | 1.00k | CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(), |
1149 | 1.00k | Context.getSourceManager()); |
1150 | 1.00k | Converter.visit(FC); |
1151 | 1.00k | } |