blob: 414f1f51f7d28cc4d3965b868db6f2857ee47653 [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
167/// init-declarator-list:
168/// init-declarator
169/// init-declarator-list ',' init-declarator
170///
171/// init-declarator:
172/// declarator initializer[opt]
173///
174/// initializer:
175/// '=' initializer-clause
176/// '(' expression-list ')'
177///
178/// initializer-clause:
179/// assignment-expression
180/// '{' initializer-list ','[opt] '}'
181/// '{' '}'
182///
183Parser::TentativeParsingResult Parser::TryParseInitDeclaratorList() {
184 // GCC only examines the first declarator for disambiguation:
185 // i.e:
186 // int(x), ++x; // GCC regards it as ill-formed declaration.
187 //
188 // Comeau and MSVC will regard the above statement as correct expression.
189 // Clang examines all of the declarators and also regards the above statement
190 // as correct expression.
191
192 while (1) {
193 // declarator
194 TentativeParsingResult TPR = TryParseDeclarator(false/*mayBeAbstract*/);
195 if (TPR != TPR_ambiguous)
196 return TPR;
197
198 // initializer[opt]
199 if (Tok.is(tok::l_paren)) {
200 // Parse through the parens.
201 ConsumeParen();
202 if (!SkipUntil(tok::r_paren))
203 return TPR_error;
204 } else if (Tok.is(tok::equal)) {
205 // MSVC won't examine the rest of declarators if '=' is encountered, it
206 // will conclude that it is a declaration.
207 // Comeau and Clang will examine the rest of declarators.
208 // Note that "int(x) = {0}, ++x;" will be interpreted as ill-formed
209 // expression.
210 //
211 // Parse through the initializer-clause.
212 SkipUntil(tok::comma, true/*StopAtSemi*/, true/*DontConsume*/);
213 }
214
215 if (Tok.isNot(tok::comma))
216 break;
217 ConsumeToken(); // the comma.
218 }
219
220 return TPR_ambiguous;
221}
222
223/// declarator:
224/// direct-declarator
225/// ptr-operator declarator
226///
227/// direct-declarator:
228/// declarator-id
229/// direct-declarator '(' parameter-declaration-clause ')'
230/// cv-qualifier-seq[opt] exception-specification[opt]
231/// direct-declarator '[' constant-expression[opt] ']'
232/// '(' declarator ')'
233///
234/// abstract-declarator:
235/// ptr-operator abstract-declarator[opt]
236/// direct-abstract-declarator
237///
238/// direct-abstract-declarator:
239/// direct-abstract-declarator[opt]
240/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
241/// exception-specification[opt]
242/// direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
243/// '(' abstract-declarator ')'
244///
245/// ptr-operator:
246/// '*' cv-qualifier-seq[opt]
247/// '&'
248/// [C++0x] '&&' [TODO]
249/// '::'[opt] nested-name-specifier '*' cv-qualifier-seq[opt] [TODO]
250///
251/// cv-qualifier-seq:
252/// cv-qualifier cv-qualifier-seq[opt]
253///
254/// cv-qualifier:
255/// 'const'
256/// 'volatile'
257///
258/// declarator-id:
259/// id-expression
260///
261/// id-expression:
262/// unqualified-id
263/// qualified-id [TODO]
264///
265/// unqualified-id:
266/// identifier
267/// operator-function-id [TODO]
268/// conversion-function-id [TODO]
269/// '~' class-name [TODO]
270/// template-id [TODO]
271///
272Parser::TentativeParsingResult Parser::TryParseDeclarator(bool mayBeAbstract) {
273 // declarator:
274 // direct-declarator
275 // ptr-operator declarator
276
277 while (1) {
278 if (Tok.is(tok::star) || Tok.is(tok::amp)) {
279 // ptr-operator
280 ConsumeToken();
281 while (Tok.is(tok::kw_const) ||
282 Tok.is(tok::kw_volatile) ||
283 Tok.is(tok::kw_restrict) )
284 ConsumeToken();
285 } else {
286 break;
287 }
288 }
289
290 // direct-declarator:
291 // direct-abstract-declarator:
292
293 if (Tok.is(tok::identifier)) {
294 // declarator-id
295 ConsumeToken();
296 } else if (Tok.is(tok::l_paren)) {
297 if (mayBeAbstract && isCXXFunctionDeclarator()) {
298 // '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
299 // exception-specification[opt]
300 TentativeParsingResult TPR = TryParseFunctionDeclarator();
301 if (TPR != TPR_ambiguous)
302 return TPR;
303 } else {
304 // '(' declarator ')'
305 // '(' abstract-declarator ')'
306 ConsumeParen();
307 TentativeParsingResult TPR = TryParseDeclarator(mayBeAbstract);
308 if (TPR != TPR_ambiguous)
309 return TPR;
310 if (Tok.isNot(tok::r_paren))
311 return TPR_false;
312 ConsumeParen();
313 }
314 } else if (!mayBeAbstract) {
315 return TPR_false;
316 }
317
318 while (1) {
319 TentativeParsingResult TPR;
320
321 if (Tok.is(tok::l_paren)) {
322 // direct-declarator '(' parameter-declaration-clause ')'
323 // cv-qualifier-seq[opt] exception-specification[opt]
324 if (!isCXXFunctionDeclarator())
325 break;
326 TPR = TryParseFunctionDeclarator();
327 } else if (Tok.is(tok::l_square)) {
328 // direct-declarator '[' constant-expression[opt] ']'
329 // direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
330 TPR = TryParseBracketDeclarator();
331 } else {
332 break;
333 }
334
335 if (TPR != TPR_ambiguous)
336 return TPR;
337 }
338
339 return TPR_ambiguous;
340}
341
342/// isCXXDeclarationSpecifier - Returns TPR_true if it is a declaration
343/// specifier, TPR_false if it is not, TPR_ambiguous if it could be either
344/// a decl-specifier or a function-style cast, and TPR_error if a parsing
345/// error was found and reported.
346///
347/// decl-specifier:
348/// storage-class-specifier
349/// type-specifier
350/// function-specifier
351/// 'friend'
352/// 'typedef'
353/// [GNU] attributes declaration-specifiers[opt]
354///
355/// storage-class-specifier:
356/// 'register'
357/// 'static'
358/// 'extern'
359/// 'mutable'
360/// 'auto'
361/// [GNU] '__thread'
362///
363/// function-specifier:
364/// 'inline'
365/// 'virtual'
366/// 'explicit'
367///
368/// typedef-name:
369/// identifier
370///
371/// type-specifier:
372/// simple-type-specifier
373/// class-specifier
374/// enum-specifier
375/// elaborated-type-specifier
376/// typename-specifier [TODO]
377/// cv-qualifier
378///
379/// simple-type-specifier:
380/// '::'[opt] nested-name-specifier[opt] type-name [TODO]
381/// '::'[opt] nested-name-specifier 'template'
382/// simple-template-id [TODO]
383/// 'char'
384/// 'wchar_t'
385/// 'bool'
386/// 'short'
387/// 'int'
388/// 'long'
389/// 'signed'
390/// 'unsigned'
391/// 'float'
392/// 'double'
393/// 'void'
394/// [GNU] typeof-specifier
395/// [GNU] '_Complex'
396/// [C++0x] 'auto' [TODO]
397///
398/// type-name:
399/// class-name
400/// enum-name
401/// typedef-name
402///
403/// elaborated-type-specifier:
404/// class-key '::'[opt] nested-name-specifier[opt] identifier
405/// class-key '::'[opt] nested-name-specifier[opt] 'template'[opt]
406/// simple-template-id
407/// 'enum' '::'[opt] nested-name-specifier[opt] identifier
408///
409/// enum-name:
410/// identifier
411///
412/// enum-specifier:
413/// 'enum' identifier[opt] '{' enumerator-list[opt] '}'
414/// 'enum' identifier[opt] '{' enumerator-list ',' '}'
415///
416/// class-specifier:
417/// class-head '{' member-specification[opt] '}'
418///
419/// class-head:
420/// class-key identifier[opt] base-clause[opt]
421/// class-key nested-name-specifier identifier base-clause[opt]
422/// class-key nested-name-specifier[opt] simple-template-id
423/// base-clause[opt]
424///
425/// class-key:
426/// 'class'
427/// 'struct'
428/// 'union'
429///
430/// cv-qualifier:
431/// 'const'
432/// 'volatile'
433/// [GNU] restrict
434///
435Parser::TentativeParsingResult Parser::isCXXDeclarationSpecifier() {
436 switch (Tok.getKind()) {
437 // decl-specifier:
438 // storage-class-specifier
439 // type-specifier
440 // function-specifier
441 // 'friend'
442 // 'typedef'
443
444 case tok::kw_friend:
445 case tok::kw_typedef:
446 // storage-class-specifier
447 case tok::kw_register:
448 case tok::kw_static:
449 case tok::kw_extern:
450 case tok::kw_mutable:
451 case tok::kw_auto:
452 case tok::kw___thread:
453 // function-specifier
454 case tok::kw_inline:
455 case tok::kw_virtual:
456 case tok::kw_explicit:
457
458 // type-specifier:
459 // simple-type-specifier
460 // class-specifier
461 // enum-specifier
462 // elaborated-type-specifier
463 // typename-specifier
464 // cv-qualifier
465
466 // class-specifier
467 // elaborated-type-specifier
468 case tok::kw_class:
469 case tok::kw_struct:
470 case tok::kw_union:
471 // enum-specifier
472 case tok::kw_enum:
473 // cv-qualifier
474 case tok::kw_const:
475 case tok::kw_volatile:
476
477 // GNU
478 case tok::kw_restrict:
479 case tok::kw__Complex:
480 case tok::kw___attribute:
481 return TPR_true;
482
483 // The ambiguity resides in a simple-type-specifier/typename-specifier
484 // followed by a '('. The '(' could either be the start of:
485 //
486 // direct-declarator:
487 // '(' declarator ')'
488 //
489 // direct-abstract-declarator:
490 // '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
491 // exception-specification[opt]
492 // '(' abstract-declarator ')'
493 //
494 // or part of a function-style cast expression:
495 //
496 // simple-type-specifier '(' expression-list[opt] ')'
497 //
498
499 // simple-type-specifier:
500
501 case tok::identifier:
502 if (!Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope))
503 return TPR_false;
504 // FALL THROUGH.
505
506 case tok::kw_char:
507 case tok::kw_wchar_t:
508 case tok::kw_bool:
509 case tok::kw_short:
510 case tok::kw_int:
511 case tok::kw_long:
512 case tok::kw_signed:
513 case tok::kw_unsigned:
514 case tok::kw_float:
515 case tok::kw_double:
516 case tok::kw_void:
517 if (NextToken().is(tok::l_paren))
518 return TPR_ambiguous;
519
520 return TPR_true;
521
522 // GNU typeof support.
523 case tok::kw_typeof: {
524 if (NextToken().isNot(tok::l_paren))
525 return TPR_true;
526
527 TentativeParsingAction PA(*this);
528
529 TentativeParsingResult TPR = TryParseTypeofSpecifier();
530 bool isFollowedByParen = Tok.is(tok::l_paren);
531
532 PA.Revert();
533
534 if (TPR == TPR_error)
535 return TPR_error;
536
537 if (isFollowedByParen)
538 return TPR_ambiguous;
539
540 return TPR_true;
541 }
542
543 default:
544 return TPR_false;
545 }
546}
547
548/// [GNU] typeof-specifier:
549/// 'typeof' '(' expressions ')'
550/// 'typeof' '(' type-name ')'
551///
552Parser::TentativeParsingResult Parser::TryParseTypeofSpecifier() {
553 assert(Tok.is(tok::kw_typeof) && "Expected 'typeof'!");
554 ConsumeToken();
555
556 assert(Tok.is(tok::l_paren) && "Expected '('");
557 // Parse through the parens after 'typeof'.
558 ConsumeParen();
559 if (!SkipUntil(tok::r_paren))
560 return TPR_error;
561
562 return TPR_ambiguous;
563}
564
565Parser::TentativeParsingResult Parser::TryParseDeclarationSpecifier() {
566 TentativeParsingResult TPR = isCXXDeclarationSpecifier();
567 if (TPR != TPR_ambiguous)
568 return TPR;
569
570 if (Tok.is(tok::kw_typeof))
571 TryParseTypeofSpecifier();
572 else
573 ConsumeToken();
574
575 assert(Tok.is(tok::l_paren) && "Expected '('!");
576 return TPR_ambiguous;
577}
578
579/// isCXXFunctionDeclarator - Disambiguates between a function declarator or
580/// a constructor-style initializer, when parsing declaration statements.
581/// Returns true for function declarator and false for constructor-style
582/// initializer.
583/// If during the disambiguation process a parsing error is encountered,
584/// the function returns true to let the declaration parsing code handle it.
585///
586/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
587/// exception-specification[opt]
588///
589bool Parser::isCXXFunctionDeclarator() {
590 TentativeParsingAction PA(*this);
591
592 ConsumeParen();
593 TentativeParsingResult TPR = TryParseParameterDeclarationClause();
594 if (TPR == TPR_ambiguous && Tok.isNot(tok::r_paren))
595 TPR = TPR_false;
596
597 PA.Revert();
598
599 // In case of an error, let the declaration parsing code handle it.
600 if (TPR == TPR_error)
601 return true;
602
603 // Function declarator has precedence over constructor-style initializer.
604 if (TPR == TPR_ambiguous)
605 return TPR_true;
606 return TPR == TPR_true;
607}
608
609/// parameter-declaration-clause:
610/// parameter-declaration-list[opt] '...'[opt]
611/// parameter-declaration-list ',' '...'
612///
613/// parameter-declaration-list:
614/// parameter-declaration
615/// parameter-declaration-list ',' parameter-declaration
616///
617/// parameter-declaration:
618/// decl-specifier-seq declarator
619/// decl-specifier-seq declarator '=' assignment-expression
620/// decl-specifier-seq abstract-declarator[opt]
621/// decl-specifier-seq abstract-declarator[opt] '=' assignment-expression
622///
623Parser::TentativeParsingResult Parser::TryParseParameterDeclarationClause() {
624
625 if (Tok.is(tok::r_paren))
626 return TPR_true;
627
628 // parameter-declaration-list[opt] '...'[opt]
629 // parameter-declaration-list ',' '...'
630 //
631 // parameter-declaration-list:
632 // parameter-declaration
633 // parameter-declaration-list ',' parameter-declaration
634 //
635 while (1) {
636 // '...'[opt]
637 if (Tok.is(tok::ellipsis)) {
638 ConsumeToken();
639 return TPR_true; // '...' is a sign of a function declarator.
640 }
641
642 // decl-specifier-seq
643 TentativeParsingResult TPR = TryParseDeclarationSpecifier();
644 if (TPR != TPR_ambiguous)
645 return TPR;
646
647 // declarator
648 // abstract-declarator[opt]
649 TPR = TryParseDeclarator(true/*mayBeAbstract*/);
650 if (TPR != TPR_ambiguous)
651 return TPR;
652
653 if (Tok.is(tok::equal)) {
654 // '=' assignment-expression
655 // Parse through assignment-expression.
656 tok::TokenKind StopToks[3] ={ tok::comma, tok::ellipsis, tok::r_paren };
657 if (!SkipUntil(StopToks, 3, true/*StopAtSemi*/, true/*DontConsume*/))
658 return TPR_error;
659 }
660
661 if (Tok.is(tok::ellipsis)) {
662 ConsumeToken();
663 return TPR_true; // '...' is a sign of a function declarator.
664 }
665
666 if (Tok.isNot(tok::comma))
667 break;
668 ConsumeToken(); // the comma.
669 }
670
671 return TPR_ambiguous;
672}
673
674/// TryParseFunctionDeclarator - We previously determined (using
675/// isCXXFunctionDeclarator) that we are at a function declarator. Now parse
676/// through it.
677///
678/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
679/// exception-specification[opt]
680///
681/// exception-specification:
682/// 'throw' '(' type-id-list[opt] ')'
683///
684Parser::TentativeParsingResult Parser::TryParseFunctionDeclarator() {
685 assert(Tok.is(tok::l_paren));
686 // Parse through the parens.
687 ConsumeParen();
688 if (!SkipUntil(tok::r_paren))
689 return TPR_error;
690
691 // cv-qualifier-seq
692 while (Tok.is(tok::kw_const) ||
693 Tok.is(tok::kw_volatile) ||
694 Tok.is(tok::kw_restrict) )
695 ConsumeToken();
696
697 // exception-specification
698 if (Tok.is(tok::kw_throw)) {
699 ConsumeToken();
700 if (Tok.isNot(tok::l_paren))
701 return TPR_error;
702
703 // Parse through the parens after 'throw'.
704 ConsumeParen();
705 if (!SkipUntil(tok::r_paren))
706 return TPR_error;
707 }
708
709 return TPR_ambiguous;
710}
711
712/// '[' constant-expression[opt] ']'
713///
714Parser::TentativeParsingResult Parser::TryParseBracketDeclarator() {
715 ConsumeBracket();
716 if (!SkipUntil(tok::r_square))
717 return TPR_error;
718
719 return TPR_ambiguous;
720}