CXComment.cpp revision f0d151c485bec560e73c74e816cd969e0c485c71
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
18#include "clang/AST/CommentVisitor.h"
19
20#include "llvm/Support/ErrorHandling.h"
21#include "llvm/Support/raw_ostream.h"
22
23#include <climits>
24
25using namespace clang;
26using namespace clang::cxstring;
27using namespace clang::comments;
28using namespace clang::cxcomment;
29
30extern "C" {
31
32enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
33  const Comment *C = getASTNode(CXC);
34  if (!C)
35    return CXComment_Null;
36
37  switch (C->getCommentKind()) {
38  case Comment::NoCommentKind:
39    return CXComment_Null;
40
41  case Comment::TextCommentKind:
42    return CXComment_Text;
43
44  case Comment::InlineCommandCommentKind:
45    return CXComment_InlineCommand;
46
47  case Comment::HTMLStartTagCommentKind:
48    return CXComment_HTMLStartTag;
49
50  case Comment::HTMLEndTagCommentKind:
51    return CXComment_HTMLEndTag;
52
53  case Comment::ParagraphCommentKind:
54    return CXComment_Paragraph;
55
56  case Comment::BlockCommandCommentKind:
57    return CXComment_BlockCommand;
58
59  case Comment::ParamCommandCommentKind:
60    return CXComment_ParamCommand;
61
62  case Comment::TParamCommandCommentKind:
63    return CXComment_TParamCommand;
64
65  case Comment::VerbatimBlockCommentKind:
66    return CXComment_VerbatimBlockCommand;
67
68  case Comment::VerbatimBlockLineCommentKind:
69    return CXComment_VerbatimBlockLine;
70
71  case Comment::VerbatimLineCommentKind:
72    return CXComment_VerbatimLine;
73
74  case Comment::FullCommentKind:
75    return CXComment_FullComment;
76  }
77  llvm_unreachable("unknown CommentKind");
78}
79
80unsigned clang_Comment_getNumChildren(CXComment CXC) {
81  const Comment *C = getASTNode(CXC);
82  if (!C)
83    return 0;
84
85  return C->child_count();
86}
87
88CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
89  const Comment *C = getASTNode(CXC);
90  if (!C || ChildIdx >= C->child_count())
91    return createCXComment(NULL);
92
93  return createCXComment(*(C->child_begin() + ChildIdx));
94}
95
96unsigned clang_Comment_isWhitespace(CXComment CXC) {
97  const Comment *C = getASTNode(CXC);
98  if (!C)
99    return false;
100
101  if (const TextComment *TC = dyn_cast<TextComment>(C))
102    return TC->isWhitespace();
103
104  if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
105    return PC->isWhitespace();
106
107  return false;
108}
109
110unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
111  const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
112  if (!ICC)
113    return false;
114
115  return ICC->hasTrailingNewline();
116}
117
118CXString clang_TextComment_getText(CXComment CXC) {
119  const TextComment *TC = getASTNodeAs<TextComment>(CXC);
120  if (!TC)
121    return createCXString((const char *) 0);
122
123  return createCXString(TC->getText(), /*DupString=*/ false);
124}
125
126CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
127  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
128  if (!ICC)
129    return createCXString((const char *) 0);
130
131  return createCXString(ICC->getCommandName(), /*DupString=*/ false);
132}
133
134enum CXCommentInlineCommandRenderKind
135clang_InlineCommandComment_getRenderKind(CXComment CXC) {
136  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
137  if (!ICC)
138    return CXCommentInlineCommandRenderKind_Normal;
139
140  switch (ICC->getRenderKind()) {
141  case InlineCommandComment::RenderNormal:
142    return CXCommentInlineCommandRenderKind_Normal;
143
144  case InlineCommandComment::RenderBold:
145    return CXCommentInlineCommandRenderKind_Bold;
146
147  case InlineCommandComment::RenderMonospaced:
148    return CXCommentInlineCommandRenderKind_Monospaced;
149
150  case InlineCommandComment::RenderEmphasized:
151    return CXCommentInlineCommandRenderKind_Emphasized;
152  }
153  llvm_unreachable("unknown InlineCommandComment::RenderKind");
154}
155
156unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
157  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
158  if (!ICC)
159    return 0;
160
161  return ICC->getNumArgs();
162}
163
164CXString clang_InlineCommandComment_getArgText(CXComment CXC,
165                                               unsigned ArgIdx) {
166  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
167  if (!ICC || ArgIdx >= ICC->getNumArgs())
168    return createCXString((const char *) 0);
169
170  return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
171}
172
173CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
174  const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
175  if (!HTC)
176    return createCXString((const char *) 0);
177
178  return createCXString(HTC->getTagName(), /*DupString=*/ false);
179}
180
181unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
182  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
183  if (!HST)
184    return false;
185
186  return HST->isSelfClosing();
187}
188
189unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
190  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
191  if (!HST)
192    return 0;
193
194  return HST->getNumAttrs();
195}
196
197CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
198  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
199  if (!HST || AttrIdx >= HST->getNumAttrs())
200    return createCXString((const char *) 0);
201
202  return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
203}
204
205CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
206  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
207  if (!HST || AttrIdx >= HST->getNumAttrs())
208    return createCXString((const char *) 0);
209
210  return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
211}
212
213CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
214  const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
215  if (!BCC)
216    return createCXString((const char *) 0);
217
218  return createCXString(BCC->getCommandName(), /*DupString=*/ false);
219}
220
221unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
222  const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
223  if (!BCC)
224    return 0;
225
226  return BCC->getNumArgs();
227}
228
229CXString clang_BlockCommandComment_getArgText(CXComment CXC,
230                                              unsigned ArgIdx) {
231  const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
232  if (!BCC || ArgIdx >= BCC->getNumArgs())
233    return createCXString((const char *) 0);
234
235  return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
236}
237
238CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
239  const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
240  if (!BCC)
241    return createCXComment(NULL);
242
243  return createCXComment(BCC->getParagraph());
244}
245
246CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
247  const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
248  if (!PCC || !PCC->hasParamName())
249    return createCXString((const char *) 0);
250
251  return createCXString(PCC->getParamName(), /*DupString=*/ false);
252}
253
254unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
255  const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
256  if (!PCC)
257    return false;
258
259  return PCC->isParamIndexValid();
260}
261
262unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
263  const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
264  if (!PCC || !PCC->isParamIndexValid())
265    return ParamCommandComment::InvalidParamIndex;
266
267  return PCC->getParamIndex();
268}
269
270unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
271  const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
272  if (!PCC)
273    return false;
274
275  return PCC->isDirectionExplicit();
276}
277
278enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
279                                                            CXComment CXC) {
280  const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
281  if (!PCC)
282    return CXCommentParamPassDirection_In;
283
284  switch (PCC->getDirection()) {
285  case ParamCommandComment::In:
286    return CXCommentParamPassDirection_In;
287
288  case ParamCommandComment::Out:
289    return CXCommentParamPassDirection_Out;
290
291  case ParamCommandComment::InOut:
292    return CXCommentParamPassDirection_InOut;
293  }
294  llvm_unreachable("unknown ParamCommandComment::PassDirection");
295}
296
297CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
298  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
299  if (!TPCC || !TPCC->hasParamName())
300    return createCXString((const char *) 0);
301
302  return createCXString(TPCC->getParamName(), /*DupString=*/ false);
303}
304
305unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
306  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
307  if (!TPCC)
308    return false;
309
310  return TPCC->isPositionValid();
311}
312
313unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
314  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
315  if (!TPCC || !TPCC->isPositionValid())
316    return 0;
317
318  return TPCC->getDepth();
319}
320
321unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
322  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
323  if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
324    return 0;
325
326  return TPCC->getIndex(Depth);
327}
328
329CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
330  const VerbatimBlockLineComment *VBL =
331      getASTNodeAs<VerbatimBlockLineComment>(CXC);
332  if (!VBL)
333    return createCXString((const char *) 0);
334
335  return createCXString(VBL->getText(), /*DupString=*/ false);
336}
337
338CXString clang_VerbatimLineComment_getText(CXComment CXC) {
339  const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
340  if (!VLC)
341    return createCXString((const char *) 0);
342
343  return createCXString(VLC->getText(), /*DupString=*/ false);
344}
345
346} // end extern "C"
347
348//===----------------------------------------------------------------------===//
349// Helpers for converting comment AST to HTML.
350//===----------------------------------------------------------------------===//
351
352namespace {
353
354/// This comparison will sort parameters with valid index by index and
355/// invalid (unresolved) parameters last.
356class ParamCommandCommentCompareIndex {
357public:
358  bool operator()(const ParamCommandComment *LHS,
359                  const ParamCommandComment *RHS) const {
360    unsigned LHSIndex = UINT_MAX;
361    unsigned RHSIndex = UINT_MAX;
362    if (LHS->isParamIndexValid())
363      LHSIndex = LHS->getParamIndex();
364    if (RHS->isParamIndexValid())
365      RHSIndex = RHS->getParamIndex();
366
367    return LHSIndex < RHSIndex;
368  }
369};
370
371/// This comparison will sort template parameters in the following order:
372/// \li real template parameters (depth = 1) in index order;
373/// \li all other names (depth > 1);
374/// \li unresolved names.
375class TParamCommandCommentComparePosition {
376public:
377  bool operator()(const TParamCommandComment *LHS,
378                  const TParamCommandComment *RHS) const {
379    // Sort unresolved names last.
380    if (!LHS->isPositionValid())
381      return false;
382    if (!RHS->isPositionValid())
383      return true;
384
385    if (LHS->getDepth() > 1)
386      return false;
387    if (RHS->getDepth() > 1)
388      return true;
389
390    // Sort template parameters in index order.
391    if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
392      return LHS->getIndex(0) < RHS->getIndex(0);
393
394    // Leave all other names in source order.
395    return true;
396  }
397};
398
399class CommentASTToHTMLConverter :
400    public ConstCommentVisitor<CommentASTToHTMLConverter> {
401public:
402  /// \param Str accumulator for HTML.
403  CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
404
405  // Inline content.
406  void visitTextComment(const TextComment *C);
407  void visitInlineCommandComment(const InlineCommandComment *C);
408  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
409  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
410
411  // Block content.
412  void visitParagraphComment(const ParagraphComment *C);
413  void visitBlockCommandComment(const BlockCommandComment *C);
414  void visitParamCommandComment(const ParamCommandComment *C);
415  void visitTParamCommandComment(const TParamCommandComment *C);
416  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
417  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
418  void visitVerbatimLineComment(const VerbatimLineComment *C);
419
420  void visitFullComment(const FullComment *C);
421
422  // Helpers.
423
424  /// Convert a paragraph that is not a block by itself (an argument to some
425  /// command).
426  void visitNonStandaloneParagraphComment(const ParagraphComment *C);
427
428  void appendToResultWithHTMLEscaping(StringRef S);
429
430private:
431  /// Output stream for HTML.
432  llvm::raw_svector_ostream Result;
433};
434} // end unnamed namespace
435
436void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
437  appendToResultWithHTMLEscaping(C->getText());
438}
439
440void CommentASTToHTMLConverter::visitInlineCommandComment(
441                                  const InlineCommandComment *C) {
442  // Nothing to render if no arguments supplied.
443  if (C->getNumArgs() == 0)
444    return;
445
446  // Nothing to render if argument is empty.
447  StringRef Arg0 = C->getArgText(0);
448  if (Arg0.empty())
449    return;
450
451  switch (C->getRenderKind()) {
452  case InlineCommandComment::RenderNormal:
453    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
454      appendToResultWithHTMLEscaping(C->getArgText(i));
455      Result << " ";
456    }
457    return;
458
459  case InlineCommandComment::RenderBold:
460    assert(C->getNumArgs() == 1);
461    Result << "<b>";
462    appendToResultWithHTMLEscaping(Arg0);
463    Result << "</b>";
464    return;
465  case InlineCommandComment::RenderMonospaced:
466    assert(C->getNumArgs() == 1);
467    Result << "<tt>";
468    appendToResultWithHTMLEscaping(Arg0);
469    Result<< "</tt>";
470    return;
471  case InlineCommandComment::RenderEmphasized:
472    assert(C->getNumArgs() == 1);
473    Result << "<em>";
474    appendToResultWithHTMLEscaping(Arg0);
475    Result << "</em>";
476    return;
477  }
478}
479
480void CommentASTToHTMLConverter::visitHTMLStartTagComment(
481                                  const HTMLStartTagComment *C) {
482  Result << "<" << C->getTagName();
483
484  if (C->getNumAttrs() != 0) {
485    for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
486      Result << " ";
487      const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
488      Result << Attr.Name;
489      if (!Attr.Value.empty())
490        Result << "=\"" << Attr.Value << "\"";
491    }
492  }
493
494  if (!C->isSelfClosing())
495    Result << ">";
496  else
497    Result << "/>";
498}
499
500void CommentASTToHTMLConverter::visitHTMLEndTagComment(
501                                  const HTMLEndTagComment *C) {
502  Result << "</" << C->getTagName() << ">";
503}
504
505void CommentASTToHTMLConverter::visitParagraphComment(
506                                  const ParagraphComment *C) {
507  if (C->isWhitespace())
508    return;
509
510  Result << "<p>";
511  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
512       I != E; ++I) {
513    visit(*I);
514  }
515  Result << "</p>";
516}
517
518void CommentASTToHTMLConverter::visitBlockCommandComment(
519                                  const BlockCommandComment *C) {
520  StringRef CommandName = C->getCommandName();
521  if (CommandName == "brief" || CommandName == "short") {
522    Result << "<p class=\"para-brief\">";
523    visitNonStandaloneParagraphComment(C->getParagraph());
524    Result << "</p>";
525    return;
526  }
527  if (CommandName == "returns" || CommandName == "return" ||
528      CommandName == "result") {
529    Result << "<p class=\"para-returns\">"
530              "<span class=\"word-returns\">Returns</span> ";
531    visitNonStandaloneParagraphComment(C->getParagraph());
532    Result << "</p>";
533    return;
534  }
535  // We don't know anything about this command.  Just render the paragraph.
536  visit(C->getParagraph());
537}
538
539void CommentASTToHTMLConverter::visitParamCommandComment(
540                                  const ParamCommandComment *C) {
541  if (C->isParamIndexValid()) {
542    Result << "<dt class=\"param-name-index-"
543           << C->getParamIndex()
544           << "\">";
545  } else
546    Result << "<dt class=\"param-name-index-invalid\">";
547
548  appendToResultWithHTMLEscaping(C->getParamName());
549  Result << "</dt>";
550
551  if (C->isParamIndexValid()) {
552    Result << "<dd class=\"param-descr-index-"
553           << C->getParamIndex()
554           << "\">";
555  } else
556    Result << "<dd class=\"param-descr-index-invalid\">";
557
558  visitNonStandaloneParagraphComment(C->getParagraph());
559  Result << "</dd>";
560}
561
562void CommentASTToHTMLConverter::visitTParamCommandComment(
563                                  const TParamCommandComment *C) {
564  if (C->isPositionValid()) {
565    if (C->getDepth() == 1)
566      Result << "<dt class=\"taram-name-index-"
567             << C->getIndex(0)
568             << "\">";
569    else
570      Result << "<dt class=\"taram-name-index-other\">";
571  } else
572    Result << "<dt class=\"tparam-name-index-invalid\">";
573
574  appendToResultWithHTMLEscaping(C->getParamName());
575  Result << "</dt>";
576
577  if (C->isPositionValid()) {
578    if (C->getDepth() == 1)
579      Result << "<dd class=\"tparam-descr-index-"
580             << C->getIndex(0)
581             << "\">";
582    else
583      Result << "<dd class=\"tparam-descr-index-other\">";
584  } else
585    Result << "<dd class=\"tparam-descr-index-invalid\">";
586
587  visitNonStandaloneParagraphComment(C->getParagraph());
588  Result << "</dd>";
589}
590
591void CommentASTToHTMLConverter::visitVerbatimBlockComment(
592                                  const VerbatimBlockComment *C) {
593  unsigned NumLines = C->getNumLines();
594  if (NumLines == 0)
595    return;
596
597  Result << "<pre>";
598  for (unsigned i = 0; i != NumLines; ++i) {
599    appendToResultWithHTMLEscaping(C->getText(i));
600    if (i + 1 != NumLines)
601      Result << '\n';
602  }
603  Result << "</pre>";
604}
605
606void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
607                                  const VerbatimBlockLineComment *C) {
608  llvm_unreachable("should not see this AST node");
609}
610
611void CommentASTToHTMLConverter::visitVerbatimLineComment(
612                                  const VerbatimLineComment *C) {
613  Result << "<pre>";
614  appendToResultWithHTMLEscaping(C->getText());
615  Result << "</pre>";
616}
617
618void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
619  const BlockContentComment *Brief = NULL;
620  const ParagraphComment *FirstParagraph = NULL;
621  const BlockCommandComment *Returns = NULL;
622  SmallVector<const ParamCommandComment *, 8> Params;
623  SmallVector<const TParamCommandComment *, 4> TParams;
624  SmallVector<const BlockContentComment *, 8> MiscBlocks;
625
626  // Extract various blocks into separate variables and vectors above.
627  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
628       I != E; ++I) {
629    const Comment *Child = *I;
630    if (!Child)
631      continue;
632    switch (Child->getCommentKind()) {
633    case Comment::NoCommentKind:
634      continue;
635
636    case Comment::ParagraphCommentKind: {
637      const ParagraphComment *PC = cast<ParagraphComment>(Child);
638      if (PC->isWhitespace())
639        break;
640      if (!FirstParagraph)
641        FirstParagraph = PC;
642
643      MiscBlocks.push_back(PC);
644      break;
645    }
646
647    case Comment::BlockCommandCommentKind: {
648      const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
649      StringRef CommandName = BCC->getCommandName();
650      if (!Brief && (CommandName == "brief" || CommandName == "short")) {
651        Brief = BCC;
652        break;
653      }
654      if (!Returns && (CommandName == "returns" || CommandName == "return")) {
655        Returns = BCC;
656        break;
657      }
658      MiscBlocks.push_back(BCC);
659      break;
660    }
661
662    case Comment::ParamCommandCommentKind: {
663      const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
664      if (!PCC->hasParamName())
665        break;
666
667      if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
668        break;
669
670      Params.push_back(PCC);
671      break;
672    }
673
674    case Comment::TParamCommandCommentKind: {
675      const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
676      if (!TPCC->hasParamName())
677        break;
678
679      if (!TPCC->hasNonWhitespaceParagraph())
680        break;
681
682      TParams.push_back(TPCC);
683      break;
684    }
685
686    case Comment::VerbatimBlockCommentKind:
687    case Comment::VerbatimLineCommentKind:
688      MiscBlocks.push_back(cast<BlockCommandComment>(Child));
689      break;
690
691    case Comment::TextCommentKind:
692    case Comment::InlineCommandCommentKind:
693    case Comment::HTMLStartTagCommentKind:
694    case Comment::HTMLEndTagCommentKind:
695    case Comment::VerbatimBlockLineCommentKind:
696    case Comment::FullCommentKind:
697      llvm_unreachable("AST node of this kind can't be a child of "
698                       "a FullComment");
699    }
700  }
701
702  // Sort params in order they are declared in the function prototype.
703  // Unresolved parameters are put at the end of the list in the same order
704  // they were seen in the comment.
705  std::stable_sort(Params.begin(), Params.end(),
706                   ParamCommandCommentCompareIndex());
707
708  std::stable_sort(TParams.begin(), TParams.end(),
709                   TParamCommandCommentComparePosition());
710
711  bool FirstParagraphIsBrief = false;
712  if (Brief)
713    visit(Brief);
714  else if (FirstParagraph) {
715    Result << "<p class=\"para-brief\">";
716    visitNonStandaloneParagraphComment(FirstParagraph);
717    Result << "</p>";
718    FirstParagraphIsBrief = true;
719  }
720
721  for (unsigned i = 0, e = MiscBlocks.size(); i != e; ++i) {
722    const Comment *C = MiscBlocks[i];
723    if (FirstParagraphIsBrief && C == FirstParagraph)
724      continue;
725    visit(C);
726  }
727
728  if (TParams.size() != 0) {
729    Result << "<dl>";
730    for (unsigned i = 0, e = TParams.size(); i != e; ++i)
731      visit(TParams[i]);
732    Result << "</dl>";
733  }
734
735  if (Params.size() != 0) {
736    Result << "<dl>";
737    for (unsigned i = 0, e = Params.size(); i != e; ++i)
738      visit(Params[i]);
739    Result << "</dl>";
740  }
741
742  if (Returns)
743    visit(Returns);
744
745  Result.flush();
746}
747
748void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
749                                  const ParagraphComment *C) {
750  if (!C)
751    return;
752
753  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
754       I != E; ++I) {
755    visit(*I);
756  }
757}
758
759void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
760  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
761    const char C = *I;
762    switch (C) {
763      case '&':
764        Result << "&amp;";
765        break;
766      case '<':
767        Result << "&lt;";
768        break;
769      case '>':
770        Result << "&gt;";
771        break;
772      case '"':
773        Result << "&quot;";
774        break;
775      case '\'':
776        Result << "&#39;";
777        break;
778      case '/':
779        Result << "&#47;";
780        break;
781      default:
782        Result << C;
783        break;
784    }
785  }
786}
787
788extern "C" {
789
790CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
791  const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
792  if (!HTC)
793    return createCXString((const char *) 0);
794
795  SmallString<128> HTML;
796  CommentASTToHTMLConverter Converter(HTML);
797  Converter.visit(HTC);
798  return createCXString(HTML.str(), /* DupString = */ true);
799}
800
801CXString clang_FullComment_getAsHTML(CXComment CXC) {
802  const FullComment *FC = getASTNodeAs<FullComment>(CXC);
803  if (!FC)
804    return createCXString((const char *) 0);
805
806  SmallString<1024> HTML;
807  CommentASTToHTMLConverter Converter(HTML);
808  Converter.visit(FC);
809  return createCXString(HTML.str(), /* DupString = */ true);
810}
811
812} // end extern "C"
813
814