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>
diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart
index 8b8b575..b1dd26d 100644
--- a/pkg/analyzer/test/generated/parser_test.dart
+++ b/pkg/analyzer/test/generated/parser_test.dart
@@ -2013,6 +2013,46 @@
     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 {
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 4dc7e66..5e27b8c 100644
--- a/pkg/front_end/lib/src/fasta/parser/type_info.dart
+++ b/pkg/front_end/lib/src/fasta/parser/type_info.dart
@@ -187,7 +187,7 @@
   if (isGeneralizedFunctionType(next)) {
     // `Function` ...
     return new ComplexTypeInfo(token, noTypeParamOrArg)
-        .computeNoTypeGFT(required);
+        .computeNoTypeGFT(token, required);
   }
 
   // We've seen an identifier.
@@ -262,20 +262,17 @@
   }
 
   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
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 2b87c5b..41df19b 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
@@ -376,6 +376,9 @@
   /// 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 @@
   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 @@
           }
         }
         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 @@
 
   /// 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 @@
     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 @@
     assert(isValidTypeReference(start));
     assert(optional('Function', start.next));
 
-    computeRest(start.next, required);
+    computeRest(start, required);
     if (gftHasReturnType == null) {
       return simpleType;
     }
@@ -531,13 +549,28 @@
     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 @@
     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 @@
     }
 
     end = typeArguments.skip(token);
-    computeRest(end.next, required);
+    computeRest(end, required);
     if (!required && !looksLikeName(end.next) && gftHasReturnType == null) {
       return noType;
     }
@@ -583,6 +616,11 @@
   }
 
   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 @@
       assert(optional(')', token));
       gftHasReturnType ??= typeVariableStart != start;
       typeVariableStarters = typeVariableStarters.prepend(typeVariableStart);
+      beforeQuestionMark = null;
       end = token;
       token = token.next;
     }
+    if (optional('?', token)) {
+      beforeQuestionMark = end;
+      end = token;
+    }
   }
 }
 
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 7b9b003..64675bb 100644
--- a/pkg/front_end/test/fasta/parser/type_info_test.dart
+++ b/pkg/front_end/test/fasta/parser/type_info_test.dart
@@ -800,6 +800,19 @@
         ]);
   }
 
+  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',