CommentParser.cpp 23 KB

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