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