Skip to content

Commit

Permalink
Add support for prefixed nullable type
Browse files Browse the repository at this point in the history
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 dart-lang/language#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>
  • Loading branch information
danrubel authored and commit-bot@chromium.org committed Dec 13, 2018
1 parent b9463d0 commit 7720689
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 13 deletions.
29 changes: 29 additions & 0 deletions pkg/analyzer/test/generated/parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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>());
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);
Expand Down
32 changes: 19 additions & 13 deletions pkg/front_end/lib/src/fasta/parser/type_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down
11 changes: 11 additions & 0 deletions pkg/front_end/lib/src/fasta/parser/type_info_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import 'type_info.dart';

import 'util.dart'
show
isOneOfOrEof,
optional,
skipMetadata,
splitGtEq,
Expand Down Expand Up @@ -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 {
Expand Down
33 changes: 33 additions & 0 deletions pkg/front_end/test/fasta/parser/type_info_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<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',
Expand Down

0 comments on commit 7720689

Please sign in to comment.