• 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/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 
isEnd() const44   bool isEnd() const {
45     return Pos.CurToken >= Toks.size();
46   }
47 
48   /// Sets up the buffer pointers to point to current token.
setupBuffer()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 
getSourceLocation() const59   SourceLocation getSourceLocation() const {
60     const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
61     return Pos.BufferStartLoc.getLocWithOffset(CharNo);
62   }
63 
peek() const64   char peek() const {
65     assert(!isEnd());
66     assert(Pos.BufferPtr != Pos.BufferEnd);
67     return *Pos.BufferPtr;
68   }
69 
consumeChar()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.
addToken()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 
consumeWhitespace()113   void consumeWhitespace() {
114     while (!isEnd()) {
115       if (isWhitespace(peek()))
116         consumeChar();
117       else
118         break;
119     }
120   }
121 
formTokenWithChars(Token & Result,SourceLocation Loc,const char * TokBegin,unsigned TokLength,StringRef Text)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:
TextTokenRetokenizer(llvm::BumpPtrAllocator & Allocator,Parser & P)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.
lexWord(Token & Tok)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 
lexDelimitedSeq(Token & Tok,char OpenDelim,char CloseDelim)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.
putBackLeftoverTokens()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 
Parser(Lexer & L,Sema & S,llvm::BumpPtrAllocator & Allocator,const SourceManager & SourceMgr,DiagnosticsEngine & Diags,const CommandTraits & Traits)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 
parseParamCommandArgs(ParamCommandComment * PC,TextTokenRetokenizer & Retokenizer)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 
parseTParamCommandArgs(TParamCommandComment * TPC,TextTokenRetokenizer & Retokenizer)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 
parseBlockCommandArgs(BlockCommandComment * BC,TextTokenRetokenizer & Retokenizer,unsigned NumArgs)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 
parseBlockCommand()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 
parseInlineCommand()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 
parseHTMLStartTag()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 
parseHTMLEndTag()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 
parseParagraphOrBlockCommand()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 
parseVerbatimBlock()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 
parseVerbatimLine()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 
parseBlockContent()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 
parseFullComment()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