forked from OSchip/llvm-project
Resolve ambiguous C++ statements (C++ 6.8p1).
'ParseTentative.cpp' implements the functionality needed to resolve ambiguous C++ statements, to either a declaration or an expression, by "tentatively parsing" them. llvm-svn: 57084
This commit is contained in:
parent
02da7667d2
commit
2c7137d8d1
|
@ -555,6 +555,8 @@ DIAG(err_expected_lparen_after_type, ERROR,
|
|||
"expected '(' for function-style cast or type construction")
|
||||
DIAG(err_expected_equal_after_declarator, ERROR,
|
||||
"expected '=' after declarator")
|
||||
DIAG(warn_statement_disambiguation, WARNING,
|
||||
"statement was disambiguated as %0")
|
||||
|
||||
// Language specific pragmas
|
||||
|
||||
|
|
|
@ -212,6 +212,44 @@ private:
|
|||
const Token &NextToken() {
|
||||
return PP.LookAhead(0);
|
||||
}
|
||||
|
||||
/// TentativeParsingAction - An object that is used as a kind of "tentative
|
||||
/// parsing transaction". It gets instantiated to mark the token position and
|
||||
/// after the token consumption is done, Commit() or Revert() is called to
|
||||
/// either "commit the consumed tokens" or revert to the previously marked
|
||||
/// token position. Example:
|
||||
///
|
||||
/// TentativeParsingAction TPA;
|
||||
/// ConsumeToken();
|
||||
/// ....
|
||||
/// TPA.Revert();
|
||||
///
|
||||
class TentativeParsingAction {
|
||||
Parser &P;
|
||||
Token PrevTok;
|
||||
bool isActive;
|
||||
|
||||
public:
|
||||
explicit TentativeParsingAction(Parser& p) : P(p) {
|
||||
PrevTok = P.Tok;
|
||||
P.PP.EnableBacktrackAtThisPos();
|
||||
isActive = true;
|
||||
}
|
||||
void Commit() {
|
||||
assert(isActive && "Parsing action was finished!");
|
||||
P.PP.CommitBacktrackedTokens();
|
||||
isActive = false;
|
||||
}
|
||||
void Revert() {
|
||||
assert(isActive && "Parsing action was finished!");
|
||||
P.PP.Backtrack();
|
||||
P.Tok = PrevTok;
|
||||
isActive = false;
|
||||
}
|
||||
~TentativeParsingAction() {
|
||||
assert(!isActive && "Forgot to call Commit or Revert!");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// MatchRHSPunctuation - For punctuation with a LHS and RHS (e.g. '['/']'),
|
||||
|
@ -534,6 +572,68 @@ private:
|
|||
bool isTypeSpecifierQualifier() const;
|
||||
bool isTypeQualifier() const;
|
||||
|
||||
/// TentativeParsingResult - Used as the result value for functions whose
|
||||
/// purpose is to disambiguate C++ constructs by "tentatively parsing" them.
|
||||
enum TentativeParsingResult {
|
||||
TPR_true,
|
||||
TPR_false,
|
||||
TPR_ambiguous,
|
||||
TPR_error
|
||||
};
|
||||
|
||||
/// isDeclarationStatement - Disambiguates between a declaration or an
|
||||
/// expression statement, when parsing function bodies.
|
||||
/// Returns true for declaration, false for expression.
|
||||
bool isDeclarationStatement() {
|
||||
if (getLang().CPlusPlus)
|
||||
return isCXXDeclarationStatement();
|
||||
return isDeclarationSpecifier();
|
||||
}
|
||||
|
||||
/// isCXXDeclarationStatement - C++-specialized function that disambiguates
|
||||
/// between a declaration or an expression statement, when parsing function
|
||||
/// bodies. Returns true for declaration, false for expression.
|
||||
bool isCXXDeclarationStatement();
|
||||
|
||||
/// isCXXSimpleDeclaration - C++-specialized function that disambiguates
|
||||
/// between a simple-declaration or an expression-statement.
|
||||
/// If during the disambiguation process a parsing error is encountered,
|
||||
/// the function returns true to let the declaration parsing code handle it.
|
||||
/// Returns false if the statement is disambiguated as expression.
|
||||
bool isCXXSimpleDeclaration();
|
||||
|
||||
/// isCXXFunctionDeclarator - Disambiguates between a function declarator or
|
||||
/// a constructor-style initializer, when parsing declaration statements.
|
||||
/// Returns true for function declarator and false for constructor-style
|
||||
/// initializer.
|
||||
/// If during the disambiguation process a parsing error is encountered,
|
||||
/// the function returns true to let the declaration parsing code handle it.
|
||||
bool isCXXFunctionDeclarator();
|
||||
|
||||
/// isCXXDeclarationSpecifier - Returns TPR_true if it is a declaration
|
||||
/// specifier, TPR_false if it is not, TPR_ambiguous if it could be either
|
||||
/// a decl-specifier or a function-style cast, and TPR_error if a parsing
|
||||
/// error was encountered.
|
||||
/// Doesn't consume tokens.
|
||||
TentativeParsingResult isCXXDeclarationSpecifier();
|
||||
|
||||
// "Tentative parsing" functions, used for disambiguation. If a parsing error
|
||||
// is encountered they will return TPR_error.
|
||||
// Returning TPR_true/false indicates that the ambiguity was resolved and
|
||||
// tentative parsing may stop. TPR_ambiguous indicates that more tentative
|
||||
// parsing is necessary for disambiguation.
|
||||
// They all consume tokens, so backtracking should be used after calling them.
|
||||
|
||||
TentativeParsingResult TryParseDeclarationSpecifier();
|
||||
TentativeParsingResult TryParseSimpleDeclaration();
|
||||
TentativeParsingResult TryParseTypeofSpecifier();
|
||||
TentativeParsingResult TryParseInitDeclaratorList();
|
||||
TentativeParsingResult TryParseDeclarator(bool mayBeAbstract);
|
||||
TentativeParsingResult TryParseParameterDeclarationClause();
|
||||
TentativeParsingResult TryParseFunctionDeclarator();
|
||||
TentativeParsingResult TryParseBracketDeclarator();
|
||||
|
||||
|
||||
TypeTy *ParseTypeName();
|
||||
AttributeList *ParseAttributes();
|
||||
void ParseTypeofSpecifier(DeclSpec &DS);
|
||||
|
|
|
@ -94,7 +94,7 @@ Parser::StmtResult Parser::ParseStatementOrDeclaration(bool OnlyStatement) {
|
|||
// PASS THROUGH.
|
||||
|
||||
default:
|
||||
if ((getLang().CPlusPlus || !OnlyStatement) && isDeclarationSpecifier()) {
|
||||
if ((getLang().CPlusPlus || !OnlyStatement) && isDeclarationStatement()) {
|
||||
SourceLocation DeclStart = Tok.getLocation();
|
||||
DeclTy *Res = ParseDeclaration(Declarator::BlockContext);
|
||||
// FIXME: Pass in the right location for the end of the declstmt.
|
||||
|
@ -368,7 +368,7 @@ Parser::StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
|
|||
Diags.setWarnOnExtensions(false);
|
||||
|
||||
// If this is the start of a declaration, parse it as such.
|
||||
if (isDeclarationSpecifier()) {
|
||||
if (isDeclarationStatement()) {
|
||||
// FIXME: Save the __extension__ on the decl as a node somehow.
|
||||
SourceLocation DeclStart = Tok.getLocation();
|
||||
DeclTy *Res = ParseDeclaration(Declarator::BlockContext);
|
||||
|
|
|
@ -0,0 +1,720 @@
|
|||
//===--- ParseTentative.cpp - Ambiguity Resolution Parsing ----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the tentative parsing portions of the Parser
|
||||
// interfaces, for ambiguity resolution.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Parse/Parser.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
using namespace clang;
|
||||
|
||||
/// isCXXDeclarationStatement - C++-specialized function that disambiguates
|
||||
/// between a declaration or an expression statement, when parsing function
|
||||
/// bodies. Returns true for declaration, false for expression.
|
||||
///
|
||||
/// declaration-statement:
|
||||
/// block-declaration
|
||||
///
|
||||
/// block-declaration:
|
||||
/// simple-declaration
|
||||
/// asm-definition
|
||||
/// namespace-alias-definition
|
||||
/// using-declaration
|
||||
/// using-directive
|
||||
/// [C++0x] static_assert-declaration [TODO]
|
||||
///
|
||||
/// asm-definition:
|
||||
/// 'asm' '(' string-literal ')' ';'
|
||||
///
|
||||
/// namespace-alias-definition:
|
||||
/// 'namespace' identifier = qualified-namespace-specifier ';'
|
||||
///
|
||||
/// using-declaration:
|
||||
/// 'using' typename[opt] '::'[opt] nested-name-specifier
|
||||
/// unqualified-id ';'
|
||||
/// 'using' '::' unqualified-id ;
|
||||
///
|
||||
/// using-directive:
|
||||
/// 'using' 'namespace' '::'[opt] nested-name-specifier[opt]
|
||||
/// namespace-name ';'
|
||||
///
|
||||
/// [C++0x] static_assert-declaration: [TODO]
|
||||
/// [C++0x] static_assert '(' constant-expression ',' string-literal ')' ';'
|
||||
///
|
||||
bool Parser::isCXXDeclarationStatement() {
|
||||
switch (Tok.getKind()) {
|
||||
// asm-definition
|
||||
case tok::kw_asm:
|
||||
// namespace-alias-definition
|
||||
case tok::kw_namespace:
|
||||
// using-declaration
|
||||
// using-directive
|
||||
case tok::kw_using:
|
||||
return true;
|
||||
default:
|
||||
// simple-declaration
|
||||
return isCXXSimpleDeclaration();
|
||||
}
|
||||
}
|
||||
|
||||
/// isCXXSimpleDeclaration - C++-specialized function that disambiguates
|
||||
/// between a simple-declaration or an expression-statement.
|
||||
/// If during the disambiguation process a parsing error is encountered,
|
||||
/// the function returns true to let the declaration parsing code handle it.
|
||||
/// Returns false if the statement is disambiguated as expression.
|
||||
///
|
||||
/// simple-declaration:
|
||||
/// decl-specifier-seq init-declarator-list[opt] ';'
|
||||
///
|
||||
bool Parser::isCXXSimpleDeclaration() {
|
||||
// C++ 6.8p1:
|
||||
// There is an ambiguity in the grammar involving expression-statements and
|
||||
// declarations: An expression-statement with a function-style explicit type
|
||||
// conversion (5.2.3) as its leftmost subexpression can be indistinguishable
|
||||
// from a declaration where the first declarator starts with a '('. In those
|
||||
// cases the statement is a declaration. [Note: To disambiguate, the whole
|
||||
// statement might have to be examined to determine if it is an
|
||||
// expression-statement or a declaration].
|
||||
|
||||
// C++ 6.8p3:
|
||||
// The disambiguation is purely syntactic; that is, the meaning of the names
|
||||
// occurring in such a statement, beyond whether they are type-names or not,
|
||||
// is not generally used in or changed by the disambiguation. Class
|
||||
// templates are instantiated as necessary to determine if a qualified name
|
||||
// is a type-name. Disambiguation precedes parsing, and a statement
|
||||
// disambiguated as a declaration may be an ill-formed declaration.
|
||||
|
||||
// We don't have to parse all of the decl-specifier-seq part. There's only
|
||||
// an ambiguity if the first decl-specifier is
|
||||
// simple-type-specifier/typename-specifier followed by a '(', which may
|
||||
// indicate a function-style cast expression.
|
||||
// isCXXDeclarationSpecifier will return TPR_ambiguous only in such a case.
|
||||
|
||||
TentativeParsingResult TPR = isCXXDeclarationSpecifier();
|
||||
if (TPR != TPR_ambiguous)
|
||||
return TPR != TPR_false; // Returns true for TPR_true or TPR_error.
|
||||
|
||||
// FIXME: Add statistics about the number of ambiguous statements encountered
|
||||
// and how they were resolved (number of declarations+number of expressions).
|
||||
|
||||
// Ok, we have a simple-type-specifier/typename-specifier followed by a '('.
|
||||
// We need tentative parsing...
|
||||
|
||||
TentativeParsingAction PA(*this);
|
||||
|
||||
TPR = TryParseSimpleDeclaration();
|
||||
SourceLocation TentativeParseLoc = Tok.getLocation();
|
||||
|
||||
PA.Revert();
|
||||
|
||||
// In case of an error, let the declaration parsing code handle it.
|
||||
if (TPR == TPR_error)
|
||||
return true;
|
||||
|
||||
// Declarations take precedence over expressions.
|
||||
if (TPR == TPR_ambiguous)
|
||||
TPR = TPR_true;
|
||||
|
||||
assert(TPR == TPR_true || TPR == TPR_false);
|
||||
if (TPR == TPR_true && Tok.isNot(tok::kw_void)) {
|
||||
// We have a declaration that looks like a functional cast; there's a high
|
||||
// chance that the author intended the statement to be an expression.
|
||||
// Emit a warning.
|
||||
Diag(Tok.getLocation(), diag::warn_statement_disambiguation,
|
||||
"declaration", SourceRange(Tok.getLocation(), TentativeParseLoc));
|
||||
} else if (TPR == TPR_false && Tok.is(tok::kw_void)) {
|
||||
// A functional cast to 'void' expression ? Warning..
|
||||
Diag(Tok.getLocation(), diag::warn_statement_disambiguation,
|
||||
"expression", SourceRange(Tok.getLocation(), TentativeParseLoc));
|
||||
}
|
||||
|
||||
return TPR == TPR_true;
|
||||
}
|
||||
|
||||
/// simple-declaration:
|
||||
/// decl-specifier-seq init-declarator-list[opt] ';'
|
||||
///
|
||||
Parser::TentativeParsingResult Parser::TryParseSimpleDeclaration() {
|
||||
// We know that we have a simple-type-specifier/typename-specifier followed
|
||||
// by a '('.
|
||||
assert(isCXXDeclarationSpecifier() == TPR_ambiguous);
|
||||
|
||||
if (Tok.is(tok::kw_typeof))
|
||||
TryParseTypeofSpecifier();
|
||||
else
|
||||
ConsumeToken();
|
||||
|
||||
assert(Tok.is(tok::l_paren) && "Expected '('");
|
||||
|
||||
TentativeParsingResult TPR = TryParseInitDeclaratorList();
|
||||
if (TPR != TPR_ambiguous)
|
||||
return TPR;
|
||||
|
||||
if (Tok.isNot(tok::semi))
|
||||
return TPR_false;
|
||||
|
||||
return TPR_ambiguous;
|
||||
}
|
||||
|
||||
/// init-declarator-list:
|
||||
/// init-declarator
|
||||
/// init-declarator-list ',' init-declarator
|
||||
///
|
||||
/// init-declarator:
|
||||
/// declarator initializer[opt]
|
||||
///
|
||||
/// initializer:
|
||||
/// '=' initializer-clause
|
||||
/// '(' expression-list ')'
|
||||
///
|
||||
/// initializer-clause:
|
||||
/// assignment-expression
|
||||
/// '{' initializer-list ','[opt] '}'
|
||||
/// '{' '}'
|
||||
///
|
||||
Parser::TentativeParsingResult Parser::TryParseInitDeclaratorList() {
|
||||
// GCC only examines the first declarator for disambiguation:
|
||||
// i.e:
|
||||
// int(x), ++x; // GCC regards it as ill-formed declaration.
|
||||
//
|
||||
// Comeau and MSVC will regard the above statement as correct expression.
|
||||
// Clang examines all of the declarators and also regards the above statement
|
||||
// as correct expression.
|
||||
|
||||
while (1) {
|
||||
// declarator
|
||||
TentativeParsingResult TPR = TryParseDeclarator(false/*mayBeAbstract*/);
|
||||
if (TPR != TPR_ambiguous)
|
||||
return TPR;
|
||||
|
||||
// initializer[opt]
|
||||
if (Tok.is(tok::l_paren)) {
|
||||
// Parse through the parens.
|
||||
ConsumeParen();
|
||||
if (!SkipUntil(tok::r_paren))
|
||||
return TPR_error;
|
||||
} else if (Tok.is(tok::equal)) {
|
||||
// MSVC won't examine the rest of declarators if '=' is encountered, it
|
||||
// will conclude that it is a declaration.
|
||||
// Comeau and Clang will examine the rest of declarators.
|
||||
// Note that "int(x) = {0}, ++x;" will be interpreted as ill-formed
|
||||
// expression.
|
||||
//
|
||||
// Parse through the initializer-clause.
|
||||
SkipUntil(tok::comma, true/*StopAtSemi*/, true/*DontConsume*/);
|
||||
}
|
||||
|
||||
if (Tok.isNot(tok::comma))
|
||||
break;
|
||||
ConsumeToken(); // the comma.
|
||||
}
|
||||
|
||||
return TPR_ambiguous;
|
||||
}
|
||||
|
||||
/// declarator:
|
||||
/// direct-declarator
|
||||
/// ptr-operator declarator
|
||||
///
|
||||
/// direct-declarator:
|
||||
/// declarator-id
|
||||
/// direct-declarator '(' parameter-declaration-clause ')'
|
||||
/// cv-qualifier-seq[opt] exception-specification[opt]
|
||||
/// direct-declarator '[' constant-expression[opt] ']'
|
||||
/// '(' declarator ')'
|
||||
///
|
||||
/// abstract-declarator:
|
||||
/// ptr-operator abstract-declarator[opt]
|
||||
/// direct-abstract-declarator
|
||||
///
|
||||
/// direct-abstract-declarator:
|
||||
/// direct-abstract-declarator[opt]
|
||||
/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
|
||||
/// exception-specification[opt]
|
||||
/// direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
|
||||
/// '(' abstract-declarator ')'
|
||||
///
|
||||
/// ptr-operator:
|
||||
/// '*' cv-qualifier-seq[opt]
|
||||
/// '&'
|
||||
/// [C++0x] '&&' [TODO]
|
||||
/// '::'[opt] nested-name-specifier '*' cv-qualifier-seq[opt] [TODO]
|
||||
///
|
||||
/// cv-qualifier-seq:
|
||||
/// cv-qualifier cv-qualifier-seq[opt]
|
||||
///
|
||||
/// cv-qualifier:
|
||||
/// 'const'
|
||||
/// 'volatile'
|
||||
///
|
||||
/// declarator-id:
|
||||
/// id-expression
|
||||
///
|
||||
/// id-expression:
|
||||
/// unqualified-id
|
||||
/// qualified-id [TODO]
|
||||
///
|
||||
/// unqualified-id:
|
||||
/// identifier
|
||||
/// operator-function-id [TODO]
|
||||
/// conversion-function-id [TODO]
|
||||
/// '~' class-name [TODO]
|
||||
/// template-id [TODO]
|
||||
///
|
||||
Parser::TentativeParsingResult Parser::TryParseDeclarator(bool mayBeAbstract) {
|
||||
// declarator:
|
||||
// direct-declarator
|
||||
// ptr-operator declarator
|
||||
|
||||
while (1) {
|
||||
if (Tok.is(tok::star) || Tok.is(tok::amp)) {
|
||||
// ptr-operator
|
||||
ConsumeToken();
|
||||
while (Tok.is(tok::kw_const) ||
|
||||
Tok.is(tok::kw_volatile) ||
|
||||
Tok.is(tok::kw_restrict) )
|
||||
ConsumeToken();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// direct-declarator:
|
||||
// direct-abstract-declarator:
|
||||
|
||||
if (Tok.is(tok::identifier)) {
|
||||
// declarator-id
|
||||
ConsumeToken();
|
||||
} else if (Tok.is(tok::l_paren)) {
|
||||
if (mayBeAbstract && isCXXFunctionDeclarator()) {
|
||||
// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
|
||||
// exception-specification[opt]
|
||||
TentativeParsingResult TPR = TryParseFunctionDeclarator();
|
||||
if (TPR != TPR_ambiguous)
|
||||
return TPR;
|
||||
} else {
|
||||
// '(' declarator ')'
|
||||
// '(' abstract-declarator ')'
|
||||
ConsumeParen();
|
||||
TentativeParsingResult TPR = TryParseDeclarator(mayBeAbstract);
|
||||
if (TPR != TPR_ambiguous)
|
||||
return TPR;
|
||||
if (Tok.isNot(tok::r_paren))
|
||||
return TPR_false;
|
||||
ConsumeParen();
|
||||
}
|
||||
} else if (!mayBeAbstract) {
|
||||
return TPR_false;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
TentativeParsingResult TPR;
|
||||
|
||||
if (Tok.is(tok::l_paren)) {
|
||||
// direct-declarator '(' parameter-declaration-clause ')'
|
||||
// cv-qualifier-seq[opt] exception-specification[opt]
|
||||
if (!isCXXFunctionDeclarator())
|
||||
break;
|
||||
TPR = TryParseFunctionDeclarator();
|
||||
} else if (Tok.is(tok::l_square)) {
|
||||
// direct-declarator '[' constant-expression[opt] ']'
|
||||
// direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
|
||||
TPR = TryParseBracketDeclarator();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (TPR != TPR_ambiguous)
|
||||
return TPR;
|
||||
}
|
||||
|
||||
return TPR_ambiguous;
|
||||
}
|
||||
|
||||
/// isCXXDeclarationSpecifier - Returns TPR_true if it is a declaration
|
||||
/// specifier, TPR_false if it is not, TPR_ambiguous if it could be either
|
||||
/// a decl-specifier or a function-style cast, and TPR_error if a parsing
|
||||
/// error was found and reported.
|
||||
///
|
||||
/// decl-specifier:
|
||||
/// storage-class-specifier
|
||||
/// type-specifier
|
||||
/// function-specifier
|
||||
/// 'friend'
|
||||
/// 'typedef'
|
||||
/// [GNU] attributes declaration-specifiers[opt]
|
||||
///
|
||||
/// storage-class-specifier:
|
||||
/// 'register'
|
||||
/// 'static'
|
||||
/// 'extern'
|
||||
/// 'mutable'
|
||||
/// 'auto'
|
||||
/// [GNU] '__thread'
|
||||
///
|
||||
/// function-specifier:
|
||||
/// 'inline'
|
||||
/// 'virtual'
|
||||
/// 'explicit'
|
||||
///
|
||||
/// typedef-name:
|
||||
/// identifier
|
||||
///
|
||||
/// type-specifier:
|
||||
/// simple-type-specifier
|
||||
/// class-specifier
|
||||
/// enum-specifier
|
||||
/// elaborated-type-specifier
|
||||
/// typename-specifier [TODO]
|
||||
/// cv-qualifier
|
||||
///
|
||||
/// simple-type-specifier:
|
||||
/// '::'[opt] nested-name-specifier[opt] type-name [TODO]
|
||||
/// '::'[opt] nested-name-specifier 'template'
|
||||
/// simple-template-id [TODO]
|
||||
/// 'char'
|
||||
/// 'wchar_t'
|
||||
/// 'bool'
|
||||
/// 'short'
|
||||
/// 'int'
|
||||
/// 'long'
|
||||
/// 'signed'
|
||||
/// 'unsigned'
|
||||
/// 'float'
|
||||
/// 'double'
|
||||
/// 'void'
|
||||
/// [GNU] typeof-specifier
|
||||
/// [GNU] '_Complex'
|
||||
/// [C++0x] 'auto' [TODO]
|
||||
///
|
||||
/// type-name:
|
||||
/// class-name
|
||||
/// enum-name
|
||||
/// typedef-name
|
||||
///
|
||||
/// elaborated-type-specifier:
|
||||
/// class-key '::'[opt] nested-name-specifier[opt] identifier
|
||||
/// class-key '::'[opt] nested-name-specifier[opt] 'template'[opt]
|
||||
/// simple-template-id
|
||||
/// 'enum' '::'[opt] nested-name-specifier[opt] identifier
|
||||
///
|
||||
/// enum-name:
|
||||
/// identifier
|
||||
///
|
||||
/// enum-specifier:
|
||||
/// 'enum' identifier[opt] '{' enumerator-list[opt] '}'
|
||||
/// 'enum' identifier[opt] '{' enumerator-list ',' '}'
|
||||
///
|
||||
/// class-specifier:
|
||||
/// class-head '{' member-specification[opt] '}'
|
||||
///
|
||||
/// class-head:
|
||||
/// class-key identifier[opt] base-clause[opt]
|
||||
/// class-key nested-name-specifier identifier base-clause[opt]
|
||||
/// class-key nested-name-specifier[opt] simple-template-id
|
||||
/// base-clause[opt]
|
||||
///
|
||||
/// class-key:
|
||||
/// 'class'
|
||||
/// 'struct'
|
||||
/// 'union'
|
||||
///
|
||||
/// cv-qualifier:
|
||||
/// 'const'
|
||||
/// 'volatile'
|
||||
/// [GNU] restrict
|
||||
///
|
||||
Parser::TentativeParsingResult Parser::isCXXDeclarationSpecifier() {
|
||||
switch (Tok.getKind()) {
|
||||
// decl-specifier:
|
||||
// storage-class-specifier
|
||||
// type-specifier
|
||||
// function-specifier
|
||||
// 'friend'
|
||||
// 'typedef'
|
||||
|
||||
case tok::kw_friend:
|
||||
case tok::kw_typedef:
|
||||
// storage-class-specifier
|
||||
case tok::kw_register:
|
||||
case tok::kw_static:
|
||||
case tok::kw_extern:
|
||||
case tok::kw_mutable:
|
||||
case tok::kw_auto:
|
||||
case tok::kw___thread:
|
||||
// function-specifier
|
||||
case tok::kw_inline:
|
||||
case tok::kw_virtual:
|
||||
case tok::kw_explicit:
|
||||
|
||||
// type-specifier:
|
||||
// simple-type-specifier
|
||||
// class-specifier
|
||||
// enum-specifier
|
||||
// elaborated-type-specifier
|
||||
// typename-specifier
|
||||
// cv-qualifier
|
||||
|
||||
// class-specifier
|
||||
// elaborated-type-specifier
|
||||
case tok::kw_class:
|
||||
case tok::kw_struct:
|
||||
case tok::kw_union:
|
||||
// enum-specifier
|
||||
case tok::kw_enum:
|
||||
// cv-qualifier
|
||||
case tok::kw_const:
|
||||
case tok::kw_volatile:
|
||||
|
||||
// GNU
|
||||
case tok::kw_restrict:
|
||||
case tok::kw__Complex:
|
||||
case tok::kw___attribute:
|
||||
return TPR_true;
|
||||
|
||||
// The ambiguity resides in a simple-type-specifier/typename-specifier
|
||||
// followed by a '('. The '(' could either be the start of:
|
||||
//
|
||||
// direct-declarator:
|
||||
// '(' declarator ')'
|
||||
//
|
||||
// direct-abstract-declarator:
|
||||
// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
|
||||
// exception-specification[opt]
|
||||
// '(' abstract-declarator ')'
|
||||
//
|
||||
// or part of a function-style cast expression:
|
||||
//
|
||||
// simple-type-specifier '(' expression-list[opt] ')'
|
||||
//
|
||||
|
||||
// simple-type-specifier:
|
||||
|
||||
case tok::identifier:
|
||||
if (!Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope))
|
||||
return TPR_false;
|
||||
// FALL THROUGH.
|
||||
|
||||
case tok::kw_char:
|
||||
case tok::kw_wchar_t:
|
||||
case tok::kw_bool:
|
||||
case tok::kw_short:
|
||||
case tok::kw_int:
|
||||
case tok::kw_long:
|
||||
case tok::kw_signed:
|
||||
case tok::kw_unsigned:
|
||||
case tok::kw_float:
|
||||
case tok::kw_double:
|
||||
case tok::kw_void:
|
||||
if (NextToken().is(tok::l_paren))
|
||||
return TPR_ambiguous;
|
||||
|
||||
return TPR_true;
|
||||
|
||||
// GNU typeof support.
|
||||
case tok::kw_typeof: {
|
||||
if (NextToken().isNot(tok::l_paren))
|
||||
return TPR_true;
|
||||
|
||||
TentativeParsingAction PA(*this);
|
||||
|
||||
TentativeParsingResult TPR = TryParseTypeofSpecifier();
|
||||
bool isFollowedByParen = Tok.is(tok::l_paren);
|
||||
|
||||
PA.Revert();
|
||||
|
||||
if (TPR == TPR_error)
|
||||
return TPR_error;
|
||||
|
||||
if (isFollowedByParen)
|
||||
return TPR_ambiguous;
|
||||
|
||||
return TPR_true;
|
||||
}
|
||||
|
||||
default:
|
||||
return TPR_false;
|
||||
}
|
||||
}
|
||||
|
||||
/// [GNU] typeof-specifier:
|
||||
/// 'typeof' '(' expressions ')'
|
||||
/// 'typeof' '(' type-name ')'
|
||||
///
|
||||
Parser::TentativeParsingResult Parser::TryParseTypeofSpecifier() {
|
||||
assert(Tok.is(tok::kw_typeof) && "Expected 'typeof'!");
|
||||
ConsumeToken();
|
||||
|
||||
assert(Tok.is(tok::l_paren) && "Expected '('");
|
||||
// Parse through the parens after 'typeof'.
|
||||
ConsumeParen();
|
||||
if (!SkipUntil(tok::r_paren))
|
||||
return TPR_error;
|
||||
|
||||
return TPR_ambiguous;
|
||||
}
|
||||
|
||||
Parser::TentativeParsingResult Parser::TryParseDeclarationSpecifier() {
|
||||
TentativeParsingResult TPR = isCXXDeclarationSpecifier();
|
||||
if (TPR != TPR_ambiguous)
|
||||
return TPR;
|
||||
|
||||
if (Tok.is(tok::kw_typeof))
|
||||
TryParseTypeofSpecifier();
|
||||
else
|
||||
ConsumeToken();
|
||||
|
||||
assert(Tok.is(tok::l_paren) && "Expected '('!");
|
||||
return TPR_ambiguous;
|
||||
}
|
||||
|
||||
/// isCXXFunctionDeclarator - Disambiguates between a function declarator or
|
||||
/// a constructor-style initializer, when parsing declaration statements.
|
||||
/// Returns true for function declarator and false for constructor-style
|
||||
/// initializer.
|
||||
/// If during the disambiguation process a parsing error is encountered,
|
||||
/// the function returns true to let the declaration parsing code handle it.
|
||||
///
|
||||
/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
|
||||
/// exception-specification[opt]
|
||||
///
|
||||
bool Parser::isCXXFunctionDeclarator() {
|
||||
TentativeParsingAction PA(*this);
|
||||
|
||||
ConsumeParen();
|
||||
TentativeParsingResult TPR = TryParseParameterDeclarationClause();
|
||||
if (TPR == TPR_ambiguous && Tok.isNot(tok::r_paren))
|
||||
TPR = TPR_false;
|
||||
|
||||
PA.Revert();
|
||||
|
||||
// In case of an error, let the declaration parsing code handle it.
|
||||
if (TPR == TPR_error)
|
||||
return true;
|
||||
|
||||
// Function declarator has precedence over constructor-style initializer.
|
||||
if (TPR == TPR_ambiguous)
|
||||
return TPR_true;
|
||||
return TPR == TPR_true;
|
||||
}
|
||||
|
||||
/// parameter-declaration-clause:
|
||||
/// parameter-declaration-list[opt] '...'[opt]
|
||||
/// parameter-declaration-list ',' '...'
|
||||
///
|
||||
/// parameter-declaration-list:
|
||||
/// parameter-declaration
|
||||
/// parameter-declaration-list ',' parameter-declaration
|
||||
///
|
||||
/// parameter-declaration:
|
||||
/// decl-specifier-seq declarator
|
||||
/// decl-specifier-seq declarator '=' assignment-expression
|
||||
/// decl-specifier-seq abstract-declarator[opt]
|
||||
/// decl-specifier-seq abstract-declarator[opt] '=' assignment-expression
|
||||
///
|
||||
Parser::TentativeParsingResult Parser::TryParseParameterDeclarationClause() {
|
||||
|
||||
if (Tok.is(tok::r_paren))
|
||||
return TPR_true;
|
||||
|
||||
// parameter-declaration-list[opt] '...'[opt]
|
||||
// parameter-declaration-list ',' '...'
|
||||
//
|
||||
// parameter-declaration-list:
|
||||
// parameter-declaration
|
||||
// parameter-declaration-list ',' parameter-declaration
|
||||
//
|
||||
while (1) {
|
||||
// '...'[opt]
|
||||
if (Tok.is(tok::ellipsis)) {
|
||||
ConsumeToken();
|
||||
return TPR_true; // '...' is a sign of a function declarator.
|
||||
}
|
||||
|
||||
// decl-specifier-seq
|
||||
TentativeParsingResult TPR = TryParseDeclarationSpecifier();
|
||||
if (TPR != TPR_ambiguous)
|
||||
return TPR;
|
||||
|
||||
// declarator
|
||||
// abstract-declarator[opt]
|
||||
TPR = TryParseDeclarator(true/*mayBeAbstract*/);
|
||||
if (TPR != TPR_ambiguous)
|
||||
return TPR;
|
||||
|
||||
if (Tok.is(tok::equal)) {
|
||||
// '=' assignment-expression
|
||||
// Parse through assignment-expression.
|
||||
tok::TokenKind StopToks[3] ={ tok::comma, tok::ellipsis, tok::r_paren };
|
||||
if (!SkipUntil(StopToks, 3, true/*StopAtSemi*/, true/*DontConsume*/))
|
||||
return TPR_error;
|
||||
}
|
||||
|
||||
if (Tok.is(tok::ellipsis)) {
|
||||
ConsumeToken();
|
||||
return TPR_true; // '...' is a sign of a function declarator.
|
||||
}
|
||||
|
||||
if (Tok.isNot(tok::comma))
|
||||
break;
|
||||
ConsumeToken(); // the comma.
|
||||
}
|
||||
|
||||
return TPR_ambiguous;
|
||||
}
|
||||
|
||||
/// TryParseFunctionDeclarator - We previously determined (using
|
||||
/// isCXXFunctionDeclarator) that we are at a function declarator. Now parse
|
||||
/// through it.
|
||||
///
|
||||
/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
|
||||
/// exception-specification[opt]
|
||||
///
|
||||
/// exception-specification:
|
||||
/// 'throw' '(' type-id-list[opt] ')'
|
||||
///
|
||||
Parser::TentativeParsingResult Parser::TryParseFunctionDeclarator() {
|
||||
assert(Tok.is(tok::l_paren));
|
||||
// Parse through the parens.
|
||||
ConsumeParen();
|
||||
if (!SkipUntil(tok::r_paren))
|
||||
return TPR_error;
|
||||
|
||||
// cv-qualifier-seq
|
||||
while (Tok.is(tok::kw_const) ||
|
||||
Tok.is(tok::kw_volatile) ||
|
||||
Tok.is(tok::kw_restrict) )
|
||||
ConsumeToken();
|
||||
|
||||
// exception-specification
|
||||
if (Tok.is(tok::kw_throw)) {
|
||||
ConsumeToken();
|
||||
if (Tok.isNot(tok::l_paren))
|
||||
return TPR_error;
|
||||
|
||||
// Parse through the parens after 'throw'.
|
||||
ConsumeParen();
|
||||
if (!SkipUntil(tok::r_paren))
|
||||
return TPR_error;
|
||||
}
|
||||
|
||||
return TPR_ambiguous;
|
||||
}
|
||||
|
||||
/// '[' constant-expression[opt] ']'
|
||||
///
|
||||
Parser::TentativeParsingResult Parser::TryParseBracketDeclarator() {
|
||||
ConsumeBracket();
|
||||
if (!SkipUntil(tok::r_square))
|
||||
return TPR_error;
|
||||
|
||||
return TPR_ambiguous;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// RUN: clang -fsyntax-only -verify %s
|
||||
|
||||
void f() {
|
||||
int a;
|
||||
struct S { int m; };
|
||||
typedef S *T;
|
||||
|
||||
// Expressions.
|
||||
T(a)->m = 7;
|
||||
int(a)++; // expected-error {{invalid lvalue in increment/decrement expression}}
|
||||
__extension__ int(a)++; // expected-error {{invalid lvalue in increment/decrement expression}}
|
||||
typeof(int)(a,5)<<a; // expected-error {{function-style cast to a builtin type can only take one argument}}
|
||||
void(a), ++a; // expected-warning {{statement was disambiguated as expression}} expected-warning {{expression result unused}}
|
||||
|
||||
// Declarations.
|
||||
T(*d)(int(p)); // expected-warning {{statement was disambiguated as declaration}} expected-error {{previous definition is here}}
|
||||
T(d)[5]; // expected-warning {{statement was disambiguated as declaration}} expected-error {{redefinition of 'd'}}
|
||||
typeof(int[])(f) = { 1, 2 }; // expected-warning {{statement was disambiguated as declaration}}
|
||||
void(b)(int);
|
||||
}
|
Loading…
Reference in New Issue