1 //===- CXComment.cpp - libclang APIs for manipulating CXComments ----------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines all libclang APIs related to walking comment AST.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "clang-c/Index.h"
15 #include "CXComment.h"
16 #include "CXCursor.h"
17 #include "CXString.h"
18 #include "SimpleFormatContext.h"
19 #include "clang/AST/CommentCommandTraits.h"
20 #include "clang/AST/CommentVisitor.h"
21 #include "clang/AST/Decl.h"
22 #include "clang/AST/PrettyPrinter.h"
23 #include "clang/Format/Format.h"
24 #include "clang/Lex/Lexer.h"
25 #include "llvm/ADT/StringExtras.h"
26 #include "llvm/ADT/StringSwitch.h"
27 #include "llvm/Support/ErrorHandling.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include <climits>
30
31 using namespace clang;
32 using namespace clang::comments;
33 using namespace clang::cxcomment;
34
35 extern "C" {
36
clang_Comment_getKind(CXComment CXC)37 enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
38 const Comment *C = getASTNode(CXC);
39 if (!C)
40 return CXComment_Null;
41
42 switch (C->getCommentKind()) {
43 case Comment::NoCommentKind:
44 return CXComment_Null;
45
46 case Comment::TextCommentKind:
47 return CXComment_Text;
48
49 case Comment::InlineCommandCommentKind:
50 return CXComment_InlineCommand;
51
52 case Comment::HTMLStartTagCommentKind:
53 return CXComment_HTMLStartTag;
54
55 case Comment::HTMLEndTagCommentKind:
56 return CXComment_HTMLEndTag;
57
58 case Comment::ParagraphCommentKind:
59 return CXComment_Paragraph;
60
61 case Comment::BlockCommandCommentKind:
62 return CXComment_BlockCommand;
63
64 case Comment::ParamCommandCommentKind:
65 return CXComment_ParamCommand;
66
67 case Comment::TParamCommandCommentKind:
68 return CXComment_TParamCommand;
69
70 case Comment::VerbatimBlockCommentKind:
71 return CXComment_VerbatimBlockCommand;
72
73 case Comment::VerbatimBlockLineCommentKind:
74 return CXComment_VerbatimBlockLine;
75
76 case Comment::VerbatimLineCommentKind:
77 return CXComment_VerbatimLine;
78
79 case Comment::FullCommentKind:
80 return CXComment_FullComment;
81 }
82 llvm_unreachable("unknown CommentKind");
83 }
84
clang_Comment_getNumChildren(CXComment CXC)85 unsigned clang_Comment_getNumChildren(CXComment CXC) {
86 const Comment *C = getASTNode(CXC);
87 if (!C)
88 return 0;
89
90 return C->child_count();
91 }
92
clang_Comment_getChild(CXComment CXC,unsigned ChildIdx)93 CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
94 const Comment *C = getASTNode(CXC);
95 if (!C || ChildIdx >= C->child_count())
96 return createCXComment(NULL, NULL);
97
98 return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
99 }
100
clang_Comment_isWhitespace(CXComment CXC)101 unsigned clang_Comment_isWhitespace(CXComment CXC) {
102 const Comment *C = getASTNode(CXC);
103 if (!C)
104 return false;
105
106 if (const TextComment *TC = dyn_cast<TextComment>(C))
107 return TC->isWhitespace();
108
109 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
110 return PC->isWhitespace();
111
112 return false;
113 }
114
clang_InlineContentComment_hasTrailingNewline(CXComment CXC)115 unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
116 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
117 if (!ICC)
118 return false;
119
120 return ICC->hasTrailingNewline();
121 }
122
clang_TextComment_getText(CXComment CXC)123 CXString clang_TextComment_getText(CXComment CXC) {
124 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
125 if (!TC)
126 return cxstring::createNull();
127
128 return cxstring::createRef(TC->getText());
129 }
130
clang_InlineCommandComment_getCommandName(CXComment CXC)131 CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
132 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
133 if (!ICC)
134 return cxstring::createNull();
135
136 const CommandTraits &Traits = getCommandTraits(CXC);
137 return cxstring::createRef(ICC->getCommandName(Traits));
138 }
139
140 enum CXCommentInlineCommandRenderKind
clang_InlineCommandComment_getRenderKind(CXComment CXC)141 clang_InlineCommandComment_getRenderKind(CXComment CXC) {
142 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
143 if (!ICC)
144 return CXCommentInlineCommandRenderKind_Normal;
145
146 switch (ICC->getRenderKind()) {
147 case InlineCommandComment::RenderNormal:
148 return CXCommentInlineCommandRenderKind_Normal;
149
150 case InlineCommandComment::RenderBold:
151 return CXCommentInlineCommandRenderKind_Bold;
152
153 case InlineCommandComment::RenderMonospaced:
154 return CXCommentInlineCommandRenderKind_Monospaced;
155
156 case InlineCommandComment::RenderEmphasized:
157 return CXCommentInlineCommandRenderKind_Emphasized;
158 }
159 llvm_unreachable("unknown InlineCommandComment::RenderKind");
160 }
161
clang_InlineCommandComment_getNumArgs(CXComment CXC)162 unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
163 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
164 if (!ICC)
165 return 0;
166
167 return ICC->getNumArgs();
168 }
169
clang_InlineCommandComment_getArgText(CXComment CXC,unsigned ArgIdx)170 CXString clang_InlineCommandComment_getArgText(CXComment CXC,
171 unsigned ArgIdx) {
172 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
173 if (!ICC || ArgIdx >= ICC->getNumArgs())
174 return cxstring::createNull();
175
176 return cxstring::createRef(ICC->getArgText(ArgIdx));
177 }
178
clang_HTMLTagComment_getTagName(CXComment CXC)179 CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
180 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
181 if (!HTC)
182 return cxstring::createNull();
183
184 return cxstring::createRef(HTC->getTagName());
185 }
186
clang_HTMLStartTagComment_isSelfClosing(CXComment CXC)187 unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
188 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
189 if (!HST)
190 return false;
191
192 return HST->isSelfClosing();
193 }
194
clang_HTMLStartTag_getNumAttrs(CXComment CXC)195 unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
196 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
197 if (!HST)
198 return 0;
199
200 return HST->getNumAttrs();
201 }
202
clang_HTMLStartTag_getAttrName(CXComment CXC,unsigned AttrIdx)203 CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
204 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
205 if (!HST || AttrIdx >= HST->getNumAttrs())
206 return cxstring::createNull();
207
208 return cxstring::createRef(HST->getAttr(AttrIdx).Name);
209 }
210
clang_HTMLStartTag_getAttrValue(CXComment CXC,unsigned AttrIdx)211 CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
212 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
213 if (!HST || AttrIdx >= HST->getNumAttrs())
214 return cxstring::createNull();
215
216 return cxstring::createRef(HST->getAttr(AttrIdx).Value);
217 }
218
clang_BlockCommandComment_getCommandName(CXComment CXC)219 CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
220 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
221 if (!BCC)
222 return cxstring::createNull();
223
224 const CommandTraits &Traits = getCommandTraits(CXC);
225 return cxstring::createRef(BCC->getCommandName(Traits));
226 }
227
clang_BlockCommandComment_getNumArgs(CXComment CXC)228 unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
229 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
230 if (!BCC)
231 return 0;
232
233 return BCC->getNumArgs();
234 }
235
clang_BlockCommandComment_getArgText(CXComment CXC,unsigned ArgIdx)236 CXString clang_BlockCommandComment_getArgText(CXComment CXC,
237 unsigned ArgIdx) {
238 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
239 if (!BCC || ArgIdx >= BCC->getNumArgs())
240 return cxstring::createNull();
241
242 return cxstring::createRef(BCC->getArgText(ArgIdx));
243 }
244
clang_BlockCommandComment_getParagraph(CXComment CXC)245 CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
246 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
247 if (!BCC)
248 return createCXComment(NULL, NULL);
249
250 return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
251 }
252
clang_ParamCommandComment_getParamName(CXComment CXC)253 CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
254 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
255 if (!PCC || !PCC->hasParamName())
256 return cxstring::createNull();
257
258 return cxstring::createRef(PCC->getParamNameAsWritten());
259 }
260
clang_ParamCommandComment_isParamIndexValid(CXComment CXC)261 unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
262 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
263 if (!PCC)
264 return false;
265
266 return PCC->isParamIndexValid();
267 }
268
clang_ParamCommandComment_getParamIndex(CXComment CXC)269 unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
270 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
271 if (!PCC || !PCC->isParamIndexValid())
272 return ParamCommandComment::InvalidParamIndex;
273
274 return PCC->getParamIndex();
275 }
276
clang_ParamCommandComment_isDirectionExplicit(CXComment CXC)277 unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
278 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
279 if (!PCC)
280 return false;
281
282 return PCC->isDirectionExplicit();
283 }
284
clang_ParamCommandComment_getDirection(CXComment CXC)285 enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
286 CXComment CXC) {
287 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
288 if (!PCC)
289 return CXCommentParamPassDirection_In;
290
291 switch (PCC->getDirection()) {
292 case ParamCommandComment::In:
293 return CXCommentParamPassDirection_In;
294
295 case ParamCommandComment::Out:
296 return CXCommentParamPassDirection_Out;
297
298 case ParamCommandComment::InOut:
299 return CXCommentParamPassDirection_InOut;
300 }
301 llvm_unreachable("unknown ParamCommandComment::PassDirection");
302 }
303
clang_TParamCommandComment_getParamName(CXComment CXC)304 CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
305 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
306 if (!TPCC || !TPCC->hasParamName())
307 return cxstring::createNull();
308
309 return cxstring::createRef(TPCC->getParamNameAsWritten());
310 }
311
clang_TParamCommandComment_isParamPositionValid(CXComment CXC)312 unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
313 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
314 if (!TPCC)
315 return false;
316
317 return TPCC->isPositionValid();
318 }
319
clang_TParamCommandComment_getDepth(CXComment CXC)320 unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
321 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
322 if (!TPCC || !TPCC->isPositionValid())
323 return 0;
324
325 return TPCC->getDepth();
326 }
327
clang_TParamCommandComment_getIndex(CXComment CXC,unsigned Depth)328 unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
329 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
330 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
331 return 0;
332
333 return TPCC->getIndex(Depth);
334 }
335
clang_VerbatimBlockLineComment_getText(CXComment CXC)336 CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
337 const VerbatimBlockLineComment *VBL =
338 getASTNodeAs<VerbatimBlockLineComment>(CXC);
339 if (!VBL)
340 return cxstring::createNull();
341
342 return cxstring::createRef(VBL->getText());
343 }
344
clang_VerbatimLineComment_getText(CXComment CXC)345 CXString clang_VerbatimLineComment_getText(CXComment CXC) {
346 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
347 if (!VLC)
348 return cxstring::createNull();
349
350 return cxstring::createRef(VLC->getText());
351 }
352
353 } // end extern "C"
354
355 //===----------------------------------------------------------------------===//
356 // Helpers for converting comment AST to HTML.
357 //===----------------------------------------------------------------------===//
358
359 namespace {
360
361 /// This comparison will sort parameters with valid index by index and
362 /// invalid (unresolved) parameters last.
363 class ParamCommandCommentCompareIndex {
364 public:
operator ()(const ParamCommandComment * LHS,const ParamCommandComment * RHS) const365 bool operator()(const ParamCommandComment *LHS,
366 const ParamCommandComment *RHS) const {
367 unsigned LHSIndex = UINT_MAX;
368 unsigned RHSIndex = UINT_MAX;
369 if (LHS->isParamIndexValid())
370 LHSIndex = LHS->getParamIndex();
371 if (RHS->isParamIndexValid())
372 RHSIndex = RHS->getParamIndex();
373
374 return LHSIndex < RHSIndex;
375 }
376 };
377
378 /// This comparison will sort template parameters in the following order:
379 /// \li real template parameters (depth = 1) in index order;
380 /// \li all other names (depth > 1);
381 /// \li unresolved names.
382 class TParamCommandCommentComparePosition {
383 public:
operator ()(const TParamCommandComment * LHS,const TParamCommandComment * RHS) const384 bool operator()(const TParamCommandComment *LHS,
385 const TParamCommandComment *RHS) const {
386 // Sort unresolved names last.
387 if (!LHS->isPositionValid())
388 return false;
389 if (!RHS->isPositionValid())
390 return true;
391
392 if (LHS->getDepth() > 1)
393 return false;
394 if (RHS->getDepth() > 1)
395 return true;
396
397 // Sort template parameters in index order.
398 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
399 return LHS->getIndex(0) < RHS->getIndex(0);
400
401 // Leave all other names in source order.
402 return true;
403 }
404 };
405
406 /// Separate parts of a FullComment.
407 struct FullCommentParts {
408 /// Take a full comment apart and initialize members accordingly.
409 FullCommentParts(const FullComment *C,
410 const CommandTraits &Traits);
411
412 const BlockContentComment *Brief;
413 const BlockContentComment *Headerfile;
414 const ParagraphComment *FirstParagraph;
415 const BlockCommandComment *Returns;
416 SmallVector<const ParamCommandComment *, 8> Params;
417 SmallVector<const TParamCommandComment *, 4> TParams;
418 SmallVector<const BlockContentComment *, 8> MiscBlocks;
419 };
420
FullCommentParts(const FullComment * C,const CommandTraits & Traits)421 FullCommentParts::FullCommentParts(const FullComment *C,
422 const CommandTraits &Traits) :
423 Brief(NULL), Headerfile(NULL), FirstParagraph(NULL), Returns(NULL) {
424 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
425 I != E; ++I) {
426 const Comment *Child = *I;
427 if (!Child)
428 continue;
429 switch (Child->getCommentKind()) {
430 case Comment::NoCommentKind:
431 continue;
432
433 case Comment::ParagraphCommentKind: {
434 const ParagraphComment *PC = cast<ParagraphComment>(Child);
435 if (PC->isWhitespace())
436 break;
437 if (!FirstParagraph)
438 FirstParagraph = PC;
439
440 MiscBlocks.push_back(PC);
441 break;
442 }
443
444 case Comment::BlockCommandCommentKind: {
445 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
446 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
447 if (!Brief && Info->IsBriefCommand) {
448 Brief = BCC;
449 break;
450 }
451 if (!Headerfile && Info->IsHeaderfileCommand) {
452 Headerfile = BCC;
453 break;
454 }
455 if (!Returns && Info->IsReturnsCommand) {
456 Returns = BCC;
457 break;
458 }
459 MiscBlocks.push_back(BCC);
460 break;
461 }
462
463 case Comment::ParamCommandCommentKind: {
464 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
465 if (!PCC->hasParamName())
466 break;
467
468 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
469 break;
470
471 Params.push_back(PCC);
472 break;
473 }
474
475 case Comment::TParamCommandCommentKind: {
476 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
477 if (!TPCC->hasParamName())
478 break;
479
480 if (!TPCC->hasNonWhitespaceParagraph())
481 break;
482
483 TParams.push_back(TPCC);
484 break;
485 }
486
487 case Comment::VerbatimBlockCommentKind:
488 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
489 break;
490
491 case Comment::VerbatimLineCommentKind: {
492 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
493 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
494 if (!Info->IsDeclarationCommand)
495 MiscBlocks.push_back(VLC);
496 break;
497 }
498
499 case Comment::TextCommentKind:
500 case Comment::InlineCommandCommentKind:
501 case Comment::HTMLStartTagCommentKind:
502 case Comment::HTMLEndTagCommentKind:
503 case Comment::VerbatimBlockLineCommentKind:
504 case Comment::FullCommentKind:
505 llvm_unreachable("AST node of this kind can't be a child of "
506 "a FullComment");
507 }
508 }
509
510 // Sort params in order they are declared in the function prototype.
511 // Unresolved parameters are put at the end of the list in the same order
512 // they were seen in the comment.
513 std::stable_sort(Params.begin(), Params.end(),
514 ParamCommandCommentCompareIndex());
515
516 std::stable_sort(TParams.begin(), TParams.end(),
517 TParamCommandCommentComparePosition());
518 }
519
PrintHTMLStartTagComment(const HTMLStartTagComment * C,llvm::raw_svector_ostream & Result)520 void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
521 llvm::raw_svector_ostream &Result) {
522 Result << "<" << C->getTagName();
523
524 if (C->getNumAttrs() != 0) {
525 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
526 Result << " ";
527 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
528 Result << Attr.Name;
529 if (!Attr.Value.empty())
530 Result << "=\"" << Attr.Value << "\"";
531 }
532 }
533
534 if (!C->isSelfClosing())
535 Result << ">";
536 else
537 Result << "/>";
538 }
539
540 class CommentASTToHTMLConverter :
541 public ConstCommentVisitor<CommentASTToHTMLConverter> {
542 public:
543 /// \param Str accumulator for HTML.
CommentASTToHTMLConverter(const FullComment * FC,SmallVectorImpl<char> & Str,const CommandTraits & Traits)544 CommentASTToHTMLConverter(const FullComment *FC,
545 SmallVectorImpl<char> &Str,
546 const CommandTraits &Traits) :
547 FC(FC), Result(Str), Traits(Traits)
548 { }
549
550 // Inline content.
551 void visitTextComment(const TextComment *C);
552 void visitInlineCommandComment(const InlineCommandComment *C);
553 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
554 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
555
556 // Block content.
557 void visitParagraphComment(const ParagraphComment *C);
558 void visitBlockCommandComment(const BlockCommandComment *C);
559 void visitParamCommandComment(const ParamCommandComment *C);
560 void visitTParamCommandComment(const TParamCommandComment *C);
561 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
562 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
563 void visitVerbatimLineComment(const VerbatimLineComment *C);
564
565 void visitFullComment(const FullComment *C);
566
567 // Helpers.
568
569 /// Convert a paragraph that is not a block by itself (an argument to some
570 /// command).
571 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
572
573 void appendToResultWithHTMLEscaping(StringRef S);
574
575 private:
576 const FullComment *FC;
577 /// Output stream for HTML.
578 llvm::raw_svector_ostream Result;
579
580 const CommandTraits &Traits;
581 };
582 } // end unnamed namespace
583
visitTextComment(const TextComment * C)584 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
585 appendToResultWithHTMLEscaping(C->getText());
586 }
587
visitInlineCommandComment(const InlineCommandComment * C)588 void CommentASTToHTMLConverter::visitInlineCommandComment(
589 const InlineCommandComment *C) {
590 // Nothing to render if no arguments supplied.
591 if (C->getNumArgs() == 0)
592 return;
593
594 // Nothing to render if argument is empty.
595 StringRef Arg0 = C->getArgText(0);
596 if (Arg0.empty())
597 return;
598
599 switch (C->getRenderKind()) {
600 case InlineCommandComment::RenderNormal:
601 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
602 appendToResultWithHTMLEscaping(C->getArgText(i));
603 Result << " ";
604 }
605 return;
606
607 case InlineCommandComment::RenderBold:
608 assert(C->getNumArgs() == 1);
609 Result << "<b>";
610 appendToResultWithHTMLEscaping(Arg0);
611 Result << "</b>";
612 return;
613 case InlineCommandComment::RenderMonospaced:
614 assert(C->getNumArgs() == 1);
615 Result << "<tt>";
616 appendToResultWithHTMLEscaping(Arg0);
617 Result<< "</tt>";
618 return;
619 case InlineCommandComment::RenderEmphasized:
620 assert(C->getNumArgs() == 1);
621 Result << "<em>";
622 appendToResultWithHTMLEscaping(Arg0);
623 Result << "</em>";
624 return;
625 }
626 }
627
visitHTMLStartTagComment(const HTMLStartTagComment * C)628 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
629 const HTMLStartTagComment *C) {
630 PrintHTMLStartTagComment(C, Result);
631 }
632
visitHTMLEndTagComment(const HTMLEndTagComment * C)633 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
634 const HTMLEndTagComment *C) {
635 Result << "</" << C->getTagName() << ">";
636 }
637
visitParagraphComment(const ParagraphComment * C)638 void CommentASTToHTMLConverter::visitParagraphComment(
639 const ParagraphComment *C) {
640 if (C->isWhitespace())
641 return;
642
643 Result << "<p>";
644 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
645 I != E; ++I) {
646 visit(*I);
647 }
648 Result << "</p>";
649 }
650
visitBlockCommandComment(const BlockCommandComment * C)651 void CommentASTToHTMLConverter::visitBlockCommandComment(
652 const BlockCommandComment *C) {
653 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
654 if (Info->IsBriefCommand) {
655 Result << "<p class=\"para-brief\">";
656 visitNonStandaloneParagraphComment(C->getParagraph());
657 Result << "</p>";
658 return;
659 }
660 if (Info->IsReturnsCommand) {
661 Result << "<p class=\"para-returns\">"
662 "<span class=\"word-returns\">Returns</span> ";
663 visitNonStandaloneParagraphComment(C->getParagraph());
664 Result << "</p>";
665 return;
666 }
667 // We don't know anything about this command. Just render the paragraph.
668 visit(C->getParagraph());
669 }
670
visitParamCommandComment(const ParamCommandComment * C)671 void CommentASTToHTMLConverter::visitParamCommandComment(
672 const ParamCommandComment *C) {
673 if (C->isParamIndexValid()) {
674 Result << "<dt class=\"param-name-index-"
675 << C->getParamIndex()
676 << "\">";
677 appendToResultWithHTMLEscaping(C->getParamName(FC));
678 } else {
679 Result << "<dt class=\"param-name-index-invalid\">";
680 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
681 }
682 Result << "</dt>";
683
684 if (C->isParamIndexValid()) {
685 Result << "<dd class=\"param-descr-index-"
686 << C->getParamIndex()
687 << "\">";
688 } else
689 Result << "<dd class=\"param-descr-index-invalid\">";
690
691 visitNonStandaloneParagraphComment(C->getParagraph());
692 Result << "</dd>";
693 }
694
visitTParamCommandComment(const TParamCommandComment * C)695 void CommentASTToHTMLConverter::visitTParamCommandComment(
696 const TParamCommandComment *C) {
697 if (C->isPositionValid()) {
698 if (C->getDepth() == 1)
699 Result << "<dt class=\"tparam-name-index-"
700 << C->getIndex(0)
701 << "\">";
702 else
703 Result << "<dt class=\"tparam-name-index-other\">";
704 appendToResultWithHTMLEscaping(C->getParamName(FC));
705 } else {
706 Result << "<dt class=\"tparam-name-index-invalid\">";
707 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
708 }
709
710 Result << "</dt>";
711
712 if (C->isPositionValid()) {
713 if (C->getDepth() == 1)
714 Result << "<dd class=\"tparam-descr-index-"
715 << C->getIndex(0)
716 << "\">";
717 else
718 Result << "<dd class=\"tparam-descr-index-other\">";
719 } else
720 Result << "<dd class=\"tparam-descr-index-invalid\">";
721
722 visitNonStandaloneParagraphComment(C->getParagraph());
723 Result << "</dd>";
724 }
725
visitVerbatimBlockComment(const VerbatimBlockComment * C)726 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
727 const VerbatimBlockComment *C) {
728 unsigned NumLines = C->getNumLines();
729 if (NumLines == 0)
730 return;
731
732 Result << "<pre>";
733 for (unsigned i = 0; i != NumLines; ++i) {
734 appendToResultWithHTMLEscaping(C->getText(i));
735 if (i + 1 != NumLines)
736 Result << '\n';
737 }
738 Result << "</pre>";
739 }
740
visitVerbatimBlockLineComment(const VerbatimBlockLineComment * C)741 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
742 const VerbatimBlockLineComment *C) {
743 llvm_unreachable("should not see this AST node");
744 }
745
visitVerbatimLineComment(const VerbatimLineComment * C)746 void CommentASTToHTMLConverter::visitVerbatimLineComment(
747 const VerbatimLineComment *C) {
748 Result << "<pre>";
749 appendToResultWithHTMLEscaping(C->getText());
750 Result << "</pre>";
751 }
752
visitFullComment(const FullComment * C)753 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
754 FullCommentParts Parts(C, Traits);
755
756 bool FirstParagraphIsBrief = false;
757 if (Parts.Headerfile)
758 visit(Parts.Headerfile);
759 if (Parts.Brief)
760 visit(Parts.Brief);
761 else if (Parts.FirstParagraph) {
762 Result << "<p class=\"para-brief\">";
763 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
764 Result << "</p>";
765 FirstParagraphIsBrief = true;
766 }
767
768 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
769 const Comment *C = Parts.MiscBlocks[i];
770 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
771 continue;
772 visit(C);
773 }
774
775 if (Parts.TParams.size() != 0) {
776 Result << "<dl>";
777 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
778 visit(Parts.TParams[i]);
779 Result << "</dl>";
780 }
781
782 if (Parts.Params.size() != 0) {
783 Result << "<dl>";
784 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
785 visit(Parts.Params[i]);
786 Result << "</dl>";
787 }
788
789 if (Parts.Returns)
790 visit(Parts.Returns);
791
792 Result.flush();
793 }
794
visitNonStandaloneParagraphComment(const ParagraphComment * C)795 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
796 const ParagraphComment *C) {
797 if (!C)
798 return;
799
800 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
801 I != E; ++I) {
802 visit(*I);
803 }
804 }
805
appendToResultWithHTMLEscaping(StringRef S)806 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
807 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
808 const char C = *I;
809 switch (C) {
810 case '&':
811 Result << "&";
812 break;
813 case '<':
814 Result << "<";
815 break;
816 case '>':
817 Result << ">";
818 break;
819 case '"':
820 Result << """;
821 break;
822 case '\'':
823 Result << "'";
824 break;
825 case '/':
826 Result << "/";
827 break;
828 default:
829 Result << C;
830 break;
831 }
832 }
833 }
834
835 extern "C" {
836
clang_HTMLTagComment_getAsString(CXComment CXC)837 CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
838 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
839 if (!HTC)
840 return cxstring::createNull();
841
842 SmallString<128> HTML;
843 CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
844 Converter.visit(HTC);
845 return cxstring::createDup(HTML.str());
846 }
847
clang_FullComment_getAsHTML(CXComment CXC)848 CXString clang_FullComment_getAsHTML(CXComment CXC) {
849 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
850 if (!FC)
851 return cxstring::createNull();
852
853 SmallString<1024> HTML;
854 CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC));
855 Converter.visit(FC);
856 return cxstring::createDup(HTML.str());
857 }
858
859 } // end extern "C"
860
861 namespace {
862 class CommentASTToXMLConverter :
863 public ConstCommentVisitor<CommentASTToXMLConverter> {
864 public:
865 /// \param Str accumulator for XML.
CommentASTToXMLConverter(const FullComment * FC,SmallVectorImpl<char> & Str,const CommandTraits & Traits,const SourceManager & SM,SimpleFormatContext & SFC,unsigned FUID)866 CommentASTToXMLConverter(const FullComment *FC,
867 SmallVectorImpl<char> &Str,
868 const CommandTraits &Traits,
869 const SourceManager &SM,
870 SimpleFormatContext &SFC,
871 unsigned FUID) :
872 FC(FC), Result(Str), Traits(Traits), SM(SM),
873 FormatRewriterContext(SFC),
874 FormatInMemoryUniqueId(FUID) { }
875
876 // Inline content.
877 void visitTextComment(const TextComment *C);
878 void visitInlineCommandComment(const InlineCommandComment *C);
879 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
880 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
881
882 // Block content.
883 void visitParagraphComment(const ParagraphComment *C);
884
885 void appendParagraphCommentWithKind(const ParagraphComment *C,
886 StringRef Kind);
887
888 void visitBlockCommandComment(const BlockCommandComment *C);
889 void visitParamCommandComment(const ParamCommandComment *C);
890 void visitTParamCommandComment(const TParamCommandComment *C);
891 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
892 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
893 void visitVerbatimLineComment(const VerbatimLineComment *C);
894
895 void visitFullComment(const FullComment *C);
896
897 // Helpers.
898 void appendToResultWithXMLEscaping(StringRef S);
899
900 void formatTextOfDeclaration(const DeclInfo *DI,
901 SmallString<128> &Declaration);
902
903 private:
904 const FullComment *FC;
905
906 /// Output stream for XML.
907 llvm::raw_svector_ostream Result;
908
909 const CommandTraits &Traits;
910 const SourceManager &SM;
911 SimpleFormatContext &FormatRewriterContext;
912 unsigned FormatInMemoryUniqueId;
913 };
914
getSourceTextOfDeclaration(const DeclInfo * ThisDecl,SmallVectorImpl<char> & Str)915 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
916 SmallVectorImpl<char> &Str) {
917 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
918 const LangOptions &LangOpts = Context.getLangOpts();
919 llvm::raw_svector_ostream OS(Str);
920 PrintingPolicy PPolicy(LangOpts);
921 PPolicy.PolishForDeclaration = true;
922 PPolicy.TerseOutput = true;
923 ThisDecl->CurrentDecl->print(OS, PPolicy,
924 /*Indentation*/0, /*PrintInstantiation*/false);
925 }
926
formatTextOfDeclaration(const DeclInfo * DI,SmallString<128> & Declaration)927 void CommentASTToXMLConverter::formatTextOfDeclaration(
928 const DeclInfo *DI,
929 SmallString<128> &Declaration) {
930 // FIXME. formatting API expects null terminated input string.
931 // There might be more efficient way of doing this.
932 std::string StringDecl = Declaration.str();
933
934 // Formatter specific code.
935 // Form a unique in memory buffer name.
936 SmallString<128> filename;
937 filename += "xmldecl";
938 filename += llvm::utostr(FormatInMemoryUniqueId);
939 filename += ".xd";
940 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
941 SourceLocation Start =
942 FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0);
943 unsigned Length = Declaration.size();
944
945 std::vector<CharSourceRange>
946 Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
947 ASTContext &Context = DI->CurrentDecl->getASTContext();
948 const LangOptions &LangOpts = Context.getLangOpts();
949 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
950 FormatRewriterContext.Sources, LangOpts);
951 tooling::Replacements Replace =
952 reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
953 applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
954 Declaration = FormatRewriterContext.getRewrittenText(ID);
955 }
956
957 } // end unnamed namespace
958
visitTextComment(const TextComment * C)959 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
960 appendToResultWithXMLEscaping(C->getText());
961 }
962
visitInlineCommandComment(const InlineCommandComment * C)963 void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
964 // Nothing to render if no arguments supplied.
965 if (C->getNumArgs() == 0)
966 return;
967
968 // Nothing to render if argument is empty.
969 StringRef Arg0 = C->getArgText(0);
970 if (Arg0.empty())
971 return;
972
973 switch (C->getRenderKind()) {
974 case InlineCommandComment::RenderNormal:
975 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
976 appendToResultWithXMLEscaping(C->getArgText(i));
977 Result << " ";
978 }
979 return;
980 case InlineCommandComment::RenderBold:
981 assert(C->getNumArgs() == 1);
982 Result << "<bold>";
983 appendToResultWithXMLEscaping(Arg0);
984 Result << "</bold>";
985 return;
986 case InlineCommandComment::RenderMonospaced:
987 assert(C->getNumArgs() == 1);
988 Result << "<monospaced>";
989 appendToResultWithXMLEscaping(Arg0);
990 Result << "</monospaced>";
991 return;
992 case InlineCommandComment::RenderEmphasized:
993 assert(C->getNumArgs() == 1);
994 Result << "<emphasized>";
995 appendToResultWithXMLEscaping(Arg0);
996 Result << "</emphasized>";
997 return;
998 }
999 }
1000
visitHTMLStartTagComment(const HTMLStartTagComment * C)1001 void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
1002 Result << "<rawHTML><![CDATA[";
1003 PrintHTMLStartTagComment(C, Result);
1004 Result << "]]></rawHTML>";
1005 }
1006
visitHTMLEndTagComment(const HTMLEndTagComment * C)1007 void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
1008 Result << "<rawHTML></" << C->getTagName() << "></rawHTML>";
1009 }
1010
visitParagraphComment(const ParagraphComment * C)1011 void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
1012 appendParagraphCommentWithKind(C, StringRef());
1013 }
1014
appendParagraphCommentWithKind(const ParagraphComment * C,StringRef ParagraphKind)1015 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
1016 const ParagraphComment *C,
1017 StringRef ParagraphKind) {
1018 if (C->isWhitespace())
1019 return;
1020
1021 if (ParagraphKind.empty())
1022 Result << "<Para>";
1023 else
1024 Result << "<Para kind=\"" << ParagraphKind << "\">";
1025
1026 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
1027 I != E; ++I) {
1028 visit(*I);
1029 }
1030 Result << "</Para>";
1031 }
1032
visitBlockCommandComment(const BlockCommandComment * C)1033 void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
1034 StringRef ParagraphKind;
1035
1036 switch (C->getCommandID()) {
1037 case CommandTraits::KCI_attention:
1038 case CommandTraits::KCI_author:
1039 case CommandTraits::KCI_authors:
1040 case CommandTraits::KCI_bug:
1041 case CommandTraits::KCI_copyright:
1042 case CommandTraits::KCI_date:
1043 case CommandTraits::KCI_invariant:
1044 case CommandTraits::KCI_note:
1045 case CommandTraits::KCI_post:
1046 case CommandTraits::KCI_pre:
1047 case CommandTraits::KCI_remark:
1048 case CommandTraits::KCI_remarks:
1049 case CommandTraits::KCI_sa:
1050 case CommandTraits::KCI_see:
1051 case CommandTraits::KCI_since:
1052 case CommandTraits::KCI_todo:
1053 case CommandTraits::KCI_version:
1054 case CommandTraits::KCI_warning:
1055 ParagraphKind = C->getCommandName(Traits);
1056 default:
1057 break;
1058 }
1059
1060 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
1061 }
1062
visitParamCommandComment(const ParamCommandComment * C)1063 void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
1064 Result << "<Parameter><Name>";
1065 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC)
1066 : C->getParamNameAsWritten());
1067 Result << "</Name>";
1068
1069 if (C->isParamIndexValid())
1070 Result << "<Index>" << C->getParamIndex() << "</Index>";
1071
1072 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
1073 switch (C->getDirection()) {
1074 case ParamCommandComment::In:
1075 Result << "in";
1076 break;
1077 case ParamCommandComment::Out:
1078 Result << "out";
1079 break;
1080 case ParamCommandComment::InOut:
1081 Result << "in,out";
1082 break;
1083 }
1084 Result << "</Direction><Discussion>";
1085 visit(C->getParagraph());
1086 Result << "</Discussion></Parameter>";
1087 }
1088
visitTParamCommandComment(const TParamCommandComment * C)1089 void CommentASTToXMLConverter::visitTParamCommandComment(
1090 const TParamCommandComment *C) {
1091 Result << "<Parameter><Name>";
1092 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
1093 : C->getParamNameAsWritten());
1094 Result << "</Name>";
1095
1096 if (C->isPositionValid() && C->getDepth() == 1) {
1097 Result << "<Index>" << C->getIndex(0) << "</Index>";
1098 }
1099
1100 Result << "<Discussion>";
1101 visit(C->getParagraph());
1102 Result << "</Discussion></Parameter>";
1103 }
1104
visitVerbatimBlockComment(const VerbatimBlockComment * C)1105 void CommentASTToXMLConverter::visitVerbatimBlockComment(
1106 const VerbatimBlockComment *C) {
1107 unsigned NumLines = C->getNumLines();
1108 if (NumLines == 0)
1109 return;
1110
1111 switch (C->getCommandID()) {
1112 case CommandTraits::KCI_code:
1113 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
1114 break;
1115 default:
1116 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1117 break;
1118 }
1119 for (unsigned i = 0; i != NumLines; ++i) {
1120 appendToResultWithXMLEscaping(C->getText(i));
1121 if (i + 1 != NumLines)
1122 Result << '\n';
1123 }
1124 Result << "</Verbatim>";
1125 }
1126
visitVerbatimBlockLineComment(const VerbatimBlockLineComment * C)1127 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1128 const VerbatimBlockLineComment *C) {
1129 llvm_unreachable("should not see this AST node");
1130 }
1131
visitVerbatimLineComment(const VerbatimLineComment * C)1132 void CommentASTToXMLConverter::visitVerbatimLineComment(
1133 const VerbatimLineComment *C) {
1134 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1135 appendToResultWithXMLEscaping(C->getText());
1136 Result << "</Verbatim>";
1137 }
1138
visitFullComment(const FullComment * C)1139 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
1140 FullCommentParts Parts(C, Traits);
1141
1142 const DeclInfo *DI = C->getDeclInfo();
1143 StringRef RootEndTag;
1144 if (DI) {
1145 switch (DI->getKind()) {
1146 case DeclInfo::OtherKind:
1147 RootEndTag = "</Other>";
1148 Result << "<Other";
1149 break;
1150 case DeclInfo::FunctionKind:
1151 RootEndTag = "</Function>";
1152 Result << "<Function";
1153 switch (DI->TemplateKind) {
1154 case DeclInfo::NotTemplate:
1155 break;
1156 case DeclInfo::Template:
1157 Result << " templateKind=\"template\"";
1158 break;
1159 case DeclInfo::TemplateSpecialization:
1160 Result << " templateKind=\"specialization\"";
1161 break;
1162 case DeclInfo::TemplatePartialSpecialization:
1163 llvm_unreachable("partial specializations of functions "
1164 "are not allowed in C++");
1165 }
1166 if (DI->IsInstanceMethod)
1167 Result << " isInstanceMethod=\"1\"";
1168 if (DI->IsClassMethod)
1169 Result << " isClassMethod=\"1\"";
1170 break;
1171 case DeclInfo::ClassKind:
1172 RootEndTag = "</Class>";
1173 Result << "<Class";
1174 switch (DI->TemplateKind) {
1175 case DeclInfo::NotTemplate:
1176 break;
1177 case DeclInfo::Template:
1178 Result << " templateKind=\"template\"";
1179 break;
1180 case DeclInfo::TemplateSpecialization:
1181 Result << " templateKind=\"specialization\"";
1182 break;
1183 case DeclInfo::TemplatePartialSpecialization:
1184 Result << " templateKind=\"partialSpecialization\"";
1185 break;
1186 }
1187 break;
1188 case DeclInfo::VariableKind:
1189 RootEndTag = "</Variable>";
1190 Result << "<Variable";
1191 break;
1192 case DeclInfo::NamespaceKind:
1193 RootEndTag = "</Namespace>";
1194 Result << "<Namespace";
1195 break;
1196 case DeclInfo::TypedefKind:
1197 RootEndTag = "</Typedef>";
1198 Result << "<Typedef";
1199 break;
1200 case DeclInfo::EnumKind:
1201 RootEndTag = "</Enum>";
1202 Result << "<Enum";
1203 break;
1204 }
1205
1206 {
1207 // Print line and column number.
1208 SourceLocation Loc = DI->CurrentDecl->getLocation();
1209 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1210 FileID FID = LocInfo.first;
1211 unsigned FileOffset = LocInfo.second;
1212
1213 if (!FID.isInvalid()) {
1214 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1215 Result << " file=\"";
1216 appendToResultWithXMLEscaping(FE->getName());
1217 Result << "\"";
1218 }
1219 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1220 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1221 << "\"";
1222 }
1223 }
1224
1225 // Finish the root tag.
1226 Result << ">";
1227
1228 bool FoundName = false;
1229 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
1230 if (DeclarationName DeclName = ND->getDeclName()) {
1231 Result << "<Name>";
1232 std::string Name = DeclName.getAsString();
1233 appendToResultWithXMLEscaping(Name);
1234 FoundName = true;
1235 Result << "</Name>";
1236 }
1237 }
1238 if (!FoundName)
1239 Result << "<Name><anonymous></Name>";
1240
1241 {
1242 // Print USR.
1243 SmallString<128> USR;
1244 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
1245 if (!USR.empty()) {
1246 Result << "<USR>";
1247 appendToResultWithXMLEscaping(USR);
1248 Result << "</USR>";
1249 }
1250 }
1251 } else {
1252 // No DeclInfo -- just emit some root tag and name tag.
1253 RootEndTag = "</Other>";
1254 Result << "<Other><Name>unknown</Name>";
1255 }
1256
1257 if (Parts.Headerfile) {
1258 Result << "<Headerfile>";
1259 visit(Parts.Headerfile);
1260 Result << "</Headerfile>";
1261 }
1262
1263 {
1264 // Pretty-print the declaration.
1265 Result << "<Declaration>";
1266 SmallString<128> Declaration;
1267 getSourceTextOfDeclaration(DI, Declaration);
1268 formatTextOfDeclaration(DI, Declaration);
1269 appendToResultWithXMLEscaping(Declaration);
1270
1271 Result << "</Declaration>";
1272 }
1273
1274 bool FirstParagraphIsBrief = false;
1275 if (Parts.Brief) {
1276 Result << "<Abstract>";
1277 visit(Parts.Brief);
1278 Result << "</Abstract>";
1279 } else if (Parts.FirstParagraph) {
1280 Result << "<Abstract>";
1281 visit(Parts.FirstParagraph);
1282 Result << "</Abstract>";
1283 FirstParagraphIsBrief = true;
1284 }
1285
1286 if (Parts.TParams.size() != 0) {
1287 Result << "<TemplateParameters>";
1288 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1289 visit(Parts.TParams[i]);
1290 Result << "</TemplateParameters>";
1291 }
1292
1293 if (Parts.Params.size() != 0) {
1294 Result << "<Parameters>";
1295 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1296 visit(Parts.Params[i]);
1297 Result << "</Parameters>";
1298 }
1299
1300 if (Parts.Returns) {
1301 Result << "<ResultDiscussion>";
1302 visit(Parts.Returns);
1303 Result << "</ResultDiscussion>";
1304 }
1305
1306 if (DI->CommentDecl->hasAttrs()) {
1307 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
1308 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1309 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1310 if (!AA) {
1311 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1312 if (DA->getMessage().empty())
1313 Result << "<Deprecated/>";
1314 else {
1315 Result << "<Deprecated>";
1316 appendToResultWithXMLEscaping(DA->getMessage());
1317 Result << "</Deprecated>";
1318 }
1319 }
1320 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1321 if (UA->getMessage().empty())
1322 Result << "<Unavailable/>";
1323 else {
1324 Result << "<Unavailable>";
1325 appendToResultWithXMLEscaping(UA->getMessage());
1326 Result << "</Unavailable>";
1327 }
1328 }
1329 continue;
1330 }
1331
1332 // 'availability' attribute.
1333 Result << "<Availability";
1334 StringRef Distribution;
1335 if (AA->getPlatform()) {
1336 Distribution = AvailabilityAttr::getPrettyPlatformName(
1337 AA->getPlatform()->getName());
1338 if (Distribution.empty())
1339 Distribution = AA->getPlatform()->getName();
1340 }
1341 Result << " distribution=\"" << Distribution << "\">";
1342 VersionTuple IntroducedInVersion = AA->getIntroduced();
1343 if (!IntroducedInVersion.empty()) {
1344 Result << "<IntroducedInVersion>"
1345 << IntroducedInVersion.getAsString()
1346 << "</IntroducedInVersion>";
1347 }
1348 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1349 if (!DeprecatedInVersion.empty()) {
1350 Result << "<DeprecatedInVersion>"
1351 << DeprecatedInVersion.getAsString()
1352 << "</DeprecatedInVersion>";
1353 }
1354 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1355 if (!RemovedAfterVersion.empty()) {
1356 Result << "<RemovedAfterVersion>"
1357 << RemovedAfterVersion.getAsString()
1358 << "</RemovedAfterVersion>";
1359 }
1360 StringRef DeprecationSummary = AA->getMessage();
1361 if (!DeprecationSummary.empty()) {
1362 Result << "<DeprecationSummary>";
1363 appendToResultWithXMLEscaping(DeprecationSummary);
1364 Result << "</DeprecationSummary>";
1365 }
1366 if (AA->getUnavailable())
1367 Result << "<Unavailable/>";
1368 Result << "</Availability>";
1369 }
1370 }
1371
1372 {
1373 bool StartTagEmitted = false;
1374 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1375 const Comment *C = Parts.MiscBlocks[i];
1376 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1377 continue;
1378 if (!StartTagEmitted) {
1379 Result << "<Discussion>";
1380 StartTagEmitted = true;
1381 }
1382 visit(C);
1383 }
1384 if (StartTagEmitted)
1385 Result << "</Discussion>";
1386 }
1387
1388 Result << RootEndTag;
1389
1390 Result.flush();
1391 }
1392
appendToResultWithXMLEscaping(StringRef S)1393 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1394 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1395 const char C = *I;
1396 switch (C) {
1397 case '&':
1398 Result << "&";
1399 break;
1400 case '<':
1401 Result << "<";
1402 break;
1403 case '>':
1404 Result << ">";
1405 break;
1406 case '"':
1407 Result << """;
1408 break;
1409 case '\'':
1410 Result << "'";
1411 break;
1412 default:
1413 Result << C;
1414 break;
1415 }
1416 }
1417 }
1418
1419 extern "C" {
1420
clang_FullComment_getAsXML(CXComment CXC)1421 CXString clang_FullComment_getAsXML(CXComment CXC) {
1422 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1423 if (!FC)
1424 return cxstring::createNull();
1425 ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext();
1426 CXTranslationUnit TU = CXC.TranslationUnit;
1427 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
1428
1429 if (!TU->FormatContext) {
1430 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
1431 } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) {
1432 // Delete after some number of iterators, so the buffers don't grow
1433 // too large.
1434 delete TU->FormatContext;
1435 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
1436 }
1437
1438 SmallString<1024> XML;
1439 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM,
1440 *TU->FormatContext,
1441 TU->FormatInMemoryUniqueId++);
1442 Converter.visit(FC);
1443 return cxstring::createDup(XML.str());
1444 }
1445
1446 } // end extern "C"
1447
1448