CXComment.cpp revision 88d285cc0fc4ecdbc88d1779dcd2da968aa8b191
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(0), /*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(0), /*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(FullComment *FC,
539                            SmallVectorImpl<char> &Str,
540                            const CommandTraits &Traits) :
541      FC(FC), Result(Str), Traits(Traits)
542  { }
543
544  // Inline content.
545  void visitTextComment(const TextComment *C);
546  void visitInlineCommandComment(const InlineCommandComment *C);
547  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
548  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
549
550  // Block content.
551  void visitParagraphComment(const ParagraphComment *C);
552  void visitBlockCommandComment(const BlockCommandComment *C);
553  void visitParamCommandComment(const ParamCommandComment *C);
554  void visitTParamCommandComment(const TParamCommandComment *C);
555  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
556  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
557  void visitVerbatimLineComment(const VerbatimLineComment *C);
558
559  void visitFullComment(const FullComment *C);
560
561  // Helpers.
562
563  /// Convert a paragraph that is not a block by itself (an argument to some
564  /// command).
565  void visitNonStandaloneParagraphComment(const ParagraphComment *C);
566
567  void appendToResultWithHTMLEscaping(StringRef S);
568
569private:
570  FullComment *FC;
571  /// Output stream for HTML.
572  llvm::raw_svector_ostream Result;
573
574  const CommandTraits &Traits;
575};
576} // end unnamed namespace
577
578void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
579  appendToResultWithHTMLEscaping(C->getText());
580}
581
582void CommentASTToHTMLConverter::visitInlineCommandComment(
583                                  const InlineCommandComment *C) {
584  // Nothing to render if no arguments supplied.
585  if (C->getNumArgs() == 0)
586    return;
587
588  // Nothing to render if argument is empty.
589  StringRef Arg0 = C->getArgText(0);
590  if (Arg0.empty())
591    return;
592
593  switch (C->getRenderKind()) {
594  case InlineCommandComment::RenderNormal:
595    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
596      appendToResultWithHTMLEscaping(C->getArgText(i));
597      Result << " ";
598    }
599    return;
600
601  case InlineCommandComment::RenderBold:
602    assert(C->getNumArgs() == 1);
603    Result << "<b>";
604    appendToResultWithHTMLEscaping(Arg0);
605    Result << "</b>";
606    return;
607  case InlineCommandComment::RenderMonospaced:
608    assert(C->getNumArgs() == 1);
609    Result << "<tt>";
610    appendToResultWithHTMLEscaping(Arg0);
611    Result<< "</tt>";
612    return;
613  case InlineCommandComment::RenderEmphasized:
614    assert(C->getNumArgs() == 1);
615    Result << "<em>";
616    appendToResultWithHTMLEscaping(Arg0);
617    Result << "</em>";
618    return;
619  }
620}
621
622void CommentASTToHTMLConverter::visitHTMLStartTagComment(
623                                  const HTMLStartTagComment *C) {
624  PrintHTMLStartTagComment(C, Result);
625}
626
627void CommentASTToHTMLConverter::visitHTMLEndTagComment(
628                                  const HTMLEndTagComment *C) {
629  Result << "</" << C->getTagName() << ">";
630}
631
632void CommentASTToHTMLConverter::visitParagraphComment(
633                                  const ParagraphComment *C) {
634  if (C->isWhitespace())
635    return;
636
637  Result << "<p>";
638  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
639       I != E; ++I) {
640    visit(*I);
641  }
642  Result << "</p>";
643}
644
645void CommentASTToHTMLConverter::visitBlockCommandComment(
646                                  const BlockCommandComment *C) {
647  const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
648  if (Info->IsBriefCommand) {
649    Result << "<p class=\"para-brief\">";
650    visitNonStandaloneParagraphComment(C->getParagraph());
651    Result << "</p>";
652    return;
653  }
654  if (Info->IsReturnsCommand) {
655    Result << "<p class=\"para-returns\">"
656              "<span class=\"word-returns\">Returns</span> ";
657    visitNonStandaloneParagraphComment(C->getParagraph());
658    Result << "</p>";
659    return;
660  }
661  // We don't know anything about this command.  Just render the paragraph.
662  visit(C->getParagraph());
663}
664
665void CommentASTToHTMLConverter::visitParamCommandComment(
666                                  const ParamCommandComment *C) {
667  if (C->isParamIndexValid()) {
668    Result << "<dt class=\"param-name-index-"
669           << C->getParamIndex()
670           << "\">";
671  } else
672    Result << "<dt class=\"param-name-index-invalid\">";
673
674  appendToResultWithHTMLEscaping(C->getParamName(FC));
675  Result << "</dt>";
676
677  if (C->isParamIndexValid()) {
678    Result << "<dd class=\"param-descr-index-"
679           << C->getParamIndex()
680           << "\">";
681  } else
682    Result << "<dd class=\"param-descr-index-invalid\">";
683
684  visitNonStandaloneParagraphComment(C->getParagraph());
685  Result << "</dd>";
686}
687
688void CommentASTToHTMLConverter::visitTParamCommandComment(
689                                  const TParamCommandComment *C) {
690  if (C->isPositionValid()) {
691    if (C->getDepth() == 1)
692      Result << "<dt class=\"tparam-name-index-"
693             << C->getIndex(0)
694             << "\">";
695    else
696      Result << "<dt class=\"tparam-name-index-other\">";
697  } else
698    Result << "<dt class=\"tparam-name-index-invalid\">";
699
700  appendToResultWithHTMLEscaping(C->getParamName(FC));
701  Result << "</dt>";
702
703  if (C->isPositionValid()) {
704    if (C->getDepth() == 1)
705      Result << "<dd class=\"tparam-descr-index-"
706             << C->getIndex(0)
707             << "\">";
708    else
709      Result << "<dd class=\"tparam-descr-index-other\">";
710  } else
711    Result << "<dd class=\"tparam-descr-index-invalid\">";
712
713  visitNonStandaloneParagraphComment(C->getParagraph());
714  Result << "</dd>";
715}
716
717void CommentASTToHTMLConverter::visitVerbatimBlockComment(
718                                  const VerbatimBlockComment *C) {
719  unsigned NumLines = C->getNumLines();
720  if (NumLines == 0)
721    return;
722
723  Result << "<pre>";
724  for (unsigned i = 0; i != NumLines; ++i) {
725    appendToResultWithHTMLEscaping(C->getText(i));
726    if (i + 1 != NumLines)
727      Result << '\n';
728  }
729  Result << "</pre>";
730}
731
732void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
733                                  const VerbatimBlockLineComment *C) {
734  llvm_unreachable("should not see this AST node");
735}
736
737void CommentASTToHTMLConverter::visitVerbatimLineComment(
738                                  const VerbatimLineComment *C) {
739  Result << "<pre>";
740  appendToResultWithHTMLEscaping(C->getText());
741  Result << "</pre>";
742}
743
744void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
745  FullCommentParts Parts(C, Traits);
746
747  bool FirstParagraphIsBrief = false;
748  if (Parts.Brief)
749    visit(Parts.Brief);
750  else if (Parts.FirstParagraph) {
751    Result << "<p class=\"para-brief\">";
752    visitNonStandaloneParagraphComment(Parts.FirstParagraph);
753    Result << "</p>";
754    FirstParagraphIsBrief = true;
755  }
756
757  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
758    const Comment *C = Parts.MiscBlocks[i];
759    if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
760      continue;
761    visit(C);
762  }
763
764  if (Parts.TParams.size() != 0) {
765    Result << "<dl>";
766    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
767      visit(Parts.TParams[i]);
768    Result << "</dl>";
769  }
770
771  if (Parts.Params.size() != 0) {
772    Result << "<dl>";
773    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
774      visit(Parts.Params[i]);
775    Result << "</dl>";
776  }
777
778  if (Parts.Returns)
779    visit(Parts.Returns);
780
781  Result.flush();
782}
783
784void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
785                                  const ParagraphComment *C) {
786  if (!C)
787    return;
788
789  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
790       I != E; ++I) {
791    visit(*I);
792  }
793}
794
795void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
796  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
797    const char C = *I;
798    switch (C) {
799      case '&':
800        Result << "&amp;";
801        break;
802      case '<':
803        Result << "&lt;";
804        break;
805      case '>':
806        Result << "&gt;";
807        break;
808      case '"':
809        Result << "&quot;";
810        break;
811      case '\'':
812        Result << "&#39;";
813        break;
814      case '/':
815        Result << "&#47;";
816        break;
817      default:
818        Result << C;
819        break;
820    }
821  }
822}
823
824extern "C" {
825
826CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
827  const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
828  if (!HTC)
829    return createCXString((const char *) 0);
830
831  SmallString<128> HTML;
832  CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
833  Converter.visit(HTC);
834  return createCXString(HTML.str(), /* DupString = */ true);
835}
836
837CXString clang_FullComment_getAsHTML(CXComment CXC) {
838  const FullComment *FC = getASTNodeAs<FullComment>(CXC);
839  if (!FC)
840    return createCXString((const char *) 0);
841
842  SmallString<1024> HTML;
843  CommentASTToHTMLConverter Converter(const_cast<FullComment *>(FC),
844                                      HTML, getCommandTraits(CXC));
845  Converter.visit(FC);
846  return createCXString(HTML.str(), /* DupString = */ true);
847}
848
849} // end extern "C"
850
851namespace {
852class CommentASTToXMLConverter :
853    public ConstCommentVisitor<CommentASTToXMLConverter> {
854public:
855  /// \param Str accumulator for XML.
856  CommentASTToXMLConverter(FullComment *FC,
857                           SmallVectorImpl<char> &Str,
858                           const CommandTraits &Traits,
859                           const SourceManager &SM) :
860      FC(FC), Result(Str), Traits(Traits), SM(SM) { }
861
862  // Inline content.
863  void visitTextComment(const TextComment *C);
864  void visitInlineCommandComment(const InlineCommandComment *C);
865  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
866  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
867
868  // Block content.
869  void visitParagraphComment(const ParagraphComment *C);
870  void visitBlockCommandComment(const BlockCommandComment *C);
871  void visitParamCommandComment(const ParamCommandComment *C);
872  void visitTParamCommandComment(const TParamCommandComment *C);
873  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
874  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
875  void visitVerbatimLineComment(const VerbatimLineComment *C);
876
877  void visitFullComment(const FullComment *C);
878
879  // Helpers.
880  void appendToResultWithXMLEscaping(StringRef S);
881
882private:
883  FullComment *FC;
884
885  /// Output stream for XML.
886  llvm::raw_svector_ostream Result;
887
888  const CommandTraits &Traits;
889  const SourceManager &SM;
890};
891} // end unnamed namespace
892
893void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
894  appendToResultWithXMLEscaping(C->getText());
895}
896
897void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
898  // Nothing to render if no arguments supplied.
899  if (C->getNumArgs() == 0)
900    return;
901
902  // Nothing to render if argument is empty.
903  StringRef Arg0 = C->getArgText(0);
904  if (Arg0.empty())
905    return;
906
907  switch (C->getRenderKind()) {
908  case InlineCommandComment::RenderNormal:
909    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
910      appendToResultWithXMLEscaping(C->getArgText(i));
911      Result << " ";
912    }
913    return;
914  case InlineCommandComment::RenderBold:
915    assert(C->getNumArgs() == 1);
916    Result << "<bold>";
917    appendToResultWithXMLEscaping(Arg0);
918    Result << "</bold>";
919    return;
920  case InlineCommandComment::RenderMonospaced:
921    assert(C->getNumArgs() == 1);
922    Result << "<monospaced>";
923    appendToResultWithXMLEscaping(Arg0);
924    Result << "</monospaced>";
925    return;
926  case InlineCommandComment::RenderEmphasized:
927    assert(C->getNumArgs() == 1);
928    Result << "<emphasized>";
929    appendToResultWithXMLEscaping(Arg0);
930    Result << "</emphasized>";
931    return;
932  }
933}
934
935void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
936  Result << "<rawHTML><![CDATA[";
937  PrintHTMLStartTagComment(C, Result);
938  Result << "]]></rawHTML>";
939}
940
941void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
942  Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
943}
944
945void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
946  if (C->isWhitespace())
947    return;
948
949  Result << "<Para>";
950  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
951       I != E; ++I) {
952    visit(*I);
953  }
954  Result << "</Para>";
955}
956
957void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
958  visit(C->getParagraph());
959}
960
961void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
962  Result << "<Parameter><Name>";
963  appendToResultWithXMLEscaping(C->getParamName(FC));
964  Result << "</Name>";
965
966  if (C->isParamIndexValid())
967    Result << "<Index>" << C->getParamIndex() << "</Index>";
968
969  Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
970  switch (C->getDirection()) {
971  case ParamCommandComment::In:
972    Result << "in";
973    break;
974  case ParamCommandComment::Out:
975    Result << "out";
976    break;
977  case ParamCommandComment::InOut:
978    Result << "in,out";
979    break;
980  }
981  Result << "</Direction><Discussion>";
982  visit(C->getParagraph());
983  Result << "</Discussion></Parameter>";
984}
985
986void CommentASTToXMLConverter::visitTParamCommandComment(
987                                  const TParamCommandComment *C) {
988  Result << "<Parameter><Name>";
989  appendToResultWithXMLEscaping(C->getParamName(FC));
990  Result << "</Name>";
991
992  if (C->isPositionValid() && C->getDepth() == 1) {
993    Result << "<Index>" << C->getIndex(0) << "</Index>";
994  }
995
996  Result << "<Discussion>";
997  visit(C->getParagraph());
998  Result << "</Discussion></Parameter>";
999}
1000
1001void CommentASTToXMLConverter::visitVerbatimBlockComment(
1002                                  const VerbatimBlockComment *C) {
1003  unsigned NumLines = C->getNumLines();
1004  if (NumLines == 0)
1005    return;
1006
1007  Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits))
1008      .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
1009      .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
1010  for (unsigned i = 0; i != NumLines; ++i) {
1011    appendToResultWithXMLEscaping(C->getText(i));
1012    if (i + 1 != NumLines)
1013      Result << '\n';
1014  }
1015  Result << "</Verbatim>";
1016}
1017
1018void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1019                                  const VerbatimBlockLineComment *C) {
1020  llvm_unreachable("should not see this AST node");
1021}
1022
1023void CommentASTToXMLConverter::visitVerbatimLineComment(
1024                                  const VerbatimLineComment *C) {
1025  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1026  appendToResultWithXMLEscaping(C->getText());
1027  Result << "</Verbatim>";
1028}
1029
1030void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
1031  FullCommentParts Parts(C, Traits);
1032
1033  const DeclInfo *DI = C->getDeclInfo();
1034  StringRef RootEndTag;
1035  if (DI) {
1036    switch (DI->getKind()) {
1037    case DeclInfo::OtherKind:
1038      RootEndTag = "</Other>";
1039      Result << "<Other";
1040      break;
1041    case DeclInfo::FunctionKind:
1042      RootEndTag = "</Function>";
1043      Result << "<Function";
1044      switch (DI->TemplateKind) {
1045      case DeclInfo::NotTemplate:
1046        break;
1047      case DeclInfo::Template:
1048        Result << " templateKind=\"template\"";
1049        break;
1050      case DeclInfo::TemplateSpecialization:
1051        Result << " templateKind=\"specialization\"";
1052        break;
1053      case DeclInfo::TemplatePartialSpecialization:
1054        llvm_unreachable("partial specializations of functions "
1055                         "are not allowed in C++");
1056      }
1057      if (DI->IsInstanceMethod)
1058        Result << " isInstanceMethod=\"1\"";
1059      if (DI->IsClassMethod)
1060        Result << " isClassMethod=\"1\"";
1061      break;
1062    case DeclInfo::ClassKind:
1063      RootEndTag = "</Class>";
1064      Result << "<Class";
1065      switch (DI->TemplateKind) {
1066      case DeclInfo::NotTemplate:
1067        break;
1068      case DeclInfo::Template:
1069        Result << " templateKind=\"template\"";
1070        break;
1071      case DeclInfo::TemplateSpecialization:
1072        Result << " templateKind=\"specialization\"";
1073        break;
1074      case DeclInfo::TemplatePartialSpecialization:
1075        Result << " templateKind=\"partialSpecialization\"";
1076        break;
1077      }
1078      break;
1079    case DeclInfo::VariableKind:
1080      RootEndTag = "</Variable>";
1081      Result << "<Variable";
1082      break;
1083    case DeclInfo::NamespaceKind:
1084      RootEndTag = "</Namespace>";
1085      Result << "<Namespace";
1086      break;
1087    case DeclInfo::TypedefKind:
1088      RootEndTag = "</Typedef>";
1089      Result << "<Typedef";
1090      break;
1091    case DeclInfo::EnumKind:
1092      RootEndTag = "</Enum>";
1093      Result << "<Enum";
1094      break;
1095    }
1096
1097    {
1098      // Print line and column number.
1099      SourceLocation Loc = DI->Loc;
1100      std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1101      FileID FID = LocInfo.first;
1102      unsigned FileOffset = LocInfo.second;
1103
1104      if (!FID.isInvalid()) {
1105        if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1106          Result << " file=\"";
1107          appendToResultWithXMLEscaping(FE->getName());
1108          Result << "\"";
1109        }
1110        Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1111               << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1112               << "\"";
1113      }
1114    }
1115
1116    // Finish the root tag.
1117    Result << ">";
1118
1119    bool FoundName = false;
1120    if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
1121      if (DeclarationName DeclName = ND->getDeclName()) {
1122        Result << "<Name>";
1123        std::string Name = DeclName.getAsString();
1124        appendToResultWithXMLEscaping(Name);
1125        FoundName = true;
1126        Result << "</Name>";
1127      }
1128    }
1129    if (!FoundName)
1130      Result << "<Name>&lt;anonymous&gt;</Name>";
1131
1132    {
1133      // Print USR.
1134      SmallString<128> USR;
1135      cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
1136      if (!USR.empty()) {
1137        Result << "<USR>";
1138        appendToResultWithXMLEscaping(USR);
1139        Result << "</USR>";
1140      }
1141    }
1142  } else {
1143    // No DeclInfo -- just emit some root tag and name tag.
1144    RootEndTag = "</Other>";
1145    Result << "<Other><Name>unknown</Name>";
1146  }
1147
1148  bool FirstParagraphIsBrief = false;
1149  if (Parts.Brief) {
1150    Result << "<Abstract>";
1151    visit(Parts.Brief);
1152    Result << "</Abstract>";
1153  } else if (Parts.FirstParagraph) {
1154    Result << "<Abstract>";
1155    visit(Parts.FirstParagraph);
1156    Result << "</Abstract>";
1157    FirstParagraphIsBrief = true;
1158  }
1159
1160  if (Parts.TParams.size() != 0) {
1161    Result << "<TemplateParameters>";
1162    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1163      visit(Parts.TParams[i]);
1164    Result << "</TemplateParameters>";
1165  }
1166
1167  if (Parts.Params.size() != 0) {
1168    Result << "<Parameters>";
1169    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1170      visit(Parts.Params[i]);
1171    Result << "</Parameters>";
1172  }
1173
1174  if (Parts.Returns) {
1175    Result << "<ResultDiscussion>";
1176    visit(Parts.Returns);
1177    Result << "</ResultDiscussion>";
1178  }
1179
1180  if (DI->CommentDecl->hasAttrs()) {
1181    const AttrVec &Attrs = DI->CommentDecl->getAttrs();
1182    for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1183      const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1184      if (!AA) {
1185        if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1186          if (DA->getMessage().empty())
1187            Result << "<Deprecated/>";
1188          else {
1189            Result << "<Deprecated>";
1190            appendToResultWithXMLEscaping(DA->getMessage());
1191            Result << "</Deprecated>";
1192          }
1193        }
1194        else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1195          if (UA->getMessage().empty())
1196            Result << "<Unavailable/>";
1197          else {
1198            Result << "<Unavailable>";
1199            appendToResultWithXMLEscaping(UA->getMessage());
1200            Result << "</Unavailable>";
1201          }
1202        }
1203        continue;
1204      }
1205
1206      // 'availability' attribute.
1207      Result << "<Availability";
1208      StringRef Distribution;
1209      if (AA->getPlatform()) {
1210        Distribution = AvailabilityAttr::getPrettyPlatformName(
1211                                        AA->getPlatform()->getName());
1212        if (Distribution.empty())
1213          Distribution = AA->getPlatform()->getName();
1214      }
1215      Result << " distribution=\"" << Distribution << "\">";
1216      VersionTuple IntroducedInVersion = AA->getIntroduced();
1217      if (!IntroducedInVersion.empty()) {
1218        Result << "<IntroducedInVersion>"
1219               << IntroducedInVersion.getAsString()
1220               << "</IntroducedInVersion>";
1221      }
1222      VersionTuple DeprecatedInVersion = AA->getDeprecated();
1223      if (!DeprecatedInVersion.empty()) {
1224        Result << "<DeprecatedInVersion>"
1225               << DeprecatedInVersion.getAsString()
1226               << "</DeprecatedInVersion>";
1227      }
1228      VersionTuple RemovedAfterVersion = AA->getObsoleted();
1229      if (!RemovedAfterVersion.empty()) {
1230        Result << "<RemovedAfterVersion>"
1231               << RemovedAfterVersion.getAsString()
1232               << "</RemovedAfterVersion>";
1233      }
1234      StringRef DeprecationSummary = AA->getMessage();
1235      if (!DeprecationSummary.empty()) {
1236        Result << "<DeprecationSummary>";
1237        appendToResultWithXMLEscaping(DeprecationSummary);
1238        Result << "</DeprecationSummary>";
1239      }
1240      if (AA->getUnavailable())
1241        Result << "<Unavailable/>";
1242      Result << "</Availability>";
1243    }
1244  }
1245
1246  {
1247    bool StartTagEmitted = false;
1248    for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1249      const Comment *C = Parts.MiscBlocks[i];
1250      if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1251        continue;
1252      if (!StartTagEmitted) {
1253        Result << "<Discussion>";
1254        StartTagEmitted = true;
1255      }
1256      visit(C);
1257    }
1258    if (StartTagEmitted)
1259      Result << "</Discussion>";
1260  }
1261
1262  Result << RootEndTag;
1263
1264  Result.flush();
1265}
1266
1267void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1268  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1269    const char C = *I;
1270    switch (C) {
1271      case '&':
1272        Result << "&amp;";
1273        break;
1274      case '<':
1275        Result << "&lt;";
1276        break;
1277      case '>':
1278        Result << "&gt;";
1279        break;
1280      case '"':
1281        Result << "&quot;";
1282        break;
1283      case '\'':
1284        Result << "&apos;";
1285        break;
1286      default:
1287        Result << C;
1288        break;
1289    }
1290  }
1291}
1292
1293extern "C" {
1294
1295CXString clang_FullComment_getAsXML(CXComment CXC) {
1296  const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1297  if (!FC)
1298    return createCXString((const char *) 0);
1299
1300  CXTranslationUnit TU = CXC.TranslationUnit;
1301  SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1302
1303  SmallString<1024> XML;
1304  CommentASTToXMLConverter Converter(const_cast<FullComment *>(FC), XML,
1305                                     getCommandTraits(CXC), SM);
1306  Converter.visit(FC);
1307  return createCXString(XML.str(), /* DupString = */ true);
1308}
1309
1310} // end extern "C"
1311
1312