blob: 1224db6e0eef3989e0c1b39ba02d81f0fd173ed5 [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 Gribenkobf881442013-02-09 15:16:58 +000014#include "clang/Basic/CharInfo.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000015#include "clang/Basic/SourceManager.h"
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000016#include "llvm/Support/ErrorHandling.h"
17
18namespace clang {
19namespace comments {
20
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000021/// Re-lexes a sequence of tok::text tokens.
22class TextTokenRetokenizer {
23 llvm::BumpPtrAllocator &Allocator;
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000024 Parser &P;
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000025
26 /// This flag is set when there are no more tokens we can fetch from lexer.
27 bool NoMoreInterestingTokens;
28
29 /// Token buffer: tokens we have processed and lookahead.
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000030 SmallVector<Token, 16> Toks;
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000031
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000032 /// A position in \c Toks.
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000033 struct Position {
34 unsigned CurToken;
35 const char *BufferStart;
36 const char *BufferEnd;
37 const char *BufferPtr;
38 SourceLocation BufferStartLoc;
39 };
40
41 /// Current position in Toks.
42 Position Pos;
43
44 bool isEnd() const {
45 return Pos.CurToken >= Toks.size();
46 }
47
48 /// Sets up the buffer pointers to point to current token.
49 void setupBuffer() {
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000050 assert(!isEnd());
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000051 const Token &Tok = Toks[Pos.CurToken];
52
53 Pos.BufferStart = Tok.getText().begin();
54 Pos.BufferEnd = Tok.getText().end();
55 Pos.BufferPtr = Pos.BufferStart;
56 Pos.BufferStartLoc = Tok.getLocation();
57 }
58
59 SourceLocation getSourceLocation() const {
60 const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
61 return Pos.BufferStartLoc.getLocWithOffset(CharNo);
62 }
63
64 char peek() const {
65 assert(!isEnd());
66 assert(Pos.BufferPtr != Pos.BufferEnd);
67 return *Pos.BufferPtr;
68 }
69
70 void consumeChar() {
71 assert(!isEnd());
72 assert(Pos.BufferPtr != Pos.BufferEnd);
73 Pos.BufferPtr++;
74 if (Pos.BufferPtr == Pos.BufferEnd) {
75 Pos.CurToken++;
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000076 if (isEnd() && !addToken())
77 return;
78
79 assert(!isEnd());
80 setupBuffer();
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000081 }
82 }
83
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000084 /// Add a token.
85 /// Returns true on success, false if there are no interesting tokens to
86 /// fetch from lexer.
87 bool addToken() {
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000088 if (NoMoreInterestingTokens)
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000089 return false;
90
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000091 if (P.Tok.is(tok::newline)) {
92 // If we see a single newline token between text tokens, skip it.
93 Token Newline = P.Tok;
94 P.consumeToken();
95 if (P.Tok.isNot(tok::text)) {
96 P.putBack(Newline);
97 NoMoreInterestingTokens = true;
98 return false;
99 }
100 }
101 if (P.Tok.isNot(tok::text)) {
102 NoMoreInterestingTokens = true;
103 return false;
104 }
105
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000106 Toks.push_back(P.Tok);
107 P.consumeToken();
108 if (Toks.size() == 1)
109 setupBuffer();
110 return true;
111 }
112
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000113 void consumeWhitespace() {
114 while (!isEnd()) {
115 if (isWhitespace(peek()))
116 consumeChar();
117 else
118 break;
119 }
120 }
121
122 void formTokenWithChars(Token &Result,
123 SourceLocation Loc,
124 const char *TokBegin,
125 unsigned TokLength,
126 StringRef Text) {
127 Result.setLocation(Loc);
128 Result.setKind(tok::text);
129 Result.setLength(TokLength);
130#ifndef NDEBUG
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000131 Result.TextPtr = "<UNSET>";
132 Result.IntVal = 7;
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000133#endif
134 Result.setText(Text);
135 }
136
137public:
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000138 TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
Dmitri Gribenko0c43a922012-07-24 18:23:31 +0000139 Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000140 Pos.CurToken = 0;
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000141 addToken();
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000142 }
143
144 /// Extract a word -- sequence of non-whitespace characters.
145 bool lexWord(Token &Tok) {
146 if (isEnd())
147 return false;
148
149 Position SavedPos = Pos;
150
151 consumeWhitespace();
152 SmallString<32> WordText;
153 const char *WordBegin = Pos.BufferPtr;
154 SourceLocation Loc = getSourceLocation();
155 while (!isEnd()) {
156 const char C = peek();
157 if (!isWhitespace(C)) {
158 WordText.push_back(C);
159 consumeChar();
160 } else
161 break;
162 }
163 const unsigned Length = WordText.size();
164 if (Length == 0) {
165 Pos = SavedPos;
166 return false;
167 }
168
169 char *TextPtr = Allocator.Allocate<char>(Length + 1);
170
171 memcpy(TextPtr, WordText.c_str(), Length + 1);
172 StringRef Text = StringRef(TextPtr, Length);
173
Dmitri Gribenkoca57ccd2012-12-19 17:34:55 +0000174 formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000175 return true;
176 }
177
178 bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
179 if (isEnd())
180 return false;
181
182 Position SavedPos = Pos;
183
184 consumeWhitespace();
185 SmallString<32> WordText;
186 const char *WordBegin = Pos.BufferPtr;
187 SourceLocation Loc = getSourceLocation();
188 bool Error = false;
189 if (!isEnd()) {
190 const char C = peek();
191 if (C == OpenDelim) {
192 WordText.push_back(C);
193 consumeChar();
194 } else
195 Error = true;
196 }
197 char C = '\0';
198 while (!Error && !isEnd()) {
199 C = peek();
200 WordText.push_back(C);
201 consumeChar();
202 if (C == CloseDelim)
203 break;
204 }
205 if (!Error && C != CloseDelim)
206 Error = true;
207
208 if (Error) {
209 Pos = SavedPos;
210 return false;
211 }
212
213 const unsigned Length = WordText.size();
214 char *TextPtr = Allocator.Allocate<char>(Length + 1);
215
216 memcpy(TextPtr, WordText.c_str(), Length + 1);
217 StringRef Text = StringRef(TextPtr, Length);
218
219 formTokenWithChars(Tok, Loc, WordBegin,
220 Pos.BufferPtr - WordBegin, Text);
221 return true;
222 }
223
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000224 /// Put back tokens that we didn't consume.
225 void putBackLeftoverTokens() {
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000226 if (isEnd())
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000227 return;
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000228
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000229 bool HavePartialTok = false;
230 Token PartialTok;
231 if (Pos.BufferPtr != Pos.BufferStart) {
232 formTokenWithChars(PartialTok, getSourceLocation(),
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000233 Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
234 StringRef(Pos.BufferPtr,
235 Pos.BufferEnd - Pos.BufferPtr));
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000236 HavePartialTok = true;
237 Pos.CurToken++;
238 }
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000239
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000240 P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
241 Pos.CurToken = Toks.size();
242
243 if (HavePartialTok)
244 P.putBack(PartialTok);
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000245 }
246};
247
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000248Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000249 const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
250 const CommandTraits &Traits):
251 L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
252 Traits(Traits) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000253 consumeToken();
254}
255
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000256void Parser::parseParamCommandArgs(ParamCommandComment *PC,
257 TextTokenRetokenizer &Retokenizer) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000258 Token Arg;
259 // Check if argument looks like direction specification: [dir]
260 // e.g., [in], [out], [in,out]
261 if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000262 S.actOnParamCommandDirectionArg(PC,
263 Arg.getLocation(),
264 Arg.getEndLocation(),
265 Arg.getText());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000266
267 if (Retokenizer.lexWord(Arg))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000268 S.actOnParamCommandParamNameArg(PC,
269 Arg.getLocation(),
270 Arg.getEndLocation(),
271 Arg.getText());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000272}
273
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000274void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
275 TextTokenRetokenizer &Retokenizer) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000276 Token Arg;
277 if (Retokenizer.lexWord(Arg))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000278 S.actOnTParamCommandParamNameArg(TPC,
279 Arg.getLocation(),
280 Arg.getEndLocation(),
281 Arg.getText());
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000282}
283
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000284void Parser::parseBlockCommandArgs(BlockCommandComment *BC,
285 TextTokenRetokenizer &Retokenizer,
286 unsigned NumArgs) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000287 typedef BlockCommandComment::Argument Argument;
Dmitri Gribenko814e2192012-07-06 16:41:59 +0000288 Argument *Args =
289 new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs];
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000290 unsigned ParsedArgs = 0;
291 Token Arg;
292 while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
293 Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(),
294 Arg.getEndLocation()),
295 Arg.getText());
296 ParsedArgs++;
297 }
298
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000299 S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000300}
301
302BlockCommandComment *Parser::parseBlockCommand() {
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000303 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000304
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000305 ParamCommandComment *PC = 0;
306 TParamCommandComment *TPC = 0;
307 BlockCommandComment *BC = 0;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000308 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000309 CommandMarkerKind CommandMarker =
310 Tok.is(tok::backslash_command) ? CMK_Backslash : CMK_At;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000311 if (Info->IsParamCommand) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000312 PC = S.actOnParamCommandStart(Tok.getLocation(),
313 Tok.getEndLocation(),
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000314 Tok.getCommandID(),
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000315 CommandMarker);
Dmitri Gribenkoeb34db72012-12-19 17:17:09 +0000316 } else if (Info->IsTParamCommand) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000317 TPC = S.actOnTParamCommandStart(Tok.getLocation(),
318 Tok.getEndLocation(),
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000319 Tok.getCommandID(),
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000320 CommandMarker);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000321 } else {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000322 BC = S.actOnBlockCommandStart(Tok.getLocation(),
323 Tok.getEndLocation(),
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000324 Tok.getCommandID(),
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000325 CommandMarker);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000326 }
327 consumeToken();
328
Dmitri Gribenko10442562013-01-26 00:36:14 +0000329 if (isTokBlockCommand()) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000330 // Block command ahead. We can't nest block commands, so pretend that this
331 // command has an empty argument.
Dmitri Gribenkoe5deb792012-07-30 18:05:28 +0000332 ParagraphComment *Paragraph = S.actOnParagraphComment(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000333 ArrayRef<InlineContentComment *>());
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000334 if (PC) {
Dmitri Gribenko8a903932012-08-06 23:48:44 +0000335 S.actOnParamCommandFinish(PC, Paragraph);
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000336 return PC;
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000337 } else if (TPC) {
Dmitri Gribenko8a903932012-08-06 23:48:44 +0000338 S.actOnTParamCommandFinish(TPC, Paragraph);
339 return TPC;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000340 } else {
341 S.actOnBlockCommandFinish(BC, Paragraph);
342 return BC;
343 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000344 }
345
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000346 if (PC || TPC || Info->NumArgs > 0) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000347 // In order to parse command arguments we need to retokenize a few
348 // following text tokens.
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000349 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000350
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000351 if (PC)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000352 parseParamCommandArgs(PC, Retokenizer);
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000353 else if (TPC)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000354 parseTParamCommandArgs(TPC, Retokenizer);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000355 else
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000356 parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000357
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000358 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000359 }
360
Dmitri Gribenko10442562013-01-26 00:36:14 +0000361 // If there's a block command ahead, we will attach an empty paragraph to
362 // this command.
363 bool EmptyParagraph = false;
364 if (isTokBlockCommand())
365 EmptyParagraph = true;
366 else if (Tok.is(tok::newline)) {
367 Token PrevTok = Tok;
368 consumeToken();
369 EmptyParagraph = isTokBlockCommand();
370 putBack(PrevTok);
371 }
372
373 ParagraphComment *Paragraph;
374 if (EmptyParagraph)
375 Paragraph = S.actOnParagraphComment(ArrayRef<InlineContentComment *>());
376 else {
377 BlockContentComment *Block = parseParagraphOrBlockCommand();
378 // Since we have checked for a block command, we should have parsed a
379 // paragraph.
380 Paragraph = cast<ParagraphComment>(Block);
381 }
382
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000383 if (PC) {
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000384 S.actOnParamCommandFinish(PC, Paragraph);
385 return PC;
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000386 } else if (TPC) {
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000387 S.actOnTParamCommandFinish(TPC, Paragraph);
388 return TPC;
389 } else {
390 S.actOnBlockCommandFinish(BC, Paragraph);
391 return BC;
392 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000393}
394
395InlineCommandComment *Parser::parseInlineCommand() {
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000396 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000397
398 const Token CommandTok = Tok;
399 consumeToken();
400
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000401 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000402
403 Token ArgTok;
404 bool ArgTokValid = Retokenizer.lexWord(ArgTok);
405
406 InlineCommandComment *IC;
407 if (ArgTokValid) {
408 IC = S.actOnInlineCommand(CommandTok.getLocation(),
409 CommandTok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000410 CommandTok.getCommandID(),
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000411 ArgTok.getLocation(),
412 ArgTok.getEndLocation(),
413 ArgTok.getText());
414 } else {
415 IC = S.actOnInlineCommand(CommandTok.getLocation(),
416 CommandTok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000417 CommandTok.getCommandID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000418 }
419
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000420 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000421
422 return IC;
423}
424
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000425HTMLStartTagComment *Parser::parseHTMLStartTag() {
426 assert(Tok.is(tok::html_start_tag));
427 HTMLStartTagComment *HST =
428 S.actOnHTMLStartTagStart(Tok.getLocation(),
429 Tok.getHTMLTagStartName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000430 consumeToken();
431
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000432 SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000433 while (true) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000434 switch (Tok.getKind()) {
435 case tok::html_ident: {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000436 Token Ident = Tok;
437 consumeToken();
438 if (Tok.isNot(tok::html_equals)) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000439 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
440 Ident.getHTMLIdent()));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000441 continue;
442 }
443 Token Equals = Tok;
444 consumeToken();
445 if (Tok.isNot(tok::html_quoted_string)) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000446 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000447 diag::warn_doc_html_start_tag_expected_quoted_string)
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000448 << SourceRange(Equals.getLocation());
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000449 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
450 Ident.getHTMLIdent()));
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000451 while (Tok.is(tok::html_equals) ||
452 Tok.is(tok::html_quoted_string))
453 consumeToken();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000454 continue;
455 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000456 Attrs.push_back(HTMLStartTagComment::Attribute(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000457 Ident.getLocation(),
458 Ident.getHTMLIdent(),
459 Equals.getLocation(),
460 SourceRange(Tok.getLocation(),
461 Tok.getEndLocation()),
462 Tok.getHTMLQuotedString()));
463 consumeToken();
464 continue;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000465 }
466
467 case tok::html_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000468 S.actOnHTMLStartTagFinish(HST,
469 S.copyArray(llvm::makeArrayRef(Attrs)),
470 Tok.getLocation(),
471 /* IsSelfClosing = */ false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000472 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000473 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000474
475 case tok::html_slash_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000476 S.actOnHTMLStartTagFinish(HST,
477 S.copyArray(llvm::makeArrayRef(Attrs)),
478 Tok.getLocation(),
479 /* IsSelfClosing = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000480 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000481 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000482
483 case tok::html_equals:
484 case tok::html_quoted_string:
485 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000486 diag::warn_doc_html_start_tag_expected_ident_or_greater);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000487 while (Tok.is(tok::html_equals) ||
488 Tok.is(tok::html_quoted_string))
489 consumeToken();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000490 if (Tok.is(tok::html_ident) ||
491 Tok.is(tok::html_greater) ||
492 Tok.is(tok::html_slash_greater))
493 continue;
494
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000495 S.actOnHTMLStartTagFinish(HST,
496 S.copyArray(llvm::makeArrayRef(Attrs)),
497 SourceLocation(),
498 /* IsSelfClosing = */ false);
499 return HST;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000500
501 default:
502 // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000503 S.actOnHTMLStartTagFinish(HST,
504 S.copyArray(llvm::makeArrayRef(Attrs)),
505 SourceLocation(),
506 /* IsSelfClosing = */ false);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000507 bool StartLineInvalid;
508 const unsigned StartLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000509 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000510 &StartLineInvalid);
511 bool EndLineInvalid;
512 const unsigned EndLine = SourceMgr.getPresumedLineNumber(
513 Tok.getLocation(),
514 &EndLineInvalid);
515 if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
516 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000517 diag::warn_doc_html_start_tag_expected_ident_or_greater)
518 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000519 else {
520 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000521 diag::warn_doc_html_start_tag_expected_ident_or_greater);
522 Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
523 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000524 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000525 return HST;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000526 }
527 }
528}
529
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000530HTMLEndTagComment *Parser::parseHTMLEndTag() {
531 assert(Tok.is(tok::html_end_tag));
532 Token TokEndTag = Tok;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000533 consumeToken();
534 SourceLocation Loc;
535 if (Tok.is(tok::html_greater)) {
536 Loc = Tok.getLocation();
537 consumeToken();
538 }
539
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000540 return S.actOnHTMLEndTag(TokEndTag.getLocation(),
541 Loc,
542 TokEndTag.getHTMLTagEndName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000543}
544
545BlockContentComment *Parser::parseParagraphOrBlockCommand() {
546 SmallVector<InlineContentComment *, 8> Content;
547
548 while (true) {
549 switch (Tok.getKind()) {
550 case tok::verbatim_block_begin:
551 case tok::verbatim_line_name:
552 case tok::eof:
553 assert(Content.size() != 0);
554 break; // Block content or EOF ahead, finish this parapgaph.
555
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000556 case tok::unknown_command:
557 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
558 Tok.getEndLocation(),
559 Tok.getUnknownCommandName()));
560 consumeToken();
561 continue;
562
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000563 case tok::backslash_command:
564 case tok::at_command: {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000565 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
566 if (Info->IsBlockCommand) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000567 if (Content.size() == 0)
568 return parseBlockCommand();
569 break; // Block command ahead, finish this parapgaph.
570 }
Dmitri Gribenko36cbbe92012-11-18 00:30:31 +0000571 if (Info->IsVerbatimBlockEndCommand) {
572 Diag(Tok.getLocation(),
573 diag::warn_verbatim_block_end_without_start)
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000574 << Tok.is(tok::at_command)
Dmitri Gribenko36cbbe92012-11-18 00:30:31 +0000575 << Info->Name
576 << SourceRange(Tok.getLocation(), Tok.getEndLocation());
577 consumeToken();
578 continue;
579 }
Dmitri Gribenkob0b8a962012-09-11 19:22:03 +0000580 if (Info->IsUnknownCommand) {
581 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
582 Tok.getEndLocation(),
583 Info->getID()));
584 consumeToken();
585 continue;
586 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000587 assert(Info->IsInlineCommand);
588 Content.push_back(parseInlineCommand());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000589 continue;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000590 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000591
592 case tok::newline: {
593 consumeToken();
594 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
595 consumeToken();
596 break; // Two newlines -- end of paragraph.
597 }
598 if (Content.size() > 0)
599 Content.back()->addTrailingNewline();
600 continue;
601 }
602
603 // Don't deal with HTML tag soup now.
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000604 case tok::html_start_tag:
605 Content.push_back(parseHTMLStartTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000606 continue;
607
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000608 case tok::html_end_tag:
609 Content.push_back(parseHTMLEndTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000610 continue;
611
612 case tok::text:
613 Content.push_back(S.actOnText(Tok.getLocation(),
614 Tok.getEndLocation(),
615 Tok.getText()));
616 consumeToken();
617 continue;
618
619 case tok::verbatim_block_line:
620 case tok::verbatim_block_end:
621 case tok::verbatim_line_text:
622 case tok::html_ident:
623 case tok::html_equals:
624 case tok::html_quoted_string:
625 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000626 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000627 llvm_unreachable("should not see this token");
628 }
629 break;
630 }
631
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000632 return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000633}
634
635VerbatimBlockComment *Parser::parseVerbatimBlock() {
636 assert(Tok.is(tok::verbatim_block_begin));
637
638 VerbatimBlockComment *VB =
639 S.actOnVerbatimBlockStart(Tok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000640 Tok.getVerbatimBlockID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000641 consumeToken();
642
643 // Don't create an empty line if verbatim opening command is followed
644 // by a newline.
645 if (Tok.is(tok::newline))
646 consumeToken();
647
648 SmallVector<VerbatimBlockLineComment *, 8> Lines;
649 while (Tok.is(tok::verbatim_block_line) ||
650 Tok.is(tok::newline)) {
651 VerbatimBlockLineComment *Line;
652 if (Tok.is(tok::verbatim_block_line)) {
653 Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
654 Tok.getVerbatimBlockText());
655 consumeToken();
656 if (Tok.is(tok::newline)) {
657 consumeToken();
658 }
659 } else {
660 // Empty line, just a tok::newline.
Dmitri Gribenko94572c32012-07-18 21:27:38 +0000661 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000662 consumeToken();
663 }
664 Lines.push_back(Line);
665 }
666
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000667 if (Tok.is(tok::verbatim_block_end)) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000668 const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000669 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000670 Info->Name,
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000671 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000672 consumeToken();
673 } else {
674 // Unterminated \\verbatim block
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000675 S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
676 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000677 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000678
679 return VB;
680}
681
682VerbatimLineComment *Parser::parseVerbatimLine() {
683 assert(Tok.is(tok::verbatim_line_name));
684
685 Token NameTok = Tok;
686 consumeToken();
687
688 SourceLocation TextBegin;
689 StringRef Text;
690 // Next token might not be a tok::verbatim_line_text if verbatim line
691 // starting command comes just before a newline or comment end.
692 if (Tok.is(tok::verbatim_line_text)) {
693 TextBegin = Tok.getLocation();
694 Text = Tok.getVerbatimLineText();
695 } else {
696 TextBegin = NameTok.getEndLocation();
697 Text = "";
698 }
699
700 VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000701 NameTok.getVerbatimLineID(),
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000702 TextBegin,
703 Text);
704 consumeToken();
705 return VL;
706}
707
708BlockContentComment *Parser::parseBlockContent() {
709 switch (Tok.getKind()) {
710 case tok::text:
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000711 case tok::unknown_command:
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000712 case tok::backslash_command:
713 case tok::at_command:
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000714 case tok::html_start_tag:
715 case tok::html_end_tag:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000716 return parseParagraphOrBlockCommand();
717
718 case tok::verbatim_block_begin:
719 return parseVerbatimBlock();
720
721 case tok::verbatim_line_name:
722 return parseVerbatimLine();
723
724 case tok::eof:
725 case tok::newline:
726 case tok::verbatim_block_line:
727 case tok::verbatim_block_end:
728 case tok::verbatim_line_text:
729 case tok::html_ident:
730 case tok::html_equals:
731 case tok::html_quoted_string:
732 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000733 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000734 llvm_unreachable("should not see this token");
735 }
Matt Beaumont-Gay4d48b5c2012-07-06 21:13:09 +0000736 llvm_unreachable("bogus token kind");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000737}
738
739FullComment *Parser::parseFullComment() {
740 // Skip newlines at the beginning of the comment.
741 while (Tok.is(tok::newline))
742 consumeToken();
743
744 SmallVector<BlockContentComment *, 8> Blocks;
745 while (Tok.isNot(tok::eof)) {
746 Blocks.push_back(parseBlockContent());
747
748 // Skip extra newlines after paragraph end.
749 while (Tok.is(tok::newline))
750 consumeToken();
751 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000752 return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000753}
754
755} // end namespace comments
756} // end namespace clang