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