From 492eab84ece070ff262b958d0786fa42db9645df Mon Sep 17 00:00:00 2001 From: Brian Wilkerson Date: Wed, 12 Apr 2017 07:27:37 -0700 Subject: [PATCH] Fix parsing of generic function types as return types R=scheglov@google.com Review-Url: https://codereview.chromium.org/2809773005 . --- pkg/analyzer/lib/src/generated/parser.dart | 97 ++++++++++++++++++-- pkg/analyzer/test/generated/parser_test.dart | 66 +++++++++++++ 2 files changed, 153 insertions(+), 10 deletions(-) diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart index 26ee60613be..ea555a569a3 100644 --- a/pkg/analyzer/lib/src/generated/parser.dart +++ b/pkg/analyzer/lib/src/generated/parser.dart @@ -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 [ @@ -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.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.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()) { diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart index bf816452f19..57e81b22d60 100644 --- a/pkg/analyzer/test/generated/parser_test.dart +++ b/pkg/analyzer/test/generated/parser_test.dart @@ -10040,6 +10040,26 @@ class SimpleParserTest extends ParserTestCase { expect(arguments, hasLength(3)); } + void test_parseClassMember_method_gftReturnType() { + createParser(''' +void Function(core.List x) m() => null; +'''); + ClassMember member = parser.parseClassMember('C'); + expect(member, new isInstanceOf()); + expect((member as MethodDeclaration).body, + new isInstanceOf()); + } + + void test_parseClassMember_method_noReturnType() { + createParser(''' +Function(core.List x) m() => null; +'''); + ClassMember member = parser.parseClassMember('C'); + expect(member, new isInstanceOf()); + expect((member as MethodDeclaration).body, + new isInstanceOf()); + } + 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(core.List x) f() => null; +'''); + CompilationUnit unit = parser.parseCompilationUnit2(); + expect(unit, isNotNull); + expect(unit.declarations, hasLength(1)); + } + + void test_parseCompilationUnitMember_function_noReturnType() { + createParser(''' +Function(core.List 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(core.List x) m() => null; +'''); + Statement statement = parser.parseStatement2(); + expect(statement, new isInstanceOf()); + expect( + (statement as FunctionDeclarationStatement) + .functionDeclaration + .functionExpression + .body, + new isInstanceOf()); + } + + void test_parseStatement_function_noReturnType() { + createParser(''' +Function(core.List x) m() => null; +'''); + Statement statement = parser.parseStatement2(); + expect(statement, new isInstanceOf()); + expect( + (statement as FunctionDeclarationStatement) + .functionDeclaration + .functionExpression + .body, + new isInstanceOf()); + } + void test_parseStatements_multiple() { List statements = parseStatements("return; return;", 2); expect(statements, hasLength(2));