clang API Documentation

CommentParser.cpp
Go to the documentation of this file.
00001 //===--- CommentParser.cpp - Doxygen comment parser -----------------------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 
00010 #include "clang/AST/CommentParser.h"
00011 #include "clang/AST/CommentCommandTraits.h"
00012 #include "clang/AST/CommentDiagnostic.h"
00013 #include "clang/AST/CommentSema.h"
00014 #include "clang/Basic/CharInfo.h"
00015 #include "clang/Basic/SourceManager.h"
00016 #include "llvm/Support/ErrorHandling.h"
00017 
00018 namespace clang {
00019 
00020 static inline bool isWhitespace(llvm::StringRef S) {
00021   for (StringRef::const_iterator I = S.begin(), E = S.end(); I != E; ++I) {
00022     if (!isWhitespace(*I))
00023       return false;
00024   }
00025   return true;
00026 }
00027 
00028 namespace comments {
00029 
00030 /// Re-lexes a sequence of tok::text tokens.
00031 class TextTokenRetokenizer {
00032   llvm::BumpPtrAllocator &Allocator;
00033   Parser &P;
00034 
00035   /// This flag is set when there are no more tokens we can fetch from lexer.
00036   bool NoMoreInterestingTokens;
00037 
00038   /// Token buffer: tokens we have processed and lookahead.
00039   SmallVector<Token, 16> Toks;
00040 
00041   /// A position in \c Toks.
00042   struct Position {
00043     unsigned CurToken;
00044     const char *BufferStart;
00045     const char *BufferEnd;
00046     const char *BufferPtr;
00047     SourceLocation BufferStartLoc;
00048   };
00049 
00050   /// Current position in Toks.
00051   Position Pos;
00052 
00053   bool isEnd() const {
00054     return Pos.CurToken >= Toks.size();
00055   }
00056 
00057   /// Sets up the buffer pointers to point to current token.
00058   void setupBuffer() {
00059     assert(!isEnd());
00060     const Token &Tok = Toks[Pos.CurToken];
00061 
00062     Pos.BufferStart = Tok.getText().begin();
00063     Pos.BufferEnd = Tok.getText().end();
00064     Pos.BufferPtr = Pos.BufferStart;
00065     Pos.BufferStartLoc = Tok.getLocation();
00066   }
00067 
00068   SourceLocation getSourceLocation() const {
00069     const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
00070     return Pos.BufferStartLoc.getLocWithOffset(CharNo);
00071   }
00072 
00073   char peek() const {
00074     assert(!isEnd());
00075     assert(Pos.BufferPtr != Pos.BufferEnd);
00076     return *Pos.BufferPtr;
00077   }
00078 
00079   void consumeChar() {
00080     assert(!isEnd());
00081     assert(Pos.BufferPtr != Pos.BufferEnd);
00082     Pos.BufferPtr++;
00083     if (Pos.BufferPtr == Pos.BufferEnd) {
00084       Pos.CurToken++;
00085       if (isEnd() && !addToken())
00086         return;
00087 
00088       assert(!isEnd());
00089       setupBuffer();
00090     }
00091   }
00092 
00093   /// Add a token.
00094   /// Returns true on success, false if there are no interesting tokens to
00095   /// fetch from lexer.
00096   bool addToken() {
00097     if (NoMoreInterestingTokens)
00098       return false;
00099 
00100     if (P.Tok.is(tok::newline)) {
00101       // If we see a single newline token between text tokens, skip it.
00102       Token Newline = P.Tok;
00103       P.consumeToken();
00104       if (P.Tok.isNot(tok::text)) {
00105         P.putBack(Newline);
00106         NoMoreInterestingTokens = true;
00107         return false;
00108       }
00109     }
00110     if (P.Tok.isNot(tok::text)) {
00111       NoMoreInterestingTokens = true;
00112       return false;
00113     }
00114 
00115     Toks.push_back(P.Tok);
00116     P.consumeToken();
00117     if (Toks.size() == 1)
00118       setupBuffer();
00119     return true;
00120   }
00121 
00122   void consumeWhitespace() {
00123     while (!isEnd()) {
00124       if (isWhitespace(peek()))
00125         consumeChar();
00126       else
00127         break;
00128     }
00129   }
00130 
00131   void formTokenWithChars(Token &Result,
00132                           SourceLocation Loc,
00133                           const char *TokBegin,
00134                           unsigned TokLength,
00135                           StringRef Text) {
00136     Result.setLocation(Loc);
00137     Result.setKind(tok::text);
00138     Result.setLength(TokLength);
00139 #ifndef NDEBUG
00140     Result.TextPtr = "<UNSET>";
00141     Result.IntVal = 7;
00142 #endif
00143     Result.setText(Text);
00144   }
00145 
00146 public:
00147   TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
00148       Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
00149     Pos.CurToken = 0;
00150     addToken();
00151   }
00152 
00153   /// Extract a word -- sequence of non-whitespace characters.
00154   bool lexWord(Token &Tok) {
00155     if (isEnd())
00156       return false;
00157 
00158     Position SavedPos = Pos;
00159 
00160     consumeWhitespace();
00161     SmallString<32> WordText;
00162     const char *WordBegin = Pos.BufferPtr;
00163     SourceLocation Loc = getSourceLocation();
00164     while (!isEnd()) {
00165       const char C = peek();
00166       if (!isWhitespace(C)) {
00167         WordText.push_back(C);
00168         consumeChar();
00169       } else
00170         break;
00171     }
00172     const unsigned Length = WordText.size();
00173     if (Length == 0) {
00174       Pos = SavedPos;
00175       return false;
00176     }
00177 
00178     char *TextPtr = Allocator.Allocate<char>(Length + 1);
00179 
00180     memcpy(TextPtr, WordText.c_str(), Length + 1);
00181     StringRef Text = StringRef(TextPtr, Length);
00182 
00183     formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
00184     return true;
00185   }
00186 
00187   bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
00188     if (isEnd())
00189       return false;
00190 
00191     Position SavedPos = Pos;
00192 
00193     consumeWhitespace();
00194     SmallString<32> WordText;
00195     const char *WordBegin = Pos.BufferPtr;
00196     SourceLocation Loc = getSourceLocation();
00197     bool Error = false;
00198     if (!isEnd()) {
00199       const char C = peek();
00200       if (C == OpenDelim) {
00201         WordText.push_back(C);
00202         consumeChar();
00203       } else
00204         Error = true;
00205     }
00206     char C = '\0';
00207     while (!Error && !isEnd()) {
00208       C = peek();
00209       WordText.push_back(C);
00210       consumeChar();
00211       if (C == CloseDelim)
00212         break;
00213     }
00214     if (!Error && C != CloseDelim)
00215       Error = true;
00216 
00217     if (Error) {
00218       Pos = SavedPos;
00219       return false;
00220     }
00221 
00222     const unsigned Length = WordText.size();
00223     char *TextPtr = Allocator.Allocate<char>(Length + 1);
00224 
00225     memcpy(TextPtr, WordText.c_str(), Length + 1);
00226     StringRef Text = StringRef(TextPtr, Length);
00227 
00228     formTokenWithChars(Tok, Loc, WordBegin,
00229                        Pos.BufferPtr - WordBegin, Text);
00230     return true;
00231   }
00232 
00233   /// Put back tokens that we didn't consume.
00234   void putBackLeftoverTokens() {
00235     if (isEnd())
00236       return;
00237 
00238     bool HavePartialTok = false;
00239     Token PartialTok;
00240     if (Pos.BufferPtr != Pos.BufferStart) {
00241       formTokenWithChars(PartialTok, getSourceLocation(),
00242                          Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
00243                          StringRef(Pos.BufferPtr,
00244                                    Pos.BufferEnd - Pos.BufferPtr));
00245       HavePartialTok = true;
00246       Pos.CurToken++;
00247     }
00248 
00249     P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
00250     Pos.CurToken = Toks.size();
00251 
00252     if (HavePartialTok)
00253       P.putBack(PartialTok);
00254   }
00255 };
00256 
00257 Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
00258                const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
00259                const CommandTraits &Traits):
00260     L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
00261     Traits(Traits) {
00262   consumeToken();
00263 }
00264 
00265 void Parser::parseParamCommandArgs(ParamCommandComment *PC,
00266                                    TextTokenRetokenizer &Retokenizer) {
00267   Token Arg;
00268   // Check if argument looks like direction specification: [dir]
00269   // e.g., [in], [out], [in,out]
00270   if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
00271     S.actOnParamCommandDirectionArg(PC,
00272                                     Arg.getLocation(),
00273                                     Arg.getEndLocation(),
00274                                     Arg.getText());
00275 
00276   if (Retokenizer.lexWord(Arg))
00277     S.actOnParamCommandParamNameArg(PC,
00278                                     Arg.getLocation(),
00279                                     Arg.getEndLocation(),
00280                                     Arg.getText());
00281 }
00282 
00283 void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
00284                                     TextTokenRetokenizer &Retokenizer) {
00285   Token Arg;
00286   if (Retokenizer.lexWord(Arg))
00287     S.actOnTParamCommandParamNameArg(TPC,
00288                                      Arg.getLocation(),
00289                                      Arg.getEndLocation(),
00290                                      Arg.getText());
00291 }
00292 
00293 void Parser::parseBlockCommandArgs(BlockCommandComment *BC,
00294                                    TextTokenRetokenizer &Retokenizer,
00295                                    unsigned NumArgs) {
00296   typedef BlockCommandComment::Argument Argument;
00297   Argument *Args =
00298       new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs];
00299   unsigned ParsedArgs = 0;
00300   Token Arg;
00301   while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
00302     Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(),
00303                                             Arg.getEndLocation()),
00304                                 Arg.getText());
00305     ParsedArgs++;
00306   }
00307 
00308   S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
00309 }
00310 
00311 BlockCommandComment *Parser::parseBlockCommand() {
00312   assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
00313 
00314   ParamCommandComment *PC = nullptr;
00315   TParamCommandComment *TPC = nullptr;
00316   BlockCommandComment *BC = nullptr;
00317   const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
00318   CommandMarkerKind CommandMarker =
00319       Tok.is(tok::backslash_command) ? CMK_Backslash : CMK_At;
00320   if (Info->IsParamCommand) {
00321     PC = S.actOnParamCommandStart(Tok.getLocation(),
00322                                   Tok.getEndLocation(),
00323                                   Tok.getCommandID(),
00324                                   CommandMarker);
00325   } else if (Info->IsTParamCommand) {
00326     TPC = S.actOnTParamCommandStart(Tok.getLocation(),
00327                                     Tok.getEndLocation(),
00328                                     Tok.getCommandID(),
00329                                     CommandMarker);
00330   } else {
00331     BC = S.actOnBlockCommandStart(Tok.getLocation(),
00332                                   Tok.getEndLocation(),
00333                                   Tok.getCommandID(),
00334                                   CommandMarker);
00335   }
00336   consumeToken();
00337 
00338   if (isTokBlockCommand()) {
00339     // Block command ahead.  We can't nest block commands, so pretend that this
00340     // command has an empty argument.
00341     ParagraphComment *Paragraph = S.actOnParagraphComment(None);
00342     if (PC) {
00343       S.actOnParamCommandFinish(PC, Paragraph);
00344       return PC;
00345     } else if (TPC) {
00346       S.actOnTParamCommandFinish(TPC, Paragraph);
00347       return TPC;
00348     } else {
00349       S.actOnBlockCommandFinish(BC, Paragraph);
00350       return BC;
00351     }
00352   }
00353 
00354   if (PC || TPC || Info->NumArgs > 0) {
00355     // In order to parse command arguments we need to retokenize a few
00356     // following text tokens.
00357     TextTokenRetokenizer Retokenizer(Allocator, *this);
00358 
00359     if (PC)
00360       parseParamCommandArgs(PC, Retokenizer);
00361     else if (TPC)
00362       parseTParamCommandArgs(TPC, Retokenizer);
00363     else
00364       parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs);
00365 
00366     Retokenizer.putBackLeftoverTokens();
00367   }
00368 
00369   // If there's a block command ahead, we will attach an empty paragraph to
00370   // this command.
00371   bool EmptyParagraph = false;
00372   if (isTokBlockCommand())
00373     EmptyParagraph = true;
00374   else if (Tok.is(tok::newline)) {
00375     Token PrevTok = Tok;
00376     consumeToken();
00377     EmptyParagraph = isTokBlockCommand();
00378     putBack(PrevTok);
00379   }
00380 
00381   ParagraphComment *Paragraph;
00382   if (EmptyParagraph)
00383     Paragraph = S.actOnParagraphComment(None);
00384   else {
00385     BlockContentComment *Block = parseParagraphOrBlockCommand();
00386     // Since we have checked for a block command, we should have parsed a
00387     // paragraph.
00388     Paragraph = cast<ParagraphComment>(Block);
00389   }
00390 
00391   if (PC) {
00392     S.actOnParamCommandFinish(PC, Paragraph);
00393     return PC;
00394   } else if (TPC) {
00395     S.actOnTParamCommandFinish(TPC, Paragraph);
00396     return TPC;
00397   } else {
00398     S.actOnBlockCommandFinish(BC, Paragraph);
00399     return BC;
00400   }
00401 }
00402 
00403 InlineCommandComment *Parser::parseInlineCommand() {
00404   assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
00405 
00406   const Token CommandTok = Tok;
00407   consumeToken();
00408 
00409   TextTokenRetokenizer Retokenizer(Allocator, *this);
00410 
00411   Token ArgTok;
00412   bool ArgTokValid = Retokenizer.lexWord(ArgTok);
00413 
00414   InlineCommandComment *IC;
00415   if (ArgTokValid) {
00416     IC = S.actOnInlineCommand(CommandTok.getLocation(),
00417                               CommandTok.getEndLocation(),
00418                               CommandTok.getCommandID(),
00419                               ArgTok.getLocation(),
00420                               ArgTok.getEndLocation(),
00421                               ArgTok.getText());
00422   } else {
00423     IC = S.actOnInlineCommand(CommandTok.getLocation(),
00424                               CommandTok.getEndLocation(),
00425                               CommandTok.getCommandID());
00426   }
00427 
00428   Retokenizer.putBackLeftoverTokens();
00429 
00430   return IC;
00431 }
00432 
00433 HTMLStartTagComment *Parser::parseHTMLStartTag() {
00434   assert(Tok.is(tok::html_start_tag));
00435   HTMLStartTagComment *HST =
00436       S.actOnHTMLStartTagStart(Tok.getLocation(),
00437                                Tok.getHTMLTagStartName());
00438   consumeToken();
00439 
00440   SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
00441   while (true) {
00442     switch (Tok.getKind()) {
00443     case tok::html_ident: {
00444       Token Ident = Tok;
00445       consumeToken();
00446       if (Tok.isNot(tok::html_equals)) {
00447         Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
00448                                                        Ident.getHTMLIdent()));
00449         continue;
00450       }
00451       Token Equals = Tok;
00452       consumeToken();
00453       if (Tok.isNot(tok::html_quoted_string)) {
00454         Diag(Tok.getLocation(),
00455              diag::warn_doc_html_start_tag_expected_quoted_string)
00456           << SourceRange(Equals.getLocation());
00457         Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
00458                                                        Ident.getHTMLIdent()));
00459         while (Tok.is(tok::html_equals) ||
00460                Tok.is(tok::html_quoted_string))
00461           consumeToken();
00462         continue;
00463       }
00464       Attrs.push_back(HTMLStartTagComment::Attribute(
00465                               Ident.getLocation(),
00466                               Ident.getHTMLIdent(),
00467                               Equals.getLocation(),
00468                               SourceRange(Tok.getLocation(),
00469                                           Tok.getEndLocation()),
00470                               Tok.getHTMLQuotedString()));
00471       consumeToken();
00472       continue;
00473     }
00474 
00475     case tok::html_greater:
00476       S.actOnHTMLStartTagFinish(HST,
00477                                 S.copyArray(llvm::makeArrayRef(Attrs)),
00478                                 Tok.getLocation(),
00479                                 /* IsSelfClosing = */ false);
00480       consumeToken();
00481       return HST;
00482 
00483     case tok::html_slash_greater:
00484       S.actOnHTMLStartTagFinish(HST,
00485                                 S.copyArray(llvm::makeArrayRef(Attrs)),
00486                                 Tok.getLocation(),
00487                                 /* IsSelfClosing = */ true);
00488       consumeToken();
00489       return HST;
00490 
00491     case tok::html_equals:
00492     case tok::html_quoted_string:
00493       Diag(Tok.getLocation(),
00494            diag::warn_doc_html_start_tag_expected_ident_or_greater);
00495       while (Tok.is(tok::html_equals) ||
00496              Tok.is(tok::html_quoted_string))
00497         consumeToken();
00498       if (Tok.is(tok::html_ident) ||
00499           Tok.is(tok::html_greater) ||
00500           Tok.is(tok::html_slash_greater))
00501         continue;
00502 
00503       S.actOnHTMLStartTagFinish(HST,
00504                                 S.copyArray(llvm::makeArrayRef(Attrs)),
00505                                 SourceLocation(),
00506                                 /* IsSelfClosing = */ false);
00507       return HST;
00508 
00509     default:
00510       // Not a token from an HTML start tag.  Thus HTML tag prematurely ended.
00511       S.actOnHTMLStartTagFinish(HST,
00512                                 S.copyArray(llvm::makeArrayRef(Attrs)),
00513                                 SourceLocation(),
00514                                 /* IsSelfClosing = */ false);
00515       bool StartLineInvalid;
00516       const unsigned StartLine = SourceMgr.getPresumedLineNumber(
00517                                                   HST->getLocation(),
00518                                                   &StartLineInvalid);
00519       bool EndLineInvalid;
00520       const unsigned EndLine = SourceMgr.getPresumedLineNumber(
00521                                                   Tok.getLocation(),
00522                                                   &EndLineInvalid);
00523       if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
00524         Diag(Tok.getLocation(),
00525              diag::warn_doc_html_start_tag_expected_ident_or_greater)
00526           << HST->getSourceRange();
00527       else {
00528         Diag(Tok.getLocation(),
00529              diag::warn_doc_html_start_tag_expected_ident_or_greater);
00530         Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
00531           << HST->getSourceRange();
00532       }
00533       return HST;
00534     }
00535   }
00536 }
00537 
00538 HTMLEndTagComment *Parser::parseHTMLEndTag() {
00539   assert(Tok.is(tok::html_end_tag));
00540   Token TokEndTag = Tok;
00541   consumeToken();
00542   SourceLocation Loc;
00543   if (Tok.is(tok::html_greater)) {
00544     Loc = Tok.getLocation();
00545     consumeToken();
00546   }
00547 
00548   return S.actOnHTMLEndTag(TokEndTag.getLocation(),
00549                            Loc,
00550                            TokEndTag.getHTMLTagEndName());
00551 }
00552 
00553 BlockContentComment *Parser::parseParagraphOrBlockCommand() {
00554   SmallVector<InlineContentComment *, 8> Content;
00555 
00556   while (true) {
00557     switch (Tok.getKind()) {
00558     case tok::verbatim_block_begin:
00559     case tok::verbatim_line_name:
00560     case tok::eof:
00561       assert(Content.size() != 0);
00562       break; // Block content or EOF ahead, finish this parapgaph.
00563 
00564     case tok::unknown_command:
00565       Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
00566                                               Tok.getEndLocation(),
00567                                               Tok.getUnknownCommandName()));
00568       consumeToken();
00569       continue;
00570 
00571     case tok::backslash_command:
00572     case tok::at_command: {
00573       const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
00574       if (Info->IsBlockCommand) {
00575         if (Content.size() == 0)
00576           return parseBlockCommand();
00577         break; // Block command ahead, finish this parapgaph.
00578       }
00579       if (Info->IsVerbatimBlockEndCommand) {
00580         Diag(Tok.getLocation(),
00581              diag::warn_verbatim_block_end_without_start)
00582           << Tok.is(tok::at_command)
00583           << Info->Name
00584           << SourceRange(Tok.getLocation(), Tok.getEndLocation());
00585         consumeToken();
00586         continue;
00587       }
00588       if (Info->IsUnknownCommand) {
00589         Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
00590                                                 Tok.getEndLocation(),
00591                                                 Info->getID()));
00592         consumeToken();
00593         continue;
00594       }
00595       assert(Info->IsInlineCommand);
00596       Content.push_back(parseInlineCommand());
00597       continue;
00598     }
00599 
00600     case tok::newline: {
00601       consumeToken();
00602       if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
00603         consumeToken();
00604         break; // Two newlines -- end of paragraph.
00605       }
00606       // Also allow [tok::newline, tok::text, tok::newline] if the middle
00607       // tok::text is just whitespace.
00608       if (Tok.is(tok::text) && isWhitespace(Tok.getText())) {
00609         Token WhitespaceTok = Tok;
00610         consumeToken();
00611         if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
00612           consumeToken();
00613           break;
00614         }
00615         // We have [tok::newline, tok::text, non-newline].  Put back tok::text.
00616         putBack(WhitespaceTok);
00617       }
00618       if (Content.size() > 0)
00619         Content.back()->addTrailingNewline();
00620       continue;
00621     }
00622 
00623     // Don't deal with HTML tag soup now.
00624     case tok::html_start_tag:
00625       Content.push_back(parseHTMLStartTag());
00626       continue;
00627 
00628     case tok::html_end_tag:
00629       Content.push_back(parseHTMLEndTag());
00630       continue;
00631 
00632     case tok::text:
00633       Content.push_back(S.actOnText(Tok.getLocation(),
00634                                     Tok.getEndLocation(),
00635                                     Tok.getText()));
00636       consumeToken();
00637       continue;
00638 
00639     case tok::verbatim_block_line:
00640     case tok::verbatim_block_end:
00641     case tok::verbatim_line_text:
00642     case tok::html_ident:
00643     case tok::html_equals:
00644     case tok::html_quoted_string:
00645     case tok::html_greater:
00646     case tok::html_slash_greater:
00647       llvm_unreachable("should not see this token");
00648     }
00649     break;
00650   }
00651 
00652   return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
00653 }
00654 
00655 VerbatimBlockComment *Parser::parseVerbatimBlock() {
00656   assert(Tok.is(tok::verbatim_block_begin));
00657 
00658   VerbatimBlockComment *VB =
00659       S.actOnVerbatimBlockStart(Tok.getLocation(),
00660                                 Tok.getVerbatimBlockID());
00661   consumeToken();
00662 
00663   // Don't create an empty line if verbatim opening command is followed
00664   // by a newline.
00665   if (Tok.is(tok::newline))
00666     consumeToken();
00667 
00668   SmallVector<VerbatimBlockLineComment *, 8> Lines;
00669   while (Tok.is(tok::verbatim_block_line) ||
00670          Tok.is(tok::newline)) {
00671     VerbatimBlockLineComment *Line;
00672     if (Tok.is(tok::verbatim_block_line)) {
00673       Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
00674                                       Tok.getVerbatimBlockText());
00675       consumeToken();
00676       if (Tok.is(tok::newline)) {
00677         consumeToken();
00678       }
00679     } else {
00680       // Empty line, just a tok::newline.
00681       Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
00682       consumeToken();
00683     }
00684     Lines.push_back(Line);
00685   }
00686 
00687   if (Tok.is(tok::verbatim_block_end)) {
00688     const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
00689     S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
00690                                Info->Name,
00691                                S.copyArray(llvm::makeArrayRef(Lines)));
00692     consumeToken();
00693   } else {
00694     // Unterminated \\verbatim block
00695     S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
00696                                S.copyArray(llvm::makeArrayRef(Lines)));
00697   }
00698 
00699   return VB;
00700 }
00701 
00702 VerbatimLineComment *Parser::parseVerbatimLine() {
00703   assert(Tok.is(tok::verbatim_line_name));
00704 
00705   Token NameTok = Tok;
00706   consumeToken();
00707 
00708   SourceLocation TextBegin;
00709   StringRef Text;
00710   // Next token might not be a tok::verbatim_line_text if verbatim line
00711   // starting command comes just before a newline or comment end.
00712   if (Tok.is(tok::verbatim_line_text)) {
00713     TextBegin = Tok.getLocation();
00714     Text = Tok.getVerbatimLineText();
00715   } else {
00716     TextBegin = NameTok.getEndLocation();
00717     Text = "";
00718   }
00719 
00720   VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
00721                                                 NameTok.getVerbatimLineID(),
00722                                                 TextBegin,
00723                                                 Text);
00724   consumeToken();
00725   return VL;
00726 }
00727 
00728 BlockContentComment *Parser::parseBlockContent() {
00729   switch (Tok.getKind()) {
00730   case tok::text:
00731   case tok::unknown_command:
00732   case tok::backslash_command:
00733   case tok::at_command:
00734   case tok::html_start_tag:
00735   case tok::html_end_tag:
00736     return parseParagraphOrBlockCommand();
00737 
00738   case tok::verbatim_block_begin:
00739     return parseVerbatimBlock();
00740 
00741   case tok::verbatim_line_name:
00742     return parseVerbatimLine();
00743 
00744   case tok::eof:
00745   case tok::newline:
00746   case tok::verbatim_block_line:
00747   case tok::verbatim_block_end:
00748   case tok::verbatim_line_text:
00749   case tok::html_ident:
00750   case tok::html_equals:
00751   case tok::html_quoted_string:
00752   case tok::html_greater:
00753   case tok::html_slash_greater:
00754     llvm_unreachable("should not see this token");
00755   }
00756   llvm_unreachable("bogus token kind");
00757 }
00758 
00759 FullComment *Parser::parseFullComment() {
00760   // Skip newlines at the beginning of the comment.
00761   while (Tok.is(tok::newline))
00762     consumeToken();
00763 
00764   SmallVector<BlockContentComment *, 8> Blocks;
00765   while (Tok.isNot(tok::eof)) {
00766     Blocks.push_back(parseBlockContent());
00767 
00768     // Skip extra newlines after paragraph end.
00769     while (Tok.is(tok::newline))
00770       consumeToken();
00771   }
00772   return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
00773 }
00774 
00775 } // end namespace comments
00776 } // end namespace clang