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