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