mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
Add support for simple nullable type return value in generalized function type
This only supports nullable return values of the form <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: I42febae9f88f7e4d8b05907988deab97c7a7425c Reviewed-on: https://dart-review.googlesource.com/c/87081 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Dan Rubel <danrubel@google.com>
This commit is contained in:
parent
6bf4d2ab1a
commit
11d081d115
4 changed files with 115 additions and 22 deletions
|
@ -2013,6 +2013,46 @@ mixin ComplexParserTestMixin implements AbstractParserTestCase {
|
|||
expect(elseExpression, new TypeMatcher<SimpleIdentifier>());
|
||||
}
|
||||
|
||||
void test_conditionalExpression_precedence_nullableTypeWithTypeArg1_is() {
|
||||
Expression expression = parseExpression('x is String<S> ? (x + y) : z');
|
||||
expect(expression, isNotNull);
|
||||
expect(expression, new TypeMatcher<ConditionalExpression>());
|
||||
ConditionalExpression conditional = expression;
|
||||
Expression condition = conditional.condition;
|
||||
expect(condition, new TypeMatcher<IsExpression>());
|
||||
Expression thenExpression = conditional.thenExpression;
|
||||
expect(thenExpression, new TypeMatcher<ParenthesizedExpression>());
|
||||
Expression elseExpression = conditional.elseExpression;
|
||||
expect(elseExpression, new TypeMatcher<SimpleIdentifier>());
|
||||
}
|
||||
|
||||
void test_conditionalExpression_precedence_nullableTypeWithTypeArg1GFT_is() {
|
||||
Expression expression =
|
||||
parseExpression('x is String<S> Function() ? (x + y) : z');
|
||||
expect(expression, isNotNull);
|
||||
expect(expression, new TypeMatcher<ConditionalExpression>());
|
||||
ConditionalExpression conditional = expression;
|
||||
Expression condition = conditional.condition;
|
||||
expect(condition, new TypeMatcher<IsExpression>());
|
||||
Expression thenExpression = conditional.thenExpression;
|
||||
expect(thenExpression, new TypeMatcher<ParenthesizedExpression>());
|
||||
Expression elseExpression = conditional.elseExpression;
|
||||
expect(elseExpression, new TypeMatcher<SimpleIdentifier>());
|
||||
}
|
||||
|
||||
void test_conditionalExpression_precedence_nullableTypeWithTypeArg2_is() {
|
||||
Expression expression = parseExpression('x is String<S,T> ? (x + y) : z');
|
||||
expect(expression, isNotNull);
|
||||
expect(expression, new TypeMatcher<ConditionalExpression>());
|
||||
ConditionalExpression conditional = expression;
|
||||
Expression condition = conditional.condition;
|
||||
expect(condition, new TypeMatcher<IsExpression>());
|
||||
Expression thenExpression = conditional.thenExpression;
|
||||
expect(thenExpression, new TypeMatcher<ParenthesizedExpression>());
|
||||
Expression elseExpression = conditional.elseExpression;
|
||||
expect(elseExpression, new TypeMatcher<SimpleIdentifier>());
|
||||
}
|
||||
|
||||
void test_constructor_initializer_withParenthesizedExpression() {
|
||||
CompilationUnit unit = parseCompilationUnit(r'''
|
||||
class C {
|
||||
|
|
|
@ -187,7 +187,7 @@ TypeInfo computeType(final Token token, bool required,
|
|||
if (isGeneralizedFunctionType(next)) {
|
||||
// `Function` ...
|
||||
return new ComplexTypeInfo(token, noTypeParamOrArg)
|
||||
.computeNoTypeGFT(required);
|
||||
.computeNoTypeGFT(token, required);
|
||||
}
|
||||
|
||||
// We've seen an identifier.
|
||||
|
@ -262,20 +262,17 @@ TypeInfo computeType(final Token token, bool required,
|
|||
}
|
||||
|
||||
if (optional('?', next)) {
|
||||
if (required) {
|
||||
next = next.next;
|
||||
if (isGeneralizedFunctionType(next)) {
|
||||
// identifier `?` Function `(`
|
||||
return new ComplexTypeInfo(token, noTypeParamOrArg)
|
||||
.computeIdentifierQuestionGFT(required);
|
||||
} else if (required ||
|
||||
(looksLikeName(next) &&
|
||||
isOneOfOrEof(
|
||||
next.next, const [';', ',', '=', '>', '>=', '>>', '>>>']))) {
|
||||
// identifier `?`
|
||||
return simpleNullableType;
|
||||
} else {
|
||||
next = next.next;
|
||||
if (isGeneralizedFunctionType(next)) {
|
||||
// identifier `?` Function `(`
|
||||
return simpleNullableType;
|
||||
} else if (looksLikeName(next) &&
|
||||
isOneOfOrEof(
|
||||
next.next, const [';', ',', '=', '>', '>=', '>>', '>>>'])) {
|
||||
// identifier `?` identifier `=`
|
||||
return simpleNullableType;
|
||||
}
|
||||
}
|
||||
} else if (required || looksLikeName(next)) {
|
||||
// identifier identifier
|
||||
|
|
|
@ -376,6 +376,9 @@ class ComplexTypeInfo implements TypeInfo {
|
|||
/// Type arguments were seen during analysis.
|
||||
final TypeParamOrArgInfo typeArguments;
|
||||
|
||||
/// The token before the trailing question mark or `null` if none.
|
||||
Token beforeQuestionMark;
|
||||
|
||||
/// The last token in the type reference.
|
||||
Token end;
|
||||
|
||||
|
@ -390,12 +393,18 @@ class ComplexTypeInfo implements TypeInfo {
|
|||
ComplexTypeInfo(Token beforeStart, this.typeArguments)
|
||||
: this.start = beforeStart.next;
|
||||
|
||||
ComplexTypeInfo._nonNullable(this.start, this.typeArguments, this.end,
|
||||
this.typeVariableStarters, this.gftHasReturnType);
|
||||
|
||||
@override
|
||||
bool get couldBeExpression => false;
|
||||
|
||||
@override
|
||||
TypeInfo asNonNullableType() {
|
||||
return this;
|
||||
return beforeQuestionMark == null
|
||||
? this
|
||||
: new ComplexTypeInfo._nonNullable(start, typeArguments,
|
||||
beforeQuestionMark, typeVariableStarters, gftHasReturnType);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -452,7 +461,15 @@ class ComplexTypeInfo implements TypeInfo {
|
|||
}
|
||||
}
|
||||
token = typeArguments.parseArguments(token, parser);
|
||||
parser.listener.handleType(typeRefOrPrefix, null);
|
||||
Token questionMark = token.next;
|
||||
if (optional('?', questionMark) &&
|
||||
(typeVariableEndGroups.isNotEmpty || beforeQuestionMark != null)) {
|
||||
// Only consume the `?` if it is part of the complex type
|
||||
token = questionMark;
|
||||
} else {
|
||||
questionMark = null;
|
||||
}
|
||||
parser.listener.handleType(typeRefOrPrefix, questionMark);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -492,10 +509,11 @@ class ComplexTypeInfo implements TypeInfo {
|
|||
|
||||
/// Given `Function` non-identifier, compute the type
|
||||
/// and return the receiver or one of the [TypeInfo] constants.
|
||||
TypeInfo computeNoTypeGFT(bool required) {
|
||||
TypeInfo computeNoTypeGFT(Token beforeStart, bool required) {
|
||||
assert(optional('Function', start));
|
||||
assert(beforeStart.next == start);
|
||||
|
||||
computeRest(start, required);
|
||||
computeRest(beforeStart, required);
|
||||
if (gftHasReturnType == null) {
|
||||
return required ? simpleType : noType;
|
||||
}
|
||||
|
@ -509,7 +527,7 @@ class ComplexTypeInfo implements TypeInfo {
|
|||
assert(optional('void', start));
|
||||
assert(optional('Function', start.next));
|
||||
|
||||
computeRest(start.next, required);
|
||||
computeRest(start, required);
|
||||
if (gftHasReturnType == null) {
|
||||
return voidType;
|
||||
}
|
||||
|
@ -523,7 +541,7 @@ class ComplexTypeInfo implements TypeInfo {
|
|||
assert(isValidTypeReference(start));
|
||||
assert(optional('Function', start.next));
|
||||
|
||||
computeRest(start.next, required);
|
||||
computeRest(start, required);
|
||||
if (gftHasReturnType == null) {
|
||||
return simpleType;
|
||||
}
|
||||
|
@ -531,13 +549,28 @@ class ComplexTypeInfo implements TypeInfo {
|
|||
return this;
|
||||
}
|
||||
|
||||
/// Given identifier `?` `Function` non-identifier, compute the type
|
||||
/// and return the receiver or one of the [TypeInfo] constants.
|
||||
TypeInfo computeIdentifierQuestionGFT(bool required) {
|
||||
assert(isValidTypeReference(start));
|
||||
assert(optional('?', start.next));
|
||||
assert(optional('Function', start.next.next));
|
||||
|
||||
computeRest(start, required);
|
||||
if (gftHasReturnType == null) {
|
||||
return simpleNullableType;
|
||||
}
|
||||
assert(end != null);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Given a builtin, return the receiver so that parseType will report
|
||||
/// an error for the builtin used as a type.
|
||||
TypeInfo computeBuiltinOrVarAsType(bool required) {
|
||||
assert(start.type.isBuiltIn || optional('var', start));
|
||||
|
||||
end = typeArguments.skip(start);
|
||||
computeRest(end.next, required);
|
||||
computeRest(end, required);
|
||||
assert(end != null);
|
||||
return this;
|
||||
}
|
||||
|
@ -550,7 +583,7 @@ class ComplexTypeInfo implements TypeInfo {
|
|||
assert(typeArguments != noTypeParamOrArg);
|
||||
|
||||
end = typeArguments.skip(start);
|
||||
computeRest(end.next, required);
|
||||
computeRest(end, required);
|
||||
|
||||
if (!required && !looksLikeName(end.next) && gftHasReturnType == null) {
|
||||
return noType;
|
||||
|
@ -574,7 +607,7 @@ class ComplexTypeInfo implements TypeInfo {
|
|||
}
|
||||
|
||||
end = typeArguments.skip(token);
|
||||
computeRest(end.next, required);
|
||||
computeRest(end, required);
|
||||
if (!required && !looksLikeName(end.next) && gftHasReturnType == null) {
|
||||
return noType;
|
||||
}
|
||||
|
@ -583,6 +616,11 @@ class ComplexTypeInfo implements TypeInfo {
|
|||
}
|
||||
|
||||
void computeRest(Token token, bool required) {
|
||||
if (optional('?', token.next)) {
|
||||
beforeQuestionMark = token;
|
||||
end = token = token.next;
|
||||
}
|
||||
token = token.next;
|
||||
while (optional('Function', token)) {
|
||||
Token typeVariableStart = token;
|
||||
// TODO(danrubel): Consider caching TypeParamOrArgInfo
|
||||
|
@ -603,9 +641,14 @@ class ComplexTypeInfo implements TypeInfo {
|
|||
assert(optional(')', token));
|
||||
gftHasReturnType ??= typeVariableStart != start;
|
||||
typeVariableStarters = typeVariableStarters.prepend(typeVariableStart);
|
||||
beforeQuestionMark = null;
|
||||
end = token;
|
||||
token = token.next;
|
||||
}
|
||||
if (optional('?', token)) {
|
||||
beforeQuestionMark = end;
|
||||
end = token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -800,6 +800,19 @@ class TypeInfoTest {
|
|||
]);
|
||||
}
|
||||
|
||||
void test_computeType_identifierComplex_questionMark() {
|
||||
expectComplexInfo('C? Function()', required: true, expectedCalls: [
|
||||
'handleNoTypeVariables (',
|
||||
'beginFunctionType C',
|
||||
'handleIdentifier C typeReference',
|
||||
'handleNoTypeArguments ?',
|
||||
'handleType C ?',
|
||||
'beginFormalParameters ( MemberKind.GeneralizedFunctionType',
|
||||
'endFormalParameters 0 ( ) MemberKind.GeneralizedFunctionType',
|
||||
'endFunctionType Function null',
|
||||
]);
|
||||
}
|
||||
|
||||
void test_computeType_identifierTypeArg() {
|
||||
expectComplexInfo('C<void>', required: true, expectedCalls: [
|
||||
'handleIdentifier C typeReference',
|
||||
|
|
Loading…
Reference in a new issue