clang  10.0.0git
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;
301  assert(C->getNumArgs() == 1);
302  Result << "<span id=\"" << Arg0 << "\"></span>";
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,
562  SmallString<128> &Declaration);
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  case InlineCommandComment::RenderAnchor:
649  assert(C->getNumArgs() == 1);
650  Result << "<anchor id=\"" << Arg0 << "\"></anchor>";
651  return;
652  }
653 }
654 
655 void CommentASTToXMLConverter::visitHTMLStartTagComment(
656  const HTMLStartTagComment *C) {
657  Result << "<rawHTML";
658  if (C->isMalformed())
659  Result << " isMalformed=\"1\"";
660  Result << ">";
661  {
662  SmallString<32> Tag;
663  {
664  llvm::raw_svector_ostream TagOS(Tag);
665  printHTMLStartTagComment(C, TagOS);
666  }
667  appendToResultWithCDATAEscaping(Tag);
668  }
669  Result << "</rawHTML>";
670 }
671 
672 void
673 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
674  Result << "<rawHTML";
675  if (C->isMalformed())
676  Result << " isMalformed=\"1\"";
677  Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
678 }
679 
680 void
681 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
682  appendParagraphCommentWithKind(C, StringRef());
683 }
684 
685 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
686  const ParagraphComment *C,
687  StringRef ParagraphKind) {
688  if (C->isWhitespace())
689  return;
690 
691  if (ParagraphKind.empty())
692  Result << "<Para>";
693  else
694  Result << "<Para kind=\"" << ParagraphKind << "\">";
695 
696  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
697  I != E; ++I) {
698  visit(*I);
699  }
700  Result << "</Para>";
701 }
702 
703 void CommentASTToXMLConverter::visitBlockCommandComment(
704  const BlockCommandComment *C) {
705  StringRef ParagraphKind;
706 
707  switch (C->getCommandID()) {
708  case CommandTraits::KCI_attention:
709  case CommandTraits::KCI_author:
710  case CommandTraits::KCI_authors:
711  case CommandTraits::KCI_bug:
712  case CommandTraits::KCI_copyright:
713  case CommandTraits::KCI_date:
714  case CommandTraits::KCI_invariant:
715  case CommandTraits::KCI_note:
716  case CommandTraits::KCI_post:
717  case CommandTraits::KCI_pre:
718  case CommandTraits::KCI_remark:
719  case CommandTraits::KCI_remarks:
720  case CommandTraits::KCI_sa:
721  case CommandTraits::KCI_see:
722  case CommandTraits::KCI_since:
723  case CommandTraits::KCI_todo:
724  case CommandTraits::KCI_version:
725  case CommandTraits::KCI_warning:
726  ParagraphKind = C->getCommandName(Traits);
727  break;
728  default:
729  break;
730  }
731 
732  appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
733 }
734 
735 void CommentASTToXMLConverter::visitParamCommandComment(
736  const ParamCommandComment *C) {
737  Result << "<Parameter><Name>";
738  appendToResultWithXMLEscaping(C->isParamIndexValid()
739  ? C->getParamName(FC)
740  : C->getParamNameAsWritten());
741  Result << "</Name>";
742 
743  if (C->isParamIndexValid()) {
744  if (C->isVarArgParam())
745  Result << "<IsVarArg />";
746  else
747  Result << "<Index>" << C->getParamIndex() << "</Index>";
748  }
749 
750  Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
751  switch (C->getDirection()) {
752  case ParamCommandComment::In:
753  Result << "in";
754  break;
755  case ParamCommandComment::Out:
756  Result << "out";
757  break;
758  case ParamCommandComment::InOut:
759  Result << "in,out";
760  break;
761  }
762  Result << "</Direction><Discussion>";
763  visit(C->getParagraph());
764  Result << "</Discussion></Parameter>";
765 }
766 
767 void CommentASTToXMLConverter::visitTParamCommandComment(
768  const TParamCommandComment *C) {
769  Result << "<Parameter><Name>";
770  appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
771  : C->getParamNameAsWritten());
772  Result << "</Name>";
773 
774  if (C->isPositionValid() && C->getDepth() == 1) {
775  Result << "<Index>" << C->getIndex(0) << "</Index>";
776  }
777 
778  Result << "<Discussion>";
779  visit(C->getParagraph());
780  Result << "</Discussion></Parameter>";
781 }
782 
783 void CommentASTToXMLConverter::visitVerbatimBlockComment(
784  const VerbatimBlockComment *C) {
785  unsigned NumLines = C->getNumLines();
786  if (NumLines == 0)
787  return;
788 
789  switch (C->getCommandID()) {
790  case CommandTraits::KCI_code:
791  Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
792  break;
793  default:
794  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
795  break;
796  }
797  for (unsigned i = 0; i != NumLines; ++i) {
798  appendToResultWithXMLEscaping(C->getText(i));
799  if (i + 1 != NumLines)
800  Result << '\n';
801  }
802  Result << "</Verbatim>";
803 }
804 
805 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
806  const VerbatimBlockLineComment *C) {
807  llvm_unreachable("should not see this AST node");
808 }
809 
810 void CommentASTToXMLConverter::visitVerbatimLineComment(
811  const VerbatimLineComment *C) {
812  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
813  appendToResultWithXMLEscaping(C->getText());
814  Result << "</Verbatim>";
815 }
816 
817 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
818  FullCommentParts Parts(C, Traits);
819 
820  const DeclInfo *DI = C->getDeclInfo();
821  StringRef RootEndTag;
822  if (DI) {
823  switch (DI->getKind()) {
824  case DeclInfo::OtherKind:
825  RootEndTag = "</Other>";
826  Result << "<Other";
827  break;
828  case DeclInfo::FunctionKind:
829  RootEndTag = "</Function>";
830  Result << "<Function";
831  switch (DI->TemplateKind) {
832  case DeclInfo::NotTemplate:
833  break;
834  case DeclInfo::Template:
835  Result << " templateKind=\"template\"";
836  break;
837  case DeclInfo::TemplateSpecialization:
838  Result << " templateKind=\"specialization\"";
839  break;
840  case DeclInfo::TemplatePartialSpecialization:
841  llvm_unreachable("partial specializations of functions "
842  "are not allowed in C++");
843  }
844  if (DI->IsInstanceMethod)
845  Result << " isInstanceMethod=\"1\"";
846  if (DI->IsClassMethod)
847  Result << " isClassMethod=\"1\"";
848  break;
849  case DeclInfo::ClassKind:
850  RootEndTag = "</Class>";
851  Result << "<Class";
852  switch (DI->TemplateKind) {
853  case DeclInfo::NotTemplate:
854  break;
855  case DeclInfo::Template:
856  Result << " templateKind=\"template\"";
857  break;
858  case DeclInfo::TemplateSpecialization:
859  Result << " templateKind=\"specialization\"";
860  break;
861  case DeclInfo::TemplatePartialSpecialization:
862  Result << " templateKind=\"partialSpecialization\"";
863  break;
864  }
865  break;
866  case DeclInfo::VariableKind:
867  RootEndTag = "</Variable>";
868  Result << "<Variable";
869  break;
870  case DeclInfo::NamespaceKind:
871  RootEndTag = "</Namespace>";
872  Result << "<Namespace";
873  break;
874  case DeclInfo::TypedefKind:
875  RootEndTag = "</Typedef>";
876  Result << "<Typedef";
877  break;
878  case DeclInfo::EnumKind:
879  RootEndTag = "</Enum>";
880  Result << "<Enum";
881  break;
882  }
883 
884  {
885  // Print line and column number.
887  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
888  FileID FID = LocInfo.first;
889  unsigned FileOffset = LocInfo.second;
890 
891  if (FID.isValid()) {
892  if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
893  Result << " file=\"";
894  appendToResultWithXMLEscaping(FE->getName());
895  Result << "\"";
896  }
897  Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
898  << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
899  << "\"";
900  }
901  }
902 
903  // Finish the root tag.
904  Result << ">";
905 
906  bool FoundName = false;
907  if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
908  if (DeclarationName DeclName = ND->getDeclName()) {
909  Result << "<Name>";
910  std::string Name = DeclName.getAsString();
911  appendToResultWithXMLEscaping(Name);
912  FoundName = true;
913  Result << "</Name>";
914  }
915  }
916  if (!FoundName)
917  Result << "<Name>&lt;anonymous&gt;</Name>";
918 
919  {
920  // Print USR.
921  SmallString<128> USR;
923  if (!USR.empty()) {
924  Result << "<USR>";
925  appendToResultWithXMLEscaping(USR);
926  Result << "</USR>";
927  }
928  }
929  } else {
930  // No DeclInfo -- just emit some root tag and name tag.
931  RootEndTag = "</Other>";
932  Result << "<Other><Name>unknown</Name>";
933  }
934 
935  if (Parts.Headerfile) {
936  Result << "<Headerfile>";
937  visit(Parts.Headerfile);
938  Result << "</Headerfile>";
939  }
940 
941  {
942  // Pretty-print the declaration.
943  Result << "<Declaration>";
944  SmallString<128> Declaration;
945  getSourceTextOfDeclaration(DI, Declaration);
946  formatTextOfDeclaration(DI, Declaration);
947  appendToResultWithXMLEscaping(Declaration);
948  Result << "</Declaration>";
949  }
950 
951  bool FirstParagraphIsBrief = false;
952  if (Parts.Brief) {
953  Result << "<Abstract>";
954  visit(Parts.Brief);
955  Result << "</Abstract>";
956  } else if (Parts.FirstParagraph) {
957  Result << "<Abstract>";
958  visit(Parts.FirstParagraph);
959  Result << "</Abstract>";
960  FirstParagraphIsBrief = true;
961  }
962 
963  if (Parts.TParams.size() != 0) {
964  Result << "<TemplateParameters>";
965  for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
966  visit(Parts.TParams[i]);
967  Result << "</TemplateParameters>";
968  }
969 
970  if (Parts.Params.size() != 0) {
971  Result << "<Parameters>";
972  for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
973  visit(Parts.Params[i]);
974  Result << "</Parameters>";
975  }
976 
977  if (Parts.Exceptions.size() != 0) {
978  Result << "<Exceptions>";
979  for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
980  visit(Parts.Exceptions[i]);
981  Result << "</Exceptions>";
982  }
983 
984  if (Parts.Returns.size() != 0) {
985  Result << "<ResultDiscussion>";
986  for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
987  visit(Parts.Returns[i]);
988  Result << "</ResultDiscussion>";
989  }
990 
991  if (DI->CommentDecl->hasAttrs()) {
992  const AttrVec &Attrs = DI->CommentDecl->getAttrs();
993  for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
994  const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
995  if (!AA) {
996  if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
997  if (DA->getMessage().empty())
998  Result << "<Deprecated/>";
999  else {
1000  Result << "<Deprecated>";
1001  appendToResultWithXMLEscaping(DA->getMessage());
1002  Result << "</Deprecated>";
1003  }
1004  }
1005  else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1006  if (UA->getMessage().empty())
1007  Result << "<Unavailable/>";
1008  else {
1009  Result << "<Unavailable>";
1010  appendToResultWithXMLEscaping(UA->getMessage());
1011  Result << "</Unavailable>";
1012  }
1013  }
1014  continue;
1015  }
1016 
1017  // 'availability' attribute.
1018  Result << "<Availability";
1019  StringRef Distribution;
1020  if (AA->getPlatform()) {
1021  Distribution = AvailabilityAttr::getPrettyPlatformName(
1022  AA->getPlatform()->getName());
1023  if (Distribution.empty())
1024  Distribution = AA->getPlatform()->getName();
1025  }
1026  Result << " distribution=\"" << Distribution << "\">";
1027  VersionTuple IntroducedInVersion = AA->getIntroduced();
1028  if (!IntroducedInVersion.empty()) {
1029  Result << "<IntroducedInVersion>"
1030  << IntroducedInVersion.getAsString()
1031  << "</IntroducedInVersion>";
1032  }
1033  VersionTuple DeprecatedInVersion = AA->getDeprecated();
1034  if (!DeprecatedInVersion.empty()) {
1035  Result << "<DeprecatedInVersion>"
1036  << DeprecatedInVersion.getAsString()
1037  << "</DeprecatedInVersion>";
1038  }
1039  VersionTuple RemovedAfterVersion = AA->getObsoleted();
1040  if (!RemovedAfterVersion.empty()) {
1041  Result << "<RemovedAfterVersion>"
1042  << RemovedAfterVersion.getAsString()
1043  << "</RemovedAfterVersion>";
1044  }
1045  StringRef DeprecationSummary = AA->getMessage();
1046  if (!DeprecationSummary.empty()) {
1047  Result << "<DeprecationSummary>";
1048  appendToResultWithXMLEscaping(DeprecationSummary);
1049  Result << "</DeprecationSummary>";
1050  }
1051  if (AA->getUnavailable())
1052  Result << "<Unavailable/>";
1053  Result << "</Availability>";
1054  }
1055  }
1056 
1057  {
1058  bool StartTagEmitted = false;
1059  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1060  const Comment *C = Parts.MiscBlocks[i];
1061  if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1062  continue;
1063  if (!StartTagEmitted) {
1064  Result << "<Discussion>";
1065  StartTagEmitted = true;
1066  }
1067  visit(C);
1068  }
1069  if (StartTagEmitted)
1070  Result << "</Discussion>";
1071  }
1072 
1073  Result << RootEndTag;
1074 }
1075 
1076 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1077  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1078  const char C = *I;
1079  switch (C) {
1080  case '&':
1081  Result << "&amp;";
1082  break;
1083  case '<':
1084  Result << "&lt;";
1085  break;
1086  case '>':
1087  Result << "&gt;";
1088  break;
1089  case '"':
1090  Result << "&quot;";
1091  break;
1092  case '\'':
1093  Result << "&apos;";
1094  break;
1095  default:
1096  Result << C;
1097  break;
1098  }
1099  }
1100 }
1101 
1102 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1103  if (S.empty())
1104  return;
1105 
1106  Result << "<![CDATA[";
1107  while (!S.empty()) {
1108  size_t Pos = S.find("]]>");
1109  if (Pos == 0) {
1110  Result << "]]]]><![CDATA[>";
1111  S = S.drop_front(3);
1112  continue;
1113  }
1114  if (Pos == StringRef::npos)
1115  Pos = S.size();
1116 
1117  Result << S.substr(0, Pos);
1118 
1119  S = S.drop_front(Pos);
1120  }
1121  Result << "]]>";
1122 }
1123 
1124 CommentToXMLConverter::CommentToXMLConverter() {}
1125 CommentToXMLConverter::~CommentToXMLConverter() {}
1126 
1127 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1128  SmallVectorImpl<char> &HTML,
1129  const ASTContext &Context) {
1130  CommentASTToHTMLConverter Converter(FC, HTML,
1131  Context.getCommentCommandTraits());
1132  Converter.visit(FC);
1133 }
1134 
1135 void CommentToXMLConverter::convertHTMLTagNodeToText(
1137  const ASTContext &Context) {
1138  CommentASTToHTMLConverter Converter(nullptr, Text,
1139  Context.getCommentCommandTraits());
1140  Converter.visit(HTC);
1141 }
1142 
1143 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1144  SmallVectorImpl<char> &XML,
1145  const ASTContext &Context) {
1146  CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1147  Context.getSourceManager());
1148  Converter.visit(FC);
1149 }
Defines the clang::ASTContext interface.
bool isVarArgParam() const LLVM_READONLY
Definition: Comment.h:778
const Decl * CommentDecl
Declaration the comment is actually attached to (in the source).
Definition: Comment.h:985
bool isPositionValid() const LLVM_READONLY
Definition: Comment.h:844
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:362
Describes how types, statements, expressions, and declarations should be printed. ...
Definition: PrettyPrinter.h:47
unsigned getIndex(unsigned Depth) const
Definition: Comment.h:853
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:691
StringRef getCommandName(const CommandTraits &Traits) const
Definition: Comment.h:656
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:168
child_iterator child_end() const
Definition: Comment.h:1117
RenderKind getRenderKind() const
Definition: Comment.h:354
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:725
const Decl * CurrentDecl
CurrentDecl is the declaration with which the FullComment is associated.
Definition: Comment.h:995
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:53
A command with word-like arguments that is considered inline content.
Definition: Comment.h:299
A line of text contained in a verbatim block.
Definition: Comment.h:865
A verbatim line command.
Definition: Comment.h:945
Any part of the comment.
Definition: Comment.h:52
unsigned getParamIndex() const LLVM_READONLY
Definition: Comment.h:787
#define UINT_MAX
Definition: limits.h:56
A verbatim block command (e.
Definition: Comment.h:893
StringRef getText() const LLVM_READONLY
Definition: Comment.h:283
unsigned TemplateKind
Is CommentDecl a template declaration.
Definition: Comment.h:1066
bool isValid() const
A source range independent of the SourceManager.
Definition: Replacement.h:44
unsigned Offset
Definition: Format.cpp:1827
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:1113
comments::CommandTraits & getCommentCommandTraits() const
Definition: ASTContext.h:880
A command that has zero or more word-like arguments (number of word-like arguments depends on command...
Definition: Comment.h:598
CommentKind getCommentKind() const
Definition: Comment.h:204
bool isDirectionExplicit() const LLVM_READONLY
Definition: Comment.h:751
An opening HTML tag with attributes.
Definition: Comment.h:415
const SourceManager & SM
Definition: Format.cpp:1685
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:774
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:2461
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:532
Comment *const * child_iterator
Definition: Comment.h:224
StringRef getParamNameAsWritten() const
Definition: Comment.h:836
PassDirection getDirection() const LLVM_READONLY
Definition: Comment.h:747
A closing HTML tag.
Definition: Comment.h:509
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:801
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:982
A single paragraph that contains inline content.
Definition: Comment.h:548
DeclKind getKind() const LLVM_READONLY
Definition: Comment.h:1083
unsigned IsInstanceMethod
Is CommentDecl a non-static member function of C++ class or instance method of ObjC class...
Definition: Comment.h:1074
StringRef getText(unsigned LineIdx) const
Definition: Comment.h:937
StringRef getTagName() const LLVM_READONLY
Definition: Comment.h:397
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:679
Abstract class for opening and closing HTML tags.
Definition: Comment.h:373
const Attribute & getAttr(unsigned Idx) const
Definition: Comment.h:478
StringRef getParamNameAsWritten() const
Definition: Comment.h:766
child_iterator child_end() const
Definition: Comment.h:578
unsigned IsClassMethod
Is CommentDecl a static member function of C++ class or class method of ObjC class.
Definition: Comment.h:1079
Doxygen \param command.
Definition: Comment.h:713
const DeclInfo * getDeclInfo() const LLVM_READONLY
Definition: Comment.h:1125
StringRef Text
Definition: Format.cpp:1826
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:223
StringRef getParamName(const FullComment *FC) const
Definition: Comment.cpp:378
child_iterator child_begin() const
Definition: Comment.h:574
const LangOptions & getLangOpts() const
Definition: ASTContext.h:724
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:1093
std::pair< FileID, unsigned > getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
const FormatStyle & Style