CXComment.cpp revision 62290ae569016345b79d4e11dd93abc300e5a681
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#include "CXTranslationUnit.h"
19
20#include "clang/AST/CommentVisitor.h"
21#include "clang/AST/CommentCommandTraits.h"
22#include "clang/AST/Decl.h"
23#include "clang/Frontend/ASTUnit.h"
24
25#include "llvm/ADT/StringSwitch.h"
26#include "llvm/Support/ErrorHandling.h"
27#include "llvm/Support/raw_ostream.h"
28
29#include <climits>
30
31using namespace clang;
32using namespace clang::cxstring;
33using namespace clang::comments;
34using namespace clang::cxcomment;
35
36extern "C" {
37
38enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
39  const Comment *C = getASTNode(CXC);
40  if (!C)
41    return CXComment_Null;
42
43  switch (C->getCommentKind()) {
44  case Comment::NoCommentKind:
45    return CXComment_Null;
46
47  case Comment::TextCommentKind:
48    return CXComment_Text;
49
50  case Comment::InlineCommandCommentKind:
51    return CXComment_InlineCommand;
52
53  case Comment::HTMLStartTagCommentKind:
54    return CXComment_HTMLStartTag;
55
56  case Comment::HTMLEndTagCommentKind:
57    return CXComment_HTMLEndTag;
58
59  case Comment::ParagraphCommentKind:
60    return CXComment_Paragraph;
61
62  case Comment::BlockCommandCommentKind:
63    return CXComment_BlockCommand;
64
65  case Comment::ParamCommandCommentKind:
66    return CXComment_ParamCommand;
67
68  case Comment::TParamCommandCommentKind:
69    return CXComment_TParamCommand;
70
71  case Comment::VerbatimBlockCommentKind:
72    return CXComment_VerbatimBlockCommand;
73
74  case Comment::VerbatimBlockLineCommentKind:
75    return CXComment_VerbatimBlockLine;
76
77  case Comment::VerbatimLineCommentKind:
78    return CXComment_VerbatimLine;
79
80  case Comment::FullCommentKind:
81    return CXComment_FullComment;
82  }
83  llvm_unreachable("unknown CommentKind");
84}
85
86unsigned clang_Comment_getNumChildren(CXComment CXC) {
87  const Comment *C = getASTNode(CXC);
88  if (!C)
89    return 0;
90
91  return C->child_count();
92}
93
94CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
95  const Comment *C = getASTNode(CXC);
96  if (!C || ChildIdx >= C->child_count())
97    return createCXComment(NULL);
98
99  return createCXComment(*(C->child_begin() + ChildIdx));
100}
101
102unsigned clang_Comment_isWhitespace(CXComment CXC) {
103  const Comment *C = getASTNode(CXC);
104  if (!C)
105    return false;
106
107  if (const TextComment *TC = dyn_cast<TextComment>(C))
108    return TC->isWhitespace();
109
110  if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
111    return PC->isWhitespace();
112
113  return false;
114}
115
116unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
117  const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
118  if (!ICC)
119    return false;
120
121  return ICC->hasTrailingNewline();
122}
123
124CXString clang_TextComment_getText(CXComment CXC) {
125  const TextComment *TC = getASTNodeAs<TextComment>(CXC);
126  if (!TC)
127    return createCXString((const char *) 0);
128
129  return createCXString(TC->getText(), /*DupString=*/ false);
130}
131
132CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
133  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
134  if (!ICC)
135    return createCXString((const char *) 0);
136
137  return createCXString(ICC->getCommandName(), /*DupString=*/ false);
138}
139
140enum CXCommentInlineCommandRenderKind
141clang_InlineCommandComment_getRenderKind(CXComment CXC) {
142  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
143  if (!ICC)
144    return CXCommentInlineCommandRenderKind_Normal;
145
146  switch (ICC->getRenderKind()) {
147  case InlineCommandComment::RenderNormal:
148    return CXCommentInlineCommandRenderKind_Normal;
149
150  case InlineCommandComment::RenderBold:
151    return CXCommentInlineCommandRenderKind_Bold;
152
153  case InlineCommandComment::RenderMonospaced:
154    return CXCommentInlineCommandRenderKind_Monospaced;
155
156  case InlineCommandComment::RenderEmphasized:
157    return CXCommentInlineCommandRenderKind_Emphasized;
158  }
159  llvm_unreachable("unknown InlineCommandComment::RenderKind");
160}
161
162unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
163  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
164  if (!ICC)
165    return 0;
166
167  return ICC->getNumArgs();
168}
169
170CXString clang_InlineCommandComment_getArgText(CXComment CXC,
171                                               unsigned ArgIdx) {
172  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
173  if (!ICC || ArgIdx >= ICC->getNumArgs())
174    return createCXString((const char *) 0);
175
176  return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
177}
178
179CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
180  const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
181  if (!HTC)
182    return createCXString((const char *) 0);
183
184  return createCXString(HTC->getTagName(), /*DupString=*/ false);
185}
186
187unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
188  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
189  if (!HST)
190    return false;
191
192  return HST->isSelfClosing();
193}
194
195unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
196  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
197  if (!HST)
198    return 0;
199
200  return HST->getNumAttrs();
201}
202
203CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
204  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
205  if (!HST || AttrIdx >= HST->getNumAttrs())
206    return createCXString((const char *) 0);
207
208  return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
209}
210
211CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
212  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
213  if (!HST || AttrIdx >= HST->getNumAttrs())
214    return createCXString((const char *) 0);
215
216  return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
217}
218
219CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
220  const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
221  if (!BCC)
222    return createCXString((const char *) 0);
223
224  return createCXString(BCC->getCommandName(), /*DupString=*/ false);
225}
226
227unsigned 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
235CXString 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
244CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
245  const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
246  if (!BCC)
247    return createCXComment(NULL);
248
249  return createCXComment(BCC->getParagraph());
250}
251
252CXString 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
260unsigned 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
268unsigned 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
276unsigned 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
284enum 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
303CXString 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
311unsigned 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
319unsigned 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
327unsigned 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
335CXString 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
344CXString 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
358namespace {
359
360/// This comparison will sort parameters with valid index by index and
361/// invalid (unresolved) parameters last.
362class ParamCommandCommentCompareIndex {
363public:
364  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.
381class TParamCommandCommentComparePosition {
382public:
383  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.
406struct FullCommentParts {
407  /// Take a full comment apart and initialize members accordingly.
408  FullCommentParts(const FullComment *C);
409
410  const BlockContentComment *Brief;
411  const ParagraphComment *FirstParagraph;
412  const BlockCommandComment *Returns;
413  SmallVector<const ParamCommandComment *, 8> Params;
414  SmallVector<const TParamCommandComment *, 4> TParams;
415  SmallVector<const BlockContentComment *, 8> MiscBlocks;
416};
417
418FullCommentParts::FullCommentParts(const FullComment *C) :
419    Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
420  const CommandTraits Traits;
421  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
422       I != E; ++I) {
423    const Comment *Child = *I;
424    if (!Child)
425      continue;
426    switch (Child->getCommentKind()) {
427    case Comment::NoCommentKind:
428      continue;
429
430    case Comment::ParagraphCommentKind: {
431      const ParagraphComment *PC = cast<ParagraphComment>(Child);
432      if (PC->isWhitespace())
433        break;
434      if (!FirstParagraph)
435        FirstParagraph = PC;
436
437      MiscBlocks.push_back(PC);
438      break;
439    }
440
441    case Comment::BlockCommandCommentKind: {
442      const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
443      StringRef CommandName = BCC->getCommandName();
444      if (!Brief && Traits.isBriefCommand(CommandName)) {
445        Brief = BCC;
446        break;
447      }
448      if (!Returns && Traits.isReturnsCommand(CommandName)) {
449        Returns = BCC;
450        break;
451      }
452      MiscBlocks.push_back(BCC);
453      break;
454    }
455
456    case Comment::ParamCommandCommentKind: {
457      const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
458      if (!PCC->hasParamName())
459        break;
460
461      if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
462        break;
463
464      Params.push_back(PCC);
465      break;
466    }
467
468    case Comment::TParamCommandCommentKind: {
469      const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
470      if (!TPCC->hasParamName())
471        break;
472
473      if (!TPCC->hasNonWhitespaceParagraph())
474        break;
475
476      TParams.push_back(TPCC);
477      break;
478    }
479
480    case Comment::VerbatimBlockCommentKind:
481      MiscBlocks.push_back(cast<BlockCommandComment>(Child));
482      break;
483
484    case Comment::VerbatimLineCommentKind: {
485      const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
486      if (!Traits.isDeclarationCommand(VLC->getCommandName()))
487        MiscBlocks.push_back(VLC);
488      break;
489    }
490
491    case Comment::TextCommentKind:
492    case Comment::InlineCommandCommentKind:
493    case Comment::HTMLStartTagCommentKind:
494    case Comment::HTMLEndTagCommentKind:
495    case Comment::VerbatimBlockLineCommentKind:
496    case Comment::FullCommentKind:
497      llvm_unreachable("AST node of this kind can't be a child of "
498                       "a FullComment");
499    }
500  }
501
502  // Sort params in order they are declared in the function prototype.
503  // Unresolved parameters are put at the end of the list in the same order
504  // they were seen in the comment.
505  std::stable_sort(Params.begin(), Params.end(),
506                   ParamCommandCommentCompareIndex());
507
508  std::stable_sort(TParams.begin(), TParams.end(),
509                   TParamCommandCommentComparePosition());
510}
511
512void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
513                              llvm::raw_svector_ostream &Result) {
514  Result << "<" << C->getTagName();
515
516  if (C->getNumAttrs() != 0) {
517    for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
518      Result << " ";
519      const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
520      Result << Attr.Name;
521      if (!Attr.Value.empty())
522        Result << "=\"" << Attr.Value << "\"";
523    }
524  }
525
526  if (!C->isSelfClosing())
527    Result << ">";
528  else
529    Result << "/>";
530}
531
532class CommentASTToHTMLConverter :
533    public ConstCommentVisitor<CommentASTToHTMLConverter> {
534public:
535  /// \param Str accumulator for HTML.
536  CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
537
538  // Inline content.
539  void visitTextComment(const TextComment *C);
540  void visitInlineCommandComment(const InlineCommandComment *C);
541  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
542  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
543
544  // Block content.
545  void visitParagraphComment(const ParagraphComment *C);
546  void visitBlockCommandComment(const BlockCommandComment *C);
547  void visitParamCommandComment(const ParamCommandComment *C);
548  void visitTParamCommandComment(const TParamCommandComment *C);
549  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
550  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
551  void visitVerbatimLineComment(const VerbatimLineComment *C);
552
553  void visitFullComment(const FullComment *C);
554
555  // Helpers.
556
557  /// Convert a paragraph that is not a block by itself (an argument to some
558  /// command).
559  void visitNonStandaloneParagraphComment(const ParagraphComment *C);
560
561  void appendToResultWithHTMLEscaping(StringRef S);
562
563private:
564  const CommandTraits Traits;
565
566  /// Output stream for HTML.
567  llvm::raw_svector_ostream Result;
568};
569} // end unnamed namespace
570
571void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
572  appendToResultWithHTMLEscaping(C->getText());
573}
574
575void CommentASTToHTMLConverter::visitInlineCommandComment(
576                                  const InlineCommandComment *C) {
577  // Nothing to render if no arguments supplied.
578  if (C->getNumArgs() == 0)
579    return;
580
581  // Nothing to render if argument is empty.
582  StringRef Arg0 = C->getArgText(0);
583  if (Arg0.empty())
584    return;
585
586  switch (C->getRenderKind()) {
587  case InlineCommandComment::RenderNormal:
588    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
589      appendToResultWithHTMLEscaping(C->getArgText(i));
590      Result << " ";
591    }
592    return;
593
594  case InlineCommandComment::RenderBold:
595    assert(C->getNumArgs() == 1);
596    Result << "<b>";
597    appendToResultWithHTMLEscaping(Arg0);
598    Result << "</b>";
599    return;
600  case InlineCommandComment::RenderMonospaced:
601    assert(C->getNumArgs() == 1);
602    Result << "<tt>";
603    appendToResultWithHTMLEscaping(Arg0);
604    Result<< "</tt>";
605    return;
606  case InlineCommandComment::RenderEmphasized:
607    assert(C->getNumArgs() == 1);
608    Result << "<em>";
609    appendToResultWithHTMLEscaping(Arg0);
610    Result << "</em>";
611    return;
612  }
613}
614
615void CommentASTToHTMLConverter::visitHTMLStartTagComment(
616                                  const HTMLStartTagComment *C) {
617  PrintHTMLStartTagComment(C, Result);
618}
619
620void CommentASTToHTMLConverter::visitHTMLEndTagComment(
621                                  const HTMLEndTagComment *C) {
622  Result << "</" << C->getTagName() << ">";
623}
624
625void CommentASTToHTMLConverter::visitParagraphComment(
626                                  const ParagraphComment *C) {
627  if (C->isWhitespace())
628    return;
629
630  Result << "<p>";
631  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
632       I != E; ++I) {
633    visit(*I);
634  }
635  Result << "</p>";
636}
637
638void CommentASTToHTMLConverter::visitBlockCommandComment(
639                                  const BlockCommandComment *C) {
640  StringRef CommandName = C->getCommandName();
641  if (Traits.isBriefCommand(CommandName)) {
642    Result << "<p class=\"para-brief\">";
643    visitNonStandaloneParagraphComment(C->getParagraph());
644    Result << "</p>";
645    return;
646  }
647  if (Traits.isReturnsCommand(CommandName)) {
648    Result << "<p class=\"para-returns\">"
649              "<span class=\"word-returns\">Returns</span> ";
650    visitNonStandaloneParagraphComment(C->getParagraph());
651    Result << "</p>";
652    return;
653  }
654  // We don't know anything about this command.  Just render the paragraph.
655  visit(C->getParagraph());
656}
657
658void CommentASTToHTMLConverter::visitParamCommandComment(
659                                  const ParamCommandComment *C) {
660  if (C->isParamIndexValid()) {
661    Result << "<dt class=\"param-name-index-"
662           << C->getParamIndex()
663           << "\">";
664  } else
665    Result << "<dt class=\"param-name-index-invalid\">";
666
667  appendToResultWithHTMLEscaping(C->getParamName());
668  Result << "</dt>";
669
670  if (C->isParamIndexValid()) {
671    Result << "<dd class=\"param-descr-index-"
672           << C->getParamIndex()
673           << "\">";
674  } else
675    Result << "<dd class=\"param-descr-index-invalid\">";
676
677  visitNonStandaloneParagraphComment(C->getParagraph());
678  Result << "</dd>";
679}
680
681void CommentASTToHTMLConverter::visitTParamCommandComment(
682                                  const TParamCommandComment *C) {
683  if (C->isPositionValid()) {
684    if (C->getDepth() == 1)
685      Result << "<dt class=\"tparam-name-index-"
686             << C->getIndex(0)
687             << "\">";
688    else
689      Result << "<dt class=\"tparam-name-index-other\">";
690  } else
691    Result << "<dt class=\"tparam-name-index-invalid\">";
692
693  appendToResultWithHTMLEscaping(C->getParamName());
694  Result << "</dt>";
695
696  if (C->isPositionValid()) {
697    if (C->getDepth() == 1)
698      Result << "<dd class=\"tparam-descr-index-"
699             << C->getIndex(0)
700             << "\">";
701    else
702      Result << "<dd class=\"tparam-descr-index-other\">";
703  } else
704    Result << "<dd class=\"tparam-descr-index-invalid\">";
705
706  visitNonStandaloneParagraphComment(C->getParagraph());
707  Result << "</dd>";
708}
709
710void CommentASTToHTMLConverter::visitVerbatimBlockComment(
711                                  const VerbatimBlockComment *C) {
712  unsigned NumLines = C->getNumLines();
713  if (NumLines == 0)
714    return;
715
716  Result << "<pre>";
717  for (unsigned i = 0; i != NumLines; ++i) {
718    appendToResultWithHTMLEscaping(C->getText(i));
719    if (i + 1 != NumLines)
720      Result << '\n';
721  }
722  Result << "</pre>";
723}
724
725void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
726                                  const VerbatimBlockLineComment *C) {
727  llvm_unreachable("should not see this AST node");
728}
729
730void CommentASTToHTMLConverter::visitVerbatimLineComment(
731                                  const VerbatimLineComment *C) {
732  Result << "<pre>";
733  appendToResultWithHTMLEscaping(C->getText());
734  Result << "</pre>";
735}
736
737void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
738  FullCommentParts Parts(C);
739
740  bool FirstParagraphIsBrief = false;
741  if (Parts.Brief)
742    visit(Parts.Brief);
743  else if (Parts.FirstParagraph) {
744    Result << "<p class=\"para-brief\">";
745    visitNonStandaloneParagraphComment(Parts.FirstParagraph);
746    Result << "</p>";
747    FirstParagraphIsBrief = true;
748  }
749
750  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
751    const Comment *C = Parts.MiscBlocks[i];
752    if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
753      continue;
754    visit(C);
755  }
756
757  if (Parts.TParams.size() != 0) {
758    Result << "<dl>";
759    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
760      visit(Parts.TParams[i]);
761    Result << "</dl>";
762  }
763
764  if (Parts.Params.size() != 0) {
765    Result << "<dl>";
766    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
767      visit(Parts.Params[i]);
768    Result << "</dl>";
769  }
770
771  if (Parts.Returns)
772    visit(Parts.Returns);
773
774  Result.flush();
775}
776
777void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
778                                  const ParagraphComment *C) {
779  if (!C)
780    return;
781
782  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
783       I != E; ++I) {
784    visit(*I);
785  }
786}
787
788void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
789  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
790    const char C = *I;
791    switch (C) {
792      case '&':
793        Result << "&amp;";
794        break;
795      case '<':
796        Result << "&lt;";
797        break;
798      case '>':
799        Result << "&gt;";
800        break;
801      case '"':
802        Result << "&quot;";
803        break;
804      case '\'':
805        Result << "&#39;";
806        break;
807      case '/':
808        Result << "&#47;";
809        break;
810      default:
811        Result << C;
812        break;
813    }
814  }
815}
816
817extern "C" {
818
819CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
820  const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
821  if (!HTC)
822    return createCXString((const char *) 0);
823
824  SmallString<128> HTML;
825  CommentASTToHTMLConverter Converter(HTML);
826  Converter.visit(HTC);
827  return createCXString(HTML.str(), /* DupString = */ true);
828}
829
830CXString clang_FullComment_getAsHTML(CXComment CXC) {
831  const FullComment *FC = getASTNodeAs<FullComment>(CXC);
832  if (!FC)
833    return createCXString((const char *) 0);
834
835  SmallString<1024> HTML;
836  CommentASTToHTMLConverter Converter(HTML);
837  Converter.visit(FC);
838  return createCXString(HTML.str(), /* DupString = */ true);
839}
840
841} // end extern "C"
842
843namespace {
844class CommentASTToXMLConverter :
845    public ConstCommentVisitor<CommentASTToXMLConverter> {
846public:
847  /// \param Str accumulator for XML.
848  CommentASTToXMLConverter(const SourceManager &SM,
849                           SmallVectorImpl<char> &Str) :
850    SM(SM), Result(Str) { }
851
852  // Inline content.
853  void visitTextComment(const TextComment *C);
854  void visitInlineCommandComment(const InlineCommandComment *C);
855  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
856  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
857
858  // Block content.
859  void visitParagraphComment(const ParagraphComment *C);
860  void visitBlockCommandComment(const BlockCommandComment *C);
861  void visitParamCommandComment(const ParamCommandComment *C);
862  void visitTParamCommandComment(const TParamCommandComment *C);
863  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
864  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
865  void visitVerbatimLineComment(const VerbatimLineComment *C);
866
867  void visitFullComment(const FullComment *C);
868
869  // Helpers.
870  void appendToResultWithXMLEscaping(StringRef S);
871
872private:
873  const SourceManager &SM;
874
875  /// Output stream for XML.
876  llvm::raw_svector_ostream Result;
877};
878} // end unnamed namespace
879
880void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
881  appendToResultWithXMLEscaping(C->getText());
882}
883
884void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
885  // Nothing to render if no arguments supplied.
886  if (C->getNumArgs() == 0)
887    return;
888
889  // Nothing to render if argument is empty.
890  StringRef Arg0 = C->getArgText(0);
891  if (Arg0.empty())
892    return;
893
894  switch (C->getRenderKind()) {
895  case InlineCommandComment::RenderNormal:
896    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
897      appendToResultWithXMLEscaping(C->getArgText(i));
898      Result << " ";
899    }
900    return;
901  case InlineCommandComment::RenderBold:
902    assert(C->getNumArgs() == 1);
903    Result << "<bold>";
904    appendToResultWithXMLEscaping(Arg0);
905    Result << "</bold>";
906    return;
907  case InlineCommandComment::RenderMonospaced:
908    assert(C->getNumArgs() == 1);
909    Result << "<monospaced>";
910    appendToResultWithXMLEscaping(Arg0);
911    Result << "</monospaced>";
912    return;
913  case InlineCommandComment::RenderEmphasized:
914    assert(C->getNumArgs() == 1);
915    Result << "<emphasized>";
916    appendToResultWithXMLEscaping(Arg0);
917    Result << "</emphasized>";
918    return;
919  }
920}
921
922void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
923  Result << "<rawHTML><![CDATA[";
924  PrintHTMLStartTagComment(C, Result);
925  Result << "]]></rawHTML>";
926}
927
928void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
929  Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
930}
931
932void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
933  if (C->isWhitespace())
934    return;
935
936  Result << "<Para>";
937  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
938       I != E; ++I) {
939    visit(*I);
940  }
941  Result << "</Para>";
942}
943
944void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
945  visit(C->getParagraph());
946}
947
948void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
949  Result << "<Parameter><Name>";
950  appendToResultWithXMLEscaping(C->getParamName());
951  Result << "</Name>";
952
953  if (C->isParamIndexValid())
954    Result << "<Index>" << C->getParamIndex() << "</Index>";
955
956  Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
957  switch (C->getDirection()) {
958  case ParamCommandComment::In:
959    Result << "in";
960    break;
961  case ParamCommandComment::Out:
962    Result << "out";
963    break;
964  case ParamCommandComment::InOut:
965    Result << "in,out";
966    break;
967  }
968  Result << "</Direction><Discussion>";
969  visit(C->getParagraph());
970  Result << "</Discussion></Parameter>";
971}
972
973void CommentASTToXMLConverter::visitTParamCommandComment(
974                                  const TParamCommandComment *C) {
975  Result << "<Parameter><Name>";
976  appendToResultWithXMLEscaping(C->getParamName());
977  Result << "</Name>";
978
979  if (C->isPositionValid() && C->getDepth() == 1) {
980    Result << "<Index>" << C->getIndex(0) << "</Index>";
981  }
982
983  Result << "<Discussion>";
984  visit(C->getParagraph());
985  Result << "</Discussion></Parameter>";
986}
987
988void CommentASTToXMLConverter::visitVerbatimBlockComment(
989                                  const VerbatimBlockComment *C) {
990  unsigned NumLines = C->getNumLines();
991  if (NumLines == 0)
992    return;
993
994  Result << llvm::StringSwitch<const char *>(C->getCommandName())
995      .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
996      .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
997  for (unsigned i = 0; i != NumLines; ++i) {
998    appendToResultWithXMLEscaping(C->getText(i));
999    if (i + 1 != NumLines)
1000      Result << '\n';
1001  }
1002  Result << "</Verbatim>";
1003}
1004
1005void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1006                                  const VerbatimBlockLineComment *C) {
1007  llvm_unreachable("should not see this AST node");
1008}
1009
1010void CommentASTToXMLConverter::visitVerbatimLineComment(
1011                                  const VerbatimLineComment *C) {
1012  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1013  appendToResultWithXMLEscaping(C->getText());
1014  Result << "</Verbatim>";
1015}
1016
1017void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
1018  FullCommentParts Parts(C);
1019
1020  const DeclInfo *DI = C->getDeclInfo();
1021  StringRef RootEndTag;
1022  if (DI) {
1023    switch (DI->getKind()) {
1024    case DeclInfo::OtherKind:
1025      RootEndTag = "</Other>";
1026      Result << "<Other";
1027      break;
1028    case DeclInfo::FunctionKind:
1029      RootEndTag = "</Function>";
1030      Result << "<Function";
1031      switch (DI->TemplateKind) {
1032      case DeclInfo::NotTemplate:
1033        break;
1034      case DeclInfo::Template:
1035        Result << " templateKind=\"template\"";
1036        break;
1037      case DeclInfo::TemplateSpecialization:
1038        Result << " templateKind=\"specialization\"";
1039        break;
1040      case DeclInfo::TemplatePartialSpecialization:
1041        llvm_unreachable("partial specializations of functions "
1042                         "are not allowed in C++");
1043      }
1044      if (DI->IsInstanceMethod)
1045        Result << " isInstanceMethod=\"1\"";
1046      if (DI->IsClassMethod)
1047        Result << " isClassMethod=\"1\"";
1048      break;
1049    case DeclInfo::ClassKind:
1050      RootEndTag = "</Class>";
1051      Result << "<Class";
1052      switch (DI->TemplateKind) {
1053      case DeclInfo::NotTemplate:
1054        break;
1055      case DeclInfo::Template:
1056        Result << " templateKind=\"template\"";
1057        break;
1058      case DeclInfo::TemplateSpecialization:
1059        Result << " templateKind=\"specialization\"";
1060        break;
1061      case DeclInfo::TemplatePartialSpecialization:
1062        Result << " templateKind=\"partialSpecialization\"";
1063        break;
1064      }
1065      break;
1066    case DeclInfo::VariableKind:
1067      RootEndTag = "</Variable>";
1068      Result << "<Variable";
1069      break;
1070    case DeclInfo::NamespaceKind:
1071      RootEndTag = "</Namespace>";
1072      Result << "<Namespace";
1073      break;
1074    case DeclInfo::TypedefKind:
1075      RootEndTag = "</Typedef>";
1076      Result << "<Typedef";
1077      break;
1078    case DeclInfo::EnumKind:
1079      RootEndTag = "</Enum>";
1080      Result << "<Enum";
1081      break;
1082    }
1083
1084    {
1085      // Print line and column number.
1086      SourceLocation Loc = DI->ThisDecl->getLocation();
1087      std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1088      FileID FID = LocInfo.first;
1089      unsigned FileOffset = LocInfo.second;
1090
1091      if (!FID.isInvalid()) {
1092        if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1093          Result << " file=\"";
1094          appendToResultWithXMLEscaping(FE->getName());
1095          Result << "\"";
1096        }
1097        Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1098               << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1099               << "\"";
1100      }
1101    }
1102
1103    // Finish the root tag.
1104    Result << ">";
1105
1106    bool FoundName = false;
1107    if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->ThisDecl)) {
1108      if (DeclarationName DeclName = ND->getDeclName()) {
1109        Result << "<Name>";
1110        std::string Name = DeclName.getAsString();
1111        appendToResultWithXMLEscaping(Name);
1112        FoundName = true;
1113        Result << "</Name>";
1114      }
1115    }
1116    if (!FoundName)
1117      Result << "<Name>&lt;anonymous&gt;</Name>";
1118
1119    {
1120      // Print USR.
1121      SmallString<128> USR;
1122      cxcursor::getDeclCursorUSR(DI->ThisDecl, USR);
1123      if (!USR.empty()) {
1124        Result << "<USR>";
1125        appendToResultWithXMLEscaping(USR);
1126        Result << "</USR>";
1127      }
1128    }
1129  } else {
1130    // No DeclInfo -- just emit some root tag and name tag.
1131    RootEndTag = "</Other>";
1132    Result << "<Other><Name>unknown</Name>";
1133  }
1134
1135  bool FirstParagraphIsBrief = false;
1136  if (Parts.Brief) {
1137    Result << "<Abstract>";
1138    visit(Parts.Brief);
1139    Result << "</Abstract>";
1140  } else if (Parts.FirstParagraph) {
1141    Result << "<Abstract>";
1142    visit(Parts.FirstParagraph);
1143    Result << "</Abstract>";
1144    FirstParagraphIsBrief = true;
1145  }
1146
1147  if (Parts.TParams.size() != 0) {
1148    Result << "<TemplateParameters>";
1149    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1150      visit(Parts.TParams[i]);
1151    Result << "</TemplateParameters>";
1152  }
1153
1154  if (Parts.Params.size() != 0) {
1155    Result << "<Parameters>";
1156    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1157      visit(Parts.Params[i]);
1158    Result << "</Parameters>";
1159  }
1160
1161  if (Parts.Returns) {
1162    Result << "<ResultDiscussion>";
1163    visit(Parts.Returns);
1164    Result << "</ResultDiscussion>";
1165  }
1166
1167  {
1168    bool StartTagEmitted = false;
1169    for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1170      const Comment *C = Parts.MiscBlocks[i];
1171      if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1172        continue;
1173      if (!StartTagEmitted) {
1174        Result << "<Discussion>";
1175        StartTagEmitted = true;
1176      }
1177      visit(C);
1178    }
1179    if (StartTagEmitted)
1180      Result << "</Discussion>";
1181  }
1182
1183  Result << RootEndTag;
1184
1185  Result.flush();
1186}
1187
1188void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1189  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1190    const char C = *I;
1191    switch (C) {
1192      case '&':
1193        Result << "&amp;";
1194        break;
1195      case '<':
1196        Result << "&lt;";
1197        break;
1198      case '>':
1199        Result << "&gt;";
1200        break;
1201      case '"':
1202        Result << "&quot;";
1203        break;
1204      case '\'':
1205        Result << "&apos;";
1206        break;
1207      default:
1208        Result << C;
1209        break;
1210    }
1211  }
1212}
1213
1214extern "C" {
1215
1216CXString clang_FullComment_getAsXML(CXTranslationUnit TU, CXComment CXC) {
1217  const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1218  if (!FC)
1219    return createCXString((const char *) 0);
1220
1221  SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1222
1223  SmallString<1024> XML;
1224  CommentASTToXMLConverter Converter(SM, XML);
1225  Converter.visit(FC);
1226  return createCXString(XML.str(), /* DupString = */ true);
1227}
1228
1229} // end extern "C"
1230
1231