Clang Project

clang_source_code/lib/Index/CommentToXML.cpp
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
9#include "clang/Index/CommentToXML.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Attr.h"
12#include "clang/AST/Comment.h"
13#include "clang/AST/CommentVisitor.h"
14#include "clang/Format/Format.h"
15#include "clang/Index/USRGeneration.h"
16#include "llvm/ADT/StringExtras.h"
17#include "llvm/ADT/TinyPtrVector.h"
18#include "llvm/Support/raw_ostream.h"
19
20using namespace clang;
21using namespace clang::comments;
22using namespace clang::index;
23
24namespace {
25
26/// This comparison will sort parameters with valid index by index, then vararg
27/// parameters, and invalid (unresolved) parameters last.
28class ParamCommandCommentCompareIndex {
29public:
30  bool operator()(const ParamCommandComment *LHS,
31                  const ParamCommandComment *RHSconst {
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.
55class TParamCommandCommentComparePosition {
56public:
57  bool operator()(const TParamCommandComment *LHS,
58                  const TParamCommandComment *RHSconst {
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.
80struct 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;
88  SmallVector<const BlockCommandComment *, 4Returns;
89  SmallVector<const ParamCommandComment *, 8Params;
90  SmallVector<const TParamCommandComment *, 4TParams;
91  llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
92  SmallVector<const BlockContentComment *, 8MiscBlocks;
93};
94
95FullCommentParts::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  std::stable_sort(Params.begin(), Params.end(),
192                   ParamCommandCommentCompareIndex());
193
194  std::stable_sort(TParams.begin(), TParams.end(),
195                   TParamCommandCommentComparePosition());
196}
197
198void printHTMLStartTagComment(const HTMLStartTagComment *C,
199                              llvm::raw_svector_ostream &Result) {
200  Result << "<" << C->getTagName();
201
202  if (C->getNumAttrs() != 0) {
203    for (unsigned i = 0e = C->getNumAttrs(); i != ei++) {
204      Result << " ";
205      const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
206      Result << Attr.Name;
207      if (!Attr.Value.empty())
208        Result << "=\"" << Attr.Value << "\"";
209    }
210  }
211
212  if (!C->isSelfClosing())
213    Result << ">";
214  else
215    Result << "/>";
216}
217
218class CommentASTToHTMLConverter :
219    public ConstCommentVisitor<CommentASTToHTMLConverter> {
220public:
221  /// \param Str accumulator for HTML.
222  CommentASTToHTMLConverter(const FullComment *FC,
223                            SmallVectorImpl<char> &Str,
224                            const CommandTraits &Traits) :
225      FC(FC), Result(Str), Traits(Traits)
226  { }
227
228  // Inline content.
229  void visitTextComment(const TextComment *C);
230  void visitInlineCommandComment(const InlineCommandComment *C);
231  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
232  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
233
234  // Block content.
235  void visitParagraphComment(const ParagraphComment *C);
236  void visitBlockCommandComment(const BlockCommandComment *C);
237  void visitParamCommandComment(const ParamCommandComment *C);
238  void visitTParamCommandComment(const TParamCommandComment *C);
239  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
240  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
241  void visitVerbatimLineComment(const VerbatimLineComment *C);
242
243  void visitFullComment(const FullComment *C);
244
245  // Helpers.
246
247  /// Convert a paragraph that is not a block by itself (an argument to some
248  /// command).
249  void visitNonStandaloneParagraphComment(const ParagraphComment *C);
250
251  void appendToResultWithHTMLEscaping(StringRef S);
252
253private:
254  const FullComment *FC;
255  /// Output stream for HTML.
256  llvm::raw_svector_ostream Result;
257
258  const CommandTraits &Traits;
259};
260// end unnamed namespace
261
262void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
263  appendToResultWithHTMLEscaping(C->getText());
264}
265
266void CommentASTToHTMLConverter::visitInlineCommandComment(
267                                  const InlineCommandComment *C) {
268  // Nothing to render if no arguments supplied.
269  if (C->getNumArgs() == 0)
270    return;
271
272  // Nothing to render if argument is empty.
273  StringRef Arg0 = C->getArgText(0);
274  if (Arg0.empty())
275    return;
276
277  switch (C->getRenderKind()) {
278  case InlineCommandComment::RenderNormal:
279    for (unsigned i = 0e = C->getNumArgs(); i != e; ++i) {
280      appendToResultWithHTMLEscaping(C->getArgText(i));
281      Result << " ";
282    }
283    return;
284
285  case InlineCommandComment::RenderBold:
286    getNumArgs() == 1", "/home/seafit/code_projects/clang_source/clang/lib/Index/CommentToXML.cpp", 286, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(C->getNumArgs() == 1);
287    Result << "<b>";
288    appendToResultWithHTMLEscaping(Arg0);
289    Result << "</b>";
290    return;
291  case InlineCommandComment::RenderMonospaced:
292    getNumArgs() == 1", "/home/seafit/code_projects/clang_source/clang/lib/Index/CommentToXML.cpp", 292, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(C->getNumArgs() == 1);
293    Result << "<tt>";
294    appendToResultWithHTMLEscaping(Arg0);
295    Result<< "</tt>";
296    return;
297  case InlineCommandComment::RenderEmphasized:
298    getNumArgs() == 1", "/home/seafit/code_projects/clang_source/clang/lib/Index/CommentToXML.cpp", 298, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(C->getNumArgs() == 1);
299    Result << "<em>";
300    appendToResultWithHTMLEscaping(Arg0);
301    Result << "</em>";
302    return;
303  }
304}
305
306void CommentASTToHTMLConverter::visitHTMLStartTagComment(
307                                  const HTMLStartTagComment *C) {
308  printHTMLStartTagComment(C, Result);
309}
310
311void CommentASTToHTMLConverter::visitHTMLEndTagComment(
312                                  const HTMLEndTagComment *C) {
313  Result << "</" << C->getTagName() << ">";
314}
315
316void CommentASTToHTMLConverter::visitParagraphComment(
317                                  const ParagraphComment *C) {
318  if (C->isWhitespace())
319    return;
320
321  Result << "<p>";
322  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
323       I != E; ++I) {
324    visit(*I);
325  }
326  Result << "</p>";
327}
328
329void CommentASTToHTMLConverter::visitBlockCommandComment(
330                                  const BlockCommandComment *C) {
331  const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
332  if (Info->IsBriefCommand) {
333    Result << "<p class=\"para-brief\">";
334    visitNonStandaloneParagraphComment(C->getParagraph());
335    Result << "</p>";
336    return;
337  }
338  if (Info->IsReturnsCommand) {
339    Result << "<p class=\"para-returns\">"
340              "<span class=\"word-returns\">Returns</span> ";
341    visitNonStandaloneParagraphComment(C->getParagraph());
342    Result << "</p>";
343    return;
344  }
345  // We don't know anything about this command.  Just render the paragraph.
346  visit(C->getParagraph());
347}
348
349void CommentASTToHTMLConverter::visitParamCommandComment(
350                                  const ParamCommandComment *C) {
351  if (C->isParamIndexValid()) {
352    if (C->isVarArgParam()) {
353      Result << "<dt class=\"param-name-index-vararg\">";
354      appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
355    } else {
356      Result << "<dt class=\"param-name-index-"
357             << C->getParamIndex()
358             << "\">";
359      appendToResultWithHTMLEscaping(C->getParamName(FC));
360    }
361  } else {
362    Result << "<dt class=\"param-name-index-invalid\">";
363    appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
364  }
365  Result << "</dt>";
366
367  if (C->isParamIndexValid()) {
368    if (C->isVarArgParam())
369      Result << "<dd class=\"param-descr-index-vararg\">";
370    else
371      Result << "<dd class=\"param-descr-index-"
372             << C->getParamIndex()
373             << "\">";
374  } else
375    Result << "<dd class=\"param-descr-index-invalid\">";
376
377  visitNonStandaloneParagraphComment(C->getParagraph());
378  Result << "</dd>";
379}
380
381void CommentASTToHTMLConverter::visitTParamCommandComment(
382                                  const TParamCommandComment *C) {
383  if (C->isPositionValid()) {
384    if (C->getDepth() == 1)
385      Result << "<dt class=\"tparam-name-index-"
386             << C->getIndex(0)
387             << "\">";
388    else
389      Result << "<dt class=\"tparam-name-index-other\">";
390    appendToResultWithHTMLEscaping(C->getParamName(FC));
391  } else {
392    Result << "<dt class=\"tparam-name-index-invalid\">";
393    appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
394  }
395
396  Result << "</dt>";
397
398  if (C->isPositionValid()) {
399    if (C->getDepth() == 1)
400      Result << "<dd class=\"tparam-descr-index-"
401             << C->getIndex(0)
402             << "\">";
403    else
404      Result << "<dd class=\"tparam-descr-index-other\">";
405  } else
406    Result << "<dd class=\"tparam-descr-index-invalid\">";
407
408  visitNonStandaloneParagraphComment(C->getParagraph());
409  Result << "</dd>";
410}
411
412void CommentASTToHTMLConverter::visitVerbatimBlockComment(
413                                  const VerbatimBlockComment *C) {
414  unsigned NumLines = C->getNumLines();
415  if (NumLines == 0)
416    return;
417
418  Result << "<pre>";
419  for (unsigned i = 0i != NumLines; ++i) {
420    appendToResultWithHTMLEscaping(C->getText(i));
421    if (i + 1 != NumLines)
422      Result << '\n';
423  }
424  Result << "</pre>";
425}
426
427void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
428                                  const VerbatimBlockLineComment *C) {
429  llvm_unreachable("should not see this AST node");
430}
431
432void CommentASTToHTMLConverter::visitVerbatimLineComment(
433                                  const VerbatimLineComment *C) {
434  Result << "<pre>";
435  appendToResultWithHTMLEscaping(C->getText());
436  Result << "</pre>";
437}
438
439void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
440  FullCommentParts Parts(CTraits);
441
442  bool FirstParagraphIsBrief = false;
443  if (Parts.Headerfile)
444    visit(Parts.Headerfile);
445  if (Parts.Brief)
446    visit(Parts.Brief);
447  else if (Parts.FirstParagraph) {
448    Result << "<p class=\"para-brief\">";
449    visitNonStandaloneParagraphComment(Parts.FirstParagraph);
450    Result << "</p>";
451    FirstParagraphIsBrief = true;
452  }
453
454  for (unsigned i = 0e = Parts.MiscBlocks.size(); i != e; ++i) {
455    const Comment *C = Parts.MiscBlocks[i];
456    if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
457      continue;
458    visit(C);
459  }
460
461  if (Parts.TParams.size() != 0) {
462    Result << "<dl>";
463    for (unsigned i = 0e = Parts.TParams.size(); i != e; ++i)
464      visit(Parts.TParams[i]);
465    Result << "</dl>";
466  }
467
468  if (Parts.Params.size() != 0) {
469    Result << "<dl>";
470    for (unsigned i = 0e = Parts.Params.size(); i != e; ++i)
471      visit(Parts.Params[i]);
472    Result << "</dl>";
473  }
474
475  if (Parts.Returns.size() != 0) {
476    Result << "<div class=\"result-discussion\">";
477    for (unsigned i = 0e = Parts.Returns.size(); i != e; ++i)
478      visit(Parts.Returns[i]);
479    Result << "</div>";
480  }
481
482}
483
484void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
485                                  const ParagraphComment *C) {
486  if (!C)
487    return;
488
489  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
490       I != E; ++I) {
491    visit(*I);
492  }
493}
494
495void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
496  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
497    const char C = *I;
498    switch (C) {
499    case '&':
500      Result << "&amp;";
501      break;
502    case '<':
503      Result << "&lt;";
504      break;
505    case '>':
506      Result << "&gt;";
507      break;
508    case '"':
509      Result << "&quot;";
510      break;
511    case '\'':
512      Result << "&#39;";
513      break;
514    case '/':
515      Result << "&#47;";
516      break;
517    default:
518      Result << C;
519      break;
520    }
521  }
522}
523
524namespace {
525class CommentASTToXMLConverter :
526    public ConstCommentVisitor<CommentASTToXMLConverter> {
527public:
528  /// \param Str accumulator for XML.
529  CommentASTToXMLConverter(const FullComment *FC,
530                           SmallVectorImpl<char> &Str,
531                           const CommandTraits &Traits,
532                           const SourceManager &SM) :
533      FC(FC), Result(Str), Traits(Traits), SM(SM) { }
534
535  // Inline content.
536  void visitTextComment(const TextComment *C);
537  void visitInlineCommandComment(const InlineCommandComment *C);
538  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
539  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
540
541  // Block content.
542  void visitParagraphComment(const ParagraphComment *C);
543
544  void appendParagraphCommentWithKind(const ParagraphComment *C,
545                                      StringRef Kind);
546
547  void visitBlockCommandComment(const BlockCommandComment *C);
548  void visitParamCommandComment(const ParamCommandComment *C);
549  void visitTParamCommandComment(const TParamCommandComment *C);
550  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
551  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
552  void visitVerbatimLineComment(const VerbatimLineComment *C);
553
554  void visitFullComment(const FullComment *C);
555
556  // Helpers.
557  void appendToResultWithXMLEscaping(StringRef S);
558  void appendToResultWithCDATAEscaping(StringRef S);
559
560  void formatTextOfDeclaration(const DeclInfo *DI,
561                               SmallString<128> &Declaration);
562
563private:
564  const FullComment *FC;
565
566  /// Output stream for XML.
567  llvm::raw_svector_ostream Result;
568
569  const CommandTraits &Traits;
570  const SourceManager &SM;
571};
572
573void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
574                                SmallVectorImpl<char> &Str) {
575  ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
576  const LangOptions &LangOpts = Context.getLangOpts();
577  llvm::raw_svector_ostream OS(Str);
578  PrintingPolicy PPolicy(LangOpts);
579  PPolicy.PolishForDeclaration = true;
580  PPolicy.TerseOutput = true;
581  PPolicy.ConstantsAsWritten = true;
582  ThisDecl->CurrentDecl->print(OS, PPolicy,
583                               /*Indentation*/0/*PrintInstantiation*/false);
584}
585
586void CommentASTToXMLConverter::formatTextOfDeclaration(
587    const DeclInfo *DI, SmallString<128> &Declaration) {
588  // Formatting API expects null terminated input string.
589  StringRef StringDecl(Declaration.c_str(), Declaration.size());
590
591  // Formatter specific code.
592  unsigned Offset = 0;
593  unsigned Length = Declaration.size();
594
595  format::FormatStyle Style = format::getLLVMStyle();
596  Style.FixNamespaceComments = false;
597  tooling::Replacements Replaces =
598      reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
599  auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
600  if (static_cast<bool>(FormattedStringDecl)) {
601    Declaration = *FormattedStringDecl;
602  }
603}
604
605// end unnamed namespace
606
607void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
608  appendToResultWithXMLEscaping(C->getText());
609}
610
611void CommentASTToXMLConverter::visitInlineCommandComment(
612    const InlineCommandComment *C) {
613  // Nothing to render if no arguments supplied.
614  if (C->getNumArgs() == 0)
615    return;
616
617  // Nothing to render if argument is empty.
618  StringRef Arg0 = C->getArgText(0);
619  if (Arg0.empty())
620    return;
621
622  switch (C->getRenderKind()) {
623  case InlineCommandComment::RenderNormal:
624    for (unsigned i = 0e = C->getNumArgs(); i != e; ++i) {
625      appendToResultWithXMLEscaping(C->getArgText(i));
626      Result << " ";
627    }
628    return;
629  case InlineCommandComment::RenderBold:
630    getNumArgs() == 1", "/home/seafit/code_projects/clang_source/clang/lib/Index/CommentToXML.cpp", 630, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(C->getNumArgs() == 1);
631    Result << "<bold>";
632    appendToResultWithXMLEscaping(Arg0);
633    Result << "</bold>";
634    return;
635  case InlineCommandComment::RenderMonospaced:
636    getNumArgs() == 1", "/home/seafit/code_projects/clang_source/clang/lib/Index/CommentToXML.cpp", 636, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(C->getNumArgs() == 1);
637    Result << "<monospaced>";
638    appendToResultWithXMLEscaping(Arg0);
639    Result << "</monospaced>";
640    return;
641  case InlineCommandComment::RenderEmphasized:
642    getNumArgs() == 1", "/home/seafit/code_projects/clang_source/clang/lib/Index/CommentToXML.cpp", 642, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(C->getNumArgs() == 1);
643    Result << "<emphasized>";
644    appendToResultWithXMLEscaping(Arg0);
645    Result << "</emphasized>";
646    return;
647  }
648}
649
650void CommentASTToXMLConverter::visitHTMLStartTagComment(
651    const HTMLStartTagComment *C) {
652  Result << "<rawHTML";
653  if (C->isMalformed())
654    Result << " isMalformed=\"1\"";
655  Result << ">";
656  {
657    SmallString<32Tag;
658    {
659      llvm::raw_svector_ostream TagOS(Tag);
660      printHTMLStartTagComment(C, TagOS);
661    }
662    appendToResultWithCDATAEscaping(Tag);
663  }
664  Result << "</rawHTML>";
665}
666
667void
668CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
669  Result << "<rawHTML";
670  if (C->isMalformed())
671    Result << " isMalformed=\"1\"";
672  Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
673}
674
675void
676CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
677  appendParagraphCommentWithKind(C, StringRef());
678}
679
680void CommentASTToXMLConverter::appendParagraphCommentWithKind(
681                                  const ParagraphComment *C,
682                                  StringRef ParagraphKind) {
683  if (C->isWhitespace())
684    return;
685
686  if (ParagraphKind.empty())
687    Result << "<Para>";
688  else
689    Result << "<Para kind=\"" << ParagraphKind << "\">";
690
691  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
692       I != E; ++I) {
693    visit(*I);
694  }
695  Result << "</Para>";
696}
697
698void CommentASTToXMLConverter::visitBlockCommandComment(
699    const BlockCommandComment *C) {
700  StringRef ParagraphKind;
701
702  switch (C->getCommandID()) {
703  case CommandTraits::KCI_attention:
704  case CommandTraits::KCI_author:
705  case CommandTraits::KCI_authors:
706  case CommandTraits::KCI_bug:
707  case CommandTraits::KCI_copyright:
708  case CommandTraits::KCI_date:
709  case CommandTraits::KCI_invariant:
710  case CommandTraits::KCI_note:
711  case CommandTraits::KCI_post:
712  case CommandTraits::KCI_pre:
713  case CommandTraits::KCI_remark:
714  case CommandTraits::KCI_remarks:
715  case CommandTraits::KCI_sa:
716  case CommandTraits::KCI_see:
717  case CommandTraits::KCI_since:
718  case CommandTraits::KCI_todo:
719  case CommandTraits::KCI_version:
720  case CommandTraits::KCI_warning:
721    ParagraphKind = C->getCommandName(Traits);
722    break;
723  default:
724    break;
725  }
726
727  appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
728}
729
730void 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
762void 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
778void 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 = 0i != NumLines; ++i) {
793    appendToResultWithXMLEscaping(C->getText(i));
794    if (i + 1 != NumLines)
795      Result << '\n';
796  }
797  Result << "</Verbatim>";
798}
799
800void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
801                                  const VerbatimBlockLineComment *C) {
802  llvm_unreachable("should not see this AST node");
803}
804
805void CommentASTToXMLConverter::visitVerbatimLineComment(
806                                  const VerbatimLineComment *C) {
807  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
808  appendToResultWithXMLEscaping(C->getText());
809  Result << "</Verbatim>";
810}
811
812void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
813  FullCommentParts Parts(CTraits);
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;
832      case DeclInfo::TemplateSpecialization:
833        Result << " templateKind=\"specialization\"";
834        break;
835      case DeclInfo::TemplatePartialSpecialization:
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;
853      case DeclInfo::TemplateSpecialization:
854        Result << " templateKind=\"specialization\"";
855        break;
856      case DeclInfo::TemplatePartialSpecialization:
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.
881      SourceLocation Loc = DI->CurrentDecl->getLocation();
882      std::pair<FileID, unsignedLocInfo = 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<128USR;
917      generateUSRForDecl(DI->CommentDecl, 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>";
939    SmallString<128Declaration;
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 = 0e = 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 = 0e = 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 = 0e = 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 = 0e = 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 = 0e = Attrs.size(); i != ei++) {
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 = 0e = 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
1071void 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
1097void 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
1119CommentToXMLConverter::CommentToXMLConverter() {}
1120CommentToXMLConverter::~CommentToXMLConverter() {}
1121
1122void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1123                                                 SmallVectorImpl<char> &HTML,
1124                                                 const ASTContext &Context) {
1125  CommentASTToHTMLConverter Converter(FCHTML,
1126                                      Context.getCommentCommandTraits());
1127  Converter.visit(FC);
1128}
1129
1130void CommentToXMLConverter::convertHTMLTagNodeToText(
1131    const comments::HTMLTagComment *HTCSmallVectorImpl<char> &Text,
1132    const ASTContext &Context) {
1133  CommentASTToHTMLConverter Converter(nullptrText,
1134                                      Context.getCommentCommandTraits());
1135  Converter.visit(HTC);
1136}
1137
1138void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1139                                                SmallVectorImpl<char> &XML,
1140                                                const ASTContext &Context) {
1141  CommentASTToXMLConverter Converter(FCXMLContext.getCommentCommandTraits(),
1142                                     Context.getSourceManager());
1143  Converter.visit(FC);
1144}
1145
clang::index::CommentToXMLConverter::convertCommentToHTML
clang::index::CommentToXMLConverter::convertHTMLTagNodeToText
clang::index::CommentToXMLConverter::convertCommentToXML