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 20 static inline bool isWhitespace(llvm::StringRef S) { 21 for (StringRef::const_iterator I = S.begin(), E = S.end(); I != E; ++I) { 22 if (!isWhitespace(*I)) 23 return false; 24 } 25 return true; 26 } 27 28 namespace comments { 29 30 /// Re-lexes a sequence of tok::text tokens. 31 class TextTokenRetokenizer { 32 llvm::BumpPtrAllocator &Allocator; 33 Parser &P; 34 35 /// This flag is set when there are no more tokens we can fetch from lexer. 36 bool NoMoreInterestingTokens; 37 38 /// Token buffer: tokens we have processed and lookahead. 39 SmallVector<Token, 16> Toks; 40 41 /// A position in \c Toks. 42 struct Position { 43 unsigned CurToken; 44 const char *BufferStart; 45 const char *BufferEnd; 46 const char *BufferPtr; 47 SourceLocation BufferStartLoc; 48 }; 49 50 /// Current position in Toks. 51 Position Pos; 52 53 bool isEnd() const { 54 return Pos.CurToken >= Toks.size(); 55 } 56 57 /// Sets up the buffer pointers to point to current token. 58 void setupBuffer() { 59 assert(!isEnd()); 60 const Token &Tok = Toks[Pos.CurToken]; 61 62 Pos.BufferStart = Tok.getText().begin(); 63 Pos.BufferEnd = Tok.getText().end(); 64 Pos.BufferPtr = Pos.BufferStart; 65 Pos.BufferStartLoc = Tok.getLocation(); 66 } 67 68 SourceLocation getSourceLocation() const { 69 const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart; 70 return Pos.BufferStartLoc.getLocWithOffset(CharNo); 71 } 72 73 char peek() const { 74 assert(!isEnd()); 75 assert(Pos.BufferPtr != Pos.BufferEnd); 76 return *Pos.BufferPtr; 77 } 78 79 void consumeChar() { 80 assert(!isEnd()); 81 assert(Pos.BufferPtr != Pos.BufferEnd); 82 Pos.BufferPtr++; 83 if (Pos.BufferPtr == Pos.BufferEnd) { 84 Pos.CurToken++; 85 if (isEnd() && !addToken()) 86 return; 87 88 assert(!isEnd()); 89 setupBuffer(); 90 } 91 } 92 93 /// Add a token. 94 /// Returns true on success, false if there are no interesting tokens to 95 /// fetch from lexer. 96 bool addToken() { 97 if (NoMoreInterestingTokens) 98 return false; 99 100 if (P.Tok.is(tok::newline)) { 101 // If we see a single newline token between text tokens, skip it. 102 Token Newline = P.Tok; 103 P.consumeToken(); 104 if (P.Tok.isNot(tok::text)) { 105 P.putBack(Newline); 106 NoMoreInterestingTokens = true; 107 return false; 108 } 109 } 110 if (P.Tok.isNot(tok::text)) { 111 NoMoreInterestingTokens = true; 112 return false; 113 } 114 115 Toks.push_back(P.Tok); 116 P.consumeToken(); 117 if (Toks.size() == 1) 118 setupBuffer(); 119 return true; 120 } 121 122 void consumeWhitespace() { 123 while (!isEnd()) { 124 if (isWhitespace(peek())) 125 consumeChar(); 126 else 127 break; 128 } 129 } 130 131 void formTokenWithChars(Token &Result, 132 SourceLocation Loc, 133 const char *TokBegin, 134 unsigned TokLength, 135 StringRef Text) { 136 Result.setLocation(Loc); 137 Result.setKind(tok::text); 138 Result.setLength(TokLength); 139 #ifndef NDEBUG 140 Result.TextPtr = "<UNSET>"; 141 Result.IntVal = 7; 142 #endif 143 Result.setText(Text); 144 } 145 146 public: 147 TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P): 148 Allocator(Allocator), P(P), NoMoreInterestingTokens(false) { 149 Pos.CurToken = 0; 150 addToken(); 151 } 152 153 /// Extract a word -- sequence of non-whitespace characters. 154 bool lexWord(Token &Tok) { 155 if (isEnd()) 156 return false; 157 158 Position SavedPos = Pos; 159 160 consumeWhitespace(); 161 SmallString<32> WordText; 162 const char *WordBegin = Pos.BufferPtr; 163 SourceLocation Loc = getSourceLocation(); 164 while (!isEnd()) { 165 const char C = peek(); 166 if (!isWhitespace(C)) { 167 WordText.push_back(C); 168 consumeChar(); 169 } else 170 break; 171 } 172 const unsigned Length = WordText.size(); 173 if (Length == 0) { 174 Pos = SavedPos; 175 return false; 176 } 177 178 char *TextPtr = Allocator.Allocate<char>(Length + 1); 179 180 memcpy(TextPtr, WordText.c_str(), Length + 1); 181 StringRef Text = StringRef(TextPtr, Length); 182 183 formTokenWithChars(Tok, Loc, WordBegin, Length, Text); 184 return true; 185 } 186 187 bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) { 188 if (isEnd()) 189 return false; 190 191 Position SavedPos = Pos; 192 193 consumeWhitespace(); 194 SmallString<32> WordText; 195 const char *WordBegin = Pos.BufferPtr; 196 SourceLocation Loc = getSourceLocation(); 197 bool Error = false; 198 if (!isEnd()) { 199 const char C = peek(); 200 if (C == OpenDelim) { 201 WordText.push_back(C); 202 consumeChar(); 203 } else 204 Error = true; 205 } 206 char C = '\0'; 207 while (!Error && !isEnd()) { 208 C = peek(); 209 WordText.push_back(C); 210 consumeChar(); 211 if (C == CloseDelim) 212 break; 213 } 214 if (!Error && C != CloseDelim) 215 Error = true; 216 217 if (Error) { 218 Pos = SavedPos; 219 return false; 220 } 221 222 const unsigned Length = WordText.size(); 223 char *TextPtr = Allocator.Allocate<char>(Length + 1); 224 225 memcpy(TextPtr, WordText.c_str(), Length + 1); 226 StringRef Text = StringRef(TextPtr, Length); 227 228 formTokenWithChars(Tok, Loc, WordBegin, 229 Pos.BufferPtr - WordBegin, Text); 230 return true; 231 } 232 233 /// Put back tokens that we didn't consume. 234 void putBackLeftoverTokens() { 235 if (isEnd()) 236 return; 237 238 bool HavePartialTok = false; 239 Token PartialTok; 240 if (Pos.BufferPtr != Pos.BufferStart) { 241 formTokenWithChars(PartialTok, getSourceLocation(), 242 Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr, 243 StringRef(Pos.BufferPtr, 244 Pos.BufferEnd - Pos.BufferPtr)); 245 HavePartialTok = true; 246 Pos.CurToken++; 247 } 248 249 P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end())); 250 Pos.CurToken = Toks.size(); 251 252 if (HavePartialTok) 253 P.putBack(PartialTok); 254 } 255 }; 256 257 Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator, 258 const SourceManager &SourceMgr, DiagnosticsEngine &Diags, 259 const CommandTraits &Traits): 260 L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), 261 Traits(Traits) { 262 consumeToken(); 263 } 264 265 void Parser::parseParamCommandArgs(ParamCommandComment *PC, 266 TextTokenRetokenizer &Retokenizer) { 267 Token Arg; 268 // Check if argument looks like direction specification: [dir] 269 // e.g., [in], [out], [in,out] 270 if (Retokenizer.lexDelimitedSeq(Arg, '[', ']')) 271 S.actOnParamCommandDirectionArg(PC, 272 Arg.getLocation(), 273 Arg.getEndLocation(), 274 Arg.getText()); 275 276 if (Retokenizer.lexWord(Arg)) 277 S.actOnParamCommandParamNameArg(PC, 278 Arg.getLocation(), 279 Arg.getEndLocation(), 280 Arg.getText()); 281 } 282 283 void Parser::parseTParamCommandArgs(TParamCommandComment *TPC, 284 TextTokenRetokenizer &Retokenizer) { 285 Token Arg; 286 if (Retokenizer.lexWord(Arg)) 287 S.actOnTParamCommandParamNameArg(TPC, 288 Arg.getLocation(), 289 Arg.getEndLocation(), 290 Arg.getText()); 291 } 292 293 void Parser::parseBlockCommandArgs(BlockCommandComment *BC, 294 TextTokenRetokenizer &Retokenizer, 295 unsigned NumArgs) { 296 typedef BlockCommandComment::Argument Argument; 297 Argument *Args = 298 new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs]; 299 unsigned ParsedArgs = 0; 300 Token Arg; 301 while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) { 302 Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(), 303 Arg.getEndLocation()), 304 Arg.getText()); 305 ParsedArgs++; 306 } 307 308 S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs)); 309 } 310 311 BlockCommandComment *Parser::parseBlockCommand() { 312 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command)); 313 314 ParamCommandComment *PC = nullptr; 315 TParamCommandComment *TPC = nullptr; 316 BlockCommandComment *BC = nullptr; 317 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID()); 318 CommandMarkerKind CommandMarker = 319 Tok.is(tok::backslash_command) ? CMK_Backslash : CMK_At; 320 if (Info->IsParamCommand) { 321 PC = S.actOnParamCommandStart(Tok.getLocation(), 322 Tok.getEndLocation(), 323 Tok.getCommandID(), 324 CommandMarker); 325 } else if (Info->IsTParamCommand) { 326 TPC = S.actOnTParamCommandStart(Tok.getLocation(), 327 Tok.getEndLocation(), 328 Tok.getCommandID(), 329 CommandMarker); 330 } else { 331 BC = S.actOnBlockCommandStart(Tok.getLocation(), 332 Tok.getEndLocation(), 333 Tok.getCommandID(), 334 CommandMarker); 335 } 336 consumeToken(); 337 338 if (isTokBlockCommand()) { 339 // Block command ahead. We can't nest block commands, so pretend that this 340 // command has an empty argument. 341 ParagraphComment *Paragraph = S.actOnParagraphComment(None); 342 if (PC) { 343 S.actOnParamCommandFinish(PC, Paragraph); 344 return PC; 345 } else if (TPC) { 346 S.actOnTParamCommandFinish(TPC, Paragraph); 347 return TPC; 348 } else { 349 S.actOnBlockCommandFinish(BC, Paragraph); 350 return BC; 351 } 352 } 353 354 if (PC || TPC || Info->NumArgs > 0) { 355 // In order to parse command arguments we need to retokenize a few 356 // following text tokens. 357 TextTokenRetokenizer Retokenizer(Allocator, *this); 358 359 if (PC) 360 parseParamCommandArgs(PC, Retokenizer); 361 else if (TPC) 362 parseTParamCommandArgs(TPC, Retokenizer); 363 else 364 parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs); 365 366 Retokenizer.putBackLeftoverTokens(); 367 } 368 369 // If there's a block command ahead, we will attach an empty paragraph to 370 // this command. 371 bool EmptyParagraph = false; 372 if (isTokBlockCommand()) 373 EmptyParagraph = true; 374 else if (Tok.is(tok::newline)) { 375 Token PrevTok = Tok; 376 consumeToken(); 377 EmptyParagraph = isTokBlockCommand(); 378 putBack(PrevTok); 379 } 380 381 ParagraphComment *Paragraph; 382 if (EmptyParagraph) 383 Paragraph = S.actOnParagraphComment(None); 384 else { 385 BlockContentComment *Block = parseParagraphOrBlockCommand(); 386 // Since we have checked for a block command, we should have parsed a 387 // paragraph. 388 Paragraph = cast<ParagraphComment>(Block); 389 } 390 391 if (PC) { 392 S.actOnParamCommandFinish(PC, Paragraph); 393 return PC; 394 } else if (TPC) { 395 S.actOnTParamCommandFinish(TPC, Paragraph); 396 return TPC; 397 } else { 398 S.actOnBlockCommandFinish(BC, Paragraph); 399 return BC; 400 } 401 } 402 403 InlineCommandComment *Parser::parseInlineCommand() { 404 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command)); 405 406 const Token CommandTok = Tok; 407 consumeToken(); 408 409 TextTokenRetokenizer Retokenizer(Allocator, *this); 410 411 Token ArgTok; 412 bool ArgTokValid = Retokenizer.lexWord(ArgTok); 413 414 InlineCommandComment *IC; 415 if (ArgTokValid) { 416 IC = S.actOnInlineCommand(CommandTok.getLocation(), 417 CommandTok.getEndLocation(), 418 CommandTok.getCommandID(), 419 ArgTok.getLocation(), 420 ArgTok.getEndLocation(), 421 ArgTok.getText()); 422 } else { 423 IC = S.actOnInlineCommand(CommandTok.getLocation(), 424 CommandTok.getEndLocation(), 425 CommandTok.getCommandID()); 426 } 427 428 Retokenizer.putBackLeftoverTokens(); 429 430 return IC; 431 } 432 433 HTMLStartTagComment *Parser::parseHTMLStartTag() { 434 assert(Tok.is(tok::html_start_tag)); 435 HTMLStartTagComment *HST = 436 S.actOnHTMLStartTagStart(Tok.getLocation(), 437 Tok.getHTMLTagStartName()); 438 consumeToken(); 439 440 SmallVector<HTMLStartTagComment::Attribute, 2> Attrs; 441 while (true) { 442 switch (Tok.getKind()) { 443 case tok::html_ident: { 444 Token Ident = Tok; 445 consumeToken(); 446 if (Tok.isNot(tok::html_equals)) { 447 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(), 448 Ident.getHTMLIdent())); 449 continue; 450 } 451 Token Equals = Tok; 452 consumeToken(); 453 if (Tok.isNot(tok::html_quoted_string)) { 454 Diag(Tok.getLocation(), 455 diag::warn_doc_html_start_tag_expected_quoted_string) 456 << SourceRange(Equals.getLocation()); 457 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(), 458 Ident.getHTMLIdent())); 459 while (Tok.is(tok::html_equals) || 460 Tok.is(tok::html_quoted_string)) 461 consumeToken(); 462 continue; 463 } 464 Attrs.push_back(HTMLStartTagComment::Attribute( 465 Ident.getLocation(), 466 Ident.getHTMLIdent(), 467 Equals.getLocation(), 468 SourceRange(Tok.getLocation(), 469 Tok.getEndLocation()), 470 Tok.getHTMLQuotedString())); 471 consumeToken(); 472 continue; 473 } 474 475 case tok::html_greater: 476 S.actOnHTMLStartTagFinish(HST, 477 S.copyArray(llvm::makeArrayRef(Attrs)), 478 Tok.getLocation(), 479 /* IsSelfClosing = */ false); 480 consumeToken(); 481 return HST; 482 483 case tok::html_slash_greater: 484 S.actOnHTMLStartTagFinish(HST, 485 S.copyArray(llvm::makeArrayRef(Attrs)), 486 Tok.getLocation(), 487 /* IsSelfClosing = */ true); 488 consumeToken(); 489 return HST; 490 491 case tok::html_equals: 492 case tok::html_quoted_string: 493 Diag(Tok.getLocation(), 494 diag::warn_doc_html_start_tag_expected_ident_or_greater); 495 while (Tok.is(tok::html_equals) || 496 Tok.is(tok::html_quoted_string)) 497 consumeToken(); 498 if (Tok.is(tok::html_ident) || 499 Tok.is(tok::html_greater) || 500 Tok.is(tok::html_slash_greater)) 501 continue; 502 503 S.actOnHTMLStartTagFinish(HST, 504 S.copyArray(llvm::makeArrayRef(Attrs)), 505 SourceLocation(), 506 /* IsSelfClosing = */ false); 507 return HST; 508 509 default: 510 // Not a token from an HTML start tag. Thus HTML tag prematurely ended. 511 S.actOnHTMLStartTagFinish(HST, 512 S.copyArray(llvm::makeArrayRef(Attrs)), 513 SourceLocation(), 514 /* IsSelfClosing = */ false); 515 bool StartLineInvalid; 516 const unsigned StartLine = SourceMgr.getPresumedLineNumber( 517 HST->getLocation(), 518 &StartLineInvalid); 519 bool EndLineInvalid; 520 const unsigned EndLine = SourceMgr.getPresumedLineNumber( 521 Tok.getLocation(), 522 &EndLineInvalid); 523 if (StartLineInvalid || EndLineInvalid || StartLine == EndLine) 524 Diag(Tok.getLocation(), 525 diag::warn_doc_html_start_tag_expected_ident_or_greater) 526 << HST->getSourceRange(); 527 else { 528 Diag(Tok.getLocation(), 529 diag::warn_doc_html_start_tag_expected_ident_or_greater); 530 Diag(HST->getLocation(), diag::note_doc_html_tag_started_here) 531 << HST->getSourceRange(); 532 } 533 return HST; 534 } 535 } 536 } 537 538 HTMLEndTagComment *Parser::parseHTMLEndTag() { 539 assert(Tok.is(tok::html_end_tag)); 540 Token TokEndTag = Tok; 541 consumeToken(); 542 SourceLocation Loc; 543 if (Tok.is(tok::html_greater)) { 544 Loc = Tok.getLocation(); 545 consumeToken(); 546 } 547 548 return S.actOnHTMLEndTag(TokEndTag.getLocation(), 549 Loc, 550 TokEndTag.getHTMLTagEndName()); 551 } 552 553 BlockContentComment *Parser::parseParagraphOrBlockCommand() { 554 SmallVector<InlineContentComment *, 8> Content; 555 556 while (true) { 557 switch (Tok.getKind()) { 558 case tok::verbatim_block_begin: 559 case tok::verbatim_line_name: 560 case tok::eof: 561 assert(Content.size() != 0); 562 break; // Block content or EOF ahead, finish this parapgaph. 563 564 case tok::unknown_command: 565 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(), 566 Tok.getEndLocation(), 567 Tok.getUnknownCommandName())); 568 consumeToken(); 569 continue; 570 571 case tok::backslash_command: 572 case tok::at_command: { 573 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID()); 574 if (Info->IsBlockCommand) { 575 if (Content.size() == 0) 576 return parseBlockCommand(); 577 break; // Block command ahead, finish this parapgaph. 578 } 579 if (Info->IsVerbatimBlockEndCommand) { 580 Diag(Tok.getLocation(), 581 diag::warn_verbatim_block_end_without_start) 582 << Tok.is(tok::at_command) 583 << Info->Name 584 << SourceRange(Tok.getLocation(), Tok.getEndLocation()); 585 consumeToken(); 586 continue; 587 } 588 if (Info->IsUnknownCommand) { 589 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(), 590 Tok.getEndLocation(), 591 Info->getID())); 592 consumeToken(); 593 continue; 594 } 595 assert(Info->IsInlineCommand); 596 Content.push_back(parseInlineCommand()); 597 continue; 598 } 599 600 case tok::newline: { 601 consumeToken(); 602 if (Tok.is(tok::newline) || Tok.is(tok::eof)) { 603 consumeToken(); 604 break; // Two newlines -- end of paragraph. 605 } 606 // Also allow [tok::newline, tok::text, tok::newline] if the middle 607 // tok::text is just whitespace. 608 if (Tok.is(tok::text) && isWhitespace(Tok.getText())) { 609 Token WhitespaceTok = Tok; 610 consumeToken(); 611 if (Tok.is(tok::newline) || Tok.is(tok::eof)) { 612 consumeToken(); 613 break; 614 } 615 // We have [tok::newline, tok::text, non-newline]. Put back tok::text. 616 putBack(WhitespaceTok); 617 } 618 if (Content.size() > 0) 619 Content.back()->addTrailingNewline(); 620 continue; 621 } 622 623 // Don't deal with HTML tag soup now. 624 case tok::html_start_tag: 625 Content.push_back(parseHTMLStartTag()); 626 continue; 627 628 case tok::html_end_tag: 629 Content.push_back(parseHTMLEndTag()); 630 continue; 631 632 case tok::text: 633 Content.push_back(S.actOnText(Tok.getLocation(), 634 Tok.getEndLocation(), 635 Tok.getText())); 636 consumeToken(); 637 continue; 638 639 case tok::verbatim_block_line: 640 case tok::verbatim_block_end: 641 case tok::verbatim_line_text: 642 case tok::html_ident: 643 case tok::html_equals: 644 case tok::html_quoted_string: 645 case tok::html_greater: 646 case tok::html_slash_greater: 647 llvm_unreachable("should not see this token"); 648 } 649 break; 650 } 651 652 return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content))); 653 } 654 655 VerbatimBlockComment *Parser::parseVerbatimBlock() { 656 assert(Tok.is(tok::verbatim_block_begin)); 657 658 VerbatimBlockComment *VB = 659 S.actOnVerbatimBlockStart(Tok.getLocation(), 660 Tok.getVerbatimBlockID()); 661 consumeToken(); 662 663 // Don't create an empty line if verbatim opening command is followed 664 // by a newline. 665 if (Tok.is(tok::newline)) 666 consumeToken(); 667 668 SmallVector<VerbatimBlockLineComment *, 8> Lines; 669 while (Tok.is(tok::verbatim_block_line) || 670 Tok.is(tok::newline)) { 671 VerbatimBlockLineComment *Line; 672 if (Tok.is(tok::verbatim_block_line)) { 673 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), 674 Tok.getVerbatimBlockText()); 675 consumeToken(); 676 if (Tok.is(tok::newline)) { 677 consumeToken(); 678 } 679 } else { 680 // Empty line, just a tok::newline. 681 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), ""); 682 consumeToken(); 683 } 684 Lines.push_back(Line); 685 } 686 687 if (Tok.is(tok::verbatim_block_end)) { 688 const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID()); 689 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(), 690 Info->Name, 691 S.copyArray(llvm::makeArrayRef(Lines))); 692 consumeToken(); 693 } else { 694 // Unterminated \\verbatim block 695 S.actOnVerbatimBlockFinish(VB, SourceLocation(), "", 696 S.copyArray(llvm::makeArrayRef(Lines))); 697 } 698 699 return VB; 700 } 701 702 VerbatimLineComment *Parser::parseVerbatimLine() { 703 assert(Tok.is(tok::verbatim_line_name)); 704 705 Token NameTok = Tok; 706 consumeToken(); 707 708 SourceLocation TextBegin; 709 StringRef Text; 710 // Next token might not be a tok::verbatim_line_text if verbatim line 711 // starting command comes just before a newline or comment end. 712 if (Tok.is(tok::verbatim_line_text)) { 713 TextBegin = Tok.getLocation(); 714 Text = Tok.getVerbatimLineText(); 715 } else { 716 TextBegin = NameTok.getEndLocation(); 717 Text = ""; 718 } 719 720 VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(), 721 NameTok.getVerbatimLineID(), 722 TextBegin, 723 Text); 724 consumeToken(); 725 return VL; 726 } 727 728 BlockContentComment *Parser::parseBlockContent() { 729 switch (Tok.getKind()) { 730 case tok::text: 731 case tok::unknown_command: 732 case tok::backslash_command: 733 case tok::at_command: 734 case tok::html_start_tag: 735 case tok::html_end_tag: 736 return parseParagraphOrBlockCommand(); 737 738 case tok::verbatim_block_begin: 739 return parseVerbatimBlock(); 740 741 case tok::verbatim_line_name: 742 return parseVerbatimLine(); 743 744 case tok::eof: 745 case tok::newline: 746 case tok::verbatim_block_line: 747 case tok::verbatim_block_end: 748 case tok::verbatim_line_text: 749 case tok::html_ident: 750 case tok::html_equals: 751 case tok::html_quoted_string: 752 case tok::html_greater: 753 case tok::html_slash_greater: 754 llvm_unreachable("should not see this token"); 755 } 756 llvm_unreachable("bogus token kind"); 757 } 758 759 FullComment *Parser::parseFullComment() { 760 // Skip newlines at the beginning of the comment. 761 while (Tok.is(tok::newline)) 762 consumeToken(); 763 764 SmallVector<BlockContentComment *, 8> Blocks; 765 while (Tok.isNot(tok::eof)) { 766 Blocks.push_back(parseBlockContent()); 767 768 // Skip extra newlines after paragraph end. 769 while (Tok.is(tok::newline)) 770 consumeToken(); 771 } 772 return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks))); 773 } 774 775 } // end namespace comments 776 } // end namespace clang 777