blob: 8d01a4192f13c8c2f8a9323d6d656ae623e66f42 [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"
21
22using namespace clang;
23using namespace clang::cxstring;
24using namespace clang::comments;
25using namespace clang::cxcomment;
26
27extern "C" {
28
29enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
30 const Comment *C = getASTNode(CXC);
31 if (!C)
32 return CXComment_Null;
33
34 switch (C->getCommentKind()) {
35 case Comment::NoCommentKind:
36 return CXComment_Null;
37
38 case Comment::TextCommentKind:
39 return CXComment_Text;
40
41 case Comment::InlineCommandCommentKind:
42 return CXComment_InlineCommand;
43
44 case Comment::HTMLStartTagCommentKind:
45 return CXComment_HTMLStartTag;
46
47 case Comment::HTMLEndTagCommentKind:
48 return CXComment_HTMLEndTag;
49
50 case Comment::ParagraphCommentKind:
51 return CXComment_Paragraph;
52
53 case Comment::BlockCommandCommentKind:
54 return CXComment_BlockCommand;
55
56 case Comment::ParamCommandCommentKind:
57 return CXComment_ParamCommand;
58
59 case Comment::VerbatimBlockCommentKind:
60 return CXComment_VerbatimBlockCommand;
61
62 case Comment::VerbatimBlockLineCommentKind:
63 return CXComment_VerbatimBlockLine;
64
65 case Comment::VerbatimLineCommentKind:
66 return CXComment_VerbatimLine;
67
68 case Comment::FullCommentKind:
69 return CXComment_FullComment;
70 }
71 llvm_unreachable("unknown CommentKind");
72}
73
74unsigned clang_Comment_getNumChildren(CXComment CXC) {
75 const Comment *C = getASTNode(CXC);
76 if (!C)
77 return 0;
78
79 return C->child_count();
80}
81
82CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
83 const Comment *C = getASTNode(CXC);
84 if (!C || ChildIdx >= C->child_count())
85 return createCXComment(NULL);
86
87 return createCXComment(*(C->child_begin() + ChildIdx));
88}
89
90unsigned clang_Comment_isWhitespace(CXComment CXC) {
91 const Comment *C = getASTNode(CXC);
92 if (!C)
93 return false;
94
95 if (const TextComment *TC = dyn_cast<TextComment>(C))
96 return TC->isWhitespace();
97
98 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
99 return PC->isWhitespace();
100
101 return false;
102}
103
104unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
105 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
106 if (!ICC)
107 return false;
108
109 return ICC->hasTrailingNewline();
110}
111
112CXString clang_TextComment_getText(CXComment CXC) {
113 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
114 if (!TC)
115 return createCXString((const char *) 0);
116
117 return createCXString(TC->getText(), /*DupString=*/ false);
118}
119
120CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
121 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
122 if (!ICC)
123 return createCXString((const char *) 0);
124
125 return createCXString(ICC->getCommandName(), /*DupString=*/ false);
126}
127
128unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
129 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
130 if (!ICC)
131 return 0;
132
133 return ICC->getNumArgs();
134}
135
136CXString clang_InlineCommandComment_getArgText(CXComment CXC,
137 unsigned ArgIdx) {
138 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
139 if (!ICC || ArgIdx >= ICC->getNumArgs())
140 return createCXString((const char *) 0);
141
142 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
143}
144
145CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
146 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
147 if (!HTC)
148 return createCXString((const char *) 0);
149
150 return createCXString(HTC->getTagName(), /*DupString=*/ false);
151}
152
153unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
154 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
155 if (!HST)
156 return false;
157
158 return HST->isSelfClosing();
159}
160
161unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
162 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
163 if (!HST)
164 return 0;
165
166 return HST->getNumAttrs();
167}
168
169CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
170 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
171 if (!HST || AttrIdx >= HST->getNumAttrs())
172 return createCXString((const char *) 0);
173
174 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
175}
176
177CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
178 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
179 if (!HST || AttrIdx >= HST->getNumAttrs())
180 return createCXString((const char *) 0);
181
182 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
183}
184
185CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
186 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
187 if (!BCC)
188 return createCXString((const char *) 0);
189
190 return createCXString(BCC->getCommandName(), /*DupString=*/ false);
191}
192
193unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
194 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
195 if (!BCC)
196 return 0;
197
198 return BCC->getNumArgs();
199}
200
201CXString clang_BlockCommandComment_getArgText(CXComment CXC,
202 unsigned ArgIdx) {
203 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
204 if (!BCC || ArgIdx >= BCC->getNumArgs())
205 return createCXString((const char *) 0);
206
207 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
208}
209
210CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
211 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
212 if (!BCC)
213 return createCXComment(NULL);
214
215 return createCXComment(BCC->getParagraph());
216}
217
218CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
219 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
220 if (!PCC)
221 return createCXString((const char *) 0);
222
223 return createCXString(PCC->getParamName(), /*DupString=*/ false);
224}
225
226unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
227 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
228 if (!PCC)
229 return false;
230
231 return PCC->isParamIndexValid();
232}
233
234unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
235 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
236 if (!PCC)
237 return ParamCommandComment::InvalidParamIndex;
238
239 return PCC->getParamIndex();
240}
241
242unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
243 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
244 if (!PCC)
245 return false;
246
247 return PCC->isDirectionExplicit();
248}
249
250enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
251 CXComment CXC) {
252 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
253 if (!PCC)
254 return CXCommentParamPassDirection_In;
255
256 switch (PCC->getDirection()) {
257 case ParamCommandComment::In:
258 return CXCommentParamPassDirection_In;
259
260 case ParamCommandComment::Out:
261 return CXCommentParamPassDirection_Out;
262
263 case ParamCommandComment::InOut:
264 return CXCommentParamPassDirection_InOut;
265 }
266 llvm_unreachable("unknown ParamCommandComment::PassDirection");
267}
268
269CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
270 const VerbatimBlockLineComment *VBL =
271 getASTNodeAs<VerbatimBlockLineComment>(CXC);
272 if (!VBL)
273 return createCXString((const char *) 0);
274
275 return createCXString(VBL->getText(), /*DupString=*/ false);
276}
277
278CXString clang_VerbatimLineComment_getText(CXComment CXC) {
279 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
280 if (!VLC)
281 return createCXString((const char *) 0);
282
283 return createCXString(VLC->getText(), /*DupString=*/ false);
284}
285
286} // end extern "C"
287
288//===----------------------------------------------------------------------===//
289// Helpers for converting comment AST to HTML.
290//===----------------------------------------------------------------------===//
291
292namespace {
293
294class ParamCommandCommentCompareIndex {
295public:
296 bool operator()(const ParamCommandComment *LHS,
297 const ParamCommandComment *RHS) const {
298 // To sort invalid (unresolved) parameters last, this comparison relies on
299 // invalid indices to be UINT_MAX.
300 return LHS->getParamIndex() < RHS->getParamIndex();
301 }
302};
303
304class CommentASTToHTMLConverter :
305 public ConstCommentVisitor<CommentASTToHTMLConverter> {
306public:
307 CommentASTToHTMLConverter() { }
308
309 // Inline content.
310 void visitTextComment(const TextComment *C);
311 void visitInlineCommandComment(const InlineCommandComment *C);
312 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
313 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
314
315 // Block content.
316 void visitParagraphComment(const ParagraphComment *C);
317 void visitBlockCommandComment(const BlockCommandComment *C);
318 void visitParamCommandComment(const ParamCommandComment *C);
319 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
320 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
321 void visitVerbatimLineComment(const VerbatimLineComment *C);
322
323 void visitFullComment(const FullComment *C);
324
325 // Helpers.
326
327 /// Convert a paragraph that is not a block by itself (an argument to some
328 /// command).
329 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
330
331 void appendToResultWithHTMLEscaping(StringRef S);
332
333 StringRef getAsHTML() const {
334 return Result;
335 }
336
337private:
338 /// Accumulator for converted HTML.
339 std::string Result;
340};
341} // end unnamed namespace
342
343void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
344 appendToResultWithHTMLEscaping(C->getText());
345}
346
347void CommentASTToHTMLConverter::visitInlineCommandComment(
348 const InlineCommandComment *C) {
349 StringRef CommandName = C->getCommandName();
350 bool HasArg0 = C->getNumArgs() > 0 && !C->getArgText(0).empty();
351 StringRef Arg0;
352 if (HasArg0)
353 Arg0 = C->getArgText(0);
354
355 if (CommandName == "b") {
356 if (!HasArg0)
357 return;
358 Result += "<b>";
359 Result += Arg0;
360 Result += "</b>";
361 return;
362 }
363 if (CommandName == "c" || CommandName == "p") {
364 if (!HasArg0)
365 return;
366 Result += "<tt>";
367 Result += Arg0;
368 Result += "</tt>";
369 return;
370 }
371 if (CommandName == "a" || CommandName == "e" || CommandName == "em") {
372 if (!HasArg0)
373 return;
374 Result += "<em>";
375 Result += Arg0;
376 Result += "</em>";
377 return;
378 }
379
380 // We don't recognize this command, so just print its arguments.
381 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
382 Result += C->getArgText(i);
383 Result += " ";
384 }
385}
386
387void CommentASTToHTMLConverter::visitHTMLStartTagComment(
388 const HTMLStartTagComment *C) {
389 Result += "<";
390 Result += C->getTagName();
391
392 if (C->getNumAttrs() != 0) {
393 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
394 Result += " ";
395 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
396 Result += Attr.Name;
397 if (!Attr.Value.empty()) {
398 Result += "=\"";
399 Result += Attr.Value;
400 Result += " \"";
401 }
402 }
403 }
404
405 if (!C->isSelfClosing())
406 Result += ">";
407 else
408 Result += "/>";
409}
410
411void CommentASTToHTMLConverter::visitHTMLEndTagComment(
412 const HTMLEndTagComment *C) {
413 Result += "</";
414 Result += C->getTagName();
415 Result += ">";
416}
417
418void CommentASTToHTMLConverter::visitParagraphComment(
419 const ParagraphComment *C) {
420 if (C->isWhitespace())
421 return;
422
423 Result += "<p>";
424 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
425 I != E; ++I) {
426 visit(*I);
427 }
428 Result += "</p>";
429}
430
431void CommentASTToHTMLConverter::visitBlockCommandComment(
432 const BlockCommandComment *C) {
433 StringRef CommandName = C->getCommandName();
434 if (CommandName == "brief" || CommandName == "short") {
435 Result += "<p class=\"para-brief\">";
436 visitNonStandaloneParagraphComment(C->getParagraph());
437 Result += "</p>";
438 return;
439 }
440 if (CommandName == "returns" || CommandName == "return") {
441 Result += "<p class=\"para-returns\">";
442 Result += "<span class=\"word-returns\">Returns</span> ";
443 visitNonStandaloneParagraphComment(C->getParagraph());
444 Result += "</p>";
445 return;
446 }
447 // We don't know anything about this command. Just render the paragraph.
448 visit(C->getParagraph());
449}
450
451void CommentASTToHTMLConverter::visitParamCommandComment(
452 const ParamCommandComment *C) {
453 Result += "<dt>";
454 Result += C->getParamName();
455 Result += "</dt>";
456 Result += "<dd>";
457 visitNonStandaloneParagraphComment(C->getParagraph());
458 Result += "</dd>";
459}
460
461void CommentASTToHTMLConverter::visitVerbatimBlockComment(
462 const VerbatimBlockComment *C) {
463 unsigned NumLines = C->getNumLines();
464 if (NumLines == 0)
465 return;
466
467 Result += "<pre>";
468 for (unsigned i = 0; i != NumLines; ++i) {
469 appendToResultWithHTMLEscaping(C->getText(i));
470 if (i + 1 != NumLines)
471 Result.append("\n");
472 }
473 Result += "</pre>";
474}
475
476void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
477 const VerbatimBlockLineComment *C) {
478 llvm_unreachable("should not see this AST node");
479}
480
481void CommentASTToHTMLConverter::visitVerbatimLineComment(
482 const VerbatimLineComment *C) {
483 Result += "<pre>";
484 appendToResultWithHTMLEscaping(C->getText());
485 Result += "</pre>";
486}
487
488void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
489 const BlockContentComment *Brief = NULL;
490 const ParagraphComment *FirstParagraph = NULL;
491 const BlockCommandComment *Returns = NULL;
492 SmallVector<const ParamCommandComment *, 8> Params;
493 SmallVector<const BlockContentComment *, 8> MiscBlocks;
494
495 // Extract various blocks into separate variables and vectors above.
496 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
497 I != E; ++I) {
498 const Comment *Child = *I;
499 if (!Child)
500 continue;
501 switch (Child->getCommentKind()) {
502 case Comment::NoCommentKind:
503 continue;
504
505 case Comment::ParagraphCommentKind: {
506 const ParagraphComment *PC = cast<ParagraphComment>(Child);
507 if (PC->isWhitespace())
508 break;
509 if (!FirstParagraph)
510 FirstParagraph = PC;
511
512 MiscBlocks.push_back(PC);
513 break;
514 }
515
516 case Comment::BlockCommandCommentKind: {
517 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
518 StringRef CommandName = BCC->getCommandName();
519 if (!Brief && (CommandName == "brief" || CommandName == "short")) {
520 Brief = BCC;
521 break;
522 }
523 if (!Returns && (CommandName == "returns" || CommandName == "return")) {
524 Returns = BCC;
525 break;
526 }
527 MiscBlocks.push_back(BCC);
528 break;
529 }
530
531 case Comment::ParamCommandCommentKind: {
532 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
533 if (!PCC->hasParamName())
534 break;
535
536 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
537 break;
538
539 Params.push_back(PCC);
540 break;
541 }
542
543 case Comment::VerbatimBlockCommentKind:
544 case Comment::VerbatimLineCommentKind:
545 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
546 break;
547
548 case Comment::TextCommentKind:
549 case Comment::InlineCommandCommentKind:
550 case Comment::HTMLStartTagCommentKind:
551 case Comment::HTMLEndTagCommentKind:
552 case Comment::VerbatimBlockLineCommentKind:
553 case Comment::FullCommentKind:
554 llvm_unreachable("AST node of this kind can't be a child of "
555 "a FullComment");
556 }
557 }
558
559 // Sort params in order they are declared in the function prototype.
560 // Unresolved parameters are put at the end of the list in the same order
561 // they were seen in the comment.
562 std::stable_sort(Params.begin(), Params.end(),
563 ParamCommandCommentCompareIndex());
564
565 bool FirstParagraphIsBrief = false;
566 if (Brief)
567 visit(Brief);
568 else if (FirstParagraph) {
569 Result += "<p class=\"para-brief\">";
570 visitNonStandaloneParagraphComment(FirstParagraph);
571 Result += "</p>";
572 FirstParagraphIsBrief = true;
573 }
574
575 for (unsigned i = 0, e = MiscBlocks.size(); i != e; ++i) {
576 const Comment *C = MiscBlocks[i];
577 if (FirstParagraphIsBrief && C == FirstParagraph)
578 continue;
579 visit(C);
580 }
581
582 if (Params.size() != 0) {
583 Result += "<dl>";
584 for (unsigned i = 0, e = Params.size(); i != e; ++i)
585 visit(Params[i]);
586 Result += "</dl>";
587 }
588
589 if (Returns)
590 visit(Returns);
591}
592
593void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
594 const ParagraphComment *C) {
595 if (!C)
596 return;
597
598 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
599 I != E; ++I) {
600 visit(*I);
601 }
602}
603
604void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
605 Result.reserve(Result.size() + S.size());
606 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
607 const char C = *I;
608 switch (C) {
609 case '&':
610 Result.append("&amp;");
611 break;
612 case '<':
613 Result.append("&lt;");
614 break;
615 case '>':
616 Result.append("&gt;");
617 break;
618 case '"':
619 Result.append("&quot;");
620 break;
621 case '\'':
622 Result.append("&#39;");
623 break;
624 case '/':
625 Result.append("&#47;");
626 break;
627 default:
628 Result.push_back(C);
629 break;
630 }
631 }
632}
633
634extern "C" {
635
636CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
637 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
638 if (!HTC)
639 return createCXString((const char *) 0);
640
641 CommentASTToHTMLConverter Converter;
642 Converter.visit(HTC);
643 return createCXString(Converter.getAsHTML());
644}
645
646CXString clang_FullComment_getAsHTML(CXComment CXC) {
647 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
648 if (!FC)
649 return createCXString((const char *) 0);
650
651 CommentASTToHTMLConverter Converter;
652 Converter.visit(FC);
653 return createCXString(Converter.getAsHTML());
654}
655
656} // end extern "C"
657