CommentToXML.cpp 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166
  1. //===--- CommentToXML.cpp - Convert comments to XML representation --------===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. #include "clang/Index/CommentToXML.h"
  10. #include "SimpleFormatContext.h"
  11. #include "clang/AST/ASTContext.h"
  12. #include "clang/AST/Attr.h"
  13. #include "clang/AST/Comment.h"
  14. #include "clang/AST/CommentVisitor.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. std::stable_sort(Params.begin(), Params.end(),
  169. ParamCommandCommentCompareIndex());
  170. std::stable_sort(TParams.begin(), TParams.end(),
  171. TParamCommandCommentComparePosition());
  172. }
  173. void printHTMLStartTagComment(const HTMLStartTagComment *C,
  174. llvm::raw_svector_ostream &Result) {
  175. Result << "<" << C->getTagName();
  176. if (C->getNumAttrs() != 0) {
  177. for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
  178. Result << " ";
  179. const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
  180. Result << Attr.Name;
  181. if (!Attr.Value.empty())
  182. Result << "=\"" << Attr.Value << "\"";
  183. }
  184. }
  185. if (!C->isSelfClosing())
  186. Result << ">";
  187. else
  188. Result << "/>";
  189. }
  190. class CommentASTToHTMLConverter :
  191. public ConstCommentVisitor<CommentASTToHTMLConverter> {
  192. public:
  193. /// \param Str accumulator for HTML.
  194. CommentASTToHTMLConverter(const FullComment *FC,
  195. SmallVectorImpl<char> &Str,
  196. const CommandTraits &Traits) :
  197. FC(FC), Result(Str), Traits(Traits)
  198. { }
  199. // Inline content.
  200. void visitTextComment(const TextComment *C);
  201. void visitInlineCommandComment(const InlineCommandComment *C);
  202. void visitHTMLStartTagComment(const HTMLStartTagComment *C);
  203. void visitHTMLEndTagComment(const HTMLEndTagComment *C);
  204. // Block content.
  205. void visitParagraphComment(const ParagraphComment *C);
  206. void visitBlockCommandComment(const BlockCommandComment *C);
  207. void visitParamCommandComment(const ParamCommandComment *C);
  208. void visitTParamCommandComment(const TParamCommandComment *C);
  209. void visitVerbatimBlockComment(const VerbatimBlockComment *C);
  210. void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
  211. void visitVerbatimLineComment(const VerbatimLineComment *C);
  212. void visitFullComment(const FullComment *C);
  213. // Helpers.
  214. /// Convert a paragraph that is not a block by itself (an argument to some
  215. /// command).
  216. void visitNonStandaloneParagraphComment(const ParagraphComment *C);
  217. void appendToResultWithHTMLEscaping(StringRef S);
  218. private:
  219. const FullComment *FC;
  220. /// Output stream for HTML.
  221. llvm::raw_svector_ostream Result;
  222. const CommandTraits &Traits;
  223. };
  224. } // end unnamed namespace
  225. void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
  226. appendToResultWithHTMLEscaping(C->getText());
  227. }
  228. void CommentASTToHTMLConverter::visitInlineCommandComment(
  229. const InlineCommandComment *C) {
  230. // Nothing to render if no arguments supplied.
  231. if (C->getNumArgs() == 0)
  232. return;
  233. // Nothing to render if argument is empty.
  234. StringRef Arg0 = C->getArgText(0);
  235. if (Arg0.empty())
  236. return;
  237. switch (C->getRenderKind()) {
  238. case InlineCommandComment::RenderNormal:
  239. for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
  240. appendToResultWithHTMLEscaping(C->getArgText(i));
  241. Result << " ";
  242. }
  243. return;
  244. case InlineCommandComment::RenderBold:
  245. assert(C->getNumArgs() == 1);
  246. Result << "<b>";
  247. appendToResultWithHTMLEscaping(Arg0);
  248. Result << "</b>";
  249. return;
  250. case InlineCommandComment::RenderMonospaced:
  251. assert(C->getNumArgs() == 1);
  252. Result << "<tt>";
  253. appendToResultWithHTMLEscaping(Arg0);
  254. Result<< "</tt>";
  255. return;
  256. case InlineCommandComment::RenderEmphasized:
  257. assert(C->getNumArgs() == 1);
  258. Result << "<em>";
  259. appendToResultWithHTMLEscaping(Arg0);
  260. Result << "</em>";
  261. return;
  262. }
  263. }
  264. void CommentASTToHTMLConverter::visitHTMLStartTagComment(
  265. const HTMLStartTagComment *C) {
  266. printHTMLStartTagComment(C, Result);
  267. }
  268. void CommentASTToHTMLConverter::visitHTMLEndTagComment(
  269. const HTMLEndTagComment *C) {
  270. Result << "</" << C->getTagName() << ">";
  271. }
  272. void CommentASTToHTMLConverter::visitParagraphComment(
  273. const ParagraphComment *C) {
  274. if (C->isWhitespace())
  275. return;
  276. Result << "<p>";
  277. for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
  278. I != E; ++I) {
  279. visit(*I);
  280. }
  281. Result << "</p>";
  282. }
  283. void CommentASTToHTMLConverter::visitBlockCommandComment(
  284. const BlockCommandComment *C) {
  285. const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
  286. if (Info->IsBriefCommand) {
  287. Result << "<p class=\"para-brief\">";
  288. visitNonStandaloneParagraphComment(C->getParagraph());
  289. Result << "</p>";
  290. return;
  291. }
  292. if (Info->IsReturnsCommand) {
  293. Result << "<p class=\"para-returns\">"
  294. "<span class=\"word-returns\">Returns</span> ";
  295. visitNonStandaloneParagraphComment(C->getParagraph());
  296. Result << "</p>";
  297. return;
  298. }
  299. // We don't know anything about this command. Just render the paragraph.
  300. visit(C->getParagraph());
  301. }
  302. void CommentASTToHTMLConverter::visitParamCommandComment(
  303. const ParamCommandComment *C) {
  304. if (C->isParamIndexValid()) {
  305. if (C->isVarArgParam()) {
  306. Result << "<dt class=\"param-name-index-vararg\">";
  307. appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
  308. } else {
  309. Result << "<dt class=\"param-name-index-"
  310. << C->getParamIndex()
  311. << "\">";
  312. appendToResultWithHTMLEscaping(C->getParamName(FC));
  313. }
  314. } else {
  315. Result << "<dt class=\"param-name-index-invalid\">";
  316. appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
  317. }
  318. Result << "</dt>";
  319. if (C->isParamIndexValid()) {
  320. if (C->isVarArgParam())
  321. Result << "<dd class=\"param-descr-index-vararg\">";
  322. else
  323. Result << "<dd class=\"param-descr-index-"
  324. << C->getParamIndex()
  325. << "\">";
  326. } else
  327. Result << "<dd class=\"param-descr-index-invalid\">";
  328. visitNonStandaloneParagraphComment(C->getParagraph());
  329. Result << "</dd>";
  330. }
  331. void CommentASTToHTMLConverter::visitTParamCommandComment(
  332. const TParamCommandComment *C) {
  333. if (C->isPositionValid()) {
  334. if (C->getDepth() == 1)
  335. Result << "<dt class=\"tparam-name-index-"
  336. << C->getIndex(0)
  337. << "\">";
  338. else
  339. Result << "<dt class=\"tparam-name-index-other\">";
  340. appendToResultWithHTMLEscaping(C->getParamName(FC));
  341. } else {
  342. Result << "<dt class=\"tparam-name-index-invalid\">";
  343. appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
  344. }
  345. Result << "</dt>";
  346. if (C->isPositionValid()) {
  347. if (C->getDepth() == 1)
  348. Result << "<dd class=\"tparam-descr-index-"
  349. << C->getIndex(0)
  350. << "\">";
  351. else
  352. Result << "<dd class=\"tparam-descr-index-other\">";
  353. } else
  354. Result << "<dd class=\"tparam-descr-index-invalid\">";
  355. visitNonStandaloneParagraphComment(C->getParagraph());
  356. Result << "</dd>";
  357. }
  358. void CommentASTToHTMLConverter::visitVerbatimBlockComment(
  359. const VerbatimBlockComment *C) {
  360. unsigned NumLines = C->getNumLines();
  361. if (NumLines == 0)
  362. return;
  363. Result << "<pre>";
  364. for (unsigned i = 0; i != NumLines; ++i) {
  365. appendToResultWithHTMLEscaping(C->getText(i));
  366. if (i + 1 != NumLines)
  367. Result << '\n';
  368. }
  369. Result << "</pre>";
  370. }
  371. void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
  372. const VerbatimBlockLineComment *C) {
  373. llvm_unreachable("should not see this AST node");
  374. }
  375. void CommentASTToHTMLConverter::visitVerbatimLineComment(
  376. const VerbatimLineComment *C) {
  377. Result << "<pre>";
  378. appendToResultWithHTMLEscaping(C->getText());
  379. Result << "</pre>";
  380. }
  381. void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
  382. FullCommentParts Parts(C, Traits);
  383. bool FirstParagraphIsBrief = false;
  384. if (Parts.Headerfile)
  385. visit(Parts.Headerfile);
  386. if (Parts.Brief)
  387. visit(Parts.Brief);
  388. else if (Parts.FirstParagraph) {
  389. Result << "<p class=\"para-brief\">";
  390. visitNonStandaloneParagraphComment(Parts.FirstParagraph);
  391. Result << "</p>";
  392. FirstParagraphIsBrief = true;
  393. }
  394. for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
  395. const Comment *C = Parts.MiscBlocks[i];
  396. if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
  397. continue;
  398. visit(C);
  399. }
  400. if (Parts.TParams.size() != 0) {
  401. Result << "<dl>";
  402. for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
  403. visit(Parts.TParams[i]);
  404. Result << "</dl>";
  405. }
  406. if (Parts.Params.size() != 0) {
  407. Result << "<dl>";
  408. for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
  409. visit(Parts.Params[i]);
  410. Result << "</dl>";
  411. }
  412. if (Parts.Returns.size() != 0) {
  413. Result << "<div class=\"result-discussion\">";
  414. for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
  415. visit(Parts.Returns[i]);
  416. Result << "</div>";
  417. }
  418. Result.flush();
  419. }
  420. void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
  421. const ParagraphComment *C) {
  422. if (!C)
  423. return;
  424. for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
  425. I != E; ++I) {
  426. visit(*I);
  427. }
  428. }
  429. void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
  430. for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
  431. const char C = *I;
  432. switch (C) {
  433. case '&':
  434. Result << "&amp;";
  435. break;
  436. case '<':
  437. Result << "&lt;";
  438. break;
  439. case '>':
  440. Result << "&gt;";
  441. break;
  442. case '"':
  443. Result << "&quot;";
  444. break;
  445. case '\'':
  446. Result << "&#39;";
  447. break;
  448. case '/':
  449. Result << "&#47;";
  450. break;
  451. default:
  452. Result << C;
  453. break;
  454. }
  455. }
  456. }
  457. namespace {
  458. class CommentASTToXMLConverter :
  459. public ConstCommentVisitor<CommentASTToXMLConverter> {
  460. public:
  461. /// \param Str accumulator for XML.
  462. CommentASTToXMLConverter(const FullComment *FC,
  463. SmallVectorImpl<char> &Str,
  464. const CommandTraits &Traits,
  465. const SourceManager &SM,
  466. SimpleFormatContext &SFC,
  467. unsigned FUID) :
  468. FC(FC), Result(Str), Traits(Traits), SM(SM),
  469. FormatRewriterContext(SFC),
  470. FormatInMemoryUniqueId(FUID) { }
  471. // Inline content.
  472. void visitTextComment(const TextComment *C);
  473. void visitInlineCommandComment(const InlineCommandComment *C);
  474. void visitHTMLStartTagComment(const HTMLStartTagComment *C);
  475. void visitHTMLEndTagComment(const HTMLEndTagComment *C);
  476. // Block content.
  477. void visitParagraphComment(const ParagraphComment *C);
  478. void appendParagraphCommentWithKind(const ParagraphComment *C,
  479. StringRef Kind);
  480. void visitBlockCommandComment(const BlockCommandComment *C);
  481. void visitParamCommandComment(const ParamCommandComment *C);
  482. void visitTParamCommandComment(const TParamCommandComment *C);
  483. void visitVerbatimBlockComment(const VerbatimBlockComment *C);
  484. void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
  485. void visitVerbatimLineComment(const VerbatimLineComment *C);
  486. void visitFullComment(const FullComment *C);
  487. // Helpers.
  488. void appendToResultWithXMLEscaping(StringRef S);
  489. void appendToResultWithCDATAEscaping(StringRef S);
  490. void formatTextOfDeclaration(const DeclInfo *DI,
  491. SmallString<128> &Declaration);
  492. private:
  493. const FullComment *FC;
  494. /// Output stream for XML.
  495. llvm::raw_svector_ostream Result;
  496. const CommandTraits &Traits;
  497. const SourceManager &SM;
  498. SimpleFormatContext &FormatRewriterContext;
  499. unsigned FormatInMemoryUniqueId;
  500. };
  501. void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
  502. SmallVectorImpl<char> &Str) {
  503. ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
  504. const LangOptions &LangOpts = Context.getLangOpts();
  505. llvm::raw_svector_ostream OS(Str);
  506. PrintingPolicy PPolicy(LangOpts);
  507. PPolicy.PolishForDeclaration = true;
  508. PPolicy.TerseOutput = true;
  509. ThisDecl->CurrentDecl->print(OS, PPolicy,
  510. /*Indentation*/0, /*PrintInstantiation*/false);
  511. }
  512. void CommentASTToXMLConverter::formatTextOfDeclaration(
  513. const DeclInfo *DI, SmallString<128> &Declaration) {
  514. // FIXME. formatting API expects null terminated input string.
  515. // There might be more efficient way of doing this.
  516. std::string StringDecl = Declaration.str();
  517. // Formatter specific code.
  518. // Form a unique in memory buffer name.
  519. SmallString<128> filename;
  520. filename += "xmldecl";
  521. filename += llvm::utostr(FormatInMemoryUniqueId);
  522. filename += ".xd";
  523. FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
  524. SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID)
  525. .getLocWithOffset(0);
  526. unsigned Length = Declaration.size();
  527. tooling::Replacements Replace = reformat(
  528. format::getLLVMStyle(), FormatRewriterContext.Sources, ID,
  529. CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
  530. applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
  531. Declaration = FormatRewriterContext.getRewrittenText(ID);
  532. }
  533. } // end unnamed namespace
  534. void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
  535. appendToResultWithXMLEscaping(C->getText());
  536. }
  537. void CommentASTToXMLConverter::visitInlineCommandComment(
  538. const InlineCommandComment *C) {
  539. // Nothing to render if no arguments supplied.
  540. if (C->getNumArgs() == 0)
  541. return;
  542. // Nothing to render if argument is empty.
  543. StringRef Arg0 = C->getArgText(0);
  544. if (Arg0.empty())
  545. return;
  546. switch (C->getRenderKind()) {
  547. case InlineCommandComment::RenderNormal:
  548. for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
  549. appendToResultWithXMLEscaping(C->getArgText(i));
  550. Result << " ";
  551. }
  552. return;
  553. case InlineCommandComment::RenderBold:
  554. assert(C->getNumArgs() == 1);
  555. Result << "<bold>";
  556. appendToResultWithXMLEscaping(Arg0);
  557. Result << "</bold>";
  558. return;
  559. case InlineCommandComment::RenderMonospaced:
  560. assert(C->getNumArgs() == 1);
  561. Result << "<monospaced>";
  562. appendToResultWithXMLEscaping(Arg0);
  563. Result << "</monospaced>";
  564. return;
  565. case InlineCommandComment::RenderEmphasized:
  566. assert(C->getNumArgs() == 1);
  567. Result << "<emphasized>";
  568. appendToResultWithXMLEscaping(Arg0);
  569. Result << "</emphasized>";
  570. return;
  571. }
  572. }
  573. void CommentASTToXMLConverter::visitHTMLStartTagComment(
  574. const HTMLStartTagComment *C) {
  575. Result << "<rawHTML";
  576. if (C->isMalformed())
  577. Result << " isMalformed=\"1\"";
  578. Result << ">";
  579. {
  580. SmallString<32> Tag;
  581. {
  582. llvm::raw_svector_ostream TagOS(Tag);
  583. printHTMLStartTagComment(C, TagOS);
  584. }
  585. appendToResultWithCDATAEscaping(Tag);
  586. }
  587. Result << "</rawHTML>";
  588. }
  589. void
  590. CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
  591. Result << "<rawHTML";
  592. if (C->isMalformed())
  593. Result << " isMalformed=\"1\"";
  594. Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
  595. }
  596. void
  597. CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
  598. appendParagraphCommentWithKind(C, StringRef());
  599. }
  600. void CommentASTToXMLConverter::appendParagraphCommentWithKind(
  601. const ParagraphComment *C,
  602. StringRef ParagraphKind) {
  603. if (C->isWhitespace())
  604. return;
  605. if (ParagraphKind.empty())
  606. Result << "<Para>";
  607. else
  608. Result << "<Para kind=\"" << ParagraphKind << "\">";
  609. for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
  610. I != E; ++I) {
  611. visit(*I);
  612. }
  613. Result << "</Para>";
  614. }
  615. void CommentASTToXMLConverter::visitBlockCommandComment(
  616. const BlockCommandComment *C) {
  617. StringRef ParagraphKind;
  618. switch (C->getCommandID()) {
  619. case CommandTraits::KCI_attention:
  620. case CommandTraits::KCI_author:
  621. case CommandTraits::KCI_authors:
  622. case CommandTraits::KCI_bug:
  623. case CommandTraits::KCI_copyright:
  624. case CommandTraits::KCI_date:
  625. case CommandTraits::KCI_invariant:
  626. case CommandTraits::KCI_note:
  627. case CommandTraits::KCI_post:
  628. case CommandTraits::KCI_pre:
  629. case CommandTraits::KCI_remark:
  630. case CommandTraits::KCI_remarks:
  631. case CommandTraits::KCI_sa:
  632. case CommandTraits::KCI_see:
  633. case CommandTraits::KCI_since:
  634. case CommandTraits::KCI_todo:
  635. case CommandTraits::KCI_version:
  636. case CommandTraits::KCI_warning:
  637. ParagraphKind = C->getCommandName(Traits);
  638. default:
  639. break;
  640. }
  641. appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
  642. }
  643. void CommentASTToXMLConverter::visitParamCommandComment(
  644. const ParamCommandComment *C) {
  645. Result << "<Parameter><Name>";
  646. appendToResultWithXMLEscaping(C->isParamIndexValid()
  647. ? C->getParamName(FC)
  648. : C->getParamNameAsWritten());
  649. Result << "</Name>";
  650. if (C->isParamIndexValid()) {
  651. if (C->isVarArgParam())
  652. Result << "<IsVarArg />";
  653. else
  654. Result << "<Index>" << C->getParamIndex() << "</Index>";
  655. }
  656. Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
  657. switch (C->getDirection()) {
  658. case ParamCommandComment::In:
  659. Result << "in";
  660. break;
  661. case ParamCommandComment::Out:
  662. Result << "out";
  663. break;
  664. case ParamCommandComment::InOut:
  665. Result << "in,out";
  666. break;
  667. }
  668. Result << "</Direction><Discussion>";
  669. visit(C->getParagraph());
  670. Result << "</Discussion></Parameter>";
  671. }
  672. void CommentASTToXMLConverter::visitTParamCommandComment(
  673. const TParamCommandComment *C) {
  674. Result << "<Parameter><Name>";
  675. appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
  676. : C->getParamNameAsWritten());
  677. Result << "</Name>";
  678. if (C->isPositionValid() && C->getDepth() == 1) {
  679. Result << "<Index>" << C->getIndex(0) << "</Index>";
  680. }
  681. Result << "<Discussion>";
  682. visit(C->getParagraph());
  683. Result << "</Discussion></Parameter>";
  684. }
  685. void CommentASTToXMLConverter::visitVerbatimBlockComment(
  686. const VerbatimBlockComment *C) {
  687. unsigned NumLines = C->getNumLines();
  688. if (NumLines == 0)
  689. return;
  690. switch (C->getCommandID()) {
  691. case CommandTraits::KCI_code:
  692. Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
  693. break;
  694. default:
  695. Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
  696. break;
  697. }
  698. for (unsigned i = 0; i != NumLines; ++i) {
  699. appendToResultWithXMLEscaping(C->getText(i));
  700. if (i + 1 != NumLines)
  701. Result << '\n';
  702. }
  703. Result << "</Verbatim>";
  704. }
  705. void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
  706. const VerbatimBlockLineComment *C) {
  707. llvm_unreachable("should not see this AST node");
  708. }
  709. void CommentASTToXMLConverter::visitVerbatimLineComment(
  710. const VerbatimLineComment *C) {
  711. Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
  712. appendToResultWithXMLEscaping(C->getText());
  713. Result << "</Verbatim>";
  714. }
  715. void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
  716. FullCommentParts Parts(C, Traits);
  717. const DeclInfo *DI = C->getDeclInfo();
  718. StringRef RootEndTag;
  719. if (DI) {
  720. switch (DI->getKind()) {
  721. case DeclInfo::OtherKind:
  722. RootEndTag = "</Other>";
  723. Result << "<Other";
  724. break;
  725. case DeclInfo::FunctionKind:
  726. RootEndTag = "</Function>";
  727. Result << "<Function";
  728. switch (DI->TemplateKind) {
  729. case DeclInfo::NotTemplate:
  730. break;
  731. case DeclInfo::Template:
  732. Result << " templateKind=\"template\"";
  733. break;
  734. case DeclInfo::TemplateSpecialization:
  735. Result << " templateKind=\"specialization\"";
  736. break;
  737. case DeclInfo::TemplatePartialSpecialization:
  738. llvm_unreachable("partial specializations of functions "
  739. "are not allowed in C++");
  740. }
  741. if (DI->IsInstanceMethod)
  742. Result << " isInstanceMethod=\"1\"";
  743. if (DI->IsClassMethod)
  744. Result << " isClassMethod=\"1\"";
  745. break;
  746. case DeclInfo::ClassKind:
  747. RootEndTag = "</Class>";
  748. Result << "<Class";
  749. switch (DI->TemplateKind) {
  750. case DeclInfo::NotTemplate:
  751. break;
  752. case DeclInfo::Template:
  753. Result << " templateKind=\"template\"";
  754. break;
  755. case DeclInfo::TemplateSpecialization:
  756. Result << " templateKind=\"specialization\"";
  757. break;
  758. case DeclInfo::TemplatePartialSpecialization:
  759. Result << " templateKind=\"partialSpecialization\"";
  760. break;
  761. }
  762. break;
  763. case DeclInfo::VariableKind:
  764. RootEndTag = "</Variable>";
  765. Result << "<Variable";
  766. break;
  767. case DeclInfo::NamespaceKind:
  768. RootEndTag = "</Namespace>";
  769. Result << "<Namespace";
  770. break;
  771. case DeclInfo::TypedefKind:
  772. RootEndTag = "</Typedef>";
  773. Result << "<Typedef";
  774. break;
  775. case DeclInfo::EnumKind:
  776. RootEndTag = "</Enum>";
  777. Result << "<Enum";
  778. break;
  779. }
  780. {
  781. // Print line and column number.
  782. SourceLocation Loc = DI->CurrentDecl->getLocation();
  783. std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
  784. FileID FID = LocInfo.first;
  785. unsigned FileOffset = LocInfo.second;
  786. if (!FID.isInvalid()) {
  787. if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
  788. Result << " file=\"";
  789. appendToResultWithXMLEscaping(FE->getName());
  790. Result << "\"";
  791. }
  792. Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
  793. << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
  794. << "\"";
  795. }
  796. }
  797. // Finish the root tag.
  798. Result << ">";
  799. bool FoundName = false;
  800. if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
  801. if (DeclarationName DeclName = ND->getDeclName()) {
  802. Result << "<Name>";
  803. std::string Name = DeclName.getAsString();
  804. appendToResultWithXMLEscaping(Name);
  805. FoundName = true;
  806. Result << "</Name>";
  807. }
  808. }
  809. if (!FoundName)
  810. Result << "<Name>&lt;anonymous&gt;</Name>";
  811. {
  812. // Print USR.
  813. SmallString<128> USR;
  814. generateUSRForDecl(DI->CommentDecl, USR);
  815. if (!USR.empty()) {
  816. Result << "<USR>";
  817. appendToResultWithXMLEscaping(USR);
  818. Result << "</USR>";
  819. }
  820. }
  821. } else {
  822. // No DeclInfo -- just emit some root tag and name tag.
  823. RootEndTag = "</Other>";
  824. Result << "<Other><Name>unknown</Name>";
  825. }
  826. if (Parts.Headerfile) {
  827. Result << "<Headerfile>";
  828. visit(Parts.Headerfile);
  829. Result << "</Headerfile>";
  830. }
  831. {
  832. // Pretty-print the declaration.
  833. Result << "<Declaration>";
  834. SmallString<128> Declaration;
  835. getSourceTextOfDeclaration(DI, Declaration);
  836. formatTextOfDeclaration(DI, Declaration);
  837. appendToResultWithXMLEscaping(Declaration);
  838. Result << "</Declaration>";
  839. }
  840. bool FirstParagraphIsBrief = false;
  841. if (Parts.Brief) {
  842. Result << "<Abstract>";
  843. visit(Parts.Brief);
  844. Result << "</Abstract>";
  845. } else if (Parts.FirstParagraph) {
  846. Result << "<Abstract>";
  847. visit(Parts.FirstParagraph);
  848. Result << "</Abstract>";
  849. FirstParagraphIsBrief = true;
  850. }
  851. if (Parts.TParams.size() != 0) {
  852. Result << "<TemplateParameters>";
  853. for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
  854. visit(Parts.TParams[i]);
  855. Result << "</TemplateParameters>";
  856. }
  857. if (Parts.Params.size() != 0) {
  858. Result << "<Parameters>";
  859. for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
  860. visit(Parts.Params[i]);
  861. Result << "</Parameters>";
  862. }
  863. if (Parts.Exceptions.size() != 0) {
  864. Result << "<Exceptions>";
  865. for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
  866. visit(Parts.Exceptions[i]);
  867. Result << "</Exceptions>";
  868. }
  869. if (Parts.Returns.size() != 0) {
  870. Result << "<ResultDiscussion>";
  871. for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
  872. visit(Parts.Returns[i]);
  873. Result << "</ResultDiscussion>";
  874. }
  875. if (DI->CommentDecl->hasAttrs()) {
  876. const AttrVec &Attrs = DI->CommentDecl->getAttrs();
  877. for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
  878. const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
  879. if (!AA) {
  880. if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
  881. if (DA->getMessage().empty())
  882. Result << "<Deprecated/>";
  883. else {
  884. Result << "<Deprecated>";
  885. appendToResultWithXMLEscaping(DA->getMessage());
  886. Result << "</Deprecated>";
  887. }
  888. }
  889. else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
  890. if (UA->getMessage().empty())
  891. Result << "<Unavailable/>";
  892. else {
  893. Result << "<Unavailable>";
  894. appendToResultWithXMLEscaping(UA->getMessage());
  895. Result << "</Unavailable>";
  896. }
  897. }
  898. continue;
  899. }
  900. // 'availability' attribute.
  901. Result << "<Availability";
  902. StringRef Distribution;
  903. if (AA->getPlatform()) {
  904. Distribution = AvailabilityAttr::getPrettyPlatformName(
  905. AA->getPlatform()->getName());
  906. if (Distribution.empty())
  907. Distribution = AA->getPlatform()->getName();
  908. }
  909. Result << " distribution=\"" << Distribution << "\">";
  910. VersionTuple IntroducedInVersion = AA->getIntroduced();
  911. if (!IntroducedInVersion.empty()) {
  912. Result << "<IntroducedInVersion>"
  913. << IntroducedInVersion.getAsString()
  914. << "</IntroducedInVersion>";
  915. }
  916. VersionTuple DeprecatedInVersion = AA->getDeprecated();
  917. if (!DeprecatedInVersion.empty()) {
  918. Result << "<DeprecatedInVersion>"
  919. << DeprecatedInVersion.getAsString()
  920. << "</DeprecatedInVersion>";
  921. }
  922. VersionTuple RemovedAfterVersion = AA->getObsoleted();
  923. if (!RemovedAfterVersion.empty()) {
  924. Result << "<RemovedAfterVersion>"
  925. << RemovedAfterVersion.getAsString()
  926. << "</RemovedAfterVersion>";
  927. }
  928. StringRef DeprecationSummary = AA->getMessage();
  929. if (!DeprecationSummary.empty()) {
  930. Result << "<DeprecationSummary>";
  931. appendToResultWithXMLEscaping(DeprecationSummary);
  932. Result << "</DeprecationSummary>";
  933. }
  934. if (AA->getUnavailable())
  935. Result << "<Unavailable/>";
  936. Result << "</Availability>";
  937. }
  938. }
  939. {
  940. bool StartTagEmitted = false;
  941. for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
  942. const Comment *C = Parts.MiscBlocks[i];
  943. if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
  944. continue;
  945. if (!StartTagEmitted) {
  946. Result << "<Discussion>";
  947. StartTagEmitted = true;
  948. }
  949. visit(C);
  950. }
  951. if (StartTagEmitted)
  952. Result << "</Discussion>";
  953. }
  954. Result << RootEndTag;
  955. Result.flush();
  956. }
  957. void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
  958. for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
  959. const char C = *I;
  960. switch (C) {
  961. case '&':
  962. Result << "&amp;";
  963. break;
  964. case '<':
  965. Result << "&lt;";
  966. break;
  967. case '>':
  968. Result << "&gt;";
  969. break;
  970. case '"':
  971. Result << "&quot;";
  972. break;
  973. case '\'':
  974. Result << "&apos;";
  975. break;
  976. default:
  977. Result << C;
  978. break;
  979. }
  980. }
  981. }
  982. void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
  983. if (S.empty())
  984. return;
  985. Result << "<![CDATA[";
  986. while (!S.empty()) {
  987. size_t Pos = S.find("]]>");
  988. if (Pos == 0) {
  989. Result << "]]]]><![CDATA[>";
  990. S = S.drop_front(3);
  991. continue;
  992. }
  993. if (Pos == StringRef::npos)
  994. Pos = S.size();
  995. Result << S.substr(0, Pos);
  996. S = S.drop_front(Pos);
  997. }
  998. Result << "]]>";
  999. }
  1000. CommentToXMLConverter::CommentToXMLConverter() : FormatInMemoryUniqueId(0) {}
  1001. CommentToXMLConverter::~CommentToXMLConverter() {}
  1002. void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
  1003. SmallVectorImpl<char> &HTML,
  1004. const ASTContext &Context) {
  1005. CommentASTToHTMLConverter Converter(FC, HTML,
  1006. Context.getCommentCommandTraits());
  1007. Converter.visit(FC);
  1008. }
  1009. void CommentToXMLConverter::convertHTMLTagNodeToText(
  1010. const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
  1011. const ASTContext &Context) {
  1012. CommentASTToHTMLConverter Converter(nullptr, Text,
  1013. Context.getCommentCommandTraits());
  1014. Converter.visit(HTC);
  1015. }
  1016. void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
  1017. SmallVectorImpl<char> &XML,
  1018. const ASTContext &Context) {
  1019. if (!FormatContext || (FormatInMemoryUniqueId % 1000) == 0) {
  1020. // Create a new format context, or re-create it after some number of
  1021. // iterations, so the buffers don't grow too large.
  1022. FormatContext.reset(new SimpleFormatContext(Context.getLangOpts()));
  1023. }
  1024. CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
  1025. Context.getSourceManager(), *FormatContext,
  1026. FormatInMemoryUniqueId++);
  1027. Converter.visit(FC);
  1028. }