blob: 5e4888f699058448af3116596ceeb29531ac093d [file] [log] [blame]
Argyrios Kyrtzidis5404a152008-10-05 00:06:24 +00001//===--- ParseTentative.cpp - Ambiguity Resolution Parsing ----------------===//
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// This file implements the tentative parsing portions of the Parser
11// interfaces, for ambiguity resolution.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/Parse/Parser.h"
16#include "clang/Basic/Diagnostic.h"
17using namespace clang;
18
19/// isCXXDeclarationStatement - C++-specialized function that disambiguates
20/// between a declaration or an expression statement, when parsing function
21/// bodies. Returns true for declaration, false for expression.
22///
23/// declaration-statement:
24/// block-declaration
25///
26/// block-declaration:
27/// simple-declaration
28/// asm-definition
29/// namespace-alias-definition
30/// using-declaration
31/// using-directive
32/// [C++0x] static_assert-declaration [TODO]
33///
34/// asm-definition:
35/// 'asm' '(' string-literal ')' ';'
36///
37/// namespace-alias-definition:
38/// 'namespace' identifier = qualified-namespace-specifier ';'
39///
40/// using-declaration:
41/// 'using' typename[opt] '::'[opt] nested-name-specifier
42/// unqualified-id ';'
43/// 'using' '::' unqualified-id ;
44///
45/// using-directive:
46/// 'using' 'namespace' '::'[opt] nested-name-specifier[opt]
47/// namespace-name ';'
48///
49/// [C++0x] static_assert-declaration: [TODO]
50/// [C++0x] static_assert '(' constant-expression ',' string-literal ')' ';'
51///
52bool Parser::isCXXDeclarationStatement() {
53 switch (Tok.getKind()) {
54 // asm-definition
55 case tok::kw_asm:
56 // namespace-alias-definition
57 case tok::kw_namespace:
58 // using-declaration
59 // using-directive
60 case tok::kw_using:
61 return true;
62 default:
63 // simple-declaration
64 return isCXXSimpleDeclaration();
65 }
66}
67
68/// isCXXSimpleDeclaration - C++-specialized function that disambiguates
69/// between a simple-declaration or an expression-statement.
70/// If during the disambiguation process a parsing error is encountered,
71/// the function returns true to let the declaration parsing code handle it.
72/// Returns false if the statement is disambiguated as expression.
73///
74/// simple-declaration:
75/// decl-specifier-seq init-declarator-list[opt] ';'
76///
77bool Parser::isCXXSimpleDeclaration() {
78 // C++ 6.8p1:
79 // There is an ambiguity in the grammar involving expression-statements and
80 // declarations: An expression-statement with a function-style explicit type
81 // conversion (5.2.3) as its leftmost subexpression can be indistinguishable
82 // from a declaration where the first declarator starts with a '('. In those
83 // cases the statement is a declaration. [Note: To disambiguate, the whole
84 // statement might have to be examined to determine if it is an
85 // expression-statement or a declaration].
86
87 // C++ 6.8p3:
88 // The disambiguation is purely syntactic; that is, the meaning of the names
89 // occurring in such a statement, beyond whether they are type-names or not,
90 // is not generally used in or changed by the disambiguation. Class
91 // templates are instantiated as necessary to determine if a qualified name
92 // is a type-name. Disambiguation precedes parsing, and a statement
93 // disambiguated as a declaration may be an ill-formed declaration.
94
95 // We don't have to parse all of the decl-specifier-seq part. There's only
96 // an ambiguity if the first decl-specifier is
97 // simple-type-specifier/typename-specifier followed by a '(', which may
98 // indicate a function-style cast expression.
99 // isCXXDeclarationSpecifier will return TPR_ambiguous only in such a case.
100
101 TentativeParsingResult TPR = isCXXDeclarationSpecifier();
102 if (TPR != TPR_ambiguous)
103 return TPR != TPR_false; // Returns true for TPR_true or TPR_error.
104
105 // FIXME: Add statistics about the number of ambiguous statements encountered
106 // and how they were resolved (number of declarations+number of expressions).
107
108 // Ok, we have a simple-type-specifier/typename-specifier followed by a '('.
109 // We need tentative parsing...
110
111 TentativeParsingAction PA(*this);
112
113 TPR = TryParseSimpleDeclaration();
114 SourceLocation TentativeParseLoc = Tok.getLocation();
115
116 PA.Revert();
117
118 // In case of an error, let the declaration parsing code handle it.
119 if (TPR == TPR_error)
120 return true;
121
122 // Declarations take precedence over expressions.
123 if (TPR == TPR_ambiguous)
124 TPR = TPR_true;
125
126 assert(TPR == TPR_true || TPR == TPR_false);
127 if (TPR == TPR_true && Tok.isNot(tok::kw_void)) {
128 // We have a declaration that looks like a functional cast; there's a high
129 // chance that the author intended the statement to be an expression.
130 // Emit a warning.
131 Diag(Tok.getLocation(), diag::warn_statement_disambiguation,
132 "declaration", SourceRange(Tok.getLocation(), TentativeParseLoc));
133 } else if (TPR == TPR_false && Tok.is(tok::kw_void)) {
134 // A functional cast to 'void' expression ? Warning..
135 Diag(Tok.getLocation(), diag::warn_statement_disambiguation,
136 "expression", SourceRange(Tok.getLocation(), TentativeParseLoc));
137 }
138
139 return TPR == TPR_true;
140}
141
142/// simple-declaration:
143/// decl-specifier-seq init-declarator-list[opt] ';'
144///
145Parser::TentativeParsingResult Parser::TryParseSimpleDeclaration() {
146 // We know that we have a simple-type-specifier/typename-specifier followed
147 // by a '('.
148 assert(isCXXDeclarationSpecifier() == TPR_ambiguous);
149
150 if (Tok.is(tok::kw_typeof))
151 TryParseTypeofSpecifier();
152 else
153 ConsumeToken();
154
155 assert(Tok.is(tok::l_paren) && "Expected '('");
156
157 TentativeParsingResult TPR = TryParseInitDeclaratorList();
158 if (TPR != TPR_ambiguous)
159 return TPR;
160
161 if (Tok.isNot(tok::semi))
162 return TPR_false;
163
164 return TPR_ambiguous;
165}
166
Argyrios Kyrtzidis1ee2c432008-10-05 14:27:18 +0000167/// init-declarator-list:
168/// init-declarator
169/// init-declarator-list ',' init-declarator
Argyrios Kyrtzidis5404a152008-10-05 00:06:24 +0000170///
Argyrios Kyrtzidis1ee2c432008-10-05 14:27:18 +0000171/// init-declarator:
172/// declarator initializer[opt]
173/// [GNU] declarator simple-asm-expr[opt] attributes[opt] initializer[opt]
Argyrios Kyrtzidis5404a152008-10-05 00:06:24 +0000174///
175/// initializer:
176/// '=' initializer-clause
177/// '(' expression-list ')'
178///
179/// initializer-clause:
180/// assignment-expression
181/// '{' initializer-list ','[opt] '}'
182/// '{' '}'
183///
184Parser::TentativeParsingResult Parser::TryParseInitDeclaratorList() {
185 // GCC only examines the first declarator for disambiguation:
186 // i.e:
187 // int(x), ++x; // GCC regards it as ill-formed declaration.
188 //
189 // Comeau and MSVC will regard the above statement as correct expression.
190 // Clang examines all of the declarators and also regards the above statement
191 // as correct expression.
192
193 while (1) {
194 // declarator
195 TentativeParsingResult TPR = TryParseDeclarator(false/*mayBeAbstract*/);
196 if (TPR != TPR_ambiguous)
197 return TPR;
198
Argyrios Kyrtzidis1ee2c432008-10-05 14:27:18 +0000199 // [GNU] simple-asm-expr[opt] attributes[opt]
200 if (Tok.is(tok::kw_asm) || Tok.is(tok::kw___attribute))
201 return TPR_true;
202
Argyrios Kyrtzidis5404a152008-10-05 00:06:24 +0000203 // initializer[opt]
204 if (Tok.is(tok::l_paren)) {
205 // Parse through the parens.
206 ConsumeParen();
207 if (!SkipUntil(tok::r_paren))
208 return TPR_error;
209 } else if (Tok.is(tok::equal)) {
210 // MSVC won't examine the rest of declarators if '=' is encountered, it
211 // will conclude that it is a declaration.
212 // Comeau and Clang will examine the rest of declarators.
213 // Note that "int(x) = {0}, ++x;" will be interpreted as ill-formed
214 // expression.
215 //
216 // Parse through the initializer-clause.
217 SkipUntil(tok::comma, true/*StopAtSemi*/, true/*DontConsume*/);
218 }
219
220 if (Tok.isNot(tok::comma))
221 break;
222 ConsumeToken(); // the comma.
223 }
224
225 return TPR_ambiguous;
226}
227
228/// declarator:
229/// direct-declarator
230/// ptr-operator declarator
231///
232/// direct-declarator:
233/// declarator-id
234/// direct-declarator '(' parameter-declaration-clause ')'
235/// cv-qualifier-seq[opt] exception-specification[opt]
236/// direct-declarator '[' constant-expression[opt] ']'
237/// '(' declarator ')'
Argyrios Kyrtzidis1ee2c432008-10-05 14:27:18 +0000238/// [GNU] '(' attributes declarator ')'
Argyrios Kyrtzidis5404a152008-10-05 00:06:24 +0000239///
240/// abstract-declarator:
241/// ptr-operator abstract-declarator[opt]
242/// direct-abstract-declarator
243///
244/// direct-abstract-declarator:
245/// direct-abstract-declarator[opt]
246/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
247/// exception-specification[opt]
248/// direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
249/// '(' abstract-declarator ')'
250///
251/// ptr-operator:
252/// '*' cv-qualifier-seq[opt]
253/// '&'
254/// [C++0x] '&&' [TODO]
255/// '::'[opt] nested-name-specifier '*' cv-qualifier-seq[opt] [TODO]
256///
257/// cv-qualifier-seq:
258/// cv-qualifier cv-qualifier-seq[opt]
259///
260/// cv-qualifier:
261/// 'const'
262/// 'volatile'
263///
264/// declarator-id:
265/// id-expression
266///
267/// id-expression:
268/// unqualified-id
269/// qualified-id [TODO]
270///
271/// unqualified-id:
272/// identifier
273/// operator-function-id [TODO]
274/// conversion-function-id [TODO]
275/// '~' class-name [TODO]
276/// template-id [TODO]
277///
278Parser::TentativeParsingResult Parser::TryParseDeclarator(bool mayBeAbstract) {
279 // declarator:
280 // direct-declarator
281 // ptr-operator declarator
282
283 while (1) {
284 if (Tok.is(tok::star) || Tok.is(tok::amp)) {
285 // ptr-operator
286 ConsumeToken();
287 while (Tok.is(tok::kw_const) ||
288 Tok.is(tok::kw_volatile) ||
289 Tok.is(tok::kw_restrict) )
290 ConsumeToken();
291 } else {
292 break;
293 }
294 }
295
296 // direct-declarator:
297 // direct-abstract-declarator:
298
299 if (Tok.is(tok::identifier)) {
300 // declarator-id
301 ConsumeToken();
302 } else if (Tok.is(tok::l_paren)) {
303 if (mayBeAbstract && isCXXFunctionDeclarator()) {
304 // '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
305 // exception-specification[opt]
306 TentativeParsingResult TPR = TryParseFunctionDeclarator();
307 if (TPR != TPR_ambiguous)
308 return TPR;
309 } else {
310 // '(' declarator ')'
Argyrios Kyrtzidis1ee2c432008-10-05 14:27:18 +0000311 // '(' attributes declarator ')'
Argyrios Kyrtzidis5404a152008-10-05 00:06:24 +0000312 // '(' abstract-declarator ')'
Argyrios Kyrtzidis1ee2c432008-10-05 14:27:18 +0000313 ConsumeParen();
314 if (Tok.is(tok::kw___attribute))
315 return TPR_true; // attributes indicate declaration
Argyrios Kyrtzidis5404a152008-10-05 00:06:24 +0000316 TentativeParsingResult TPR = TryParseDeclarator(mayBeAbstract);
317 if (TPR != TPR_ambiguous)
318 return TPR;
319 if (Tok.isNot(tok::r_paren))
320 return TPR_false;
321 ConsumeParen();
322 }
323 } else if (!mayBeAbstract) {
324 return TPR_false;
325 }
326
327 while (1) {
328 TentativeParsingResult TPR;
329
330 if (Tok.is(tok::l_paren)) {
331 // direct-declarator '(' parameter-declaration-clause ')'
332 // cv-qualifier-seq[opt] exception-specification[opt]
333 if (!isCXXFunctionDeclarator())
334 break;
335 TPR = TryParseFunctionDeclarator();
336 } else if (Tok.is(tok::l_square)) {
337 // direct-declarator '[' constant-expression[opt] ']'
338 // direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
339 TPR = TryParseBracketDeclarator();
340 } else {
341 break;
342 }
343
344 if (TPR != TPR_ambiguous)
345 return TPR;
346 }
347
348 return TPR_ambiguous;
349}
350
351/// isCXXDeclarationSpecifier - Returns TPR_true if it is a declaration
352/// specifier, TPR_false if it is not, TPR_ambiguous if it could be either
353/// a decl-specifier or a function-style cast, and TPR_error if a parsing
354/// error was found and reported.
355///
356/// decl-specifier:
357/// storage-class-specifier
358/// type-specifier
359/// function-specifier
360/// 'friend'
361/// 'typedef'
362/// [GNU] attributes declaration-specifiers[opt]
363///
364/// storage-class-specifier:
365/// 'register'
366/// 'static'
367/// 'extern'
368/// 'mutable'
369/// 'auto'
370/// [GNU] '__thread'
371///
372/// function-specifier:
373/// 'inline'
374/// 'virtual'
375/// 'explicit'
376///
377/// typedef-name:
378/// identifier
379///
380/// type-specifier:
381/// simple-type-specifier
382/// class-specifier
383/// enum-specifier
384/// elaborated-type-specifier
385/// typename-specifier [TODO]
386/// cv-qualifier
387///
388/// simple-type-specifier:
389/// '::'[opt] nested-name-specifier[opt] type-name [TODO]
390/// '::'[opt] nested-name-specifier 'template'
391/// simple-template-id [TODO]
392/// 'char'
393/// 'wchar_t'
394/// 'bool'
395/// 'short'
396/// 'int'
397/// 'long'
398/// 'signed'
399/// 'unsigned'
400/// 'float'
401/// 'double'
402/// 'void'
403/// [GNU] typeof-specifier
404/// [GNU] '_Complex'
405/// [C++0x] 'auto' [TODO]
406///
407/// type-name:
408/// class-name
409/// enum-name
410/// typedef-name
411///
412/// elaborated-type-specifier:
413/// class-key '::'[opt] nested-name-specifier[opt] identifier
414/// class-key '::'[opt] nested-name-specifier[opt] 'template'[opt]
415/// simple-template-id
416/// 'enum' '::'[opt] nested-name-specifier[opt] identifier
417///
418/// enum-name:
419/// identifier
420///
421/// enum-specifier:
422/// 'enum' identifier[opt] '{' enumerator-list[opt] '}'
423/// 'enum' identifier[opt] '{' enumerator-list ',' '}'
424///
425/// class-specifier:
426/// class-head '{' member-specification[opt] '}'
427///
428/// class-head:
429/// class-key identifier[opt] base-clause[opt]
430/// class-key nested-name-specifier identifier base-clause[opt]
431/// class-key nested-name-specifier[opt] simple-template-id
432/// base-clause[opt]
433///
434/// class-key:
435/// 'class'
436/// 'struct'
437/// 'union'
438///
439/// cv-qualifier:
440/// 'const'
441/// 'volatile'
442/// [GNU] restrict
443///
444Parser::TentativeParsingResult Parser::isCXXDeclarationSpecifier() {
445 switch (Tok.getKind()) {
446 // decl-specifier:
447 // storage-class-specifier
448 // type-specifier
449 // function-specifier
450 // 'friend'
451 // 'typedef'
452
453 case tok::kw_friend:
454 case tok::kw_typedef:
455 // storage-class-specifier
456 case tok::kw_register:
457 case tok::kw_static:
458 case tok::kw_extern:
459 case tok::kw_mutable:
460 case tok::kw_auto:
461 case tok::kw___thread:
462 // function-specifier
463 case tok::kw_inline:
464 case tok::kw_virtual:
465 case tok::kw_explicit:
466
467 // type-specifier:
468 // simple-type-specifier
469 // class-specifier
470 // enum-specifier
471 // elaborated-type-specifier
472 // typename-specifier
473 // cv-qualifier
474
475 // class-specifier
476 // elaborated-type-specifier
477 case tok::kw_class:
478 case tok::kw_struct:
479 case tok::kw_union:
480 // enum-specifier
481 case tok::kw_enum:
482 // cv-qualifier
483 case tok::kw_const:
484 case tok::kw_volatile:
485
486 // GNU
487 case tok::kw_restrict:
488 case tok::kw__Complex:
489 case tok::kw___attribute:
490 return TPR_true;
491
492 // The ambiguity resides in a simple-type-specifier/typename-specifier
493 // followed by a '('. The '(' could either be the start of:
494 //
495 // direct-declarator:
496 // '(' declarator ')'
497 //
498 // direct-abstract-declarator:
499 // '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
500 // exception-specification[opt]
501 // '(' abstract-declarator ')'
502 //
503 // or part of a function-style cast expression:
504 //
505 // simple-type-specifier '(' expression-list[opt] ')'
506 //
507
508 // simple-type-specifier:
509
510 case tok::identifier:
511 if (!Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope))
512 return TPR_false;
513 // FALL THROUGH.
514
515 case tok::kw_char:
516 case tok::kw_wchar_t:
517 case tok::kw_bool:
518 case tok::kw_short:
519 case tok::kw_int:
520 case tok::kw_long:
521 case tok::kw_signed:
522 case tok::kw_unsigned:
523 case tok::kw_float:
524 case tok::kw_double:
525 case tok::kw_void:
526 if (NextToken().is(tok::l_paren))
527 return TPR_ambiguous;
528
529 return TPR_true;
530
531 // GNU typeof support.
532 case tok::kw_typeof: {
533 if (NextToken().isNot(tok::l_paren))
534 return TPR_true;
535
536 TentativeParsingAction PA(*this);
537
538 TentativeParsingResult TPR = TryParseTypeofSpecifier();
539 bool isFollowedByParen = Tok.is(tok::l_paren);
540
541 PA.Revert();
542
543 if (TPR == TPR_error)
544 return TPR_error;
545
546 if (isFollowedByParen)
547 return TPR_ambiguous;
548
549 return TPR_true;
550 }
551
552 default:
553 return TPR_false;
554 }
555}
556
557/// [GNU] typeof-specifier:
558/// 'typeof' '(' expressions ')'
559/// 'typeof' '(' type-name ')'
560///
561Parser::TentativeParsingResult Parser::TryParseTypeofSpecifier() {
562 assert(Tok.is(tok::kw_typeof) && "Expected 'typeof'!");
563 ConsumeToken();
564
565 assert(Tok.is(tok::l_paren) && "Expected '('");
566 // Parse through the parens after 'typeof'.
567 ConsumeParen();
568 if (!SkipUntil(tok::r_paren))
569 return TPR_error;
570
571 return TPR_ambiguous;
572}
573
574Parser::TentativeParsingResult Parser::TryParseDeclarationSpecifier() {
575 TentativeParsingResult TPR = isCXXDeclarationSpecifier();
576 if (TPR != TPR_ambiguous)
577 return TPR;
578
579 if (Tok.is(tok::kw_typeof))
580 TryParseTypeofSpecifier();
581 else
582 ConsumeToken();
583
584 assert(Tok.is(tok::l_paren) && "Expected '('!");
585 return TPR_ambiguous;
586}
587
588/// isCXXFunctionDeclarator - Disambiguates between a function declarator or
589/// a constructor-style initializer, when parsing declaration statements.
590/// Returns true for function declarator and false for constructor-style
591/// initializer.
592/// If during the disambiguation process a parsing error is encountered,
593/// the function returns true to let the declaration parsing code handle it.
594///
595/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
596/// exception-specification[opt]
597///
598bool Parser::isCXXFunctionDeclarator() {
599 TentativeParsingAction PA(*this);
600
601 ConsumeParen();
602 TentativeParsingResult TPR = TryParseParameterDeclarationClause();
603 if (TPR == TPR_ambiguous && Tok.isNot(tok::r_paren))
604 TPR = TPR_false;
605
606 PA.Revert();
607
608 // In case of an error, let the declaration parsing code handle it.
609 if (TPR == TPR_error)
610 return true;
611
612 // Function declarator has precedence over constructor-style initializer.
613 if (TPR == TPR_ambiguous)
614 return TPR_true;
615 return TPR == TPR_true;
616}
617
618/// parameter-declaration-clause:
619/// parameter-declaration-list[opt] '...'[opt]
620/// parameter-declaration-list ',' '...'
621///
622/// parameter-declaration-list:
623/// parameter-declaration
624/// parameter-declaration-list ',' parameter-declaration
625///
626/// parameter-declaration:
627/// decl-specifier-seq declarator
628/// decl-specifier-seq declarator '=' assignment-expression
629/// decl-specifier-seq abstract-declarator[opt]
630/// decl-specifier-seq abstract-declarator[opt] '=' assignment-expression
631///
632Parser::TentativeParsingResult Parser::TryParseParameterDeclarationClause() {
633
634 if (Tok.is(tok::r_paren))
635 return TPR_true;
636
637 // parameter-declaration-list[opt] '...'[opt]
638 // parameter-declaration-list ',' '...'
639 //
640 // parameter-declaration-list:
641 // parameter-declaration
642 // parameter-declaration-list ',' parameter-declaration
643 //
644 while (1) {
645 // '...'[opt]
646 if (Tok.is(tok::ellipsis)) {
647 ConsumeToken();
648 return TPR_true; // '...' is a sign of a function declarator.
649 }
650
651 // decl-specifier-seq
652 TentativeParsingResult TPR = TryParseDeclarationSpecifier();
653 if (TPR != TPR_ambiguous)
654 return TPR;
655
656 // declarator
657 // abstract-declarator[opt]
658 TPR = TryParseDeclarator(true/*mayBeAbstract*/);
659 if (TPR != TPR_ambiguous)
660 return TPR;
661
662 if (Tok.is(tok::equal)) {
663 // '=' assignment-expression
664 // Parse through assignment-expression.
665 tok::TokenKind StopToks[3] ={ tok::comma, tok::ellipsis, tok::r_paren };
666 if (!SkipUntil(StopToks, 3, true/*StopAtSemi*/, true/*DontConsume*/))
667 return TPR_error;
668 }
669
670 if (Tok.is(tok::ellipsis)) {
671 ConsumeToken();
672 return TPR_true; // '...' is a sign of a function declarator.
673 }
674
675 if (Tok.isNot(tok::comma))
676 break;
677 ConsumeToken(); // the comma.
678 }
679
680 return TPR_ambiguous;
681}
682
683/// TryParseFunctionDeclarator - We previously determined (using
684/// isCXXFunctionDeclarator) that we are at a function declarator. Now parse
685/// through it.
686///
687/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
688/// exception-specification[opt]
689///
690/// exception-specification:
691/// 'throw' '(' type-id-list[opt] ')'
692///
693Parser::TentativeParsingResult Parser::TryParseFunctionDeclarator() {
694 assert(Tok.is(tok::l_paren));
695 // Parse through the parens.
696 ConsumeParen();
697 if (!SkipUntil(tok::r_paren))
698 return TPR_error;
699
700 // cv-qualifier-seq
701 while (Tok.is(tok::kw_const) ||
702 Tok.is(tok::kw_volatile) ||
703 Tok.is(tok::kw_restrict) )
704 ConsumeToken();
705
706 // exception-specification
707 if (Tok.is(tok::kw_throw)) {
708 ConsumeToken();
709 if (Tok.isNot(tok::l_paren))
710 return TPR_error;
711
712 // Parse through the parens after 'throw'.
713 ConsumeParen();
714 if (!SkipUntil(tok::r_paren))
715 return TPR_error;
716 }
717
718 return TPR_ambiguous;
719}
720
721/// '[' constant-expression[opt] ']'
722///
723Parser::TentativeParsingResult Parser::TryParseBracketDeclarator() {
724 ConsumeBracket();
725 if (!SkipUntil(tok::r_square))
726 return TPR_error;
727
728 return TPR_ambiguous;
729}