• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 << "&amp;";
799         break;
800       case '<':
801         Result << "&lt;";
802         break;
803       case '>':
804         Result << "&gt;";
805         break;
806       case '"':
807         Result << "&quot;";
808         break;
809       case '\'':
810         Result << "&#39;";
811         break;
812       case '/':
813         Result << "&#47;";
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>&lt;/" << C->getTagName() << "&gt;</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>&lt;anonymous&gt;</Name>";
1125 
1126     {
1127       // Print USR.
1128       SmallString<128> USR;
1129       cxcursor::getDeclCursorUSR(DI->ThisDecl, USR);
1130       if (!USR.empty()) {
1131         Result << "<USR>";
1132         appendToResultWithXMLEscaping(USR);
1133         Result << "</USR>";
1134       }
1135     }
1136   } else {
1137     // No DeclInfo -- just emit some root tag and name tag.
1138     RootEndTag = "</Other>";
1139     Result << "<Other><Name>unknown</Name>";
1140   }
1141 
1142   bool FirstParagraphIsBrief = false;
1143   if (Parts.Brief) {
1144     Result << "<Abstract>";
1145     visit(Parts.Brief);
1146     Result << "</Abstract>";
1147   } else if (Parts.FirstParagraph) {
1148     Result << "<Abstract>";
1149     visit(Parts.FirstParagraph);
1150     Result << "</Abstract>";
1151     FirstParagraphIsBrief = true;
1152   }
1153 
1154   if (Parts.TParams.size() != 0) {
1155     Result << "<TemplateParameters>";
1156     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1157       visit(Parts.TParams[i]);
1158     Result << "</TemplateParameters>";
1159   }
1160 
1161   if (Parts.Params.size() != 0) {
1162     Result << "<Parameters>";
1163     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1164       visit(Parts.Params[i]);
1165     Result << "</Parameters>";
1166   }
1167 
1168   if (Parts.Returns) {
1169     Result << "<ResultDiscussion>";
1170     visit(Parts.Returns);
1171     Result << "</ResultDiscussion>";
1172   }
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 << "&amp;";
1201         break;
1202       case '<':
1203         Result << "&lt;";
1204         break;
1205       case '>':
1206         Result << "&gt;";
1207         break;
1208       case '"':
1209         Result << "&quot;";
1210         break;
1211       case '\'':
1212         Result << "&apos;";
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