clang API Documentation
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