blob: 0d971c390a39851ddc7b09c030c68eceeedafac0 [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
Dmitri Gribenko221a6d72012-07-30 17:49:32 +000023#include <climits>
24
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000025using namespace clang;
26using namespace clang::cxstring;
27using namespace clang::comments;
28using namespace clang::cxcomment;
29
30extern "C" {
31
32enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
33 const Comment *C = getASTNode(CXC);
34 if (!C)
35 return CXComment_Null;
36
37 switch (C->getCommentKind()) {
38 case Comment::NoCommentKind:
39 return CXComment_Null;
40
41 case Comment::TextCommentKind:
42 return CXComment_Text;
43
44 case Comment::InlineCommandCommentKind:
45 return CXComment_InlineCommand;
46
47 case Comment::HTMLStartTagCommentKind:
48 return CXComment_HTMLStartTag;
49
50 case Comment::HTMLEndTagCommentKind:
51 return CXComment_HTMLEndTag;
52
53 case Comment::ParagraphCommentKind:
54 return CXComment_Paragraph;
55
56 case Comment::BlockCommandCommentKind:
57 return CXComment_BlockCommand;
58
59 case Comment::ParamCommandCommentKind:
60 return CXComment_ParamCommand;
61
Dmitri Gribenko96b09862012-07-31 22:37:06 +000062 case Comment::TParamCommandCommentKind:
63 return CXComment_TParamCommand;
64
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000065 case Comment::VerbatimBlockCommentKind:
66 return CXComment_VerbatimBlockCommand;
67
68 case Comment::VerbatimBlockLineCommentKind:
69 return CXComment_VerbatimBlockLine;
70
71 case Comment::VerbatimLineCommentKind:
72 return CXComment_VerbatimLine;
73
74 case Comment::FullCommentKind:
75 return CXComment_FullComment;
76 }
77 llvm_unreachable("unknown CommentKind");
78}
79
80unsigned clang_Comment_getNumChildren(CXComment CXC) {
81 const Comment *C = getASTNode(CXC);
82 if (!C)
83 return 0;
84
85 return C->child_count();
86}
87
88CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
89 const Comment *C = getASTNode(CXC);
90 if (!C || ChildIdx >= C->child_count())
91 return createCXComment(NULL);
92
93 return createCXComment(*(C->child_begin() + ChildIdx));
94}
95
96unsigned clang_Comment_isWhitespace(CXComment CXC) {
97 const Comment *C = getASTNode(CXC);
98 if (!C)
99 return false;
100
101 if (const TextComment *TC = dyn_cast<TextComment>(C))
102 return TC->isWhitespace();
103
104 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
105 return PC->isWhitespace();
106
107 return false;
108}
109
110unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
111 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
112 if (!ICC)
113 return false;
114
115 return ICC->hasTrailingNewline();
116}
117
118CXString clang_TextComment_getText(CXComment CXC) {
119 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
120 if (!TC)
121 return createCXString((const char *) 0);
122
123 return createCXString(TC->getText(), /*DupString=*/ false);
124}
125
126CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
127 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
128 if (!ICC)
129 return createCXString((const char *) 0);
130
131 return createCXString(ICC->getCommandName(), /*DupString=*/ false);
132}
133
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000134enum CXCommentInlineCommandRenderKind
135clang_InlineCommandComment_getRenderKind(CXComment CXC) {
136 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
137 if (!ICC)
138 return CXCommentInlineCommandRenderKind_Normal;
139
140 switch (ICC->getRenderKind()) {
141 case InlineCommandComment::RenderNormal:
142 return CXCommentInlineCommandRenderKind_Normal;
143
144 case InlineCommandComment::RenderBold:
145 return CXCommentInlineCommandRenderKind_Bold;
146
147 case InlineCommandComment::RenderMonospaced:
148 return CXCommentInlineCommandRenderKind_Monospaced;
149
150 case InlineCommandComment::RenderEmphasized:
151 return CXCommentInlineCommandRenderKind_Emphasized;
152 }
153 llvm_unreachable("unknown InlineCommandComment::RenderKind");
154}
155
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000156unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
157 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
158 if (!ICC)
159 return 0;
160
161 return ICC->getNumArgs();
162}
163
164CXString clang_InlineCommandComment_getArgText(CXComment CXC,
165 unsigned ArgIdx) {
166 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
167 if (!ICC || ArgIdx >= ICC->getNumArgs())
168 return createCXString((const char *) 0);
169
170 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
171}
172
173CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
174 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
175 if (!HTC)
176 return createCXString((const char *) 0);
177
178 return createCXString(HTC->getTagName(), /*DupString=*/ false);
179}
180
181unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
182 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
183 if (!HST)
184 return false;
185
186 return HST->isSelfClosing();
187}
188
189unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
190 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
191 if (!HST)
192 return 0;
193
194 return HST->getNumAttrs();
195}
196
197CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
198 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
199 if (!HST || AttrIdx >= HST->getNumAttrs())
200 return createCXString((const char *) 0);
201
202 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
203}
204
205CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
206 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
207 if (!HST || AttrIdx >= HST->getNumAttrs())
208 return createCXString((const char *) 0);
209
210 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
211}
212
213CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
214 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
215 if (!BCC)
216 return createCXString((const char *) 0);
217
218 return createCXString(BCC->getCommandName(), /*DupString=*/ false);
219}
220
221unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
222 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
223 if (!BCC)
224 return 0;
225
226 return BCC->getNumArgs();
227}
228
229CXString clang_BlockCommandComment_getArgText(CXComment CXC,
230 unsigned ArgIdx) {
231 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
232 if (!BCC || ArgIdx >= BCC->getNumArgs())
233 return createCXString((const char *) 0);
234
235 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
236}
237
238CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
239 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
240 if (!BCC)
241 return createCXComment(NULL);
242
243 return createCXComment(BCC->getParagraph());
244}
245
246CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
247 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko1f8c5292012-07-23 19:41:49 +0000248 if (!PCC || !PCC->hasParamName())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000249 return createCXString((const char *) 0);
250
251 return createCXString(PCC->getParamName(), /*DupString=*/ false);
252}
253
254unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
255 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
256 if (!PCC)
257 return false;
258
259 return PCC->isParamIndexValid();
260}
261
262unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
263 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000264 if (!PCC || !PCC->isParamIndexValid())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000265 return ParamCommandComment::InvalidParamIndex;
266
267 return PCC->getParamIndex();
268}
269
270unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
271 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
272 if (!PCC)
273 return false;
274
275 return PCC->isDirectionExplicit();
276}
277
278enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
279 CXComment CXC) {
280 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
281 if (!PCC)
282 return CXCommentParamPassDirection_In;
283
284 switch (PCC->getDirection()) {
285 case ParamCommandComment::In:
286 return CXCommentParamPassDirection_In;
287
288 case ParamCommandComment::Out:
289 return CXCommentParamPassDirection_Out;
290
291 case ParamCommandComment::InOut:
292 return CXCommentParamPassDirection_InOut;
293 }
294 llvm_unreachable("unknown ParamCommandComment::PassDirection");
295}
296
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000297CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
298 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
299 if (!TPCC || !TPCC->hasParamName())
300 return createCXString((const char *) 0);
301
302 return createCXString(TPCC->getParamName(), /*DupString=*/ false);
303}
304
305unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
306 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
307 if (!TPCC)
308 return false;
309
310 return TPCC->isPositionValid();
311}
312
313unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
314 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
315 if (!TPCC || !TPCC->isPositionValid())
316 return 0;
317
318 return TPCC->getDepth();
319}
320
321unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
322 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
323 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
324 return 0;
325
326 return TPCC->getIndex(Depth);
327}
328
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000329CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
330 const VerbatimBlockLineComment *VBL =
331 getASTNodeAs<VerbatimBlockLineComment>(CXC);
332 if (!VBL)
333 return createCXString((const char *) 0);
334
335 return createCXString(VBL->getText(), /*DupString=*/ false);
336}
337
338CXString clang_VerbatimLineComment_getText(CXComment CXC) {
339 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
340 if (!VLC)
341 return createCXString((const char *) 0);
342
343 return createCXString(VLC->getText(), /*DupString=*/ false);
344}
345
346} // end extern "C"
347
348//===----------------------------------------------------------------------===//
349// Helpers for converting comment AST to HTML.
350//===----------------------------------------------------------------------===//
351
352namespace {
353
Dmitri Gribenkoe5db09c2012-07-30 19:47:34 +0000354/// This comparison will sort parameters with valid index by index and
355/// invalid (unresolved) parameters last.
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000356class ParamCommandCommentCompareIndex {
357public:
358 bool operator()(const ParamCommandComment *LHS,
359 const ParamCommandComment *RHS) const {
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000360 unsigned LHSIndex = UINT_MAX;
361 unsigned RHSIndex = UINT_MAX;
362 if (LHS->isParamIndexValid())
363 LHSIndex = LHS->getParamIndex();
364 if (RHS->isParamIndexValid())
365 RHSIndex = RHS->getParamIndex();
366
367 return LHSIndex < RHSIndex;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000368 }
369};
370
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000371/// This comparison will sort template parameters in the following order:
372/// \li real template parameters (depth = 1) in index order;
373/// \li all other names (depth > 1);
374/// \li unresolved names.
375class TParamCommandCommentComparePosition {
376public:
377 bool operator()(const TParamCommandComment *LHS,
378 const TParamCommandComment *RHS) const {
379 // Sort unresolved names last.
380 if (!LHS->isPositionValid())
381 return false;
382 if (!RHS->isPositionValid())
383 return true;
384
385 if (LHS->getDepth() > 1)
386 return false;
387 if (RHS->getDepth() > 1)
388 return true;
389
390 // Sort template parameters in index order.
391 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
392 return LHS->getIndex(0) < RHS->getIndex(0);
393
394 // Leave all other names in source order.
395 return true;
396 }
397};
398
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000399class CommentASTToHTMLConverter :
400 public ConstCommentVisitor<CommentASTToHTMLConverter> {
401public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000402 /// \param Str accumulator for HTML.
403 CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000404
405 // Inline content.
406 void visitTextComment(const TextComment *C);
407 void visitInlineCommandComment(const InlineCommandComment *C);
408 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
409 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
410
411 // Block content.
412 void visitParagraphComment(const ParagraphComment *C);
413 void visitBlockCommandComment(const BlockCommandComment *C);
414 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000415 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000416 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
417 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
418 void visitVerbatimLineComment(const VerbatimLineComment *C);
419
420 void visitFullComment(const FullComment *C);
421
422 // Helpers.
423
424 /// Convert a paragraph that is not a block by itself (an argument to some
425 /// command).
426 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
427
428 void appendToResultWithHTMLEscaping(StringRef S);
429
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000430private:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000431 /// Output stream for HTML.
432 llvm::raw_svector_ostream Result;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000433};
434} // end unnamed namespace
435
436void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
437 appendToResultWithHTMLEscaping(C->getText());
438}
439
440void CommentASTToHTMLConverter::visitInlineCommandComment(
441 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000442 // Nothing to render if no arguments supplied.
443 if (C->getNumArgs() == 0)
444 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000445
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000446 // Nothing to render if argument is empty.
447 StringRef Arg0 = C->getArgText(0);
448 if (Arg0.empty())
449 return;
450
451 switch (C->getRenderKind()) {
452 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000453 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
454 appendToResultWithHTMLEscaping(C->getArgText(i));
455 Result << " ";
456 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000457 return;
458
459 case InlineCommandComment::RenderBold:
460 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000461 Result << "<b>";
462 appendToResultWithHTMLEscaping(Arg0);
463 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000464 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000465 case InlineCommandComment::RenderMonospaced:
466 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000467 Result << "<tt>";
468 appendToResultWithHTMLEscaping(Arg0);
469 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000470 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000471 case InlineCommandComment::RenderEmphasized:
472 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000473 Result << "<em>";
474 appendToResultWithHTMLEscaping(Arg0);
475 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000476 return;
477 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000478}
479
480void CommentASTToHTMLConverter::visitHTMLStartTagComment(
481 const HTMLStartTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000482 Result << "<" << C->getTagName();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000483
484 if (C->getNumAttrs() != 0) {
485 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000486 Result << " ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000487 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000488 Result << Attr.Name;
489 if (!Attr.Value.empty())
490 Result << "=\"" << Attr.Value << "\"";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000491 }
492 }
493
494 if (!C->isSelfClosing())
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000495 Result << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000496 else
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000497 Result << "/>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000498}
499
500void CommentASTToHTMLConverter::visitHTMLEndTagComment(
501 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000502 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000503}
504
505void CommentASTToHTMLConverter::visitParagraphComment(
506 const ParagraphComment *C) {
507 if (C->isWhitespace())
508 return;
509
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000510 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000511 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
512 I != E; ++I) {
513 visit(*I);
514 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000515 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000516}
517
518void CommentASTToHTMLConverter::visitBlockCommandComment(
519 const BlockCommandComment *C) {
520 StringRef CommandName = C->getCommandName();
521 if (CommandName == "brief" || CommandName == "short") {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000522 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000523 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000524 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000525 return;
526 }
Dmitri Gribenko2c6b00e2012-07-25 17:14:58 +0000527 if (CommandName == "returns" || CommandName == "return" ||
528 CommandName == "result") {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000529 Result << "<p class=\"para-returns\">"
530 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000531 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000532 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000533 return;
534 }
535 // We don't know anything about this command. Just render the paragraph.
536 visit(C->getParagraph());
537}
538
539void CommentASTToHTMLConverter::visitParamCommandComment(
540 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000541 if (C->isParamIndexValid()) {
542 Result << "<dt class=\"param-name-index-"
543 << C->getParamIndex()
544 << "\">";
545 } else
546 Result << "<dt class=\"param-name-index-invalid\">";
547
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000548 appendToResultWithHTMLEscaping(C->getParamName());
549 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000550
551 if (C->isParamIndexValid()) {
552 Result << "<dd class=\"param-descr-index-"
553 << C->getParamIndex()
554 << "\">";
555 } else
556 Result << "<dd class=\"param-descr-index-invalid\">";
557
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000558 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000559 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000560}
561
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000562void CommentASTToHTMLConverter::visitTParamCommandComment(
563 const TParamCommandComment *C) {
564 if (C->isPositionValid()) {
565 if (C->getDepth() == 1)
566 Result << "<dt class=\"taram-name-index-"
567 << C->getIndex(0)
568 << "\">";
569 else
570 Result << "<dt class=\"taram-name-index-other\">";
571 } else
572 Result << "<dt class=\"tparam-name-index-invalid\">";
573
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000574 appendToResultWithHTMLEscaping(C->getParamName());
575 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000576
577 if (C->isPositionValid()) {
578 if (C->getDepth() == 1)
579 Result << "<dd class=\"tparam-descr-index-"
580 << C->getIndex(0)
581 << "\">";
582 else
583 Result << "<dd class=\"tparam-descr-index-other\">";
584 } else
585 Result << "<dd class=\"tparam-descr-index-invalid\">";
586
587 visitNonStandaloneParagraphComment(C->getParagraph());
588 Result << "</dd>";
589}
590
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000591void CommentASTToHTMLConverter::visitVerbatimBlockComment(
592 const VerbatimBlockComment *C) {
593 unsigned NumLines = C->getNumLines();
594 if (NumLines == 0)
595 return;
596
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000597 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000598 for (unsigned i = 0; i != NumLines; ++i) {
599 appendToResultWithHTMLEscaping(C->getText(i));
600 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000601 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000602 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000603 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000604}
605
606void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
607 const VerbatimBlockLineComment *C) {
608 llvm_unreachable("should not see this AST node");
609}
610
611void CommentASTToHTMLConverter::visitVerbatimLineComment(
612 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000613 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000614 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000615 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000616}
617
618void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
619 const BlockContentComment *Brief = NULL;
620 const ParagraphComment *FirstParagraph = NULL;
621 const BlockCommandComment *Returns = NULL;
622 SmallVector<const ParamCommandComment *, 8> Params;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000623 SmallVector<const TParamCommandComment *, 4> TParams;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000624 SmallVector<const BlockContentComment *, 8> MiscBlocks;
625
626 // Extract various blocks into separate variables and vectors above.
627 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
628 I != E; ++I) {
629 const Comment *Child = *I;
630 if (!Child)
631 continue;
632 switch (Child->getCommentKind()) {
633 case Comment::NoCommentKind:
634 continue;
635
636 case Comment::ParagraphCommentKind: {
637 const ParagraphComment *PC = cast<ParagraphComment>(Child);
638 if (PC->isWhitespace())
639 break;
640 if (!FirstParagraph)
641 FirstParagraph = PC;
642
643 MiscBlocks.push_back(PC);
644 break;
645 }
646
647 case Comment::BlockCommandCommentKind: {
648 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
649 StringRef CommandName = BCC->getCommandName();
650 if (!Brief && (CommandName == "brief" || CommandName == "short")) {
651 Brief = BCC;
652 break;
653 }
654 if (!Returns && (CommandName == "returns" || CommandName == "return")) {
655 Returns = BCC;
656 break;
657 }
658 MiscBlocks.push_back(BCC);
659 break;
660 }
661
662 case Comment::ParamCommandCommentKind: {
663 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
664 if (!PCC->hasParamName())
665 break;
666
667 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
668 break;
669
670 Params.push_back(PCC);
671 break;
672 }
673
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000674 case Comment::TParamCommandCommentKind: {
675 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
676 if (!TPCC->hasParamName())
677 break;
678
679 TParams.push_back(TPCC);
680 break;
681 }
682
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000683 case Comment::VerbatimBlockCommentKind:
684 case Comment::VerbatimLineCommentKind:
685 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
686 break;
687
688 case Comment::TextCommentKind:
689 case Comment::InlineCommandCommentKind:
690 case Comment::HTMLStartTagCommentKind:
691 case Comment::HTMLEndTagCommentKind:
692 case Comment::VerbatimBlockLineCommentKind:
693 case Comment::FullCommentKind:
694 llvm_unreachable("AST node of this kind can't be a child of "
695 "a FullComment");
696 }
697 }
698
699 // Sort params in order they are declared in the function prototype.
700 // Unresolved parameters are put at the end of the list in the same order
701 // they were seen in the comment.
702 std::stable_sort(Params.begin(), Params.end(),
703 ParamCommandCommentCompareIndex());
704
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000705 std::stable_sort(TParams.begin(), TParams.end(),
706 TParamCommandCommentComparePosition());
707
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000708 bool FirstParagraphIsBrief = false;
709 if (Brief)
710 visit(Brief);
711 else if (FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000712 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000713 visitNonStandaloneParagraphComment(FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000714 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000715 FirstParagraphIsBrief = true;
716 }
717
718 for (unsigned i = 0, e = MiscBlocks.size(); i != e; ++i) {
719 const Comment *C = MiscBlocks[i];
720 if (FirstParagraphIsBrief && C == FirstParagraph)
721 continue;
722 visit(C);
723 }
724
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000725 if (TParams.size() != 0) {
726 Result << "<dl>";
727 for (unsigned i = 0, e = TParams.size(); i != e; ++i)
728 visit(TParams[i]);
729 Result << "</dl>";
730 }
731
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000732 if (Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000733 Result << "<dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000734 for (unsigned i = 0, e = Params.size(); i != e; ++i)
735 visit(Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000736 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000737 }
738
739 if (Returns)
740 visit(Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000741
742 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000743}
744
745void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
746 const ParagraphComment *C) {
747 if (!C)
748 return;
749
750 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
751 I != E; ++I) {
752 visit(*I);
753 }
754}
755
756void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000757 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
758 const char C = *I;
759 switch (C) {
760 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000761 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000762 break;
763 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000764 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000765 break;
766 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000767 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000768 break;
769 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000770 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000771 break;
772 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000773 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000774 break;
775 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000776 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000777 break;
778 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000779 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000780 break;
781 }
782 }
783}
784
785extern "C" {
786
787CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
788 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
789 if (!HTC)
790 return createCXString((const char *) 0);
791
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000792 SmallString<128> HTML;
793 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000794 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000795 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000796}
797
798CXString clang_FullComment_getAsHTML(CXComment CXC) {
799 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
800 if (!FC)
801 return createCXString((const char *) 0);
802
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000803 SmallString<1024> HTML;
804 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000805 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000806 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000807}
808
809} // end extern "C"
810