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; 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 Tok.is(tok::at_command)); 317 } else if (Info->IsTParamCommand) { 318 IsTParam = true; 319 TPC = S.actOnTParamCommandStart(Tok.getLocation(), 320 Tok.getEndLocation(), 321 Tok.getCommandID(), 322 Tok.is(tok::at_command)); 323 } else { 324 BC = S.actOnBlockCommandStart(Tok.getLocation(), 325 Tok.getEndLocation(), 326 Tok.getCommandID(), 327 Tok.is(tok::at_command)); 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::backslash_command) || Tok.is(tok::at_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::backslash_command: 566 case tok::at_command: { 567 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID()); 568 if (Info->IsBlockCommand) { 569 if (Content.size() == 0) 570 return parseBlockCommand(); 571 break; // Block command ahead, finish this parapgaph. 572 } 573 if (Info->IsVerbatimBlockEndCommand) { 574 Diag(Tok.getLocation(), 575 diag::warn_verbatim_block_end_without_start) 576 << Tok.is(tok::at_command) 577 << Info->Name 578 << SourceRange(Tok.getLocation(), Tok.getEndLocation()); 579 consumeToken(); 580 continue; 581 } 582 if (Info->IsUnknownCommand) { 583 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(), 584 Tok.getEndLocation(), 585 Info->getID())); 586 consumeToken(); 587 continue; 588 } 589 assert(Info->IsInlineCommand); 590 Content.push_back(parseInlineCommand()); 591 continue; 592 } 593 594 case tok::newline: { 595 consumeToken(); 596 if (Tok.is(tok::newline) || Tok.is(tok::eof)) { 597 consumeToken(); 598 break; // Two newlines -- end of paragraph. 599 } 600 if (Content.size() > 0) 601 Content.back()->addTrailingNewline(); 602 continue; 603 } 604 605 // Don't deal with HTML tag soup now. 606 case tok::html_start_tag: 607 Content.push_back(parseHTMLStartTag()); 608 continue; 609 610 case tok::html_end_tag: 611 Content.push_back(parseHTMLEndTag()); 612 continue; 613 614 case tok::text: 615 Content.push_back(S.actOnText(Tok.getLocation(), 616 Tok.getEndLocation(), 617 Tok.getText())); 618 consumeToken(); 619 continue; 620 621 case tok::verbatim_block_line: 622 case tok::verbatim_block_end: 623 case tok::verbatim_line_text: 624 case tok::html_ident: 625 case tok::html_equals: 626 case tok::html_quoted_string: 627 case tok::html_greater: 628 case tok::html_slash_greater: 629 llvm_unreachable("should not see this token"); 630 } 631 break; 632 } 633 634 return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content))); 635 } 636 637 VerbatimBlockComment *Parser::parseVerbatimBlock() { 638 assert(Tok.is(tok::verbatim_block_begin)); 639 640 VerbatimBlockComment *VB = 641 S.actOnVerbatimBlockStart(Tok.getLocation(), 642 Tok.getVerbatimBlockID()); 643 consumeToken(); 644 645 // Don't create an empty line if verbatim opening command is followed 646 // by a newline. 647 if (Tok.is(tok::newline)) 648 consumeToken(); 649 650 SmallVector<VerbatimBlockLineComment *, 8> Lines; 651 while (Tok.is(tok::verbatim_block_line) || 652 Tok.is(tok::newline)) { 653 VerbatimBlockLineComment *Line; 654 if (Tok.is(tok::verbatim_block_line)) { 655 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), 656 Tok.getVerbatimBlockText()); 657 consumeToken(); 658 if (Tok.is(tok::newline)) { 659 consumeToken(); 660 } 661 } else { 662 // Empty line, just a tok::newline. 663 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), ""); 664 consumeToken(); 665 } 666 Lines.push_back(Line); 667 } 668 669 if (Tok.is(tok::verbatim_block_end)) { 670 const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID()); 671 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(), 672 Info->Name, 673 S.copyArray(llvm::makeArrayRef(Lines))); 674 consumeToken(); 675 } else { 676 // Unterminated \\verbatim block 677 S.actOnVerbatimBlockFinish(VB, SourceLocation(), "", 678 S.copyArray(llvm::makeArrayRef(Lines))); 679 } 680 681 return VB; 682 } 683 684 VerbatimLineComment *Parser::parseVerbatimLine() { 685 assert(Tok.is(tok::verbatim_line_name)); 686 687 Token NameTok = Tok; 688 consumeToken(); 689 690 SourceLocation TextBegin; 691 StringRef Text; 692 // Next token might not be a tok::verbatim_line_text if verbatim line 693 // starting command comes just before a newline or comment end. 694 if (Tok.is(tok::verbatim_line_text)) { 695 TextBegin = Tok.getLocation(); 696 Text = Tok.getVerbatimLineText(); 697 } else { 698 TextBegin = NameTok.getEndLocation(); 699 Text = ""; 700 } 701 702 VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(), 703 NameTok.getVerbatimLineID(), 704 TextBegin, 705 Text); 706 consumeToken(); 707 return VL; 708 } 709 710 BlockContentComment *Parser::parseBlockContent() { 711 switch (Tok.getKind()) { 712 case tok::text: 713 case tok::unknown_command: 714 case tok::backslash_command: 715 case tok::at_command: 716 case tok::html_start_tag: 717 case tok::html_end_tag: 718 return parseParagraphOrBlockCommand(); 719 720 case tok::verbatim_block_begin: 721 return parseVerbatimBlock(); 722 723 case tok::verbatim_line_name: 724 return parseVerbatimLine(); 725 726 case tok::eof: 727 case tok::newline: 728 case tok::verbatim_block_line: 729 case tok::verbatim_block_end: 730 case tok::verbatim_line_text: 731 case tok::html_ident: 732 case tok::html_equals: 733 case tok::html_quoted_string: 734 case tok::html_greater: 735 case tok::html_slash_greater: 736 llvm_unreachable("should not see this token"); 737 } 738 llvm_unreachable("bogus token kind"); 739 } 740 741 FullComment *Parser::parseFullComment() { 742 // Skip newlines at the beginning of the comment. 743 while (Tok.is(tok::newline)) 744 consumeToken(); 745 746 SmallVector<BlockContentComment *, 8> Blocks; 747 while (Tok.isNot(tok::eof)) { 748 Blocks.push_back(parseBlockContent()); 749 750 // Skip extra newlines after paragraph end. 751 while (Tok.is(tok::newline)) 752 consumeToken(); 753 } 754 return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks))); 755 } 756 757 } // end namespace comments 758 } // end namespace clang 759