CommentToXML.cpp 34 KB

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