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