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