blob: a7ba013efccc18be97dc0ce3f76fcb7ad2af34af [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"
Dmitri Gribenkoaa580812012-08-09 00:03:17 +000011#include "clang/AST/CommentCommandTraits.h"
Chandler Carruth55fc8732012-12-04 09:13:33 +000012#include "clang/AST/CommentDiagnostic.h"
13#include "clang/AST/CommentSema.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
Dmitri Gribenkoca57ccd2012-12-19 17:34:55 +0000178 formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000179 return true;
180 }
181
182 bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
183 if (isEnd())
184 return false;
185
186 Position SavedPos = Pos;
187
188 consumeWhitespace();
189 SmallString<32> WordText;
190 const char *WordBegin = Pos.BufferPtr;
191 SourceLocation Loc = getSourceLocation();
192 bool Error = false;
193 if (!isEnd()) {
194 const char C = peek();
195 if (C == OpenDelim) {
196 WordText.push_back(C);
197 consumeChar();
198 } else
199 Error = true;
200 }
201 char C = '\0';
202 while (!Error && !isEnd()) {
203 C = peek();
204 WordText.push_back(C);
205 consumeChar();
206 if (C == CloseDelim)
207 break;
208 }
209 if (!Error && C != CloseDelim)
210 Error = true;
211
212 if (Error) {
213 Pos = SavedPos;
214 return false;
215 }
216
217 const unsigned Length = WordText.size();
218 char *TextPtr = Allocator.Allocate<char>(Length + 1);
219
220 memcpy(TextPtr, WordText.c_str(), Length + 1);
221 StringRef Text = StringRef(TextPtr, Length);
222
223 formTokenWithChars(Tok, Loc, WordBegin,
224 Pos.BufferPtr - WordBegin, Text);
225 return true;
226 }
227
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000228 /// Put back tokens that we didn't consume.
229 void putBackLeftoverTokens() {
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000230 if (isEnd())
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000231 return;
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000232
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000233 bool HavePartialTok = false;
234 Token PartialTok;
235 if (Pos.BufferPtr != Pos.BufferStart) {
236 formTokenWithChars(PartialTok, getSourceLocation(),
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000237 Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
238 StringRef(Pos.BufferPtr,
239 Pos.BufferEnd - Pos.BufferPtr));
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000240 HavePartialTok = true;
241 Pos.CurToken++;
242 }
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000243
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000244 P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
245 Pos.CurToken = Toks.size();
246
247 if (HavePartialTok)
248 P.putBack(PartialTok);
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000249 }
250};
251
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000252Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000253 const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
254 const CommandTraits &Traits):
255 L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
256 Traits(Traits) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000257 consumeToken();
258}
259
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000260void Parser::parseParamCommandArgs(ParamCommandComment *PC,
261 TextTokenRetokenizer &Retokenizer) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000262 Token Arg;
263 // Check if argument looks like direction specification: [dir]
264 // e.g., [in], [out], [in,out]
265 if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000266 S.actOnParamCommandDirectionArg(PC,
267 Arg.getLocation(),
268 Arg.getEndLocation(),
269 Arg.getText());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000270
271 if (Retokenizer.lexWord(Arg))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000272 S.actOnParamCommandParamNameArg(PC,
273 Arg.getLocation(),
274 Arg.getEndLocation(),
275 Arg.getText());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000276}
277
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000278void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
279 TextTokenRetokenizer &Retokenizer) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000280 Token Arg;
281 if (Retokenizer.lexWord(Arg))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000282 S.actOnTParamCommandParamNameArg(TPC,
283 Arg.getLocation(),
284 Arg.getEndLocation(),
285 Arg.getText());
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000286}
287
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000288void Parser::parseBlockCommandArgs(BlockCommandComment *BC,
289 TextTokenRetokenizer &Retokenizer,
290 unsigned NumArgs) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000291 typedef BlockCommandComment::Argument Argument;
Dmitri Gribenko814e2192012-07-06 16:41:59 +0000292 Argument *Args =
293 new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs];
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000294 unsigned ParsedArgs = 0;
295 Token Arg;
296 while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
297 Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(),
298 Arg.getEndLocation()),
299 Arg.getText());
300 ParsedArgs++;
301 }
302
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000303 S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000304}
305
306BlockCommandComment *Parser::parseBlockCommand() {
307 assert(Tok.is(tok::command));
308
309 ParamCommandComment *PC;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000310 TParamCommandComment *TPC;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000311 BlockCommandComment *BC;
312 bool IsParam = false;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000313 bool IsTParam = false;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000314 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
315 if (Info->IsParamCommand) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000316 IsParam = true;
317 PC = S.actOnParamCommandStart(Tok.getLocation(),
318 Tok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000319 Tok.getCommandID());
Dmitri Gribenkoeb34db72012-12-19 17:17:09 +0000320 } else if (Info->IsTParamCommand) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000321 IsTParam = true;
322 TPC = S.actOnTParamCommandStart(Tok.getLocation(),
323 Tok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000324 Tok.getCommandID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000325 } else {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000326 BC = S.actOnBlockCommandStart(Tok.getLocation(),
327 Tok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000328 Tok.getCommandID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000329 }
330 consumeToken();
331
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000332 if (Tok.is(tok::command) &&
333 Traits.getCommandInfo(Tok.getCommandID())->IsBlockCommand) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000334 // Block command ahead. We can't nest block commands, so pretend that this
335 // command has an empty argument.
Dmitri Gribenkoe5deb792012-07-30 18:05:28 +0000336 ParagraphComment *Paragraph = S.actOnParagraphComment(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000337 ArrayRef<InlineContentComment *>());
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000338 if (IsParam) {
Dmitri Gribenko8a903932012-08-06 23:48:44 +0000339 S.actOnParamCommandFinish(PC, Paragraph);
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000340 return PC;
Dmitri Gribenko8a903932012-08-06 23:48:44 +0000341 } else if (IsTParam) {
342 S.actOnTParamCommandFinish(TPC, Paragraph);
343 return TPC;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000344 } else {
345 S.actOnBlockCommandFinish(BC, Paragraph);
346 return BC;
347 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000348 }
349
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000350 if (IsParam || IsTParam || Info->NumArgs > 0) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000351 // In order to parse command arguments we need to retokenize a few
352 // following text tokens.
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000353 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000354
355 if (IsParam)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000356 parseParamCommandArgs(PC, Retokenizer);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000357 else if (IsTParam)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000358 parseTParamCommandArgs(TPC, Retokenizer);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000359 else
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000360 parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000361
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000362 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000363 }
364
365 BlockContentComment *Block = parseParagraphOrBlockCommand();
366 // Since we have checked for a block command, we should have parsed a
367 // paragraph.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000368 ParagraphComment *Paragraph = cast<ParagraphComment>(Block);
369 if (IsParam) {
370 S.actOnParamCommandFinish(PC, Paragraph);
371 return PC;
372 } else if (IsTParam) {
373 S.actOnTParamCommandFinish(TPC, Paragraph);
374 return TPC;
375 } else {
376 S.actOnBlockCommandFinish(BC, Paragraph);
377 return BC;
378 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000379}
380
381InlineCommandComment *Parser::parseInlineCommand() {
382 assert(Tok.is(tok::command));
383
384 const Token CommandTok = Tok;
385 consumeToken();
386
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000387 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000388
389 Token ArgTok;
390 bool ArgTokValid = Retokenizer.lexWord(ArgTok);
391
392 InlineCommandComment *IC;
393 if (ArgTokValid) {
394 IC = S.actOnInlineCommand(CommandTok.getLocation(),
395 CommandTok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000396 CommandTok.getCommandID(),
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000397 ArgTok.getLocation(),
398 ArgTok.getEndLocation(),
399 ArgTok.getText());
400 } else {
401 IC = S.actOnInlineCommand(CommandTok.getLocation(),
402 CommandTok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000403 CommandTok.getCommandID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000404 }
405
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000406 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000407
408 return IC;
409}
410
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000411HTMLStartTagComment *Parser::parseHTMLStartTag() {
412 assert(Tok.is(tok::html_start_tag));
413 HTMLStartTagComment *HST =
414 S.actOnHTMLStartTagStart(Tok.getLocation(),
415 Tok.getHTMLTagStartName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000416 consumeToken();
417
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000418 SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000419 while (true) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000420 switch (Tok.getKind()) {
421 case tok::html_ident: {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000422 Token Ident = Tok;
423 consumeToken();
424 if (Tok.isNot(tok::html_equals)) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000425 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
426 Ident.getHTMLIdent()));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000427 continue;
428 }
429 Token Equals = Tok;
430 consumeToken();
431 if (Tok.isNot(tok::html_quoted_string)) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000432 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000433 diag::warn_doc_html_start_tag_expected_quoted_string)
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000434 << SourceRange(Equals.getLocation());
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000435 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
436 Ident.getHTMLIdent()));
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000437 while (Tok.is(tok::html_equals) ||
438 Tok.is(tok::html_quoted_string))
439 consumeToken();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000440 continue;
441 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000442 Attrs.push_back(HTMLStartTagComment::Attribute(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000443 Ident.getLocation(),
444 Ident.getHTMLIdent(),
445 Equals.getLocation(),
446 SourceRange(Tok.getLocation(),
447 Tok.getEndLocation()),
448 Tok.getHTMLQuotedString()));
449 consumeToken();
450 continue;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000451 }
452
453 case tok::html_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000454 S.actOnHTMLStartTagFinish(HST,
455 S.copyArray(llvm::makeArrayRef(Attrs)),
456 Tok.getLocation(),
457 /* IsSelfClosing = */ false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000458 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000459 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000460
461 case tok::html_slash_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000462 S.actOnHTMLStartTagFinish(HST,
463 S.copyArray(llvm::makeArrayRef(Attrs)),
464 Tok.getLocation(),
465 /* IsSelfClosing = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000466 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000467 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000468
469 case tok::html_equals:
470 case tok::html_quoted_string:
471 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000472 diag::warn_doc_html_start_tag_expected_ident_or_greater);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000473 while (Tok.is(tok::html_equals) ||
474 Tok.is(tok::html_quoted_string))
475 consumeToken();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000476 if (Tok.is(tok::html_ident) ||
477 Tok.is(tok::html_greater) ||
478 Tok.is(tok::html_slash_greater))
479 continue;
480
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000481 S.actOnHTMLStartTagFinish(HST,
482 S.copyArray(llvm::makeArrayRef(Attrs)),
483 SourceLocation(),
484 /* IsSelfClosing = */ false);
485 return HST;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000486
487 default:
488 // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000489 S.actOnHTMLStartTagFinish(HST,
490 S.copyArray(llvm::makeArrayRef(Attrs)),
491 SourceLocation(),
492 /* IsSelfClosing = */ false);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000493 bool StartLineInvalid;
494 const unsigned StartLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000495 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000496 &StartLineInvalid);
497 bool EndLineInvalid;
498 const unsigned EndLine = SourceMgr.getPresumedLineNumber(
499 Tok.getLocation(),
500 &EndLineInvalid);
501 if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
502 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000503 diag::warn_doc_html_start_tag_expected_ident_or_greater)
504 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000505 else {
506 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000507 diag::warn_doc_html_start_tag_expected_ident_or_greater);
508 Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
509 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000510 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000511 return HST;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000512 }
513 }
514}
515
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000516HTMLEndTagComment *Parser::parseHTMLEndTag() {
517 assert(Tok.is(tok::html_end_tag));
518 Token TokEndTag = Tok;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000519 consumeToken();
520 SourceLocation Loc;
521 if (Tok.is(tok::html_greater)) {
522 Loc = Tok.getLocation();
523 consumeToken();
524 }
525
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000526 return S.actOnHTMLEndTag(TokEndTag.getLocation(),
527 Loc,
528 TokEndTag.getHTMLTagEndName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000529}
530
531BlockContentComment *Parser::parseParagraphOrBlockCommand() {
532 SmallVector<InlineContentComment *, 8> Content;
533
534 while (true) {
535 switch (Tok.getKind()) {
536 case tok::verbatim_block_begin:
537 case tok::verbatim_line_name:
538 case tok::eof:
539 assert(Content.size() != 0);
540 break; // Block content or EOF ahead, finish this parapgaph.
541
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000542 case tok::unknown_command:
543 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
544 Tok.getEndLocation(),
545 Tok.getUnknownCommandName()));
546 consumeToken();
547 continue;
548
549 case tok::command: {
550 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
551 if (Info->IsBlockCommand) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000552 if (Content.size() == 0)
553 return parseBlockCommand();
554 break; // Block command ahead, finish this parapgaph.
555 }
Dmitri Gribenko36cbbe92012-11-18 00:30:31 +0000556 if (Info->IsVerbatimBlockEndCommand) {
557 Diag(Tok.getLocation(),
558 diag::warn_verbatim_block_end_without_start)
559 << Info->Name
560 << SourceRange(Tok.getLocation(), Tok.getEndLocation());
561 consumeToken();
562 continue;
563 }
Dmitri Gribenkob0b8a962012-09-11 19:22:03 +0000564 if (Info->IsUnknownCommand) {
565 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
566 Tok.getEndLocation(),
567 Info->getID()));
568 consumeToken();
569 continue;
570 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000571 assert(Info->IsInlineCommand);
572 Content.push_back(parseInlineCommand());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000573 continue;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000574 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000575
576 case tok::newline: {
577 consumeToken();
578 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
579 consumeToken();
580 break; // Two newlines -- end of paragraph.
581 }
582 if (Content.size() > 0)
583 Content.back()->addTrailingNewline();
584 continue;
585 }
586
587 // Don't deal with HTML tag soup now.
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000588 case tok::html_start_tag:
589 Content.push_back(parseHTMLStartTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000590 continue;
591
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000592 case tok::html_end_tag:
593 Content.push_back(parseHTMLEndTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000594 continue;
595
596 case tok::text:
597 Content.push_back(S.actOnText(Tok.getLocation(),
598 Tok.getEndLocation(),
599 Tok.getText()));
600 consumeToken();
601 continue;
602
603 case tok::verbatim_block_line:
604 case tok::verbatim_block_end:
605 case tok::verbatim_line_text:
606 case tok::html_ident:
607 case tok::html_equals:
608 case tok::html_quoted_string:
609 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000610 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000611 llvm_unreachable("should not see this token");
612 }
613 break;
614 }
615
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000616 return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000617}
618
619VerbatimBlockComment *Parser::parseVerbatimBlock() {
620 assert(Tok.is(tok::verbatim_block_begin));
621
622 VerbatimBlockComment *VB =
623 S.actOnVerbatimBlockStart(Tok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000624 Tok.getVerbatimBlockID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000625 consumeToken();
626
627 // Don't create an empty line if verbatim opening command is followed
628 // by a newline.
629 if (Tok.is(tok::newline))
630 consumeToken();
631
632 SmallVector<VerbatimBlockLineComment *, 8> Lines;
633 while (Tok.is(tok::verbatim_block_line) ||
634 Tok.is(tok::newline)) {
635 VerbatimBlockLineComment *Line;
636 if (Tok.is(tok::verbatim_block_line)) {
637 Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
638 Tok.getVerbatimBlockText());
639 consumeToken();
640 if (Tok.is(tok::newline)) {
641 consumeToken();
642 }
643 } else {
644 // Empty line, just a tok::newline.
Dmitri Gribenko94572c32012-07-18 21:27:38 +0000645 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000646 consumeToken();
647 }
648 Lines.push_back(Line);
649 }
650
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000651 if (Tok.is(tok::verbatim_block_end)) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000652 const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000653 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000654 Info->Name,
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000655 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000656 consumeToken();
657 } else {
658 // Unterminated \\verbatim block
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000659 S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
660 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000661 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000662
663 return VB;
664}
665
666VerbatimLineComment *Parser::parseVerbatimLine() {
667 assert(Tok.is(tok::verbatim_line_name));
668
669 Token NameTok = Tok;
670 consumeToken();
671
672 SourceLocation TextBegin;
673 StringRef Text;
674 // Next token might not be a tok::verbatim_line_text if verbatim line
675 // starting command comes just before a newline or comment end.
676 if (Tok.is(tok::verbatim_line_text)) {
677 TextBegin = Tok.getLocation();
678 Text = Tok.getVerbatimLineText();
679 } else {
680 TextBegin = NameTok.getEndLocation();
681 Text = "";
682 }
683
684 VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000685 NameTok.getVerbatimLineID(),
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000686 TextBegin,
687 Text);
688 consumeToken();
689 return VL;
690}
691
692BlockContentComment *Parser::parseBlockContent() {
693 switch (Tok.getKind()) {
694 case tok::text:
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000695 case tok::unknown_command:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000696 case tok::command:
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000697 case tok::html_start_tag:
698 case tok::html_end_tag:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000699 return parseParagraphOrBlockCommand();
700
701 case tok::verbatim_block_begin:
702 return parseVerbatimBlock();
703
704 case tok::verbatim_line_name:
705 return parseVerbatimLine();
706
707 case tok::eof:
708 case tok::newline:
709 case tok::verbatim_block_line:
710 case tok::verbatim_block_end:
711 case tok::verbatim_line_text:
712 case tok::html_ident:
713 case tok::html_equals:
714 case tok::html_quoted_string:
715 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000716 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000717 llvm_unreachable("should not see this token");
718 }
Matt Beaumont-Gay4d48b5c2012-07-06 21:13:09 +0000719 llvm_unreachable("bogus token kind");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000720}
721
722FullComment *Parser::parseFullComment() {
723 // Skip newlines at the beginning of the comment.
724 while (Tok.is(tok::newline))
725 consumeToken();
726
727 SmallVector<BlockContentComment *, 8> Blocks;
728 while (Tok.isNot(tok::eof)) {
729 Blocks.push_back(parseBlockContent());
730
731 // Skip extra newlines after paragraph end.
732 while (Tok.is(tok::newline))
733 consumeToken();
734 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000735 return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000736}
737
738} // end namespace comments
739} // end namespace clang