blob: a132a0d1bafde01dd8e54c493b96003b3a1b172e [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"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000018
19#include "clang/AST/CommentVisitor.h"
Dmitri Gribenkod1db1252012-08-09 17:33:20 +000020#include "clang/AST/CommentCommandTraits.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000021#include "clang/AST/Decl.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000022
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000023#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000024#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko3e63d332012-07-21 01:47:43 +000025#include "llvm/Support/raw_ostream.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000026
Dmitri Gribenko221a6d72012-07-30 17:49:32 +000027#include <climits>
28
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000029using namespace clang;
30using namespace clang::cxstring;
31using namespace clang::comments;
32using namespace clang::cxcomment;
33
34extern "C" {
35
36enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
37 const Comment *C = getASTNode(CXC);
38 if (!C)
39 return CXComment_Null;
40
41 switch (C->getCommentKind()) {
42 case Comment::NoCommentKind:
43 return CXComment_Null;
44
45 case Comment::TextCommentKind:
46 return CXComment_Text;
47
48 case Comment::InlineCommandCommentKind:
49 return CXComment_InlineCommand;
50
51 case Comment::HTMLStartTagCommentKind:
52 return CXComment_HTMLStartTag;
53
54 case Comment::HTMLEndTagCommentKind:
55 return CXComment_HTMLEndTag;
56
57 case Comment::ParagraphCommentKind:
58 return CXComment_Paragraph;
59
60 case Comment::BlockCommandCommentKind:
61 return CXComment_BlockCommand;
62
63 case Comment::ParamCommandCommentKind:
64 return CXComment_ParamCommand;
65
Dmitri Gribenko96b09862012-07-31 22:37:06 +000066 case Comment::TParamCommandCommentKind:
67 return CXComment_TParamCommand;
68
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000069 case Comment::VerbatimBlockCommentKind:
70 return CXComment_VerbatimBlockCommand;
71
72 case Comment::VerbatimBlockLineCommentKind:
73 return CXComment_VerbatimBlockLine;
74
75 case Comment::VerbatimLineCommentKind:
76 return CXComment_VerbatimLine;
77
78 case Comment::FullCommentKind:
79 return CXComment_FullComment;
80 }
81 llvm_unreachable("unknown CommentKind");
82}
83
84unsigned clang_Comment_getNumChildren(CXComment CXC) {
85 const Comment *C = getASTNode(CXC);
86 if (!C)
87 return 0;
88
89 return C->child_count();
90}
91
92CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
93 const Comment *C = getASTNode(CXC);
94 if (!C || ChildIdx >= C->child_count())
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000095 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000096
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000097 return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000098}
99
100unsigned clang_Comment_isWhitespace(CXComment CXC) {
101 const Comment *C = getASTNode(CXC);
102 if (!C)
103 return false;
104
105 if (const TextComment *TC = dyn_cast<TextComment>(C))
106 return TC->isWhitespace();
107
108 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
109 return PC->isWhitespace();
110
111 return false;
112}
113
114unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
115 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
116 if (!ICC)
117 return false;
118
119 return ICC->hasTrailingNewline();
120}
121
122CXString clang_TextComment_getText(CXComment CXC) {
123 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
124 if (!TC)
125 return createCXString((const char *) 0);
126
127 return createCXString(TC->getText(), /*DupString=*/ false);
128}
129
130CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
131 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
132 if (!ICC)
133 return createCXString((const char *) 0);
134
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000135 const CommandTraits &Traits = getCommandTraits(CXC);
136 return createCXString(ICC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000137}
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
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000223 const CommandTraits &Traits = getCommandTraits(CXC);
224 return createCXString(BCC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000225}
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)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000247 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000248
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000249 return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000250}
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.
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000408 FullCommentParts(const FullComment *C,
409 const CommandTraits &Traits);
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000410
411 const BlockContentComment *Brief;
412 const ParagraphComment *FirstParagraph;
413 const BlockCommandComment *Returns;
414 SmallVector<const ParamCommandComment *, 8> Params;
415 SmallVector<const TParamCommandComment *, 4> TParams;
416 SmallVector<const BlockContentComment *, 8> MiscBlocks;
417};
418
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000419FullCommentParts::FullCommentParts(const FullComment *C,
420 const CommandTraits &Traits) :
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000421 Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
422 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
423 I != E; ++I) {
424 const Comment *Child = *I;
425 if (!Child)
426 continue;
427 switch (Child->getCommentKind()) {
428 case Comment::NoCommentKind:
429 continue;
430
431 case Comment::ParagraphCommentKind: {
432 const ParagraphComment *PC = cast<ParagraphComment>(Child);
433 if (PC->isWhitespace())
434 break;
435 if (!FirstParagraph)
436 FirstParagraph = PC;
437
438 MiscBlocks.push_back(PC);
439 break;
440 }
441
442 case Comment::BlockCommandCommentKind: {
443 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000444 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
445 if (!Brief && Info->IsBriefCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000446 Brief = BCC;
447 break;
448 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000449 if (!Returns && Info->IsReturnsCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000450 Returns = BCC;
451 break;
452 }
453 MiscBlocks.push_back(BCC);
454 break;
455 }
456
457 case Comment::ParamCommandCommentKind: {
458 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
459 if (!PCC->hasParamName())
460 break;
461
462 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
463 break;
464
465 Params.push_back(PCC);
466 break;
467 }
468
469 case Comment::TParamCommandCommentKind: {
470 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
471 if (!TPCC->hasParamName())
472 break;
473
474 if (!TPCC->hasNonWhitespaceParagraph())
475 break;
476
477 TParams.push_back(TPCC);
478 break;
479 }
480
481 case Comment::VerbatimBlockCommentKind:
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000482 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
483 break;
484
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000485 case Comment::VerbatimLineCommentKind: {
486 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000487 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
488 if (!Info->IsDeclarationCommand)
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000489 MiscBlocks.push_back(VLC);
490 break;
491 }
492
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000493 case Comment::TextCommentKind:
494 case Comment::InlineCommandCommentKind:
495 case Comment::HTMLStartTagCommentKind:
496 case Comment::HTMLEndTagCommentKind:
497 case Comment::VerbatimBlockLineCommentKind:
498 case Comment::FullCommentKind:
499 llvm_unreachable("AST node of this kind can't be a child of "
500 "a FullComment");
501 }
502 }
503
504 // Sort params in order they are declared in the function prototype.
505 // Unresolved parameters are put at the end of the list in the same order
506 // they were seen in the comment.
507 std::stable_sort(Params.begin(), Params.end(),
508 ParamCommandCommentCompareIndex());
509
510 std::stable_sort(TParams.begin(), TParams.end(),
511 TParamCommandCommentComparePosition());
512}
513
514void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
515 llvm::raw_svector_ostream &Result) {
516 Result << "<" << C->getTagName();
517
518 if (C->getNumAttrs() != 0) {
519 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
520 Result << " ";
521 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
522 Result << Attr.Name;
523 if (!Attr.Value.empty())
524 Result << "=\"" << Attr.Value << "\"";
525 }
526 }
527
528 if (!C->isSelfClosing())
529 Result << ">";
530 else
531 Result << "/>";
532}
533
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000534class CommentASTToHTMLConverter :
535 public ConstCommentVisitor<CommentASTToHTMLConverter> {
536public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000537 /// \param Str accumulator for HTML.
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000538 CommentASTToHTMLConverter(SmallVectorImpl<char> &Str,
539 const CommandTraits &Traits) :
540 Result(Str), Traits(Traits)
541 { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000542
543 // Inline content.
544 void visitTextComment(const TextComment *C);
545 void visitInlineCommandComment(const InlineCommandComment *C);
546 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
547 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
548
549 // Block content.
550 void visitParagraphComment(const ParagraphComment *C);
551 void visitBlockCommandComment(const BlockCommandComment *C);
552 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000553 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000554 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
555 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
556 void visitVerbatimLineComment(const VerbatimLineComment *C);
557
558 void visitFullComment(const FullComment *C);
559
560 // Helpers.
561
562 /// Convert a paragraph that is not a block by itself (an argument to some
563 /// command).
564 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
565
566 void appendToResultWithHTMLEscaping(StringRef S);
567
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000568private:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000569 /// Output stream for HTML.
570 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000571
572 const CommandTraits &Traits;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000573};
574} // end unnamed namespace
575
576void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
577 appendToResultWithHTMLEscaping(C->getText());
578}
579
580void CommentASTToHTMLConverter::visitInlineCommandComment(
581 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000582 // Nothing to render if no arguments supplied.
583 if (C->getNumArgs() == 0)
584 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000585
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000586 // Nothing to render if argument is empty.
587 StringRef Arg0 = C->getArgText(0);
588 if (Arg0.empty())
589 return;
590
591 switch (C->getRenderKind()) {
592 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000593 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
594 appendToResultWithHTMLEscaping(C->getArgText(i));
595 Result << " ";
596 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000597 return;
598
599 case InlineCommandComment::RenderBold:
600 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000601 Result << "<b>";
602 appendToResultWithHTMLEscaping(Arg0);
603 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000604 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000605 case InlineCommandComment::RenderMonospaced:
606 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000607 Result << "<tt>";
608 appendToResultWithHTMLEscaping(Arg0);
609 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000610 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000611 case InlineCommandComment::RenderEmphasized:
612 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000613 Result << "<em>";
614 appendToResultWithHTMLEscaping(Arg0);
615 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000616 return;
617 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000618}
619
620void CommentASTToHTMLConverter::visitHTMLStartTagComment(
621 const HTMLStartTagComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000622 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000623}
624
625void CommentASTToHTMLConverter::visitHTMLEndTagComment(
626 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000627 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000628}
629
630void CommentASTToHTMLConverter::visitParagraphComment(
631 const ParagraphComment *C) {
632 if (C->isWhitespace())
633 return;
634
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000635 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000636 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
637 I != E; ++I) {
638 visit(*I);
639 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000640 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000641}
642
643void CommentASTToHTMLConverter::visitBlockCommandComment(
644 const BlockCommandComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000645 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
646 if (Info->IsBriefCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000647 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000648 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000649 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000650 return;
651 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000652 if (Info->IsReturnsCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000653 Result << "<p class=\"para-returns\">"
654 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000655 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000656 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000657 return;
658 }
659 // We don't know anything about this command. Just render the paragraph.
660 visit(C->getParagraph());
661}
662
663void CommentASTToHTMLConverter::visitParamCommandComment(
664 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000665 if (C->isParamIndexValid()) {
666 Result << "<dt class=\"param-name-index-"
667 << C->getParamIndex()
668 << "\">";
669 } else
670 Result << "<dt class=\"param-name-index-invalid\">";
671
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000672 appendToResultWithHTMLEscaping(C->getParamName());
673 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000674
675 if (C->isParamIndexValid()) {
676 Result << "<dd class=\"param-descr-index-"
677 << C->getParamIndex()
678 << "\">";
679 } else
680 Result << "<dd class=\"param-descr-index-invalid\">";
681
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000682 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000683 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000684}
685
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000686void CommentASTToHTMLConverter::visitTParamCommandComment(
687 const TParamCommandComment *C) {
688 if (C->isPositionValid()) {
689 if (C->getDepth() == 1)
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000690 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000691 << C->getIndex(0)
692 << "\">";
693 else
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000694 Result << "<dt class=\"tparam-name-index-other\">";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000695 } else
696 Result << "<dt class=\"tparam-name-index-invalid\">";
697
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000698 appendToResultWithHTMLEscaping(C->getParamName());
699 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000700
701 if (C->isPositionValid()) {
702 if (C->getDepth() == 1)
703 Result << "<dd class=\"tparam-descr-index-"
704 << C->getIndex(0)
705 << "\">";
706 else
707 Result << "<dd class=\"tparam-descr-index-other\">";
708 } else
709 Result << "<dd class=\"tparam-descr-index-invalid\">";
710
711 visitNonStandaloneParagraphComment(C->getParagraph());
712 Result << "</dd>";
713}
714
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000715void CommentASTToHTMLConverter::visitVerbatimBlockComment(
716 const VerbatimBlockComment *C) {
717 unsigned NumLines = C->getNumLines();
718 if (NumLines == 0)
719 return;
720
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000721 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000722 for (unsigned i = 0; i != NumLines; ++i) {
723 appendToResultWithHTMLEscaping(C->getText(i));
724 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000725 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000726 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000727 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000728}
729
730void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
731 const VerbatimBlockLineComment *C) {
732 llvm_unreachable("should not see this AST node");
733}
734
735void CommentASTToHTMLConverter::visitVerbatimLineComment(
736 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000737 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000738 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000739 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000740}
741
742void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000743 FullCommentParts Parts(C, Traits);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000744
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000745 bool FirstParagraphIsBrief = false;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000746 if (Parts.Brief)
747 visit(Parts.Brief);
748 else if (Parts.FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000749 Result << "<p class=\"para-brief\">";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000750 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000751 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000752 FirstParagraphIsBrief = true;
753 }
754
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000755 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
756 const Comment *C = Parts.MiscBlocks[i];
757 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000758 continue;
759 visit(C);
760 }
761
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000762 if (Parts.TParams.size() != 0) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000763 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000764 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
765 visit(Parts.TParams[i]);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000766 Result << "</dl>";
767 }
768
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000769 if (Parts.Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000770 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000771 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
772 visit(Parts.Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000773 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000774 }
775
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000776 if (Parts.Returns)
777 visit(Parts.Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000778
779 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000780}
781
782void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
783 const ParagraphComment *C) {
784 if (!C)
785 return;
786
787 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
788 I != E; ++I) {
789 visit(*I);
790 }
791}
792
793void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000794 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
795 const char C = *I;
796 switch (C) {
797 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000798 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000799 break;
800 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000801 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000802 break;
803 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000804 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000805 break;
806 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000807 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000808 break;
809 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000810 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000811 break;
812 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000813 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000814 break;
815 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000816 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000817 break;
818 }
819 }
820}
821
822extern "C" {
823
824CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
825 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
826 if (!HTC)
827 return createCXString((const char *) 0);
828
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000829 SmallString<128> HTML;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000830 CommentASTToHTMLConverter Converter(HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000831 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000832 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000833}
834
835CXString clang_FullComment_getAsHTML(CXComment CXC) {
836 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
837 if (!FC)
838 return createCXString((const char *) 0);
839
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000840 SmallString<1024> HTML;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000841 CommentASTToHTMLConverter Converter(HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000842 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000843 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000844}
845
846} // end extern "C"
847
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000848namespace {
849class CommentASTToXMLConverter :
850 public ConstCommentVisitor<CommentASTToXMLConverter> {
851public:
852 /// \param Str accumulator for XML.
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000853 CommentASTToXMLConverter(SmallVectorImpl<char> &Str,
854 const CommandTraits &Traits,
855 const SourceManager &SM) :
856 Result(Str), Traits(Traits), SM(SM) { }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000857
858 // Inline content.
859 void visitTextComment(const TextComment *C);
860 void visitInlineCommandComment(const InlineCommandComment *C);
861 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
862 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
863
864 // Block content.
865 void visitParagraphComment(const ParagraphComment *C);
866 void visitBlockCommandComment(const BlockCommandComment *C);
867 void visitParamCommandComment(const ParamCommandComment *C);
868 void visitTParamCommandComment(const TParamCommandComment *C);
869 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
870 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
871 void visitVerbatimLineComment(const VerbatimLineComment *C);
872
873 void visitFullComment(const FullComment *C);
874
875 // Helpers.
876 void appendToResultWithXMLEscaping(StringRef S);
877
878private:
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000879 /// Output stream for XML.
880 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000881
882 const CommandTraits &Traits;
883 const SourceManager &SM;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000884};
885} // end unnamed namespace
886
887void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
888 appendToResultWithXMLEscaping(C->getText());
889}
890
891void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
892 // Nothing to render if no arguments supplied.
893 if (C->getNumArgs() == 0)
894 return;
895
896 // Nothing to render if argument is empty.
897 StringRef Arg0 = C->getArgText(0);
898 if (Arg0.empty())
899 return;
900
901 switch (C->getRenderKind()) {
902 case InlineCommandComment::RenderNormal:
903 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
904 appendToResultWithXMLEscaping(C->getArgText(i));
905 Result << " ";
906 }
907 return;
908 case InlineCommandComment::RenderBold:
909 assert(C->getNumArgs() == 1);
910 Result << "<bold>";
911 appendToResultWithXMLEscaping(Arg0);
912 Result << "</bold>";
913 return;
914 case InlineCommandComment::RenderMonospaced:
915 assert(C->getNumArgs() == 1);
916 Result << "<monospaced>";
917 appendToResultWithXMLEscaping(Arg0);
918 Result << "</monospaced>";
919 return;
920 case InlineCommandComment::RenderEmphasized:
921 assert(C->getNumArgs() == 1);
922 Result << "<emphasized>";
923 appendToResultWithXMLEscaping(Arg0);
924 Result << "</emphasized>";
925 return;
926 }
927}
928
929void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
930 Result << "<rawHTML><![CDATA[";
931 PrintHTMLStartTagComment(C, Result);
932 Result << "]]></rawHTML>";
933}
934
935void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
936 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
937}
938
939void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
940 if (C->isWhitespace())
941 return;
942
943 Result << "<Para>";
944 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
945 I != E; ++I) {
946 visit(*I);
947 }
948 Result << "</Para>";
949}
950
951void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
952 visit(C->getParagraph());
953}
954
955void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
956 Result << "<Parameter><Name>";
957 appendToResultWithXMLEscaping(C->getParamName());
958 Result << "</Name>";
959
960 if (C->isParamIndexValid())
961 Result << "<Index>" << C->getParamIndex() << "</Index>";
962
963 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
964 switch (C->getDirection()) {
965 case ParamCommandComment::In:
966 Result << "in";
967 break;
968 case ParamCommandComment::Out:
969 Result << "out";
970 break;
971 case ParamCommandComment::InOut:
972 Result << "in,out";
973 break;
974 }
975 Result << "</Direction><Discussion>";
976 visit(C->getParagraph());
977 Result << "</Discussion></Parameter>";
978}
979
980void CommentASTToXMLConverter::visitTParamCommandComment(
981 const TParamCommandComment *C) {
982 Result << "<Parameter><Name>";
983 appendToResultWithXMLEscaping(C->getParamName());
984 Result << "</Name>";
985
986 if (C->isPositionValid() && C->getDepth() == 1) {
987 Result << "<Index>" << C->getIndex(0) << "</Index>";
988 }
989
990 Result << "<Discussion>";
991 visit(C->getParagraph());
992 Result << "</Discussion></Parameter>";
993}
994
995void CommentASTToXMLConverter::visitVerbatimBlockComment(
996 const VerbatimBlockComment *C) {
997 unsigned NumLines = C->getNumLines();
998 if (NumLines == 0)
999 return;
1000
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001001 Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits))
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001002 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
1003 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001004 for (unsigned i = 0; i != NumLines; ++i) {
1005 appendToResultWithXMLEscaping(C->getText(i));
1006 if (i + 1 != NumLines)
1007 Result << '\n';
1008 }
1009 Result << "</Verbatim>";
1010}
1011
1012void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1013 const VerbatimBlockLineComment *C) {
1014 llvm_unreachable("should not see this AST node");
1015}
1016
1017void CommentASTToXMLConverter::visitVerbatimLineComment(
1018 const VerbatimLineComment *C) {
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001019 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001020 appendToResultWithXMLEscaping(C->getText());
1021 Result << "</Verbatim>";
1022}
1023
1024void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001025 FullCommentParts Parts(C, Traits);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001026
1027 const DeclInfo *DI = C->getDeclInfo();
1028 StringRef RootEndTag;
1029 if (DI) {
1030 switch (DI->getKind()) {
1031 case DeclInfo::OtherKind:
1032 RootEndTag = "</Other>";
1033 Result << "<Other";
1034 break;
1035 case DeclInfo::FunctionKind:
1036 RootEndTag = "</Function>";
1037 Result << "<Function";
1038 switch (DI->TemplateKind) {
1039 case DeclInfo::NotTemplate:
1040 break;
1041 case DeclInfo::Template:
1042 Result << " templateKind=\"template\"";
1043 break;
1044 case DeclInfo::TemplateSpecialization:
1045 Result << " templateKind=\"specialization\"";
1046 break;
1047 case DeclInfo::TemplatePartialSpecialization:
1048 llvm_unreachable("partial specializations of functions "
1049 "are not allowed in C++");
1050 }
1051 if (DI->IsInstanceMethod)
1052 Result << " isInstanceMethod=\"1\"";
1053 if (DI->IsClassMethod)
1054 Result << " isClassMethod=\"1\"";
1055 break;
1056 case DeclInfo::ClassKind:
1057 RootEndTag = "</Class>";
1058 Result << "<Class";
1059 switch (DI->TemplateKind) {
1060 case DeclInfo::NotTemplate:
1061 break;
1062 case DeclInfo::Template:
1063 Result << " templateKind=\"template\"";
1064 break;
1065 case DeclInfo::TemplateSpecialization:
1066 Result << " templateKind=\"specialization\"";
1067 break;
1068 case DeclInfo::TemplatePartialSpecialization:
1069 Result << " templateKind=\"partialSpecialization\"";
1070 break;
1071 }
1072 break;
1073 case DeclInfo::VariableKind:
1074 RootEndTag = "</Variable>";
1075 Result << "<Variable";
1076 break;
1077 case DeclInfo::NamespaceKind:
1078 RootEndTag = "</Namespace>";
1079 Result << "<Namespace";
1080 break;
1081 case DeclInfo::TypedefKind:
1082 RootEndTag = "</Typedef>";
1083 Result << "<Typedef";
1084 break;
Dmitri Gribenkocff339a2012-08-07 18:59:04 +00001085 case DeclInfo::EnumKind:
1086 RootEndTag = "</Enum>";
1087 Result << "<Enum";
1088 break;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001089 }
1090
1091 {
1092 // Print line and column number.
1093 SourceLocation Loc = DI->ThisDecl->getLocation();
1094 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1095 FileID FID = LocInfo.first;
1096 unsigned FileOffset = LocInfo.second;
1097
1098 if (!FID.isInvalid()) {
1099 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1100 Result << " file=\"";
1101 appendToResultWithXMLEscaping(FE->getName());
1102 Result << "\"";
1103 }
1104 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1105 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1106 << "\"";
1107 }
1108 }
1109
1110 // Finish the root tag.
1111 Result << ">";
1112
1113 bool FoundName = false;
1114 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->ThisDecl)) {
1115 if (DeclarationName DeclName = ND->getDeclName()) {
1116 Result << "<Name>";
1117 std::string Name = DeclName.getAsString();
1118 appendToResultWithXMLEscaping(Name);
1119 FoundName = true;
1120 Result << "</Name>";
1121 }
1122 }
1123 if (!FoundName)
1124 Result << "<Name>&lt;anonymous&gt;</Name>";
1125
1126 {
1127 // Print USR.
1128 SmallString<128> USR;
1129 cxcursor::getDeclCursorUSR(DI->ThisDecl, USR);
1130 if (!USR.empty()) {
1131 Result << "<USR>";
1132 appendToResultWithXMLEscaping(USR);
1133 Result << "</USR>";
1134 }
1135 }
1136 } else {
1137 // No DeclInfo -- just emit some root tag and name tag.
1138 RootEndTag = "</Other>";
1139 Result << "<Other><Name>unknown</Name>";
1140 }
1141
1142 bool FirstParagraphIsBrief = false;
1143 if (Parts.Brief) {
1144 Result << "<Abstract>";
1145 visit(Parts.Brief);
1146 Result << "</Abstract>";
1147 } else if (Parts.FirstParagraph) {
1148 Result << "<Abstract>";
1149 visit(Parts.FirstParagraph);
1150 Result << "</Abstract>";
1151 FirstParagraphIsBrief = true;
1152 }
1153
1154 if (Parts.TParams.size() != 0) {
1155 Result << "<TemplateParameters>";
1156 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1157 visit(Parts.TParams[i]);
1158 Result << "</TemplateParameters>";
1159 }
1160
1161 if (Parts.Params.size() != 0) {
1162 Result << "<Parameters>";
1163 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1164 visit(Parts.Params[i]);
1165 Result << "</Parameters>";
1166 }
1167
1168 if (Parts.Returns) {
1169 Result << "<ResultDiscussion>";
1170 visit(Parts.Returns);
1171 Result << "</ResultDiscussion>";
1172 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001173
1174 if (DI->ThisDecl->hasAttrs()) {
1175 const AttrVec &Attrs = DI->ThisDecl->getAttrs();
1176 for (unsigned i = 0, e = Attrs.size(); i != e;) {
1177 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i++]);
1178 if (!AA)
1179 continue;
1180 // availability attribute info.
1181
1182 Result << "<Availability";
1183 StringRef distribution;
1184 if (AA->getPlatform()) {
1185 distribution = AA->getPlatform()->getName();
1186 if (distribution == "macosx")
1187 distribution = "OSX";
1188 else
1189 distribution = "iOS";
1190 }
1191
1192 Result << " distribution=\"";
1193 Result << distribution;
1194 Result << "\">";
1195 VersionTuple IntroducedInVersion = AA->getIntroduced();
1196 if (!IntroducedInVersion.empty()) {
1197 Result << " <IntroducedInVersion>";
1198 Result << IntroducedInVersion.getAsString();
1199 Result << "</IntroducedInVersion>";
1200 }
1201 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1202 if (!DeprecatedInVersion.empty()) {
1203 Result << " <DeprecatedInVersion>";
1204 Result << DeprecatedInVersion.getAsString();
1205 Result << "</DeprecatedInVersion>";
1206 }
1207 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1208 if (!RemovedAfterVersion.empty()) {
1209 Result << " <RemovedAfterVersion>";
1210 Result << RemovedAfterVersion.getAsString();
1211 Result << "</RemovedAfterVersion>";
1212 }
1213 StringRef DeprecationSummary = AA->getMessage();
1214 if (!DeprecationSummary.empty()) {
1215 Result << " <DeprecationSummary>";
1216 Result << DeprecationSummary;
1217 Result << "</DeprecationSummary>";
1218 }
1219 Result << " <Unavailable>";
1220 if (AA->getUnavailable())
1221 Result << "true";
1222 else
1223 Result << "false";
1224 Result << "</Unavailable>";
1225 Result << " </Availability>";
1226 }
1227 }
1228
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001229 {
1230 bool StartTagEmitted = false;
1231 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1232 const Comment *C = Parts.MiscBlocks[i];
1233 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1234 continue;
1235 if (!StartTagEmitted) {
1236 Result << "<Discussion>";
1237 StartTagEmitted = true;
1238 }
1239 visit(C);
1240 }
1241 if (StartTagEmitted)
1242 Result << "</Discussion>";
1243 }
1244
1245 Result << RootEndTag;
1246
1247 Result.flush();
1248}
1249
1250void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1251 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1252 const char C = *I;
1253 switch (C) {
1254 case '&':
1255 Result << "&amp;";
1256 break;
1257 case '<':
1258 Result << "&lt;";
1259 break;
1260 case '>':
1261 Result << "&gt;";
1262 break;
1263 case '"':
1264 Result << "&quot;";
1265 break;
1266 case '\'':
1267 Result << "&apos;";
1268 break;
1269 default:
1270 Result << C;
1271 break;
1272 }
1273 }
1274}
1275
1276extern "C" {
1277
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001278CXString clang_FullComment_getAsXML(CXComment CXC) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001279 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1280 if (!FC)
1281 return createCXString((const char *) 0);
1282
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001283 CXTranslationUnit TU = CXC.TranslationUnit;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001284 SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1285
1286 SmallString<1024> XML;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001287 CommentASTToXMLConverter Converter(XML, getCommandTraits(CXC), SM);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001288 Converter.visit(FC);
1289 return createCXString(XML.str(), /* DupString = */ true);
1290}
1291
1292} // end extern "C"
1293