blob: d053dc0f18008bf4a3a233ee7e8816981d314718 [file] [log] [blame]
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +00001//===--- CommentParser.cpp - Doxygen comment parser -----------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "clang/AST/CommentParser.h"
11#include "clang/AST/CommentSema.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000012#include "clang/AST/CommentDiagnostic.h"
Dmitri Gribenkoaa580812012-08-09 00:03:17 +000013#include "clang/AST/CommentCommandTraits.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000014#include "clang/Basic/SourceManager.h"
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000015#include "llvm/Support/ErrorHandling.h"
16
17namespace clang {
18namespace comments {
19
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000020/// Re-lexes a sequence of tok::text tokens.
21class TextTokenRetokenizer {
22 llvm::BumpPtrAllocator &Allocator;
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000023 Parser &P;
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000024
25 /// This flag is set when there are no more tokens we can fetch from lexer.
26 bool NoMoreInterestingTokens;
27
28 /// Token buffer: tokens we have processed and lookahead.
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000029 SmallVector<Token, 16> Toks;
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000030
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000031 /// A position in \c Toks.
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000032 struct Position {
33 unsigned CurToken;
34 const char *BufferStart;
35 const char *BufferEnd;
36 const char *BufferPtr;
37 SourceLocation BufferStartLoc;
38 };
39
40 /// Current position in Toks.
41 Position Pos;
42
43 bool isEnd() const {
44 return Pos.CurToken >= Toks.size();
45 }
46
47 /// Sets up the buffer pointers to point to current token.
48 void setupBuffer() {
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000049 assert(!isEnd());
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000050 const Token &Tok = Toks[Pos.CurToken];
51
52 Pos.BufferStart = Tok.getText().begin();
53 Pos.BufferEnd = Tok.getText().end();
54 Pos.BufferPtr = Pos.BufferStart;
55 Pos.BufferStartLoc = Tok.getLocation();
56 }
57
58 SourceLocation getSourceLocation() const {
59 const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
60 return Pos.BufferStartLoc.getLocWithOffset(CharNo);
61 }
62
63 char peek() const {
64 assert(!isEnd());
65 assert(Pos.BufferPtr != Pos.BufferEnd);
66 return *Pos.BufferPtr;
67 }
68
69 void consumeChar() {
70 assert(!isEnd());
71 assert(Pos.BufferPtr != Pos.BufferEnd);
72 Pos.BufferPtr++;
73 if (Pos.BufferPtr == Pos.BufferEnd) {
74 Pos.CurToken++;
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000075 if (isEnd() && !addToken())
76 return;
77
78 assert(!isEnd());
79 setupBuffer();
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000080 }
81 }
82
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000083 /// Add a token.
84 /// Returns true on success, false if there are no interesting tokens to
85 /// fetch from lexer.
86 bool addToken() {
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000087 if (NoMoreInterestingTokens)
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000088 return false;
89
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000090 if (P.Tok.is(tok::newline)) {
91 // If we see a single newline token between text tokens, skip it.
92 Token Newline = P.Tok;
93 P.consumeToken();
94 if (P.Tok.isNot(tok::text)) {
95 P.putBack(Newline);
96 NoMoreInterestingTokens = true;
97 return false;
98 }
99 }
100 if (P.Tok.isNot(tok::text)) {
101 NoMoreInterestingTokens = true;
102 return false;
103 }
104
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000105 Toks.push_back(P.Tok);
106 P.consumeToken();
107 if (Toks.size() == 1)
108 setupBuffer();
109 return true;
110 }
111
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000112 static bool isWhitespace(char C) {
113 return C == ' ' || C == '\n' || C == '\r' ||
114 C == '\t' || C == '\f' || C == '\v';
115 }
116
117 void consumeWhitespace() {
118 while (!isEnd()) {
119 if (isWhitespace(peek()))
120 consumeChar();
121 else
122 break;
123 }
124 }
125
126 void formTokenWithChars(Token &Result,
127 SourceLocation Loc,
128 const char *TokBegin,
129 unsigned TokLength,
130 StringRef Text) {
131 Result.setLocation(Loc);
132 Result.setKind(tok::text);
133 Result.setLength(TokLength);
134#ifndef NDEBUG
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000135 Result.TextPtr = "<UNSET>";
136 Result.IntVal = 7;
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000137#endif
138 Result.setText(Text);
139 }
140
141public:
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000142 TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
Dmitri Gribenko0c43a922012-07-24 18:23:31 +0000143 Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000144 Pos.CurToken = 0;
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000145 addToken();
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000146 }
147
148 /// Extract a word -- sequence of non-whitespace characters.
149 bool lexWord(Token &Tok) {
150 if (isEnd())
151 return false;
152
153 Position SavedPos = Pos;
154
155 consumeWhitespace();
156 SmallString<32> WordText;
157 const char *WordBegin = Pos.BufferPtr;
158 SourceLocation Loc = getSourceLocation();
159 while (!isEnd()) {
160 const char C = peek();
161 if (!isWhitespace(C)) {
162 WordText.push_back(C);
163 consumeChar();
164 } else
165 break;
166 }
167 const unsigned Length = WordText.size();
168 if (Length == 0) {
169 Pos = SavedPos;
170 return false;
171 }
172
173 char *TextPtr = Allocator.Allocate<char>(Length + 1);
174
175 memcpy(TextPtr, WordText.c_str(), Length + 1);
176 StringRef Text = StringRef(TextPtr, Length);
177
178 formTokenWithChars(Tok, Loc, WordBegin,
179 Pos.BufferPtr - WordBegin, Text);
180 return true;
181 }
182
183 bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
184 if (isEnd())
185 return false;
186
187 Position SavedPos = Pos;
188
189 consumeWhitespace();
190 SmallString<32> WordText;
191 const char *WordBegin = Pos.BufferPtr;
192 SourceLocation Loc = getSourceLocation();
193 bool Error = false;
194 if (!isEnd()) {
195 const char C = peek();
196 if (C == OpenDelim) {
197 WordText.push_back(C);
198 consumeChar();
199 } else
200 Error = true;
201 }
202 char C = '\0';
203 while (!Error && !isEnd()) {
204 C = peek();
205 WordText.push_back(C);
206 consumeChar();
207 if (C == CloseDelim)
208 break;
209 }
210 if (!Error && C != CloseDelim)
211 Error = true;
212
213 if (Error) {
214 Pos = SavedPos;
215 return false;
216 }
217
218 const unsigned Length = WordText.size();
219 char *TextPtr = Allocator.Allocate<char>(Length + 1);
220
221 memcpy(TextPtr, WordText.c_str(), Length + 1);
222 StringRef Text = StringRef(TextPtr, Length);
223
224 formTokenWithChars(Tok, Loc, WordBegin,
225 Pos.BufferPtr - WordBegin, Text);
226 return true;
227 }
228
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000229 /// Put back tokens that we didn't consume.
230 void putBackLeftoverTokens() {
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000231 if (isEnd())
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000232 return;
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000233
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000234 bool HavePartialTok = false;
235 Token PartialTok;
236 if (Pos.BufferPtr != Pos.BufferStart) {
237 formTokenWithChars(PartialTok, getSourceLocation(),
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000238 Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
239 StringRef(Pos.BufferPtr,
240 Pos.BufferEnd - Pos.BufferPtr));
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000241 HavePartialTok = true;
242 Pos.CurToken++;
243 }
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000244
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000245 P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
246 Pos.CurToken = Toks.size();
247
248 if (HavePartialTok)
249 P.putBack(PartialTok);
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000250 }
251};
252
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000253Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000254 const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
255 const CommandTraits &Traits):
256 L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
257 Traits(Traits) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000258 consumeToken();
259}
260
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000261void Parser::parseParamCommandArgs(ParamCommandComment *PC,
262 TextTokenRetokenizer &Retokenizer) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000263 Token Arg;
264 // Check if argument looks like direction specification: [dir]
265 // e.g., [in], [out], [in,out]
266 if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000267 S.actOnParamCommandDirectionArg(PC,
268 Arg.getLocation(),
269 Arg.getEndLocation(),
270 Arg.getText());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000271
272 if (Retokenizer.lexWord(Arg))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000273 S.actOnParamCommandParamNameArg(PC,
274 Arg.getLocation(),
275 Arg.getEndLocation(),
276 Arg.getText());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000277}
278
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000279void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
280 TextTokenRetokenizer &Retokenizer) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000281 Token Arg;
282 if (Retokenizer.lexWord(Arg))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000283 S.actOnTParamCommandParamNameArg(TPC,
284 Arg.getLocation(),
285 Arg.getEndLocation(),
286 Arg.getText());
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000287}
288
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000289void Parser::parseBlockCommandArgs(BlockCommandComment *BC,
290 TextTokenRetokenizer &Retokenizer,
291 unsigned NumArgs) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000292 typedef BlockCommandComment::Argument Argument;
Dmitri Gribenko814e2192012-07-06 16:41:59 +0000293 Argument *Args =
294 new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs];
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000295 unsigned ParsedArgs = 0;
296 Token Arg;
297 while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
298 Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(),
299 Arg.getEndLocation()),
300 Arg.getText());
301 ParsedArgs++;
302 }
303
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000304 S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000305}
306
307BlockCommandComment *Parser::parseBlockCommand() {
308 assert(Tok.is(tok::command));
309
310 ParamCommandComment *PC;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000311 TParamCommandComment *TPC;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000312 BlockCommandComment *BC;
313 bool IsParam = false;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000314 bool IsTParam = false;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000315 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
316 if (Info->IsParamCommand) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000317 IsParam = true;
318 PC = S.actOnParamCommandStart(Tok.getLocation(),
319 Tok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000320 Tok.getCommandID());
321 } if (Info->IsTParamCommand) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000322 IsTParam = true;
323 TPC = S.actOnTParamCommandStart(Tok.getLocation(),
324 Tok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000325 Tok.getCommandID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000326 } else {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000327 BC = S.actOnBlockCommandStart(Tok.getLocation(),
328 Tok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000329 Tok.getCommandID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000330 }
331 consumeToken();
332
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000333 if (Tok.is(tok::command) &&
334 Traits.getCommandInfo(Tok.getCommandID())->IsBlockCommand) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000335 // Block command ahead. We can't nest block commands, so pretend that this
336 // command has an empty argument.
Dmitri Gribenkoe5deb792012-07-30 18:05:28 +0000337 ParagraphComment *Paragraph = S.actOnParagraphComment(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000338 ArrayRef<InlineContentComment *>());
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000339 if (IsParam) {
Dmitri Gribenko8a903932012-08-06 23:48:44 +0000340 S.actOnParamCommandFinish(PC, Paragraph);
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000341 return PC;
Dmitri Gribenko8a903932012-08-06 23:48:44 +0000342 } else if (IsTParam) {
343 S.actOnTParamCommandFinish(TPC, Paragraph);
344 return TPC;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000345 } else {
346 S.actOnBlockCommandFinish(BC, Paragraph);
347 return BC;
348 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000349 }
350
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000351 if (IsParam || IsTParam || Info->NumArgs > 0) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000352 // In order to parse command arguments we need to retokenize a few
353 // following text tokens.
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000354 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000355
356 if (IsParam)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000357 parseParamCommandArgs(PC, Retokenizer);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000358 else if (IsTParam)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000359 parseTParamCommandArgs(TPC, Retokenizer);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000360 else
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000361 parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000362
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000363 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000364 }
365
366 BlockContentComment *Block = parseParagraphOrBlockCommand();
367 // Since we have checked for a block command, we should have parsed a
368 // paragraph.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000369 ParagraphComment *Paragraph = cast<ParagraphComment>(Block);
370 if (IsParam) {
371 S.actOnParamCommandFinish(PC, Paragraph);
372 return PC;
373 } else if (IsTParam) {
374 S.actOnTParamCommandFinish(TPC, Paragraph);
375 return TPC;
376 } else {
377 S.actOnBlockCommandFinish(BC, Paragraph);
378 return BC;
379 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000380}
381
382InlineCommandComment *Parser::parseInlineCommand() {
383 assert(Tok.is(tok::command));
384
385 const Token CommandTok = Tok;
386 consumeToken();
387
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000388 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000389
390 Token ArgTok;
391 bool ArgTokValid = Retokenizer.lexWord(ArgTok);
392
393 InlineCommandComment *IC;
394 if (ArgTokValid) {
395 IC = S.actOnInlineCommand(CommandTok.getLocation(),
396 CommandTok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000397 CommandTok.getCommandID(),
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000398 ArgTok.getLocation(),
399 ArgTok.getEndLocation(),
400 ArgTok.getText());
401 } else {
402 IC = S.actOnInlineCommand(CommandTok.getLocation(),
403 CommandTok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000404 CommandTok.getCommandID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000405 }
406
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000407 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000408
409 return IC;
410}
411
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000412HTMLStartTagComment *Parser::parseHTMLStartTag() {
413 assert(Tok.is(tok::html_start_tag));
414 HTMLStartTagComment *HST =
415 S.actOnHTMLStartTagStart(Tok.getLocation(),
416 Tok.getHTMLTagStartName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000417 consumeToken();
418
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000419 SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000420 while (true) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000421 switch (Tok.getKind()) {
422 case tok::html_ident: {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000423 Token Ident = Tok;
424 consumeToken();
425 if (Tok.isNot(tok::html_equals)) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000426 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
427 Ident.getHTMLIdent()));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000428 continue;
429 }
430 Token Equals = Tok;
431 consumeToken();
432 if (Tok.isNot(tok::html_quoted_string)) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000433 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000434 diag::warn_doc_html_start_tag_expected_quoted_string)
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000435 << SourceRange(Equals.getLocation());
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000436 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
437 Ident.getHTMLIdent()));
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000438 while (Tok.is(tok::html_equals) ||
439 Tok.is(tok::html_quoted_string))
440 consumeToken();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000441 continue;
442 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000443 Attrs.push_back(HTMLStartTagComment::Attribute(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000444 Ident.getLocation(),
445 Ident.getHTMLIdent(),
446 Equals.getLocation(),
447 SourceRange(Tok.getLocation(),
448 Tok.getEndLocation()),
449 Tok.getHTMLQuotedString()));
450 consumeToken();
451 continue;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000452 }
453
454 case tok::html_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000455 S.actOnHTMLStartTagFinish(HST,
456 S.copyArray(llvm::makeArrayRef(Attrs)),
457 Tok.getLocation(),
458 /* IsSelfClosing = */ false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000459 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000460 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000461
462 case tok::html_slash_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000463 S.actOnHTMLStartTagFinish(HST,
464 S.copyArray(llvm::makeArrayRef(Attrs)),
465 Tok.getLocation(),
466 /* IsSelfClosing = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000467 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000468 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000469
470 case tok::html_equals:
471 case tok::html_quoted_string:
472 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000473 diag::warn_doc_html_start_tag_expected_ident_or_greater);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000474 while (Tok.is(tok::html_equals) ||
475 Tok.is(tok::html_quoted_string))
476 consumeToken();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000477 if (Tok.is(tok::html_ident) ||
478 Tok.is(tok::html_greater) ||
479 Tok.is(tok::html_slash_greater))
480 continue;
481
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000482 S.actOnHTMLStartTagFinish(HST,
483 S.copyArray(llvm::makeArrayRef(Attrs)),
484 SourceLocation(),
485 /* IsSelfClosing = */ false);
486 return HST;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000487
488 default:
489 // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000490 S.actOnHTMLStartTagFinish(HST,
491 S.copyArray(llvm::makeArrayRef(Attrs)),
492 SourceLocation(),
493 /* IsSelfClosing = */ false);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000494 bool StartLineInvalid;
495 const unsigned StartLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000496 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000497 &StartLineInvalid);
498 bool EndLineInvalid;
499 const unsigned EndLine = SourceMgr.getPresumedLineNumber(
500 Tok.getLocation(),
501 &EndLineInvalid);
502 if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
503 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000504 diag::warn_doc_html_start_tag_expected_ident_or_greater)
505 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000506 else {
507 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000508 diag::warn_doc_html_start_tag_expected_ident_or_greater);
509 Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
510 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000511 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000512 return HST;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000513 }
514 }
515}
516
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000517HTMLEndTagComment *Parser::parseHTMLEndTag() {
518 assert(Tok.is(tok::html_end_tag));
519 Token TokEndTag = Tok;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000520 consumeToken();
521 SourceLocation Loc;
522 if (Tok.is(tok::html_greater)) {
523 Loc = Tok.getLocation();
524 consumeToken();
525 }
526
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000527 return S.actOnHTMLEndTag(TokEndTag.getLocation(),
528 Loc,
529 TokEndTag.getHTMLTagEndName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000530}
531
532BlockContentComment *Parser::parseParagraphOrBlockCommand() {
533 SmallVector<InlineContentComment *, 8> Content;
534
535 while (true) {
536 switch (Tok.getKind()) {
537 case tok::verbatim_block_begin:
538 case tok::verbatim_line_name:
539 case tok::eof:
540 assert(Content.size() != 0);
541 break; // Block content or EOF ahead, finish this parapgaph.
542
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000543 case tok::unknown_command:
544 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
545 Tok.getEndLocation(),
546 Tok.getUnknownCommandName()));
547 consumeToken();
548 continue;
549
550 case tok::command: {
551 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
552 if (Info->IsBlockCommand) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000553 if (Content.size() == 0)
554 return parseBlockCommand();
555 break; // Block command ahead, finish this parapgaph.
556 }
Dmitri Gribenkob0b8a962012-09-11 19:22:03 +0000557 if (Info->IsUnknownCommand) {
558 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
559 Tok.getEndLocation(),
560 Info->getID()));
561 consumeToken();
562 continue;
563 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000564 assert(Info->IsInlineCommand);
565 Content.push_back(parseInlineCommand());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000566 continue;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000567 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000568
569 case tok::newline: {
570 consumeToken();
571 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
572 consumeToken();
573 break; // Two newlines -- end of paragraph.
574 }
575 if (Content.size() > 0)
576 Content.back()->addTrailingNewline();
577 continue;
578 }
579
580 // Don't deal with HTML tag soup now.
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000581 case tok::html_start_tag:
582 Content.push_back(parseHTMLStartTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000583 continue;
584
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000585 case tok::html_end_tag:
586 Content.push_back(parseHTMLEndTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000587 continue;
588
589 case tok::text:
590 Content.push_back(S.actOnText(Tok.getLocation(),
591 Tok.getEndLocation(),
592 Tok.getText()));
593 consumeToken();
594 continue;
595
596 case tok::verbatim_block_line:
597 case tok::verbatim_block_end:
598 case tok::verbatim_line_text:
599 case tok::html_ident:
600 case tok::html_equals:
601 case tok::html_quoted_string:
602 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000603 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000604 llvm_unreachable("should not see this token");
605 }
606 break;
607 }
608
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000609 return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000610}
611
612VerbatimBlockComment *Parser::parseVerbatimBlock() {
613 assert(Tok.is(tok::verbatim_block_begin));
614
615 VerbatimBlockComment *VB =
616 S.actOnVerbatimBlockStart(Tok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000617 Tok.getVerbatimBlockID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000618 consumeToken();
619
620 // Don't create an empty line if verbatim opening command is followed
621 // by a newline.
622 if (Tok.is(tok::newline))
623 consumeToken();
624
625 SmallVector<VerbatimBlockLineComment *, 8> Lines;
626 while (Tok.is(tok::verbatim_block_line) ||
627 Tok.is(tok::newline)) {
628 VerbatimBlockLineComment *Line;
629 if (Tok.is(tok::verbatim_block_line)) {
630 Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
631 Tok.getVerbatimBlockText());
632 consumeToken();
633 if (Tok.is(tok::newline)) {
634 consumeToken();
635 }
636 } else {
637 // Empty line, just a tok::newline.
Dmitri Gribenko94572c32012-07-18 21:27:38 +0000638 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000639 consumeToken();
640 }
641 Lines.push_back(Line);
642 }
643
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000644 if (Tok.is(tok::verbatim_block_end)) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000645 const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000646 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000647 Info->Name,
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000648 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000649 consumeToken();
650 } else {
651 // Unterminated \\verbatim block
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000652 S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
653 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000654 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000655
656 return VB;
657}
658
659VerbatimLineComment *Parser::parseVerbatimLine() {
660 assert(Tok.is(tok::verbatim_line_name));
661
662 Token NameTok = Tok;
663 consumeToken();
664
665 SourceLocation TextBegin;
666 StringRef Text;
667 // Next token might not be a tok::verbatim_line_text if verbatim line
668 // starting command comes just before a newline or comment end.
669 if (Tok.is(tok::verbatim_line_text)) {
670 TextBegin = Tok.getLocation();
671 Text = Tok.getVerbatimLineText();
672 } else {
673 TextBegin = NameTok.getEndLocation();
674 Text = "";
675 }
676
677 VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000678 NameTok.getVerbatimLineID(),
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000679 TextBegin,
680 Text);
681 consumeToken();
682 return VL;
683}
684
685BlockContentComment *Parser::parseBlockContent() {
686 switch (Tok.getKind()) {
687 case tok::text:
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000688 case tok::unknown_command:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000689 case tok::command:
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000690 case tok::html_start_tag:
691 case tok::html_end_tag:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000692 return parseParagraphOrBlockCommand();
693
694 case tok::verbatim_block_begin:
695 return parseVerbatimBlock();
696
697 case tok::verbatim_line_name:
698 return parseVerbatimLine();
699
700 case tok::eof:
701 case tok::newline:
702 case tok::verbatim_block_line:
703 case tok::verbatim_block_end:
704 case tok::verbatim_line_text:
705 case tok::html_ident:
706 case tok::html_equals:
707 case tok::html_quoted_string:
708 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000709 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000710 llvm_unreachable("should not see this token");
711 }
Matt Beaumont-Gay4d48b5c2012-07-06 21:13:09 +0000712 llvm_unreachable("bogus token kind");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000713}
714
715FullComment *Parser::parseFullComment() {
716 // Skip newlines at the beginning of the comment.
717 while (Tok.is(tok::newline))
718 consumeToken();
719
720 SmallVector<BlockContentComment *, 8> Blocks;
721 while (Tok.isNot(tok::eof)) {
722 Blocks.push_back(parseBlockContent());
723
724 // Skip extra newlines after paragraph end.
725 while (Tok.is(tok::newline))
726 consumeToken();
727 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000728 return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000729}
730
731} // end namespace comments
732} // end namespace clang