CommentParser.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. //===--- CommentParser.cpp - Doxygen comment parser -----------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "clang/AST/CommentParser.h"
  9. #include "clang/AST/CommentCommandTraits.h"
  10. #include "clang/AST/CommentDiagnostic.h"
  11. #include "clang/AST/CommentSema.h"
  12. #include "clang/Basic/CharInfo.h"
  13. #include "clang/Basic/SourceManager.h"
  14. #include "llvm/Support/ErrorHandling.h"
  15. namespace clang {
  16. static inline bool isWhitespace(llvm::StringRef S) {
  17. for (StringRef::const_iterator I = S.begin(), E = S.end(); I != E; ++I) {
  18. if (!isWhitespace(*I))
  19. return false;
  20. }
  21. return true;
  22. }
  23. namespace comments {
  24. /// Re-lexes a sequence of tok::text tokens.
  25. class TextTokenRetokenizer {
  26. llvm::BumpPtrAllocator &Allocator;
  27. Parser &P;
  28. /// This flag is set when there are no more tokens we can fetch from lexer.
  29. bool NoMoreInterestingTokens;
  30. /// Token buffer: tokens we have processed and lookahead.
  31. SmallVector<Token, 16> Toks;
  32. /// A position in \c Toks.
  33. struct Position {
  34. const char *BufferStart;
  35. const char *BufferEnd;
  36. const char *BufferPtr;
  37. SourceLocation BufferStartLoc;
  38. unsigned CurToken;
  39. };
  40. /// Current position in Toks.
  41. Position Pos;
  42. bool isEnd() const {
  43. return Pos.CurToken >= Toks.size();
  44. }
  45. /// Sets up the buffer pointers to point to current token.
  46. void setupBuffer() {
  47. assert(!isEnd());
  48. const Token &Tok = Toks[Pos.CurToken];
  49. Pos.BufferStart = Tok.getText().begin();
  50. Pos.BufferEnd = Tok.getText().end();
  51. Pos.BufferPtr = Pos.BufferStart;
  52. Pos.BufferStartLoc = Tok.getLocation();
  53. }
  54. SourceLocation getSourceLocation() const {
  55. const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
  56. return Pos.BufferStartLoc.getLocWithOffset(CharNo);
  57. }
  58. char peek() const {
  59. assert(!isEnd());
  60. assert(Pos.BufferPtr != Pos.BufferEnd);
  61. return *Pos.BufferPtr;
  62. }
  63. void consumeChar() {
  64. assert(!isEnd());
  65. assert(Pos.BufferPtr != Pos.BufferEnd);
  66. Pos.BufferPtr++;
  67. if (Pos.BufferPtr == Pos.BufferEnd) {
  68. Pos.CurToken++;
  69. if (isEnd() && !addToken())
  70. return;
  71. assert(!isEnd());
  72. setupBuffer();
  73. }
  74. }
  75. /// Add a token.
  76. /// Returns true on success, false if there are no interesting tokens to
  77. /// fetch from lexer.
  78. bool addToken() {
  79. if (NoMoreInterestingTokens)
  80. return false;
  81. if (P.Tok.is(tok::newline)) {
  82. // If we see a single newline token between text tokens, skip it.
  83. Token Newline = P.Tok;
  84. P.consumeToken();
  85. if (P.Tok.isNot(tok::text)) {
  86. P.putBack(Newline);
  87. NoMoreInterestingTokens = true;
  88. return false;
  89. }
  90. }
  91. if (P.Tok.isNot(tok::text)) {
  92. NoMoreInterestingTokens = true;
  93. return false;
  94. }
  95. Toks.push_back(P.Tok);
  96. P.consumeToken();
  97. if (Toks.size() == 1)
  98. setupBuffer();
  99. return true;
  100. }
  101. void consumeWhitespace() {
  102. while (!isEnd()) {
  103. if (isWhitespace(peek()))
  104. consumeChar();
  105. else
  106. break;
  107. }
  108. }
  109. void formTokenWithChars(Token &Result,
  110. SourceLocation Loc,
  111. const char *TokBegin,
  112. unsigned TokLength,
  113. StringRef Text) {
  114. Result.setLocation(Loc);
  115. Result.setKind(tok::text);
  116. Result.setLength(TokLength);
  117. #ifndef NDEBUG
  118. Result.TextPtr = "<UNSET>";
  119. Result.IntVal = 7;
  120. #endif
  121. Result.setText(Text);
  122. }
  123. public:
  124. TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
  125. Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
  126. Pos.CurToken = 0;
  127. addToken();
  128. }
  129. /// Extract a word -- sequence of non-whitespace characters.
  130. bool lexWord(Token &Tok) {
  131. if (isEnd())
  132. return false;
  133. Position SavedPos = Pos;
  134. consumeWhitespace();
  135. SmallString<32> WordText;
  136. const char *WordBegin = Pos.BufferPtr;
  137. SourceLocation Loc = getSourceLocation();
  138. while (!isEnd()) {
  139. const char C = peek();
  140. if (!isWhitespace(C)) {
  141. WordText.push_back(C);
  142. consumeChar();
  143. } else
  144. break;
  145. }
  146. const unsigned Length = WordText.size();
  147. if (Length == 0) {
  148. Pos = SavedPos;
  149. return false;
  150. }
  151. char *TextPtr = Allocator.Allocate<char>(Length + 1);
  152. memcpy(TextPtr, WordText.c_str(), Length + 1);
  153. StringRef Text = StringRef(TextPtr, Length);
  154. formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
  155. return true;
  156. }
  157. bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
  158. if (isEnd())
  159. return false;
  160. Position SavedPos = Pos;
  161. consumeWhitespace();
  162. SmallString<32> WordText;
  163. const char *WordBegin = Pos.BufferPtr;
  164. SourceLocation Loc = getSourceLocation();
  165. bool Error = false;
  166. if (!isEnd()) {
  167. const char C = peek();
  168. if (C == OpenDelim) {
  169. WordText.push_back(C);
  170. consumeChar();
  171. } else
  172. Error = true;
  173. }
  174. char C = '\0';
  175. while (!Error && !isEnd()) {
  176. C = peek();
  177. WordText.push_back(C);
  178. consumeChar();
  179. if (C == CloseDelim)
  180. break;
  181. }
  182. if (!Error && C != CloseDelim)
  183. Error = true;
  184. if (Error) {
  185. Pos = SavedPos;
  186. return false;
  187. }
  188. const unsigned Length = WordText.size();
  189. char *TextPtr = Allocator.Allocate<char>(Length + 1);
  190. memcpy(TextPtr, WordText.c_str(), Length + 1);
  191. StringRef Text = StringRef(TextPtr, Length);
  192. formTokenWithChars(Tok, Loc, WordBegin,
  193. Pos.BufferPtr - WordBegin, Text);
  194. return true;
  195. }
  196. /// Put back tokens that we didn't consume.
  197. void putBackLeftoverTokens() {
  198. if (isEnd())
  199. return;
  200. bool HavePartialTok = false;
  201. Token PartialTok;
  202. if (Pos.BufferPtr != Pos.BufferStart) {
  203. formTokenWithChars(PartialTok, getSourceLocation(),
  204. Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
  205. StringRef(Pos.BufferPtr,
  206. Pos.BufferEnd - Pos.BufferPtr));
  207. HavePartialTok = true;
  208. Pos.CurToken++;
  209. }
  210. P.putBack(llvm::ArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
  211. Pos.CurToken = Toks.size();
  212. if (HavePartialTok)
  213. P.putBack(PartialTok);
  214. }
  215. };
  216. Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
  217. const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
  218. const CommandTraits &Traits):
  219. L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
  220. Traits(Traits) {
  221. consumeToken();
  222. }
  223. void Parser::parseParamCommandArgs(ParamCommandComment *PC,
  224. TextTokenRetokenizer &Retokenizer) {
  225. Token Arg;
  226. // Check if argument looks like direction specification: [dir]
  227. // e.g., [in], [out], [in,out]
  228. if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
  229. S.actOnParamCommandDirectionArg(PC,
  230. Arg.getLocation(),
  231. Arg.getEndLocation(),
  232. Arg.getText());
  233. if (Retokenizer.lexWord(Arg))
  234. S.actOnParamCommandParamNameArg(PC,
  235. Arg.getLocation(),
  236. Arg.getEndLocation(),
  237. Arg.getText());
  238. }
  239. void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
  240. TextTokenRetokenizer &Retokenizer) {
  241. Token Arg;
  242. if (Retokenizer.lexWord(Arg))
  243. S.actOnTParamCommandParamNameArg(TPC,
  244. Arg.getLocation(),
  245. Arg.getEndLocation(),
  246. Arg.getText());
  247. }
  248. ArrayRef<Comment::Argument>
  249. Parser::parseCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs) {
  250. auto *Args = new (Allocator.Allocate<Comment::Argument>(NumArgs))
  251. Comment::Argument[NumArgs];
  252. unsigned ParsedArgs = 0;
  253. Token Arg;
  254. while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
  255. Args[ParsedArgs] = Comment::Argument{
  256. SourceRange(Arg.getLocation(), Arg.getEndLocation()), Arg.getText()};
  257. ParsedArgs++;
  258. }
  259. return llvm::ArrayRef(Args, ParsedArgs);
  260. }
  261. BlockCommandComment *Parser::parseBlockCommand() {
  262. assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
  263. ParamCommandComment *PC = nullptr;
  264. TParamCommandComment *TPC = nullptr;
  265. BlockCommandComment *BC = nullptr;
  266. const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
  267. CommandMarkerKind CommandMarker =
  268. Tok.is(tok::backslash_command) ? CMK_Backslash : CMK_At;
  269. if (Info->IsParamCommand) {
  270. PC = S.actOnParamCommandStart(Tok.getLocation(),
  271. Tok.getEndLocation(),
  272. Tok.getCommandID(),
  273. CommandMarker);
  274. } else if (Info->IsTParamCommand) {
  275. TPC = S.actOnTParamCommandStart(Tok.getLocation(),
  276. Tok.getEndLocation(),
  277. Tok.getCommandID(),
  278. CommandMarker);
  279. } else {
  280. BC = S.actOnBlockCommandStart(Tok.getLocation(),
  281. Tok.getEndLocation(),
  282. Tok.getCommandID(),
  283. CommandMarker);
  284. }
  285. consumeToken();
  286. if (isTokBlockCommand()) {
  287. // Block command ahead. We can't nest block commands, so pretend that this
  288. // command has an empty argument.
  289. ParagraphComment *Paragraph = S.actOnParagraphComment(std::nullopt);
  290. if (PC) {
  291. S.actOnParamCommandFinish(PC, Paragraph);
  292. return PC;
  293. } else if (TPC) {
  294. S.actOnTParamCommandFinish(TPC, Paragraph);
  295. return TPC;
  296. } else {
  297. S.actOnBlockCommandFinish(BC, Paragraph);
  298. return BC;
  299. }
  300. }
  301. if (PC || TPC || Info->NumArgs > 0) {
  302. // In order to parse command arguments we need to retokenize a few
  303. // following text tokens.
  304. TextTokenRetokenizer Retokenizer(Allocator, *this);
  305. if (PC)
  306. parseParamCommandArgs(PC, Retokenizer);
  307. else if (TPC)
  308. parseTParamCommandArgs(TPC, Retokenizer);
  309. else
  310. S.actOnBlockCommandArgs(BC, parseCommandArgs(Retokenizer, Info->NumArgs));
  311. Retokenizer.putBackLeftoverTokens();
  312. }
  313. // If there's a block command ahead, we will attach an empty paragraph to
  314. // this command.
  315. bool EmptyParagraph = false;
  316. if (isTokBlockCommand())
  317. EmptyParagraph = true;
  318. else if (Tok.is(tok::newline)) {
  319. Token PrevTok = Tok;
  320. consumeToken();
  321. EmptyParagraph = isTokBlockCommand();
  322. putBack(PrevTok);
  323. }
  324. ParagraphComment *Paragraph;
  325. if (EmptyParagraph)
  326. Paragraph = S.actOnParagraphComment(std::nullopt);
  327. else {
  328. BlockContentComment *Block = parseParagraphOrBlockCommand();
  329. // Since we have checked for a block command, we should have parsed a
  330. // paragraph.
  331. Paragraph = cast<ParagraphComment>(Block);
  332. }
  333. if (PC) {
  334. S.actOnParamCommandFinish(PC, Paragraph);
  335. return PC;
  336. } else if (TPC) {
  337. S.actOnTParamCommandFinish(TPC, Paragraph);
  338. return TPC;
  339. } else {
  340. S.actOnBlockCommandFinish(BC, Paragraph);
  341. return BC;
  342. }
  343. }
  344. InlineCommandComment *Parser::parseInlineCommand() {
  345. assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
  346. const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
  347. const Token CommandTok = Tok;
  348. consumeToken();
  349. TextTokenRetokenizer Retokenizer(Allocator, *this);
  350. ArrayRef<Comment::Argument> Args =
  351. parseCommandArgs(Retokenizer, Info->NumArgs);
  352. InlineCommandComment *IC = S.actOnInlineCommand(
  353. CommandTok.getLocation(), CommandTok.getEndLocation(),
  354. CommandTok.getCommandID(), Args);
  355. if (Args.size() < Info->NumArgs) {
  356. Diag(CommandTok.getEndLocation().getLocWithOffset(1),
  357. diag::warn_doc_inline_command_not_enough_arguments)
  358. << CommandTok.is(tok::at_command) << Info->Name << Args.size()
  359. << Info->NumArgs
  360. << SourceRange(CommandTok.getLocation(), CommandTok.getEndLocation());
  361. }
  362. Retokenizer.putBackLeftoverTokens();
  363. return IC;
  364. }
  365. HTMLStartTagComment *Parser::parseHTMLStartTag() {
  366. assert(Tok.is(tok::html_start_tag));
  367. HTMLStartTagComment *HST =
  368. S.actOnHTMLStartTagStart(Tok.getLocation(),
  369. Tok.getHTMLTagStartName());
  370. consumeToken();
  371. SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
  372. while (true) {
  373. switch (Tok.getKind()) {
  374. case tok::html_ident: {
  375. Token Ident = Tok;
  376. consumeToken();
  377. if (Tok.isNot(tok::html_equals)) {
  378. Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
  379. Ident.getHTMLIdent()));
  380. continue;
  381. }
  382. Token Equals = Tok;
  383. consumeToken();
  384. if (Tok.isNot(tok::html_quoted_string)) {
  385. Diag(Tok.getLocation(),
  386. diag::warn_doc_html_start_tag_expected_quoted_string)
  387. << SourceRange(Equals.getLocation());
  388. Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
  389. Ident.getHTMLIdent()));
  390. while (Tok.is(tok::html_equals) ||
  391. Tok.is(tok::html_quoted_string))
  392. consumeToken();
  393. continue;
  394. }
  395. Attrs.push_back(HTMLStartTagComment::Attribute(
  396. Ident.getLocation(),
  397. Ident.getHTMLIdent(),
  398. Equals.getLocation(),
  399. SourceRange(Tok.getLocation(),
  400. Tok.getEndLocation()),
  401. Tok.getHTMLQuotedString()));
  402. consumeToken();
  403. continue;
  404. }
  405. case tok::html_greater:
  406. S.actOnHTMLStartTagFinish(HST, S.copyArray(llvm::ArrayRef(Attrs)),
  407. Tok.getLocation(),
  408. /* IsSelfClosing = */ false);
  409. consumeToken();
  410. return HST;
  411. case tok::html_slash_greater:
  412. S.actOnHTMLStartTagFinish(HST, S.copyArray(llvm::ArrayRef(Attrs)),
  413. Tok.getLocation(),
  414. /* IsSelfClosing = */ true);
  415. consumeToken();
  416. return HST;
  417. case tok::html_equals:
  418. case tok::html_quoted_string:
  419. Diag(Tok.getLocation(),
  420. diag::warn_doc_html_start_tag_expected_ident_or_greater);
  421. while (Tok.is(tok::html_equals) ||
  422. Tok.is(tok::html_quoted_string))
  423. consumeToken();
  424. if (Tok.is(tok::html_ident) ||
  425. Tok.is(tok::html_greater) ||
  426. Tok.is(tok::html_slash_greater))
  427. continue;
  428. S.actOnHTMLStartTagFinish(HST, S.copyArray(llvm::ArrayRef(Attrs)),
  429. SourceLocation(),
  430. /* IsSelfClosing = */ false);
  431. return HST;
  432. default:
  433. // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
  434. S.actOnHTMLStartTagFinish(HST, S.copyArray(llvm::ArrayRef(Attrs)),
  435. SourceLocation(),
  436. /* IsSelfClosing = */ false);
  437. bool StartLineInvalid;
  438. const unsigned StartLine = SourceMgr.getPresumedLineNumber(
  439. HST->getLocation(),
  440. &StartLineInvalid);
  441. bool EndLineInvalid;
  442. const unsigned EndLine = SourceMgr.getPresumedLineNumber(
  443. Tok.getLocation(),
  444. &EndLineInvalid);
  445. if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
  446. Diag(Tok.getLocation(),
  447. diag::warn_doc_html_start_tag_expected_ident_or_greater)
  448. << HST->getSourceRange();
  449. else {
  450. Diag(Tok.getLocation(),
  451. diag::warn_doc_html_start_tag_expected_ident_or_greater);
  452. Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
  453. << HST->getSourceRange();
  454. }
  455. return HST;
  456. }
  457. }
  458. }
  459. HTMLEndTagComment *Parser::parseHTMLEndTag() {
  460. assert(Tok.is(tok::html_end_tag));
  461. Token TokEndTag = Tok;
  462. consumeToken();
  463. SourceLocation Loc;
  464. if (Tok.is(tok::html_greater)) {
  465. Loc = Tok.getLocation();
  466. consumeToken();
  467. }
  468. return S.actOnHTMLEndTag(TokEndTag.getLocation(),
  469. Loc,
  470. TokEndTag.getHTMLTagEndName());
  471. }
  472. BlockContentComment *Parser::parseParagraphOrBlockCommand() {
  473. SmallVector<InlineContentComment *, 8> Content;
  474. while (true) {
  475. switch (Tok.getKind()) {
  476. case tok::verbatim_block_begin:
  477. case tok::verbatim_line_name:
  478. case tok::eof:
  479. break; // Block content or EOF ahead, finish this parapgaph.
  480. case tok::unknown_command:
  481. Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
  482. Tok.getEndLocation(),
  483. Tok.getUnknownCommandName()));
  484. consumeToken();
  485. continue;
  486. case tok::backslash_command:
  487. case tok::at_command: {
  488. const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
  489. if (Info->IsBlockCommand) {
  490. if (Content.size() == 0)
  491. return parseBlockCommand();
  492. break; // Block command ahead, finish this parapgaph.
  493. }
  494. if (Info->IsVerbatimBlockEndCommand) {
  495. Diag(Tok.getLocation(),
  496. diag::warn_verbatim_block_end_without_start)
  497. << Tok.is(tok::at_command)
  498. << Info->Name
  499. << SourceRange(Tok.getLocation(), Tok.getEndLocation());
  500. consumeToken();
  501. continue;
  502. }
  503. if (Info->IsUnknownCommand) {
  504. Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
  505. Tok.getEndLocation(),
  506. Info->getID()));
  507. consumeToken();
  508. continue;
  509. }
  510. assert(Info->IsInlineCommand);
  511. Content.push_back(parseInlineCommand());
  512. continue;
  513. }
  514. case tok::newline: {
  515. consumeToken();
  516. if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
  517. consumeToken();
  518. break; // Two newlines -- end of paragraph.
  519. }
  520. // Also allow [tok::newline, tok::text, tok::newline] if the middle
  521. // tok::text is just whitespace.
  522. if (Tok.is(tok::text) && isWhitespace(Tok.getText())) {
  523. Token WhitespaceTok = Tok;
  524. consumeToken();
  525. if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
  526. consumeToken();
  527. break;
  528. }
  529. // We have [tok::newline, tok::text, non-newline]. Put back tok::text.
  530. putBack(WhitespaceTok);
  531. }
  532. if (Content.size() > 0)
  533. Content.back()->addTrailingNewline();
  534. continue;
  535. }
  536. // Don't deal with HTML tag soup now.
  537. case tok::html_start_tag:
  538. Content.push_back(parseHTMLStartTag());
  539. continue;
  540. case tok::html_end_tag:
  541. Content.push_back(parseHTMLEndTag());
  542. continue;
  543. case tok::text:
  544. Content.push_back(S.actOnText(Tok.getLocation(),
  545. Tok.getEndLocation(),
  546. Tok.getText()));
  547. consumeToken();
  548. continue;
  549. case tok::verbatim_block_line:
  550. case tok::verbatim_block_end:
  551. case tok::verbatim_line_text:
  552. case tok::html_ident:
  553. case tok::html_equals:
  554. case tok::html_quoted_string:
  555. case tok::html_greater:
  556. case tok::html_slash_greater:
  557. llvm_unreachable("should not see this token");
  558. }
  559. break;
  560. }
  561. return S.actOnParagraphComment(S.copyArray(llvm::ArrayRef(Content)));
  562. }
  563. VerbatimBlockComment *Parser::parseVerbatimBlock() {
  564. assert(Tok.is(tok::verbatim_block_begin));
  565. VerbatimBlockComment *VB =
  566. S.actOnVerbatimBlockStart(Tok.getLocation(),
  567. Tok.getVerbatimBlockID());
  568. consumeToken();
  569. // Don't create an empty line if verbatim opening command is followed
  570. // by a newline.
  571. if (Tok.is(tok::newline))
  572. consumeToken();
  573. SmallVector<VerbatimBlockLineComment *, 8> Lines;
  574. while (Tok.is(tok::verbatim_block_line) ||
  575. Tok.is(tok::newline)) {
  576. VerbatimBlockLineComment *Line;
  577. if (Tok.is(tok::verbatim_block_line)) {
  578. Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
  579. Tok.getVerbatimBlockText());
  580. consumeToken();
  581. if (Tok.is(tok::newline)) {
  582. consumeToken();
  583. }
  584. } else {
  585. // Empty line, just a tok::newline.
  586. Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
  587. consumeToken();
  588. }
  589. Lines.push_back(Line);
  590. }
  591. if (Tok.is(tok::verbatim_block_end)) {
  592. const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
  593. S.actOnVerbatimBlockFinish(VB, Tok.getLocation(), Info->Name,
  594. S.copyArray(llvm::ArrayRef(Lines)));
  595. consumeToken();
  596. } else {
  597. // Unterminated \\verbatim block
  598. S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
  599. S.copyArray(llvm::ArrayRef(Lines)));
  600. }
  601. return VB;
  602. }
  603. VerbatimLineComment *Parser::parseVerbatimLine() {
  604. assert(Tok.is(tok::verbatim_line_name));
  605. Token NameTok = Tok;
  606. consumeToken();
  607. SourceLocation TextBegin;
  608. StringRef Text;
  609. // Next token might not be a tok::verbatim_line_text if verbatim line
  610. // starting command comes just before a newline or comment end.
  611. if (Tok.is(tok::verbatim_line_text)) {
  612. TextBegin = Tok.getLocation();
  613. Text = Tok.getVerbatimLineText();
  614. } else {
  615. TextBegin = NameTok.getEndLocation();
  616. Text = "";
  617. }
  618. VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
  619. NameTok.getVerbatimLineID(),
  620. TextBegin,
  621. Text);
  622. consumeToken();
  623. return VL;
  624. }
  625. BlockContentComment *Parser::parseBlockContent() {
  626. switch (Tok.getKind()) {
  627. case tok::text:
  628. case tok::unknown_command:
  629. case tok::backslash_command:
  630. case tok::at_command:
  631. case tok::html_start_tag:
  632. case tok::html_end_tag:
  633. return parseParagraphOrBlockCommand();
  634. case tok::verbatim_block_begin:
  635. return parseVerbatimBlock();
  636. case tok::verbatim_line_name:
  637. return parseVerbatimLine();
  638. case tok::eof:
  639. case tok::newline:
  640. case tok::verbatim_block_line:
  641. case tok::verbatim_block_end:
  642. case tok::verbatim_line_text:
  643. case tok::html_ident:
  644. case tok::html_equals:
  645. case tok::html_quoted_string:
  646. case tok::html_greater:
  647. case tok::html_slash_greater:
  648. llvm_unreachable("should not see this token");
  649. }
  650. llvm_unreachable("bogus token kind");
  651. }
  652. FullComment *Parser::parseFullComment() {
  653. // Skip newlines at the beginning of the comment.
  654. while (Tok.is(tok::newline))
  655. consumeToken();
  656. SmallVector<BlockContentComment *, 8> Blocks;
  657. while (Tok.isNot(tok::eof)) {
  658. Blocks.push_back(parseBlockContent());
  659. // Skip extra newlines after paragraph end.
  660. while (Tok.is(tok::newline))
  661. consumeToken();
  662. }
  663. return S.actOnFullComment(S.copyArray(llvm::ArrayRef(Blocks)));
  664. }
  665. } // end namespace comments
  666. } // end namespace clang