Add support for prefixed nullable type
This adds support for nullable types of the form
<identifier> '.' <identifier> '?'
and
<identifier> '.' <identifier> '?' '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 <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart
index b1dd26d..b3e24df 100644
--- a/pkg/analyzer/test/generated/parser_test.dart
+++ b/pkg/analyzer/test/generated/parser_test.dart
@@ -1987,6 +1987,35 @@
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>());
+ ConditionalExpression conditional = expression;
+ Expression condition = conditional.condition;
+ expect(condition, new TypeMatcher<AsExpression>());
+ Expression thenExpression = conditional.thenExpression;
+ expect(thenExpression, new TypeMatcher<ParenthesizedExpression>());
+ Expression elseExpression = conditional.elseExpression;
+ expect(elseExpression, new TypeMatcher<SimpleIdentifier>());
+ }
+
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 5e27b8c..2a5a8f6 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 '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 @@
// 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 @@
// 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 41df19b..323b535 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 'util.dart'
show
+ isOneOfOrEof,
optional,
skipMetadata,
splitGtEq,
@@ -367,6 +368,16 @@
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 64675bb..84c5130 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 @@
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<T>(int x) Function<T>(int x)',
+ required: false, expectedAfter: 'Function');
+ expectComplexInfo('C.a? Function<T>(int x) Function<T>(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<T>', required: true, expectedCalls: [
'handleIdentifier C prefixedTypeReference',