blob: 0bb6ab78782ac02f40a4740a7f2f1adc4994d87a [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 Gribenkod1db1252012-08-09 17:33:20 +000021#include "clang/AST/CommentCommandTraits.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000022#include "clang/AST/Decl.h"
23#include "clang/Frontend/ASTUnit.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000024
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000025#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000026#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko3e63d332012-07-21 01:47:43 +000027#include "llvm/Support/raw_ostream.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000028
Dmitri Gribenko221a6d72012-07-30 17:49:32 +000029#include <climits>
30
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000031using namespace clang;
32using namespace clang::cxstring;
33using namespace clang::comments;
34using namespace clang::cxcomment;
35
36extern "C" {
37
38enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
39 const Comment *C = getASTNode(CXC);
40 if (!C)
41 return CXComment_Null;
42
43 switch (C->getCommentKind()) {
44 case Comment::NoCommentKind:
45 return CXComment_Null;
46
47 case Comment::TextCommentKind:
48 return CXComment_Text;
49
50 case Comment::InlineCommandCommentKind:
51 return CXComment_InlineCommand;
52
53 case Comment::HTMLStartTagCommentKind:
54 return CXComment_HTMLStartTag;
55
56 case Comment::HTMLEndTagCommentKind:
57 return CXComment_HTMLEndTag;
58
59 case Comment::ParagraphCommentKind:
60 return CXComment_Paragraph;
61
62 case Comment::BlockCommandCommentKind:
63 return CXComment_BlockCommand;
64
65 case Comment::ParamCommandCommentKind:
66 return CXComment_ParamCommand;
67
Dmitri Gribenko96b09862012-07-31 22:37:06 +000068 case Comment::TParamCommandCommentKind:
69 return CXComment_TParamCommand;
70
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000071 case Comment::VerbatimBlockCommentKind:
72 return CXComment_VerbatimBlockCommand;
73
74 case Comment::VerbatimBlockLineCommentKind:
75 return CXComment_VerbatimBlockLine;
76
77 case Comment::VerbatimLineCommentKind:
78 return CXComment_VerbatimLine;
79
80 case Comment::FullCommentKind:
81 return CXComment_FullComment;
82 }
83 llvm_unreachable("unknown CommentKind");
84}
85
86unsigned clang_Comment_getNumChildren(CXComment CXC) {
87 const Comment *C = getASTNode(CXC);
88 if (!C)
89 return 0;
90
91 return C->child_count();
92}
93
94CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
95 const Comment *C = getASTNode(CXC);
96 if (!C || ChildIdx >= C->child_count())
97 return createCXComment(NULL);
98
99 return createCXComment(*(C->child_begin() + ChildIdx));
100}
101
102unsigned clang_Comment_isWhitespace(CXComment CXC) {
103 const Comment *C = getASTNode(CXC);
104 if (!C)
105 return false;
106
107 if (const TextComment *TC = dyn_cast<TextComment>(C))
108 return TC->isWhitespace();
109
110 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
111 return PC->isWhitespace();
112
113 return false;
114}
115
116unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
117 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
118 if (!ICC)
119 return false;
120
121 return ICC->hasTrailingNewline();
122}
123
124CXString clang_TextComment_getText(CXComment CXC) {
125 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
126 if (!TC)
127 return createCXString((const char *) 0);
128
129 return createCXString(TC->getText(), /*DupString=*/ false);
130}
131
132CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
133 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
134 if (!ICC)
135 return createCXString((const char *) 0);
136
137 return createCXString(ICC->getCommandName(), /*DupString=*/ false);
138}
139
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000140enum CXCommentInlineCommandRenderKind
141clang_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
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000162unsigned 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
170CXString clang_InlineCommandComment_getArgText(CXComment CXC,
171 unsigned ArgIdx) {
172 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
173 if (!ICC || ArgIdx >= ICC->getNumArgs())
174 return createCXString((const char *) 0);
175
176 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
177}
178
179CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
180 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
181 if (!HTC)
182 return createCXString((const char *) 0);
183
184 return createCXString(HTC->getTagName(), /*DupString=*/ false);
185}
186
187unsigned 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
195unsigned 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
203CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
204 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
205 if (!HST || AttrIdx >= HST->getNumAttrs())
206 return createCXString((const char *) 0);
207
208 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
209}
210
211CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
212 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
213 if (!HST || AttrIdx >= HST->getNumAttrs())
214 return createCXString((const char *) 0);
215
216 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
217}
218
219CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
220 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
221 if (!BCC)
222 return createCXString((const char *) 0);
223
224 return createCXString(BCC->getCommandName(), /*DupString=*/ false);
225}
226
227unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
228 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
229 if (!BCC)
230 return 0;
231
232 return BCC->getNumArgs();
233}
234
235CXString clang_BlockCommandComment_getArgText(CXComment CXC,
236 unsigned ArgIdx) {
237 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
238 if (!BCC || ArgIdx >= BCC->getNumArgs())
239 return createCXString((const char *) 0);
240
241 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
242}
243
244CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
245 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
246 if (!BCC)
247 return createCXComment(NULL);
248
249 return createCXComment(BCC->getParagraph());
250}
251
252CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
253 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko1f8c5292012-07-23 19:41:49 +0000254 if (!PCC || !PCC->hasParamName())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000255 return createCXString((const char *) 0);
256
257 return createCXString(PCC->getParamName(), /*DupString=*/ false);
258}
259
260unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
261 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
262 if (!PCC)
263 return false;
264
265 return PCC->isParamIndexValid();
266}
267
268unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
269 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000270 if (!PCC || !PCC->isParamIndexValid())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000271 return ParamCommandComment::InvalidParamIndex;
272
273 return PCC->getParamIndex();
274}
275
276unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
277 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
278 if (!PCC)
279 return false;
280
281 return PCC->isDirectionExplicit();
282}
283
284enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
285 CXComment CXC) {
286 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
287 if (!PCC)
288 return CXCommentParamPassDirection_In;
289
290 switch (PCC->getDirection()) {
291 case ParamCommandComment::In:
292 return CXCommentParamPassDirection_In;
293
294 case ParamCommandComment::Out:
295 return CXCommentParamPassDirection_Out;
296
297 case ParamCommandComment::InOut:
298 return CXCommentParamPassDirection_InOut;
299 }
300 llvm_unreachable("unknown ParamCommandComment::PassDirection");
301}
302
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000303CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
304 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
305 if (!TPCC || !TPCC->hasParamName())
306 return createCXString((const char *) 0);
307
308 return createCXString(TPCC->getParamName(), /*DupString=*/ false);
309}
310
311unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
312 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
313 if (!TPCC)
314 return false;
315
316 return TPCC->isPositionValid();
317}
318
319unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
320 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
321 if (!TPCC || !TPCC->isPositionValid())
322 return 0;
323
324 return TPCC->getDepth();
325}
326
327unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
328 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
329 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
330 return 0;
331
332 return TPCC->getIndex(Depth);
333}
334
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000335CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
336 const VerbatimBlockLineComment *VBL =
337 getASTNodeAs<VerbatimBlockLineComment>(CXC);
338 if (!VBL)
339 return createCXString((const char *) 0);
340
341 return createCXString(VBL->getText(), /*DupString=*/ false);
342}
343
344CXString clang_VerbatimLineComment_getText(CXComment CXC) {
345 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
346 if (!VLC)
347 return createCXString((const char *) 0);
348
349 return createCXString(VLC->getText(), /*DupString=*/ false);
350}
351
352} // end extern "C"
353
354//===----------------------------------------------------------------------===//
355// Helpers for converting comment AST to HTML.
356//===----------------------------------------------------------------------===//
357
358namespace {
359
Dmitri Gribenkoe5db09c2012-07-30 19:47:34 +0000360/// This comparison will sort parameters with valid index by index and
361/// invalid (unresolved) parameters last.
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000362class ParamCommandCommentCompareIndex {
363public:
364 bool operator()(const ParamCommandComment *LHS,
365 const ParamCommandComment *RHS) const {
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000366 unsigned LHSIndex = UINT_MAX;
367 unsigned RHSIndex = UINT_MAX;
368 if (LHS->isParamIndexValid())
369 LHSIndex = LHS->getParamIndex();
370 if (RHS->isParamIndexValid())
371 RHSIndex = RHS->getParamIndex();
372
373 return LHSIndex < RHSIndex;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000374 }
375};
376
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000377/// This comparison will sort template parameters in the following order:
378/// \li real template parameters (depth = 1) in index order;
379/// \li all other names (depth > 1);
380/// \li unresolved names.
381class TParamCommandCommentComparePosition {
382public:
383 bool operator()(const TParamCommandComment *LHS,
384 const TParamCommandComment *RHS) const {
385 // Sort unresolved names last.
386 if (!LHS->isPositionValid())
387 return false;
388 if (!RHS->isPositionValid())
389 return true;
390
391 if (LHS->getDepth() > 1)
392 return false;
393 if (RHS->getDepth() > 1)
394 return true;
395
396 // Sort template parameters in index order.
397 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
398 return LHS->getIndex(0) < RHS->getIndex(0);
399
400 // Leave all other names in source order.
401 return true;
402 }
403};
404
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000405/// Separate parts of a FullComment.
406struct FullCommentParts {
407 /// Take a full comment apart and initialize members accordingly.
408 FullCommentParts(const FullComment *C);
409
410 const BlockContentComment *Brief;
411 const ParagraphComment *FirstParagraph;
412 const BlockCommandComment *Returns;
413 SmallVector<const ParamCommandComment *, 8> Params;
414 SmallVector<const TParamCommandComment *, 4> TParams;
415 SmallVector<const BlockContentComment *, 8> MiscBlocks;
416};
417
418FullCommentParts::FullCommentParts(const FullComment *C) :
419 Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
Dmitri Gribenkod1db1252012-08-09 17:33:20 +0000420 const CommandTraits Traits;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000421 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
422 I != E; ++I) {
423 const Comment *Child = *I;
424 if (!Child)
425 continue;
426 switch (Child->getCommentKind()) {
427 case Comment::NoCommentKind:
428 continue;
429
430 case Comment::ParagraphCommentKind: {
431 const ParagraphComment *PC = cast<ParagraphComment>(Child);
432 if (PC->isWhitespace())
433 break;
434 if (!FirstParagraph)
435 FirstParagraph = PC;
436
437 MiscBlocks.push_back(PC);
438 break;
439 }
440
441 case Comment::BlockCommandCommentKind: {
442 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
443 StringRef CommandName = BCC->getCommandName();
Dmitri Gribenkod1db1252012-08-09 17:33:20 +0000444 if (!Brief && Traits.isBriefCommand(CommandName)) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000445 Brief = BCC;
446 break;
447 }
Dmitri Gribenkod1db1252012-08-09 17:33:20 +0000448 if (!Returns && Traits.isReturnsCommand(CommandName)) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000449 Returns = BCC;
450 break;
451 }
452 MiscBlocks.push_back(BCC);
453 break;
454 }
455
456 case Comment::ParamCommandCommentKind: {
457 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
458 if (!PCC->hasParamName())
459 break;
460
461 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
462 break;
463
464 Params.push_back(PCC);
465 break;
466 }
467
468 case Comment::TParamCommandCommentKind: {
469 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
470 if (!TPCC->hasParamName())
471 break;
472
473 if (!TPCC->hasNonWhitespaceParagraph())
474 break;
475
476 TParams.push_back(TPCC);
477 break;
478 }
479
480 case Comment::VerbatimBlockCommentKind:
481 case Comment::VerbatimLineCommentKind:
482 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
483 break;
484
485 case Comment::TextCommentKind:
486 case Comment::InlineCommandCommentKind:
487 case Comment::HTMLStartTagCommentKind:
488 case Comment::HTMLEndTagCommentKind:
489 case Comment::VerbatimBlockLineCommentKind:
490 case Comment::FullCommentKind:
491 llvm_unreachable("AST node of this kind can't be a child of "
492 "a FullComment");
493 }
494 }
495
496 // Sort params in order they are declared in the function prototype.
497 // Unresolved parameters are put at the end of the list in the same order
498 // they were seen in the comment.
499 std::stable_sort(Params.begin(), Params.end(),
500 ParamCommandCommentCompareIndex());
501
502 std::stable_sort(TParams.begin(), TParams.end(),
503 TParamCommandCommentComparePosition());
504}
505
506void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
507 llvm::raw_svector_ostream &Result) {
508 Result << "<" << C->getTagName();
509
510 if (C->getNumAttrs() != 0) {
511 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
512 Result << " ";
513 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
514 Result << Attr.Name;
515 if (!Attr.Value.empty())
516 Result << "=\"" << Attr.Value << "\"";
517 }
518 }
519
520 if (!C->isSelfClosing())
521 Result << ">";
522 else
523 Result << "/>";
524}
525
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000526class CommentASTToHTMLConverter :
527 public ConstCommentVisitor<CommentASTToHTMLConverter> {
528public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000529 /// \param Str accumulator for HTML.
530 CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000531
532 // Inline content.
533 void visitTextComment(const TextComment *C);
534 void visitInlineCommandComment(const InlineCommandComment *C);
535 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
536 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
537
538 // Block content.
539 void visitParagraphComment(const ParagraphComment *C);
540 void visitBlockCommandComment(const BlockCommandComment *C);
541 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000542 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000543 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
544 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
545 void visitVerbatimLineComment(const VerbatimLineComment *C);
546
547 void visitFullComment(const FullComment *C);
548
549 // Helpers.
550
551 /// Convert a paragraph that is not a block by itself (an argument to some
552 /// command).
553 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
554
555 void appendToResultWithHTMLEscaping(StringRef S);
556
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000557private:
Dmitri Gribenkod1db1252012-08-09 17:33:20 +0000558 const CommandTraits Traits;
559
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000560 /// Output stream for HTML.
561 llvm::raw_svector_ostream Result;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000562};
563} // end unnamed namespace
564
565void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
566 appendToResultWithHTMLEscaping(C->getText());
567}
568
569void CommentASTToHTMLConverter::visitInlineCommandComment(
570 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000571 // Nothing to render if no arguments supplied.
572 if (C->getNumArgs() == 0)
573 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000574
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000575 // Nothing to render if argument is empty.
576 StringRef Arg0 = C->getArgText(0);
577 if (Arg0.empty())
578 return;
579
580 switch (C->getRenderKind()) {
581 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000582 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
583 appendToResultWithHTMLEscaping(C->getArgText(i));
584 Result << " ";
585 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000586 return;
587
588 case InlineCommandComment::RenderBold:
589 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000590 Result << "<b>";
591 appendToResultWithHTMLEscaping(Arg0);
592 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000593 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000594 case InlineCommandComment::RenderMonospaced:
595 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000596 Result << "<tt>";
597 appendToResultWithHTMLEscaping(Arg0);
598 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000599 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000600 case InlineCommandComment::RenderEmphasized:
601 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000602 Result << "<em>";
603 appendToResultWithHTMLEscaping(Arg0);
604 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000605 return;
606 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000607}
608
609void CommentASTToHTMLConverter::visitHTMLStartTagComment(
610 const HTMLStartTagComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000611 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000612}
613
614void CommentASTToHTMLConverter::visitHTMLEndTagComment(
615 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000616 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000617}
618
619void CommentASTToHTMLConverter::visitParagraphComment(
620 const ParagraphComment *C) {
621 if (C->isWhitespace())
622 return;
623
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000624 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000625 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
626 I != E; ++I) {
627 visit(*I);
628 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000629 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000630}
631
632void CommentASTToHTMLConverter::visitBlockCommandComment(
633 const BlockCommandComment *C) {
634 StringRef CommandName = C->getCommandName();
Dmitri Gribenkod1db1252012-08-09 17:33:20 +0000635 if (Traits.isBriefCommand(CommandName)) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000636 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000637 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000638 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000639 return;
640 }
Dmitri Gribenkod1db1252012-08-09 17:33:20 +0000641 if (Traits.isReturnsCommand(CommandName)) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000642 Result << "<p class=\"para-returns\">"
643 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000644 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000645 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000646 return;
647 }
648 // We don't know anything about this command. Just render the paragraph.
649 visit(C->getParagraph());
650}
651
652void CommentASTToHTMLConverter::visitParamCommandComment(
653 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000654 if (C->isParamIndexValid()) {
655 Result << "<dt class=\"param-name-index-"
656 << C->getParamIndex()
657 << "\">";
658 } else
659 Result << "<dt class=\"param-name-index-invalid\">";
660
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000661 appendToResultWithHTMLEscaping(C->getParamName());
662 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000663
664 if (C->isParamIndexValid()) {
665 Result << "<dd class=\"param-descr-index-"
666 << C->getParamIndex()
667 << "\">";
668 } else
669 Result << "<dd class=\"param-descr-index-invalid\">";
670
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000671 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000672 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000673}
674
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000675void CommentASTToHTMLConverter::visitTParamCommandComment(
676 const TParamCommandComment *C) {
677 if (C->isPositionValid()) {
678 if (C->getDepth() == 1)
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000679 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000680 << C->getIndex(0)
681 << "\">";
682 else
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000683 Result << "<dt class=\"tparam-name-index-other\">";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000684 } else
685 Result << "<dt class=\"tparam-name-index-invalid\">";
686
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000687 appendToResultWithHTMLEscaping(C->getParamName());
688 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000689
690 if (C->isPositionValid()) {
691 if (C->getDepth() == 1)
692 Result << "<dd class=\"tparam-descr-index-"
693 << C->getIndex(0)
694 << "\">";
695 else
696 Result << "<dd class=\"tparam-descr-index-other\">";
697 } else
698 Result << "<dd class=\"tparam-descr-index-invalid\">";
699
700 visitNonStandaloneParagraphComment(C->getParagraph());
701 Result << "</dd>";
702}
703
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000704void CommentASTToHTMLConverter::visitVerbatimBlockComment(
705 const VerbatimBlockComment *C) {
706 unsigned NumLines = C->getNumLines();
707 if (NumLines == 0)
708 return;
709
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000710 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000711 for (unsigned i = 0; i != NumLines; ++i) {
712 appendToResultWithHTMLEscaping(C->getText(i));
713 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000714 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000715 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000716 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000717}
718
719void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
720 const VerbatimBlockLineComment *C) {
721 llvm_unreachable("should not see this AST node");
722}
723
724void CommentASTToHTMLConverter::visitVerbatimLineComment(
725 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000726 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000727 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000728 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000729}
730
731void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000732 FullCommentParts Parts(C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000733
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000734 bool FirstParagraphIsBrief = false;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000735 if (Parts.Brief)
736 visit(Parts.Brief);
737 else if (Parts.FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000738 Result << "<p class=\"para-brief\">";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000739 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000740 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000741 FirstParagraphIsBrief = true;
742 }
743
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000744 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
745 const Comment *C = Parts.MiscBlocks[i];
746 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000747 continue;
748 visit(C);
749 }
750
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000751 if (Parts.TParams.size() != 0) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000752 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000753 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
754 visit(Parts.TParams[i]);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000755 Result << "</dl>";
756 }
757
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000758 if (Parts.Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000759 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000760 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
761 visit(Parts.Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000762 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000763 }
764
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000765 if (Parts.Returns)
766 visit(Parts.Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000767
768 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000769}
770
771void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
772 const ParagraphComment *C) {
773 if (!C)
774 return;
775
776 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
777 I != E; ++I) {
778 visit(*I);
779 }
780}
781
782void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000783 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
784 const char C = *I;
785 switch (C) {
786 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000787 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000788 break;
789 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000790 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000791 break;
792 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000793 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000794 break;
795 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000796 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000797 break;
798 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000799 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000800 break;
801 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000802 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000803 break;
804 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000805 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000806 break;
807 }
808 }
809}
810
811extern "C" {
812
813CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
814 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
815 if (!HTC)
816 return createCXString((const char *) 0);
817
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000818 SmallString<128> HTML;
819 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000820 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000821 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000822}
823
824CXString clang_FullComment_getAsHTML(CXComment CXC) {
825 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
826 if (!FC)
827 return createCXString((const char *) 0);
828
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000829 SmallString<1024> HTML;
830 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000831 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000832 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000833}
834
835} // end extern "C"
836
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000837namespace {
838class CommentASTToXMLConverter :
839 public ConstCommentVisitor<CommentASTToXMLConverter> {
840public:
841 /// \param Str accumulator for XML.
842 CommentASTToXMLConverter(const SourceManager &SM,
843 SmallVectorImpl<char> &Str) :
844 SM(SM), Result(Str) { }
845
846 // Inline content.
847 void visitTextComment(const TextComment *C);
848 void visitInlineCommandComment(const InlineCommandComment *C);
849 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
850 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
851
852 // Block content.
853 void visitParagraphComment(const ParagraphComment *C);
854 void visitBlockCommandComment(const BlockCommandComment *C);
855 void visitParamCommandComment(const ParamCommandComment *C);
856 void visitTParamCommandComment(const TParamCommandComment *C);
857 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
858 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
859 void visitVerbatimLineComment(const VerbatimLineComment *C);
860
861 void visitFullComment(const FullComment *C);
862
863 // Helpers.
864 void appendToResultWithXMLEscaping(StringRef S);
865
866private:
867 const SourceManager &SM;
Dmitri Gribenkod1db1252012-08-09 17:33:20 +0000868
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000869 /// Output stream for XML.
870 llvm::raw_svector_ostream Result;
871};
872} // end unnamed namespace
873
874void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
875 appendToResultWithXMLEscaping(C->getText());
876}
877
878void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
879 // Nothing to render if no arguments supplied.
880 if (C->getNumArgs() == 0)
881 return;
882
883 // Nothing to render if argument is empty.
884 StringRef Arg0 = C->getArgText(0);
885 if (Arg0.empty())
886 return;
887
888 switch (C->getRenderKind()) {
889 case InlineCommandComment::RenderNormal:
890 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
891 appendToResultWithXMLEscaping(C->getArgText(i));
892 Result << " ";
893 }
894 return;
895 case InlineCommandComment::RenderBold:
896 assert(C->getNumArgs() == 1);
897 Result << "<bold>";
898 appendToResultWithXMLEscaping(Arg0);
899 Result << "</bold>";
900 return;
901 case InlineCommandComment::RenderMonospaced:
902 assert(C->getNumArgs() == 1);
903 Result << "<monospaced>";
904 appendToResultWithXMLEscaping(Arg0);
905 Result << "</monospaced>";
906 return;
907 case InlineCommandComment::RenderEmphasized:
908 assert(C->getNumArgs() == 1);
909 Result << "<emphasized>";
910 appendToResultWithXMLEscaping(Arg0);
911 Result << "</emphasized>";
912 return;
913 }
914}
915
916void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
917 Result << "<rawHTML><![CDATA[";
918 PrintHTMLStartTagComment(C, Result);
919 Result << "]]></rawHTML>";
920}
921
922void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
923 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
924}
925
926void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
927 if (C->isWhitespace())
928 return;
929
930 Result << "<Para>";
931 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
932 I != E; ++I) {
933 visit(*I);
934 }
935 Result << "</Para>";
936}
937
938void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
939 visit(C->getParagraph());
940}
941
942void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
943 Result << "<Parameter><Name>";
944 appendToResultWithXMLEscaping(C->getParamName());
945 Result << "</Name>";
946
947 if (C->isParamIndexValid())
948 Result << "<Index>" << C->getParamIndex() << "</Index>";
949
950 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
951 switch (C->getDirection()) {
952 case ParamCommandComment::In:
953 Result << "in";
954 break;
955 case ParamCommandComment::Out:
956 Result << "out";
957 break;
958 case ParamCommandComment::InOut:
959 Result << "in,out";
960 break;
961 }
962 Result << "</Direction><Discussion>";
963 visit(C->getParagraph());
964 Result << "</Discussion></Parameter>";
965}
966
967void CommentASTToXMLConverter::visitTParamCommandComment(
968 const TParamCommandComment *C) {
969 Result << "<Parameter><Name>";
970 appendToResultWithXMLEscaping(C->getParamName());
971 Result << "</Name>";
972
973 if (C->isPositionValid() && C->getDepth() == 1) {
974 Result << "<Index>" << C->getIndex(0) << "</Index>";
975 }
976
977 Result << "<Discussion>";
978 visit(C->getParagraph());
979 Result << "</Discussion></Parameter>";
980}
981
982void CommentASTToXMLConverter::visitVerbatimBlockComment(
983 const VerbatimBlockComment *C) {
984 unsigned NumLines = C->getNumLines();
985 if (NumLines == 0)
986 return;
987
988 Result << llvm::StringSwitch<const char *>(C->getCommandName())
Dmitri Gribenko6cd44202012-08-08 22:10:24 +0000989 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
990 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000991 for (unsigned i = 0; i != NumLines; ++i) {
992 appendToResultWithXMLEscaping(C->getText(i));
993 if (i + 1 != NumLines)
994 Result << '\n';
995 }
996 Result << "</Verbatim>";
997}
998
999void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1000 const VerbatimBlockLineComment *C) {
1001 llvm_unreachable("should not see this AST node");
1002}
1003
1004void CommentASTToXMLConverter::visitVerbatimLineComment(
1005 const VerbatimLineComment *C) {
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001006 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001007 appendToResultWithXMLEscaping(C->getText());
1008 Result << "</Verbatim>";
1009}
1010
1011void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
1012 FullCommentParts Parts(C);
1013
1014 const DeclInfo *DI = C->getDeclInfo();
1015 StringRef RootEndTag;
1016 if (DI) {
1017 switch (DI->getKind()) {
1018 case DeclInfo::OtherKind:
1019 RootEndTag = "</Other>";
1020 Result << "<Other";
1021 break;
1022 case DeclInfo::FunctionKind:
1023 RootEndTag = "</Function>";
1024 Result << "<Function";
1025 switch (DI->TemplateKind) {
1026 case DeclInfo::NotTemplate:
1027 break;
1028 case DeclInfo::Template:
1029 Result << " templateKind=\"template\"";
1030 break;
1031 case DeclInfo::TemplateSpecialization:
1032 Result << " templateKind=\"specialization\"";
1033 break;
1034 case DeclInfo::TemplatePartialSpecialization:
1035 llvm_unreachable("partial specializations of functions "
1036 "are not allowed in C++");
1037 }
1038 if (DI->IsInstanceMethod)
1039 Result << " isInstanceMethod=\"1\"";
1040 if (DI->IsClassMethod)
1041 Result << " isClassMethod=\"1\"";
1042 break;
1043 case DeclInfo::ClassKind:
1044 RootEndTag = "</Class>";
1045 Result << "<Class";
1046 switch (DI->TemplateKind) {
1047 case DeclInfo::NotTemplate:
1048 break;
1049 case DeclInfo::Template:
1050 Result << " templateKind=\"template\"";
1051 break;
1052 case DeclInfo::TemplateSpecialization:
1053 Result << " templateKind=\"specialization\"";
1054 break;
1055 case DeclInfo::TemplatePartialSpecialization:
1056 Result << " templateKind=\"partialSpecialization\"";
1057 break;
1058 }
1059 break;
1060 case DeclInfo::VariableKind:
1061 RootEndTag = "</Variable>";
1062 Result << "<Variable";
1063 break;
1064 case DeclInfo::NamespaceKind:
1065 RootEndTag = "</Namespace>";
1066 Result << "<Namespace";
1067 break;
1068 case DeclInfo::TypedefKind:
1069 RootEndTag = "</Typedef>";
1070 Result << "<Typedef";
1071 break;
Dmitri Gribenkocff339a2012-08-07 18:59:04 +00001072 case DeclInfo::EnumKind:
1073 RootEndTag = "</Enum>";
1074 Result << "<Enum";
1075 break;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001076 }
1077
1078 {
1079 // Print line and column number.
1080 SourceLocation Loc = DI->ThisDecl->getLocation();
1081 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1082 FileID FID = LocInfo.first;
1083 unsigned FileOffset = LocInfo.second;
1084
1085 if (!FID.isInvalid()) {
1086 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1087 Result << " file=\"";
1088 appendToResultWithXMLEscaping(FE->getName());
1089 Result << "\"";
1090 }
1091 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1092 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1093 << "\"";
1094 }
1095 }
1096
1097 // Finish the root tag.
1098 Result << ">";
1099
1100 bool FoundName = false;
1101 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->ThisDecl)) {
1102 if (DeclarationName DeclName = ND->getDeclName()) {
1103 Result << "<Name>";
1104 std::string Name = DeclName.getAsString();
1105 appendToResultWithXMLEscaping(Name);
1106 FoundName = true;
1107 Result << "</Name>";
1108 }
1109 }
1110 if (!FoundName)
1111 Result << "<Name>&lt;anonymous&gt;</Name>";
1112
1113 {
1114 // Print USR.
1115 SmallString<128> USR;
1116 cxcursor::getDeclCursorUSR(DI->ThisDecl, USR);
1117 if (!USR.empty()) {
1118 Result << "<USR>";
1119 appendToResultWithXMLEscaping(USR);
1120 Result << "</USR>";
1121 }
1122 }
1123 } else {
1124 // No DeclInfo -- just emit some root tag and name tag.
1125 RootEndTag = "</Other>";
1126 Result << "<Other><Name>unknown</Name>";
1127 }
1128
1129 bool FirstParagraphIsBrief = false;
1130 if (Parts.Brief) {
1131 Result << "<Abstract>";
1132 visit(Parts.Brief);
1133 Result << "</Abstract>";
1134 } else if (Parts.FirstParagraph) {
1135 Result << "<Abstract>";
1136 visit(Parts.FirstParagraph);
1137 Result << "</Abstract>";
1138 FirstParagraphIsBrief = true;
1139 }
1140
1141 if (Parts.TParams.size() != 0) {
1142 Result << "<TemplateParameters>";
1143 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1144 visit(Parts.TParams[i]);
1145 Result << "</TemplateParameters>";
1146 }
1147
1148 if (Parts.Params.size() != 0) {
1149 Result << "<Parameters>";
1150 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1151 visit(Parts.Params[i]);
1152 Result << "</Parameters>";
1153 }
1154
1155 if (Parts.Returns) {
1156 Result << "<ResultDiscussion>";
1157 visit(Parts.Returns);
1158 Result << "</ResultDiscussion>";
1159 }
1160
1161 {
1162 bool StartTagEmitted = false;
1163 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1164 const Comment *C = Parts.MiscBlocks[i];
1165 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1166 continue;
1167 if (!StartTagEmitted) {
1168 Result << "<Discussion>";
1169 StartTagEmitted = true;
1170 }
1171 visit(C);
1172 }
1173 if (StartTagEmitted)
1174 Result << "</Discussion>";
1175 }
1176
1177 Result << RootEndTag;
1178
1179 Result.flush();
1180}
1181
1182void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1183 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1184 const char C = *I;
1185 switch (C) {
1186 case '&':
1187 Result << "&amp;";
1188 break;
1189 case '<':
1190 Result << "&lt;";
1191 break;
1192 case '>':
1193 Result << "&gt;";
1194 break;
1195 case '"':
1196 Result << "&quot;";
1197 break;
1198 case '\'':
1199 Result << "&apos;";
1200 break;
1201 default:
1202 Result << C;
1203 break;
1204 }
1205 }
1206}
1207
1208extern "C" {
1209
1210CXString clang_FullComment_getAsXML(CXTranslationUnit TU, CXComment CXC) {
1211 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1212 if (!FC)
1213 return createCXString((const char *) 0);
1214
1215 SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1216
1217 SmallString<1024> XML;
1218 CommentASTToXMLConverter Converter(SM, XML);
1219 Converter.visit(FC);
1220 return createCXString(XML.str(), /* DupString = */ true);
1221}
1222
1223} // end extern "C"
1224