From 7720689931c2eb579afe8ffbfba036854a243e24 Mon Sep 17 00:00:00 2001 From: danrubel Date: Thu, 13 Dec 2018 23:24:04 +0000 Subject: [PATCH] Add support for prefixed nullable type This adds support for nullable types of the form '.' '?' and '.' '?' 'Function' '(' ... This is an increment CL in the ongoing effort to add nullable type support as outlined in https://github.com/dart-lang/language/issues/110 Change-Id: I526aecbe64bacbd442cea0b4c52d36ff23b0443b Reviewed-on: https://dart-review.googlesource.com/c/87083 Reviewed-by: Brian Wilkerson Commit-Queue: Dan Rubel --- pkg/analyzer/test/generated/parser_test.dart | 29 ++++++++++++++++ .../lib/src/fasta/parser/type_info.dart | 32 ++++++++++-------- .../lib/src/fasta/parser/type_info_impl.dart | 11 +++++++ .../test/fasta/parser/type_info_test.dart | 33 +++++++++++++++++++ 4 files changed, 92 insertions(+), 13 deletions(-) diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart index b1dd26d96e3a..b3e24df48142 100644 --- a/pkg/analyzer/test/generated/parser_test.dart +++ b/pkg/analyzer/test/generated/parser_test.dart @@ -1987,6 +1987,35 @@ mixin ComplexParserTestMixin implements AbstractParserTestCase { BinaryExpression, expression.condition); } + void test_conditionalExpression_prefixedValue() { + ExpressionStatement statement = parseStatement('a.b ? y : z;'); + ConditionalExpression expression = statement.expression; + EngineTestCase.assertInstanceOf((obj) => obj is PrefixedIdentifier, + PrefixedIdentifier, expression.condition); + } + + void test_conditionalExpression_prefixedValue2() { + ExpressionStatement statement = parseStatement('a.b ? x.y : z;'); + ConditionalExpression expression = statement.expression; + EngineTestCase.assertInstanceOf((obj) => obj is PrefixedIdentifier, + PrefixedIdentifier, expression.condition); + EngineTestCase.assertInstanceOf((obj) => obj is PrefixedIdentifier, + PrefixedIdentifier, expression.thenExpression); + } + + void test_conditionalExpression_precedence_prefixedNullableType_as() { + Expression expression = parseExpression('x as p.A ? (x + y) : z'); + expect(expression, isNotNull); + expect(expression, new TypeMatcher()); + ConditionalExpression conditional = expression; + Expression condition = conditional.condition; + expect(condition, new TypeMatcher()); + Expression thenExpression = conditional.thenExpression; + expect(thenExpression, new TypeMatcher()); + Expression elseExpression = conditional.elseExpression; + expect(elseExpression, new TypeMatcher()); + } + void test_conditionalExpression_precedence_nullableType_as() { Expression expression = parseExpression('x as String ? (x + y) : z'); expect(expression, isNotNull); diff --git a/pkg/front_end/lib/src/fasta/parser/type_info.dart b/pkg/front_end/lib/src/fasta/parser/type_info.dart index 5e27b8c36bc9..2a5a8f6a3984 100644 --- a/pkg/front_end/lib/src/fasta/parser/type_info.dart +++ b/pkg/front_end/lib/src/fasta/parser/type_info.dart @@ -12,7 +12,7 @@ import 'parser.dart' show Parser; import 'type_info_impl.dart'; -import 'util.dart' show isOneOf, isOneOfOrEof, optional; +import 'util.dart' show isOneOf, optional; /// [TypeInfo] provides information collected by [computeType] /// about a particular type reference. @@ -231,14 +231,23 @@ TypeInfo computeType(final Token token, bool required, // We've seen identifier `.` identifier typeParamOrArg = computeTypeParamOrArg(next, inDeclaration); next = next.next; - if (typeParamOrArg == noTypeParamOrArg && - !isGeneralizedFunctionType(next)) { - if (required || looksLikeName(next)) { - // identifier `.` identifier identifier - return prefixedType; - } else { - // identifier `.` identifier non-identifier - return noType; + if (typeParamOrArg == noTypeParamOrArg) { + if (optional('?', next)) { + next = next.next; + // identifier `.` identifier `?` + if (!required && + !looksLikeVarName(next) && + !isGeneralizedFunctionType(next)) { + return noType; + } + } else if (!isGeneralizedFunctionType(next)) { + if (required || looksLikeName(next)) { + // identifier `.` identifier identifier + return prefixedType; + } else { + // identifier `.` identifier non-identifier + return noType; + } } } // identifier `.` identifier @@ -267,10 +276,7 @@ TypeInfo computeType(final Token token, bool required, // identifier `?` Function `(` return new ComplexTypeInfo(token, noTypeParamOrArg) .computeIdentifierQuestionGFT(required); - } else if (required || - (looksLikeName(next) && - isOneOfOrEof( - next.next, const [';', ',', '=', '>', '>=', '>>', '>>>']))) { + } else if (required || looksLikeVarName(next)) { // identifier `?` return simpleNullableType; } diff --git a/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart b/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart index 41df19b79cdd..323b535447b4 100644 --- a/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart +++ b/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart @@ -26,6 +26,7 @@ import 'type_info.dart'; import 'util.dart' show + isOneOfOrEof, optional, skipMetadata, splitGtEq, @@ -367,6 +368,16 @@ bool looksLikeTypeParamOrArg(bool inDeclaration, Token token) { return false; } +bool looksLikeVarName(Token token) { + return (looksLikeName(token) && + isOneOfOrEof(token.next, const [ + ';', ',', + // TODO(danrubel): Consider refactoring this into TokenType.isAssignment + '=', '&&=', '&=', '||=', '|=', '^=', '>>=', '<<=', '-=', '%=', '+=', + '??=', '/=', '*=', '~/=', + ])); +} + /// Instances of [ComplexTypeInfo] are returned by [computeType] to represent /// type references that cannot be represented by the constants above. class ComplexTypeInfo implements TypeInfo { diff --git a/pkg/front_end/test/fasta/parser/type_info_test.dart b/pkg/front_end/test/fasta/parser/type_info_test.dart index 64675bb3e218..84c5130f8c1f 100644 --- a/pkg/front_end/test/fasta/parser/type_info_test.dart +++ b/pkg/front_end/test/fasta/parser/type_info_test.dart @@ -1013,6 +1013,39 @@ class TypeInfoTest { required: true); } + void test_computeType_prefixedGFT_questionMark() { + expectComplexInfo('C.a? Function(', // Scanner inserts synthetic ')'. + required: true, + expectedCalls: [ + 'handleNoTypeVariables (', + 'beginFunctionType C', + 'handleIdentifier C prefixedTypeReference', + 'handleIdentifier a typeReferenceContinuation', + 'handleQualified .', + 'handleNoTypeArguments ?', + 'handleType C ?', + 'beginFormalParameters ( MemberKind.GeneralizedFunctionType', + 'endFormalParameters 0 ( ) MemberKind.GeneralizedFunctionType', + 'endFunctionType Function null', + ]); + expectComplexInfo('C.a? Function(int x) Function(int x)', + required: false, expectedAfter: 'Function'); + expectComplexInfo('C.a? Function(int x) Function(int x)', + required: true); + } + + void test_computeType_prefixedQuestionMark() { + expectComplexInfo('C.a? Function', + expectedAfter: 'Function', + expectedCalls: [ + 'handleIdentifier C prefixedTypeReference', + 'handleIdentifier a typeReferenceContinuation', + 'handleQualified .', + 'handleNoTypeArguments ?', + 'handleType C ?', + ]); + } + void test_computeType_prefixedTypeArg() { expectComplexInfo('C.a', required: true, expectedCalls: [ 'handleIdentifier C prefixedTypeReference',