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