blob: 03e01015b95440ffb096f2c42bb5900b4c84ceba [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 {
Dmitri Gribenko2b429352013-08-23 18:03:40 +000019
20static inline bool isWhitespace(llvm::StringRef S) {
21 for (StringRef::const_iterator I = S.begin(), E = S.end(); I != E; ++I) {
22 if (!isWhitespace(*I))
23 return false;
24 }
25 return true;
26}
27
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000028namespace comments {
29
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000030/// Re-lexes a sequence of tok::text tokens.
31class TextTokenRetokenizer {
32 llvm::BumpPtrAllocator &Allocator;
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000033 Parser &P;
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000034
35 /// This flag is set when there are no more tokens we can fetch from lexer.
36 bool NoMoreInterestingTokens;
37
38 /// Token buffer: tokens we have processed and lookahead.
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000039 SmallVector<Token, 16> Toks;
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000040
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000041 /// A position in \c Toks.
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000042 struct Position {
43 unsigned CurToken;
44 const char *BufferStart;
45 const char *BufferEnd;
46 const char *BufferPtr;
47 SourceLocation BufferStartLoc;
48 };
49
50 /// Current position in Toks.
51 Position Pos;
52
53 bool isEnd() const {
54 return Pos.CurToken >= Toks.size();
55 }
56
57 /// Sets up the buffer pointers to point to current token.
58 void setupBuffer() {
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000059 assert(!isEnd());
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000060 const Token &Tok = Toks[Pos.CurToken];
61
62 Pos.BufferStart = Tok.getText().begin();
63 Pos.BufferEnd = Tok.getText().end();
64 Pos.BufferPtr = Pos.BufferStart;
65 Pos.BufferStartLoc = Tok.getLocation();
66 }
67
68 SourceLocation getSourceLocation() const {
69 const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
70 return Pos.BufferStartLoc.getLocWithOffset(CharNo);
71 }
72
73 char peek() const {
74 assert(!isEnd());
75 assert(Pos.BufferPtr != Pos.BufferEnd);
76 return *Pos.BufferPtr;
77 }
78
79 void consumeChar() {
80 assert(!isEnd());
81 assert(Pos.BufferPtr != Pos.BufferEnd);
82 Pos.BufferPtr++;
83 if (Pos.BufferPtr == Pos.BufferEnd) {
84 Pos.CurToken++;
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000085 if (isEnd() && !addToken())
86 return;
87
88 assert(!isEnd());
89 setupBuffer();
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +000090 }
91 }
92
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000093 /// Add a token.
94 /// Returns true on success, false if there are no interesting tokens to
95 /// fetch from lexer.
96 bool addToken() {
Dmitri Gribenko0c43a922012-07-24 18:23:31 +000097 if (NoMoreInterestingTokens)
Dmitri Gribenkodb13f042012-07-24 17:52:18 +000098 return false;
99
Dmitri Gribenko0c43a922012-07-24 18:23:31 +0000100 if (P.Tok.is(tok::newline)) {
101 // If we see a single newline token between text tokens, skip it.
102 Token Newline = P.Tok;
103 P.consumeToken();
104 if (P.Tok.isNot(tok::text)) {
105 P.putBack(Newline);
106 NoMoreInterestingTokens = true;
107 return false;
108 }
109 }
110 if (P.Tok.isNot(tok::text)) {
111 NoMoreInterestingTokens = true;
112 return false;
113 }
114
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000115 Toks.push_back(P.Tok);
116 P.consumeToken();
117 if (Toks.size() == 1)
118 setupBuffer();
119 return true;
120 }
121
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000122 void consumeWhitespace() {
123 while (!isEnd()) {
124 if (isWhitespace(peek()))
125 consumeChar();
126 else
127 break;
128 }
129 }
130
131 void formTokenWithChars(Token &Result,
132 SourceLocation Loc,
133 const char *TokBegin,
134 unsigned TokLength,
135 StringRef Text) {
136 Result.setLocation(Loc);
137 Result.setKind(tok::text);
138 Result.setLength(TokLength);
139#ifndef NDEBUG
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000140 Result.TextPtr = "<UNSET>";
141 Result.IntVal = 7;
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000142#endif
143 Result.setText(Text);
144 }
145
146public:
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000147 TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
Dmitri Gribenko0c43a922012-07-24 18:23:31 +0000148 Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000149 Pos.CurToken = 0;
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000150 addToken();
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000151 }
152
153 /// Extract a word -- sequence of non-whitespace characters.
154 bool lexWord(Token &Tok) {
155 if (isEnd())
156 return false;
157
158 Position SavedPos = Pos;
159
160 consumeWhitespace();
161 SmallString<32> WordText;
162 const char *WordBegin = Pos.BufferPtr;
163 SourceLocation Loc = getSourceLocation();
164 while (!isEnd()) {
165 const char C = peek();
166 if (!isWhitespace(C)) {
167 WordText.push_back(C);
168 consumeChar();
169 } else
170 break;
171 }
172 const unsigned Length = WordText.size();
173 if (Length == 0) {
174 Pos = SavedPos;
175 return false;
176 }
177
178 char *TextPtr = Allocator.Allocate<char>(Length + 1);
179
180 memcpy(TextPtr, WordText.c_str(), Length + 1);
181 StringRef Text = StringRef(TextPtr, Length);
182
Dmitri Gribenkoca57ccd2012-12-19 17:34:55 +0000183 formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000184 return true;
185 }
186
187 bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
188 if (isEnd())
189 return false;
190
191 Position SavedPos = Pos;
192
193 consumeWhitespace();
194 SmallString<32> WordText;
195 const char *WordBegin = Pos.BufferPtr;
196 SourceLocation Loc = getSourceLocation();
197 bool Error = false;
198 if (!isEnd()) {
199 const char C = peek();
200 if (C == OpenDelim) {
201 WordText.push_back(C);
202 consumeChar();
203 } else
204 Error = true;
205 }
206 char C = '\0';
207 while (!Error && !isEnd()) {
208 C = peek();
209 WordText.push_back(C);
210 consumeChar();
211 if (C == CloseDelim)
212 break;
213 }
214 if (!Error && C != CloseDelim)
215 Error = true;
216
217 if (Error) {
218 Pos = SavedPos;
219 return false;
220 }
221
222 const unsigned Length = WordText.size();
223 char *TextPtr = Allocator.Allocate<char>(Length + 1);
224
225 memcpy(TextPtr, WordText.c_str(), Length + 1);
226 StringRef Text = StringRef(TextPtr, Length);
227
228 formTokenWithChars(Tok, Loc, WordBegin,
229 Pos.BufferPtr - WordBegin, Text);
230 return true;
231 }
232
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000233 /// Put back tokens that we didn't consume.
234 void putBackLeftoverTokens() {
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000235 if (isEnd())
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000236 return;
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000237
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000238 bool HavePartialTok = false;
239 Token PartialTok;
240 if (Pos.BufferPtr != Pos.BufferStart) {
241 formTokenWithChars(PartialTok, getSourceLocation(),
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000242 Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
243 StringRef(Pos.BufferPtr,
244 Pos.BufferEnd - Pos.BufferPtr));
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000245 HavePartialTok = true;
246 Pos.CurToken++;
247 }
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000248
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000249 P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
250 Pos.CurToken = Toks.size();
251
252 if (HavePartialTok)
253 P.putBack(PartialTok);
Dmitri Gribenkoc4b0f9b2012-07-24 17:43:18 +0000254 }
255};
256
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000257Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000258 const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
259 const CommandTraits &Traits):
260 L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
261 Traits(Traits) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000262 consumeToken();
263}
264
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000265void Parser::parseParamCommandArgs(ParamCommandComment *PC,
266 TextTokenRetokenizer &Retokenizer) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000267 Token Arg;
268 // Check if argument looks like direction specification: [dir]
269 // e.g., [in], [out], [in,out]
270 if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000271 S.actOnParamCommandDirectionArg(PC,
272 Arg.getLocation(),
273 Arg.getEndLocation(),
274 Arg.getText());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000275
276 if (Retokenizer.lexWord(Arg))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000277 S.actOnParamCommandParamNameArg(PC,
278 Arg.getLocation(),
279 Arg.getEndLocation(),
280 Arg.getText());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000281}
282
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000283void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
284 TextTokenRetokenizer &Retokenizer) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000285 Token Arg;
286 if (Retokenizer.lexWord(Arg))
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000287 S.actOnTParamCommandParamNameArg(TPC,
288 Arg.getLocation(),
289 Arg.getEndLocation(),
290 Arg.getText());
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000291}
292
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000293void Parser::parseBlockCommandArgs(BlockCommandComment *BC,
294 TextTokenRetokenizer &Retokenizer,
295 unsigned NumArgs) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000296 typedef BlockCommandComment::Argument Argument;
Dmitri Gribenko814e2192012-07-06 16:41:59 +0000297 Argument *Args =
298 new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs];
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000299 unsigned ParsedArgs = 0;
300 Token Arg;
301 while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
302 Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(),
303 Arg.getEndLocation()),
304 Arg.getText());
305 ParsedArgs++;
306 }
307
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000308 S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000309}
310
311BlockCommandComment *Parser::parseBlockCommand() {
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000312 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000313
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000314 ParamCommandComment *PC = 0;
315 TParamCommandComment *TPC = 0;
316 BlockCommandComment *BC = 0;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000317 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000318 CommandMarkerKind CommandMarker =
319 Tok.is(tok::backslash_command) ? CMK_Backslash : CMK_At;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000320 if (Info->IsParamCommand) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000321 PC = S.actOnParamCommandStart(Tok.getLocation(),
322 Tok.getEndLocation(),
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000323 Tok.getCommandID(),
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000324 CommandMarker);
Dmitri Gribenkoeb34db72012-12-19 17:17:09 +0000325 } else if (Info->IsTParamCommand) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000326 TPC = S.actOnTParamCommandStart(Tok.getLocation(),
327 Tok.getEndLocation(),
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000328 Tok.getCommandID(),
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000329 CommandMarker);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000330 } else {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000331 BC = S.actOnBlockCommandStart(Tok.getLocation(),
332 Tok.getEndLocation(),
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000333 Tok.getCommandID(),
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000334 CommandMarker);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000335 }
336 consumeToken();
337
Dmitri Gribenko10442562013-01-26 00:36:14 +0000338 if (isTokBlockCommand()) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000339 // Block command ahead. We can't nest block commands, so pretend that this
340 // command has an empty argument.
Dmitri Gribenko55431692013-05-05 00:41:58 +0000341 ParagraphComment *Paragraph = S.actOnParagraphComment(None);
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000342 if (PC) {
Dmitri Gribenko8a903932012-08-06 23:48:44 +0000343 S.actOnParamCommandFinish(PC, Paragraph);
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000344 return PC;
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000345 } else if (TPC) {
Dmitri Gribenko8a903932012-08-06 23:48:44 +0000346 S.actOnTParamCommandFinish(TPC, Paragraph);
347 return TPC;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000348 } else {
349 S.actOnBlockCommandFinish(BC, Paragraph);
350 return BC;
351 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000352 }
353
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000354 if (PC || TPC || Info->NumArgs > 0) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000355 // In order to parse command arguments we need to retokenize a few
356 // following text tokens.
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000357 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000358
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000359 if (PC)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000360 parseParamCommandArgs(PC, Retokenizer);
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000361 else if (TPC)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000362 parseTParamCommandArgs(TPC, Retokenizer);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000363 else
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000364 parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000365
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000366 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000367 }
368
Dmitri Gribenko10442562013-01-26 00:36:14 +0000369 // If there's a block command ahead, we will attach an empty paragraph to
370 // this command.
371 bool EmptyParagraph = false;
372 if (isTokBlockCommand())
373 EmptyParagraph = true;
374 else if (Tok.is(tok::newline)) {
375 Token PrevTok = Tok;
376 consumeToken();
377 EmptyParagraph = isTokBlockCommand();
378 putBack(PrevTok);
379 }
380
381 ParagraphComment *Paragraph;
382 if (EmptyParagraph)
Dmitri Gribenko55431692013-05-05 00:41:58 +0000383 Paragraph = S.actOnParagraphComment(None);
Dmitri Gribenko10442562013-01-26 00:36:14 +0000384 else {
385 BlockContentComment *Block = parseParagraphOrBlockCommand();
386 // Since we have checked for a block command, we should have parsed a
387 // paragraph.
388 Paragraph = cast<ParagraphComment>(Block);
389 }
390
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000391 if (PC) {
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000392 S.actOnParamCommandFinish(PC, Paragraph);
393 return PC;
Dmitri Gribenko3a589122013-04-18 20:50:35 +0000394 } else if (TPC) {
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000395 S.actOnTParamCommandFinish(TPC, Paragraph);
396 return TPC;
397 } else {
398 S.actOnBlockCommandFinish(BC, Paragraph);
399 return BC;
400 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000401}
402
403InlineCommandComment *Parser::parseInlineCommand() {
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000404 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000405
406 const Token CommandTok = Tok;
407 consumeToken();
408
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000409 TextTokenRetokenizer Retokenizer(Allocator, *this);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000410
411 Token ArgTok;
412 bool ArgTokValid = Retokenizer.lexWord(ArgTok);
413
414 InlineCommandComment *IC;
415 if (ArgTokValid) {
416 IC = S.actOnInlineCommand(CommandTok.getLocation(),
417 CommandTok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000418 CommandTok.getCommandID(),
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000419 ArgTok.getLocation(),
420 ArgTok.getEndLocation(),
421 ArgTok.getText());
422 } else {
423 IC = S.actOnInlineCommand(CommandTok.getLocation(),
424 CommandTok.getEndLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000425 CommandTok.getCommandID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000426 }
427
Dmitri Gribenkodb13f042012-07-24 17:52:18 +0000428 Retokenizer.putBackLeftoverTokens();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000429
430 return IC;
431}
432
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000433HTMLStartTagComment *Parser::parseHTMLStartTag() {
434 assert(Tok.is(tok::html_start_tag));
435 HTMLStartTagComment *HST =
436 S.actOnHTMLStartTagStart(Tok.getLocation(),
437 Tok.getHTMLTagStartName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000438 consumeToken();
439
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000440 SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000441 while (true) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000442 switch (Tok.getKind()) {
443 case tok::html_ident: {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000444 Token Ident = Tok;
445 consumeToken();
446 if (Tok.isNot(tok::html_equals)) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000447 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
448 Ident.getHTMLIdent()));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000449 continue;
450 }
451 Token Equals = Tok;
452 consumeToken();
453 if (Tok.isNot(tok::html_quoted_string)) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000454 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000455 diag::warn_doc_html_start_tag_expected_quoted_string)
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000456 << SourceRange(Equals.getLocation());
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000457 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
458 Ident.getHTMLIdent()));
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000459 while (Tok.is(tok::html_equals) ||
460 Tok.is(tok::html_quoted_string))
461 consumeToken();
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000462 continue;
463 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000464 Attrs.push_back(HTMLStartTagComment::Attribute(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000465 Ident.getLocation(),
466 Ident.getHTMLIdent(),
467 Equals.getLocation(),
468 SourceRange(Tok.getLocation(),
469 Tok.getEndLocation()),
470 Tok.getHTMLQuotedString()));
471 consumeToken();
472 continue;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000473 }
474
475 case tok::html_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000476 S.actOnHTMLStartTagFinish(HST,
477 S.copyArray(llvm::makeArrayRef(Attrs)),
478 Tok.getLocation(),
479 /* IsSelfClosing = */ false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000480 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000481 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000482
483 case tok::html_slash_greater:
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000484 S.actOnHTMLStartTagFinish(HST,
485 S.copyArray(llvm::makeArrayRef(Attrs)),
486 Tok.getLocation(),
487 /* IsSelfClosing = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000488 consumeToken();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000489 return HST;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000490
491 case tok::html_equals:
492 case tok::html_quoted_string:
493 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000494 diag::warn_doc_html_start_tag_expected_ident_or_greater);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000495 while (Tok.is(tok::html_equals) ||
496 Tok.is(tok::html_quoted_string))
497 consumeToken();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000498 if (Tok.is(tok::html_ident) ||
499 Tok.is(tok::html_greater) ||
500 Tok.is(tok::html_slash_greater))
501 continue;
502
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000503 S.actOnHTMLStartTagFinish(HST,
504 S.copyArray(llvm::makeArrayRef(Attrs)),
505 SourceLocation(),
506 /* IsSelfClosing = */ false);
507 return HST;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000508
509 default:
510 // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000511 S.actOnHTMLStartTagFinish(HST,
512 S.copyArray(llvm::makeArrayRef(Attrs)),
513 SourceLocation(),
514 /* IsSelfClosing = */ false);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000515 bool StartLineInvalid;
516 const unsigned StartLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000517 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000518 &StartLineInvalid);
519 bool EndLineInvalid;
520 const unsigned EndLine = SourceMgr.getPresumedLineNumber(
521 Tok.getLocation(),
522 &EndLineInvalid);
523 if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
524 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000525 diag::warn_doc_html_start_tag_expected_ident_or_greater)
526 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000527 else {
528 Diag(Tok.getLocation(),
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000529 diag::warn_doc_html_start_tag_expected_ident_or_greater);
530 Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
531 << HST->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000532 }
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000533 return HST;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000534 }
535 }
536}
537
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000538HTMLEndTagComment *Parser::parseHTMLEndTag() {
539 assert(Tok.is(tok::html_end_tag));
540 Token TokEndTag = Tok;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000541 consumeToken();
542 SourceLocation Loc;
543 if (Tok.is(tok::html_greater)) {
544 Loc = Tok.getLocation();
545 consumeToken();
546 }
547
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000548 return S.actOnHTMLEndTag(TokEndTag.getLocation(),
549 Loc,
550 TokEndTag.getHTMLTagEndName());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000551}
552
553BlockContentComment *Parser::parseParagraphOrBlockCommand() {
554 SmallVector<InlineContentComment *, 8> Content;
555
556 while (true) {
557 switch (Tok.getKind()) {
558 case tok::verbatim_block_begin:
559 case tok::verbatim_line_name:
560 case tok::eof:
561 assert(Content.size() != 0);
562 break; // Block content or EOF ahead, finish this parapgaph.
563
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000564 case tok::unknown_command:
565 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
566 Tok.getEndLocation(),
567 Tok.getUnknownCommandName()));
568 consumeToken();
569 continue;
570
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000571 case tok::backslash_command:
572 case tok::at_command: {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000573 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
574 if (Info->IsBlockCommand) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000575 if (Content.size() == 0)
576 return parseBlockCommand();
577 break; // Block command ahead, finish this parapgaph.
578 }
Dmitri Gribenko36cbbe92012-11-18 00:30:31 +0000579 if (Info->IsVerbatimBlockEndCommand) {
580 Diag(Tok.getLocation(),
581 diag::warn_verbatim_block_end_without_start)
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000582 << Tok.is(tok::at_command)
Dmitri Gribenko36cbbe92012-11-18 00:30:31 +0000583 << Info->Name
584 << SourceRange(Tok.getLocation(), Tok.getEndLocation());
585 consumeToken();
586 continue;
587 }
Dmitri Gribenkob0b8a962012-09-11 19:22:03 +0000588 if (Info->IsUnknownCommand) {
589 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
590 Tok.getEndLocation(),
591 Info->getID()));
592 consumeToken();
593 continue;
594 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000595 assert(Info->IsInlineCommand);
596 Content.push_back(parseInlineCommand());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000597 continue;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000598 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000599
600 case tok::newline: {
601 consumeToken();
602 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
603 consumeToken();
604 break; // Two newlines -- end of paragraph.
605 }
Dmitri Gribenko2b429352013-08-23 18:03:40 +0000606 // Also allow [tok::newline, tok::text, tok::newline] if the middle
607 // tok::text is just whitespace.
608 if (Tok.is(tok::text) && isWhitespace(Tok.getText())) {
609 Token WhitespaceTok = Tok;
610 consumeToken();
611 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
612 consumeToken();
613 break;
614 }
615 // We have [tok::newline, tok::text, non-newline]. Put back tok::text.
616 putBack(WhitespaceTok);
617 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000618 if (Content.size() > 0)
619 Content.back()->addTrailingNewline();
620 continue;
621 }
622
623 // Don't deal with HTML tag soup now.
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000624 case tok::html_start_tag:
625 Content.push_back(parseHTMLStartTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000626 continue;
627
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000628 case tok::html_end_tag:
629 Content.push_back(parseHTMLEndTag());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000630 continue;
631
632 case tok::text:
633 Content.push_back(S.actOnText(Tok.getLocation(),
634 Tok.getEndLocation(),
635 Tok.getText()));
636 consumeToken();
637 continue;
638
639 case tok::verbatim_block_line:
640 case tok::verbatim_block_end:
641 case tok::verbatim_line_text:
642 case tok::html_ident:
643 case tok::html_equals:
644 case tok::html_quoted_string:
645 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000646 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000647 llvm_unreachable("should not see this token");
648 }
649 break;
650 }
651
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000652 return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000653}
654
655VerbatimBlockComment *Parser::parseVerbatimBlock() {
656 assert(Tok.is(tok::verbatim_block_begin));
657
658 VerbatimBlockComment *VB =
659 S.actOnVerbatimBlockStart(Tok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000660 Tok.getVerbatimBlockID());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000661 consumeToken();
662
663 // Don't create an empty line if verbatim opening command is followed
664 // by a newline.
665 if (Tok.is(tok::newline))
666 consumeToken();
667
668 SmallVector<VerbatimBlockLineComment *, 8> Lines;
669 while (Tok.is(tok::verbatim_block_line) ||
670 Tok.is(tok::newline)) {
671 VerbatimBlockLineComment *Line;
672 if (Tok.is(tok::verbatim_block_line)) {
673 Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
674 Tok.getVerbatimBlockText());
675 consumeToken();
676 if (Tok.is(tok::newline)) {
677 consumeToken();
678 }
679 } else {
680 // Empty line, just a tok::newline.
Dmitri Gribenko94572c32012-07-18 21:27:38 +0000681 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000682 consumeToken();
683 }
684 Lines.push_back(Line);
685 }
686
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000687 if (Tok.is(tok::verbatim_block_end)) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000688 const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000689 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000690 Info->Name,
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000691 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000692 consumeToken();
693 } else {
694 // Unterminated \\verbatim block
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000695 S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
696 S.copyArray(llvm::makeArrayRef(Lines)));
Dmitri Gribenko9f08f492012-07-20 20:18:53 +0000697 }
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000698
699 return VB;
700}
701
702VerbatimLineComment *Parser::parseVerbatimLine() {
703 assert(Tok.is(tok::verbatim_line_name));
704
705 Token NameTok = Tok;
706 consumeToken();
707
708 SourceLocation TextBegin;
709 StringRef Text;
710 // Next token might not be a tok::verbatim_line_text if verbatim line
711 // starting command comes just before a newline or comment end.
712 if (Tok.is(tok::verbatim_line_text)) {
713 TextBegin = Tok.getLocation();
714 Text = Tok.getVerbatimLineText();
715 } else {
716 TextBegin = NameTok.getEndLocation();
717 Text = "";
718 }
719
720 VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000721 NameTok.getVerbatimLineID(),
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000722 TextBegin,
723 Text);
724 consumeToken();
725 return VL;
726}
727
728BlockContentComment *Parser::parseBlockContent() {
729 switch (Tok.getKind()) {
730 case tok::text:
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000731 case tok::unknown_command:
Fariborz Jahanian8536fa12013-03-02 02:39:57 +0000732 case tok::backslash_command:
733 case tok::at_command:
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000734 case tok::html_start_tag:
735 case tok::html_end_tag:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000736 return parseParagraphOrBlockCommand();
737
738 case tok::verbatim_block_begin:
739 return parseVerbatimBlock();
740
741 case tok::verbatim_line_name:
742 return parseVerbatimLine();
743
744 case tok::eof:
745 case tok::newline:
746 case tok::verbatim_block_line:
747 case tok::verbatim_block_end:
748 case tok::verbatim_line_text:
749 case tok::html_ident:
750 case tok::html_equals:
751 case tok::html_quoted_string:
752 case tok::html_greater:
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000753 case tok::html_slash_greater:
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000754 llvm_unreachable("should not see this token");
755 }
Matt Beaumont-Gay4d48b5c2012-07-06 21:13:09 +0000756 llvm_unreachable("bogus token kind");
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000757}
758
759FullComment *Parser::parseFullComment() {
760 // Skip newlines at the beginning of the comment.
761 while (Tok.is(tok::newline))
762 consumeToken();
763
764 SmallVector<BlockContentComment *, 8> Blocks;
765 while (Tok.isNot(tok::eof)) {
766 Blocks.push_back(parseBlockContent());
767
768 // Skip extra newlines after paragraph end.
769 while (Tok.is(tok::newline))
770 consumeToken();
771 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000772 return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000773}
774
775} // end namespace comments
776} // end namespace clang