CXComment.cpp revision 8da68b8b03d72314b67ea44568e013ef79d93b40
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
29using namespace clang;
30using namespace clang::cxstring;
31using namespace clang::comments;
32using namespace clang::cxcomment;
33
34extern "C" {
35
36enum 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
84unsigned 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
92CXComment 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
100unsigned 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
114unsigned 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
122CXString 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
130CXString 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
139enum CXCommentInlineCommandRenderKind
140clang_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
161unsigned 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
169CXString 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
178CXString 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
186unsigned 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
194unsigned 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
202CXString 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
210CXString 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
218CXString 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
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, NULL);
248
249  return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
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                   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
419FullCommentParts::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
514void 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
534class CommentASTToHTMLConverter :
535    public ConstCommentVisitor<CommentASTToHTMLConverter> {
536public:
537  /// \param Str accumulator for HTML.
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
568private:
569  /// Output stream for HTML.
570  llvm::raw_svector_ostream Result;
571
572  const CommandTraits &Traits;
573};
574} // end unnamed namespace
575
576void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
577  appendToResultWithHTMLEscaping(C->getText());
578}
579
580void 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
620void CommentASTToHTMLConverter::visitHTMLStartTagComment(
621                                  const HTMLStartTagComment *C) {
622  PrintHTMLStartTagComment(C, Result);
623}
624
625void CommentASTToHTMLConverter::visitHTMLEndTagComment(
626                                  const HTMLEndTagComment *C) {
627  Result << "</" << C->getTagName() << ">";
628}
629
630void 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
643void 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
663void 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
686void 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
715void 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
730void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
731                                  const VerbatimBlockLineComment *C) {
732  llvm_unreachable("should not see this AST node");
733}
734
735void CommentASTToHTMLConverter::visitVerbatimLineComment(
736                                  const VerbatimLineComment *C) {
737  Result << "<pre>";
738  appendToResultWithHTMLEscaping(C->getText());
739  Result << "</pre>";
740}
741
742void 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
782void 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
793void 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
822extern "C" {
823
824CXString 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
835CXString 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
848namespace {
849class CommentASTToXMLConverter :
850    public ConstCommentVisitor<CommentASTToXMLConverter> {
851public:
852  /// \param Str accumulator for XML.
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
878private:
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
887void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
888  appendToResultWithXMLEscaping(C->getText());
889}
890
891void 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
929void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
930  Result << "<rawHTML><![CDATA[";
931  PrintHTMLStartTagComment(C, Result);
932  Result << "]]></rawHTML>";
933}
934
935void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
936  Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
937}
938
939void 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
951void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
952  visit(C->getParagraph());
953}
954
955void 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
980void 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
995void 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
1012void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1013                                  const VerbatimBlockLineComment *C) {
1014  llvm_unreachable("should not see this AST node");
1015}
1016
1017void CommentASTToXMLConverter::visitVerbatimLineComment(
1018                                  const VerbatimLineComment *C) {
1019  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1020  appendToResultWithXMLEscaping(C->getText());
1021  Result << "</Verbatim>";
1022}
1023
1024void 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  if (DI->ThisDecl->hasAttrs()) {
1175    const AttrVec &Attrs = DI->ThisDecl->getAttrs();
1176    for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1177      const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1178      if (!AA) {
1179        if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1180          if (DA->getMessage().empty())
1181            Result << "<Deprecated/>";
1182          else {
1183            Result << "<Deprecated>"
1184                   << DA->getMessage()
1185                   << "</Deprecated>";
1186          }
1187        }
1188        else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1189          if (UA->getMessage().empty())
1190            Result << "<Unavailable/>";
1191          else {
1192            Result << "<Unavailable>"
1193                   << UA->getMessage()
1194                   << "</Unavailable>";
1195          }
1196        }
1197        continue;
1198      }
1199
1200      // 'availability' attribute.
1201      Result << "<Availability";
1202      StringRef Distribution;
1203      if (AA->getPlatform()) {
1204        Distribution = AvailabilityAttr::getPrettyPlatformName(
1205                                        AA->getPlatform()->getName());
1206        if (Distribution.empty())
1207          Distribution = AA->getPlatform()->getName();
1208      }
1209      Result << " distribution=\"" << Distribution << "\">";
1210      VersionTuple IntroducedInVersion = AA->getIntroduced();
1211      if (!IntroducedInVersion.empty()) {
1212        Result << "<IntroducedInVersion>"
1213               << IntroducedInVersion.getAsString()
1214               << "</IntroducedInVersion>";
1215      }
1216      VersionTuple DeprecatedInVersion = AA->getDeprecated();
1217      if (!DeprecatedInVersion.empty()) {
1218        Result << "<DeprecatedInVersion>"
1219               << DeprecatedInVersion.getAsString()
1220               << "</DeprecatedInVersion>";
1221      }
1222      VersionTuple RemovedAfterVersion = AA->getObsoleted();
1223      if (!RemovedAfterVersion.empty()) {
1224        Result << "<RemovedAfterVersion>"
1225               << RemovedAfterVersion.getAsString()
1226               << "</RemovedAfterVersion>";
1227      }
1228      // 'deprecated' attribute.
1229      StringRef DeprecationSummary = AA->getMessage();
1230      if (!DeprecationSummary.empty()) {
1231        Result << " <DeprecationSummary>"
1232               << DeprecationSummary
1233               << "</DeprecationSummary>";
1234      }
1235      // 'unavailable' attribute.
1236      if (AA->getUnavailable())
1237        Result << "<Unavailable/>";
1238      Result << "</Availability>";
1239    }
1240  }
1241
1242  {
1243    bool StartTagEmitted = false;
1244    for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1245      const Comment *C = Parts.MiscBlocks[i];
1246      if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1247        continue;
1248      if (!StartTagEmitted) {
1249        Result << "<Discussion>";
1250        StartTagEmitted = true;
1251      }
1252      visit(C);
1253    }
1254    if (StartTagEmitted)
1255      Result << "</Discussion>";
1256  }
1257
1258  Result << RootEndTag;
1259
1260  Result.flush();
1261}
1262
1263void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1264  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1265    const char C = *I;
1266    switch (C) {
1267      case '&':
1268        Result << "&amp;";
1269        break;
1270      case '<':
1271        Result << "&lt;";
1272        break;
1273      case '>':
1274        Result << "&gt;";
1275        break;
1276      case '"':
1277        Result << "&quot;";
1278        break;
1279      case '\'':
1280        Result << "&apos;";
1281        break;
1282      default:
1283        Result << C;
1284        break;
1285    }
1286  }
1287}
1288
1289extern "C" {
1290
1291CXString clang_FullComment_getAsXML(CXComment CXC) {
1292  const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1293  if (!FC)
1294    return createCXString((const char *) 0);
1295
1296  CXTranslationUnit TU = CXC.TranslationUnit;
1297  SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1298
1299  SmallString<1024> XML;
1300  CommentASTToXMLConverter Converter(XML, getCommandTraits(CXC), SM);
1301  Converter.visit(FC);
1302  return createCXString(XML.str(), /* DupString = */ true);
1303}
1304
1305} // end extern "C"
1306
1307