1 //===- 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 #include "CXCursor.h"
18
19 #include "clang/AST/CommentVisitor.h"
20 #include "clang/AST/CommentCommandTraits.h"
21 #include "clang/AST/Decl.h"
22
23 #include "llvm/ADT/StringSwitch.h"
24 #include "llvm/Support/ErrorHandling.h"
25 #include "llvm/Support/raw_ostream.h"
26
27 #include <climits>
28
29 using namespace clang;
30 using namespace clang::cxstring;
31 using namespace clang::comments;
32 using namespace clang::cxcomment;
33
34 extern "C" {
35
clang_Comment_getKind(CXComment CXC)36 enum 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
66 case Comment::TParamCommandCommentKind:
67 return CXComment_TParamCommand;
68
69 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
clang_Comment_getNumChildren(CXComment CXC)84 unsigned 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
clang_Comment_getChild(CXComment CXC,unsigned ChildIdx)92 CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
93 const Comment *C = getASTNode(CXC);
94 if (!C || ChildIdx >= C->child_count())
95 return createCXComment(NULL, NULL);
96
97 return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
98 }
99
clang_Comment_isWhitespace(CXComment CXC)100 unsigned 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
clang_InlineContentComment_hasTrailingNewline(CXComment CXC)114 unsigned 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
clang_TextComment_getText(CXComment CXC)122 CXString 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
clang_InlineCommandComment_getCommandName(CXComment CXC)130 CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
131 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
132 if (!ICC)
133 return createCXString((const char *) 0);
134
135 const CommandTraits &Traits = getCommandTraits(CXC);
136 return createCXString(ICC->getCommandName(Traits), /*DupString=*/ false);
137 }
138
139 enum CXCommentInlineCommandRenderKind
clang_InlineCommandComment_getRenderKind(CXComment CXC)140 clang_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
clang_InlineCommandComment_getNumArgs(CXComment CXC)161 unsigned 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
clang_InlineCommandComment_getArgText(CXComment CXC,unsigned ArgIdx)169 CXString 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
clang_HTMLTagComment_getTagName(CXComment CXC)178 CXString 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
clang_HTMLStartTagComment_isSelfClosing(CXComment CXC)186 unsigned 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
clang_HTMLStartTag_getNumAttrs(CXComment CXC)194 unsigned 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
clang_HTMLStartTag_getAttrName(CXComment CXC,unsigned AttrIdx)202 CXString 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
clang_HTMLStartTag_getAttrValue(CXComment CXC,unsigned AttrIdx)210 CXString 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
clang_BlockCommandComment_getCommandName(CXComment CXC)218 CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
219 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
220 if (!BCC)
221 return createCXString((const char *) 0);
222
223 const CommandTraits &Traits = getCommandTraits(CXC);
224 return createCXString(BCC->getCommandName(Traits), /*DupString=*/ false);
225 }
226
clang_BlockCommandComment_getNumArgs(CXComment CXC)227 unsigned 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
clang_BlockCommandComment_getArgText(CXComment CXC,unsigned ArgIdx)235 CXString 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
clang_BlockCommandComment_getParagraph(CXComment CXC)244 CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
245 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
246 if (!BCC)
247 return createCXComment(NULL, NULL);
248
249 return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
250 }
251
clang_ParamCommandComment_getParamName(CXComment CXC)252 CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
253 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
254 if (!PCC || !PCC->hasParamName())
255 return createCXString((const char *) 0);
256
257 return createCXString(PCC->getParamName(), /*DupString=*/ false);
258 }
259
clang_ParamCommandComment_isParamIndexValid(CXComment CXC)260 unsigned 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
clang_ParamCommandComment_getParamIndex(CXComment CXC)268 unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
269 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
270 if (!PCC || !PCC->isParamIndexValid())
271 return ParamCommandComment::InvalidParamIndex;
272
273 return PCC->getParamIndex();
274 }
275
clang_ParamCommandComment_isDirectionExplicit(CXComment CXC)276 unsigned 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
clang_ParamCommandComment_getDirection(CXComment CXC)284 enum 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
clang_TParamCommandComment_getParamName(CXComment CXC)303 CXString 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
clang_TParamCommandComment_isParamPositionValid(CXComment CXC)311 unsigned 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
clang_TParamCommandComment_getDepth(CXComment CXC)319 unsigned 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
clang_TParamCommandComment_getIndex(CXComment CXC,unsigned Depth)327 unsigned 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
clang_VerbatimBlockLineComment_getText(CXComment CXC)335 CXString 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
clang_VerbatimLineComment_getText(CXComment CXC)344 CXString 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
358 namespace {
359
360 /// This comparison will sort parameters with valid index by index and
361 /// invalid (unresolved) parameters last.
362 class ParamCommandCommentCompareIndex {
363 public:
operator ()(const ParamCommandComment * LHS,const ParamCommandComment * RHS) const364 bool operator()(const ParamCommandComment *LHS,
365 const ParamCommandComment *RHS) const {
366 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;
374 }
375 };
376
377 /// 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.
381 class TParamCommandCommentComparePosition {
382 public:
operator ()(const TParamCommandComment * LHS,const TParamCommandComment * RHS) const383 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
405 /// Separate parts of a FullComment.
406 struct FullCommentParts {
407 /// Take a full comment apart and initialize members accordingly.
408 FullCommentParts(const FullComment *C,
409 const CommandTraits &Traits);
410
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
FullCommentParts(const FullComment * C,const CommandTraits & Traits)419 FullCommentParts::FullCommentParts(const FullComment *C,
420 const CommandTraits &Traits) :
421 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);
444 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
445 if (!Brief && Info->IsBriefCommand) {
446 Brief = BCC;
447 break;
448 }
449 if (!Returns && Info->IsReturnsCommand) {
450 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:
482 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
483 break;
484
485 case Comment::VerbatimLineCommentKind: {
486 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
487 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
488 if (!Info->IsDeclarationCommand)
489 MiscBlocks.push_back(VLC);
490 break;
491 }
492
493 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
PrintHTMLStartTagComment(const HTMLStartTagComment * C,llvm::raw_svector_ostream & Result)514 void 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
534 class CommentASTToHTMLConverter :
535 public ConstCommentVisitor<CommentASTToHTMLConverter> {
536 public:
537 /// \param Str accumulator for HTML.
CommentASTToHTMLConverter(SmallVectorImpl<char> & Str,const CommandTraits & Traits)538 CommentASTToHTMLConverter(SmallVectorImpl<char> &Str,
539 const CommandTraits &Traits) :
540 Result(Str), Traits(Traits)
541 { }
542
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);
553 void visitTParamCommandComment(const TParamCommandComment *C);
554 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
568 private:
569 /// Output stream for HTML.
570 llvm::raw_svector_ostream Result;
571
572 const CommandTraits &Traits;
573 };
574 } // end unnamed namespace
575
visitTextComment(const TextComment * C)576 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
577 appendToResultWithHTMLEscaping(C->getText());
578 }
579
visitInlineCommandComment(const InlineCommandComment * C)580 void CommentASTToHTMLConverter::visitInlineCommandComment(
581 const InlineCommandComment *C) {
582 // Nothing to render if no arguments supplied.
583 if (C->getNumArgs() == 0)
584 return;
585
586 // 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:
593 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
594 appendToResultWithHTMLEscaping(C->getArgText(i));
595 Result << " ";
596 }
597 return;
598
599 case InlineCommandComment::RenderBold:
600 assert(C->getNumArgs() == 1);
601 Result << "<b>";
602 appendToResultWithHTMLEscaping(Arg0);
603 Result << "</b>";
604 return;
605 case InlineCommandComment::RenderMonospaced:
606 assert(C->getNumArgs() == 1);
607 Result << "<tt>";
608 appendToResultWithHTMLEscaping(Arg0);
609 Result<< "</tt>";
610 return;
611 case InlineCommandComment::RenderEmphasized:
612 assert(C->getNumArgs() == 1);
613 Result << "<em>";
614 appendToResultWithHTMLEscaping(Arg0);
615 Result << "</em>";
616 return;
617 }
618 }
619
visitHTMLStartTagComment(const HTMLStartTagComment * C)620 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
621 const HTMLStartTagComment *C) {
622 PrintHTMLStartTagComment(C, Result);
623 }
624
visitHTMLEndTagComment(const HTMLEndTagComment * C)625 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
626 const HTMLEndTagComment *C) {
627 Result << "</" << C->getTagName() << ">";
628 }
629
visitParagraphComment(const ParagraphComment * C)630 void CommentASTToHTMLConverter::visitParagraphComment(
631 const ParagraphComment *C) {
632 if (C->isWhitespace())
633 return;
634
635 Result << "<p>";
636 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
637 I != E; ++I) {
638 visit(*I);
639 }
640 Result << "</p>";
641 }
642
visitBlockCommandComment(const BlockCommandComment * C)643 void CommentASTToHTMLConverter::visitBlockCommandComment(
644 const BlockCommandComment *C) {
645 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
646 if (Info->IsBriefCommand) {
647 Result << "<p class=\"para-brief\">";
648 visitNonStandaloneParagraphComment(C->getParagraph());
649 Result << "</p>";
650 return;
651 }
652 if (Info->IsReturnsCommand) {
653 Result << "<p class=\"para-returns\">"
654 "<span class=\"word-returns\">Returns</span> ";
655 visitNonStandaloneParagraphComment(C->getParagraph());
656 Result << "</p>";
657 return;
658 }
659 // We don't know anything about this command. Just render the paragraph.
660 visit(C->getParagraph());
661 }
662
visitParamCommandComment(const ParamCommandComment * C)663 void CommentASTToHTMLConverter::visitParamCommandComment(
664 const ParamCommandComment *C) {
665 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
672 appendToResultWithHTMLEscaping(C->getParamName());
673 Result << "</dt>";
674
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
682 visitNonStandaloneParagraphComment(C->getParagraph());
683 Result << "</dd>";
684 }
685
visitTParamCommandComment(const TParamCommandComment * C)686 void CommentASTToHTMLConverter::visitTParamCommandComment(
687 const TParamCommandComment *C) {
688 if (C->isPositionValid()) {
689 if (C->getDepth() == 1)
690 Result << "<dt class=\"tparam-name-index-"
691 << C->getIndex(0)
692 << "\">";
693 else
694 Result << "<dt class=\"tparam-name-index-other\">";
695 } else
696 Result << "<dt class=\"tparam-name-index-invalid\">";
697
698 appendToResultWithHTMLEscaping(C->getParamName());
699 Result << "</dt>";
700
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
visitVerbatimBlockComment(const VerbatimBlockComment * C)715 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
716 const VerbatimBlockComment *C) {
717 unsigned NumLines = C->getNumLines();
718 if (NumLines == 0)
719 return;
720
721 Result << "<pre>";
722 for (unsigned i = 0; i != NumLines; ++i) {
723 appendToResultWithHTMLEscaping(C->getText(i));
724 if (i + 1 != NumLines)
725 Result << '\n';
726 }
727 Result << "</pre>";
728 }
729
visitVerbatimBlockLineComment(const VerbatimBlockLineComment * C)730 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
731 const VerbatimBlockLineComment *C) {
732 llvm_unreachable("should not see this AST node");
733 }
734
visitVerbatimLineComment(const VerbatimLineComment * C)735 void CommentASTToHTMLConverter::visitVerbatimLineComment(
736 const VerbatimLineComment *C) {
737 Result << "<pre>";
738 appendToResultWithHTMLEscaping(C->getText());
739 Result << "</pre>";
740 }
741
visitFullComment(const FullComment * C)742 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
743 FullCommentParts Parts(C, Traits);
744
745 bool FirstParagraphIsBrief = false;
746 if (Parts.Brief)
747 visit(Parts.Brief);
748 else if (Parts.FirstParagraph) {
749 Result << "<p class=\"para-brief\">";
750 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
751 Result << "</p>";
752 FirstParagraphIsBrief = true;
753 }
754
755 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
756 const Comment *C = Parts.MiscBlocks[i];
757 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
758 continue;
759 visit(C);
760 }
761
762 if (Parts.TParams.size() != 0) {
763 Result << "<dl>";
764 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
765 visit(Parts.TParams[i]);
766 Result << "</dl>";
767 }
768
769 if (Parts.Params.size() != 0) {
770 Result << "<dl>";
771 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
772 visit(Parts.Params[i]);
773 Result << "</dl>";
774 }
775
776 if (Parts.Returns)
777 visit(Parts.Returns);
778
779 Result.flush();
780 }
781
visitNonStandaloneParagraphComment(const ParagraphComment * C)782 void 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
appendToResultWithHTMLEscaping(StringRef S)793 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
794 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
795 const char C = *I;
796 switch (C) {
797 case '&':
798 Result << "&";
799 break;
800 case '<':
801 Result << "<";
802 break;
803 case '>':
804 Result << ">";
805 break;
806 case '"':
807 Result << """;
808 break;
809 case '\'':
810 Result << "'";
811 break;
812 case '/':
813 Result << "/";
814 break;
815 default:
816 Result << C;
817 break;
818 }
819 }
820 }
821
822 extern "C" {
823
clang_HTMLTagComment_getAsString(CXComment CXC)824 CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
825 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
826 if (!HTC)
827 return createCXString((const char *) 0);
828
829 SmallString<128> HTML;
830 CommentASTToHTMLConverter Converter(HTML, getCommandTraits(CXC));
831 Converter.visit(HTC);
832 return createCXString(HTML.str(), /* DupString = */ true);
833 }
834
clang_FullComment_getAsHTML(CXComment CXC)835 CXString clang_FullComment_getAsHTML(CXComment CXC) {
836 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
837 if (!FC)
838 return createCXString((const char *) 0);
839
840 SmallString<1024> HTML;
841 CommentASTToHTMLConverter Converter(HTML, getCommandTraits(CXC));
842 Converter.visit(FC);
843 return createCXString(HTML.str(), /* DupString = */ true);
844 }
845
846 } // end extern "C"
847
848 namespace {
849 class CommentASTToXMLConverter :
850 public ConstCommentVisitor<CommentASTToXMLConverter> {
851 public:
852 /// \param Str accumulator for XML.
CommentASTToXMLConverter(SmallVectorImpl<char> & Str,const CommandTraits & Traits,const SourceManager & SM)853 CommentASTToXMLConverter(SmallVectorImpl<char> &Str,
854 const CommandTraits &Traits,
855 const SourceManager &SM) :
856 Result(Str), Traits(Traits), SM(SM) { }
857
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
878 private:
879 /// Output stream for XML.
880 llvm::raw_svector_ostream Result;
881
882 const CommandTraits &Traits;
883 const SourceManager &SM;
884 };
885 } // end unnamed namespace
886
visitTextComment(const TextComment * C)887 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
888 appendToResultWithXMLEscaping(C->getText());
889 }
890
visitInlineCommandComment(const InlineCommandComment * C)891 void 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
visitHTMLStartTagComment(const HTMLStartTagComment * C)929 void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
930 Result << "<rawHTML><![CDATA[";
931 PrintHTMLStartTagComment(C, Result);
932 Result << "]]></rawHTML>";
933 }
934
visitHTMLEndTagComment(const HTMLEndTagComment * C)935 void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
936 Result << "<rawHTML></" << C->getTagName() << "></rawHTML>";
937 }
938
visitParagraphComment(const ParagraphComment * C)939 void 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
visitBlockCommandComment(const BlockCommandComment * C)951 void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
952 visit(C->getParagraph());
953 }
954
visitParamCommandComment(const ParamCommandComment * C)955 void 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
visitTParamCommandComment(const TParamCommandComment * C)980 void 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
visitVerbatimBlockComment(const VerbatimBlockComment * C)995 void CommentASTToXMLConverter::visitVerbatimBlockComment(
996 const VerbatimBlockComment *C) {
997 unsigned NumLines = C->getNumLines();
998 if (NumLines == 0)
999 return;
1000
1001 Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits))
1002 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
1003 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
1004 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
visitVerbatimBlockLineComment(const VerbatimBlockLineComment * C)1012 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1013 const VerbatimBlockLineComment *C) {
1014 llvm_unreachable("should not see this AST node");
1015 }
1016
visitVerbatimLineComment(const VerbatimLineComment * C)1017 void CommentASTToXMLConverter::visitVerbatimLineComment(
1018 const VerbatimLineComment *C) {
1019 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1020 appendToResultWithXMLEscaping(C->getText());
1021 Result << "</Verbatim>";
1022 }
1023
visitFullComment(const FullComment * C)1024 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
1025 FullCommentParts Parts(C, Traits);
1026
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;
1085 case DeclInfo::EnumKind:
1086 RootEndTag = "</Enum>";
1087 Result << "<Enum";
1088 break;
1089 }
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><anonymous></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 }
1173
1174 {
1175 bool StartTagEmitted = false;
1176 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1177 const Comment *C = Parts.MiscBlocks[i];
1178 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1179 continue;
1180 if (!StartTagEmitted) {
1181 Result << "<Discussion>";
1182 StartTagEmitted = true;
1183 }
1184 visit(C);
1185 }
1186 if (StartTagEmitted)
1187 Result << "</Discussion>";
1188 }
1189
1190 Result << RootEndTag;
1191
1192 Result.flush();
1193 }
1194
appendToResultWithXMLEscaping(StringRef S)1195 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1196 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1197 const char C = *I;
1198 switch (C) {
1199 case '&':
1200 Result << "&";
1201 break;
1202 case '<':
1203 Result << "<";
1204 break;
1205 case '>':
1206 Result << ">";
1207 break;
1208 case '"':
1209 Result << """;
1210 break;
1211 case '\'':
1212 Result << "'";
1213 break;
1214 default:
1215 Result << C;
1216 break;
1217 }
1218 }
1219 }
1220
1221 extern "C" {
1222
clang_FullComment_getAsXML(CXComment CXC)1223 CXString clang_FullComment_getAsXML(CXComment CXC) {
1224 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1225 if (!FC)
1226 return createCXString((const char *) 0);
1227
1228 CXTranslationUnit TU = CXC.TranslationUnit;
1229 SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1230
1231 SmallString<1024> XML;
1232 CommentASTToXMLConverter Converter(XML, getCommandTraits(CXC), SM);
1233 Converter.visit(FC);
1234 return createCXString(XML.str(), /* DupString = */ true);
1235 }
1236
1237 } // end extern "C"
1238
1239