clang  3.8.0
CommentToXML.cpp
Go to the documentation of this file.
1 //===--- CommentToXML.cpp - Convert comments to XML representation --------===//
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 
11 #include "SimpleFormatContext.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/AST/Attr.h"
14 #include "clang/AST/Comment.h"
16 #include "clang/Format/Format.h"
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/ADT/TinyPtrVector.h"
20 #include "llvm/Support/raw_ostream.h"
21 
22 using namespace clang;
23 using namespace clang::comments;
24 using namespace clang::index;
25 
26 namespace {
27 
28 /// This comparison will sort parameters with valid index by index, then vararg
29 /// parameters, and invalid (unresolved) parameters last.
30 class ParamCommandCommentCompareIndex {
31 public:
32  bool operator()(const ParamCommandComment *LHS,
33  const ParamCommandComment *RHS) const {
34  unsigned LHSIndex = UINT_MAX;
35  unsigned RHSIndex = UINT_MAX;
36 
37  if (LHS->isParamIndexValid()) {
38  if (LHS->isVarArgParam())
39  LHSIndex = UINT_MAX - 1;
40  else
41  LHSIndex = LHS->getParamIndex();
42  }
43  if (RHS->isParamIndexValid()) {
44  if (RHS->isVarArgParam())
45  RHSIndex = UINT_MAX - 1;
46  else
47  RHSIndex = RHS->getParamIndex();
48  }
49  return LHSIndex < RHSIndex;
50  }
51 };
52 
53 /// This comparison will sort template parameters in the following order:
54 /// \li real template parameters (depth = 1) in index order;
55 /// \li all other names (depth > 1);
56 /// \li unresolved names.
57 class TParamCommandCommentComparePosition {
58 public:
59  bool operator()(const TParamCommandComment *LHS,
60  const TParamCommandComment *RHS) const {
61  // Sort unresolved names last.
62  if (!LHS->isPositionValid())
63  return false;
64  if (!RHS->isPositionValid())
65  return true;
66 
67  if (LHS->getDepth() > 1)
68  return false;
69  if (RHS->getDepth() > 1)
70  return true;
71 
72  // Sort template parameters in index order.
73  if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
74  return LHS->getIndex(0) < RHS->getIndex(0);
75 
76  // Leave all other names in source order.
77  return true;
78  }
79 };
80 
81 /// Separate parts of a FullComment.
82 struct FullCommentParts {
83  /// Take a full comment apart and initialize members accordingly.
84  FullCommentParts(const FullComment *C,
85  const CommandTraits &Traits);
86 
87  const BlockContentComment *Brief;
88  const BlockContentComment *Headerfile;
89  const ParagraphComment *FirstParagraph;
93  llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
95 };
96 
97 FullCommentParts::FullCommentParts(const FullComment *C,
98  const CommandTraits &Traits) :
99  Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
100  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
101  I != E; ++I) {
102  const Comment *Child = *I;
103  if (!Child)
104  continue;
105  switch (Child->getCommentKind()) {
107  continue;
108 
109  case Comment::ParagraphCommentKind: {
110  const ParagraphComment *PC = cast<ParagraphComment>(Child);
111  if (PC->isWhitespace())
112  break;
113  if (!FirstParagraph)
114  FirstParagraph = PC;
115 
116  MiscBlocks.push_back(PC);
117  break;
118  }
119 
120  case Comment::BlockCommandCommentKind: {
121  const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
122  const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
123  if (!Brief && Info->IsBriefCommand) {
124  Brief = BCC;
125  break;
126  }
127  if (!Headerfile && Info->IsHeaderfileCommand) {
128  Headerfile = BCC;
129  break;
130  }
131  if (Info->IsReturnsCommand) {
132  Returns.push_back(BCC);
133  break;
134  }
135  if (Info->IsThrowsCommand) {
136  Exceptions.push_back(BCC);
137  break;
138  }
139  MiscBlocks.push_back(BCC);
140  break;
141  }
142 
143  case Comment::ParamCommandCommentKind: {
144  const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
145  if (!PCC->hasParamName())
146  break;
147 
148  if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
149  break;
150 
151  Params.push_back(PCC);
152  break;
153  }
154 
155  case Comment::TParamCommandCommentKind: {
156  const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
157  if (!TPCC->hasParamName())
158  break;
159 
160  if (!TPCC->hasNonWhitespaceParagraph())
161  break;
162 
163  TParams.push_back(TPCC);
164  break;
165  }
166 
167  case Comment::VerbatimBlockCommentKind:
168  MiscBlocks.push_back(cast<BlockCommandComment>(Child));
169  break;
170 
171  case Comment::VerbatimLineCommentKind: {
172  const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
173  const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
174  if (!Info->IsDeclarationCommand)
175  MiscBlocks.push_back(VLC);
176  break;
177  }
178 
179  case Comment::TextCommentKind:
180  case Comment::InlineCommandCommentKind:
181  case Comment::HTMLStartTagCommentKind:
182  case Comment::HTMLEndTagCommentKind:
183  case Comment::VerbatimBlockLineCommentKind:
184  case Comment::FullCommentKind:
185  llvm_unreachable("AST node of this kind can't be a child of "
186  "a FullComment");
187  }
188  }
189 
190  // Sort params in order they are declared in the function prototype.
191  // Unresolved parameters are put at the end of the list in the same order
192  // they were seen in the comment.
193  std::stable_sort(Params.begin(), Params.end(),
194  ParamCommandCommentCompareIndex());
195 
196  std::stable_sort(TParams.begin(), TParams.end(),
197  TParamCommandCommentComparePosition());
198 }
199 
200 void printHTMLStartTagComment(const HTMLStartTagComment *C,
201  llvm::raw_svector_ostream &Result) {
202  Result << "<" << C->getTagName();
203 
204  if (C->getNumAttrs() != 0) {
205  for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
206  Result << " ";
208  Result << Attr.Name;
209  if (!Attr.Value.empty())
210  Result << "=\"" << Attr.Value << "\"";
211  }
212  }
213 
214  if (!C->isSelfClosing())
215  Result << ">";
216  else
217  Result << "/>";
218 }
219 
220 class CommentASTToHTMLConverter :
221  public ConstCommentVisitor<CommentASTToHTMLConverter> {
222 public:
223  /// \param Str accumulator for HTML.
224  CommentASTToHTMLConverter(const FullComment *FC,
226  const CommandTraits &Traits) :
227  FC(FC), Result(Str), Traits(Traits)
228  { }
229 
230  // Inline content.
231  void visitTextComment(const TextComment *C);
232  void visitInlineCommandComment(const InlineCommandComment *C);
233  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
234  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
235 
236  // Block content.
237  void visitParagraphComment(const ParagraphComment *C);
238  void visitBlockCommandComment(const BlockCommandComment *C);
239  void visitParamCommandComment(const ParamCommandComment *C);
240  void visitTParamCommandComment(const TParamCommandComment *C);
241  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
242  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
243  void visitVerbatimLineComment(const VerbatimLineComment *C);
244 
245  void visitFullComment(const FullComment *C);
246 
247  // Helpers.
248 
249  /// Convert a paragraph that is not a block by itself (an argument to some
250  /// command).
251  void visitNonStandaloneParagraphComment(const ParagraphComment *C);
252 
253  void appendToResultWithHTMLEscaping(StringRef S);
254 
255 private:
256  const FullComment *FC;
257  /// Output stream for HTML.
258  llvm::raw_svector_ostream Result;
259 
260  const CommandTraits &Traits;
261 };
262 } // end unnamed namespace
263 
264 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
265  appendToResultWithHTMLEscaping(C->getText());
266 }
267 
268 void CommentASTToHTMLConverter::visitInlineCommandComment(
269  const InlineCommandComment *C) {
270  // Nothing to render if no arguments supplied.
271  if (C->getNumArgs() == 0)
272  return;
273 
274  // Nothing to render if argument is empty.
275  StringRef Arg0 = C->getArgText(0);
276  if (Arg0.empty())
277  return;
278 
279  switch (C->getRenderKind()) {
281  for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
282  appendToResultWithHTMLEscaping(C->getArgText(i));
283  Result << " ";
284  }
285  return;
286 
288  assert(C->getNumArgs() == 1);
289  Result << "<b>";
290  appendToResultWithHTMLEscaping(Arg0);
291  Result << "</b>";
292  return;
294  assert(C->getNumArgs() == 1);
295  Result << "<tt>";
296  appendToResultWithHTMLEscaping(Arg0);
297  Result<< "</tt>";
298  return;
300  assert(C->getNumArgs() == 1);
301  Result << "<em>";
302  appendToResultWithHTMLEscaping(Arg0);
303  Result << "</em>";
304  return;
305  }
306 }
307 
308 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
309  const HTMLStartTagComment *C) {
310  printHTMLStartTagComment(C, Result);
311 }
312 
313 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
314  const HTMLEndTagComment *C) {
315  Result << "</" << C->getTagName() << ">";
316 }
317 
318 void CommentASTToHTMLConverter::visitParagraphComment(
319  const ParagraphComment *C) {
320  if (C->isWhitespace())
321  return;
322 
323  Result << "<p>";
324  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
325  I != E; ++I) {
326  visit(*I);
327  }
328  Result << "</p>";
329 }
330 
331 void CommentASTToHTMLConverter::visitBlockCommandComment(
332  const BlockCommandComment *C) {
333  const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
334  if (Info->IsBriefCommand) {
335  Result << "<p class=\"para-brief\">";
336  visitNonStandaloneParagraphComment(C->getParagraph());
337  Result << "</p>";
338  return;
339  }
340  if (Info->IsReturnsCommand) {
341  Result << "<p class=\"para-returns\">"
342  "<span class=\"word-returns\">Returns</span> ";
343  visitNonStandaloneParagraphComment(C->getParagraph());
344  Result << "</p>";
345  return;
346  }
347  // We don't know anything about this command. Just render the paragraph.
348  visit(C->getParagraph());
349 }
350 
351 void CommentASTToHTMLConverter::visitParamCommandComment(
352  const ParamCommandComment *C) {
353  if (C->isParamIndexValid()) {
354  if (C->isVarArgParam()) {
355  Result << "<dt class=\"param-name-index-vararg\">";
356  appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
357  } else {
358  Result << "<dt class=\"param-name-index-"
359  << C->getParamIndex()
360  << "\">";
361  appendToResultWithHTMLEscaping(C->getParamName(FC));
362  }
363  } else {
364  Result << "<dt class=\"param-name-index-invalid\">";
365  appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
366  }
367  Result << "</dt>";
368 
369  if (C->isParamIndexValid()) {
370  if (C->isVarArgParam())
371  Result << "<dd class=\"param-descr-index-vararg\">";
372  else
373  Result << "<dd class=\"param-descr-index-"
374  << C->getParamIndex()
375  << "\">";
376  } else
377  Result << "<dd class=\"param-descr-index-invalid\">";
378 
379  visitNonStandaloneParagraphComment(C->getParagraph());
380  Result << "</dd>";
381 }
382 
383 void CommentASTToHTMLConverter::visitTParamCommandComment(
384  const TParamCommandComment *C) {
385  if (C->isPositionValid()) {
386  if (C->getDepth() == 1)
387  Result << "<dt class=\"tparam-name-index-"
388  << C->getIndex(0)
389  << "\">";
390  else
391  Result << "<dt class=\"tparam-name-index-other\">";
392  appendToResultWithHTMLEscaping(C->getParamName(FC));
393  } else {
394  Result << "<dt class=\"tparam-name-index-invalid\">";
395  appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
396  }
397 
398  Result << "</dt>";
399 
400  if (C->isPositionValid()) {
401  if (C->getDepth() == 1)
402  Result << "<dd class=\"tparam-descr-index-"
403  << C->getIndex(0)
404  << "\">";
405  else
406  Result << "<dd class=\"tparam-descr-index-other\">";
407  } else
408  Result << "<dd class=\"tparam-descr-index-invalid\">";
409 
410  visitNonStandaloneParagraphComment(C->getParagraph());
411  Result << "</dd>";
412 }
413 
414 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
415  const VerbatimBlockComment *C) {
416  unsigned NumLines = C->getNumLines();
417  if (NumLines == 0)
418  return;
419 
420  Result << "<pre>";
421  for (unsigned i = 0; i != NumLines; ++i) {
422  appendToResultWithHTMLEscaping(C->getText(i));
423  if (i + 1 != NumLines)
424  Result << '\n';
425  }
426  Result << "</pre>";
427 }
428 
429 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
430  const VerbatimBlockLineComment *C) {
431  llvm_unreachable("should not see this AST node");
432 }
433 
434 void CommentASTToHTMLConverter::visitVerbatimLineComment(
435  const VerbatimLineComment *C) {
436  Result << "<pre>";
437  appendToResultWithHTMLEscaping(C->getText());
438  Result << "</pre>";
439 }
440 
441 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
442  FullCommentParts Parts(C, Traits);
443 
444  bool FirstParagraphIsBrief = false;
445  if (Parts.Headerfile)
446  visit(Parts.Headerfile);
447  if (Parts.Brief)
448  visit(Parts.Brief);
449  else if (Parts.FirstParagraph) {
450  Result << "<p class=\"para-brief\">";
451  visitNonStandaloneParagraphComment(Parts.FirstParagraph);
452  Result << "</p>";
453  FirstParagraphIsBrief = true;
454  }
455 
456  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
457  const Comment *C = Parts.MiscBlocks[i];
458  if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
459  continue;
460  visit(C);
461  }
462 
463  if (Parts.TParams.size() != 0) {
464  Result << "<dl>";
465  for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
466  visit(Parts.TParams[i]);
467  Result << "</dl>";
468  }
469 
470  if (Parts.Params.size() != 0) {
471  Result << "<dl>";
472  for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
473  visit(Parts.Params[i]);
474  Result << "</dl>";
475  }
476 
477  if (Parts.Returns.size() != 0) {
478  Result << "<div class=\"result-discussion\">";
479  for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
480  visit(Parts.Returns[i]);
481  Result << "</div>";
482  }
483 
484 }
485 
486 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
487  const ParagraphComment *C) {
488  if (!C)
489  return;
490 
491  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
492  I != E; ++I) {
493  visit(*I);
494  }
495 }
496 
497 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
498  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
499  const char C = *I;
500  switch (C) {
501  case '&':
502  Result << "&amp;";
503  break;
504  case '<':
505  Result << "&lt;";
506  break;
507  case '>':
508  Result << "&gt;";
509  break;
510  case '"':
511  Result << "&quot;";
512  break;
513  case '\'':
514  Result << "&#39;";
515  break;
516  case '/':
517  Result << "&#47;";
518  break;
519  default:
520  Result << C;
521  break;
522  }
523  }
524 }
525 
526 namespace {
527 class CommentASTToXMLConverter :
528  public ConstCommentVisitor<CommentASTToXMLConverter> {
529 public:
530  /// \param Str accumulator for XML.
531  CommentASTToXMLConverter(const FullComment *FC,
533  const CommandTraits &Traits,
534  const SourceManager &SM,
535  SimpleFormatContext &SFC,
536  unsigned FUID) :
537  FC(FC), Result(Str), Traits(Traits), SM(SM),
538  FormatRewriterContext(SFC),
539  FormatInMemoryUniqueId(FUID) { }
540 
541  // Inline content.
542  void visitTextComment(const TextComment *C);
543  void visitInlineCommandComment(const InlineCommandComment *C);
544  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
545  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
546 
547  // Block content.
548  void visitParagraphComment(const ParagraphComment *C);
549 
550  void appendParagraphCommentWithKind(const ParagraphComment *C,
551  StringRef Kind);
552 
553  void visitBlockCommandComment(const BlockCommandComment *C);
554  void visitParamCommandComment(const ParamCommandComment *C);
555  void visitTParamCommandComment(const TParamCommandComment *C);
556  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
557  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
558  void visitVerbatimLineComment(const VerbatimLineComment *C);
559 
560  void visitFullComment(const FullComment *C);
561 
562  // Helpers.
563  void appendToResultWithXMLEscaping(StringRef S);
564  void appendToResultWithCDATAEscaping(StringRef S);
565 
566  void formatTextOfDeclaration(const DeclInfo *DI,
567  SmallString<128> &Declaration);
568 
569 private:
570  const FullComment *FC;
571 
572  /// Output stream for XML.
573  llvm::raw_svector_ostream Result;
574 
575  const CommandTraits &Traits;
576  const SourceManager &SM;
577  SimpleFormatContext &FormatRewriterContext;
578  unsigned FormatInMemoryUniqueId;
579 };
580 
581 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
582  SmallVectorImpl<char> &Str) {
584  const LangOptions &LangOpts = Context.getLangOpts();
585  llvm::raw_svector_ostream OS(Str);
586  PrintingPolicy PPolicy(LangOpts);
587  PPolicy.PolishForDeclaration = true;
588  PPolicy.TerseOutput = true;
589  ThisDecl->CurrentDecl->print(OS, PPolicy,
590  /*Indentation*/0, /*PrintInstantiation*/false);
591 }
592 
593 void CommentASTToXMLConverter::formatTextOfDeclaration(
594  const DeclInfo *DI, SmallString<128> &Declaration) {
595  // FIXME. formatting API expects null terminated input string.
596  // There might be more efficient way of doing this.
597  std::string StringDecl = Declaration.str();
598 
599  // Formatter specific code.
600  // Form a unique in memory buffer name.
601  SmallString<128> filename;
602  filename += "xmldecl";
603  filename += llvm::utostr(FormatInMemoryUniqueId);
604  filename += ".xd";
605  FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
606  SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID)
607  .getLocWithOffset(0);
608  unsigned Length = Declaration.size();
609 
611  format::getLLVMStyle(), FormatRewriterContext.Sources, ID,
612  CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
613  applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
614  Declaration = FormatRewriterContext.getRewrittenText(ID);
615 }
616 
617 } // end unnamed namespace
618 
619 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
620  appendToResultWithXMLEscaping(C->getText());
621 }
622 
623 void CommentASTToXMLConverter::visitInlineCommandComment(
624  const InlineCommandComment *C) {
625  // Nothing to render if no arguments supplied.
626  if (C->getNumArgs() == 0)
627  return;
628 
629  // Nothing to render if argument is empty.
630  StringRef Arg0 = C->getArgText(0);
631  if (Arg0.empty())
632  return;
633 
634  switch (C->getRenderKind()) {
636  for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
637  appendToResultWithXMLEscaping(C->getArgText(i));
638  Result << " ";
639  }
640  return;
642  assert(C->getNumArgs() == 1);
643  Result << "<bold>";
644  appendToResultWithXMLEscaping(Arg0);
645  Result << "</bold>";
646  return;
648  assert(C->getNumArgs() == 1);
649  Result << "<monospaced>";
650  appendToResultWithXMLEscaping(Arg0);
651  Result << "</monospaced>";
652  return;
654  assert(C->getNumArgs() == 1);
655  Result << "<emphasized>";
656  appendToResultWithXMLEscaping(Arg0);
657  Result << "</emphasized>";
658  return;
659  }
660 }
661 
662 void CommentASTToXMLConverter::visitHTMLStartTagComment(
663  const HTMLStartTagComment *C) {
664  Result << "<rawHTML";
665  if (C->isMalformed())
666  Result << " isMalformed=\"1\"";
667  Result << ">";
668  {
669  SmallString<32> Tag;
670  {
671  llvm::raw_svector_ostream TagOS(Tag);
672  printHTMLStartTagComment(C, TagOS);
673  }
674  appendToResultWithCDATAEscaping(Tag);
675  }
676  Result << "</rawHTML>";
677 }
678 
679 void
680 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
681  Result << "<rawHTML";
682  if (C->isMalformed())
683  Result << " isMalformed=\"1\"";
684  Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
685 }
686 
687 void
688 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
689  appendParagraphCommentWithKind(C, StringRef());
690 }
691 
692 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
693  const ParagraphComment *C,
694  StringRef ParagraphKind) {
695  if (C->isWhitespace())
696  return;
697 
698  if (ParagraphKind.empty())
699  Result << "<Para>";
700  else
701  Result << "<Para kind=\"" << ParagraphKind << "\">";
702 
703  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
704  I != E; ++I) {
705  visit(*I);
706  }
707  Result << "</Para>";
708 }
709 
710 void CommentASTToXMLConverter::visitBlockCommandComment(
711  const BlockCommandComment *C) {
712  StringRef ParagraphKind;
713 
714  switch (C->getCommandID()) {
715  case CommandTraits::KCI_attention:
716  case CommandTraits::KCI_author:
717  case CommandTraits::KCI_authors:
718  case CommandTraits::KCI_bug:
719  case CommandTraits::KCI_copyright:
720  case CommandTraits::KCI_date:
721  case CommandTraits::KCI_invariant:
722  case CommandTraits::KCI_note:
723  case CommandTraits::KCI_post:
724  case CommandTraits::KCI_pre:
725  case CommandTraits::KCI_remark:
726  case CommandTraits::KCI_remarks:
727  case CommandTraits::KCI_sa:
728  case CommandTraits::KCI_see:
729  case CommandTraits::KCI_since:
730  case CommandTraits::KCI_todo:
731  case CommandTraits::KCI_version:
732  case CommandTraits::KCI_warning:
733  ParagraphKind = C->getCommandName(Traits);
734  default:
735  break;
736  }
737 
738  appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
739 }
740 
741 void CommentASTToXMLConverter::visitParamCommandComment(
742  const ParamCommandComment *C) {
743  Result << "<Parameter><Name>";
744  appendToResultWithXMLEscaping(C->isParamIndexValid()
745  ? C->getParamName(FC)
746  : C->getParamNameAsWritten());
747  Result << "</Name>";
748 
749  if (C->isParamIndexValid()) {
750  if (C->isVarArgParam())
751  Result << "<IsVarArg />";
752  else
753  Result << "<Index>" << C->getParamIndex() << "</Index>";
754  }
755 
756  Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
757  switch (C->getDirection()) {
759  Result << "in";
760  break;
762  Result << "out";
763  break;
765  Result << "in,out";
766  break;
767  }
768  Result << "</Direction><Discussion>";
769  visit(C->getParagraph());
770  Result << "</Discussion></Parameter>";
771 }
772 
773 void CommentASTToXMLConverter::visitTParamCommandComment(
774  const TParamCommandComment *C) {
775  Result << "<Parameter><Name>";
776  appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
777  : C->getParamNameAsWritten());
778  Result << "</Name>";
779 
780  if (C->isPositionValid() && C->getDepth() == 1) {
781  Result << "<Index>" << C->getIndex(0) << "</Index>";
782  }
783 
784  Result << "<Discussion>";
785  visit(C->getParagraph());
786  Result << "</Discussion></Parameter>";
787 }
788 
789 void CommentASTToXMLConverter::visitVerbatimBlockComment(
790  const VerbatimBlockComment *C) {
791  unsigned NumLines = C->getNumLines();
792  if (NumLines == 0)
793  return;
794 
795  switch (C->getCommandID()) {
796  case CommandTraits::KCI_code:
797  Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
798  break;
799  default:
800  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
801  break;
802  }
803  for (unsigned i = 0; i != NumLines; ++i) {
804  appendToResultWithXMLEscaping(C->getText(i));
805  if (i + 1 != NumLines)
806  Result << '\n';
807  }
808  Result << "</Verbatim>";
809 }
810 
811 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
812  const VerbatimBlockLineComment *C) {
813  llvm_unreachable("should not see this AST node");
814 }
815 
816 void CommentASTToXMLConverter::visitVerbatimLineComment(
817  const VerbatimLineComment *C) {
818  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
819  appendToResultWithXMLEscaping(C->getText());
820  Result << "</Verbatim>";
821 }
822 
823 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
824  FullCommentParts Parts(C, Traits);
825 
826  const DeclInfo *DI = C->getDeclInfo();
827  StringRef RootEndTag;
828  if (DI) {
829  switch (DI->getKind()) {
830  case DeclInfo::OtherKind:
831  RootEndTag = "</Other>";
832  Result << "<Other";
833  break;
835  RootEndTag = "</Function>";
836  Result << "<Function";
837  switch (DI->TemplateKind) {
839  break;
840  case DeclInfo::Template:
841  Result << " templateKind=\"template\"";
842  break;
844  Result << " templateKind=\"specialization\"";
845  break;
847  llvm_unreachable("partial specializations of functions "
848  "are not allowed in C++");
849  }
850  if (DI->IsInstanceMethod)
851  Result << " isInstanceMethod=\"1\"";
852  if (DI->IsClassMethod)
853  Result << " isClassMethod=\"1\"";
854  break;
855  case DeclInfo::ClassKind:
856  RootEndTag = "</Class>";
857  Result << "<Class";
858  switch (DI->TemplateKind) {
860  break;
861  case DeclInfo::Template:
862  Result << " templateKind=\"template\"";
863  break;
865  Result << " templateKind=\"specialization\"";
866  break;
868  Result << " templateKind=\"partialSpecialization\"";
869  break;
870  }
871  break;
873  RootEndTag = "</Variable>";
874  Result << "<Variable";
875  break;
877  RootEndTag = "</Namespace>";
878  Result << "<Namespace";
879  break;
881  RootEndTag = "</Typedef>";
882  Result << "<Typedef";
883  break;
884  case DeclInfo::EnumKind:
885  RootEndTag = "</Enum>";
886  Result << "<Enum";
887  break;
888  }
889 
890  {
891  // Print line and column number.
893  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
894  FileID FID = LocInfo.first;
895  unsigned FileOffset = LocInfo.second;
896 
897  if (FID.isValid()) {
898  if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
899  Result << " file=\"";
900  appendToResultWithXMLEscaping(FE->getName());
901  Result << "\"";
902  }
903  Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
904  << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
905  << "\"";
906  }
907  }
908 
909  // Finish the root tag.
910  Result << ">";
911 
912  bool FoundName = false;
913  if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
914  if (DeclarationName DeclName = ND->getDeclName()) {
915  Result << "<Name>";
916  std::string Name = DeclName.getAsString();
917  appendToResultWithXMLEscaping(Name);
918  FoundName = true;
919  Result << "</Name>";
920  }
921  }
922  if (!FoundName)
923  Result << "<Name>&lt;anonymous&gt;</Name>";
924 
925  {
926  // Print USR.
927  SmallString<128> USR;
929  if (!USR.empty()) {
930  Result << "<USR>";
931  appendToResultWithXMLEscaping(USR);
932  Result << "</USR>";
933  }
934  }
935  } else {
936  // No DeclInfo -- just emit some root tag and name tag.
937  RootEndTag = "</Other>";
938  Result << "<Other><Name>unknown</Name>";
939  }
940 
941  if (Parts.Headerfile) {
942  Result << "<Headerfile>";
943  visit(Parts.Headerfile);
944  Result << "</Headerfile>";
945  }
946 
947  {
948  // Pretty-print the declaration.
949  Result << "<Declaration>";
950  SmallString<128> Declaration;
951  getSourceTextOfDeclaration(DI, Declaration);
952  formatTextOfDeclaration(DI, Declaration);
953  appendToResultWithXMLEscaping(Declaration);
954  Result << "</Declaration>";
955  }
956 
957  bool FirstParagraphIsBrief = false;
958  if (Parts.Brief) {
959  Result << "<Abstract>";
960  visit(Parts.Brief);
961  Result << "</Abstract>";
962  } else if (Parts.FirstParagraph) {
963  Result << "<Abstract>";
964  visit(Parts.FirstParagraph);
965  Result << "</Abstract>";
966  FirstParagraphIsBrief = true;
967  }
968 
969  if (Parts.TParams.size() != 0) {
970  Result << "<TemplateParameters>";
971  for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
972  visit(Parts.TParams[i]);
973  Result << "</TemplateParameters>";
974  }
975 
976  if (Parts.Params.size() != 0) {
977  Result << "<Parameters>";
978  for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
979  visit(Parts.Params[i]);
980  Result << "</Parameters>";
981  }
982 
983  if (Parts.Exceptions.size() != 0) {
984  Result << "<Exceptions>";
985  for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
986  visit(Parts.Exceptions[i]);
987  Result << "</Exceptions>";
988  }
989 
990  if (Parts.Returns.size() != 0) {
991  Result << "<ResultDiscussion>";
992  for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
993  visit(Parts.Returns[i]);
994  Result << "</ResultDiscussion>";
995  }
996 
997  if (DI->CommentDecl->hasAttrs()) {
998  const AttrVec &Attrs = DI->CommentDecl->getAttrs();
999  for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1000  const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1001  if (!AA) {
1002  if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1003  if (DA->getMessage().empty())
1004  Result << "<Deprecated/>";
1005  else {
1006  Result << "<Deprecated>";
1007  appendToResultWithXMLEscaping(DA->getMessage());
1008  Result << "</Deprecated>";
1009  }
1010  }
1011  else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1012  if (UA->getMessage().empty())
1013  Result << "<Unavailable/>";
1014  else {
1015  Result << "<Unavailable>";
1016  appendToResultWithXMLEscaping(UA->getMessage());
1017  Result << "</Unavailable>";
1018  }
1019  }
1020  continue;
1021  }
1022 
1023  // 'availability' attribute.
1024  Result << "<Availability";
1025  StringRef Distribution;
1026  if (AA->getPlatform()) {
1027  Distribution = AvailabilityAttr::getPrettyPlatformName(
1028  AA->getPlatform()->getName());
1029  if (Distribution.empty())
1030  Distribution = AA->getPlatform()->getName();
1031  }
1032  Result << " distribution=\"" << Distribution << "\">";
1033  VersionTuple IntroducedInVersion = AA->getIntroduced();
1034  if (!IntroducedInVersion.empty()) {
1035  Result << "<IntroducedInVersion>"
1036  << IntroducedInVersion.getAsString()
1037  << "</IntroducedInVersion>";
1038  }
1039  VersionTuple DeprecatedInVersion = AA->getDeprecated();
1040  if (!DeprecatedInVersion.empty()) {
1041  Result << "<DeprecatedInVersion>"
1042  << DeprecatedInVersion.getAsString()
1043  << "</DeprecatedInVersion>";
1044  }
1045  VersionTuple RemovedAfterVersion = AA->getObsoleted();
1046  if (!RemovedAfterVersion.empty()) {
1047  Result << "<RemovedAfterVersion>"
1048  << RemovedAfterVersion.getAsString()
1049  << "</RemovedAfterVersion>";
1050  }
1051  StringRef DeprecationSummary = AA->getMessage();
1052  if (!DeprecationSummary.empty()) {
1053  Result << "<DeprecationSummary>";
1054  appendToResultWithXMLEscaping(DeprecationSummary);
1055  Result << "</DeprecationSummary>";
1056  }
1057  if (AA->getUnavailable())
1058  Result << "<Unavailable/>";
1059  Result << "</Availability>";
1060  }
1061  }
1062 
1063  {
1064  bool StartTagEmitted = false;
1065  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1066  const Comment *C = Parts.MiscBlocks[i];
1067  if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1068  continue;
1069  if (!StartTagEmitted) {
1070  Result << "<Discussion>";
1071  StartTagEmitted = true;
1072  }
1073  visit(C);
1074  }
1075  if (StartTagEmitted)
1076  Result << "</Discussion>";
1077  }
1078 
1079  Result << RootEndTag;
1080 }
1081 
1082 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1083  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1084  const char C = *I;
1085  switch (C) {
1086  case '&':
1087  Result << "&amp;";
1088  break;
1089  case '<':
1090  Result << "&lt;";
1091  break;
1092  case '>':
1093  Result << "&gt;";
1094  break;
1095  case '"':
1096  Result << "&quot;";
1097  break;
1098  case '\'':
1099  Result << "&apos;";
1100  break;
1101  default:
1102  Result << C;
1103  break;
1104  }
1105  }
1106 }
1107 
1108 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1109  if (S.empty())
1110  return;
1111 
1112  Result << "<![CDATA[";
1113  while (!S.empty()) {
1114  size_t Pos = S.find("]]>");
1115  if (Pos == 0) {
1116  Result << "]]]]><![CDATA[>";
1117  S = S.drop_front(3);
1118  continue;
1119  }
1120  if (Pos == StringRef::npos)
1121  Pos = S.size();
1122 
1123  Result << S.substr(0, Pos);
1124 
1125  S = S.drop_front(Pos);
1126  }
1127  Result << "]]>";
1128 }
1129 
1130 CommentToXMLConverter::CommentToXMLConverter() : FormatInMemoryUniqueId(0) {}
1132 
1134  SmallVectorImpl<char> &HTML,
1135  const ASTContext &Context) {
1136  CommentASTToHTMLConverter Converter(FC, HTML,
1137  Context.getCommentCommandTraits());
1138  Converter.visit(FC);
1139 }
1140 
1143  const ASTContext &Context) {
1144  CommentASTToHTMLConverter Converter(nullptr, Text,
1145  Context.getCommentCommandTraits());
1146  Converter.visit(HTC);
1147 }
1148 
1150  SmallVectorImpl<char> &XML,
1151  const ASTContext &Context) {
1152  if (!FormatContext || (FormatInMemoryUniqueId % 1000) == 0) {
1153  // Create a new format context, or re-create it after some number of
1154  // iterations, so the buffers don't grow too large.
1155  FormatContext.reset(new SimpleFormatContext(Context.getLangOpts()));
1156  }
1157 
1158  CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1159  Context.getSourceManager(), *FormatContext,
1160  FormatInMemoryUniqueId++);
1161  Converter.visit(FC);
1162 }
1163 
Defines the clang::ASTContext interface.
bool isVarArgParam() const LLVM_READONLY
Definition: Comment.h:782
child_iterator child_begin() const
Definition: Comment.h:1117
const Decl * CommentDecl
Declaration the comment is actually attached to (in the source).
Definition: Comment.h:989
unsigned Length
Represents a version number in the form major[.minor[.subminor[.build]]].
Definition: VersionTuple.h:26
bool isPositionValid() const LLVM_READONLY
Definition: Comment.h:848
Defines a utility class for use of clang-format in libclang.
std::set< Replacement > Replacements
A set of Replacements.
Definition: Replacement.h:141
Something that we consider a "variable":
Definition: Comment.h:1042
Something that we consider a "function":
Definition: Comment.h:1030
Describes how types, statements, expressions, and declarations should be printed. ...
Definition: PrettyPrinter.h:35
A small class to be used by libclang clients to format a declaration string in memory.
Information about a single command.
const CommandInfo * getCommandInfo(StringRef Name) const
bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite)
Apply all replacements in Replaces to the Rewriter Rewrite.
ParagraphComment * getParagraph() const LLVM_READONLY
Definition: Comment.h:695
comments::CommandTraits & getCommentCommandTraits() const
Definition: ASTContext.h:760
StringRef getParamNameAsWritten() const
Definition: Comment.h:770
class LLVM_ALIGNAS(8) DependentTemplateSpecializationType const IdentifierInfo * Name
Represents a template specialization type whose template cannot be resolved, e.g. ...
Definition: Type.h:4381
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:91
StringRef getParamNameAsWritten() const
Definition: Comment.h:840
const Decl * CurrentDecl
CurrentDecl is the declaration with which the FullComment is associated.
Definition: Comment.h:999
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:48
A command with word-like arguments that is considered inline content.
Definition: Comment.h:303
child_iterator child_end() const
Definition: Comment.h:1121
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
A line of text contained in a verbatim block.
Definition: Comment.h:869
std::string getAsString() const
Retrieve a string representation of the version number.
const LangOptions & getLangOpts() const
Definition: ASTContext.h:596
A verbatim line command.
Definition: Comment.h:949
Something that we consider a "class":
Definition: Comment.h:1036
Any part of the comment.
Definition: Comment.h:53
unsigned getParamIndex() const LLVM_READONLY
Definition: Comment.h:791
#define UINT_MAX
Definition: limits.h:72
bool hasNonWhitespaceParagraph() const
Definition: Comment.h:699
child_iterator child_end() const
Definition: Comment.h:582
unsigned getCommandID() const
Definition: Comment.h:656
detail::InMemoryDirectory::const_iterator I
void convertCommentToHTML(const comments::FullComment *FC, SmallVectorImpl< char > &HTML, const ASTContext &Context)
A verbatim block command (e.
Definition: Comment.h:897
StringRef getText() const LLVM_READONLY
Definition: Comment.h:287
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
unsigned TemplateKind
Is CommentDecl a template declaration.
Definition: Comment.h:1070
ASTContext * Context
FormatStyle getLLVMStyle()
Returns a format style complying with the LLVM coding standards: http://llvm.org/docs/CodingStandards...
Definition: Format.cpp:464
ID
Defines the set of possible language-specific address spaces.
Definition: AddressSpaces.h:27
SourceManager & SM
unsigned IsBriefCommand
True if this command is introducing a brief documentation paragraph (\brief or an alias)...
A command that has zero or more word-like arguments (number of word-like arguments depends on command...
Definition: Comment.h:602
bool isDirectionExplicit() const LLVM_READONLY
Definition: Comment.h:755
StringRef getParamName(const FullComment *FC) const
Definition: Comment.cpp:331
An enumeration or scoped enumeration.
Definition: Comment.h:1052
An opening HTML tag with attributes.
Definition: Comment.h:419
unsigned getColumnNumber(FileID FID, unsigned FilePos, bool *Invalid=nullptr) const
Return the column # for the specified file position.
AttrVec & getAttrs()
Definition: DeclBase.h:443
static CharSourceRange getCharRange(SourceRange R)
This class provides information about commands that can be used in comments.
Kind
RenderKind getRenderKind() const
Definition: Comment.h:358
Encodes a location in the source.
const TemplateArgument * iterator
Definition: Type.h:4070
CommentKind getCommentKind() const
Definition: Comment.h:204
Various functions to configurably format source code.
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:311
void convertHTMLTagNodeToText(const comments::HTMLTagComment *HTC, SmallVectorImpl< char > &Text, const ASTContext &Context)
bool isParamIndexValid() const LLVM_READONLY
Definition: Comment.h:778
Cached information about one file (either on disk or in the virtual file system). ...
Definition: FileManager.h:53
const Attribute & getAttr(unsigned Idx) const
Definition: Comment.h:482
Block content (contains inline content).
Definition: Comment.h:536
unsigned getIndex(unsigned Depth) const
Definition: Comment.h:857
Comment *const * child_iterator
Definition: Comment.h:228
PassDirection getDirection() const LLVM_READONLY
Definition: Comment.h:751
A closing HTML tag.
Definition: Comment.h:513
Everything else not explicitly mentioned below.
Definition: Comment.h:1018
Doxygen \tparam command, describes a template parameter.
Definition: Comment.h:805
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool empty() const
Determine whether this version information is empty (e.g., all version components are zero)...
Definition: VersionTuple.h:64
DeclarationName - The name of a declaration.
unsigned getLineNumber(FileID FID, unsigned FilePos, bool *Invalid=nullptr) const
Given a SourceLocation, return the spelling line number for the position indicated.
bool hasAttrs() const
Definition: DeclBase.h:439
Information about the declaration, useful to clients of FullComment.
Definition: Comment.h:986
detail::InMemoryDirectory::const_iterator E
A single paragraph that contains inline content.
Definition: Comment.h:552
DeclKind getKind() const LLVM_READONLY
Definition: Comment.h:1087
unsigned IsInstanceMethod
Is CommentDecl a non-static member function of C++ class or instance method of ObjC class...
Definition: Comment.h:1078
StringRef getTagName() const LLVM_READONLY
Definition: Comment.h:401
void print(raw_ostream &Out, unsigned Indentation=0, bool PrintInstantiation=false) const
A C++ typedef-name (a 'typedef' decl specifier or alias-declaration), see TypedefNameDecl.
Definition: Comment.h:1049
StringRef getParamName(const FullComment *FC) const
Definition: Comment.cpp:324
child_iterator child_begin() const
Definition: Comment.h:578
unsigned IsReturnsCommand
True if this command is \returns or an alias.
SourceManager & getSourceManager()
Definition: ASTContext.h:553
StringRef getText(unsigned LineIdx) const
Definition: Comment.h:941
Abstract class for opening and closing HTML tags.
Definition: Comment.h:377
StringRef getArgText(unsigned Idx) const
Definition: Comment.h:366
unsigned IsClassMethod
Is CommentDecl a static member function of C++ class or class method of ObjC class.
Definition: Comment.h:1083
Doxygen \param command.
Definition: Comment.h:717
const DeclInfo * getDeclInfo() const LLVM_READONLY
Definition: Comment.h:1129
tooling::Replacements reformat(const FormatStyle &Style, SourceManager &SourceMgr, FileID ID, ArrayRef< CharSourceRange > Ranges, bool *IncompleteFormat=nullptr)
Reformats the given Ranges in the file ID.
Definition: Format.cpp:1883
StringRef Text
Definition: Format.cpp:1724
bool generateUSRForDecl(const Decl *D, SmallVectorImpl< char > &Buf)
Generate a USR for a Decl, including the USR prefix.
SourceLocation getLocation() const
Definition: DeclBase.h:384
NamedDecl - This represents a decl with a name.
Definition: Decl.h:145
bool isValid() const
std::pair< FileID, unsigned > getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
void convertCommentToXML(const comments::FullComment *FC, SmallVectorImpl< char > &XML, const ASTContext &Context)
This class handles loading and caching of source files into memory.
StringRef getCommandName(const CommandTraits &Traits) const
Definition: Comment.h:660
Attr - This represents one attribute.
Definition: Attr.h:44
A full comment attached to a declaration, contains block content.
Definition: Comment.h:1097