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