CommentToXML.cpp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162
  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. }
  419. void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
  420. const ParagraphComment *C) {
  421. if (!C)
  422. return;
  423. for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
  424. I != E; ++I) {
  425. visit(*I);
  426. }
  427. }
  428. void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
  429. for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
  430. const char C = *I;
  431. switch (C) {
  432. case '&':
  433. Result << "&amp;";
  434. break;
  435. case '<':
  436. Result << "&lt;";
  437. break;
  438. case '>':
  439. Result << "&gt;";
  440. break;
  441. case '"':
  442. Result << "&quot;";
  443. break;
  444. case '\'':
  445. Result << "&#39;";
  446. break;
  447. case '/':
  448. Result << "&#47;";
  449. break;
  450. default:
  451. Result << C;
  452. break;
  453. }
  454. }
  455. }
  456. namespace {
  457. class CommentASTToXMLConverter :
  458. public ConstCommentVisitor<CommentASTToXMLConverter> {
  459. public:
  460. /// \param Str accumulator for XML.
  461. CommentASTToXMLConverter(const FullComment *FC,
  462. SmallVectorImpl<char> &Str,
  463. const CommandTraits &Traits,
  464. const SourceManager &SM,
  465. SimpleFormatContext &SFC,
  466. unsigned FUID) :
  467. FC(FC), Result(Str), Traits(Traits), SM(SM),
  468. FormatRewriterContext(SFC),
  469. FormatInMemoryUniqueId(FUID) { }
  470. // Inline content.
  471. void visitTextComment(const TextComment *C);
  472. void visitInlineCommandComment(const InlineCommandComment *C);
  473. void visitHTMLStartTagComment(const HTMLStartTagComment *C);
  474. void visitHTMLEndTagComment(const HTMLEndTagComment *C);
  475. // Block content.
  476. void visitParagraphComment(const ParagraphComment *C);
  477. void appendParagraphCommentWithKind(const ParagraphComment *C,
  478. StringRef Kind);
  479. void visitBlockCommandComment(const BlockCommandComment *C);
  480. void visitParamCommandComment(const ParamCommandComment *C);
  481. void visitTParamCommandComment(const TParamCommandComment *C);
  482. void visitVerbatimBlockComment(const VerbatimBlockComment *C);
  483. void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
  484. void visitVerbatimLineComment(const VerbatimLineComment *C);
  485. void visitFullComment(const FullComment *C);
  486. // Helpers.
  487. void appendToResultWithXMLEscaping(StringRef S);
  488. void appendToResultWithCDATAEscaping(StringRef S);
  489. void formatTextOfDeclaration(const DeclInfo *DI,
  490. SmallString<128> &Declaration);
  491. private:
  492. const FullComment *FC;
  493. /// Output stream for XML.
  494. llvm::raw_svector_ostream Result;
  495. const CommandTraits &Traits;
  496. const SourceManager &SM;
  497. SimpleFormatContext &FormatRewriterContext;
  498. unsigned FormatInMemoryUniqueId;
  499. };
  500. void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
  501. SmallVectorImpl<char> &Str) {
  502. ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
  503. const LangOptions &LangOpts = Context.getLangOpts();
  504. llvm::raw_svector_ostream OS(Str);
  505. PrintingPolicy PPolicy(LangOpts);
  506. PPolicy.PolishForDeclaration = true;
  507. PPolicy.TerseOutput = true;
  508. ThisDecl->CurrentDecl->print(OS, PPolicy,
  509. /*Indentation*/0, /*PrintInstantiation*/false);
  510. }
  511. void CommentASTToXMLConverter::formatTextOfDeclaration(
  512. const DeclInfo *DI, SmallString<128> &Declaration) {
  513. // Formatting API expects null terminated input string.
  514. StringRef StringDecl(Declaration.c_str(), Declaration.size());
  515. // Formatter specific code.
  516. // Form a unique in memory buffer name.
  517. SmallString<128> filename;
  518. filename += "xmldecl";
  519. filename += llvm::utostr(FormatInMemoryUniqueId);
  520. filename += ".xd";
  521. FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
  522. SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID)
  523. .getLocWithOffset(0);
  524. unsigned Length = Declaration.size();
  525. tooling::Replacements Replace = reformat(
  526. format::getLLVMStyle(), FormatRewriterContext.Sources, ID,
  527. CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
  528. applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
  529. Declaration = FormatRewriterContext.getRewrittenText(ID);
  530. }
  531. } // end unnamed namespace
  532. void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
  533. appendToResultWithXMLEscaping(C->getText());
  534. }
  535. void CommentASTToXMLConverter::visitInlineCommandComment(
  536. const InlineCommandComment *C) {
  537. // Nothing to render if no arguments supplied.
  538. if (C->getNumArgs() == 0)
  539. return;
  540. // Nothing to render if argument is empty.
  541. StringRef Arg0 = C->getArgText(0);
  542. if (Arg0.empty())
  543. return;
  544. switch (C->getRenderKind()) {
  545. case InlineCommandComment::RenderNormal:
  546. for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
  547. appendToResultWithXMLEscaping(C->getArgText(i));
  548. Result << " ";
  549. }
  550. return;
  551. case InlineCommandComment::RenderBold:
  552. assert(C->getNumArgs() == 1);
  553. Result << "<bold>";
  554. appendToResultWithXMLEscaping(Arg0);
  555. Result << "</bold>";
  556. return;
  557. case InlineCommandComment::RenderMonospaced:
  558. assert(C->getNumArgs() == 1);
  559. Result << "<monospaced>";
  560. appendToResultWithXMLEscaping(Arg0);
  561. Result << "</monospaced>";
  562. return;
  563. case InlineCommandComment::RenderEmphasized:
  564. assert(C->getNumArgs() == 1);
  565. Result << "<emphasized>";
  566. appendToResultWithXMLEscaping(Arg0);
  567. Result << "</emphasized>";
  568. return;
  569. }
  570. }
  571. void CommentASTToXMLConverter::visitHTMLStartTagComment(
  572. const HTMLStartTagComment *C) {
  573. Result << "<rawHTML";
  574. if (C->isMalformed())
  575. Result << " isMalformed=\"1\"";
  576. Result << ">";
  577. {
  578. SmallString<32> Tag;
  579. {
  580. llvm::raw_svector_ostream TagOS(Tag);
  581. printHTMLStartTagComment(C, TagOS);
  582. }
  583. appendToResultWithCDATAEscaping(Tag);
  584. }
  585. Result << "</rawHTML>";
  586. }
  587. void
  588. CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
  589. Result << "<rawHTML";
  590. if (C->isMalformed())
  591. Result << " isMalformed=\"1\"";
  592. Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
  593. }
  594. void
  595. CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
  596. appendParagraphCommentWithKind(C, StringRef());
  597. }
  598. void CommentASTToXMLConverter::appendParagraphCommentWithKind(
  599. const ParagraphComment *C,
  600. StringRef ParagraphKind) {
  601. if (C->isWhitespace())
  602. return;
  603. if (ParagraphKind.empty())
  604. Result << "<Para>";
  605. else
  606. Result << "<Para kind=\"" << ParagraphKind << "\">";
  607. for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
  608. I != E; ++I) {
  609. visit(*I);
  610. }
  611. Result << "</Para>";
  612. }
  613. void CommentASTToXMLConverter::visitBlockCommandComment(
  614. const BlockCommandComment *C) {
  615. StringRef ParagraphKind;
  616. switch (C->getCommandID()) {
  617. case CommandTraits::KCI_attention:
  618. case CommandTraits::KCI_author:
  619. case CommandTraits::KCI_authors:
  620. case CommandTraits::KCI_bug:
  621. case CommandTraits::KCI_copyright:
  622. case CommandTraits::KCI_date:
  623. case CommandTraits::KCI_invariant:
  624. case CommandTraits::KCI_note:
  625. case CommandTraits::KCI_post:
  626. case CommandTraits::KCI_pre:
  627. case CommandTraits::KCI_remark:
  628. case CommandTraits::KCI_remarks:
  629. case CommandTraits::KCI_sa:
  630. case CommandTraits::KCI_see:
  631. case CommandTraits::KCI_since:
  632. case CommandTraits::KCI_todo:
  633. case CommandTraits::KCI_version:
  634. case CommandTraits::KCI_warning:
  635. ParagraphKind = C->getCommandName(Traits);
  636. default:
  637. break;
  638. }
  639. appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
  640. }
  641. void CommentASTToXMLConverter::visitParamCommandComment(
  642. const ParamCommandComment *C) {
  643. Result << "<Parameter><Name>";
  644. appendToResultWithXMLEscaping(C->isParamIndexValid()
  645. ? C->getParamName(FC)
  646. : C->getParamNameAsWritten());
  647. Result << "</Name>";
  648. if (C->isParamIndexValid()) {
  649. if (C->isVarArgParam())
  650. Result << "<IsVarArg />";
  651. else
  652. Result << "<Index>" << C->getParamIndex() << "</Index>";
  653. }
  654. Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
  655. switch (C->getDirection()) {
  656. case ParamCommandComment::In:
  657. Result << "in";
  658. break;
  659. case ParamCommandComment::Out:
  660. Result << "out";
  661. break;
  662. case ParamCommandComment::InOut:
  663. Result << "in,out";
  664. break;
  665. }
  666. Result << "</Direction><Discussion>";
  667. visit(C->getParagraph());
  668. Result << "</Discussion></Parameter>";
  669. }
  670. void CommentASTToXMLConverter::visitTParamCommandComment(
  671. const TParamCommandComment *C) {
  672. Result << "<Parameter><Name>";
  673. appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
  674. : C->getParamNameAsWritten());
  675. Result << "</Name>";
  676. if (C->isPositionValid() && C->getDepth() == 1) {
  677. Result << "<Index>" << C->getIndex(0) << "</Index>";
  678. }
  679. Result << "<Discussion>";
  680. visit(C->getParagraph());
  681. Result << "</Discussion></Parameter>";
  682. }
  683. void CommentASTToXMLConverter::visitVerbatimBlockComment(
  684. const VerbatimBlockComment *C) {
  685. unsigned NumLines = C->getNumLines();
  686. if (NumLines == 0)
  687. return;
  688. switch (C->getCommandID()) {
  689. case CommandTraits::KCI_code:
  690. Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
  691. break;
  692. default:
  693. Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
  694. break;
  695. }
  696. for (unsigned i = 0; i != NumLines; ++i) {
  697. appendToResultWithXMLEscaping(C->getText(i));
  698. if (i + 1 != NumLines)
  699. Result << '\n';
  700. }
  701. Result << "</Verbatim>";
  702. }
  703. void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
  704. const VerbatimBlockLineComment *C) {
  705. llvm_unreachable("should not see this AST node");
  706. }
  707. void CommentASTToXMLConverter::visitVerbatimLineComment(
  708. const VerbatimLineComment *C) {
  709. Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
  710. appendToResultWithXMLEscaping(C->getText());
  711. Result << "</Verbatim>";
  712. }
  713. void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
  714. FullCommentParts Parts(C, Traits);
  715. const DeclInfo *DI = C->getDeclInfo();
  716. StringRef RootEndTag;
  717. if (DI) {
  718. switch (DI->getKind()) {
  719. case DeclInfo::OtherKind:
  720. RootEndTag = "</Other>";
  721. Result << "<Other";
  722. break;
  723. case DeclInfo::FunctionKind:
  724. RootEndTag = "</Function>";
  725. Result << "<Function";
  726. switch (DI->TemplateKind) {
  727. case DeclInfo::NotTemplate:
  728. break;
  729. case DeclInfo::Template:
  730. Result << " templateKind=\"template\"";
  731. break;
  732. case DeclInfo::TemplateSpecialization:
  733. Result << " templateKind=\"specialization\"";
  734. break;
  735. case DeclInfo::TemplatePartialSpecialization:
  736. llvm_unreachable("partial specializations of functions "
  737. "are not allowed in C++");
  738. }
  739. if (DI->IsInstanceMethod)
  740. Result << " isInstanceMethod=\"1\"";
  741. if (DI->IsClassMethod)
  742. Result << " isClassMethod=\"1\"";
  743. break;
  744. case DeclInfo::ClassKind:
  745. RootEndTag = "</Class>";
  746. Result << "<Class";
  747. switch (DI->TemplateKind) {
  748. case DeclInfo::NotTemplate:
  749. break;
  750. case DeclInfo::Template:
  751. Result << " templateKind=\"template\"";
  752. break;
  753. case DeclInfo::TemplateSpecialization:
  754. Result << " templateKind=\"specialization\"";
  755. break;
  756. case DeclInfo::TemplatePartialSpecialization:
  757. Result << " templateKind=\"partialSpecialization\"";
  758. break;
  759. }
  760. break;
  761. case DeclInfo::VariableKind:
  762. RootEndTag = "</Variable>";
  763. Result << "<Variable";
  764. break;
  765. case DeclInfo::NamespaceKind:
  766. RootEndTag = "</Namespace>";
  767. Result << "<Namespace";
  768. break;
  769. case DeclInfo::TypedefKind:
  770. RootEndTag = "</Typedef>";
  771. Result << "<Typedef";
  772. break;
  773. case DeclInfo::EnumKind:
  774. RootEndTag = "</Enum>";
  775. Result << "<Enum";
  776. break;
  777. }
  778. {
  779. // Print line and column number.
  780. SourceLocation Loc = DI->CurrentDecl->getLocation();
  781. std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
  782. FileID FID = LocInfo.first;
  783. unsigned FileOffset = LocInfo.second;
  784. if (FID.isValid()) {
  785. if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
  786. Result << " file=\"";
  787. appendToResultWithXMLEscaping(FE->getName());
  788. Result << "\"";
  789. }
  790. Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
  791. << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
  792. << "\"";
  793. }
  794. }
  795. // Finish the root tag.
  796. Result << ">";
  797. bool FoundName = false;
  798. if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
  799. if (DeclarationName DeclName = ND->getDeclName()) {
  800. Result << "<Name>";
  801. std::string Name = DeclName.getAsString();
  802. appendToResultWithXMLEscaping(Name);
  803. FoundName = true;
  804. Result << "</Name>";
  805. }
  806. }
  807. if (!FoundName)
  808. Result << "<Name>&lt;anonymous&gt;</Name>";
  809. {
  810. // Print USR.
  811. SmallString<128> USR;
  812. generateUSRForDecl(DI->CommentDecl, USR);
  813. if (!USR.empty()) {
  814. Result << "<USR>";
  815. appendToResultWithXMLEscaping(USR);
  816. Result << "</USR>";
  817. }
  818. }
  819. } else {
  820. // No DeclInfo -- just emit some root tag and name tag.
  821. RootEndTag = "</Other>";
  822. Result << "<Other><Name>unknown</Name>";
  823. }
  824. if (Parts.Headerfile) {
  825. Result << "<Headerfile>";
  826. visit(Parts.Headerfile);
  827. Result << "</Headerfile>";
  828. }
  829. {
  830. // Pretty-print the declaration.
  831. Result << "<Declaration>";
  832. SmallString<128> Declaration;
  833. getSourceTextOfDeclaration(DI, Declaration);
  834. formatTextOfDeclaration(DI, Declaration);
  835. appendToResultWithXMLEscaping(Declaration);
  836. Result << "</Declaration>";
  837. }
  838. bool FirstParagraphIsBrief = false;
  839. if (Parts.Brief) {
  840. Result << "<Abstract>";
  841. visit(Parts.Brief);
  842. Result << "</Abstract>";
  843. } else if (Parts.FirstParagraph) {
  844. Result << "<Abstract>";
  845. visit(Parts.FirstParagraph);
  846. Result << "</Abstract>";
  847. FirstParagraphIsBrief = true;
  848. }
  849. if (Parts.TParams.size() != 0) {
  850. Result << "<TemplateParameters>";
  851. for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
  852. visit(Parts.TParams[i]);
  853. Result << "</TemplateParameters>";
  854. }
  855. if (Parts.Params.size() != 0) {
  856. Result << "<Parameters>";
  857. for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
  858. visit(Parts.Params[i]);
  859. Result << "</Parameters>";
  860. }
  861. if (Parts.Exceptions.size() != 0) {
  862. Result << "<Exceptions>";
  863. for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
  864. visit(Parts.Exceptions[i]);
  865. Result << "</Exceptions>";
  866. }
  867. if (Parts.Returns.size() != 0) {
  868. Result << "<ResultDiscussion>";
  869. for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
  870. visit(Parts.Returns[i]);
  871. Result << "</ResultDiscussion>";
  872. }
  873. if (DI->CommentDecl->hasAttrs()) {
  874. const AttrVec &Attrs = DI->CommentDecl->getAttrs();
  875. for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
  876. const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
  877. if (!AA) {
  878. if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
  879. if (DA->getMessage().empty())
  880. Result << "<Deprecated/>";
  881. else {
  882. Result << "<Deprecated>";
  883. appendToResultWithXMLEscaping(DA->getMessage());
  884. Result << "</Deprecated>";
  885. }
  886. }
  887. else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
  888. if (UA->getMessage().empty())
  889. Result << "<Unavailable/>";
  890. else {
  891. Result << "<Unavailable>";
  892. appendToResultWithXMLEscaping(UA->getMessage());
  893. Result << "</Unavailable>";
  894. }
  895. }
  896. continue;
  897. }
  898. // 'availability' attribute.
  899. Result << "<Availability";
  900. StringRef Distribution;
  901. if (AA->getPlatform()) {
  902. Distribution = AvailabilityAttr::getPrettyPlatformName(
  903. AA->getPlatform()->getName());
  904. if (Distribution.empty())
  905. Distribution = AA->getPlatform()->getName();
  906. }
  907. Result << " distribution=\"" << Distribution << "\">";
  908. VersionTuple IntroducedInVersion = AA->getIntroduced();
  909. if (!IntroducedInVersion.empty()) {
  910. Result << "<IntroducedInVersion>"
  911. << IntroducedInVersion.getAsString()
  912. << "</IntroducedInVersion>";
  913. }
  914. VersionTuple DeprecatedInVersion = AA->getDeprecated();
  915. if (!DeprecatedInVersion.empty()) {
  916. Result << "<DeprecatedInVersion>"
  917. << DeprecatedInVersion.getAsString()
  918. << "</DeprecatedInVersion>";
  919. }
  920. VersionTuple RemovedAfterVersion = AA->getObsoleted();
  921. if (!RemovedAfterVersion.empty()) {
  922. Result << "<RemovedAfterVersion>"
  923. << RemovedAfterVersion.getAsString()
  924. << "</RemovedAfterVersion>";
  925. }
  926. StringRef DeprecationSummary = AA->getMessage();
  927. if (!DeprecationSummary.empty()) {
  928. Result << "<DeprecationSummary>";
  929. appendToResultWithXMLEscaping(DeprecationSummary);
  930. Result << "</DeprecationSummary>";
  931. }
  932. if (AA->getUnavailable())
  933. Result << "<Unavailable/>";
  934. Result << "</Availability>";
  935. }
  936. }
  937. {
  938. bool StartTagEmitted = false;
  939. for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
  940. const Comment *C = Parts.MiscBlocks[i];
  941. if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
  942. continue;
  943. if (!StartTagEmitted) {
  944. Result << "<Discussion>";
  945. StartTagEmitted = true;
  946. }
  947. visit(C);
  948. }
  949. if (StartTagEmitted)
  950. Result << "</Discussion>";
  951. }
  952. Result << RootEndTag;
  953. }
  954. void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
  955. for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
  956. const char C = *I;
  957. switch (C) {
  958. case '&':
  959. Result << "&amp;";
  960. break;
  961. case '<':
  962. Result << "&lt;";
  963. break;
  964. case '>':
  965. Result << "&gt;";
  966. break;
  967. case '"':
  968. Result << "&quot;";
  969. break;
  970. case '\'':
  971. Result << "&apos;";
  972. break;
  973. default:
  974. Result << C;
  975. break;
  976. }
  977. }
  978. }
  979. void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
  980. if (S.empty())
  981. return;
  982. Result << "<![CDATA[";
  983. while (!S.empty()) {
  984. size_t Pos = S.find("]]>");
  985. if (Pos == 0) {
  986. Result << "]]]]><![CDATA[>";
  987. S = S.drop_front(3);
  988. continue;
  989. }
  990. if (Pos == StringRef::npos)
  991. Pos = S.size();
  992. Result << S.substr(0, Pos);
  993. S = S.drop_front(Pos);
  994. }
  995. Result << "]]>";
  996. }
  997. CommentToXMLConverter::CommentToXMLConverter() : FormatInMemoryUniqueId(0) {}
  998. CommentToXMLConverter::~CommentToXMLConverter() {}
  999. void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
  1000. SmallVectorImpl<char> &HTML,
  1001. const ASTContext &Context) {
  1002. CommentASTToHTMLConverter Converter(FC, HTML,
  1003. Context.getCommentCommandTraits());
  1004. Converter.visit(FC);
  1005. }
  1006. void CommentToXMLConverter::convertHTMLTagNodeToText(
  1007. const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
  1008. const ASTContext &Context) {
  1009. CommentASTToHTMLConverter Converter(nullptr, Text,
  1010. Context.getCommentCommandTraits());
  1011. Converter.visit(HTC);
  1012. }
  1013. void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
  1014. SmallVectorImpl<char> &XML,
  1015. const ASTContext &Context) {
  1016. if (!FormatContext || (FormatInMemoryUniqueId % 1000) == 0) {
  1017. // Create a new format context, or re-create it after some number of
  1018. // iterations, so the buffers don't grow too large.
  1019. FormatContext.reset(new SimpleFormatContext(Context.getLangOpts()));
  1020. }
  1021. CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
  1022. Context.getSourceManager(), *FormatContext,
  1023. FormatInMemoryUniqueId++);
  1024. Converter.visit(FC);
  1025. }