CXComment.cpp revision 2ff84b514e53f4273c7067a5aade680a155a9045
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
399/// Separate parts of a FullComment.
400struct FullCommentParts {
401  /// Take a full comment apart and initialize members accordingly.
402  FullCommentParts(const FullComment *C);
403
404  const BlockContentComment *Brief;
405  const ParagraphComment *FirstParagraph;
406  const BlockCommandComment *Returns;
407  SmallVector<const ParamCommandComment *, 8> Params;
408  SmallVector<const TParamCommandComment *, 4> TParams;
409  SmallVector<const BlockContentComment *, 8> MiscBlocks;
410};
411
412FullCommentParts::FullCommentParts(const FullComment *C) :
413    Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
414  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
415       I != E; ++I) {
416    const Comment *Child = *I;
417    if (!Child)
418      continue;
419    switch (Child->getCommentKind()) {
420    case Comment::NoCommentKind:
421      continue;
422
423    case Comment::ParagraphCommentKind: {
424      const ParagraphComment *PC = cast<ParagraphComment>(Child);
425      if (PC->isWhitespace())
426        break;
427      if (!FirstParagraph)
428        FirstParagraph = PC;
429
430      MiscBlocks.push_back(PC);
431      break;
432    }
433
434    case Comment::BlockCommandCommentKind: {
435      const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
436      StringRef CommandName = BCC->getCommandName();
437      if (!Brief && (CommandName == "brief" || CommandName == "short")) {
438        Brief = BCC;
439        break;
440      }
441      if (!Returns && (CommandName == "returns" || CommandName == "return")) {
442        Returns = BCC;
443        break;
444      }
445      MiscBlocks.push_back(BCC);
446      break;
447    }
448
449    case Comment::ParamCommandCommentKind: {
450      const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
451      if (!PCC->hasParamName())
452        break;
453
454      if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
455        break;
456
457      Params.push_back(PCC);
458      break;
459    }
460
461    case Comment::TParamCommandCommentKind: {
462      const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
463      if (!TPCC->hasParamName())
464        break;
465
466      if (!TPCC->hasNonWhitespaceParagraph())
467        break;
468
469      TParams.push_back(TPCC);
470      break;
471    }
472
473    case Comment::VerbatimBlockCommentKind:
474    case Comment::VerbatimLineCommentKind:
475      MiscBlocks.push_back(cast<BlockCommandComment>(Child));
476      break;
477
478    case Comment::TextCommentKind:
479    case Comment::InlineCommandCommentKind:
480    case Comment::HTMLStartTagCommentKind:
481    case Comment::HTMLEndTagCommentKind:
482    case Comment::VerbatimBlockLineCommentKind:
483    case Comment::FullCommentKind:
484      llvm_unreachable("AST node of this kind can't be a child of "
485                       "a FullComment");
486    }
487  }
488
489  // Sort params in order they are declared in the function prototype.
490  // Unresolved parameters are put at the end of the list in the same order
491  // they were seen in the comment.
492  std::stable_sort(Params.begin(), Params.end(),
493                   ParamCommandCommentCompareIndex());
494
495  std::stable_sort(TParams.begin(), TParams.end(),
496                   TParamCommandCommentComparePosition());
497}
498
499void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
500                              llvm::raw_svector_ostream &Result) {
501  Result << "<" << C->getTagName();
502
503  if (C->getNumAttrs() != 0) {
504    for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
505      Result << " ";
506      const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
507      Result << Attr.Name;
508      if (!Attr.Value.empty())
509        Result << "=\"" << Attr.Value << "\"";
510    }
511  }
512
513  if (!C->isSelfClosing())
514    Result << ">";
515  else
516    Result << "/>";
517}
518
519class CommentASTToHTMLConverter :
520    public ConstCommentVisitor<CommentASTToHTMLConverter> {
521public:
522  /// \param Str accumulator for HTML.
523  CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
524
525  // Inline content.
526  void visitTextComment(const TextComment *C);
527  void visitInlineCommandComment(const InlineCommandComment *C);
528  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
529  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
530
531  // Block content.
532  void visitParagraphComment(const ParagraphComment *C);
533  void visitBlockCommandComment(const BlockCommandComment *C);
534  void visitParamCommandComment(const ParamCommandComment *C);
535  void visitTParamCommandComment(const TParamCommandComment *C);
536  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
537  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
538  void visitVerbatimLineComment(const VerbatimLineComment *C);
539
540  void visitFullComment(const FullComment *C);
541
542  // Helpers.
543
544  /// Convert a paragraph that is not a block by itself (an argument to some
545  /// command).
546  void visitNonStandaloneParagraphComment(const ParagraphComment *C);
547
548  void appendToResultWithHTMLEscaping(StringRef S);
549
550private:
551  /// Output stream for HTML.
552  llvm::raw_svector_ostream Result;
553};
554} // end unnamed namespace
555
556void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
557  appendToResultWithHTMLEscaping(C->getText());
558}
559
560void CommentASTToHTMLConverter::visitInlineCommandComment(
561                                  const InlineCommandComment *C) {
562  // Nothing to render if no arguments supplied.
563  if (C->getNumArgs() == 0)
564    return;
565
566  // Nothing to render if argument is empty.
567  StringRef Arg0 = C->getArgText(0);
568  if (Arg0.empty())
569    return;
570
571  switch (C->getRenderKind()) {
572  case InlineCommandComment::RenderNormal:
573    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
574      appendToResultWithHTMLEscaping(C->getArgText(i));
575      Result << " ";
576    }
577    return;
578
579  case InlineCommandComment::RenderBold:
580    assert(C->getNumArgs() == 1);
581    Result << "<b>";
582    appendToResultWithHTMLEscaping(Arg0);
583    Result << "</b>";
584    return;
585  case InlineCommandComment::RenderMonospaced:
586    assert(C->getNumArgs() == 1);
587    Result << "<tt>";
588    appendToResultWithHTMLEscaping(Arg0);
589    Result<< "</tt>";
590    return;
591  case InlineCommandComment::RenderEmphasized:
592    assert(C->getNumArgs() == 1);
593    Result << "<em>";
594    appendToResultWithHTMLEscaping(Arg0);
595    Result << "</em>";
596    return;
597  }
598}
599
600void CommentASTToHTMLConverter::visitHTMLStartTagComment(
601                                  const HTMLStartTagComment *C) {
602  PrintHTMLStartTagComment(C, Result);
603}
604
605void CommentASTToHTMLConverter::visitHTMLEndTagComment(
606                                  const HTMLEndTagComment *C) {
607  Result << "</" << C->getTagName() << ">";
608}
609
610void CommentASTToHTMLConverter::visitParagraphComment(
611                                  const ParagraphComment *C) {
612  if (C->isWhitespace())
613    return;
614
615  Result << "<p>";
616  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
617       I != E; ++I) {
618    visit(*I);
619  }
620  Result << "</p>";
621}
622
623void CommentASTToHTMLConverter::visitBlockCommandComment(
624                                  const BlockCommandComment *C) {
625  StringRef CommandName = C->getCommandName();
626  if (CommandName == "brief" || CommandName == "short") {
627    Result << "<p class=\"para-brief\">";
628    visitNonStandaloneParagraphComment(C->getParagraph());
629    Result << "</p>";
630    return;
631  }
632  if (CommandName == "returns" || CommandName == "return" ||
633      CommandName == "result") {
634    Result << "<p class=\"para-returns\">"
635              "<span class=\"word-returns\">Returns</span> ";
636    visitNonStandaloneParagraphComment(C->getParagraph());
637    Result << "</p>";
638    return;
639  }
640  // We don't know anything about this command.  Just render the paragraph.
641  visit(C->getParagraph());
642}
643
644void CommentASTToHTMLConverter::visitParamCommandComment(
645                                  const ParamCommandComment *C) {
646  if (C->isParamIndexValid()) {
647    Result << "<dt class=\"param-name-index-"
648           << C->getParamIndex()
649           << "\">";
650  } else
651    Result << "<dt class=\"param-name-index-invalid\">";
652
653  appendToResultWithHTMLEscaping(C->getParamName());
654  Result << "</dt>";
655
656  if (C->isParamIndexValid()) {
657    Result << "<dd class=\"param-descr-index-"
658           << C->getParamIndex()
659           << "\">";
660  } else
661    Result << "<dd class=\"param-descr-index-invalid\">";
662
663  visitNonStandaloneParagraphComment(C->getParagraph());
664  Result << "</dd>";
665}
666
667void CommentASTToHTMLConverter::visitTParamCommandComment(
668                                  const TParamCommandComment *C) {
669  if (C->isPositionValid()) {
670    if (C->getDepth() == 1)
671      Result << "<dt class=\"taram-name-index-"
672             << C->getIndex(0)
673             << "\">";
674    else
675      Result << "<dt class=\"taram-name-index-other\">";
676  } else
677    Result << "<dt class=\"tparam-name-index-invalid\">";
678
679  appendToResultWithHTMLEscaping(C->getParamName());
680  Result << "</dt>";
681
682  if (C->isPositionValid()) {
683    if (C->getDepth() == 1)
684      Result << "<dd class=\"tparam-descr-index-"
685             << C->getIndex(0)
686             << "\">";
687    else
688      Result << "<dd class=\"tparam-descr-index-other\">";
689  } else
690    Result << "<dd class=\"tparam-descr-index-invalid\">";
691
692  visitNonStandaloneParagraphComment(C->getParagraph());
693  Result << "</dd>";
694}
695
696void CommentASTToHTMLConverter::visitVerbatimBlockComment(
697                                  const VerbatimBlockComment *C) {
698  unsigned NumLines = C->getNumLines();
699  if (NumLines == 0)
700    return;
701
702  Result << "<pre>";
703  for (unsigned i = 0; i != NumLines; ++i) {
704    appendToResultWithHTMLEscaping(C->getText(i));
705    if (i + 1 != NumLines)
706      Result << '\n';
707  }
708  Result << "</pre>";
709}
710
711void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
712                                  const VerbatimBlockLineComment *C) {
713  llvm_unreachable("should not see this AST node");
714}
715
716void CommentASTToHTMLConverter::visitVerbatimLineComment(
717                                  const VerbatimLineComment *C) {
718  Result << "<pre>";
719  appendToResultWithHTMLEscaping(C->getText());
720  Result << "</pre>";
721}
722
723void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
724  FullCommentParts Parts(C);
725
726  bool FirstParagraphIsBrief = false;
727  if (Parts.Brief)
728    visit(Parts.Brief);
729  else if (Parts.FirstParagraph) {
730    Result << "<p class=\"para-brief\">";
731    visitNonStandaloneParagraphComment(Parts.FirstParagraph);
732    Result << "</p>";
733    FirstParagraphIsBrief = true;
734  }
735
736  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
737    const Comment *C = Parts.MiscBlocks[i];
738    if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
739      continue;
740    visit(C);
741  }
742
743  if (Parts.TParams.size() != 0) {
744    Result << "<dl>";
745    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
746      visit(Parts.TParams[i]);
747    Result << "</dl>";
748  }
749
750  if (Parts.Params.size() != 0) {
751    Result << "<dl>";
752    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
753      visit(Parts.Params[i]);
754    Result << "</dl>";
755  }
756
757  if (Parts.Returns)
758    visit(Parts.Returns);
759
760  Result.flush();
761}
762
763void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
764                                  const ParagraphComment *C) {
765  if (!C)
766    return;
767
768  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
769       I != E; ++I) {
770    visit(*I);
771  }
772}
773
774void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
775  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
776    const char C = *I;
777    switch (C) {
778      case '&':
779        Result << "&amp;";
780        break;
781      case '<':
782        Result << "&lt;";
783        break;
784      case '>':
785        Result << "&gt;";
786        break;
787      case '"':
788        Result << "&quot;";
789        break;
790      case '\'':
791        Result << "&#39;";
792        break;
793      case '/':
794        Result << "&#47;";
795        break;
796      default:
797        Result << C;
798        break;
799    }
800  }
801}
802
803extern "C" {
804
805CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
806  const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
807  if (!HTC)
808    return createCXString((const char *) 0);
809
810  SmallString<128> HTML;
811  CommentASTToHTMLConverter Converter(HTML);
812  Converter.visit(HTC);
813  return createCXString(HTML.str(), /* DupString = */ true);
814}
815
816CXString clang_FullComment_getAsHTML(CXComment CXC) {
817  const FullComment *FC = getASTNodeAs<FullComment>(CXC);
818  if (!FC)
819    return createCXString((const char *) 0);
820
821  SmallString<1024> HTML;
822  CommentASTToHTMLConverter Converter(HTML);
823  Converter.visit(FC);
824  return createCXString(HTML.str(), /* DupString = */ true);
825}
826
827} // end extern "C"
828
829