Fix parsing of generic function types as return types

R=scheglov@google.com

Review-Url: https://codereview.chromium.org/2809773005 .
This commit is contained in:
Brian Wilkerson 2017-04-12 07:27:37 -07:00
parent 7b5010d440
commit 492eab84ec
2 changed files with 153 additions and 10 deletions

View file

@ -1234,9 +1234,19 @@ class Parser {
CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
Modifiers modifiers = parseModifiers();
Keyword keyword = _currentToken.keyword;
if (keyword == Keyword.VOID) {
TypeName returnType = astFactory.typeName(
astFactory.simpleIdentifier(getAndAdvance()), null);
if (keyword == Keyword.VOID ||
_atGenericFunctionTypeAfterReturnType(_currentToken)) {
TypeAnnotation returnType;
if (keyword == Keyword.VOID) {
if (_atGenericFunctionTypeAfterReturnType(_peek())) {
returnType = parseTypeAnnotation(false);
} else {
returnType = astFactory.typeName(
astFactory.simpleIdentifier(getAndAdvance()), null);
}
} else {
returnType = parseTypeAnnotation(false);
}
keyword = _currentToken.keyword;
Token next = _peek();
bool isFollowedByIdentifier = _tokenMatchesIdentifier(next);
@ -2031,9 +2041,19 @@ class Parser {
} else if (keyword == Keyword.ENUM) {
_validateModifiersForEnum(modifiers);
return parseEnumDeclaration(commentAndMetadata);
} else if (keyword == Keyword.VOID) {
TypeName returnType = astFactory.typeName(
astFactory.simpleIdentifier(getAndAdvance()), null);
} else if (keyword == Keyword.VOID ||
_atGenericFunctionTypeAfterReturnType(_currentToken)) {
TypeAnnotation returnType;
if (keyword == Keyword.VOID) {
if (_atGenericFunctionTypeAfterReturnType(next)) {
returnType = parseTypeAnnotation(false);
} else {
returnType = astFactory.typeName(
astFactory.simpleIdentifier(getAndAdvance()), null);
}
} else {
returnType = parseTypeAnnotation(false);
}
keyword = _currentToken.keyword;
next = _peek();
if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
@ -4019,8 +4039,13 @@ class Parser {
return parseVariableDeclarationStatementAfterMetadata(
commentAndMetadata);
} else if (keyword == Keyword.VOID) {
TypeName returnType = astFactory.typeName(
astFactory.simpleIdentifier(getAndAdvance()), null);
TypeAnnotation returnType;
if (_atGenericFunctionTypeAfterReturnType(_peek())) {
returnType = parseTypeAnnotation(false);
} else {
returnType = astFactory.typeName(
astFactory.simpleIdentifier(getAndAdvance()), null);
}
Token next = _currentToken.next;
if (_matchesIdentifier() &&
next.matchesAny(const <TokenType>[
@ -4101,6 +4126,48 @@ class Parser {
return astFactory
.emptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
}
} else if (_atGenericFunctionTypeAfterReturnType(_currentToken)) {
TypeAnnotation returnType = parseTypeAnnotation(false);
Token next = _currentToken.next;
if (_matchesIdentifier() &&
next.matchesAny(const <TokenType>[
TokenType.OPEN_PAREN,
TokenType.OPEN_CURLY_BRACKET,
TokenType.FUNCTION,
TokenType.LT
])) {
return _parseFunctionDeclarationStatementAfterReturnType(
commentAndMetadata, returnType);
} else {
//
// We have found an error of some kind. Try to recover.
//
if (_matchesIdentifier()) {
if (next.matchesAny(const <TokenType>[
TokenType.EQ,
TokenType.COMMA,
TokenType.SEMICOLON
])) {
//
// We appear to have a variable declaration with a type of "void".
//
_reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType);
return parseVariableDeclarationStatementAfterMetadata(
commentAndMetadata);
}
} else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
//
// We appear to have found an incomplete statement at the end of a
// block. Parse it as a variable declaration.
//
return _parseVariableDeclarationStatementAfterType(
commentAndMetadata, null, returnType);
}
_reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT);
// TODO(brianwilkerson) Recover from this error.
return astFactory
.emptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
}
} else if (_inGenerator && _matchesKeyword(Keyword.YIELD)) {
return parseYieldStatement();
} else if (_inAsync && _matchesKeyword(Keyword.AWAIT)) {
@ -4618,8 +4685,12 @@ class Parser {
*/
TypeAnnotation parseReturnType(bool inExpression) {
if (_currentToken.keyword == Keyword.VOID) {
return astFactory.typeName(
astFactory.simpleIdentifier(getAndAdvance()), null);
if (_atGenericFunctionTypeAfterReturnType(_peek())) {
return parseTypeAnnotation(false);
} else {
return astFactory.typeName(
astFactory.simpleIdentifier(getAndAdvance()), null);
}
} else {
return parseTypeAnnotation(inExpression);
}
@ -5480,6 +5551,9 @@ class Parser {
*/
Token skipReturnType(Token startToken) {
if (_tokenMatchesKeyword(startToken, Keyword.VOID)) {
if (_atGenericFunctionTypeAfterReturnType(_peek())) {
return skipTypeAnnotation(startToken);
}
return startToken.next;
} else {
return skipTypeAnnotation(startToken);
@ -7148,6 +7222,9 @@ class Parser {
}
Keyword keyword = _currentToken.keyword;
if (keyword == Keyword.VOID) {
if (_atGenericFunctionTypeAfterReturnType(_peek())) {
return parseTypeAnnotation(false);
}
return astFactory.typeName(
astFactory.simpleIdentifier(getAndAdvance()), null);
} else if (_matchesIdentifier()) {

View file

@ -10040,6 +10040,26 @@ class SimpleParserTest extends ParserTestCase {
expect(arguments, hasLength(3));
}
void test_parseClassMember_method_gftReturnType() {
createParser('''
void Function<A>(core.List<core.int> x) m() => null;
''');
ClassMember member = parser.parseClassMember('C');
expect(member, new isInstanceOf<MethodDeclaration>());
expect((member as MethodDeclaration).body,
new isInstanceOf<ExpressionFunctionBody>());
}
void test_parseClassMember_method_noReturnType() {
createParser('''
Function<A>(core.List<core.int> x) m() => null;
''');
ClassMember member = parser.parseClassMember('C');
expect(member, new isInstanceOf<MethodDeclaration>());
expect((member as MethodDeclaration).body,
new isInstanceOf<ExpressionFunctionBody>());
}
void test_parseCombinator_hide() {
createParser('hide a;');
Combinator combinator = parser.parseCombinator();
@ -10640,6 +10660,24 @@ void''');
expect(reference.offset, 15);
}
void test_parseCompilationUnitMember_function_gftReturnType() {
createParser('''
void Function<A>(core.List<core.int> x) f() => null;
''');
CompilationUnit unit = parser.parseCompilationUnit2();
expect(unit, isNotNull);
expect(unit.declarations, hasLength(1));
}
void test_parseCompilationUnitMember_function_noReturnType() {
createParser('''
Function<A>(core.List<core.int> x) f() => null;
''');
CompilationUnit unit = parser.parseCompilationUnit2();
expect(unit, isNotNull);
expect(unit.declarations, hasLength(1));
}
void test_parseConfiguration_noOperator_dottedIdentifier() {
createParser("if (a.b) 'c.dart'");
Configuration configuration = parser.parseConfiguration();
@ -11303,6 +11341,34 @@ void''');
expect(typeName.typeArguments, isNull);
}
void test_parseStatement_function_gftReturnType() {
createParser('''
void Function<A>(core.List<core.int> x) m() => null;
''');
Statement statement = parser.parseStatement2();
expect(statement, new isInstanceOf<FunctionDeclarationStatement>());
expect(
(statement as FunctionDeclarationStatement)
.functionDeclaration
.functionExpression
.body,
new isInstanceOf<ExpressionFunctionBody>());
}
void test_parseStatement_function_noReturnType() {
createParser('''
Function<A>(core.List<core.int> x) m() => null;
''');
Statement statement = parser.parseStatement2();
expect(statement, new isInstanceOf<FunctionDeclarationStatement>());
expect(
(statement as FunctionDeclarationStatement)
.functionDeclaration
.functionExpression
.body,
new isInstanceOf<ExpressionFunctionBody>());
}
void test_parseStatements_multiple() {
List<Statement> statements = parseStatements("return; return;", 2);
expect(statements, hasLength(2));