clang  3.9.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) {
583  ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
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  // Formatting API expects null terminated input string.
596  StringRef StringDecl(Declaration.c_str(), Declaration.size());
597 
598  // Formatter specific code.
599  // Form a unique in memory buffer name.
600  SmallString<128> filename;
601  filename += "xmldecl";
602  filename += llvm::utostr(FormatInMemoryUniqueId);
603  filename += ".xd";
604  FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
605  SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID)
606  .getLocWithOffset(0);
607  unsigned Length = Declaration.size();
608 
610  format::getLLVMStyle(), FormatRewriterContext.Sources, ID,
611  CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
612  applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
613  Declaration = FormatRewriterContext.getRewrittenText(ID);
614 }
615 
616 } // end unnamed namespace
617 
618 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
619  appendToResultWithXMLEscaping(C->getText());
620 }
621 
622 void CommentASTToXMLConverter::visitInlineCommandComment(
623  const InlineCommandComment *C) {
624  // Nothing to render if no arguments supplied.
625  if (C->getNumArgs() == 0)
626  return;
627 
628  // Nothing to render if argument is empty.
629  StringRef Arg0 = C->getArgText(0);
630  if (Arg0.empty())
631  return;
632 
633  switch (C->getRenderKind()) {
635  for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
636  appendToResultWithXMLEscaping(C->getArgText(i));
637  Result << " ";
638  }
639  return;
641  assert(C->getNumArgs() == 1);
642  Result << "<bold>";
643  appendToResultWithXMLEscaping(Arg0);
644  Result << "</bold>";
645  return;
647  assert(C->getNumArgs() == 1);
648  Result << "<monospaced>";
649  appendToResultWithXMLEscaping(Arg0);
650  Result << "</monospaced>";
651  return;
653  assert(C->getNumArgs() == 1);
654  Result << "<emphasized>";
655  appendToResultWithXMLEscaping(Arg0);
656  Result << "</emphasized>";
657  return;
658  }
659 }
660 
661 void CommentASTToXMLConverter::visitHTMLStartTagComment(
662  const HTMLStartTagComment *C) {
663  Result << "<rawHTML";
664  if (C->isMalformed())
665  Result << " isMalformed=\"1\"";
666  Result << ">";
667  {
668  SmallString<32> Tag;
669  {
670  llvm::raw_svector_ostream TagOS(Tag);
671  printHTMLStartTagComment(C, TagOS);
672  }
673  appendToResultWithCDATAEscaping(Tag);
674  }
675  Result << "</rawHTML>";
676 }
677 
678 void
679 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
680  Result << "<rawHTML";
681  if (C->isMalformed())
682  Result << " isMalformed=\"1\"";
683  Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
684 }
685 
686 void
687 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
688  appendParagraphCommentWithKind(C, StringRef());
689 }
690 
691 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
692  const ParagraphComment *C,
693  StringRef ParagraphKind) {
694  if (C->isWhitespace())
695  return;
696 
697  if (ParagraphKind.empty())
698  Result << "<Para>";
699  else
700  Result << "<Para kind=\"" << ParagraphKind << "\">";
701 
702  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
703  I != E; ++I) {
704  visit(*I);
705  }
706  Result << "</Para>";
707 }
708 
709 void CommentASTToXMLConverter::visitBlockCommandComment(
710  const BlockCommandComment *C) {
711  StringRef ParagraphKind;
712 
713  switch (C->getCommandID()) {
714  case CommandTraits::KCI_attention:
715  case CommandTraits::KCI_author:
716  case CommandTraits::KCI_authors:
717  case CommandTraits::KCI_bug:
718  case CommandTraits::KCI_copyright:
719  case CommandTraits::KCI_date:
720  case CommandTraits::KCI_invariant:
721  case CommandTraits::KCI_note:
722  case CommandTraits::KCI_post:
723  case CommandTraits::KCI_pre:
724  case CommandTraits::KCI_remark:
725  case CommandTraits::KCI_remarks:
726  case CommandTraits::KCI_sa:
727  case CommandTraits::KCI_see:
728  case CommandTraits::KCI_since:
729  case CommandTraits::KCI_todo:
730  case CommandTraits::KCI_version:
731  case CommandTraits::KCI_warning:
732  ParagraphKind = C->getCommandName(Traits);
733  default:
734  break;
735  }
736 
737  appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
738 }
739 
740 void CommentASTToXMLConverter::visitParamCommandComment(
741  const ParamCommandComment *C) {
742  Result << "<Parameter><Name>";
743  appendToResultWithXMLEscaping(C->isParamIndexValid()
744  ? C->getParamName(FC)
745  : C->getParamNameAsWritten());
746  Result << "</Name>";
747 
748  if (C->isParamIndexValid()) {
749  if (C->isVarArgParam())
750  Result << "<IsVarArg />";
751  else
752  Result << "<Index>" << C->getParamIndex() << "</Index>";
753  }
754 
755  Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
756  switch (C->getDirection()) {
758  Result << "in";
759  break;
761  Result << "out";
762  break;
764  Result << "in,out";
765  break;
766  }
767  Result << "</Direction><Discussion>";
768  visit(C->getParagraph());
769  Result << "</Discussion></Parameter>";
770 }
771 
772 void CommentASTToXMLConverter::visitTParamCommandComment(
773  const TParamCommandComment *C) {
774  Result << "<Parameter><Name>";
775  appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
776  : C->getParamNameAsWritten());
777  Result << "</Name>";
778 
779  if (C->isPositionValid() && C->getDepth() == 1) {
780  Result << "<Index>" << C->getIndex(0) << "</Index>";
781  }
782 
783  Result << "<Discussion>";
784  visit(C->getParagraph());
785  Result << "</Discussion></Parameter>";
786 }
787 
788 void CommentASTToXMLConverter::visitVerbatimBlockComment(
789  const VerbatimBlockComment *C) {
790  unsigned NumLines = C->getNumLines();
791  if (NumLines == 0)
792  return;
793 
794  switch (C->getCommandID()) {
795  case CommandTraits::KCI_code:
796  Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
797  break;
798  default:
799  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
800  break;
801  }
802  for (unsigned i = 0; i != NumLines; ++i) {
803  appendToResultWithXMLEscaping(C->getText(i));
804  if (i + 1 != NumLines)
805  Result << '\n';
806  }
807  Result << "</Verbatim>";
808 }
809 
810 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
811  const VerbatimBlockLineComment *C) {
812  llvm_unreachable("should not see this AST node");
813 }
814 
815 void CommentASTToXMLConverter::visitVerbatimLineComment(
816  const VerbatimLineComment *C) {
817  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
818  appendToResultWithXMLEscaping(C->getText());
819  Result << "</Verbatim>";
820 }
821 
822 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
823  FullCommentParts Parts(C, Traits);
824 
825  const DeclInfo *DI = C->getDeclInfo();
826  StringRef RootEndTag;
827  if (DI) {
828  switch (DI->getKind()) {
829  case DeclInfo::OtherKind:
830  RootEndTag = "</Other>";
831  Result << "<Other";
832  break;
834  RootEndTag = "</Function>";
835  Result << "<Function";
836  switch (DI->TemplateKind) {
838  break;
839  case DeclInfo::Template:
840  Result << " templateKind=\"template\"";
841  break;
843  Result << " templateKind=\"specialization\"";
844  break;
846  llvm_unreachable("partial specializations of functions "
847  "are not allowed in C++");
848  }
849  if (DI->IsInstanceMethod)
850  Result << " isInstanceMethod=\"1\"";
851  if (DI->IsClassMethod)
852  Result << " isClassMethod=\"1\"";
853  break;
854  case DeclInfo::ClassKind:
855  RootEndTag = "</Class>";
856  Result << "<Class";
857  switch (DI->TemplateKind) {
859  break;
860  case DeclInfo::Template:
861  Result << " templateKind=\"template\"";
862  break;
864  Result << " templateKind=\"specialization\"";
865  break;
867  Result << " templateKind=\"partialSpecialization\"";
868  break;
869  }
870  break;
872  RootEndTag = "</Variable>";
873  Result << "<Variable";
874  break;
876  RootEndTag = "</Namespace>";
877  Result << "<Namespace";
878  break;
880  RootEndTag = "</Typedef>";
881  Result << "<Typedef";
882  break;
883  case DeclInfo::EnumKind:
884  RootEndTag = "</Enum>";
885  Result << "<Enum";
886  break;
887  }
888 
889  {
890  // Print line and column number.
891  SourceLocation Loc = DI->CurrentDecl->getLocation();
892  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
893  FileID FID = LocInfo.first;
894  unsigned FileOffset = LocInfo.second;
895 
896  if (FID.isValid()) {
897  if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
898  Result << " file=\"";
899  appendToResultWithXMLEscaping(FE->getName());
900  Result << "\"";
901  }
902  Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
903  << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
904  << "\"";
905  }
906  }
907 
908  // Finish the root tag.
909  Result << ">";
910 
911  bool FoundName = false;
912  if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
913  if (DeclarationName DeclName = ND->getDeclName()) {
914  Result << "<Name>";
915  std::string Name = DeclName.getAsString();
916  appendToResultWithXMLEscaping(Name);
917  FoundName = true;
918  Result << "</Name>";
919  }
920  }
921  if (!FoundName)
922  Result << "<Name>&lt;anonymous&gt;</Name>";
923 
924  {
925  // Print USR.
926  SmallString<128> USR;
928  if (!USR.empty()) {
929  Result << "<USR>";
930  appendToResultWithXMLEscaping(USR);
931  Result << "</USR>";
932  }
933  }
934  } else {
935  // No DeclInfo -- just emit some root tag and name tag.
936  RootEndTag = "</Other>";
937  Result << "<Other><Name>unknown</Name>";
938  }
939 
940  if (Parts.Headerfile) {
941  Result << "<Headerfile>";
942  visit(Parts.Headerfile);
943  Result << "</Headerfile>";
944  }
945 
946  {
947  // Pretty-print the declaration.
948  Result << "<Declaration>";
949  SmallString<128> Declaration;
950  getSourceTextOfDeclaration(DI, Declaration);
951  formatTextOfDeclaration(DI, Declaration);
952  appendToResultWithXMLEscaping(Declaration);
953  Result << "</Declaration>";
954  }
955 
956  bool FirstParagraphIsBrief = false;
957  if (Parts.Brief) {
958  Result << "<Abstract>";
959  visit(Parts.Brief);
960  Result << "</Abstract>";
961  } else if (Parts.FirstParagraph) {
962  Result << "<Abstract>";
963  visit(Parts.FirstParagraph);
964  Result << "</Abstract>";
965  FirstParagraphIsBrief = true;
966  }
967 
968  if (Parts.TParams.size() != 0) {
969  Result << "<TemplateParameters>";
970  for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
971  visit(Parts.TParams[i]);
972  Result << "</TemplateParameters>";
973  }
974 
975  if (Parts.Params.size() != 0) {
976  Result << "<Parameters>";
977  for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
978  visit(Parts.Params[i]);
979  Result << "</Parameters>";
980  }
981 
982  if (Parts.Exceptions.size() != 0) {
983  Result << "<Exceptions>";
984  for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
985  visit(Parts.Exceptions[i]);
986  Result << "</Exceptions>";
987  }
988 
989  if (Parts.Returns.size() != 0) {
990  Result << "<ResultDiscussion>";
991  for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
992  visit(Parts.Returns[i]);
993  Result << "</ResultDiscussion>";
994  }
995 
996  if (DI->CommentDecl->hasAttrs()) {
997  const AttrVec &Attrs = DI->CommentDecl->getAttrs();
998  for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
999  const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1000  if (!AA) {
1001  if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1002  if (DA->getMessage().empty())
1003  Result << "<Deprecated/>";
1004  else {
1005  Result << "<Deprecated>";
1006  appendToResultWithXMLEscaping(DA->getMessage());
1007  Result << "</Deprecated>";
1008  }
1009  }
1010  else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1011  if (UA->getMessage().empty())
1012  Result << "<Unavailable/>";
1013  else {
1014  Result << "<Unavailable>";
1015  appendToResultWithXMLEscaping(UA->getMessage());
1016  Result << "</Unavailable>";
1017  }
1018  }
1019  continue;
1020  }
1021 
1022  // 'availability' attribute.
1023  Result << "<Availability";
1024  StringRef Distribution;
1025  if (AA->getPlatform()) {
1026  Distribution = AvailabilityAttr::getPrettyPlatformName(
1027  AA->getPlatform()->getName());
1028  if (Distribution.empty())
1029  Distribution = AA->getPlatform()->getName();
1030  }
1031  Result << " distribution=\"" << Distribution << "\">";
1032  VersionTuple IntroducedInVersion = AA->getIntroduced();
1033  if (!IntroducedInVersion.empty()) {
1034  Result << "<IntroducedInVersion>"
1035  << IntroducedInVersion.getAsString()
1036  << "</IntroducedInVersion>";
1037  }
1038  VersionTuple DeprecatedInVersion = AA->getDeprecated();
1039  if (!DeprecatedInVersion.empty()) {
1040  Result << "<DeprecatedInVersion>"
1041  << DeprecatedInVersion.getAsString()
1042  << "</DeprecatedInVersion>";
1043  }
1044  VersionTuple RemovedAfterVersion = AA->getObsoleted();
1045  if (!RemovedAfterVersion.empty()) {
1046  Result << "<RemovedAfterVersion>"
1047  << RemovedAfterVersion.getAsString()
1048  << "</RemovedAfterVersion>";
1049  }
1050  StringRef DeprecationSummary = AA->getMessage();
1051  if (!DeprecationSummary.empty()) {
1052  Result << "<DeprecationSummary>";
1053  appendToResultWithXMLEscaping(DeprecationSummary);
1054  Result << "</DeprecationSummary>";
1055  }
1056  if (AA->getUnavailable())
1057  Result << "<Unavailable/>";
1058  Result << "</Availability>";
1059  }
1060  }
1061 
1062  {
1063  bool StartTagEmitted = false;
1064  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1065  const Comment *C = Parts.MiscBlocks[i];
1066  if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1067  continue;
1068  if (!StartTagEmitted) {
1069  Result << "<Discussion>";
1070  StartTagEmitted = true;
1071  }
1072  visit(C);
1073  }
1074  if (StartTagEmitted)
1075  Result << "</Discussion>";
1076  }
1077 
1078  Result << RootEndTag;
1079 }
1080 
1081 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1082  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1083  const char C = *I;
1084  switch (C) {
1085  case '&':
1086  Result << "&amp;";
1087  break;
1088  case '<':
1089  Result << "&lt;";
1090  break;
1091  case '>':
1092  Result << "&gt;";
1093  break;
1094  case '"':
1095  Result << "&quot;";
1096  break;
1097  case '\'':
1098  Result << "&apos;";
1099  break;
1100  default:
1101  Result << C;
1102  break;
1103  }
1104  }
1105 }
1106 
1107 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1108  if (S.empty())
1109  return;
1110 
1111  Result << "<![CDATA[";
1112  while (!S.empty()) {
1113  size_t Pos = S.find("]]>");
1114  if (Pos == 0) {
1115  Result << "]]]]><![CDATA[>";
1116  S = S.drop_front(3);
1117  continue;
1118  }
1119  if (Pos == StringRef::npos)
1120  Pos = S.size();
1121 
1122  Result << S.substr(0, Pos);
1123 
1124  S = S.drop_front(Pos);
1125  }
1126  Result << "]]>";
1127 }
1128 
1129 CommentToXMLConverter::CommentToXMLConverter() : FormatInMemoryUniqueId(0) {}
1131 
1133  SmallVectorImpl<char> &HTML,
1134  const ASTContext &Context) {
1135  CommentASTToHTMLConverter Converter(FC, HTML,
1136  Context.getCommentCommandTraits());
1137  Converter.visit(FC);
1138 }
1139 
1142  const ASTContext &Context) {
1143  CommentASTToHTMLConverter Converter(nullptr, Text,
1144  Context.getCommentCommandTraits());
1145  Converter.visit(HTC);
1146 }
1147 
1149  SmallVectorImpl<char> &XML,
1150  const ASTContext &Context) {
1151  if (!FormatContext || (FormatInMemoryUniqueId % 1000) == 0) {
1152  // Create a new format context, or re-create it after some number of
1153  // iterations, so the buffers don't grow too large.
1154  FormatContext.reset(new SimpleFormatContext(Context.getLangOpts()));
1155  }
1156 
1157  CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1158  Context.getSourceManager(), *FormatContext,
1159  FormatInMemoryUniqueId++);
1160  Converter.visit(FC);
1161 }
1162 
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:148
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:38
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:768
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:4549
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:92
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:604
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:487
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.
const SourceManager & SM
Definition: Format.cpp:1184
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:4233
CommentKind getCommentKind() const
Definition: Comment.h:204
Various functions to configurably format source code.
const std::string ID
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:69
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.
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
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:561
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:1614
StringRef Text
Definition: Format.cpp:1195
bool generateUSRForDecl(const Decl *D, SmallVectorImpl< char > &Buf)
Generate a USR for a Decl, including the USR prefix.
NamedDecl - This represents a decl with a name.
Definition: Decl.h:213
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:45
A full comment attached to a declaration, contains block content.
Definition: Comment.h:1097