CommentParser.cpp 22 KB

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