blob: 4189b31446a379361caebd7d120736446616483e [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"
17
18#include "clang/AST/CommentVisitor.h"
19
20#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko3e63d332012-07-21 01:47:43 +000021#include "llvm/Support/raw_ostream.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000022
23using namespace clang;
24using namespace clang::cxstring;
25using namespace clang::comments;
26using namespace clang::cxcomment;
27
28extern "C" {
29
30enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
31 const Comment *C = getASTNode(CXC);
32 if (!C)
33 return CXComment_Null;
34
35 switch (C->getCommentKind()) {
36 case Comment::NoCommentKind:
37 return CXComment_Null;
38
39 case Comment::TextCommentKind:
40 return CXComment_Text;
41
42 case Comment::InlineCommandCommentKind:
43 return CXComment_InlineCommand;
44
45 case Comment::HTMLStartTagCommentKind:
46 return CXComment_HTMLStartTag;
47
48 case Comment::HTMLEndTagCommentKind:
49 return CXComment_HTMLEndTag;
50
51 case Comment::ParagraphCommentKind:
52 return CXComment_Paragraph;
53
54 case Comment::BlockCommandCommentKind:
55 return CXComment_BlockCommand;
56
57 case Comment::ParamCommandCommentKind:
58 return CXComment_ParamCommand;
59
60 case Comment::VerbatimBlockCommentKind:
61 return CXComment_VerbatimBlockCommand;
62
63 case Comment::VerbatimBlockLineCommentKind:
64 return CXComment_VerbatimBlockLine;
65
66 case Comment::VerbatimLineCommentKind:
67 return CXComment_VerbatimLine;
68
69 case Comment::FullCommentKind:
70 return CXComment_FullComment;
71 }
72 llvm_unreachable("unknown CommentKind");
73}
74
75unsigned clang_Comment_getNumChildren(CXComment CXC) {
76 const Comment *C = getASTNode(CXC);
77 if (!C)
78 return 0;
79
80 return C->child_count();
81}
82
83CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
84 const Comment *C = getASTNode(CXC);
85 if (!C || ChildIdx >= C->child_count())
86 return createCXComment(NULL);
87
88 return createCXComment(*(C->child_begin() + ChildIdx));
89}
90
91unsigned clang_Comment_isWhitespace(CXComment CXC) {
92 const Comment *C = getASTNode(CXC);
93 if (!C)
94 return false;
95
96 if (const TextComment *TC = dyn_cast<TextComment>(C))
97 return TC->isWhitespace();
98
99 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
100 return PC->isWhitespace();
101
102 return false;
103}
104
105unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
106 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
107 if (!ICC)
108 return false;
109
110 return ICC->hasTrailingNewline();
111}
112
113CXString clang_TextComment_getText(CXComment CXC) {
114 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
115 if (!TC)
116 return createCXString((const char *) 0);
117
118 return createCXString(TC->getText(), /*DupString=*/ false);
119}
120
121CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
122 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
123 if (!ICC)
124 return createCXString((const char *) 0);
125
126 return createCXString(ICC->getCommandName(), /*DupString=*/ false);
127}
128
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000129enum CXCommentInlineCommandRenderKind
130clang_InlineCommandComment_getRenderKind(CXComment CXC) {
131 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
132 if (!ICC)
133 return CXCommentInlineCommandRenderKind_Normal;
134
135 switch (ICC->getRenderKind()) {
136 case InlineCommandComment::RenderNormal:
137 return CXCommentInlineCommandRenderKind_Normal;
138
139 case InlineCommandComment::RenderBold:
140 return CXCommentInlineCommandRenderKind_Bold;
141
142 case InlineCommandComment::RenderMonospaced:
143 return CXCommentInlineCommandRenderKind_Monospaced;
144
145 case InlineCommandComment::RenderEmphasized:
146 return CXCommentInlineCommandRenderKind_Emphasized;
147 }
148 llvm_unreachable("unknown InlineCommandComment::RenderKind");
149}
150
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000151unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
152 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
153 if (!ICC)
154 return 0;
155
156 return ICC->getNumArgs();
157}
158
159CXString clang_InlineCommandComment_getArgText(CXComment CXC,
160 unsigned ArgIdx) {
161 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
162 if (!ICC || ArgIdx >= ICC->getNumArgs())
163 return createCXString((const char *) 0);
164
165 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
166}
167
168CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
169 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
170 if (!HTC)
171 return createCXString((const char *) 0);
172
173 return createCXString(HTC->getTagName(), /*DupString=*/ false);
174}
175
176unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
177 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
178 if (!HST)
179 return false;
180
181 return HST->isSelfClosing();
182}
183
184unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
185 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
186 if (!HST)
187 return 0;
188
189 return HST->getNumAttrs();
190}
191
192CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
193 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
194 if (!HST || AttrIdx >= HST->getNumAttrs())
195 return createCXString((const char *) 0);
196
197 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
198}
199
200CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
201 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
202 if (!HST || AttrIdx >= HST->getNumAttrs())
203 return createCXString((const char *) 0);
204
205 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
206}
207
208CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
209 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
210 if (!BCC)
211 return createCXString((const char *) 0);
212
213 return createCXString(BCC->getCommandName(), /*DupString=*/ false);
214}
215
216unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
217 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
218 if (!BCC)
219 return 0;
220
221 return BCC->getNumArgs();
222}
223
224CXString clang_BlockCommandComment_getArgText(CXComment CXC,
225 unsigned ArgIdx) {
226 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
227 if (!BCC || ArgIdx >= BCC->getNumArgs())
228 return createCXString((const char *) 0);
229
230 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
231}
232
233CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
234 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
235 if (!BCC)
236 return createCXComment(NULL);
237
238 return createCXComment(BCC->getParagraph());
239}
240
241CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
242 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko1f8c5292012-07-23 19:41:49 +0000243 if (!PCC || !PCC->hasParamName())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000244 return createCXString((const char *) 0);
245
246 return createCXString(PCC->getParamName(), /*DupString=*/ false);
247}
248
249unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
250 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
251 if (!PCC)
252 return false;
253
254 return PCC->isParamIndexValid();
255}
256
257unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
258 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000259 if (!PCC || !PCC->isParamIndexValid())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000260 return ParamCommandComment::InvalidParamIndex;
261
262 return PCC->getParamIndex();
263}
264
265unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
266 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
267 if (!PCC)
268 return false;
269
270 return PCC->isDirectionExplicit();
271}
272
273enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
274 CXComment CXC) {
275 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
276 if (!PCC)
277 return CXCommentParamPassDirection_In;
278
279 switch (PCC->getDirection()) {
280 case ParamCommandComment::In:
281 return CXCommentParamPassDirection_In;
282
283 case ParamCommandComment::Out:
284 return CXCommentParamPassDirection_Out;
285
286 case ParamCommandComment::InOut:
287 return CXCommentParamPassDirection_InOut;
288 }
289 llvm_unreachable("unknown ParamCommandComment::PassDirection");
290}
291
292CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
293 const VerbatimBlockLineComment *VBL =
294 getASTNodeAs<VerbatimBlockLineComment>(CXC);
295 if (!VBL)
296 return createCXString((const char *) 0);
297
298 return createCXString(VBL->getText(), /*DupString=*/ false);
299}
300
301CXString clang_VerbatimLineComment_getText(CXComment CXC) {
302 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
303 if (!VLC)
304 return createCXString((const char *) 0);
305
306 return createCXString(VLC->getText(), /*DupString=*/ false);
307}
308
309} // end extern "C"
310
311//===----------------------------------------------------------------------===//
312// Helpers for converting comment AST to HTML.
313//===----------------------------------------------------------------------===//
314
315namespace {
316
317class ParamCommandCommentCompareIndex {
318public:
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000319 /// This comparison will sort parameters with valid index by index and
320 /// invalid (unresolved) parameters last.
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000321 bool operator()(const ParamCommandComment *LHS,
322 const ParamCommandComment *RHS) const {
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000323 unsigned LHSIndex = UINT_MAX;
324 unsigned RHSIndex = UINT_MAX;
325 if (LHS->isParamIndexValid())
326 LHSIndex = LHS->getParamIndex();
327 if (RHS->isParamIndexValid())
328 RHSIndex = RHS->getParamIndex();
329
330 return LHSIndex < RHSIndex;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000331 }
332};
333
334class CommentASTToHTMLConverter :
335 public ConstCommentVisitor<CommentASTToHTMLConverter> {
336public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000337 /// \param Str accumulator for HTML.
338 CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000339
340 // Inline content.
341 void visitTextComment(const TextComment *C);
342 void visitInlineCommandComment(const InlineCommandComment *C);
343 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
344 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
345
346 // Block content.
347 void visitParagraphComment(const ParagraphComment *C);
348 void visitBlockCommandComment(const BlockCommandComment *C);
349 void visitParamCommandComment(const ParamCommandComment *C);
350 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
351 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
352 void visitVerbatimLineComment(const VerbatimLineComment *C);
353
354 void visitFullComment(const FullComment *C);
355
356 // Helpers.
357
358 /// Convert a paragraph that is not a block by itself (an argument to some
359 /// command).
360 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
361
362 void appendToResultWithHTMLEscaping(StringRef S);
363
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000364private:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000365 /// Output stream for HTML.
366 llvm::raw_svector_ostream Result;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000367};
368} // end unnamed namespace
369
370void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
371 appendToResultWithHTMLEscaping(C->getText());
372}
373
374void CommentASTToHTMLConverter::visitInlineCommandComment(
375 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000376 // Nothing to render if no arguments supplied.
377 if (C->getNumArgs() == 0)
378 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000379
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000380 // Nothing to render if argument is empty.
381 StringRef Arg0 = C->getArgText(0);
382 if (Arg0.empty())
383 return;
384
385 switch (C->getRenderKind()) {
386 case InlineCommandComment::RenderNormal:
387 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i)
388 Result << C->getArgText(i) << " ";
389 return;
390
391 case InlineCommandComment::RenderBold:
392 assert(C->getNumArgs() == 1);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000393 Result << "<b>" << Arg0 << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000394 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000395 case InlineCommandComment::RenderMonospaced:
396 assert(C->getNumArgs() == 1);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000397 Result << "<tt>" << Arg0 << "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000398 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000399 case InlineCommandComment::RenderEmphasized:
400 assert(C->getNumArgs() == 1);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000401 Result << "<em>" << Arg0 << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000402 return;
403 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000404}
405
406void CommentASTToHTMLConverter::visitHTMLStartTagComment(
407 const HTMLStartTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000408 Result << "<" << C->getTagName();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000409
410 if (C->getNumAttrs() != 0) {
411 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000412 Result << " ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000413 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000414 Result << Attr.Name;
415 if (!Attr.Value.empty())
416 Result << "=\"" << Attr.Value << "\"";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000417 }
418 }
419
420 if (!C->isSelfClosing())
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000421 Result << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000422 else
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000423 Result << "/>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000424}
425
426void CommentASTToHTMLConverter::visitHTMLEndTagComment(
427 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000428 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000429}
430
431void CommentASTToHTMLConverter::visitParagraphComment(
432 const ParagraphComment *C) {
433 if (C->isWhitespace())
434 return;
435
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000436 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000437 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
438 I != E; ++I) {
439 visit(*I);
440 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000441 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000442}
443
444void CommentASTToHTMLConverter::visitBlockCommandComment(
445 const BlockCommandComment *C) {
446 StringRef CommandName = C->getCommandName();
447 if (CommandName == "brief" || CommandName == "short") {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000448 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000449 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000450 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000451 return;
452 }
Dmitri Gribenko2c6b00e2012-07-25 17:14:58 +0000453 if (CommandName == "returns" || CommandName == "return" ||
454 CommandName == "result") {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000455 Result << "<p class=\"para-returns\">"
456 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000457 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000458 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000459 return;
460 }
461 // We don't know anything about this command. Just render the paragraph.
462 visit(C->getParagraph());
463}
464
465void CommentASTToHTMLConverter::visitParamCommandComment(
466 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000467 if (C->isParamIndexValid()) {
468 Result << "<dt class=\"param-name-index-"
469 << C->getParamIndex()
470 << "\">";
471 } else
472 Result << "<dt class=\"param-name-index-invalid\">";
473
474 Result << C->getParamName() << "</dt>";
475
476 if (C->isParamIndexValid()) {
477 Result << "<dd class=\"param-descr-index-"
478 << C->getParamIndex()
479 << "\">";
480 } else
481 Result << "<dd class=\"param-descr-index-invalid\">";
482
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000483 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000484 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000485}
486
487void CommentASTToHTMLConverter::visitVerbatimBlockComment(
488 const VerbatimBlockComment *C) {
489 unsigned NumLines = C->getNumLines();
490 if (NumLines == 0)
491 return;
492
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000493 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000494 for (unsigned i = 0; i != NumLines; ++i) {
495 appendToResultWithHTMLEscaping(C->getText(i));
496 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000497 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000498 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000499 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000500}
501
502void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
503 const VerbatimBlockLineComment *C) {
504 llvm_unreachable("should not see this AST node");
505}
506
507void CommentASTToHTMLConverter::visitVerbatimLineComment(
508 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000509 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000510 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000511 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000512}
513
514void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
515 const BlockContentComment *Brief = NULL;
516 const ParagraphComment *FirstParagraph = NULL;
517 const BlockCommandComment *Returns = NULL;
518 SmallVector<const ParamCommandComment *, 8> Params;
519 SmallVector<const BlockContentComment *, 8> MiscBlocks;
520
521 // Extract various blocks into separate variables and vectors above.
522 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
523 I != E; ++I) {
524 const Comment *Child = *I;
525 if (!Child)
526 continue;
527 switch (Child->getCommentKind()) {
528 case Comment::NoCommentKind:
529 continue;
530
531 case Comment::ParagraphCommentKind: {
532 const ParagraphComment *PC = cast<ParagraphComment>(Child);
533 if (PC->isWhitespace())
534 break;
535 if (!FirstParagraph)
536 FirstParagraph = PC;
537
538 MiscBlocks.push_back(PC);
539 break;
540 }
541
542 case Comment::BlockCommandCommentKind: {
543 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
544 StringRef CommandName = BCC->getCommandName();
545 if (!Brief && (CommandName == "brief" || CommandName == "short")) {
546 Brief = BCC;
547 break;
548 }
549 if (!Returns && (CommandName == "returns" || CommandName == "return")) {
550 Returns = BCC;
551 break;
552 }
553 MiscBlocks.push_back(BCC);
554 break;
555 }
556
557 case Comment::ParamCommandCommentKind: {
558 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
559 if (!PCC->hasParamName())
560 break;
561
562 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
563 break;
564
565 Params.push_back(PCC);
566 break;
567 }
568
569 case Comment::VerbatimBlockCommentKind:
570 case Comment::VerbatimLineCommentKind:
571 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
572 break;
573
574 case Comment::TextCommentKind:
575 case Comment::InlineCommandCommentKind:
576 case Comment::HTMLStartTagCommentKind:
577 case Comment::HTMLEndTagCommentKind:
578 case Comment::VerbatimBlockLineCommentKind:
579 case Comment::FullCommentKind:
580 llvm_unreachable("AST node of this kind can't be a child of "
581 "a FullComment");
582 }
583 }
584
585 // Sort params in order they are declared in the function prototype.
586 // Unresolved parameters are put at the end of the list in the same order
587 // they were seen in the comment.
588 std::stable_sort(Params.begin(), Params.end(),
589 ParamCommandCommentCompareIndex());
590
591 bool FirstParagraphIsBrief = false;
592 if (Brief)
593 visit(Brief);
594 else if (FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000595 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000596 visitNonStandaloneParagraphComment(FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000597 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000598 FirstParagraphIsBrief = true;
599 }
600
601 for (unsigned i = 0, e = MiscBlocks.size(); i != e; ++i) {
602 const Comment *C = MiscBlocks[i];
603 if (FirstParagraphIsBrief && C == FirstParagraph)
604 continue;
605 visit(C);
606 }
607
608 if (Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000609 Result << "<dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000610 for (unsigned i = 0, e = Params.size(); i != e; ++i)
611 visit(Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000612 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000613 }
614
615 if (Returns)
616 visit(Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000617
618 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000619}
620
621void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
622 const ParagraphComment *C) {
623 if (!C)
624 return;
625
626 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
627 I != E; ++I) {
628 visit(*I);
629 }
630}
631
632void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000633 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
634 const char C = *I;
635 switch (C) {
636 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000637 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000638 break;
639 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000640 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000641 break;
642 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000643 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000644 break;
645 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000646 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000647 break;
648 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000649 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000650 break;
651 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000652 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000653 break;
654 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000655 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000656 break;
657 }
658 }
659}
660
661extern "C" {
662
663CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
664 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
665 if (!HTC)
666 return createCXString((const char *) 0);
667
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000668 SmallString<128> HTML;
669 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000670 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000671 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000672}
673
674CXString clang_FullComment_getAsHTML(CXComment CXC) {
675 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
676 if (!FC)
677 return createCXString((const char *) 0);
678
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000679 SmallString<1024> HTML;
680 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000681 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000682 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000683}
684
685} // end extern "C"
686