Skip to content

Commit 11d081d

Browse files
Dan Rubelcommit-bot@chromium.org
Dan Rubel
authored andcommitted
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 dart-lang/language#110 Change-Id: I42febae9f88f7e4d8b05907988deab97c7a7425c Reviewed-on: https://dart-review.googlesource.com/c/87081 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Dan Rubel <[email protected]>
1 parent 6bf4d2a commit 11d081d

File tree

4 files changed

+115
-22
lines changed

4 files changed

+115
-22
lines changed

pkg/analyzer/test/generated/parser_test.dart

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2013,6 +2013,46 @@ mixin ComplexParserTestMixin implements AbstractParserTestCase {
20132013
expect(elseExpression, new TypeMatcher<SimpleIdentifier>());
20142014
}
20152015

2016+
void test_conditionalExpression_precedence_nullableTypeWithTypeArg1_is() {
2017+
Expression expression = parseExpression('x is String<S> ? (x + y) : z');
2018+
expect(expression, isNotNull);
2019+
expect(expression, new TypeMatcher<ConditionalExpression>());
2020+
ConditionalExpression conditional = expression;
2021+
Expression condition = conditional.condition;
2022+
expect(condition, new TypeMatcher<IsExpression>());
2023+
Expression thenExpression = conditional.thenExpression;
2024+
expect(thenExpression, new TypeMatcher<ParenthesizedExpression>());
2025+
Expression elseExpression = conditional.elseExpression;
2026+
expect(elseExpression, new TypeMatcher<SimpleIdentifier>());
2027+
}
2028+
2029+
void test_conditionalExpression_precedence_nullableTypeWithTypeArg1GFT_is() {
2030+
Expression expression =
2031+
parseExpression('x is String<S> Function() ? (x + y) : z');
2032+
expect(expression, isNotNull);
2033+
expect(expression, new TypeMatcher<ConditionalExpression>());
2034+
ConditionalExpression conditional = expression;
2035+
Expression condition = conditional.condition;
2036+
expect(condition, new TypeMatcher<IsExpression>());
2037+
Expression thenExpression = conditional.thenExpression;
2038+
expect(thenExpression, new TypeMatcher<ParenthesizedExpression>());
2039+
Expression elseExpression = conditional.elseExpression;
2040+
expect(elseExpression, new TypeMatcher<SimpleIdentifier>());
2041+
}
2042+
2043+
void test_conditionalExpression_precedence_nullableTypeWithTypeArg2_is() {
2044+
Expression expression = parseExpression('x is String<S,T> ? (x + y) : z');
2045+
expect(expression, isNotNull);
2046+
expect(expression, new TypeMatcher<ConditionalExpression>());
2047+
ConditionalExpression conditional = expression;
2048+
Expression condition = conditional.condition;
2049+
expect(condition, new TypeMatcher<IsExpression>());
2050+
Expression thenExpression = conditional.thenExpression;
2051+
expect(thenExpression, new TypeMatcher<ParenthesizedExpression>());
2052+
Expression elseExpression = conditional.elseExpression;
2053+
expect(elseExpression, new TypeMatcher<SimpleIdentifier>());
2054+
}
2055+
20162056
void test_constructor_initializer_withParenthesizedExpression() {
20172057
CompilationUnit unit = parseCompilationUnit(r'''
20182058
class C {

pkg/front_end/lib/src/fasta/parser/type_info.dart

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ TypeInfo computeType(final Token token, bool required,
187187
if (isGeneralizedFunctionType(next)) {
188188
// `Function` ...
189189
return new ComplexTypeInfo(token, noTypeParamOrArg)
190-
.computeNoTypeGFT(required);
190+
.computeNoTypeGFT(token, required);
191191
}
192192

193193
// We've seen an identifier.
@@ -262,20 +262,17 @@ TypeInfo computeType(final Token token, bool required,
262262
}
263263

264264
if (optional('?', next)) {
265-
if (required) {
265+
next = next.next;
266+
if (isGeneralizedFunctionType(next)) {
267+
// identifier `?` Function `(`
268+
return new ComplexTypeInfo(token, noTypeParamOrArg)
269+
.computeIdentifierQuestionGFT(required);
270+
} else if (required ||
271+
(looksLikeName(next) &&
272+
isOneOfOrEof(
273+
next.next, const [';', ',', '=', '>', '>=', '>>', '>>>']))) {
266274
// identifier `?`
267275
return simpleNullableType;
268-
} else {
269-
next = next.next;
270-
if (isGeneralizedFunctionType(next)) {
271-
// identifier `?` Function `(`
272-
return simpleNullableType;
273-
} else if (looksLikeName(next) &&
274-
isOneOfOrEof(
275-
next.next, const [';', ',', '=', '>', '>=', '>>', '>>>'])) {
276-
// identifier `?` identifier `=`
277-
return simpleNullableType;
278-
}
279276
}
280277
} else if (required || looksLikeName(next)) {
281278
// identifier identifier

pkg/front_end/lib/src/fasta/parser/type_info_impl.dart

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,9 @@ class ComplexTypeInfo implements TypeInfo {
376376
/// Type arguments were seen during analysis.
377377
final TypeParamOrArgInfo typeArguments;
378378

379+
/// The token before the trailing question mark or `null` if none.
380+
Token beforeQuestionMark;
381+
379382
/// The last token in the type reference.
380383
Token end;
381384

@@ -390,12 +393,18 @@ class ComplexTypeInfo implements TypeInfo {
390393
ComplexTypeInfo(Token beforeStart, this.typeArguments)
391394
: this.start = beforeStart.next;
392395

396+
ComplexTypeInfo._nonNullable(this.start, this.typeArguments, this.end,
397+
this.typeVariableStarters, this.gftHasReturnType);
398+
393399
@override
394400
bool get couldBeExpression => false;
395401

396402
@override
397403
TypeInfo asNonNullableType() {
398-
return this;
404+
return beforeQuestionMark == null
405+
? this
406+
: new ComplexTypeInfo._nonNullable(start, typeArguments,
407+
beforeQuestionMark, typeVariableStarters, gftHasReturnType);
399408
}
400409

401410
@override
@@ -452,7 +461,15 @@ class ComplexTypeInfo implements TypeInfo {
452461
}
453462
}
454463
token = typeArguments.parseArguments(token, parser);
455-
parser.listener.handleType(typeRefOrPrefix, null);
464+
Token questionMark = token.next;
465+
if (optional('?', questionMark) &&
466+
(typeVariableEndGroups.isNotEmpty || beforeQuestionMark != null)) {
467+
// Only consume the `?` if it is part of the complex type
468+
token = questionMark;
469+
} else {
470+
questionMark = null;
471+
}
472+
parser.listener.handleType(typeRefOrPrefix, questionMark);
456473
}
457474
}
458475

@@ -492,10 +509,11 @@ class ComplexTypeInfo implements TypeInfo {
492509

493510
/// Given `Function` non-identifier, compute the type
494511
/// and return the receiver or one of the [TypeInfo] constants.
495-
TypeInfo computeNoTypeGFT(bool required) {
512+
TypeInfo computeNoTypeGFT(Token beforeStart, bool required) {
496513
assert(optional('Function', start));
514+
assert(beforeStart.next == start);
497515

498-
computeRest(start, required);
516+
computeRest(beforeStart, required);
499517
if (gftHasReturnType == null) {
500518
return required ? simpleType : noType;
501519
}
@@ -509,7 +527,7 @@ class ComplexTypeInfo implements TypeInfo {
509527
assert(optional('void', start));
510528
assert(optional('Function', start.next));
511529

512-
computeRest(start.next, required);
530+
computeRest(start, required);
513531
if (gftHasReturnType == null) {
514532
return voidType;
515533
}
@@ -523,21 +541,36 @@ class ComplexTypeInfo implements TypeInfo {
523541
assert(isValidTypeReference(start));
524542
assert(optional('Function', start.next));
525543

526-
computeRest(start.next, required);
544+
computeRest(start, required);
527545
if (gftHasReturnType == null) {
528546
return simpleType;
529547
}
530548
assert(end != null);
531549
return this;
532550
}
533551

552+
/// Given identifier `?` `Function` non-identifier, compute the type
553+
/// and return the receiver or one of the [TypeInfo] constants.
554+
TypeInfo computeIdentifierQuestionGFT(bool required) {
555+
assert(isValidTypeReference(start));
556+
assert(optional('?', start.next));
557+
assert(optional('Function', start.next.next));
558+
559+
computeRest(start, required);
560+
if (gftHasReturnType == null) {
561+
return simpleNullableType;
562+
}
563+
assert(end != null);
564+
return this;
565+
}
566+
534567
/// Given a builtin, return the receiver so that parseType will report
535568
/// an error for the builtin used as a type.
536569
TypeInfo computeBuiltinOrVarAsType(bool required) {
537570
assert(start.type.isBuiltIn || optional('var', start));
538571

539572
end = typeArguments.skip(start);
540-
computeRest(end.next, required);
573+
computeRest(end, required);
541574
assert(end != null);
542575
return this;
543576
}
@@ -550,7 +583,7 @@ class ComplexTypeInfo implements TypeInfo {
550583
assert(typeArguments != noTypeParamOrArg);
551584

552585
end = typeArguments.skip(start);
553-
computeRest(end.next, required);
586+
computeRest(end, required);
554587

555588
if (!required && !looksLikeName(end.next) && gftHasReturnType == null) {
556589
return noType;
@@ -574,7 +607,7 @@ class ComplexTypeInfo implements TypeInfo {
574607
}
575608

576609
end = typeArguments.skip(token);
577-
computeRest(end.next, required);
610+
computeRest(end, required);
578611
if (!required && !looksLikeName(end.next) && gftHasReturnType == null) {
579612
return noType;
580613
}
@@ -583,6 +616,11 @@ class ComplexTypeInfo implements TypeInfo {
583616
}
584617

585618
void computeRest(Token token, bool required) {
619+
if (optional('?', token.next)) {
620+
beforeQuestionMark = token;
621+
end = token = token.next;
622+
}
623+
token = token.next;
586624
while (optional('Function', token)) {
587625
Token typeVariableStart = token;
588626
// TODO(danrubel): Consider caching TypeParamOrArgInfo
@@ -603,9 +641,14 @@ class ComplexTypeInfo implements TypeInfo {
603641
assert(optional(')', token));
604642
gftHasReturnType ??= typeVariableStart != start;
605643
typeVariableStarters = typeVariableStarters.prepend(typeVariableStart);
644+
beforeQuestionMark = null;
606645
end = token;
607646
token = token.next;
608647
}
648+
if (optional('?', token)) {
649+
beforeQuestionMark = end;
650+
end = token;
651+
}
609652
}
610653
}
611654

pkg/front_end/test/fasta/parser/type_info_test.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,19 @@ class TypeInfoTest {
800800
]);
801801
}
802802

803+
void test_computeType_identifierComplex_questionMark() {
804+
expectComplexInfo('C? Function()', required: true, expectedCalls: [
805+
'handleNoTypeVariables (',
806+
'beginFunctionType C',
807+
'handleIdentifier C typeReference',
808+
'handleNoTypeArguments ?',
809+
'handleType C ?',
810+
'beginFormalParameters ( MemberKind.GeneralizedFunctionType',
811+
'endFormalParameters 0 ( ) MemberKind.GeneralizedFunctionType',
812+
'endFunctionType Function null',
813+
]);
814+
}
815+
803816
void test_computeType_identifierTypeArg() {
804817
expectComplexInfo('C<void>', required: true, expectedCalls: [
805818
'handleIdentifier C typeReference',

0 commit comments

Comments
 (0)