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