Home | History | Annotate | Download | only in AST
      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