1ec92531cSDmitri Gribenko //===--- CommentParser.cpp - Doxygen comment parser -----------------------===//
2ec92531cSDmitri Gribenko //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ec92531cSDmitri Gribenko //
7ec92531cSDmitri Gribenko //===----------------------------------------------------------------------===//
8ec92531cSDmitri Gribenko 
9ec92531cSDmitri Gribenko #include "clang/AST/CommentParser.h"
10ca7f80adSDmitri Gribenko #include "clang/AST/CommentCommandTraits.h"
113a02247dSChandler Carruth #include "clang/AST/CommentDiagnostic.h"
123a02247dSChandler Carruth #include "clang/AST/CommentSema.h"
13bcef3411SDmitri Gribenko #include "clang/Basic/CharInfo.h"
14f26054f0SDmitri Gribenko #include "clang/Basic/SourceManager.h"
15ec92531cSDmitri Gribenko #include "llvm/Support/ErrorHandling.h"
16ec92531cSDmitri Gribenko 
17ec92531cSDmitri Gribenko namespace clang {
181e50cbf3SDmitri Gribenko 
isWhitespace(llvm::StringRef S)191e50cbf3SDmitri Gribenko static inline bool isWhitespace(llvm::StringRef S) {
201e50cbf3SDmitri Gribenko   for (StringRef::const_iterator I = S.begin(), E = S.end(); I != E; ++I) {
211e50cbf3SDmitri Gribenko     if (!isWhitespace(*I))
221e50cbf3SDmitri Gribenko       return false;
231e50cbf3SDmitri Gribenko   }
241e50cbf3SDmitri Gribenko   return true;
251e50cbf3SDmitri Gribenko }
261e50cbf3SDmitri Gribenko 
27ec92531cSDmitri Gribenko namespace comments {
28ec92531cSDmitri Gribenko 
291bfd9dadSDmitri Gribenko /// Re-lexes a sequence of tok::text tokens.
301bfd9dadSDmitri Gribenko class TextTokenRetokenizer {
311bfd9dadSDmitri Gribenko   llvm::BumpPtrAllocator &Allocator;
320a36302aSDmitri Gribenko   Parser &P;
3335b0c09bSDmitri Gribenko 
3435b0c09bSDmitri Gribenko   /// This flag is set when there are no more tokens we can fetch from lexer.
3535b0c09bSDmitri Gribenko   bool NoMoreInterestingTokens;
3635b0c09bSDmitri Gribenko 
3735b0c09bSDmitri Gribenko   /// Token buffer: tokens we have processed and lookahead.
380a36302aSDmitri Gribenko   SmallVector<Token, 16> Toks;
391bfd9dadSDmitri Gribenko 
4035b0c09bSDmitri Gribenko   /// A position in \c Toks.
411bfd9dadSDmitri Gribenko   struct Position {
421bfd9dadSDmitri Gribenko     const char *BufferStart;
431bfd9dadSDmitri Gribenko     const char *BufferEnd;
441bfd9dadSDmitri Gribenko     const char *BufferPtr;
451bfd9dadSDmitri Gribenko     SourceLocation BufferStartLoc;
46c937026eSAlexander Shaposhnikov     unsigned CurToken;
471bfd9dadSDmitri Gribenko   };
481bfd9dadSDmitri Gribenko 
491bfd9dadSDmitri Gribenko   /// Current position in Toks.
501bfd9dadSDmitri Gribenko   Position Pos;
511bfd9dadSDmitri Gribenko 
isEnd() const521bfd9dadSDmitri Gribenko   bool isEnd() const {
531bfd9dadSDmitri Gribenko     return Pos.CurToken >= Toks.size();
541bfd9dadSDmitri Gribenko   }
551bfd9dadSDmitri Gribenko 
561bfd9dadSDmitri Gribenko   /// Sets up the buffer pointers to point to current token.
setupBuffer()571bfd9dadSDmitri Gribenko   void setupBuffer() {
580a36302aSDmitri Gribenko     assert(!isEnd());
591bfd9dadSDmitri Gribenko     const Token &Tok = Toks[Pos.CurToken];
601bfd9dadSDmitri Gribenko 
611bfd9dadSDmitri Gribenko     Pos.BufferStart = Tok.getText().begin();
621bfd9dadSDmitri Gribenko     Pos.BufferEnd = Tok.getText().end();
631bfd9dadSDmitri Gribenko     Pos.BufferPtr = Pos.BufferStart;
641bfd9dadSDmitri Gribenko     Pos.BufferStartLoc = Tok.getLocation();
651bfd9dadSDmitri Gribenko   }
661bfd9dadSDmitri Gribenko 
getSourceLocation() const671bfd9dadSDmitri Gribenko   SourceLocation getSourceLocation() const {
681bfd9dadSDmitri Gribenko     const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
691bfd9dadSDmitri Gribenko     return Pos.BufferStartLoc.getLocWithOffset(CharNo);
701bfd9dadSDmitri Gribenko   }
711bfd9dadSDmitri Gribenko 
peek() const721bfd9dadSDmitri Gribenko   char peek() const {
731bfd9dadSDmitri Gribenko     assert(!isEnd());
741bfd9dadSDmitri Gribenko     assert(Pos.BufferPtr != Pos.BufferEnd);
751bfd9dadSDmitri Gribenko     return *Pos.BufferPtr;
761bfd9dadSDmitri Gribenko   }
771bfd9dadSDmitri Gribenko 
consumeChar()781bfd9dadSDmitri Gribenko   void consumeChar() {
791bfd9dadSDmitri Gribenko     assert(!isEnd());
801bfd9dadSDmitri Gribenko     assert(Pos.BufferPtr != Pos.BufferEnd);
811bfd9dadSDmitri Gribenko     Pos.BufferPtr++;
821bfd9dadSDmitri Gribenko     if (Pos.BufferPtr == Pos.BufferEnd) {
831bfd9dadSDmitri Gribenko       Pos.CurToken++;
8435b0c09bSDmitri Gribenko       if (isEnd() && !addToken())
8535b0c09bSDmitri Gribenko         return;
8635b0c09bSDmitri Gribenko 
870a36302aSDmitri Gribenko       assert(!isEnd());
881bfd9dadSDmitri Gribenko       setupBuffer();
891bfd9dadSDmitri Gribenko     }
901bfd9dadSDmitri Gribenko   }
910a36302aSDmitri Gribenko 
920a36302aSDmitri Gribenko   /// Add a token.
930a36302aSDmitri Gribenko   /// Returns true on success, false if there are no interesting tokens to
940a36302aSDmitri Gribenko   /// fetch from lexer.
addToken()950a36302aSDmitri Gribenko   bool addToken() {
9635b0c09bSDmitri Gribenko     if (NoMoreInterestingTokens)
970a36302aSDmitri Gribenko       return false;
980a36302aSDmitri Gribenko 
9935b0c09bSDmitri Gribenko     if (P.Tok.is(tok::newline)) {
10035b0c09bSDmitri Gribenko       // If we see a single newline token between text tokens, skip it.
10135b0c09bSDmitri Gribenko       Token Newline = P.Tok;
10235b0c09bSDmitri Gribenko       P.consumeToken();
10335b0c09bSDmitri Gribenko       if (P.Tok.isNot(tok::text)) {
10435b0c09bSDmitri Gribenko         P.putBack(Newline);
10535b0c09bSDmitri Gribenko         NoMoreInterestingTokens = true;
10635b0c09bSDmitri Gribenko         return false;
10735b0c09bSDmitri Gribenko       }
10835b0c09bSDmitri Gribenko     }
10935b0c09bSDmitri Gribenko     if (P.Tok.isNot(tok::text)) {
11035b0c09bSDmitri Gribenko       NoMoreInterestingTokens = true;
11135b0c09bSDmitri Gribenko       return false;
11235b0c09bSDmitri Gribenko     }
11335b0c09bSDmitri Gribenko 
1140a36302aSDmitri Gribenko     Toks.push_back(P.Tok);
1150a36302aSDmitri Gribenko     P.consumeToken();
1160a36302aSDmitri Gribenko     if (Toks.size() == 1)
1170a36302aSDmitri Gribenko       setupBuffer();
1180a36302aSDmitri Gribenko     return true;
1190a36302aSDmitri Gribenko   }
1201bfd9dadSDmitri Gribenko 
consumeWhitespace()1211bfd9dadSDmitri Gribenko   void consumeWhitespace() {
1221bfd9dadSDmitri Gribenko     while (!isEnd()) {
1231bfd9dadSDmitri Gribenko       if (isWhitespace(peek()))
1241bfd9dadSDmitri Gribenko         consumeChar();
1251bfd9dadSDmitri Gribenko       else
1261bfd9dadSDmitri Gribenko         break;
1271bfd9dadSDmitri Gribenko     }
1281bfd9dadSDmitri Gribenko   }
1291bfd9dadSDmitri Gribenko 
formTokenWithChars(Token & Result,SourceLocation Loc,const char * TokBegin,unsigned TokLength,StringRef Text)1301bfd9dadSDmitri Gribenko   void formTokenWithChars(Token &Result,
1311bfd9dadSDmitri Gribenko                           SourceLocation Loc,
1321bfd9dadSDmitri Gribenko                           const char *TokBegin,
1331bfd9dadSDmitri Gribenko                           unsigned TokLength,
1341bfd9dadSDmitri Gribenko                           StringRef Text) {
1351bfd9dadSDmitri Gribenko     Result.setLocation(Loc);
1361bfd9dadSDmitri Gribenko     Result.setKind(tok::text);
1371bfd9dadSDmitri Gribenko     Result.setLength(TokLength);
1381bfd9dadSDmitri Gribenko #ifndef NDEBUG
1397acbf00fSDmitri Gribenko     Result.TextPtr = "<UNSET>";
1407acbf00fSDmitri Gribenko     Result.IntVal = 7;
1411bfd9dadSDmitri Gribenko #endif
1421bfd9dadSDmitri Gribenko     Result.setText(Text);
1431bfd9dadSDmitri Gribenko   }
1441bfd9dadSDmitri Gribenko 
1451bfd9dadSDmitri Gribenko public:
TextTokenRetokenizer(llvm::BumpPtrAllocator & Allocator,Parser & P)1460a36302aSDmitri Gribenko   TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
14735b0c09bSDmitri Gribenko       Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
1481bfd9dadSDmitri Gribenko     Pos.CurToken = 0;
1490a36302aSDmitri Gribenko     addToken();
1501bfd9dadSDmitri Gribenko   }
1511bfd9dadSDmitri Gribenko 
1521bfd9dadSDmitri Gribenko   /// Extract a word -- sequence of non-whitespace characters.
lexWord(Token & Tok)1531bfd9dadSDmitri Gribenko   bool lexWord(Token &Tok) {
1541bfd9dadSDmitri Gribenko     if (isEnd())
1551bfd9dadSDmitri Gribenko       return false;
1561bfd9dadSDmitri Gribenko 
1571bfd9dadSDmitri Gribenko     Position SavedPos = Pos;
1581bfd9dadSDmitri Gribenko 
1591bfd9dadSDmitri Gribenko     consumeWhitespace();
1601bfd9dadSDmitri Gribenko     SmallString<32> WordText;
1611bfd9dadSDmitri Gribenko     const char *WordBegin = Pos.BufferPtr;
1621bfd9dadSDmitri Gribenko     SourceLocation Loc = getSourceLocation();
1631bfd9dadSDmitri Gribenko     while (!isEnd()) {
1641bfd9dadSDmitri Gribenko       const char C = peek();
1651bfd9dadSDmitri Gribenko       if (!isWhitespace(C)) {
1661bfd9dadSDmitri Gribenko         WordText.push_back(C);
1671bfd9dadSDmitri Gribenko         consumeChar();
1681bfd9dadSDmitri Gribenko       } else
1691bfd9dadSDmitri Gribenko         break;
1701bfd9dadSDmitri Gribenko     }
1711bfd9dadSDmitri Gribenko     const unsigned Length = WordText.size();
1721bfd9dadSDmitri Gribenko     if (Length == 0) {
1731bfd9dadSDmitri Gribenko       Pos = SavedPos;
1741bfd9dadSDmitri Gribenko       return false;
1751bfd9dadSDmitri Gribenko     }
1761bfd9dadSDmitri Gribenko 
1771bfd9dadSDmitri Gribenko     char *TextPtr = Allocator.Allocate<char>(Length + 1);
1781bfd9dadSDmitri Gribenko 
1791bfd9dadSDmitri Gribenko     memcpy(TextPtr, WordText.c_str(), Length + 1);
1801bfd9dadSDmitri Gribenko     StringRef Text = StringRef(TextPtr, Length);
1811bfd9dadSDmitri Gribenko 
182c55c6fc6SDmitri Gribenko     formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
1831bfd9dadSDmitri Gribenko     return true;
1841bfd9dadSDmitri Gribenko   }
1851bfd9dadSDmitri Gribenko 
lexDelimitedSeq(Token & Tok,char OpenDelim,char CloseDelim)1861bfd9dadSDmitri Gribenko   bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
1871bfd9dadSDmitri Gribenko     if (isEnd())
1881bfd9dadSDmitri Gribenko       return false;
1891bfd9dadSDmitri Gribenko 
1901bfd9dadSDmitri Gribenko     Position SavedPos = Pos;
1911bfd9dadSDmitri Gribenko 
1921bfd9dadSDmitri Gribenko     consumeWhitespace();
1931bfd9dadSDmitri Gribenko     SmallString<32> WordText;
1941bfd9dadSDmitri Gribenko     const char *WordBegin = Pos.BufferPtr;
1951bfd9dadSDmitri Gribenko     SourceLocation Loc = getSourceLocation();
1961bfd9dadSDmitri Gribenko     bool Error = false;
1971bfd9dadSDmitri Gribenko     if (!isEnd()) {
1981bfd9dadSDmitri Gribenko       const char C = peek();
1991bfd9dadSDmitri Gribenko       if (C == OpenDelim) {
2001bfd9dadSDmitri Gribenko         WordText.push_back(C);
2011bfd9dadSDmitri Gribenko         consumeChar();
2021bfd9dadSDmitri Gribenko       } else
2031bfd9dadSDmitri Gribenko         Error = true;
2041bfd9dadSDmitri Gribenko     }
2051bfd9dadSDmitri Gribenko     char C = '\0';
2061bfd9dadSDmitri Gribenko     while (!Error && !isEnd()) {
2071bfd9dadSDmitri Gribenko       C = peek();
2081bfd9dadSDmitri Gribenko       WordText.push_back(C);
2091bfd9dadSDmitri Gribenko       consumeChar();
2101bfd9dadSDmitri Gribenko       if (C == CloseDelim)
2111bfd9dadSDmitri Gribenko         break;
2121bfd9dadSDmitri Gribenko     }
2131bfd9dadSDmitri Gribenko     if (!Error && C != CloseDelim)
2141bfd9dadSDmitri Gribenko       Error = true;
2151bfd9dadSDmitri Gribenko 
2161bfd9dadSDmitri Gribenko     if (Error) {
2171bfd9dadSDmitri Gribenko       Pos = SavedPos;
2181bfd9dadSDmitri Gribenko       return false;
2191bfd9dadSDmitri Gribenko     }
2201bfd9dadSDmitri Gribenko 
2211bfd9dadSDmitri Gribenko     const unsigned Length = WordText.size();
2221bfd9dadSDmitri Gribenko     char *TextPtr = Allocator.Allocate<char>(Length + 1);
2231bfd9dadSDmitri Gribenko 
2241bfd9dadSDmitri Gribenko     memcpy(TextPtr, WordText.c_str(), Length + 1);
2251bfd9dadSDmitri Gribenko     StringRef Text = StringRef(TextPtr, Length);
2261bfd9dadSDmitri Gribenko 
2271bfd9dadSDmitri Gribenko     formTokenWithChars(Tok, Loc, WordBegin,
2281bfd9dadSDmitri Gribenko                        Pos.BufferPtr - WordBegin, Text);
2291bfd9dadSDmitri Gribenko     return true;
2301bfd9dadSDmitri Gribenko   }
2311bfd9dadSDmitri Gribenko 
2320a36302aSDmitri Gribenko   /// Put back tokens that we didn't consume.
putBackLeftoverTokens()2330a36302aSDmitri Gribenko   void putBackLeftoverTokens() {
2341bfd9dadSDmitri Gribenko     if (isEnd())
2350a36302aSDmitri Gribenko       return;
2361bfd9dadSDmitri Gribenko 
2370a36302aSDmitri Gribenko     bool HavePartialTok = false;
2380a36302aSDmitri Gribenko     Token PartialTok;
2390a36302aSDmitri Gribenko     if (Pos.BufferPtr != Pos.BufferStart) {
2400a36302aSDmitri Gribenko       formTokenWithChars(PartialTok, getSourceLocation(),
2411bfd9dadSDmitri Gribenko                          Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
2421bfd9dadSDmitri Gribenko                          StringRef(Pos.BufferPtr,
2431bfd9dadSDmitri Gribenko                                    Pos.BufferEnd - Pos.BufferPtr));
2440a36302aSDmitri Gribenko       HavePartialTok = true;
2451bfd9dadSDmitri Gribenko       Pos.CurToken++;
2460a36302aSDmitri Gribenko     }
2470a36302aSDmitri Gribenko 
2480a36302aSDmitri Gribenko     P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
2490a36302aSDmitri Gribenko     Pos.CurToken = Toks.size();
2500a36302aSDmitri Gribenko 
2510a36302aSDmitri Gribenko     if (HavePartialTok)
2520a36302aSDmitri Gribenko       P.putBack(PartialTok);
2531bfd9dadSDmitri Gribenko   }
2541bfd9dadSDmitri Gribenko };
2551bfd9dadSDmitri Gribenko 
Parser(Lexer & L,Sema & S,llvm::BumpPtrAllocator & Allocator,const SourceManager & SourceMgr,DiagnosticsEngine & Diags,const CommandTraits & Traits)256f26054f0SDmitri Gribenko Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
257ca7f80adSDmitri Gribenko                const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
258ca7f80adSDmitri Gribenko                const CommandTraits &Traits):
259ca7f80adSDmitri Gribenko     L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
260ca7f80adSDmitri Gribenko     Traits(Traits) {
261ec92531cSDmitri Gribenko   consumeToken();
262ec92531cSDmitri Gribenko }
263ec92531cSDmitri Gribenko 
parseParamCommandArgs(ParamCommandComment * PC,TextTokenRetokenizer & Retokenizer)264a9770ad8SDmitri Gribenko void Parser::parseParamCommandArgs(ParamCommandComment *PC,
265ec92531cSDmitri Gribenko                                    TextTokenRetokenizer &Retokenizer) {
266ec92531cSDmitri Gribenko   Token Arg;
267ec92531cSDmitri Gribenko   // Check if argument looks like direction specification: [dir]
268ec92531cSDmitri Gribenko   // e.g., [in], [out], [in,out]
269ec92531cSDmitri Gribenko   if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
270a9770ad8SDmitri Gribenko     S.actOnParamCommandDirectionArg(PC,
271ec92531cSDmitri Gribenko                                     Arg.getLocation(),
272ec92531cSDmitri Gribenko                                     Arg.getEndLocation(),
273f26054f0SDmitri Gribenko                                     Arg.getText());
274ec92531cSDmitri Gribenko 
275ec92531cSDmitri Gribenko   if (Retokenizer.lexWord(Arg))
276a9770ad8SDmitri Gribenko     S.actOnParamCommandParamNameArg(PC,
277ec92531cSDmitri Gribenko                                     Arg.getLocation(),
278ec92531cSDmitri Gribenko                                     Arg.getEndLocation(),
279f26054f0SDmitri Gribenko                                     Arg.getText());
280ec92531cSDmitri Gribenko }
281ec92531cSDmitri Gribenko 
parseTParamCommandArgs(TParamCommandComment * TPC,TextTokenRetokenizer & Retokenizer)282a9770ad8SDmitri Gribenko void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
28334df2204SDmitri Gribenko                                     TextTokenRetokenizer &Retokenizer) {
28434df2204SDmitri Gribenko   Token Arg;
28534df2204SDmitri Gribenko   if (Retokenizer.lexWord(Arg))
286a9770ad8SDmitri Gribenko     S.actOnTParamCommandParamNameArg(TPC,
28734df2204SDmitri Gribenko                                      Arg.getLocation(),
28834df2204SDmitri Gribenko                                      Arg.getEndLocation(),
28934df2204SDmitri Gribenko                                      Arg.getText());
29034df2204SDmitri Gribenko }
29134df2204SDmitri Gribenko 
292d3a4033dSAaron Puchert ArrayRef<Comment::Argument>
parseCommandArgs(TextTokenRetokenizer & Retokenizer,unsigned NumArgs)293d3a4033dSAaron Puchert Parser::parseCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs) {
294d3a4033dSAaron Puchert   auto *Args = new (Allocator.Allocate<Comment::Argument>(NumArgs))
295d3a4033dSAaron Puchert       Comment::Argument[NumArgs];
296ec92531cSDmitri Gribenko   unsigned ParsedArgs = 0;
297ec92531cSDmitri Gribenko   Token Arg;
298ec92531cSDmitri Gribenko   while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
299d3a4033dSAaron Puchert     Args[ParsedArgs] = Comment::Argument{
300d3a4033dSAaron Puchert         SourceRange(Arg.getLocation(), Arg.getEndLocation()), Arg.getText()};
301ec92531cSDmitri Gribenko     ParsedArgs++;
302ec92531cSDmitri Gribenko   }
303ec92531cSDmitri Gribenko 
304d3a4033dSAaron Puchert   return llvm::makeArrayRef(Args, ParsedArgs);
305ec92531cSDmitri Gribenko }
306ec92531cSDmitri Gribenko 
parseBlockCommand()307ec92531cSDmitri Gribenko BlockCommandComment *Parser::parseBlockCommand() {
308e400cb70SFariborz Jahanian   assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
309ec92531cSDmitri Gribenko 
31036250ad6SCraig Topper   ParamCommandComment *PC = nullptr;
31136250ad6SCraig Topper   TParamCommandComment *TPC = nullptr;
31236250ad6SCraig Topper   BlockCommandComment *BC = nullptr;
3137acbf00fSDmitri Gribenko   const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
314bcf7f4d3SDmitri Gribenko   CommandMarkerKind CommandMarker =
315bcf7f4d3SDmitri Gribenko       Tok.is(tok::backslash_command) ? CMK_Backslash : CMK_At;
3167acbf00fSDmitri Gribenko   if (Info->IsParamCommand) {
317ec92531cSDmitri Gribenko     PC = S.actOnParamCommandStart(Tok.getLocation(),
318ec92531cSDmitri Gribenko                                   Tok.getEndLocation(),
319e400cb70SFariborz Jahanian                                   Tok.getCommandID(),
320bcf7f4d3SDmitri Gribenko                                   CommandMarker);
321696d7226SDmitri Gribenko   } else if (Info->IsTParamCommand) {
32234df2204SDmitri Gribenko     TPC = S.actOnTParamCommandStart(Tok.getLocation(),
32334df2204SDmitri Gribenko                                     Tok.getEndLocation(),
324e400cb70SFariborz Jahanian                                     Tok.getCommandID(),
325bcf7f4d3SDmitri Gribenko                                     CommandMarker);
326ec92531cSDmitri Gribenko   } else {
327ec92531cSDmitri Gribenko     BC = S.actOnBlockCommandStart(Tok.getLocation(),
328ec92531cSDmitri Gribenko                                   Tok.getEndLocation(),
329e400cb70SFariborz Jahanian                                   Tok.getCommandID(),
330bcf7f4d3SDmitri Gribenko                                   CommandMarker);
331ec92531cSDmitri Gribenko   }
332ec92531cSDmitri Gribenko   consumeToken();
333ec92531cSDmitri Gribenko 
33471469305SDmitri Gribenko   if (isTokBlockCommand()) {
335ec92531cSDmitri Gribenko     // Block command ahead.  We can't nest block commands, so pretend that this
336ec92531cSDmitri Gribenko     // command has an empty argument.
33744ebbd54SDmitri Gribenko     ParagraphComment *Paragraph = S.actOnParagraphComment(None);
338c2c804d1SDmitri Gribenko     if (PC) {
3396297fa8aSDmitri Gribenko       S.actOnParamCommandFinish(PC, Paragraph);
340a9770ad8SDmitri Gribenko       return PC;
341c2c804d1SDmitri Gribenko     } else if (TPC) {
3426297fa8aSDmitri Gribenko       S.actOnTParamCommandFinish(TPC, Paragraph);
3436297fa8aSDmitri Gribenko       return TPC;
344a9770ad8SDmitri Gribenko     } else {
345a9770ad8SDmitri Gribenko       S.actOnBlockCommandFinish(BC, Paragraph);
346a9770ad8SDmitri Gribenko       return BC;
347a9770ad8SDmitri Gribenko     }
348ec92531cSDmitri Gribenko   }
349ec92531cSDmitri Gribenko 
350c2c804d1SDmitri Gribenko   if (PC || TPC || Info->NumArgs > 0) {
351ec92531cSDmitri Gribenko     // In order to parse command arguments we need to retokenize a few
352ec92531cSDmitri Gribenko     // following text tokens.
3530a36302aSDmitri Gribenko     TextTokenRetokenizer Retokenizer(Allocator, *this);
354ec92531cSDmitri Gribenko 
355c2c804d1SDmitri Gribenko     if (PC)
356a9770ad8SDmitri Gribenko       parseParamCommandArgs(PC, Retokenizer);
357c2c804d1SDmitri Gribenko     else if (TPC)
358a9770ad8SDmitri Gribenko       parseTParamCommandArgs(TPC, Retokenizer);
359ec92531cSDmitri Gribenko     else
360d3a4033dSAaron Puchert       S.actOnBlockCommandArgs(BC, parseCommandArgs(Retokenizer, Info->NumArgs));
361ec92531cSDmitri Gribenko 
3620a36302aSDmitri Gribenko     Retokenizer.putBackLeftoverTokens();
363ec92531cSDmitri Gribenko   }
364ec92531cSDmitri Gribenko 
36571469305SDmitri Gribenko   // If there's a block command ahead, we will attach an empty paragraph to
36671469305SDmitri Gribenko   // this command.
36771469305SDmitri Gribenko   bool EmptyParagraph = false;
36871469305SDmitri Gribenko   if (isTokBlockCommand())
36971469305SDmitri Gribenko     EmptyParagraph = true;
37071469305SDmitri Gribenko   else if (Tok.is(tok::newline)) {
37171469305SDmitri Gribenko     Token PrevTok = Tok;
37271469305SDmitri Gribenko     consumeToken();
37371469305SDmitri Gribenko     EmptyParagraph = isTokBlockCommand();
37471469305SDmitri Gribenko     putBack(PrevTok);
37571469305SDmitri Gribenko   }
37671469305SDmitri Gribenko 
37771469305SDmitri Gribenko   ParagraphComment *Paragraph;
37871469305SDmitri Gribenko   if (EmptyParagraph)
37944ebbd54SDmitri Gribenko     Paragraph = S.actOnParagraphComment(None);
38071469305SDmitri Gribenko   else {
381ec92531cSDmitri Gribenko     BlockContentComment *Block = parseParagraphOrBlockCommand();
382ec92531cSDmitri Gribenko     // Since we have checked for a block command, we should have parsed a
383ec92531cSDmitri Gribenko     // paragraph.
38471469305SDmitri Gribenko     Paragraph = cast<ParagraphComment>(Block);
38571469305SDmitri Gribenko   }
38671469305SDmitri Gribenko 
387c2c804d1SDmitri Gribenko   if (PC) {
388a9770ad8SDmitri Gribenko     S.actOnParamCommandFinish(PC, Paragraph);
389a9770ad8SDmitri Gribenko     return PC;
390c2c804d1SDmitri Gribenko   } else if (TPC) {
391a9770ad8SDmitri Gribenko     S.actOnTParamCommandFinish(TPC, Paragraph);
392a9770ad8SDmitri Gribenko     return TPC;
393a9770ad8SDmitri Gribenko   } else {
394a9770ad8SDmitri Gribenko     S.actOnBlockCommandFinish(BC, Paragraph);
395a9770ad8SDmitri Gribenko     return BC;
396a9770ad8SDmitri Gribenko   }
397ec92531cSDmitri Gribenko }
398ec92531cSDmitri Gribenko 
parseInlineCommand()399ec92531cSDmitri Gribenko InlineCommandComment *Parser::parseInlineCommand() {
400e400cb70SFariborz Jahanian   assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
401d3a4033dSAaron Puchert   const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
402ec92531cSDmitri Gribenko 
403ec92531cSDmitri Gribenko   const Token CommandTok = Tok;
404ec92531cSDmitri Gribenko   consumeToken();
405ec92531cSDmitri Gribenko 
4060a36302aSDmitri Gribenko   TextTokenRetokenizer Retokenizer(Allocator, *this);
407d3a4033dSAaron Puchert   ArrayRef<Comment::Argument> Args =
408d3a4033dSAaron Puchert       parseCommandArgs(Retokenizer, Info->NumArgs);
409ec92531cSDmitri Gribenko 
410d3a4033dSAaron Puchert   InlineCommandComment *IC = S.actOnInlineCommand(
411d3a4033dSAaron Puchert       CommandTok.getLocation(), CommandTok.getEndLocation(),
412d3a4033dSAaron Puchert       CommandTok.getCommandID(), Args);
413ec92531cSDmitri Gribenko 
414d3a4033dSAaron Puchert   if (Args.size() < Info->NumArgs) {
415657330eeSDmitri Gribenko     Diag(CommandTok.getEndLocation().getLocWithOffset(1),
416d3a4033dSAaron Puchert          diag::warn_doc_inline_command_not_enough_arguments)
417*ac7a9ef0SAaron Puchert         << CommandTok.is(tok::at_command) << Info->Name << Args.size()
418d3a4033dSAaron Puchert         << Info->NumArgs
419657330eeSDmitri Gribenko         << SourceRange(CommandTok.getLocation(), CommandTok.getEndLocation());
420ec92531cSDmitri Gribenko   }
421ec92531cSDmitri Gribenko 
4220a36302aSDmitri Gribenko   Retokenizer.putBackLeftoverTokens();
423ec92531cSDmitri Gribenko 
424ec92531cSDmitri Gribenko   return IC;
425ec92531cSDmitri Gribenko }
426ec92531cSDmitri Gribenko 
parseHTMLStartTag()427e00ffc7bSDmitri Gribenko HTMLStartTagComment *Parser::parseHTMLStartTag() {
428e00ffc7bSDmitri Gribenko   assert(Tok.is(tok::html_start_tag));
429e00ffc7bSDmitri Gribenko   HTMLStartTagComment *HST =
430e00ffc7bSDmitri Gribenko       S.actOnHTMLStartTagStart(Tok.getLocation(),
431e00ffc7bSDmitri Gribenko                                Tok.getHTMLTagStartName());
432ec92531cSDmitri Gribenko   consumeToken();
433ec92531cSDmitri Gribenko 
434e00ffc7bSDmitri Gribenko   SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
435ec92531cSDmitri Gribenko   while (true) {
436f26054f0SDmitri Gribenko     switch (Tok.getKind()) {
437f26054f0SDmitri Gribenko     case tok::html_ident: {
438ec92531cSDmitri Gribenko       Token Ident = Tok;
439ec92531cSDmitri Gribenko       consumeToken();
440ec92531cSDmitri Gribenko       if (Tok.isNot(tok::html_equals)) {
441e00ffc7bSDmitri Gribenko         Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
442ec92531cSDmitri Gribenko                                                        Ident.getHTMLIdent()));
443ec92531cSDmitri Gribenko         continue;
444ec92531cSDmitri Gribenko       }
445ec92531cSDmitri Gribenko       Token Equals = Tok;
446ec92531cSDmitri Gribenko       consumeToken();
447ec92531cSDmitri Gribenko       if (Tok.isNot(tok::html_quoted_string)) {
448f26054f0SDmitri Gribenko         Diag(Tok.getLocation(),
449e00ffc7bSDmitri Gribenko              diag::warn_doc_html_start_tag_expected_quoted_string)
450f26054f0SDmitri Gribenko           << SourceRange(Equals.getLocation());
451e00ffc7bSDmitri Gribenko         Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
452ec92531cSDmitri Gribenko                                                        Ident.getHTMLIdent()));
453f26054f0SDmitri Gribenko         while (Tok.is(tok::html_equals) ||
454f26054f0SDmitri Gribenko                Tok.is(tok::html_quoted_string))
455f26054f0SDmitri Gribenko           consumeToken();
456ec92531cSDmitri Gribenko         continue;
457ec92531cSDmitri Gribenko       }
458e00ffc7bSDmitri Gribenko       Attrs.push_back(HTMLStartTagComment::Attribute(
459ec92531cSDmitri Gribenko                               Ident.getLocation(),
460ec92531cSDmitri Gribenko                               Ident.getHTMLIdent(),
461ec92531cSDmitri Gribenko                               Equals.getLocation(),
462ec92531cSDmitri Gribenko                               SourceRange(Tok.getLocation(),
463ec92531cSDmitri Gribenko                                           Tok.getEndLocation()),
464ec92531cSDmitri Gribenko                               Tok.getHTMLQuotedString()));
465ec92531cSDmitri Gribenko       consumeToken();
466ec92531cSDmitri Gribenko       continue;
467f26054f0SDmitri Gribenko     }
468f26054f0SDmitri Gribenko 
469f26054f0SDmitri Gribenko     case tok::html_greater:
470a9770ad8SDmitri Gribenko       S.actOnHTMLStartTagFinish(HST,
47134df2204SDmitri Gribenko                                 S.copyArray(llvm::makeArrayRef(Attrs)),
472f26054f0SDmitri Gribenko                                 Tok.getLocation(),
473f26054f0SDmitri Gribenko                                 /* IsSelfClosing = */ false);
474ec92531cSDmitri Gribenko       consumeToken();
475e00ffc7bSDmitri Gribenko       return HST;
476f26054f0SDmitri Gribenko 
477f26054f0SDmitri Gribenko     case tok::html_slash_greater:
478a9770ad8SDmitri Gribenko       S.actOnHTMLStartTagFinish(HST,
47934df2204SDmitri Gribenko                                 S.copyArray(llvm::makeArrayRef(Attrs)),
480f26054f0SDmitri Gribenko                                 Tok.getLocation(),
481f26054f0SDmitri Gribenko                                 /* IsSelfClosing = */ true);
482f26054f0SDmitri Gribenko       consumeToken();
483e00ffc7bSDmitri Gribenko       return HST;
484f26054f0SDmitri Gribenko 
485f26054f0SDmitri Gribenko     case tok::html_equals:
486f26054f0SDmitri Gribenko     case tok::html_quoted_string:
487f26054f0SDmitri Gribenko       Diag(Tok.getLocation(),
488e00ffc7bSDmitri Gribenko            diag::warn_doc_html_start_tag_expected_ident_or_greater);
489ec92531cSDmitri Gribenko       while (Tok.is(tok::html_equals) ||
490ec92531cSDmitri Gribenko              Tok.is(tok::html_quoted_string))
491ec92531cSDmitri Gribenko         consumeToken();
492f26054f0SDmitri Gribenko       if (Tok.is(tok::html_ident) ||
493f26054f0SDmitri Gribenko           Tok.is(tok::html_greater) ||
494f26054f0SDmitri Gribenko           Tok.is(tok::html_slash_greater))
495f26054f0SDmitri Gribenko         continue;
496f26054f0SDmitri Gribenko 
497a9770ad8SDmitri Gribenko       S.actOnHTMLStartTagFinish(HST,
49834df2204SDmitri Gribenko                                 S.copyArray(llvm::makeArrayRef(Attrs)),
499f26054f0SDmitri Gribenko                                 SourceLocation(),
500f26054f0SDmitri Gribenko                                 /* IsSelfClosing = */ false);
501a9770ad8SDmitri Gribenko       return HST;
502f26054f0SDmitri Gribenko 
503f26054f0SDmitri Gribenko     default:
504e00ffc7bSDmitri Gribenko       // Not a token from an HTML start tag.  Thus HTML tag prematurely ended.
505a9770ad8SDmitri Gribenko       S.actOnHTMLStartTagFinish(HST,
50634df2204SDmitri Gribenko                                 S.copyArray(llvm::makeArrayRef(Attrs)),
507f26054f0SDmitri Gribenko                                 SourceLocation(),
508f26054f0SDmitri Gribenko                                 /* IsSelfClosing = */ false);
509f26054f0SDmitri Gribenko       bool StartLineInvalid;
510f26054f0SDmitri Gribenko       const unsigned StartLine = SourceMgr.getPresumedLineNumber(
511e00ffc7bSDmitri Gribenko                                                   HST->getLocation(),
512f26054f0SDmitri Gribenko                                                   &StartLineInvalid);
513f26054f0SDmitri Gribenko       bool EndLineInvalid;
514f26054f0SDmitri Gribenko       const unsigned EndLine = SourceMgr.getPresumedLineNumber(
515f26054f0SDmitri Gribenko                                                   Tok.getLocation(),
516f26054f0SDmitri Gribenko                                                   &EndLineInvalid);
517f26054f0SDmitri Gribenko       if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
518f26054f0SDmitri Gribenko         Diag(Tok.getLocation(),
519e00ffc7bSDmitri Gribenko              diag::warn_doc_html_start_tag_expected_ident_or_greater)
520e00ffc7bSDmitri Gribenko           << HST->getSourceRange();
521f26054f0SDmitri Gribenko       else {
522f26054f0SDmitri Gribenko         Diag(Tok.getLocation(),
523e00ffc7bSDmitri Gribenko              diag::warn_doc_html_start_tag_expected_ident_or_greater);
524e00ffc7bSDmitri Gribenko         Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
525e00ffc7bSDmitri Gribenko           << HST->getSourceRange();
526f26054f0SDmitri Gribenko       }
527e00ffc7bSDmitri Gribenko       return HST;
528ec92531cSDmitri Gribenko     }
529ec92531cSDmitri Gribenko   }
530ec92531cSDmitri Gribenko }
531ec92531cSDmitri Gribenko 
parseHTMLEndTag()532e00ffc7bSDmitri Gribenko HTMLEndTagComment *Parser::parseHTMLEndTag() {
533e00ffc7bSDmitri Gribenko   assert(Tok.is(tok::html_end_tag));
534e00ffc7bSDmitri Gribenko   Token TokEndTag = Tok;
535ec92531cSDmitri Gribenko   consumeToken();
536ec92531cSDmitri Gribenko   SourceLocation Loc;
537ec92531cSDmitri Gribenko   if (Tok.is(tok::html_greater)) {
538ec92531cSDmitri Gribenko     Loc = Tok.getLocation();
539ec92531cSDmitri Gribenko     consumeToken();
540ec92531cSDmitri Gribenko   }
541ec92531cSDmitri Gribenko 
542e00ffc7bSDmitri Gribenko   return S.actOnHTMLEndTag(TokEndTag.getLocation(),
543ec92531cSDmitri Gribenko                            Loc,
544e00ffc7bSDmitri Gribenko                            TokEndTag.getHTMLTagEndName());
545ec92531cSDmitri Gribenko }
546ec92531cSDmitri Gribenko 
parseParagraphOrBlockCommand()547ec92531cSDmitri Gribenko BlockContentComment *Parser::parseParagraphOrBlockCommand() {
548ec92531cSDmitri Gribenko   SmallVector<InlineContentComment *, 8> Content;
549ec92531cSDmitri Gribenko 
550ec92531cSDmitri Gribenko   while (true) {
551ec92531cSDmitri Gribenko     switch (Tok.getKind()) {
552ec92531cSDmitri Gribenko     case tok::verbatim_block_begin:
553ec92531cSDmitri Gribenko     case tok::verbatim_line_name:
554ec92531cSDmitri Gribenko     case tok::eof:
555ec92531cSDmitri Gribenko       break; // Block content or EOF ahead, finish this parapgaph.
556ec92531cSDmitri Gribenko 
5577acbf00fSDmitri Gribenko     case tok::unknown_command:
5587acbf00fSDmitri Gribenko       Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
5597acbf00fSDmitri Gribenko                                               Tok.getEndLocation(),
5607acbf00fSDmitri Gribenko                                               Tok.getUnknownCommandName()));
5617acbf00fSDmitri Gribenko       consumeToken();
5627acbf00fSDmitri Gribenko       continue;
5637acbf00fSDmitri Gribenko 
564e400cb70SFariborz Jahanian     case tok::backslash_command:
565e400cb70SFariborz Jahanian     case tok::at_command: {
5667acbf00fSDmitri Gribenko       const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
5677acbf00fSDmitri Gribenko       if (Info->IsBlockCommand) {
568ec92531cSDmitri Gribenko         if (Content.size() == 0)
569ec92531cSDmitri Gribenko           return parseBlockCommand();
570ec92531cSDmitri Gribenko         break; // Block command ahead, finish this parapgaph.
571ec92531cSDmitri Gribenko       }
57276b91c34SDmitri Gribenko       if (Info->IsVerbatimBlockEndCommand) {
57376b91c34SDmitri Gribenko         Diag(Tok.getLocation(),
57476b91c34SDmitri Gribenko              diag::warn_verbatim_block_end_without_start)
575e400cb70SFariborz Jahanian           << Tok.is(tok::at_command)
57676b91c34SDmitri Gribenko           << Info->Name
57776b91c34SDmitri Gribenko           << SourceRange(Tok.getLocation(), Tok.getEndLocation());
57876b91c34SDmitri Gribenko         consumeToken();
57976b91c34SDmitri Gribenko         continue;
58076b91c34SDmitri Gribenko       }
5819304d863SDmitri Gribenko       if (Info->IsUnknownCommand) {
5829304d863SDmitri Gribenko         Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
5839304d863SDmitri Gribenko                                                 Tok.getEndLocation(),
5849304d863SDmitri Gribenko                                                 Info->getID()));
5859304d863SDmitri Gribenko         consumeToken();
5869304d863SDmitri Gribenko         continue;
5879304d863SDmitri Gribenko       }
5887acbf00fSDmitri Gribenko       assert(Info->IsInlineCommand);
589ec92531cSDmitri Gribenko       Content.push_back(parseInlineCommand());
590ec92531cSDmitri Gribenko       continue;
591ec92531cSDmitri Gribenko     }
592ec92531cSDmitri Gribenko 
593ec92531cSDmitri Gribenko     case tok::newline: {
594ec92531cSDmitri Gribenko       consumeToken();
595ec92531cSDmitri Gribenko       if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
596ec92531cSDmitri Gribenko         consumeToken();
597ec92531cSDmitri Gribenko         break; // Two newlines -- end of paragraph.
598ec92531cSDmitri Gribenko       }
5991e50cbf3SDmitri Gribenko       // Also allow [tok::newline, tok::text, tok::newline] if the middle
6001e50cbf3SDmitri Gribenko       // tok::text is just whitespace.
6011e50cbf3SDmitri Gribenko       if (Tok.is(tok::text) && isWhitespace(Tok.getText())) {
6021e50cbf3SDmitri Gribenko         Token WhitespaceTok = Tok;
6031e50cbf3SDmitri Gribenko         consumeToken();
6041e50cbf3SDmitri Gribenko         if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
6051e50cbf3SDmitri Gribenko           consumeToken();
6061e50cbf3SDmitri Gribenko           break;
6071e50cbf3SDmitri Gribenko         }
6081e50cbf3SDmitri Gribenko         // We have [tok::newline, tok::text, non-newline].  Put back tok::text.
6091e50cbf3SDmitri Gribenko         putBack(WhitespaceTok);
6101e50cbf3SDmitri Gribenko       }
611ec92531cSDmitri Gribenko       if (Content.size() > 0)
612ec92531cSDmitri Gribenko         Content.back()->addTrailingNewline();
613ec92531cSDmitri Gribenko       continue;
614ec92531cSDmitri Gribenko     }
615ec92531cSDmitri Gribenko 
616ec92531cSDmitri Gribenko     // Don't deal with HTML tag soup now.
617e00ffc7bSDmitri Gribenko     case tok::html_start_tag:
618e00ffc7bSDmitri Gribenko       Content.push_back(parseHTMLStartTag());
619ec92531cSDmitri Gribenko       continue;
620ec92531cSDmitri Gribenko 
621e00ffc7bSDmitri Gribenko     case tok::html_end_tag:
622e00ffc7bSDmitri Gribenko       Content.push_back(parseHTMLEndTag());
623ec92531cSDmitri Gribenko       continue;
624ec92531cSDmitri Gribenko 
625ec92531cSDmitri Gribenko     case tok::text:
626ec92531cSDmitri Gribenko       Content.push_back(S.actOnText(Tok.getLocation(),
627ec92531cSDmitri Gribenko                                     Tok.getEndLocation(),
628ec92531cSDmitri Gribenko                                     Tok.getText()));
629ec92531cSDmitri Gribenko       consumeToken();
630ec92531cSDmitri Gribenko       continue;
631ec92531cSDmitri Gribenko 
632ec92531cSDmitri Gribenko     case tok::verbatim_block_line:
633ec92531cSDmitri Gribenko     case tok::verbatim_block_end:
634ec92531cSDmitri Gribenko     case tok::verbatim_line_text:
635ec92531cSDmitri Gribenko     case tok::html_ident:
636ec92531cSDmitri Gribenko     case tok::html_equals:
637ec92531cSDmitri Gribenko     case tok::html_quoted_string:
638ec92531cSDmitri Gribenko     case tok::html_greater:
639f26054f0SDmitri Gribenko     case tok::html_slash_greater:
640ec92531cSDmitri Gribenko       llvm_unreachable("should not see this token");
641ec92531cSDmitri Gribenko     }
642ec92531cSDmitri Gribenko     break;
643ec92531cSDmitri Gribenko   }
644ec92531cSDmitri Gribenko 
64534df2204SDmitri Gribenko   return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
646ec92531cSDmitri Gribenko }
647ec92531cSDmitri Gribenko 
parseVerbatimBlock()648ec92531cSDmitri Gribenko VerbatimBlockComment *Parser::parseVerbatimBlock() {
649ec92531cSDmitri Gribenko   assert(Tok.is(tok::verbatim_block_begin));
650ec92531cSDmitri Gribenko 
651ec92531cSDmitri Gribenko   VerbatimBlockComment *VB =
652ec92531cSDmitri Gribenko       S.actOnVerbatimBlockStart(Tok.getLocation(),
6537acbf00fSDmitri Gribenko                                 Tok.getVerbatimBlockID());
654ec92531cSDmitri Gribenko   consumeToken();
655ec92531cSDmitri Gribenko 
656ec92531cSDmitri Gribenko   // Don't create an empty line if verbatim opening command is followed
657ec92531cSDmitri Gribenko   // by a newline.
658ec92531cSDmitri Gribenko   if (Tok.is(tok::newline))
659ec92531cSDmitri Gribenko     consumeToken();
660ec92531cSDmitri Gribenko 
661ec92531cSDmitri Gribenko   SmallVector<VerbatimBlockLineComment *, 8> Lines;
662ec92531cSDmitri Gribenko   while (Tok.is(tok::verbatim_block_line) ||
663ec92531cSDmitri Gribenko          Tok.is(tok::newline)) {
664ec92531cSDmitri Gribenko     VerbatimBlockLineComment *Line;
665ec92531cSDmitri Gribenko     if (Tok.is(tok::verbatim_block_line)) {
666ec92531cSDmitri Gribenko       Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
667ec92531cSDmitri Gribenko                                       Tok.getVerbatimBlockText());
668ec92531cSDmitri Gribenko       consumeToken();
669ec92531cSDmitri Gribenko       if (Tok.is(tok::newline)) {
670ec92531cSDmitri Gribenko         consumeToken();
671ec92531cSDmitri Gribenko       }
672ec92531cSDmitri Gribenko     } else {
673ec92531cSDmitri Gribenko       // Empty line, just a tok::newline.
674b03cc7e9SDmitri Gribenko       Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
675ec92531cSDmitri Gribenko       consumeToken();
676ec92531cSDmitri Gribenko     }
677ec92531cSDmitri Gribenko     Lines.push_back(Line);
678ec92531cSDmitri Gribenko   }
679ec92531cSDmitri Gribenko 
68066a00c76SDmitri Gribenko   if (Tok.is(tok::verbatim_block_end)) {
6817acbf00fSDmitri Gribenko     const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
682a9770ad8SDmitri Gribenko     S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
6837acbf00fSDmitri Gribenko                                Info->Name,
68434df2204SDmitri Gribenko                                S.copyArray(llvm::makeArrayRef(Lines)));
685ec92531cSDmitri Gribenko     consumeToken();
68666a00c76SDmitri Gribenko   } else {
68766a00c76SDmitri Gribenko     // Unterminated \\verbatim block
688a9770ad8SDmitri Gribenko     S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
68934df2204SDmitri Gribenko                                S.copyArray(llvm::makeArrayRef(Lines)));
69066a00c76SDmitri Gribenko   }
691ec92531cSDmitri Gribenko 
692ec92531cSDmitri Gribenko   return VB;
693ec92531cSDmitri Gribenko }
694ec92531cSDmitri Gribenko 
parseVerbatimLine()695ec92531cSDmitri Gribenko VerbatimLineComment *Parser::parseVerbatimLine() {
696ec92531cSDmitri Gribenko   assert(Tok.is(tok::verbatim_line_name));
697ec92531cSDmitri Gribenko 
698ec92531cSDmitri Gribenko   Token NameTok = Tok;
699ec92531cSDmitri Gribenko   consumeToken();
700ec92531cSDmitri Gribenko 
701ec92531cSDmitri Gribenko   SourceLocation TextBegin;
702ec92531cSDmitri Gribenko   StringRef Text;
703ec92531cSDmitri Gribenko   // Next token might not be a tok::verbatim_line_text if verbatim line
704ec92531cSDmitri Gribenko   // starting command comes just before a newline or comment end.
705ec92531cSDmitri Gribenko   if (Tok.is(tok::verbatim_line_text)) {
706ec92531cSDmitri Gribenko     TextBegin = Tok.getLocation();
707ec92531cSDmitri Gribenko     Text = Tok.getVerbatimLineText();
708ec92531cSDmitri Gribenko   } else {
709ec92531cSDmitri Gribenko     TextBegin = NameTok.getEndLocation();
710ec92531cSDmitri Gribenko     Text = "";
711ec92531cSDmitri Gribenko   }
712ec92531cSDmitri Gribenko 
713ec92531cSDmitri Gribenko   VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
7147acbf00fSDmitri Gribenko                                                 NameTok.getVerbatimLineID(),
715ec92531cSDmitri Gribenko                                                 TextBegin,
716ec92531cSDmitri Gribenko                                                 Text);
717ec92531cSDmitri Gribenko   consumeToken();
718ec92531cSDmitri Gribenko   return VL;
719ec92531cSDmitri Gribenko }
720ec92531cSDmitri Gribenko 
parseBlockContent()721ec92531cSDmitri Gribenko BlockContentComment *Parser::parseBlockContent() {
722ec92531cSDmitri Gribenko   switch (Tok.getKind()) {
723ec92531cSDmitri Gribenko   case tok::text:
7247acbf00fSDmitri Gribenko   case tok::unknown_command:
725e400cb70SFariborz Jahanian   case tok::backslash_command:
726e400cb70SFariborz Jahanian   case tok::at_command:
727e00ffc7bSDmitri Gribenko   case tok::html_start_tag:
728e00ffc7bSDmitri Gribenko   case tok::html_end_tag:
729ec92531cSDmitri Gribenko     return parseParagraphOrBlockCommand();
730ec92531cSDmitri Gribenko 
731ec92531cSDmitri Gribenko   case tok::verbatim_block_begin:
732ec92531cSDmitri Gribenko     return parseVerbatimBlock();
733ec92531cSDmitri Gribenko 
734ec92531cSDmitri Gribenko   case tok::verbatim_line_name:
735ec92531cSDmitri Gribenko     return parseVerbatimLine();
736ec92531cSDmitri Gribenko 
737ec92531cSDmitri Gribenko   case tok::eof:
738ec92531cSDmitri Gribenko   case tok::newline:
739ec92531cSDmitri Gribenko   case tok::verbatim_block_line:
740ec92531cSDmitri Gribenko   case tok::verbatim_block_end:
741ec92531cSDmitri Gribenko   case tok::verbatim_line_text:
742ec92531cSDmitri Gribenko   case tok::html_ident:
743ec92531cSDmitri Gribenko   case tok::html_equals:
744ec92531cSDmitri Gribenko   case tok::html_quoted_string:
745ec92531cSDmitri Gribenko   case tok::html_greater:
746f26054f0SDmitri Gribenko   case tok::html_slash_greater:
747ec92531cSDmitri Gribenko     llvm_unreachable("should not see this token");
748ec92531cSDmitri Gribenko   }
7494106ea3bSMatt Beaumont-Gay   llvm_unreachable("bogus token kind");
750ec92531cSDmitri Gribenko }
751ec92531cSDmitri Gribenko 
parseFullComment()752ec92531cSDmitri Gribenko FullComment *Parser::parseFullComment() {
753ec92531cSDmitri Gribenko   // Skip newlines at the beginning of the comment.
754ec92531cSDmitri Gribenko   while (Tok.is(tok::newline))
755ec92531cSDmitri Gribenko     consumeToken();
756ec92531cSDmitri Gribenko 
757ec92531cSDmitri Gribenko   SmallVector<BlockContentComment *, 8> Blocks;
758ec92531cSDmitri Gribenko   while (Tok.isNot(tok::eof)) {
759ec92531cSDmitri Gribenko     Blocks.push_back(parseBlockContent());
760ec92531cSDmitri Gribenko 
761ec92531cSDmitri Gribenko     // Skip extra newlines after paragraph end.
762ec92531cSDmitri Gribenko     while (Tok.is(tok::newline))
763ec92531cSDmitri Gribenko       consumeToken();
764ec92531cSDmitri Gribenko   }
76534df2204SDmitri Gribenko   return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
766ec92531cSDmitri Gribenko }
767ec92531cSDmitri Gribenko 
768ec92531cSDmitri Gribenko } // end namespace comments
769ec92531cSDmitri Gribenko } // end namespace clang
770