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::command)); 304 305 ParamCommandComment *PC; 306 TParamCommandComment *TPC; 307 BlockCommandComment *BC; 308 bool IsParam = false; 309 bool IsTParam = false; 310 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID()); 311 if (Info->IsParamCommand) { 312 IsParam = true; 313 PC = S.actOnParamCommandStart(Tok.getLocation(), 314 Tok.getEndLocation(), 315 Tok.getCommandID()); 316 PC->setHDCommand(Tok.getHDCommand()); 317 } else if (Info->IsTParamCommand) { 318 IsTParam = true; 319 TPC = S.actOnTParamCommandStart(Tok.getLocation(), 320 Tok.getEndLocation(), 321 Tok.getCommandID()); 322 TPC->setHDCommand(Tok.getHDCommand()); 323 } else { 324 BC = S.actOnBlockCommandStart(Tok.getLocation(), 325 Tok.getEndLocation(), 326 Tok.getCommandID()); 327 BC->setHDCommand(Tok.getHDCommand()); 328 } 329 consumeToken(); 330 331 if (isTokBlockCommand()) { 332 // Block command ahead. We can't nest block commands, so pretend that this 333 // command has an empty argument. 334 ParagraphComment *Paragraph = S.actOnParagraphComment( 335 ArrayRef<InlineContentComment *>()); 336 if (IsParam) { 337 S.actOnParamCommandFinish(PC, Paragraph); 338 return PC; 339 } else if (IsTParam) { 340 S.actOnTParamCommandFinish(TPC, Paragraph); 341 return TPC; 342 } else { 343 S.actOnBlockCommandFinish(BC, Paragraph); 344 return BC; 345 } 346 } 347 348 if (IsParam || IsTParam || Info->NumArgs > 0) { 349 // In order to parse command arguments we need to retokenize a few 350 // following text tokens. 351 TextTokenRetokenizer Retokenizer(Allocator, *this); 352 353 if (IsParam) 354 parseParamCommandArgs(PC, Retokenizer); 355 else if (IsTParam) 356 parseTParamCommandArgs(TPC, Retokenizer); 357 else 358 parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs); 359 360 Retokenizer.putBackLeftoverTokens(); 361 } 362 363 // If there's a block command ahead, we will attach an empty paragraph to 364 // this command. 365 bool EmptyParagraph = false; 366 if (isTokBlockCommand()) 367 EmptyParagraph = true; 368 else if (Tok.is(tok::newline)) { 369 Token PrevTok = Tok; 370 consumeToken(); 371 EmptyParagraph = isTokBlockCommand(); 372 putBack(PrevTok); 373 } 374 375 ParagraphComment *Paragraph; 376 if (EmptyParagraph) 377 Paragraph = S.actOnParagraphComment(ArrayRef<InlineContentComment *>()); 378 else { 379 BlockContentComment *Block = parseParagraphOrBlockCommand(); 380 // Since we have checked for a block command, we should have parsed a 381 // paragraph. 382 Paragraph = cast<ParagraphComment>(Block); 383 } 384 385 if (IsParam) { 386 S.actOnParamCommandFinish(PC, Paragraph); 387 return PC; 388 } else if (IsTParam) { 389 S.actOnTParamCommandFinish(TPC, Paragraph); 390 return TPC; 391 } else { 392 S.actOnBlockCommandFinish(BC, Paragraph); 393 return BC; 394 } 395 } 396 397 InlineCommandComment *Parser::parseInlineCommand() { 398 assert(Tok.is(tok::command)); 399 400 const Token CommandTok = Tok; 401 consumeToken(); 402 403 TextTokenRetokenizer Retokenizer(Allocator, *this); 404 405 Token ArgTok; 406 bool ArgTokValid = Retokenizer.lexWord(ArgTok); 407 408 InlineCommandComment *IC; 409 if (ArgTokValid) { 410 IC = S.actOnInlineCommand(CommandTok.getLocation(), 411 CommandTok.getEndLocation(), 412 CommandTok.getCommandID(), 413 ArgTok.getLocation(), 414 ArgTok.getEndLocation(), 415 ArgTok.getText()); 416 } else { 417 IC = S.actOnInlineCommand(CommandTok.getLocation(), 418 CommandTok.getEndLocation(), 419 CommandTok.getCommandID()); 420 } 421 422 Retokenizer.putBackLeftoverTokens(); 423 424 return IC; 425 } 426 427 HTMLStartTagComment *Parser::parseHTMLStartTag() { 428 assert(Tok.is(tok::html_start_tag)); 429 HTMLStartTagComment *HST = 430 S.actOnHTMLStartTagStart(Tok.getLocation(), 431 Tok.getHTMLTagStartName()); 432 consumeToken(); 433 434 SmallVector<HTMLStartTagComment::Attribute, 2> Attrs; 435 while (true) { 436 switch (Tok.getKind()) { 437 case tok::html_ident: { 438 Token Ident = Tok; 439 consumeToken(); 440 if (Tok.isNot(tok::html_equals)) { 441 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(), 442 Ident.getHTMLIdent())); 443 continue; 444 } 445 Token Equals = Tok; 446 consumeToken(); 447 if (Tok.isNot(tok::html_quoted_string)) { 448 Diag(Tok.getLocation(), 449 diag::warn_doc_html_start_tag_expected_quoted_string) 450 << SourceRange(Equals.getLocation()); 451 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(), 452 Ident.getHTMLIdent())); 453 while (Tok.is(tok::html_equals) || 454 Tok.is(tok::html_quoted_string)) 455 consumeToken(); 456 continue; 457 } 458 Attrs.push_back(HTMLStartTagComment::Attribute( 459 Ident.getLocation(), 460 Ident.getHTMLIdent(), 461 Equals.getLocation(), 462 SourceRange(Tok.getLocation(), 463 Tok.getEndLocation()), 464 Tok.getHTMLQuotedString())); 465 consumeToken(); 466 continue; 467 } 468 469 case tok::html_greater: 470 S.actOnHTMLStartTagFinish(HST, 471 S.copyArray(llvm::makeArrayRef(Attrs)), 472 Tok.getLocation(), 473 /* IsSelfClosing = */ false); 474 consumeToken(); 475 return HST; 476 477 case tok::html_slash_greater: 478 S.actOnHTMLStartTagFinish(HST, 479 S.copyArray(llvm::makeArrayRef(Attrs)), 480 Tok.getLocation(), 481 /* IsSelfClosing = */ true); 482 consumeToken(); 483 return HST; 484 485 case tok::html_equals: 486 case tok::html_quoted_string: 487 Diag(Tok.getLocation(), 488 diag::warn_doc_html_start_tag_expected_ident_or_greater); 489 while (Tok.is(tok::html_equals) || 490 Tok.is(tok::html_quoted_string)) 491 consumeToken(); 492 if (Tok.is(tok::html_ident) || 493 Tok.is(tok::html_greater) || 494 Tok.is(tok::html_slash_greater)) 495 continue; 496 497 S.actOnHTMLStartTagFinish(HST, 498 S.copyArray(llvm::makeArrayRef(Attrs)), 499 SourceLocation(), 500 /* IsSelfClosing = */ false); 501 return HST; 502 503 default: 504 // Not a token from an HTML start tag. Thus HTML tag prematurely ended. 505 S.actOnHTMLStartTagFinish(HST, 506 S.copyArray(llvm::makeArrayRef(Attrs)), 507 SourceLocation(), 508 /* IsSelfClosing = */ false); 509 bool StartLineInvalid; 510 const unsigned StartLine = SourceMgr.getPresumedLineNumber( 511 HST->getLocation(), 512 &StartLineInvalid); 513 bool EndLineInvalid; 514 const unsigned EndLine = SourceMgr.getPresumedLineNumber( 515 Tok.getLocation(), 516 &EndLineInvalid); 517 if (StartLineInvalid || EndLineInvalid || StartLine == EndLine) 518 Diag(Tok.getLocation(), 519 diag::warn_doc_html_start_tag_expected_ident_or_greater) 520 << HST->getSourceRange(); 521 else { 522 Diag(Tok.getLocation(), 523 diag::warn_doc_html_start_tag_expected_ident_or_greater); 524 Diag(HST->getLocation(), diag::note_doc_html_tag_started_here) 525 << HST->getSourceRange(); 526 } 527 return HST; 528 } 529 } 530 } 531 532 HTMLEndTagComment *Parser::parseHTMLEndTag() { 533 assert(Tok.is(tok::html_end_tag)); 534 Token TokEndTag = Tok; 535 consumeToken(); 536 SourceLocation Loc; 537 if (Tok.is(tok::html_greater)) { 538 Loc = Tok.getLocation(); 539 consumeToken(); 540 } 541 542 return S.actOnHTMLEndTag(TokEndTag.getLocation(), 543 Loc, 544 TokEndTag.getHTMLTagEndName()); 545 } 546 547 BlockContentComment *Parser::parseParagraphOrBlockCommand() { 548 SmallVector<InlineContentComment *, 8> Content; 549 550 while (true) { 551 switch (Tok.getKind()) { 552 case tok::verbatim_block_begin: 553 case tok::verbatim_line_name: 554 case tok::eof: 555 assert(Content.size() != 0); 556 break; // Block content or EOF ahead, finish this parapgaph. 557 558 case tok::unknown_command: 559 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(), 560 Tok.getEndLocation(), 561 Tok.getUnknownCommandName())); 562 consumeToken(); 563 continue; 564 565 case tok::command: { 566 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID()); 567 if (Info->IsBlockCommand) { 568 if (Content.size() == 0) 569 return parseBlockCommand(); 570 break; // Block command ahead, finish this parapgaph. 571 } 572 if (Info->IsVerbatimBlockEndCommand) { 573 Diag(Tok.getLocation(), 574 diag::warn_verbatim_block_end_without_start) 575 << Tok.getHDCommand() 576 << Info->Name 577 << SourceRange(Tok.getLocation(), Tok.getEndLocation()); 578 consumeToken(); 579 continue; 580 } 581 if (Info->IsUnknownCommand) { 582 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(), 583 Tok.getEndLocation(), 584 Info->getID())); 585 consumeToken(); 586 continue; 587 } 588 assert(Info->IsInlineCommand); 589 Content.push_back(parseInlineCommand()); 590 continue; 591 } 592 593 case tok::newline: { 594 consumeToken(); 595 if (Tok.is(tok::newline) || Tok.is(tok::eof)) { 596 consumeToken(); 597 break; // Two newlines -- end of paragraph. 598 } 599 if (Content.size() > 0) 600 Content.back()->addTrailingNewline(); 601 continue; 602 } 603 604 // Don't deal with HTML tag soup now. 605 case tok::html_start_tag: 606 Content.push_back(parseHTMLStartTag()); 607 continue; 608 609 case tok::html_end_tag: 610 Content.push_back(parseHTMLEndTag()); 611 continue; 612 613 case tok::text: 614 Content.push_back(S.actOnText(Tok.getLocation(), 615 Tok.getEndLocation(), 616 Tok.getText())); 617 consumeToken(); 618 continue; 619 620 case tok::verbatim_block_line: 621 case tok::verbatim_block_end: 622 case tok::verbatim_line_text: 623 case tok::html_ident: 624 case tok::html_equals: 625 case tok::html_quoted_string: 626 case tok::html_greater: 627 case tok::html_slash_greater: 628 llvm_unreachable("should not see this token"); 629 } 630 break; 631 } 632 633 return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content))); 634 } 635 636 VerbatimBlockComment *Parser::parseVerbatimBlock() { 637 assert(Tok.is(tok::verbatim_block_begin)); 638 639 VerbatimBlockComment *VB = 640 S.actOnVerbatimBlockStart(Tok.getLocation(), 641 Tok.getVerbatimBlockID()); 642 consumeToken(); 643 644 // Don't create an empty line if verbatim opening command is followed 645 // by a newline. 646 if (Tok.is(tok::newline)) 647 consumeToken(); 648 649 SmallVector<VerbatimBlockLineComment *, 8> Lines; 650 while (Tok.is(tok::verbatim_block_line) || 651 Tok.is(tok::newline)) { 652 VerbatimBlockLineComment *Line; 653 if (Tok.is(tok::verbatim_block_line)) { 654 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), 655 Tok.getVerbatimBlockText()); 656 consumeToken(); 657 if (Tok.is(tok::newline)) { 658 consumeToken(); 659 } 660 } else { 661 // Empty line, just a tok::newline. 662 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), ""); 663 consumeToken(); 664 } 665 Lines.push_back(Line); 666 } 667 668 if (Tok.is(tok::verbatim_block_end)) { 669 const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID()); 670 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(), 671 Info->Name, 672 S.copyArray(llvm::makeArrayRef(Lines))); 673 consumeToken(); 674 } else { 675 // Unterminated \\verbatim block 676 S.actOnVerbatimBlockFinish(VB, SourceLocation(), "", 677 S.copyArray(llvm::makeArrayRef(Lines))); 678 } 679 680 return VB; 681 } 682 683 VerbatimLineComment *Parser::parseVerbatimLine() { 684 assert(Tok.is(tok::verbatim_line_name)); 685 686 Token NameTok = Tok; 687 consumeToken(); 688 689 SourceLocation TextBegin; 690 StringRef Text; 691 // Next token might not be a tok::verbatim_line_text if verbatim line 692 // starting command comes just before a newline or comment end. 693 if (Tok.is(tok::verbatim_line_text)) { 694 TextBegin = Tok.getLocation(); 695 Text = Tok.getVerbatimLineText(); 696 } else { 697 TextBegin = NameTok.getEndLocation(); 698 Text = ""; 699 } 700 701 VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(), 702 NameTok.getVerbatimLineID(), 703 TextBegin, 704 Text); 705 consumeToken(); 706 return VL; 707 } 708 709 BlockContentComment *Parser::parseBlockContent() { 710 switch (Tok.getKind()) { 711 case tok::text: 712 case tok::unknown_command: 713 case tok::command: 714 case tok::html_start_tag: 715 case tok::html_end_tag: 716 return parseParagraphOrBlockCommand(); 717 718 case tok::verbatim_block_begin: 719 return parseVerbatimBlock(); 720 721 case tok::verbatim_line_name: 722 return parseVerbatimLine(); 723 724 case tok::eof: 725 case tok::newline: 726 case tok::verbatim_block_line: 727 case tok::verbatim_block_end: 728 case tok::verbatim_line_text: 729 case tok::html_ident: 730 case tok::html_equals: 731 case tok::html_quoted_string: 732 case tok::html_greater: 733 case tok::html_slash_greater: 734 llvm_unreachable("should not see this token"); 735 } 736 llvm_unreachable("bogus token kind"); 737 } 738 739 FullComment *Parser::parseFullComment() { 740 // Skip newlines at the beginning of the comment. 741 while (Tok.is(tok::newline)) 742 consumeToken(); 743 744 SmallVector<BlockContentComment *, 8> Blocks; 745 while (Tok.isNot(tok::eof)) { 746 Blocks.push_back(parseBlockContent()); 747 748 // Skip extra newlines after paragraph end. 749 while (Tok.is(tok::newline)) 750 consumeToken(); 751 } 752 return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks))); 753 } 754 755 } // end namespace comments 756 } // end namespace clang 757