blob: eb1027a9b6396772166cc76f10c18c8e2d24207e [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"
13#include "clang/Basic/SourceManager.h"
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000014#include "llvm/Support/ErrorHandling.h"
15
16namespace clang {
17namespace comments {
18
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000019/// Re-lexes a sequence of tok::text tokens.
20class TextTokenRetokenizer {
21 llvm::BumpPtrAllocator &Allocator;
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000022 Parser &P;
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000023
24 /// This flag is set when there are no more tokens we can fetch from lexer.
25 bool NoMoreInterestingTokens;
26
27 /// Token buffer: tokens we have processed and lookahead.
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000028 SmallVector<Token, 16> Toks;
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000029
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000030 /// A position in \c Toks.
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000031 struct Position {
32 unsigned CurToken;
33 const char *BufferStart;
34 const char *BufferEnd;
35 const char *BufferPtr;
36 SourceLocation BufferStartLoc;
37 };
38
39 /// Current position in Toks.
40 Position Pos;
41
42 bool isEnd() const {
43 return Pos.CurToken >= Toks.size();
44 }
45
46 /// Sets up the buffer pointers to point to current token.
47 void setupBuffer() {
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000048 assert(!isEnd());
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000049 const Token &Tok = Toks[Pos.CurToken];
50
51 Pos.BufferStart = Tok.getText().begin();
52 Pos.BufferEnd = Tok.getText().end();
53 Pos.BufferPtr = Pos.BufferStart;
54 Pos.BufferStartLoc = Tok.getLocation();
55 }
56
57 SourceLocation getSourceLocation() const {
58 const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
59 return Pos.BufferStartLoc.getLocWithOffset(CharNo);
60 }
61
62 char peek() const {
63 assert(!isEnd());
64 assert(Pos.BufferPtr != Pos.BufferEnd);
65 return *Pos.BufferPtr;
66 }
67
68 void consumeChar() {
69 assert(!isEnd());
70 assert(Pos.BufferPtr != Pos.BufferEnd);
71 Pos.BufferPtr++;
72 if (Pos.BufferPtr == Pos.BufferEnd) {
73 Pos.CurToken++;
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000074 if (isEnd() && !addToken())
75 return;
76
77 assert(!isEnd());
78 setupBuffer();
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000079 }
80 }
81
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000082 /// Add a token.
83 /// Returns true on success, false if there are no interesting tokens to
84 /// fetch from lexer.
85 bool addToken() {
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000086 if (NoMoreInterestingTokens)
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000087 return false;
88
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000089 if (P.Tok.is(tok::newline)) {
90 // If we see a single newline token between text tokens, skip it.
91 Token Newline = P.Tok;
92 P.consumeToken();
93 if (P.Tok.isNot(tok::text)) {
94 P.putBack(Newline);
95 NoMoreInterestingTokens = true;
96 return false;
97 }
98 }
99 if (P.Tok.isNot(tok::text)) {
100 NoMoreInterestingTokens = true;
101 return false;
102 }
103
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000104 Toks.push_back(P.Tok);
105 P.consumeToken();
106 if (Toks.size() == 1)
107 setupBuffer();
108 return true;
109 }
110
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000111 static bool isWhitespace(char C) {
112 return C == ' ' || C == '\n' || C == '\r' ||
113 C == '\t' || C == '\f' || C == '\v';
114 }
115
116 void consumeWhitespace() {
117 while (!isEnd()) {
118 if (isWhitespace(peek()))
119 consumeChar();
120 else
121 break;
122 }
123 }
124
125 void formTokenWithChars(Token &Result,
126 SourceLocation Loc,
127 const char *TokBegin,
128 unsigned TokLength,
129 StringRef Text) {
130 Result.setLocation(Loc);
131 Result.setKind(tok::text);
132 Result.setLength(TokLength);
133#ifndef NDEBUG
134 Result.TextPtr1 = "<UNSET>";
135 Result.TextLen1 = 7;
136#endif
137 Result.setText(Text);
138 }
139
140public:
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000141 TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
Dmitri Gribenko0c43a922012-07-24 18:23:31 +0000142 Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000143 Pos.CurToken = 0;
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000144 addToken();
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000145 }
146
147 /// Extract a word -- sequence of non-whitespace characters.
148 bool lexWord(Token &Tok) {
149 if (isEnd())
150 return false;
151
152 Position SavedPos = Pos;
153
154 consumeWhitespace();
155 SmallString<32> WordText;
156 const char *WordBegin = Pos.BufferPtr;
157 SourceLocation Loc = getSourceLocation();
158 while (!isEnd()) {
159 const char C = peek();
160 if (!isWhitespace(C)) {
161 WordText.push_back(C);
162 consumeChar();
163 } else
164 break;
165 }
166 const unsigned Length = WordText.size();
167 if (Length == 0) {
168 Pos = SavedPos;
169 return false;
170 }
171
172 char *TextPtr = Allocator.Allocate<char>(Length + 1);
173
174 memcpy(TextPtr, WordText.c_str(), Length + 1);
175 StringRef Text = StringRef(TextPtr, Length);
176
177 formTokenWithChars(Tok, Loc, WordBegin,
178 Pos.BufferPtr - WordBegin, Text);
179 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,
253 const SourceManager &SourceMgr, DiagnosticsEngine &Diags):
254 L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000255 consumeToken();
256}
257
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000258void Parser::parseParamCommandArgs(ParamCommandComment *PC,
259 TextTokenRetokenizer &Retokenizer) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000260 Token Arg;
261 // Check if argument looks like direction specification: [dir]
262 // e.g., [in], [out], [in,out]
263 if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000264 S.actOnParamCommandDirectionArg(PC,
265 Arg.getLocation(),
266 Arg.getEndLocation(),
267 Arg.getText());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000268
269 if (Retokenizer.lexWord(Arg))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000270 S.actOnParamCommandParamNameArg(PC,
271 Arg.getLocation(),
272 Arg.getEndLocation(),
273 Arg.getText());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000274}
275
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000276void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
277 TextTokenRetokenizer &Retokenizer) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000278 Token Arg;
279 if (Retokenizer.lexWord(Arg))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000280 S.actOnTParamCommandParamNameArg(TPC,
281 Arg.getLocation(),
282 Arg.getEndLocation(),
283 Arg.getText());
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000284}
285
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000286void Parser::parseBlockCommandArgs(BlockCommandComment *BC,
287 TextTokenRetokenizer &Retokenizer,
288 unsigned NumArgs) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000289 typedef BlockCommandComment::Argument Argument;
Dmitri Gribenko814e2192012-07-06 16:41:59 +0000290 Argument *Args =
291 new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs];
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000292 unsigned ParsedArgs = 0;
293 Token Arg;
294 while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
295 Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(),
296 Arg.getEndLocation()),
297 Arg.getText());
298 ParsedArgs++;
299 }
300
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000301 S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000302}
303
304BlockCommandComment *Parser::parseBlockCommand() {
305 assert(Tok.is(tok::command));
306
307 ParamCommandComment *PC;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000308 TParamCommandComment *TPC;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000309 BlockCommandComment *BC;
310 bool IsParam = false;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000311 bool IsTParam = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000312 unsigned NumArgs = 0;
313 if (S.isParamCommand(Tok.getCommandName())) {
314 IsParam = true;
315 PC = S.actOnParamCommandStart(Tok.getLocation(),
316 Tok.getEndLocation(),
317 Tok.getCommandName());
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000318 } if (S.isTParamCommand(Tok.getCommandName())) {
319 IsTParam = true;
320 TPC = S.actOnTParamCommandStart(Tok.getLocation(),
321 Tok.getEndLocation(),
322 Tok.getCommandName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000323 } else {
324 NumArgs = S.getBlockCommandNumArgs(Tok.getCommandName());
325 BC = S.actOnBlockCommandStart(Tok.getLocation(),
326 Tok.getEndLocation(),
327 Tok.getCommandName());
328 }
329 consumeToken();
330
331 if (Tok.is(tok::command) && S.isBlockCommand(Tok.getCommandName())) {
332 // Block command ahead. We can't nest block commands, so pretend that this
333 // command has an empty argument.
Dmitri Gribenkoe5deb792012-07-30 18:05:28 +0000334 ParagraphComment *Paragraph = S.actOnParagraphComment(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000335 ArrayRef<InlineContentComment *>());
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000336 if (IsParam) {
Dmitri Gribenko8a903932012-08-06 23:48:44 +0000337 S.actOnParamCommandFinish(PC, Paragraph);
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000338 return PC;
Dmitri Gribenko8a903932012-08-06 23:48:44 +0000339 } else if (IsTParam) {
340 S.actOnTParamCommandFinish(TPC, Paragraph);
341 return TPC;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000342 } else {
343 S.actOnBlockCommandFinish(BC, Paragraph);
344 return BC;
345 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000346 }
347
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000348 if (IsParam || IsTParam || NumArgs > 0) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000349 // In order to parse command arguments we need to retokenize a few
350 // following text tokens.
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000351 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000352
353 if (IsParam)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000354 parseParamCommandArgs(PC, Retokenizer);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000355 else if (IsTParam)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000356 parseTParamCommandArgs(TPC, Retokenizer);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000357 else
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000358 parseBlockCommandArgs(BC, Retokenizer, NumArgs);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000359
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000360 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000361 }
362
363 BlockContentComment *Block = parseParagraphOrBlockCommand();
364 // Since we have checked for a block command, we should have parsed a
365 // paragraph.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000366 ParagraphComment *Paragraph = cast<ParagraphComment>(Block);
367 if (IsParam) {
368 S.actOnParamCommandFinish(PC, Paragraph);
369 return PC;
370 } else if (IsTParam) {
371 S.actOnTParamCommandFinish(TPC, Paragraph);
372 return TPC;
373 } else {
374 S.actOnBlockCommandFinish(BC, Paragraph);
375 return BC;
376 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000377}
378
379InlineCommandComment *Parser::parseInlineCommand() {
380 assert(Tok.is(tok::command));
381
382 const Token CommandTok = Tok;
383 consumeToken();
384
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000385 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000386
387 Token ArgTok;
388 bool ArgTokValid = Retokenizer.lexWord(ArgTok);
389
390 InlineCommandComment *IC;
391 if (ArgTokValid) {
392 IC = S.actOnInlineCommand(CommandTok.getLocation(),
393 CommandTok.getEndLocation(),
394 CommandTok.getCommandName(),
395 ArgTok.getLocation(),
396 ArgTok.getEndLocation(),
397 ArgTok.getText());
398 } else {
399 IC = S.actOnInlineCommand(CommandTok.getLocation(),
400 CommandTok.getEndLocation(),
401 CommandTok.getCommandName());
402 }
403
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000404 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000405
406 return IC;
407}
408
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000409HTMLStartTagComment *Parser::parseHTMLStartTag() {
410 assert(Tok.is(tok::html_start_tag));
411 HTMLStartTagComment *HST =
412 S.actOnHTMLStartTagStart(Tok.getLocation(),
413 Tok.getHTMLTagStartName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000414 consumeToken();
415
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000416 SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000417 while (true) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000418 switch (Tok.getKind()) {
419 case tok::html_ident: {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000420 Token Ident = Tok;
421 consumeToken();
422 if (Tok.isNot(tok::html_equals)) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000423 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
424 Ident.getHTMLIdent()));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000425 continue;
426 }
427 Token Equals = Tok;
428 consumeToken();
429 if (Tok.isNot(tok::html_quoted_string)) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000430 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000431 diag::warn_doc_html_start_tag_expected_quoted_string)
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000432 << SourceRange(Equals.getLocation());
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000433 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
434 Ident.getHTMLIdent()));
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000435 while (Tok.is(tok::html_equals) ||
436 Tok.is(tok::html_quoted_string))
437 consumeToken();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000438 continue;
439 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000440 Attrs.push_back(HTMLStartTagComment::Attribute(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000441 Ident.getLocation(),
442 Ident.getHTMLIdent(),
443 Equals.getLocation(),
444 SourceRange(Tok.getLocation(),
445 Tok.getEndLocation()),
446 Tok.getHTMLQuotedString()));
447 consumeToken();
448 continue;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000449 }
450
451 case tok::html_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000452 S.actOnHTMLStartTagFinish(HST,
453 S.copyArray(llvm::makeArrayRef(Attrs)),
454 Tok.getLocation(),
455 /* IsSelfClosing = */ false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000456 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000457 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000458
459 case tok::html_slash_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000460 S.actOnHTMLStartTagFinish(HST,
461 S.copyArray(llvm::makeArrayRef(Attrs)),
462 Tok.getLocation(),
463 /* IsSelfClosing = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000464 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000465 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000466
467 case tok::html_equals:
468 case tok::html_quoted_string:
469 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000470 diag::warn_doc_html_start_tag_expected_ident_or_greater);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000471 while (Tok.is(tok::html_equals) ||
472 Tok.is(tok::html_quoted_string))
473 consumeToken();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000474 if (Tok.is(tok::html_ident) ||
475 Tok.is(tok::html_greater) ||
476 Tok.is(tok::html_slash_greater))
477 continue;
478
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000479 S.actOnHTMLStartTagFinish(HST,
480 S.copyArray(llvm::makeArrayRef(Attrs)),
481 SourceLocation(),
482 /* IsSelfClosing = */ false);
483 return HST;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000484
485 default:
486 // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000487 S.actOnHTMLStartTagFinish(HST,
488 S.copyArray(llvm::makeArrayRef(Attrs)),
489 SourceLocation(),
490 /* IsSelfClosing = */ false);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000491 bool StartLineInvalid;
492 const unsigned StartLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000493 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000494 &StartLineInvalid);
495 bool EndLineInvalid;
496 const unsigned EndLine = SourceMgr.getPresumedLineNumber(
497 Tok.getLocation(),
498 &EndLineInvalid);
499 if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
500 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000501 diag::warn_doc_html_start_tag_expected_ident_or_greater)
502 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000503 else {
504 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000505 diag::warn_doc_html_start_tag_expected_ident_or_greater);
506 Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
507 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000508 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000509 return HST;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000510 }
511 }
512}
513
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000514HTMLEndTagComment *Parser::parseHTMLEndTag() {
515 assert(Tok.is(tok::html_end_tag));
516 Token TokEndTag = Tok;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000517 consumeToken();
518 SourceLocation Loc;
519 if (Tok.is(tok::html_greater)) {
520 Loc = Tok.getLocation();
521 consumeToken();
522 }
523
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000524 return S.actOnHTMLEndTag(TokEndTag.getLocation(),
525 Loc,
526 TokEndTag.getHTMLTagEndName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000527}
528
529BlockContentComment *Parser::parseParagraphOrBlockCommand() {
530 SmallVector<InlineContentComment *, 8> Content;
531
532 while (true) {
533 switch (Tok.getKind()) {
534 case tok::verbatim_block_begin:
535 case tok::verbatim_line_name:
536 case tok::eof:
537 assert(Content.size() != 0);
538 break; // Block content or EOF ahead, finish this parapgaph.
539
540 case tok::command:
541 if (S.isBlockCommand(Tok.getCommandName())) {
542 if (Content.size() == 0)
543 return parseBlockCommand();
544 break; // Block command ahead, finish this parapgaph.
545 }
546 if (S.isInlineCommand(Tok.getCommandName())) {
547 Content.push_back(parseInlineCommand());
548 continue;
549 }
550
551 // Not a block command, not an inline command ==> an unknown command.
552 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
553 Tok.getEndLocation(),
554 Tok.getCommandName()));
555 consumeToken();
556 continue;
557
558 case tok::newline: {
559 consumeToken();
560 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
561 consumeToken();
562 break; // Two newlines -- end of paragraph.
563 }
564 if (Content.size() > 0)
565 Content.back()->addTrailingNewline();
566 continue;
567 }
568
569 // Don't deal with HTML tag soup now.
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000570 case tok::html_start_tag:
571 Content.push_back(parseHTMLStartTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000572 continue;
573
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000574 case tok::html_end_tag:
575 Content.push_back(parseHTMLEndTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000576 continue;
577
578 case tok::text:
579 Content.push_back(S.actOnText(Tok.getLocation(),
580 Tok.getEndLocation(),
581 Tok.getText()));
582 consumeToken();
583 continue;
584
585 case tok::verbatim_block_line:
586 case tok::verbatim_block_end:
587 case tok::verbatim_line_text:
588 case tok::html_ident:
589 case tok::html_equals:
590 case tok::html_quoted_string:
591 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000592 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000593 llvm_unreachable("should not see this token");
594 }
595 break;
596 }
597
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000598 return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000599}
600
601VerbatimBlockComment *Parser::parseVerbatimBlock() {
602 assert(Tok.is(tok::verbatim_block_begin));
603
604 VerbatimBlockComment *VB =
605 S.actOnVerbatimBlockStart(Tok.getLocation(),
606 Tok.getVerbatimBlockName());
607 consumeToken();
608
609 // Don't create an empty line if verbatim opening command is followed
610 // by a newline.
611 if (Tok.is(tok::newline))
612 consumeToken();
613
614 SmallVector<VerbatimBlockLineComment *, 8> Lines;
615 while (Tok.is(tok::verbatim_block_line) ||
616 Tok.is(tok::newline)) {
617 VerbatimBlockLineComment *Line;
618 if (Tok.is(tok::verbatim_block_line)) {
619 Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
620 Tok.getVerbatimBlockText());
621 consumeToken();
622 if (Tok.is(tok::newline)) {
623 consumeToken();
624 }
625 } else {
626 // Empty line, just a tok::newline.
Dmitri Gribenko94572c32012-07-18 21:27:38 +0000627 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000628 consumeToken();
629 }
630 Lines.push_back(Line);
631 }
632
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000633 if (Tok.is(tok::verbatim_block_end)) {
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000634 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
635 Tok.getVerbatimBlockName(),
636 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000637 consumeToken();
638 } else {
639 // Unterminated \\verbatim block
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000640 S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
641 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000642 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000643
644 return VB;
645}
646
647VerbatimLineComment *Parser::parseVerbatimLine() {
648 assert(Tok.is(tok::verbatim_line_name));
649
650 Token NameTok = Tok;
651 consumeToken();
652
653 SourceLocation TextBegin;
654 StringRef Text;
655 // Next token might not be a tok::verbatim_line_text if verbatim line
656 // starting command comes just before a newline or comment end.
657 if (Tok.is(tok::verbatim_line_text)) {
658 TextBegin = Tok.getLocation();
659 Text = Tok.getVerbatimLineText();
660 } else {
661 TextBegin = NameTok.getEndLocation();
662 Text = "";
663 }
664
665 VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
666 NameTok.getVerbatimLineName(),
667 TextBegin,
668 Text);
669 consumeToken();
670 return VL;
671}
672
673BlockContentComment *Parser::parseBlockContent() {
674 switch (Tok.getKind()) {
675 case tok::text:
676 case tok::command:
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000677 case tok::html_start_tag:
678 case tok::html_end_tag:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000679 return parseParagraphOrBlockCommand();
680
681 case tok::verbatim_block_begin:
682 return parseVerbatimBlock();
683
684 case tok::verbatim_line_name:
685 return parseVerbatimLine();
686
687 case tok::eof:
688 case tok::newline:
689 case tok::verbatim_block_line:
690 case tok::verbatim_block_end:
691 case tok::verbatim_line_text:
692 case tok::html_ident:
693 case tok::html_equals:
694 case tok::html_quoted_string:
695 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000696 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000697 llvm_unreachable("should not see this token");
698 }
Matt Beaumont-Gay4d48b5c2012-07-06 21:13:09 +0000699 llvm_unreachable("bogus token kind");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000700}
701
702FullComment *Parser::parseFullComment() {
703 // Skip newlines at the beginning of the comment.
704 while (Tok.is(tok::newline))
705 consumeToken();
706
707 SmallVector<BlockContentComment *, 8> Blocks;
708 while (Tok.isNot(tok::eof)) {
709 Blocks.push_back(parseBlockContent());
710
711 // Skip extra newlines after paragraph end.
712 while (Tok.is(tok::newline))
713 consumeToken();
714 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000715 return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000716}
717
718} // end namespace comments
719} // end namespace clang