CommentParser.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780
  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::makeArrayRef(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. void Parser::parseBlockCommandArgs(BlockCommandComment *BC,
  249. TextTokenRetokenizer &Retokenizer,
  250. unsigned NumArgs) {
  251. typedef BlockCommandComment::Argument Argument;
  252. Argument *Args =
  253. new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs];
  254. unsigned ParsedArgs = 0;
  255. Token Arg;
  256. while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
  257. Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(),
  258. Arg.getEndLocation()),
  259. Arg.getText());
  260. ParsedArgs++;
  261. }
  262. S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
  263. }
  264. BlockCommandComment *Parser::parseBlockCommand() {
  265. assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
  266. ParamCommandComment *PC = nullptr;
  267. TParamCommandComment *TPC = nullptr;
  268. BlockCommandComment *BC = nullptr;
  269. const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
  270. CommandMarkerKind CommandMarker =
  271. Tok.is(tok::backslash_command) ? CMK_Backslash : CMK_At;
  272. if (Info->IsParamCommand) {
  273. PC = S.actOnParamCommandStart(Tok.getLocation(),
  274. Tok.getEndLocation(),
  275. Tok.getCommandID(),
  276. CommandMarker);
  277. } else if (Info->IsTParamCommand) {
  278. TPC = S.actOnTParamCommandStart(Tok.getLocation(),
  279. Tok.getEndLocation(),
  280. Tok.getCommandID(),
  281. CommandMarker);
  282. } else {
  283. BC = S.actOnBlockCommandStart(Tok.getLocation(),
  284. Tok.getEndLocation(),
  285. Tok.getCommandID(),
  286. CommandMarker);
  287. }
  288. consumeToken();
  289. if (isTokBlockCommand()) {
  290. // Block command ahead. We can't nest block commands, so pretend that this
  291. // command has an empty argument.
  292. ParagraphComment *Paragraph = S.actOnParagraphComment(None);
  293. if (PC) {
  294. S.actOnParamCommandFinish(PC, Paragraph);
  295. return PC;
  296. } else if (TPC) {
  297. S.actOnTParamCommandFinish(TPC, Paragraph);
  298. return TPC;
  299. } else {
  300. S.actOnBlockCommandFinish(BC, Paragraph);
  301. return BC;
  302. }
  303. }
  304. if (PC || TPC || Info->NumArgs > 0) {
  305. // In order to parse command arguments we need to retokenize a few
  306. // following text tokens.
  307. TextTokenRetokenizer Retokenizer(Allocator, *this);
  308. if (PC)
  309. parseParamCommandArgs(PC, Retokenizer);
  310. else if (TPC)
  311. parseTParamCommandArgs(TPC, Retokenizer);
  312. else
  313. parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs);
  314. Retokenizer.putBackLeftoverTokens();
  315. }
  316. // If there's a block command ahead, we will attach an empty paragraph to
  317. // this command.
  318. bool EmptyParagraph = false;
  319. if (isTokBlockCommand())
  320. EmptyParagraph = true;
  321. else if (Tok.is(tok::newline)) {
  322. Token PrevTok = Tok;
  323. consumeToken();
  324. EmptyParagraph = isTokBlockCommand();
  325. putBack(PrevTok);
  326. }
  327. ParagraphComment *Paragraph;
  328. if (EmptyParagraph)
  329. Paragraph = S.actOnParagraphComment(None);
  330. else {
  331. BlockContentComment *Block = parseParagraphOrBlockCommand();
  332. // Since we have checked for a block command, we should have parsed a
  333. // paragraph.
  334. Paragraph = cast<ParagraphComment>(Block);
  335. }
  336. if (PC) {
  337. S.actOnParamCommandFinish(PC, Paragraph);
  338. return PC;
  339. } else if (TPC) {
  340. S.actOnTParamCommandFinish(TPC, Paragraph);
  341. return TPC;
  342. } else {
  343. S.actOnBlockCommandFinish(BC, Paragraph);
  344. return BC;
  345. }
  346. }
  347. InlineCommandComment *Parser::parseInlineCommand() {
  348. assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
  349. const Token CommandTok = Tok;
  350. consumeToken();
  351. TextTokenRetokenizer Retokenizer(Allocator, *this);
  352. Token ArgTok;
  353. bool ArgTokValid = Retokenizer.lexWord(ArgTok);
  354. InlineCommandComment *IC;
  355. if (ArgTokValid) {
  356. IC = S.actOnInlineCommand(CommandTok.getLocation(),
  357. CommandTok.getEndLocation(),
  358. CommandTok.getCommandID(),
  359. ArgTok.getLocation(),
  360. ArgTok.getEndLocation(),
  361. ArgTok.getText());
  362. } else {
  363. IC = S.actOnInlineCommand(CommandTok.getLocation(),
  364. CommandTok.getEndLocation(),
  365. CommandTok.getCommandID());
  366. Diag(CommandTok.getEndLocation().getLocWithOffset(1),
  367. diag::warn_doc_inline_contents_no_argument)
  368. << CommandTok.is(tok::at_command)
  369. << Traits.getCommandInfo(CommandTok.getCommandID())->Name
  370. << SourceRange(CommandTok.getLocation(), CommandTok.getEndLocation());
  371. }
  372. Retokenizer.putBackLeftoverTokens();
  373. return IC;
  374. }
  375. HTMLStartTagComment *Parser::parseHTMLStartTag() {
  376. assert(Tok.is(tok::html_start_tag));
  377. HTMLStartTagComment *HST =
  378. S.actOnHTMLStartTagStart(Tok.getLocation(),
  379. Tok.getHTMLTagStartName());
  380. consumeToken();
  381. SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
  382. while (true) {
  383. switch (Tok.getKind()) {
  384. case tok::html_ident: {
  385. Token Ident = Tok;
  386. consumeToken();
  387. if (Tok.isNot(tok::html_equals)) {
  388. Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
  389. Ident.getHTMLIdent()));
  390. continue;
  391. }
  392. Token Equals = Tok;
  393. consumeToken();
  394. if (Tok.isNot(tok::html_quoted_string)) {
  395. Diag(Tok.getLocation(),
  396. diag::warn_doc_html_start_tag_expected_quoted_string)
  397. << SourceRange(Equals.getLocation());
  398. Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
  399. Ident.getHTMLIdent()));
  400. while (Tok.is(tok::html_equals) ||
  401. Tok.is(tok::html_quoted_string))
  402. consumeToken();
  403. continue;
  404. }
  405. Attrs.push_back(HTMLStartTagComment::Attribute(
  406. Ident.getLocation(),
  407. Ident.getHTMLIdent(),
  408. Equals.getLocation(),
  409. SourceRange(Tok.getLocation(),
  410. Tok.getEndLocation()),
  411. Tok.getHTMLQuotedString()));
  412. consumeToken();
  413. continue;
  414. }
  415. case tok::html_greater:
  416. S.actOnHTMLStartTagFinish(HST,
  417. S.copyArray(llvm::makeArrayRef(Attrs)),
  418. Tok.getLocation(),
  419. /* IsSelfClosing = */ false);
  420. consumeToken();
  421. return HST;
  422. case tok::html_slash_greater:
  423. S.actOnHTMLStartTagFinish(HST,
  424. S.copyArray(llvm::makeArrayRef(Attrs)),
  425. Tok.getLocation(),
  426. /* IsSelfClosing = */ true);
  427. consumeToken();
  428. return HST;
  429. case tok::html_equals:
  430. case tok::html_quoted_string:
  431. Diag(Tok.getLocation(),
  432. diag::warn_doc_html_start_tag_expected_ident_or_greater);
  433. while (Tok.is(tok::html_equals) ||
  434. Tok.is(tok::html_quoted_string))
  435. consumeToken();
  436. if (Tok.is(tok::html_ident) ||
  437. Tok.is(tok::html_greater) ||
  438. Tok.is(tok::html_slash_greater))
  439. continue;
  440. S.actOnHTMLStartTagFinish(HST,
  441. S.copyArray(llvm::makeArrayRef(Attrs)),
  442. SourceLocation(),
  443. /* IsSelfClosing = */ false);
  444. return HST;
  445. default:
  446. // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
  447. S.actOnHTMLStartTagFinish(HST,
  448. S.copyArray(llvm::makeArrayRef(Attrs)),
  449. SourceLocation(),
  450. /* IsSelfClosing = */ false);
  451. bool StartLineInvalid;
  452. const unsigned StartLine = SourceMgr.getPresumedLineNumber(
  453. HST->getLocation(),
  454. &StartLineInvalid);
  455. bool EndLineInvalid;
  456. const unsigned EndLine = SourceMgr.getPresumedLineNumber(
  457. Tok.getLocation(),
  458. &EndLineInvalid);
  459. if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
  460. Diag(Tok.getLocation(),
  461. diag::warn_doc_html_start_tag_expected_ident_or_greater)
  462. << HST->getSourceRange();
  463. else {
  464. Diag(Tok.getLocation(),
  465. diag::warn_doc_html_start_tag_expected_ident_or_greater);
  466. Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
  467. << HST->getSourceRange();
  468. }
  469. return HST;
  470. }
  471. }
  472. }
  473. HTMLEndTagComment *Parser::parseHTMLEndTag() {
  474. assert(Tok.is(tok::html_end_tag));
  475. Token TokEndTag = Tok;
  476. consumeToken();
  477. SourceLocation Loc;
  478. if (Tok.is(tok::html_greater)) {
  479. Loc = Tok.getLocation();
  480. consumeToken();
  481. }
  482. return S.actOnHTMLEndTag(TokEndTag.getLocation(),
  483. Loc,
  484. TokEndTag.getHTMLTagEndName());
  485. }
  486. BlockContentComment *Parser::parseParagraphOrBlockCommand() {
  487. SmallVector<InlineContentComment *, 8> Content;
  488. while (true) {
  489. switch (Tok.getKind()) {
  490. case tok::verbatim_block_begin:
  491. case tok::verbatim_line_name:
  492. case tok::eof:
  493. break; // Block content or EOF ahead, finish this parapgaph.
  494. case tok::unknown_command:
  495. Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
  496. Tok.getEndLocation(),
  497. Tok.getUnknownCommandName()));
  498. consumeToken();
  499. continue;
  500. case tok::backslash_command:
  501. case tok::at_command: {
  502. const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
  503. if (Info->IsBlockCommand) {
  504. if (Content.size() == 0)
  505. return parseBlockCommand();
  506. break; // Block command ahead, finish this parapgaph.
  507. }
  508. if (Info->IsVerbatimBlockEndCommand) {
  509. Diag(Tok.getLocation(),
  510. diag::warn_verbatim_block_end_without_start)
  511. << Tok.is(tok::at_command)
  512. << Info->Name
  513. << SourceRange(Tok.getLocation(), Tok.getEndLocation());
  514. consumeToken();
  515. continue;
  516. }
  517. if (Info->IsUnknownCommand) {
  518. Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
  519. Tok.getEndLocation(),
  520. Info->getID()));
  521. consumeToken();
  522. continue;
  523. }
  524. assert(Info->IsInlineCommand);
  525. Content.push_back(parseInlineCommand());
  526. continue;
  527. }
  528. case tok::newline: {
  529. consumeToken();
  530. if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
  531. consumeToken();
  532. break; // Two newlines -- end of paragraph.
  533. }
  534. // Also allow [tok::newline, tok::text, tok::newline] if the middle
  535. // tok::text is just whitespace.
  536. if (Tok.is(tok::text) && isWhitespace(Tok.getText())) {
  537. Token WhitespaceTok = Tok;
  538. consumeToken();
  539. if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
  540. consumeToken();
  541. break;
  542. }
  543. // We have [tok::newline, tok::text, non-newline]. Put back tok::text.
  544. putBack(WhitespaceTok);
  545. }
  546. if (Content.size() > 0)
  547. Content.back()->addTrailingNewline();
  548. continue;
  549. }
  550. // Don't deal with HTML tag soup now.
  551. case tok::html_start_tag:
  552. Content.push_back(parseHTMLStartTag());
  553. continue;
  554. case tok::html_end_tag:
  555. Content.push_back(parseHTMLEndTag());
  556. continue;
  557. case tok::text:
  558. Content.push_back(S.actOnText(Tok.getLocation(),
  559. Tok.getEndLocation(),
  560. Tok.getText()));
  561. consumeToken();
  562. continue;
  563. case tok::verbatim_block_line:
  564. case tok::verbatim_block_end:
  565. case tok::verbatim_line_text:
  566. case tok::html_ident:
  567. case tok::html_equals:
  568. case tok::html_quoted_string:
  569. case tok::html_greater:
  570. case tok::html_slash_greater:
  571. llvm_unreachable("should not see this token");
  572. }
  573. break;
  574. }
  575. return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
  576. }
  577. VerbatimBlockComment *Parser::parseVerbatimBlock() {
  578. assert(Tok.is(tok::verbatim_block_begin));
  579. VerbatimBlockComment *VB =
  580. S.actOnVerbatimBlockStart(Tok.getLocation(),
  581. Tok.getVerbatimBlockID());
  582. consumeToken();
  583. // Don't create an empty line if verbatim opening command is followed
  584. // by a newline.
  585. if (Tok.is(tok::newline))
  586. consumeToken();
  587. SmallVector<VerbatimBlockLineComment *, 8> Lines;
  588. while (Tok.is(tok::verbatim_block_line) ||
  589. Tok.is(tok::newline)) {
  590. VerbatimBlockLineComment *Line;
  591. if (Tok.is(tok::verbatim_block_line)) {
  592. Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
  593. Tok.getVerbatimBlockText());
  594. consumeToken();
  595. if (Tok.is(tok::newline)) {
  596. consumeToken();
  597. }
  598. } else {
  599. // Empty line, just a tok::newline.
  600. Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
  601. consumeToken();
  602. }
  603. Lines.push_back(Line);
  604. }
  605. if (Tok.is(tok::verbatim_block_end)) {
  606. const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
  607. S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
  608. Info->Name,
  609. S.copyArray(llvm::makeArrayRef(Lines)));
  610. consumeToken();
  611. } else {
  612. // Unterminated \\verbatim block
  613. S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
  614. S.copyArray(llvm::makeArrayRef(Lines)));
  615. }
  616. return VB;
  617. }
  618. VerbatimLineComment *Parser::parseVerbatimLine() {
  619. assert(Tok.is(tok::verbatim_line_name));
  620. Token NameTok = Tok;
  621. consumeToken();
  622. SourceLocation TextBegin;
  623. StringRef Text;
  624. // Next token might not be a tok::verbatim_line_text if verbatim line
  625. // starting command comes just before a newline or comment end.
  626. if (Tok.is(tok::verbatim_line_text)) {
  627. TextBegin = Tok.getLocation();
  628. Text = Tok.getVerbatimLineText();
  629. } else {
  630. TextBegin = NameTok.getEndLocation();
  631. Text = "";
  632. }
  633. VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
  634. NameTok.getVerbatimLineID(),
  635. TextBegin,
  636. Text);
  637. consumeToken();
  638. return VL;
  639. }
  640. BlockContentComment *Parser::parseBlockContent() {
  641. switch (Tok.getKind()) {
  642. case tok::text:
  643. case tok::unknown_command:
  644. case tok::backslash_command:
  645. case tok::at_command:
  646. case tok::html_start_tag:
  647. case tok::html_end_tag:
  648. return parseParagraphOrBlockCommand();
  649. case tok::verbatim_block_begin:
  650. return parseVerbatimBlock();
  651. case tok::verbatim_line_name:
  652. return parseVerbatimLine();
  653. case tok::eof:
  654. case tok::newline:
  655. case tok::verbatim_block_line:
  656. case tok::verbatim_block_end:
  657. case tok::verbatim_line_text:
  658. case tok::html_ident:
  659. case tok::html_equals:
  660. case tok::html_quoted_string:
  661. case tok::html_greater:
  662. case tok::html_slash_greater:
  663. llvm_unreachable("should not see this token");
  664. }
  665. llvm_unreachable("bogus token kind");
  666. }
  667. FullComment *Parser::parseFullComment() {
  668. // Skip newlines at the beginning of the comment.
  669. while (Tok.is(tok::newline))
  670. consumeToken();
  671. SmallVector<BlockContentComment *, 8> Blocks;
  672. while (Tok.isNot(tok::eof)) {
  673. Blocks.push_back(parseBlockContent());
  674. // Skip extra newlines after paragraph end.
  675. while (Tok.is(tok::newline))
  676. consumeToken();
  677. }
  678. return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
  679. }
  680. } // end namespace comments
  681. } // end namespace clang