@@ -376,6 +376,9 @@ class ComplexTypeInfo implements TypeInfo {
376
376
/// Type arguments were seen during analysis.
377
377
final TypeParamOrArgInfo typeArguments;
378
378
379
+ /// The token before the trailing question mark or `null` if none.
380
+ Token beforeQuestionMark;
381
+
379
382
/// The last token in the type reference.
380
383
Token end;
381
384
@@ -390,12 +393,18 @@ class ComplexTypeInfo implements TypeInfo {
390
393
ComplexTypeInfo (Token beforeStart, this .typeArguments)
391
394
: this .start = beforeStart.next;
392
395
396
+ ComplexTypeInfo ._nonNullable (this .start, this .typeArguments, this .end,
397
+ this .typeVariableStarters, this .gftHasReturnType);
398
+
393
399
@override
394
400
bool get couldBeExpression => false ;
395
401
396
402
@override
397
403
TypeInfo asNonNullableType () {
398
- return this ;
404
+ return beforeQuestionMark == null
405
+ ? this
406
+ : new ComplexTypeInfo ._nonNullable (start, typeArguments,
407
+ beforeQuestionMark, typeVariableStarters, gftHasReturnType);
399
408
}
400
409
401
410
@override
@@ -452,7 +461,15 @@ class ComplexTypeInfo implements TypeInfo {
452
461
}
453
462
}
454
463
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);
456
473
}
457
474
}
458
475
@@ -492,10 +509,11 @@ class ComplexTypeInfo implements TypeInfo {
492
509
493
510
/// Given `Function` non-identifier, compute the type
494
511
/// and return the receiver or one of the [TypeInfo] constants.
495
- TypeInfo computeNoTypeGFT (bool required ) {
512
+ TypeInfo computeNoTypeGFT (Token beforeStart, bool required ) {
496
513
assert (optional ('Function' , start));
514
+ assert (beforeStart.next == start);
497
515
498
- computeRest (start , required );
516
+ computeRest (beforeStart , required );
499
517
if (gftHasReturnType == null ) {
500
518
return required ? simpleType : noType;
501
519
}
@@ -509,7 +527,7 @@ class ComplexTypeInfo implements TypeInfo {
509
527
assert (optional ('void' , start));
510
528
assert (optional ('Function' , start.next));
511
529
512
- computeRest (start.next , required );
530
+ computeRest (start, required );
513
531
if (gftHasReturnType == null ) {
514
532
return voidType;
515
533
}
@@ -523,21 +541,36 @@ class ComplexTypeInfo implements TypeInfo {
523
541
assert (isValidTypeReference (start));
524
542
assert (optional ('Function' , start.next));
525
543
526
- computeRest (start.next , required );
544
+ computeRest (start, required );
527
545
if (gftHasReturnType == null ) {
528
546
return simpleType;
529
547
}
530
548
assert (end != null );
531
549
return this ;
532
550
}
533
551
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
+
534
567
/// Given a builtin, return the receiver so that parseType will report
535
568
/// an error for the builtin used as a type.
536
569
TypeInfo computeBuiltinOrVarAsType (bool required ) {
537
570
assert (start.type.isBuiltIn || optional ('var' , start));
538
571
539
572
end = typeArguments.skip (start);
540
- computeRest (end.next , required );
573
+ computeRest (end, required );
541
574
assert (end != null );
542
575
return this ;
543
576
}
@@ -550,7 +583,7 @@ class ComplexTypeInfo implements TypeInfo {
550
583
assert (typeArguments != noTypeParamOrArg);
551
584
552
585
end = typeArguments.skip (start);
553
- computeRest (end.next , required );
586
+ computeRest (end, required );
554
587
555
588
if (! required && ! looksLikeName (end.next) && gftHasReturnType == null ) {
556
589
return noType;
@@ -574,7 +607,7 @@ class ComplexTypeInfo implements TypeInfo {
574
607
}
575
608
576
609
end = typeArguments.skip (token);
577
- computeRest (end.next , required );
610
+ computeRest (end, required );
578
611
if (! required && ! looksLikeName (end.next) && gftHasReturnType == null ) {
579
612
return noType;
580
613
}
@@ -583,6 +616,11 @@ class ComplexTypeInfo implements TypeInfo {
583
616
}
584
617
585
618
void computeRest (Token token, bool required ) {
619
+ if (optional ('?' , token.next)) {
620
+ beforeQuestionMark = token;
621
+ end = token = token.next;
622
+ }
623
+ token = token.next;
586
624
while (optional ('Function' , token)) {
587
625
Token typeVariableStart = token;
588
626
// TODO(danrubel): Consider caching TypeParamOrArgInfo
@@ -603,9 +641,14 @@ class ComplexTypeInfo implements TypeInfo {
603
641
assert (optional (')' , token));
604
642
gftHasReturnType ?? = typeVariableStart != start;
605
643
typeVariableStarters = typeVariableStarters.prepend (typeVariableStart);
644
+ beforeQuestionMark = null ;
606
645
end = token;
607
646
token = token.next;
608
647
}
648
+ if (optional ('?' , token)) {
649
+ beforeQuestionMark = end;
650
+ end = token;
651
+ }
609
652
}
610
653
}
611
654
0 commit comments