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