blob: 8d7716a9d5baf9eccdd1549dae4909a1c3d06d73 [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) {
337 S.actOnBlockCommandFinish(PC, Paragraph);
338 return PC;
339 } else {
340 S.actOnBlockCommandFinish(BC, Paragraph);
341 return BC;
342 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000343 }
344
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000345 if (IsParam || IsTParam || NumArgs > 0) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000346 // In order to parse command arguments we need to retokenize a few
347 // following text tokens.
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000348 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000349
350 if (IsParam)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000351 parseParamCommandArgs(PC, Retokenizer);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000352 else if (IsTParam)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000353 parseTParamCommandArgs(TPC, Retokenizer);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000354 else
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000355 parseBlockCommandArgs(BC, Retokenizer, NumArgs);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000356
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000357 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000358 }
359
360 BlockContentComment *Block = parseParagraphOrBlockCommand();
361 // Since we have checked for a block command, we should have parsed a
362 // paragraph.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000363 ParagraphComment *Paragraph = cast<ParagraphComment>(Block);
364 if (IsParam) {
365 S.actOnParamCommandFinish(PC, Paragraph);
366 return PC;
367 } else if (IsTParam) {
368 S.actOnTParamCommandFinish(TPC, Paragraph);
369 return TPC;
370 } else {
371 S.actOnBlockCommandFinish(BC, Paragraph);
372 return BC;
373 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000374}
375
376InlineCommandComment *Parser::parseInlineCommand() {
377 assert(Tok.is(tok::command));
378
379 const Token CommandTok = Tok;
380 consumeToken();
381
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000382 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000383
384 Token ArgTok;
385 bool ArgTokValid = Retokenizer.lexWord(ArgTok);
386
387 InlineCommandComment *IC;
388 if (ArgTokValid) {
389 IC = S.actOnInlineCommand(CommandTok.getLocation(),
390 CommandTok.getEndLocation(),
391 CommandTok.getCommandName(),
392 ArgTok.getLocation(),
393 ArgTok.getEndLocation(),
394 ArgTok.getText());
395 } else {
396 IC = S.actOnInlineCommand(CommandTok.getLocation(),
397 CommandTok.getEndLocation(),
398 CommandTok.getCommandName());
399 }
400
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000401 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000402
403 return IC;
404}
405
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000406HTMLStartTagComment *Parser::parseHTMLStartTag() {
407 assert(Tok.is(tok::html_start_tag));
408 HTMLStartTagComment *HST =
409 S.actOnHTMLStartTagStart(Tok.getLocation(),
410 Tok.getHTMLTagStartName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000411 consumeToken();
412
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000413 SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000414 while (true) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000415 switch (Tok.getKind()) {
416 case tok::html_ident: {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000417 Token Ident = Tok;
418 consumeToken();
419 if (Tok.isNot(tok::html_equals)) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000420 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
421 Ident.getHTMLIdent()));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000422 continue;
423 }
424 Token Equals = Tok;
425 consumeToken();
426 if (Tok.isNot(tok::html_quoted_string)) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000427 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000428 diag::warn_doc_html_start_tag_expected_quoted_string)
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000429 << SourceRange(Equals.getLocation());
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000430 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
431 Ident.getHTMLIdent()));
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000432 while (Tok.is(tok::html_equals) ||
433 Tok.is(tok::html_quoted_string))
434 consumeToken();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000435 continue;
436 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000437 Attrs.push_back(HTMLStartTagComment::Attribute(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000438 Ident.getLocation(),
439 Ident.getHTMLIdent(),
440 Equals.getLocation(),
441 SourceRange(Tok.getLocation(),
442 Tok.getEndLocation()),
443 Tok.getHTMLQuotedString()));
444 consumeToken();
445 continue;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000446 }
447
448 case tok::html_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000449 S.actOnHTMLStartTagFinish(HST,
450 S.copyArray(llvm::makeArrayRef(Attrs)),
451 Tok.getLocation(),
452 /* IsSelfClosing = */ false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000453 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000454 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000455
456 case tok::html_slash_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000457 S.actOnHTMLStartTagFinish(HST,
458 S.copyArray(llvm::makeArrayRef(Attrs)),
459 Tok.getLocation(),
460 /* IsSelfClosing = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000461 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000462 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000463
464 case tok::html_equals:
465 case tok::html_quoted_string:
466 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000467 diag::warn_doc_html_start_tag_expected_ident_or_greater);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000468 while (Tok.is(tok::html_equals) ||
469 Tok.is(tok::html_quoted_string))
470 consumeToken();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000471 if (Tok.is(tok::html_ident) ||
472 Tok.is(tok::html_greater) ||
473 Tok.is(tok::html_slash_greater))
474 continue;
475
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000476 S.actOnHTMLStartTagFinish(HST,
477 S.copyArray(llvm::makeArrayRef(Attrs)),
478 SourceLocation(),
479 /* IsSelfClosing = */ false);
480 return HST;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000481
482 default:
483 // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000484 S.actOnHTMLStartTagFinish(HST,
485 S.copyArray(llvm::makeArrayRef(Attrs)),
486 SourceLocation(),
487 /* IsSelfClosing = */ false);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000488 bool StartLineInvalid;
489 const unsigned StartLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000490 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000491 &StartLineInvalid);
492 bool EndLineInvalid;
493 const unsigned EndLine = SourceMgr.getPresumedLineNumber(
494 Tok.getLocation(),
495 &EndLineInvalid);
496 if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
497 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000498 diag::warn_doc_html_start_tag_expected_ident_or_greater)
499 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000500 else {
501 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000502 diag::warn_doc_html_start_tag_expected_ident_or_greater);
503 Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
504 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000505 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000506 return HST;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000507 }
508 }
509}
510
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000511HTMLEndTagComment *Parser::parseHTMLEndTag() {
512 assert(Tok.is(tok::html_end_tag));
513 Token TokEndTag = Tok;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000514 consumeToken();
515 SourceLocation Loc;
516 if (Tok.is(tok::html_greater)) {
517 Loc = Tok.getLocation();
518 consumeToken();
519 }
520
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000521 return S.actOnHTMLEndTag(TokEndTag.getLocation(),
522 Loc,
523 TokEndTag.getHTMLTagEndName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000524}
525
526BlockContentComment *Parser::parseParagraphOrBlockCommand() {
527 SmallVector<InlineContentComment *, 8> Content;
528
529 while (true) {
530 switch (Tok.getKind()) {
531 case tok::verbatim_block_begin:
532 case tok::verbatim_line_name:
533 case tok::eof:
534 assert(Content.size() != 0);
535 break; // Block content or EOF ahead, finish this parapgaph.
536
537 case tok::command:
538 if (S.isBlockCommand(Tok.getCommandName())) {
539 if (Content.size() == 0)
540 return parseBlockCommand();
541 break; // Block command ahead, finish this parapgaph.
542 }
543 if (S.isInlineCommand(Tok.getCommandName())) {
544 Content.push_back(parseInlineCommand());
545 continue;
546 }
547
548 // Not a block command, not an inline command ==> an unknown command.
549 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
550 Tok.getEndLocation(),
551 Tok.getCommandName()));
552 consumeToken();
553 continue;
554
555 case tok::newline: {
556 consumeToken();
557 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
558 consumeToken();
559 break; // Two newlines -- end of paragraph.
560 }
561 if (Content.size() > 0)
562 Content.back()->addTrailingNewline();
563 continue;
564 }
565
566 // Don't deal with HTML tag soup now.
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000567 case tok::html_start_tag:
568 Content.push_back(parseHTMLStartTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000569 continue;
570
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000571 case tok::html_end_tag:
572 Content.push_back(parseHTMLEndTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000573 continue;
574
575 case tok::text:
576 Content.push_back(S.actOnText(Tok.getLocation(),
577 Tok.getEndLocation(),
578 Tok.getText()));
579 consumeToken();
580 continue;
581
582 case tok::verbatim_block_line:
583 case tok::verbatim_block_end:
584 case tok::verbatim_line_text:
585 case tok::html_ident:
586 case tok::html_equals:
587 case tok::html_quoted_string:
588 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000589 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000590 llvm_unreachable("should not see this token");
591 }
592 break;
593 }
594
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000595 return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000596}
597
598VerbatimBlockComment *Parser::parseVerbatimBlock() {
599 assert(Tok.is(tok::verbatim_block_begin));
600
601 VerbatimBlockComment *VB =
602 S.actOnVerbatimBlockStart(Tok.getLocation(),
603 Tok.getVerbatimBlockName());
604 consumeToken();
605
606 // Don't create an empty line if verbatim opening command is followed
607 // by a newline.
608 if (Tok.is(tok::newline))
609 consumeToken();
610
611 SmallVector<VerbatimBlockLineComment *, 8> Lines;
612 while (Tok.is(tok::verbatim_block_line) ||
613 Tok.is(tok::newline)) {
614 VerbatimBlockLineComment *Line;
615 if (Tok.is(tok::verbatim_block_line)) {
616 Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
617 Tok.getVerbatimBlockText());
618 consumeToken();
619 if (Tok.is(tok::newline)) {
620 consumeToken();
621 }
622 } else {
623 // Empty line, just a tok::newline.
Dmitri Gribenko94572c32012-07-18 21:27:38 +0000624 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000625 consumeToken();
626 }
627 Lines.push_back(Line);
628 }
629
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000630 if (Tok.is(tok::verbatim_block_end)) {
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000631 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
632 Tok.getVerbatimBlockName(),
633 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000634 consumeToken();
635 } else {
636 // Unterminated \\verbatim block
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000637 S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
638 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000639 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000640
641 return VB;
642}
643
644VerbatimLineComment *Parser::parseVerbatimLine() {
645 assert(Tok.is(tok::verbatim_line_name));
646
647 Token NameTok = Tok;
648 consumeToken();
649
650 SourceLocation TextBegin;
651 StringRef Text;
652 // Next token might not be a tok::verbatim_line_text if verbatim line
653 // starting command comes just before a newline or comment end.
654 if (Tok.is(tok::verbatim_line_text)) {
655 TextBegin = Tok.getLocation();
656 Text = Tok.getVerbatimLineText();
657 } else {
658 TextBegin = NameTok.getEndLocation();
659 Text = "";
660 }
661
662 VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
663 NameTok.getVerbatimLineName(),
664 TextBegin,
665 Text);
666 consumeToken();
667 return VL;
668}
669
670BlockContentComment *Parser::parseBlockContent() {
671 switch (Tok.getKind()) {
672 case tok::text:
673 case tok::command:
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000674 case tok::html_start_tag:
675 case tok::html_end_tag:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000676 return parseParagraphOrBlockCommand();
677
678 case tok::verbatim_block_begin:
679 return parseVerbatimBlock();
680
681 case tok::verbatim_line_name:
682 return parseVerbatimLine();
683
684 case tok::eof:
685 case tok::newline:
686 case tok::verbatim_block_line:
687 case tok::verbatim_block_end:
688 case tok::verbatim_line_text:
689 case tok::html_ident:
690 case tok::html_equals:
691 case tok::html_quoted_string:
692 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000693 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000694 llvm_unreachable("should not see this token");
695 }
Matt Beaumont-Gay4d48b5c2012-07-06 21:13:09 +0000696 llvm_unreachable("bogus token kind");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000697}
698
699FullComment *Parser::parseFullComment() {
700 // Skip newlines at the beginning of the comment.
701 while (Tok.is(tok::newline))
702 consumeToken();
703
704 SmallVector<BlockContentComment *, 8> Blocks;
705 while (Tok.isNot(tok::eof)) {
706 Blocks.push_back(parseBlockContent());
707
708 // Skip extra newlines after paragraph end.
709 while (Tok.is(tok::newline))
710 consumeToken();
711 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000712 return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000713}
714
715} // end namespace comments
716} // end namespace clang