blob: c5c9ca8cf38397a55cc8e498430bf62d1e4a2ba5 [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:
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000481 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
482 break;
483
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000484 case Comment::VerbatimLineCommentKind: {
485 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
486 if (!Traits.isDeclarationCommand(VLC->getCommandName()))
487 MiscBlocks.push_back(VLC);
488 break;
489 }
490
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000491 case Comment::TextCommentKind:
492 case Comment::InlineCommandCommentKind:
493 case Comment::HTMLStartTagCommentKind:
494 case Comment::HTMLEndTagCommentKind:
495 case Comment::VerbatimBlockLineCommentKind:
496 case Comment::FullCommentKind:
497 llvm_unreachable("AST node of this kind can't be a child of "
498 "a FullComment");
499 }
500 }
501
502 // Sort params in order they are declared in the function prototype.
503 // Unresolved parameters are put at the end of the list in the same order
504 // they were seen in the comment.
505 std::stable_sort(Params.begin(), Params.end(),
506 ParamCommandCommentCompareIndex());
507
508 std::stable_sort(TParams.begin(), TParams.end(),
509 TParamCommandCommentComparePosition());
510}
511
512void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
513 llvm::raw_svector_ostream &Result) {
514 Result << "<" << C->getTagName();
515
516 if (C->getNumAttrs() != 0) {
517 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
518 Result << " ";
519 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
520 Result << Attr.Name;
521 if (!Attr.Value.empty())
522 Result << "=\"" << Attr.Value << "\"";
523 }
524 }
525
526 if (!C->isSelfClosing())
527 Result << ">";
528 else
529 Result << "/>";
530}
531
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000532class CommentASTToHTMLConverter :
533 public ConstCommentVisitor<CommentASTToHTMLConverter> {
534public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000535 /// \param Str accumulator for HTML.
536 CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000537
538 // Inline content.
539 void visitTextComment(const TextComment *C);
540 void visitInlineCommandComment(const InlineCommandComment *C);
541 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
542 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
543
544 // Block content.
545 void visitParagraphComment(const ParagraphComment *C);
546 void visitBlockCommandComment(const BlockCommandComment *C);
547 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000548 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000549 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
550 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
551 void visitVerbatimLineComment(const VerbatimLineComment *C);
552
553 void visitFullComment(const FullComment *C);
554
555 // Helpers.
556
557 /// Convert a paragraph that is not a block by itself (an argument to some
558 /// command).
559 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
560
561 void appendToResultWithHTMLEscaping(StringRef S);
562
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000563private:
Dmitri Gribenkod1db1252012-08-09 17:33:20 +0000564 const CommandTraits Traits;
565
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000566 /// Output stream for HTML.
567 llvm::raw_svector_ostream Result;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000568};
569} // end unnamed namespace
570
571void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
572 appendToResultWithHTMLEscaping(C->getText());
573}
574
575void CommentASTToHTMLConverter::visitInlineCommandComment(
576 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000577 // Nothing to render if no arguments supplied.
578 if (C->getNumArgs() == 0)
579 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000580
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000581 // Nothing to render if argument is empty.
582 StringRef Arg0 = C->getArgText(0);
583 if (Arg0.empty())
584 return;
585
586 switch (C->getRenderKind()) {
587 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000588 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
589 appendToResultWithHTMLEscaping(C->getArgText(i));
590 Result << " ";
591 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000592 return;
593
594 case InlineCommandComment::RenderBold:
595 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000596 Result << "<b>";
597 appendToResultWithHTMLEscaping(Arg0);
598 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000599 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000600 case InlineCommandComment::RenderMonospaced:
601 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000602 Result << "<tt>";
603 appendToResultWithHTMLEscaping(Arg0);
604 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000605 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000606 case InlineCommandComment::RenderEmphasized:
607 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000608 Result << "<em>";
609 appendToResultWithHTMLEscaping(Arg0);
610 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000611 return;
612 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000613}
614
615void CommentASTToHTMLConverter::visitHTMLStartTagComment(
616 const HTMLStartTagComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000617 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000618}
619
620void CommentASTToHTMLConverter::visitHTMLEndTagComment(
621 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000622 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000623}
624
625void CommentASTToHTMLConverter::visitParagraphComment(
626 const ParagraphComment *C) {
627 if (C->isWhitespace())
628 return;
629
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000630 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000631 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
632 I != E; ++I) {
633 visit(*I);
634 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000635 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000636}
637
638void CommentASTToHTMLConverter::visitBlockCommandComment(
639 const BlockCommandComment *C) {
640 StringRef CommandName = C->getCommandName();
Dmitri Gribenkod1db1252012-08-09 17:33:20 +0000641 if (Traits.isBriefCommand(CommandName)) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000642 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000643 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000644 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000645 return;
646 }
Dmitri Gribenkod1db1252012-08-09 17:33:20 +0000647 if (Traits.isReturnsCommand(CommandName)) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000648 Result << "<p class=\"para-returns\">"
649 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000650 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000651 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000652 return;
653 }
654 // We don't know anything about this command. Just render the paragraph.
655 visit(C->getParagraph());
656}
657
658void CommentASTToHTMLConverter::visitParamCommandComment(
659 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000660 if (C->isParamIndexValid()) {
661 Result << "<dt class=\"param-name-index-"
662 << C->getParamIndex()
663 << "\">";
664 } else
665 Result << "<dt class=\"param-name-index-invalid\">";
666
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000667 appendToResultWithHTMLEscaping(C->getParamName());
668 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000669
670 if (C->isParamIndexValid()) {
671 Result << "<dd class=\"param-descr-index-"
672 << C->getParamIndex()
673 << "\">";
674 } else
675 Result << "<dd class=\"param-descr-index-invalid\">";
676
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000677 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000678 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000679}
680
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000681void CommentASTToHTMLConverter::visitTParamCommandComment(
682 const TParamCommandComment *C) {
683 if (C->isPositionValid()) {
684 if (C->getDepth() == 1)
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000685 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000686 << C->getIndex(0)
687 << "\">";
688 else
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000689 Result << "<dt class=\"tparam-name-index-other\">";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000690 } else
691 Result << "<dt class=\"tparam-name-index-invalid\">";
692
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000693 appendToResultWithHTMLEscaping(C->getParamName());
694 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000695
696 if (C->isPositionValid()) {
697 if (C->getDepth() == 1)
698 Result << "<dd class=\"tparam-descr-index-"
699 << C->getIndex(0)
700 << "\">";
701 else
702 Result << "<dd class=\"tparam-descr-index-other\">";
703 } else
704 Result << "<dd class=\"tparam-descr-index-invalid\">";
705
706 visitNonStandaloneParagraphComment(C->getParagraph());
707 Result << "</dd>";
708}
709
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000710void CommentASTToHTMLConverter::visitVerbatimBlockComment(
711 const VerbatimBlockComment *C) {
712 unsigned NumLines = C->getNumLines();
713 if (NumLines == 0)
714 return;
715
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000716 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000717 for (unsigned i = 0; i != NumLines; ++i) {
718 appendToResultWithHTMLEscaping(C->getText(i));
719 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000720 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000721 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000722 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000723}
724
725void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
726 const VerbatimBlockLineComment *C) {
727 llvm_unreachable("should not see this AST node");
728}
729
730void CommentASTToHTMLConverter::visitVerbatimLineComment(
731 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000732 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000733 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000734 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000735}
736
737void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000738 FullCommentParts Parts(C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000739
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000740 bool FirstParagraphIsBrief = false;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000741 if (Parts.Brief)
742 visit(Parts.Brief);
743 else if (Parts.FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000744 Result << "<p class=\"para-brief\">";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000745 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000746 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000747 FirstParagraphIsBrief = true;
748 }
749
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000750 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
751 const Comment *C = Parts.MiscBlocks[i];
752 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000753 continue;
754 visit(C);
755 }
756
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000757 if (Parts.TParams.size() != 0) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000758 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000759 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
760 visit(Parts.TParams[i]);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000761 Result << "</dl>";
762 }
763
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000764 if (Parts.Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000765 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000766 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
767 visit(Parts.Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000768 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000769 }
770
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000771 if (Parts.Returns)
772 visit(Parts.Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000773
774 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000775}
776
777void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
778 const ParagraphComment *C) {
779 if (!C)
780 return;
781
782 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
783 I != E; ++I) {
784 visit(*I);
785 }
786}
787
788void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000789 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
790 const char C = *I;
791 switch (C) {
792 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000793 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000794 break;
795 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000796 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000797 break;
798 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000799 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000800 break;
801 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000802 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000803 break;
804 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000805 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000806 break;
807 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000808 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000809 break;
810 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000811 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000812 break;
813 }
814 }
815}
816
817extern "C" {
818
819CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
820 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
821 if (!HTC)
822 return createCXString((const char *) 0);
823
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000824 SmallString<128> HTML;
825 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000826 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000827 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000828}
829
830CXString clang_FullComment_getAsHTML(CXComment CXC) {
831 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
832 if (!FC)
833 return createCXString((const char *) 0);
834
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000835 SmallString<1024> HTML;
836 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000837 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000838 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000839}
840
841} // end extern "C"
842
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000843namespace {
844class CommentASTToXMLConverter :
845 public ConstCommentVisitor<CommentASTToXMLConverter> {
846public:
847 /// \param Str accumulator for XML.
848 CommentASTToXMLConverter(const SourceManager &SM,
849 SmallVectorImpl<char> &Str) :
850 SM(SM), Result(Str) { }
851
852 // Inline content.
853 void visitTextComment(const TextComment *C);
854 void visitInlineCommandComment(const InlineCommandComment *C);
855 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
856 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
857
858 // Block content.
859 void visitParagraphComment(const ParagraphComment *C);
860 void visitBlockCommandComment(const BlockCommandComment *C);
861 void visitParamCommandComment(const ParamCommandComment *C);
862 void visitTParamCommandComment(const TParamCommandComment *C);
863 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
864 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
865 void visitVerbatimLineComment(const VerbatimLineComment *C);
866
867 void visitFullComment(const FullComment *C);
868
869 // Helpers.
870 void appendToResultWithXMLEscaping(StringRef S);
871
872private:
873 const SourceManager &SM;
Dmitri Gribenkod1db1252012-08-09 17:33:20 +0000874
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000875 /// Output stream for XML.
876 llvm::raw_svector_ostream Result;
877};
878} // end unnamed namespace
879
880void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
881 appendToResultWithXMLEscaping(C->getText());
882}
883
884void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
885 // Nothing to render if no arguments supplied.
886 if (C->getNumArgs() == 0)
887 return;
888
889 // Nothing to render if argument is empty.
890 StringRef Arg0 = C->getArgText(0);
891 if (Arg0.empty())
892 return;
893
894 switch (C->getRenderKind()) {
895 case InlineCommandComment::RenderNormal:
896 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
897 appendToResultWithXMLEscaping(C->getArgText(i));
898 Result << " ";
899 }
900 return;
901 case InlineCommandComment::RenderBold:
902 assert(C->getNumArgs() == 1);
903 Result << "<bold>";
904 appendToResultWithXMLEscaping(Arg0);
905 Result << "</bold>";
906 return;
907 case InlineCommandComment::RenderMonospaced:
908 assert(C->getNumArgs() == 1);
909 Result << "<monospaced>";
910 appendToResultWithXMLEscaping(Arg0);
911 Result << "</monospaced>";
912 return;
913 case InlineCommandComment::RenderEmphasized:
914 assert(C->getNumArgs() == 1);
915 Result << "<emphasized>";
916 appendToResultWithXMLEscaping(Arg0);
917 Result << "</emphasized>";
918 return;
919 }
920}
921
922void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
923 Result << "<rawHTML><![CDATA[";
924 PrintHTMLStartTagComment(C, Result);
925 Result << "]]></rawHTML>";
926}
927
928void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
929 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
930}
931
932void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
933 if (C->isWhitespace())
934 return;
935
936 Result << "<Para>";
937 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
938 I != E; ++I) {
939 visit(*I);
940 }
941 Result << "</Para>";
942}
943
944void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
945 visit(C->getParagraph());
946}
947
948void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
949 Result << "<Parameter><Name>";
950 appendToResultWithXMLEscaping(C->getParamName());
951 Result << "</Name>";
952
953 if (C->isParamIndexValid())
954 Result << "<Index>" << C->getParamIndex() << "</Index>";
955
956 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
957 switch (C->getDirection()) {
958 case ParamCommandComment::In:
959 Result << "in";
960 break;
961 case ParamCommandComment::Out:
962 Result << "out";
963 break;
964 case ParamCommandComment::InOut:
965 Result << "in,out";
966 break;
967 }
968 Result << "</Direction><Discussion>";
969 visit(C->getParagraph());
970 Result << "</Discussion></Parameter>";
971}
972
973void CommentASTToXMLConverter::visitTParamCommandComment(
974 const TParamCommandComment *C) {
975 Result << "<Parameter><Name>";
976 appendToResultWithXMLEscaping(C->getParamName());
977 Result << "</Name>";
978
979 if (C->isPositionValid() && C->getDepth() == 1) {
980 Result << "<Index>" << C->getIndex(0) << "</Index>";
981 }
982
983 Result << "<Discussion>";
984 visit(C->getParagraph());
985 Result << "</Discussion></Parameter>";
986}
987
988void CommentASTToXMLConverter::visitVerbatimBlockComment(
989 const VerbatimBlockComment *C) {
990 unsigned NumLines = C->getNumLines();
991 if (NumLines == 0)
992 return;
993
994 Result << llvm::StringSwitch<const char *>(C->getCommandName())
Dmitri Gribenko6cd44202012-08-08 22:10:24 +0000995 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
996 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000997 for (unsigned i = 0; i != NumLines; ++i) {
998 appendToResultWithXMLEscaping(C->getText(i));
999 if (i + 1 != NumLines)
1000 Result << '\n';
1001 }
1002 Result << "</Verbatim>";
1003}
1004
1005void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1006 const VerbatimBlockLineComment *C) {
1007 llvm_unreachable("should not see this AST node");
1008}
1009
1010void CommentASTToXMLConverter::visitVerbatimLineComment(
1011 const VerbatimLineComment *C) {
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001012 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001013 appendToResultWithXMLEscaping(C->getText());
1014 Result << "</Verbatim>";
1015}
1016
1017void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
1018 FullCommentParts Parts(C);
1019
1020 const DeclInfo *DI = C->getDeclInfo();
1021 StringRef RootEndTag;
1022 if (DI) {
1023 switch (DI->getKind()) {
1024 case DeclInfo::OtherKind:
1025 RootEndTag = "</Other>";
1026 Result << "<Other";
1027 break;
1028 case DeclInfo::FunctionKind:
1029 RootEndTag = "</Function>";
1030 Result << "<Function";
1031 switch (DI->TemplateKind) {
1032 case DeclInfo::NotTemplate:
1033 break;
1034 case DeclInfo::Template:
1035 Result << " templateKind=\"template\"";
1036 break;
1037 case DeclInfo::TemplateSpecialization:
1038 Result << " templateKind=\"specialization\"";
1039 break;
1040 case DeclInfo::TemplatePartialSpecialization:
1041 llvm_unreachable("partial specializations of functions "
1042 "are not allowed in C++");
1043 }
1044 if (DI->IsInstanceMethod)
1045 Result << " isInstanceMethod=\"1\"";
1046 if (DI->IsClassMethod)
1047 Result << " isClassMethod=\"1\"";
1048 break;
1049 case DeclInfo::ClassKind:
1050 RootEndTag = "</Class>";
1051 Result << "<Class";
1052 switch (DI->TemplateKind) {
1053 case DeclInfo::NotTemplate:
1054 break;
1055 case DeclInfo::Template:
1056 Result << " templateKind=\"template\"";
1057 break;
1058 case DeclInfo::TemplateSpecialization:
1059 Result << " templateKind=\"specialization\"";
1060 break;
1061 case DeclInfo::TemplatePartialSpecialization:
1062 Result << " templateKind=\"partialSpecialization\"";
1063 break;
1064 }
1065 break;
1066 case DeclInfo::VariableKind:
1067 RootEndTag = "</Variable>";
1068 Result << "<Variable";
1069 break;
1070 case DeclInfo::NamespaceKind:
1071 RootEndTag = "</Namespace>";
1072 Result << "<Namespace";
1073 break;
1074 case DeclInfo::TypedefKind:
1075 RootEndTag = "</Typedef>";
1076 Result << "<Typedef";
1077 break;
Dmitri Gribenkocff339a2012-08-07 18:59:04 +00001078 case DeclInfo::EnumKind:
1079 RootEndTag = "</Enum>";
1080 Result << "<Enum";
1081 break;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001082 }
1083
1084 {
1085 // Print line and column number.
1086 SourceLocation Loc = DI->ThisDecl->getLocation();
1087 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1088 FileID FID = LocInfo.first;
1089 unsigned FileOffset = LocInfo.second;
1090
1091 if (!FID.isInvalid()) {
1092 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1093 Result << " file=\"";
1094 appendToResultWithXMLEscaping(FE->getName());
1095 Result << "\"";
1096 }
1097 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1098 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1099 << "\"";
1100 }
1101 }
1102
1103 // Finish the root tag.
1104 Result << ">";
1105
1106 bool FoundName = false;
1107 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->ThisDecl)) {
1108 if (DeclarationName DeclName = ND->getDeclName()) {
1109 Result << "<Name>";
1110 std::string Name = DeclName.getAsString();
1111 appendToResultWithXMLEscaping(Name);
1112 FoundName = true;
1113 Result << "</Name>";
1114 }
1115 }
1116 if (!FoundName)
1117 Result << "<Name>&lt;anonymous&gt;</Name>";
1118
1119 {
1120 // Print USR.
1121 SmallString<128> USR;
1122 cxcursor::getDeclCursorUSR(DI->ThisDecl, USR);
1123 if (!USR.empty()) {
1124 Result << "<USR>";
1125 appendToResultWithXMLEscaping(USR);
1126 Result << "</USR>";
1127 }
1128 }
1129 } else {
1130 // No DeclInfo -- just emit some root tag and name tag.
1131 RootEndTag = "</Other>";
1132 Result << "<Other><Name>unknown</Name>";
1133 }
1134
1135 bool FirstParagraphIsBrief = false;
1136 if (Parts.Brief) {
1137 Result << "<Abstract>";
1138 visit(Parts.Brief);
1139 Result << "</Abstract>";
1140 } else if (Parts.FirstParagraph) {
1141 Result << "<Abstract>";
1142 visit(Parts.FirstParagraph);
1143 Result << "</Abstract>";
1144 FirstParagraphIsBrief = true;
1145 }
1146
1147 if (Parts.TParams.size() != 0) {
1148 Result << "<TemplateParameters>";
1149 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1150 visit(Parts.TParams[i]);
1151 Result << "</TemplateParameters>";
1152 }
1153
1154 if (Parts.Params.size() != 0) {
1155 Result << "<Parameters>";
1156 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1157 visit(Parts.Params[i]);
1158 Result << "</Parameters>";
1159 }
1160
1161 if (Parts.Returns) {
1162 Result << "<ResultDiscussion>";
1163 visit(Parts.Returns);
1164 Result << "</ResultDiscussion>";
1165 }
1166
1167 {
1168 bool StartTagEmitted = false;
1169 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1170 const Comment *C = Parts.MiscBlocks[i];
1171 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1172 continue;
1173 if (!StartTagEmitted) {
1174 Result << "<Discussion>";
1175 StartTagEmitted = true;
1176 }
1177 visit(C);
1178 }
1179 if (StartTagEmitted)
1180 Result << "</Discussion>";
1181 }
1182
1183 Result << RootEndTag;
1184
1185 Result.flush();
1186}
1187
1188void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1189 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1190 const char C = *I;
1191 switch (C) {
1192 case '&':
1193 Result << "&amp;";
1194 break;
1195 case '<':
1196 Result << "&lt;";
1197 break;
1198 case '>':
1199 Result << "&gt;";
1200 break;
1201 case '"':
1202 Result << "&quot;";
1203 break;
1204 case '\'':
1205 Result << "&apos;";
1206 break;
1207 default:
1208 Result << C;
1209 break;
1210 }
1211 }
1212}
1213
1214extern "C" {
1215
1216CXString clang_FullComment_getAsXML(CXTranslationUnit TU, CXComment CXC) {
1217 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1218 if (!FC)
1219 return createCXString((const char *) 0);
1220
1221 SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1222
1223 SmallString<1024> XML;
1224 CommentASTToXMLConverter Converter(SM, XML);
1225 Converter.visit(FC);
1226 return createCXString(XML.str(), /* DupString = */ true);
1227}
1228
1229} // end extern "C"
1230