blob: 4e7bf7695134fcb493d33418fa0d977712a07b31 [file] [log] [blame]
Dmitri Gribenkoae99b752012-07-20 21:34:34 +00001//===- 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 "CXString.h"
16#include "CXComment.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000017#include "CXCursor.h"
18#include "CXTranslationUnit.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000019
20#include "clang/AST/CommentVisitor.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000021#include "clang/AST/Decl.h"
22#include "clang/Frontend/ASTUnit.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000023
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000024#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000025#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko3e63d332012-07-21 01:47:43 +000026#include "llvm/Support/raw_ostream.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000027
Dmitri Gribenko221a6d72012-07-30 17:49:32 +000028#include <climits>
29
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000030using namespace clang;
31using namespace clang::cxstring;
32using namespace clang::comments;
33using namespace clang::cxcomment;
34
35extern "C" {
36
37enum 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
Dmitri Gribenko96b09862012-07-31 22:37:06 +000067 case Comment::TParamCommandCommentKind:
68 return CXComment_TParamCommand;
69
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000070 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
85unsigned 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
93CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
94 const Comment *C = getASTNode(CXC);
95 if (!C || ChildIdx >= C->child_count())
96 return createCXComment(NULL);
97
98 return createCXComment(*(C->child_begin() + ChildIdx));
99}
100
101unsigned 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
115unsigned 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
123CXString clang_TextComment_getText(CXComment CXC) {
124 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
125 if (!TC)
126 return createCXString((const char *) 0);
127
128 return createCXString(TC->getText(), /*DupString=*/ false);
129}
130
131CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
132 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
133 if (!ICC)
134 return createCXString((const char *) 0);
135
136 return createCXString(ICC->getCommandName(), /*DupString=*/ false);
137}
138
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000139enum CXCommentInlineCommandRenderKind
140clang_InlineCommandComment_getRenderKind(CXComment CXC) {
141 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
142 if (!ICC)
143 return CXCommentInlineCommandRenderKind_Normal;
144
145 switch (ICC->getRenderKind()) {
146 case InlineCommandComment::RenderNormal:
147 return CXCommentInlineCommandRenderKind_Normal;
148
149 case InlineCommandComment::RenderBold:
150 return CXCommentInlineCommandRenderKind_Bold;
151
152 case InlineCommandComment::RenderMonospaced:
153 return CXCommentInlineCommandRenderKind_Monospaced;
154
155 case InlineCommandComment::RenderEmphasized:
156 return CXCommentInlineCommandRenderKind_Emphasized;
157 }
158 llvm_unreachable("unknown InlineCommandComment::RenderKind");
159}
160
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000161unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
162 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
163 if (!ICC)
164 return 0;
165
166 return ICC->getNumArgs();
167}
168
169CXString clang_InlineCommandComment_getArgText(CXComment CXC,
170 unsigned ArgIdx) {
171 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
172 if (!ICC || ArgIdx >= ICC->getNumArgs())
173 return createCXString((const char *) 0);
174
175 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
176}
177
178CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
179 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
180 if (!HTC)
181 return createCXString((const char *) 0);
182
183 return createCXString(HTC->getTagName(), /*DupString=*/ false);
184}
185
186unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
187 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
188 if (!HST)
189 return false;
190
191 return HST->isSelfClosing();
192}
193
194unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
195 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
196 if (!HST)
197 return 0;
198
199 return HST->getNumAttrs();
200}
201
202CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
203 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
204 if (!HST || AttrIdx >= HST->getNumAttrs())
205 return createCXString((const char *) 0);
206
207 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
208}
209
210CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
211 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
212 if (!HST || AttrIdx >= HST->getNumAttrs())
213 return createCXString((const char *) 0);
214
215 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
216}
217
218CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
219 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
220 if (!BCC)
221 return createCXString((const char *) 0);
222
223 return createCXString(BCC->getCommandName(), /*DupString=*/ false);
224}
225
226unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
227 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
228 if (!BCC)
229 return 0;
230
231 return BCC->getNumArgs();
232}
233
234CXString clang_BlockCommandComment_getArgText(CXComment CXC,
235 unsigned ArgIdx) {
236 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
237 if (!BCC || ArgIdx >= BCC->getNumArgs())
238 return createCXString((const char *) 0);
239
240 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
241}
242
243CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
244 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
245 if (!BCC)
246 return createCXComment(NULL);
247
248 return createCXComment(BCC->getParagraph());
249}
250
251CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
252 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko1f8c5292012-07-23 19:41:49 +0000253 if (!PCC || !PCC->hasParamName())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000254 return createCXString((const char *) 0);
255
256 return createCXString(PCC->getParamName(), /*DupString=*/ false);
257}
258
259unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
260 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
261 if (!PCC)
262 return false;
263
264 return PCC->isParamIndexValid();
265}
266
267unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
268 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000269 if (!PCC || !PCC->isParamIndexValid())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000270 return ParamCommandComment::InvalidParamIndex;
271
272 return PCC->getParamIndex();
273}
274
275unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
276 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
277 if (!PCC)
278 return false;
279
280 return PCC->isDirectionExplicit();
281}
282
283enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
284 CXComment CXC) {
285 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
286 if (!PCC)
287 return CXCommentParamPassDirection_In;
288
289 switch (PCC->getDirection()) {
290 case ParamCommandComment::In:
291 return CXCommentParamPassDirection_In;
292
293 case ParamCommandComment::Out:
294 return CXCommentParamPassDirection_Out;
295
296 case ParamCommandComment::InOut:
297 return CXCommentParamPassDirection_InOut;
298 }
299 llvm_unreachable("unknown ParamCommandComment::PassDirection");
300}
301
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000302CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
303 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
304 if (!TPCC || !TPCC->hasParamName())
305 return createCXString((const char *) 0);
306
307 return createCXString(TPCC->getParamName(), /*DupString=*/ false);
308}
309
310unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
311 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
312 if (!TPCC)
313 return false;
314
315 return TPCC->isPositionValid();
316}
317
318unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
319 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
320 if (!TPCC || !TPCC->isPositionValid())
321 return 0;
322
323 return TPCC->getDepth();
324}
325
326unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
327 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
328 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
329 return 0;
330
331 return TPCC->getIndex(Depth);
332}
333
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000334CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
335 const VerbatimBlockLineComment *VBL =
336 getASTNodeAs<VerbatimBlockLineComment>(CXC);
337 if (!VBL)
338 return createCXString((const char *) 0);
339
340 return createCXString(VBL->getText(), /*DupString=*/ false);
341}
342
343CXString clang_VerbatimLineComment_getText(CXComment CXC) {
344 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
345 if (!VLC)
346 return createCXString((const char *) 0);
347
348 return createCXString(VLC->getText(), /*DupString=*/ false);
349}
350
351} // end extern "C"
352
353//===----------------------------------------------------------------------===//
354// Helpers for converting comment AST to HTML.
355//===----------------------------------------------------------------------===//
356
357namespace {
358
Dmitri Gribenkoe5db09c2012-07-30 19:47:34 +0000359/// This comparison will sort parameters with valid index by index and
360/// invalid (unresolved) parameters last.
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000361class ParamCommandCommentCompareIndex {
362public:
363 bool operator()(const ParamCommandComment *LHS,
364 const ParamCommandComment *RHS) const {
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000365 unsigned LHSIndex = UINT_MAX;
366 unsigned RHSIndex = UINT_MAX;
367 if (LHS->isParamIndexValid())
368 LHSIndex = LHS->getParamIndex();
369 if (RHS->isParamIndexValid())
370 RHSIndex = RHS->getParamIndex();
371
372 return LHSIndex < RHSIndex;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000373 }
374};
375
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000376/// This comparison will sort template parameters in the following order:
377/// \li real template parameters (depth = 1) in index order;
378/// \li all other names (depth > 1);
379/// \li unresolved names.
380class TParamCommandCommentComparePosition {
381public:
382 bool operator()(const TParamCommandComment *LHS,
383 const TParamCommandComment *RHS) const {
384 // Sort unresolved names last.
385 if (!LHS->isPositionValid())
386 return false;
387 if (!RHS->isPositionValid())
388 return true;
389
390 if (LHS->getDepth() > 1)
391 return false;
392 if (RHS->getDepth() > 1)
393 return true;
394
395 // Sort template parameters in index order.
396 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
397 return LHS->getIndex(0) < RHS->getIndex(0);
398
399 // Leave all other names in source order.
400 return true;
401 }
402};
403
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000404/// Separate parts of a FullComment.
405struct FullCommentParts {
406 /// Take a full comment apart and initialize members accordingly.
407 FullCommentParts(const FullComment *C);
408
409 const BlockContentComment *Brief;
410 const ParagraphComment *FirstParagraph;
411 const BlockCommandComment *Returns;
412 SmallVector<const ParamCommandComment *, 8> Params;
413 SmallVector<const TParamCommandComment *, 4> TParams;
414 SmallVector<const BlockContentComment *, 8> MiscBlocks;
415};
416
417FullCommentParts::FullCommentParts(const FullComment *C) :
418 Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
419 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
420 I != E; ++I) {
421 const Comment *Child = *I;
422 if (!Child)
423 continue;
424 switch (Child->getCommentKind()) {
425 case Comment::NoCommentKind:
426 continue;
427
428 case Comment::ParagraphCommentKind: {
429 const ParagraphComment *PC = cast<ParagraphComment>(Child);
430 if (PC->isWhitespace())
431 break;
432 if (!FirstParagraph)
433 FirstParagraph = PC;
434
435 MiscBlocks.push_back(PC);
436 break;
437 }
438
439 case Comment::BlockCommandCommentKind: {
440 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
441 StringRef CommandName = BCC->getCommandName();
442 if (!Brief && (CommandName == "brief" || CommandName == "short")) {
443 Brief = BCC;
444 break;
445 }
446 if (!Returns && (CommandName == "returns" || CommandName == "return")) {
447 Returns = BCC;
448 break;
449 }
450 MiscBlocks.push_back(BCC);
451 break;
452 }
453
454 case Comment::ParamCommandCommentKind: {
455 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
456 if (!PCC->hasParamName())
457 break;
458
459 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
460 break;
461
462 Params.push_back(PCC);
463 break;
464 }
465
466 case Comment::TParamCommandCommentKind: {
467 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
468 if (!TPCC->hasParamName())
469 break;
470
471 if (!TPCC->hasNonWhitespaceParagraph())
472 break;
473
474 TParams.push_back(TPCC);
475 break;
476 }
477
478 case Comment::VerbatimBlockCommentKind:
479 case Comment::VerbatimLineCommentKind:
480 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
481 break;
482
483 case Comment::TextCommentKind:
484 case Comment::InlineCommandCommentKind:
485 case Comment::HTMLStartTagCommentKind:
486 case Comment::HTMLEndTagCommentKind:
487 case Comment::VerbatimBlockLineCommentKind:
488 case Comment::FullCommentKind:
489 llvm_unreachable("AST node of this kind can't be a child of "
490 "a FullComment");
491 }
492 }
493
494 // Sort params in order they are declared in the function prototype.
495 // Unresolved parameters are put at the end of the list in the same order
496 // they were seen in the comment.
497 std::stable_sort(Params.begin(), Params.end(),
498 ParamCommandCommentCompareIndex());
499
500 std::stable_sort(TParams.begin(), TParams.end(),
501 TParamCommandCommentComparePosition());
502}
503
504void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
505 llvm::raw_svector_ostream &Result) {
506 Result << "<" << C->getTagName();
507
508 if (C->getNumAttrs() != 0) {
509 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
510 Result << " ";
511 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
512 Result << Attr.Name;
513 if (!Attr.Value.empty())
514 Result << "=\"" << Attr.Value << "\"";
515 }
516 }
517
518 if (!C->isSelfClosing())
519 Result << ">";
520 else
521 Result << "/>";
522}
523
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000524class CommentASTToHTMLConverter :
525 public ConstCommentVisitor<CommentASTToHTMLConverter> {
526public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000527 /// \param Str accumulator for HTML.
528 CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000529
530 // Inline content.
531 void visitTextComment(const TextComment *C);
532 void visitInlineCommandComment(const InlineCommandComment *C);
533 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
534 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
535
536 // Block content.
537 void visitParagraphComment(const ParagraphComment *C);
538 void visitBlockCommandComment(const BlockCommandComment *C);
539 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000540 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000541 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
542 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
543 void visitVerbatimLineComment(const VerbatimLineComment *C);
544
545 void visitFullComment(const FullComment *C);
546
547 // Helpers.
548
549 /// Convert a paragraph that is not a block by itself (an argument to some
550 /// command).
551 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
552
553 void appendToResultWithHTMLEscaping(StringRef S);
554
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000555private:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000556 /// Output stream for HTML.
557 llvm::raw_svector_ostream Result;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000558};
559} // end unnamed namespace
560
561void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
562 appendToResultWithHTMLEscaping(C->getText());
563}
564
565void CommentASTToHTMLConverter::visitInlineCommandComment(
566 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000567 // Nothing to render if no arguments supplied.
568 if (C->getNumArgs() == 0)
569 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000570
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000571 // Nothing to render if argument is empty.
572 StringRef Arg0 = C->getArgText(0);
573 if (Arg0.empty())
574 return;
575
576 switch (C->getRenderKind()) {
577 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000578 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
579 appendToResultWithHTMLEscaping(C->getArgText(i));
580 Result << " ";
581 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000582 return;
583
584 case InlineCommandComment::RenderBold:
585 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000586 Result << "<b>";
587 appendToResultWithHTMLEscaping(Arg0);
588 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000589 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000590 case InlineCommandComment::RenderMonospaced:
591 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000592 Result << "<tt>";
593 appendToResultWithHTMLEscaping(Arg0);
594 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000595 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000596 case InlineCommandComment::RenderEmphasized:
597 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000598 Result << "<em>";
599 appendToResultWithHTMLEscaping(Arg0);
600 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000601 return;
602 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000603}
604
605void CommentASTToHTMLConverter::visitHTMLStartTagComment(
606 const HTMLStartTagComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000607 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000608}
609
610void CommentASTToHTMLConverter::visitHTMLEndTagComment(
611 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000612 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000613}
614
615void CommentASTToHTMLConverter::visitParagraphComment(
616 const ParagraphComment *C) {
617 if (C->isWhitespace())
618 return;
619
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000620 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000621 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
622 I != E; ++I) {
623 visit(*I);
624 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000625 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000626}
627
628void CommentASTToHTMLConverter::visitBlockCommandComment(
629 const BlockCommandComment *C) {
630 StringRef CommandName = C->getCommandName();
631 if (CommandName == "brief" || CommandName == "short") {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000632 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000633 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000634 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000635 return;
636 }
Dmitri Gribenko2c6b00e2012-07-25 17:14:58 +0000637 if (CommandName == "returns" || CommandName == "return" ||
638 CommandName == "result") {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000639 Result << "<p class=\"para-returns\">"
640 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000641 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000642 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000643 return;
644 }
645 // We don't know anything about this command. Just render the paragraph.
646 visit(C->getParagraph());
647}
648
649void CommentASTToHTMLConverter::visitParamCommandComment(
650 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000651 if (C->isParamIndexValid()) {
652 Result << "<dt class=\"param-name-index-"
653 << C->getParamIndex()
654 << "\">";
655 } else
656 Result << "<dt class=\"param-name-index-invalid\">";
657
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000658 appendToResultWithHTMLEscaping(C->getParamName());
659 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000660
661 if (C->isParamIndexValid()) {
662 Result << "<dd class=\"param-descr-index-"
663 << C->getParamIndex()
664 << "\">";
665 } else
666 Result << "<dd class=\"param-descr-index-invalid\">";
667
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000668 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000669 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000670}
671
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000672void CommentASTToHTMLConverter::visitTParamCommandComment(
673 const TParamCommandComment *C) {
674 if (C->isPositionValid()) {
675 if (C->getDepth() == 1)
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000676 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000677 << C->getIndex(0)
678 << "\">";
679 else
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000680 Result << "<dt class=\"tparam-name-index-other\">";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000681 } else
682 Result << "<dt class=\"tparam-name-index-invalid\">";
683
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000684 appendToResultWithHTMLEscaping(C->getParamName());
685 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000686
687 if (C->isPositionValid()) {
688 if (C->getDepth() == 1)
689 Result << "<dd class=\"tparam-descr-index-"
690 << C->getIndex(0)
691 << "\">";
692 else
693 Result << "<dd class=\"tparam-descr-index-other\">";
694 } else
695 Result << "<dd class=\"tparam-descr-index-invalid\">";
696
697 visitNonStandaloneParagraphComment(C->getParagraph());
698 Result << "</dd>";
699}
700
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000701void CommentASTToHTMLConverter::visitVerbatimBlockComment(
702 const VerbatimBlockComment *C) {
703 unsigned NumLines = C->getNumLines();
704 if (NumLines == 0)
705 return;
706
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000707 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000708 for (unsigned i = 0; i != NumLines; ++i) {
709 appendToResultWithHTMLEscaping(C->getText(i));
710 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000711 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000712 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000713 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000714}
715
716void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
717 const VerbatimBlockLineComment *C) {
718 llvm_unreachable("should not see this AST node");
719}
720
721void CommentASTToHTMLConverter::visitVerbatimLineComment(
722 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000723 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000724 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000725 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000726}
727
728void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000729 FullCommentParts Parts(C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000730
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000731 bool FirstParagraphIsBrief = false;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000732 if (Parts.Brief)
733 visit(Parts.Brief);
734 else if (Parts.FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000735 Result << "<p class=\"para-brief\">";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000736 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000737 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000738 FirstParagraphIsBrief = true;
739 }
740
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000741 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
742 const Comment *C = Parts.MiscBlocks[i];
743 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000744 continue;
745 visit(C);
746 }
747
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000748 if (Parts.TParams.size() != 0) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000749 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000750 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
751 visit(Parts.TParams[i]);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000752 Result << "</dl>";
753 }
754
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000755 if (Parts.Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000756 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000757 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
758 visit(Parts.Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000759 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000760 }
761
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000762 if (Parts.Returns)
763 visit(Parts.Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000764
765 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000766}
767
768void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
769 const ParagraphComment *C) {
770 if (!C)
771 return;
772
773 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
774 I != E; ++I) {
775 visit(*I);
776 }
777}
778
779void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000780 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
781 const char C = *I;
782 switch (C) {
783 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000784 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000785 break;
786 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000787 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000788 break;
789 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000790 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000791 break;
792 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000793 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000794 break;
795 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000796 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000797 break;
798 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000799 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000800 break;
801 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000802 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000803 break;
804 }
805 }
806}
807
808extern "C" {
809
810CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
811 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
812 if (!HTC)
813 return createCXString((const char *) 0);
814
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000815 SmallString<128> HTML;
816 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000817 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000818 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000819}
820
821CXString clang_FullComment_getAsHTML(CXComment CXC) {
822 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
823 if (!FC)
824 return createCXString((const char *) 0);
825
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000826 SmallString<1024> HTML;
827 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000828 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000829 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000830}
831
832} // end extern "C"
833
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000834namespace {
835class CommentASTToXMLConverter :
836 public ConstCommentVisitor<CommentASTToXMLConverter> {
837public:
838 /// \param Str accumulator for XML.
839 CommentASTToXMLConverter(const SourceManager &SM,
840 SmallVectorImpl<char> &Str) :
841 SM(SM), Result(Str) { }
842
843 // Inline content.
844 void visitTextComment(const TextComment *C);
845 void visitInlineCommandComment(const InlineCommandComment *C);
846 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
847 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
848
849 // Block content.
850 void visitParagraphComment(const ParagraphComment *C);
851 void visitBlockCommandComment(const BlockCommandComment *C);
852 void visitParamCommandComment(const ParamCommandComment *C);
853 void visitTParamCommandComment(const TParamCommandComment *C);
854 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
855 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
856 void visitVerbatimLineComment(const VerbatimLineComment *C);
857
858 void visitFullComment(const FullComment *C);
859
860 // Helpers.
861 void appendToResultWithXMLEscaping(StringRef S);
862
863private:
864 const SourceManager &SM;
865 /// Output stream for XML.
866 llvm::raw_svector_ostream Result;
867};
868} // end unnamed namespace
869
870void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
871 appendToResultWithXMLEscaping(C->getText());
872}
873
874void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
875 // Nothing to render if no arguments supplied.
876 if (C->getNumArgs() == 0)
877 return;
878
879 // Nothing to render if argument is empty.
880 StringRef Arg0 = C->getArgText(0);
881 if (Arg0.empty())
882 return;
883
884 switch (C->getRenderKind()) {
885 case InlineCommandComment::RenderNormal:
886 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
887 appendToResultWithXMLEscaping(C->getArgText(i));
888 Result << " ";
889 }
890 return;
891 case InlineCommandComment::RenderBold:
892 assert(C->getNumArgs() == 1);
893 Result << "<bold>";
894 appendToResultWithXMLEscaping(Arg0);
895 Result << "</bold>";
896 return;
897 case InlineCommandComment::RenderMonospaced:
898 assert(C->getNumArgs() == 1);
899 Result << "<monospaced>";
900 appendToResultWithXMLEscaping(Arg0);
901 Result << "</monospaced>";
902 return;
903 case InlineCommandComment::RenderEmphasized:
904 assert(C->getNumArgs() == 1);
905 Result << "<emphasized>";
906 appendToResultWithXMLEscaping(Arg0);
907 Result << "</emphasized>";
908 return;
909 }
910}
911
912void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
913 Result << "<rawHTML><![CDATA[";
914 PrintHTMLStartTagComment(C, Result);
915 Result << "]]></rawHTML>";
916}
917
918void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
919 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
920}
921
922void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
923 if (C->isWhitespace())
924 return;
925
926 Result << "<Para>";
927 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
928 I != E; ++I) {
929 visit(*I);
930 }
931 Result << "</Para>";
932}
933
934void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
935 visit(C->getParagraph());
936}
937
938void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
939 Result << "<Parameter><Name>";
940 appendToResultWithXMLEscaping(C->getParamName());
941 Result << "</Name>";
942
943 if (C->isParamIndexValid())
944 Result << "<Index>" << C->getParamIndex() << "</Index>";
945
946 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
947 switch (C->getDirection()) {
948 case ParamCommandComment::In:
949 Result << "in";
950 break;
951 case ParamCommandComment::Out:
952 Result << "out";
953 break;
954 case ParamCommandComment::InOut:
955 Result << "in,out";
956 break;
957 }
958 Result << "</Direction><Discussion>";
959 visit(C->getParagraph());
960 Result << "</Discussion></Parameter>";
961}
962
963void CommentASTToXMLConverter::visitTParamCommandComment(
964 const TParamCommandComment *C) {
965 Result << "<Parameter><Name>";
966 appendToResultWithXMLEscaping(C->getParamName());
967 Result << "</Name>";
968
969 if (C->isPositionValid() && C->getDepth() == 1) {
970 Result << "<Index>" << C->getIndex(0) << "</Index>";
971 }
972
973 Result << "<Discussion>";
974 visit(C->getParagraph());
975 Result << "</Discussion></Parameter>";
976}
977
978void CommentASTToXMLConverter::visitVerbatimBlockComment(
979 const VerbatimBlockComment *C) {
980 unsigned NumLines = C->getNumLines();
981 if (NumLines == 0)
982 return;
983
984 Result << llvm::StringSwitch<const char *>(C->getCommandName())
985 .Case("code", "<Verbatim kind=\"code\">")
986 .Default("<Verbatim kind=\"verbatim\">");
987 for (unsigned i = 0; i != NumLines; ++i) {
988 appendToResultWithXMLEscaping(C->getText(i));
989 if (i + 1 != NumLines)
990 Result << '\n';
991 }
992 Result << "</Verbatim>";
993}
994
995void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
996 const VerbatimBlockLineComment *C) {
997 llvm_unreachable("should not see this AST node");
998}
999
1000void CommentASTToXMLConverter::visitVerbatimLineComment(
1001 const VerbatimLineComment *C) {
1002 Result << "<Verbatim kind=\"verbatim\">";
1003 appendToResultWithXMLEscaping(C->getText());
1004 Result << "</Verbatim>";
1005}
1006
1007void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
1008 FullCommentParts Parts(C);
1009
1010 const DeclInfo *DI = C->getDeclInfo();
1011 StringRef RootEndTag;
1012 if (DI) {
1013 switch (DI->getKind()) {
1014 case DeclInfo::OtherKind:
1015 RootEndTag = "</Other>";
1016 Result << "<Other";
1017 break;
1018 case DeclInfo::FunctionKind:
1019 RootEndTag = "</Function>";
1020 Result << "<Function";
1021 switch (DI->TemplateKind) {
1022 case DeclInfo::NotTemplate:
1023 break;
1024 case DeclInfo::Template:
1025 Result << " templateKind=\"template\"";
1026 break;
1027 case DeclInfo::TemplateSpecialization:
1028 Result << " templateKind=\"specialization\"";
1029 break;
1030 case DeclInfo::TemplatePartialSpecialization:
1031 llvm_unreachable("partial specializations of functions "
1032 "are not allowed in C++");
1033 }
1034 if (DI->IsInstanceMethod)
1035 Result << " isInstanceMethod=\"1\"";
1036 if (DI->IsClassMethod)
1037 Result << " isClassMethod=\"1\"";
1038 break;
1039 case DeclInfo::ClassKind:
1040 RootEndTag = "</Class>";
1041 Result << "<Class";
1042 switch (DI->TemplateKind) {
1043 case DeclInfo::NotTemplate:
1044 break;
1045 case DeclInfo::Template:
1046 Result << " templateKind=\"template\"";
1047 break;
1048 case DeclInfo::TemplateSpecialization:
1049 Result << " templateKind=\"specialization\"";
1050 break;
1051 case DeclInfo::TemplatePartialSpecialization:
1052 Result << " templateKind=\"partialSpecialization\"";
1053 break;
1054 }
1055 break;
1056 case DeclInfo::VariableKind:
1057 RootEndTag = "</Variable>";
1058 Result << "<Variable";
1059 break;
1060 case DeclInfo::NamespaceKind:
1061 RootEndTag = "</Namespace>";
1062 Result << "<Namespace";
1063 break;
1064 case DeclInfo::TypedefKind:
1065 RootEndTag = "</Typedef>";
1066 Result << "<Typedef";
1067 break;
Dmitri Gribenkocff339a2012-08-07 18:59:04 +00001068 case DeclInfo::EnumKind:
1069 RootEndTag = "</Enum>";
1070 Result << "<Enum";
1071 break;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001072 }
1073
1074 {
1075 // Print line and column number.
1076 SourceLocation Loc = DI->ThisDecl->getLocation();
1077 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1078 FileID FID = LocInfo.first;
1079 unsigned FileOffset = LocInfo.second;
1080
1081 if (!FID.isInvalid()) {
1082 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1083 Result << " file=\"";
1084 appendToResultWithXMLEscaping(FE->getName());
1085 Result << "\"";
1086 }
1087 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1088 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1089 << "\"";
1090 }
1091 }
1092
1093 // Finish the root tag.
1094 Result << ">";
1095
1096 bool FoundName = false;
1097 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->ThisDecl)) {
1098 if (DeclarationName DeclName = ND->getDeclName()) {
1099 Result << "<Name>";
1100 std::string Name = DeclName.getAsString();
1101 appendToResultWithXMLEscaping(Name);
1102 FoundName = true;
1103 Result << "</Name>";
1104 }
1105 }
1106 if (!FoundName)
1107 Result << "<Name>&lt;anonymous&gt;</Name>";
1108
1109 {
1110 // Print USR.
1111 SmallString<128> USR;
1112 cxcursor::getDeclCursorUSR(DI->ThisDecl, USR);
1113 if (!USR.empty()) {
1114 Result << "<USR>";
1115 appendToResultWithXMLEscaping(USR);
1116 Result << "</USR>";
1117 }
1118 }
1119 } else {
1120 // No DeclInfo -- just emit some root tag and name tag.
1121 RootEndTag = "</Other>";
1122 Result << "<Other><Name>unknown</Name>";
1123 }
1124
1125 bool FirstParagraphIsBrief = false;
1126 if (Parts.Brief) {
1127 Result << "<Abstract>";
1128 visit(Parts.Brief);
1129 Result << "</Abstract>";
1130 } else if (Parts.FirstParagraph) {
1131 Result << "<Abstract>";
1132 visit(Parts.FirstParagraph);
1133 Result << "</Abstract>";
1134 FirstParagraphIsBrief = true;
1135 }
1136
1137 if (Parts.TParams.size() != 0) {
1138 Result << "<TemplateParameters>";
1139 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1140 visit(Parts.TParams[i]);
1141 Result << "</TemplateParameters>";
1142 }
1143
1144 if (Parts.Params.size() != 0) {
1145 Result << "<Parameters>";
1146 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1147 visit(Parts.Params[i]);
1148 Result << "</Parameters>";
1149 }
1150
1151 if (Parts.Returns) {
1152 Result << "<ResultDiscussion>";
1153 visit(Parts.Returns);
1154 Result << "</ResultDiscussion>";
1155 }
1156
1157 {
1158 bool StartTagEmitted = false;
1159 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1160 const Comment *C = Parts.MiscBlocks[i];
1161 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1162 continue;
1163 if (!StartTagEmitted) {
1164 Result << "<Discussion>";
1165 StartTagEmitted = true;
1166 }
1167 visit(C);
1168 }
1169 if (StartTagEmitted)
1170 Result << "</Discussion>";
1171 }
1172
1173 Result << RootEndTag;
1174
1175 Result.flush();
1176}
1177
1178void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1179 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1180 const char C = *I;
1181 switch (C) {
1182 case '&':
1183 Result << "&amp;";
1184 break;
1185 case '<':
1186 Result << "&lt;";
1187 break;
1188 case '>':
1189 Result << "&gt;";
1190 break;
1191 case '"':
1192 Result << "&quot;";
1193 break;
1194 case '\'':
1195 Result << "&apos;";
1196 break;
1197 default:
1198 Result << C;
1199 break;
1200 }
1201 }
1202}
1203
1204extern "C" {
1205
1206CXString clang_FullComment_getAsXML(CXTranslationUnit TU, CXComment CXC) {
1207 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1208 if (!FC)
1209 return createCXString((const char *) 0);
1210
1211 SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1212
1213 SmallString<1024> XML;
1214 CommentASTToXMLConverter Converter(SM, XML);
1215 Converter.visit(FC);
1216 return createCXString(XML.str(), /* DupString = */ true);
1217}
1218
1219} // end extern "C"
1220