CommentToXML.cpp 34 KB

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