Fix parsing typedefs with <T>=

This is the first CL of several needed to fix typedef parsing
when there is no space between the type parameters and the `=`.

  typedef F<T>= T Function(T);

* Fix some typedef parsing failures
* Add new consts for simple type var ending with ">>" and ">="
* Update computeType and computeTypeParamOrArg to return new consts
* Extract splitGtEq/GtGt/GtGtEq from splitEndGroup
* Hide internal TypeInfo and TypeParamOrArgInfo consts

Change-Id: Ice550f5b15df1f3cad616a2a342644974f14d8ee
Reviewed-on: https://dart-review.googlesource.com/69261
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 0024e05..e7298b6 100644
--- a/pkg/analyzer/test/generated/parser_test.dart
+++ b/pkg/analyzer/test/generated/parser_test.dart
@@ -17917,6 +17917,21 @@
     expect(alias.semicolon, isNotNull);
   }
 
+  void test_parseGenericTypeAlias_typeParameters2() {
+    // The scanner creates a single token for `>=`
+    // then the parser must split it into two separate tokens.
+    createParser('typedef F<T>= T Function(T);');
+    GenericTypeAlias alias = parseFullCompilationUnitMember();
+    expect(alias, isNotNull);
+    assertNoErrors();
+    expect(alias.name, isNotNull);
+    expect(alias.name.name, 'F');
+    expect(alias.typeParameters, isNotNull);
+    expect(alias.equals, isNotNull);
+    expect(alias.functionType, isNotNull);
+    expect(alias.semicolon, isNotNull);
+  }
+
   void test_parseImportDirective_configuration_multiple() {
     createParser("import 'lib/lib.dart' if (a) 'b.dart' if (c) 'd.dart';");
     ImportDirective directive = parseFullDirective();
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index 01b159d..d0e4017 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -1060,7 +1060,7 @@
     TypeInfo typeInfo = computeType(typedefKeyword, false);
     token = typeInfo.skipType(typedefKeyword).next;
     Token equals;
-    TypeParamOrArgInfo typeParam = computeTypeParamOrArg(token);
+    TypeParamOrArgInfo typeParam = computeTypeParamOrArg(token, true);
     if (typeInfo == noType &&
         (token.kind == IDENTIFIER_TOKEN || token.type.isPseudo) &&
         optional('=', typeParam.skip(token).next)) {
diff --git a/pkg/front_end/lib/src/fasta/parser/token_stream_rewriter.dart b/pkg/front_end/lib/src/fasta/parser/token_stream_rewriter.dart
index e3ae092..0c8d710 100644
--- a/pkg/front_end/lib/src/fasta/parser/token_stream_rewriter.dart
+++ b/pkg/front_end/lib/src/fasta/parser/token_stream_rewriter.dart
@@ -14,7 +14,8 @@
         Token,
         TokenType;
 
-import 'util.dart' show optional;
+import 'util.dart'
+    show optional, splitGtEq, splitGtGt, splitGtGtEq, syntheticGt;
 
 /// Provides the capability of inserting tokens into a token stream. This
 /// implementation does this by rewriting the previous token to point to the
@@ -143,20 +144,13 @@
 
     Token gt;
     if (optional('>>', end)) {
-      gt = new SimpleToken(TokenType.GT, end.charOffset, end.precedingComments)
-        ..setNext(new SimpleToken(TokenType.GT, end.charOffset + 1)
-          ..setNext(end.next));
+      gt = splitGtGt(end);
     } else if (optional('>=', end)) {
-      gt = new SimpleToken(TokenType.GT, end.charOffset, end.precedingComments)
-        ..setNext(new SimpleToken(TokenType.EQ, end.charOffset + 1)
-          ..setNext(end.next));
+      gt = splitGtEq(end);
     } else if (optional('>>=', end)) {
-      gt = new SimpleToken(TokenType.GT, end.charOffset, end.precedingComments)
-        ..setNext(new SimpleToken(TokenType.GT, end.charOffset + 1)
-          ..setNext(new SimpleToken(TokenType.EQ, end.charOffset + 2)
-            ..setNext(end.next)));
+      gt = splitGtGtEq(end);
     } else {
-      gt = new SyntheticToken(TokenType.GT, end.charOffset)..setNext(end);
+      gt = syntheticGt(end);
     }
 
     Token token = start;
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 c94e6f4..19d5bd6 100644
--- a/pkg/front_end/lib/src/fasta/parser/type_info.dart
+++ b/pkg/front_end/lib/src/fasta/parser/type_info.dart
@@ -59,6 +59,17 @@
 /// [computeTypeParamOrArg] about a particular group of type arguments
 /// or type parameters.
 abstract class TypeParamOrArgInfo {
+  const TypeParamOrArgInfo();
+
+  /// Return `true` if the receiver represents a single type argument
+  bool get isSimpleTypeArgument => false;
+
+  /// Return the simple type associated with this simple type argument
+  /// or throw an exception if this is not a simple type argument.
+  TypeInfo get typeInfo {
+    throw "Internal error: $runtimeType is not a SimpleTypeArgument.";
+  }
+
   /// Call this function to parse optional type arguments after [token].
   /// This function will call the appropriate event methods on the [Parser]'s
   /// listener to handle the arguments. This may modify the token stream
@@ -82,31 +93,13 @@
 /// there is no type information in the source.
 const TypeInfo noType = const NoType();
 
-/// [VoidType] is a specialized [TypeInfo] returned by [computeType] when
-/// there is a single identifier as the type reference.
-const TypeInfo voidType = const VoidType();
-
-/// [SimpleType] is a specialized [TypeInfo] returned by [computeType]
-/// when there is a single identifier as the type reference.
-const TypeInfo simpleType = const SimpleType();
-
-/// [PrefixedType] is a specialized [TypeInfo] returned by [computeType]
-/// when the type reference is of the form: identifier `.` identifier.
-const TypeInfo prefixedType = const PrefixedType();
-
-/// [SimpleTypeWith1Argument] is a specialized [TypeInfo] returned by
-/// [computeType] when the type reference is of the form:
-/// identifier `<` identifier `>`.
-const TypeInfo simpleTypeWith1Argument = const SimpleTypeWith1Argument();
-
 /// [NoTypeParamOrArg] is a specialized [TypeParamOrArgInfo] returned by
 /// [computeTypeParamOrArg] when no type parameters or arguments are found.
 const TypeParamOrArgInfo noTypeParamOrArg = const NoTypeParamOrArg();
 
-/// [SimpleTypeArgument1] is a specialized [TypeParamOrArgInfo] returned by
-/// [computeTypeParamOrArg] when the type reference is of the form:
-/// `<` identifier `>`.
-const TypeParamOrArgInfo simpleTypeArgument1 = const SimpleTypeArgument1();
+/// [VoidType] is a specialized [TypeInfo] returned by [computeType] when
+/// there is a single identifier as the type reference.
+const TypeInfo voidType = const VoidType();
 
 bool isGeneralizedFunctionType(Token token) {
   return optional('Function', token) &&
@@ -191,13 +184,13 @@
   TypeParamOrArgInfo typeParamOrArg =
       computeTypeParamOrArg(next, inDeclaration, innerEndGroup);
   if (typeParamOrArg != noTypeParamOrArg) {
-    if (typeParamOrArg == simpleTypeArgument1) {
+    if (typeParamOrArg.isSimpleTypeArgument) {
       // We've seen identifier `<` identifier `>`
       next = typeParamOrArg.skip(next).next;
       if (!isGeneralizedFunctionType(next)) {
         if (required || looksLikeName(next)) {
           // identifier `<` identifier `>` identifier
-          return simpleTypeWith1Argument;
+          return typeParamOrArg.typeInfo;
         } else {
           // identifier `<` identifier `>` non-identifier
           return noType;
@@ -272,28 +265,33 @@
 /// has not been split or the first `>` if the `>>` token has been split.
 TypeParamOrArgInfo computeTypeParamOrArg(Token token,
     [bool inDeclaration = false, Token innerEndGroup]) {
-  Token next = token.next;
-  if (!optional('<', next)) {
+  Token beginGroup = token.next;
+  if (!optional('<', beginGroup)) {
     return noTypeParamOrArg;
   }
-  Token endGroup = next.endGroup ?? innerEndGroup;
+  Token endGroup = beginGroup.endGroup ?? innerEndGroup;
   if (endGroup == null) {
-    if (inDeclaration) {
-      // Recovery
-      // Since the leading `<` cannot be part of an expression,
-      // try to more aggressively recover given an unbalanced '<'.
-      return new ComplexTypeParamOrArgInfo(token, inDeclaration)
-          .compute(innerEndGroup);
+    if (!inDeclaration) {
+      return noTypeParamOrArg;
     }
-    return noTypeParamOrArg;
+    // Recovery:
+    // Since the leading `<` cannot be part of an expression, fall through and
+    // try to more aggressively recover given an unbalanced '<'.
   }
-  Token identifier = next.next;
+
   // identifier `<` `void` `>` and `<` `dynamic` `>`
   // are handled by ComplexTypeInfo.
-  if ((identifier.kind == IDENTIFIER_TOKEN || identifier.type.isPseudo) &&
-      identifier.next == endGroup) {
-    return simpleTypeArgument1;
+  Token identifier = beginGroup.next;
+  if ((identifier.kind == IDENTIFIER_TOKEN || identifier.type.isPseudo)) {
+    if (optional('>', identifier.next)) {
+      return simpleTypeArgument1;
+    } else if (optional('>>', identifier.next)) {
+      return simpleTypeArgument1GtGt;
+    } else if (optional('>=', identifier.next)) {
+      return simpleTypeArgument1GtEq;
+    }
   }
+
   // TODO(danrubel): Consider adding additional const for common situations.
   return new ComplexTypeParamOrArgInfo(token, inDeclaration)
       .compute(innerEndGroup);
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 3ec6155..e3bb31c 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
@@ -23,7 +23,50 @@
 
 import 'type_info.dart';
 
-import 'util.dart' show isOneOf, optional, skipMetadata;
+import 'util.dart' show isOneOf, optional, skipMetadata, splitGtEq, splitGtGt;
+
+/// [SimpleType] is a specialized [TypeInfo] returned by [computeType]
+/// when there is a single identifier as the type reference.
+const TypeInfo simpleType = const SimpleType();
+
+/// [PrefixedType] is a specialized [TypeInfo] returned by [computeType]
+/// when the type reference is of the form: identifier `.` identifier.
+const TypeInfo prefixedType = const PrefixedType();
+
+/// [SimpleTypeWith1Argument] is a specialized [TypeInfo] returned by
+/// [computeType] when the type reference is of the form:
+/// identifier `<` identifier `>`.
+const TypeInfo simpleTypeWith1Argument =
+    const SimpleTypeWith1Argument(simpleTypeArgument1);
+
+/// [SimpleTypeWith1Argument] is a specialized [TypeInfo] returned by
+/// [computeType] when the type reference is of the form:
+/// identifier `<` identifier `>=`.
+const TypeInfo simpleTypeWith1ArgumentGtEq =
+    const SimpleTypeWith1Argument(simpleTypeArgument1GtEq);
+
+/// [SimpleTypeWith1Argument] is a specialized [TypeInfo] returned by
+/// [computeType] when the type reference is of the form:
+/// identifier `<` identifier `>>`.
+const TypeInfo simpleTypeWith1ArgumentGtGt =
+    const SimpleTypeWith1Argument(simpleTypeArgument1GtGt);
+
+/// [SimpleTypeArgument1] is a specialized [TypeParamOrArgInfo] returned by
+/// [computeTypeParamOrArg] when the type reference is of the form:
+/// `<` identifier `>`.
+const TypeParamOrArgInfo simpleTypeArgument1 = const SimpleTypeArgument1();
+
+/// [SimpleTypeArgument1] is a specialized [TypeParamOrArgInfo] returned by
+/// [computeTypeParamOrArg] when the type reference is of the form:
+/// `<` identifier `>=`.
+const TypeParamOrArgInfo simpleTypeArgument1GtEq =
+    const SimpleTypeArgument1GtEq();
+
+/// [SimpleTypeArgument1] is a specialized [TypeParamOrArgInfo] returned by
+/// [computeTypeParamOrArg] when the type reference is of the form:
+/// `<` identifier `>>`.
+const TypeParamOrArgInfo simpleTypeArgument1GtGt =
+    const SimpleTypeArgument1GtGt();
 
 /// See documentation on the [noType] const.
 class NoType implements TypeInfo {
@@ -108,7 +151,9 @@
 
 /// See documentation on the [simpleTypeWith1Argument] const.
 class SimpleTypeWith1Argument implements TypeInfo {
-  const SimpleTypeWith1Argument();
+  final TypeParamOrArgInfo typeArg;
+
+  const SimpleTypeWith1Argument(this.typeArg);
 
   @override
   bool get couldBeExpression => false;
@@ -131,7 +176,7 @@
     assert(token.isKeywordOrIdentifier);
     Listener listener = parser.listener;
     listener.handleIdentifier(token, IdentifierContext.typeReference);
-    token = simpleTypeArgument1.parseArguments(token, parser);
+    token = typeArg.parseArguments(token, parser);
     listener.handleType(start, token.next);
     return token;
   }
@@ -140,7 +185,7 @@
   Token skipType(Token token) {
     token = token.next;
     assert(token.isKeywordOrIdentifier);
-    return simpleTypeArgument1.skip(token);
+    return typeArg.skip(token);
   }
 }
 
@@ -470,7 +515,7 @@
 }
 
 /// See [noTypeParamOrArg].
-class NoTypeParamOrArg implements TypeParamOrArgInfo {
+class NoTypeParamOrArg extends TypeParamOrArgInfo {
   const NoTypeParamOrArg();
 
   @override
@@ -489,37 +534,43 @@
   Token skip(Token token) => token;
 }
 
-class SimpleTypeArgument1 implements TypeParamOrArgInfo {
+class SimpleTypeArgument1 extends TypeParamOrArgInfo {
   const SimpleTypeArgument1();
 
   @override
+  bool get isSimpleTypeArgument => true;
+
+  @override
+  TypeInfo get typeInfo => simpleTypeWith1Argument;
+
+  @override
   Token parseArguments(Token token, Parser parser) {
-    BeginToken start = token = token.next;
-    assert(optional('<', token));
+    BeginToken beginGroup = token.next;
+    assert(optional('<', beginGroup));
     Listener listener = parser.listener;
-    listener.beginTypeArguments(token);
-    token = simpleType.parseType(token, parser);
-    token = processEndGroup(token, start, parser);
-    parser.listener.endTypeArguments(1, start, token);
+    listener.beginTypeArguments(beginGroup);
+    token = simpleType.parseType(beginGroup, parser);
+    token = updateEndGroup(beginGroup, token);
+    parser.listener.endTypeArguments(1, beginGroup, token);
     return token;
   }
 
   @override
   Token parseVariables(Token token, Parser parser) {
-    BeginToken start = token = token.next;
-    assert(optional('<', token));
+    BeginToken beginGroup = token.next;
+    assert(optional('<', beginGroup));
     Listener listener = parser.listener;
-    listener.beginTypeVariables(token);
-    token = token.next;
+    listener.beginTypeVariables(beginGroup);
+    token = beginGroup.next;
     listener.beginMetadataStar(token);
     listener.endMetadataStar(0);
     listener.handleIdentifier(token, IdentifierContext.typeVariableDeclaration);
     listener.beginTypeVariable(token);
     listener.handleTypeVariablesDefined(token, 1);
     listener.handleNoType(token);
-    token = processEndGroup(token, start, parser);
+    token = updateEndGroup(beginGroup, token);
     listener.endTypeVariable(token, 0, null);
-    listener.endTypeVariables(start, token);
+    listener.endTypeVariables(beginGroup, token);
     return token;
   }
 
@@ -527,15 +578,68 @@
   Token skip(Token token) {
     token = token.next;
     assert(optional('<', token));
-    assert(token.endGroup != null ||
-        (optional('>', token.next.next) || optional('>>', token.next.next)));
-    return (optional('>', token.endGroup ?? token.next.next)
-        ? token.next.next
-        : token.next);
+    token = token.next;
+    assert(token.isKeywordOrIdentifier);
+    return skipEndGroup(token);
+  }
+
+  Token skipEndGroup(Token token) {
+    token = token.next;
+    assert(optional('>', token));
+    return token;
+  }
+
+  Token updateEndGroup(BeginToken beginGroup, Token token) {
+    token = token.next;
+    assert(optional('>', token));
+    beginGroup.endGroup = token;
+    return token;
   }
 }
 
-class ComplexTypeParamOrArgInfo implements TypeParamOrArgInfo {
+class SimpleTypeArgument1GtEq extends SimpleTypeArgument1 {
+  const SimpleTypeArgument1GtEq();
+
+  @override
+  TypeInfo get typeInfo => simpleTypeWith1ArgumentGtEq;
+
+  Token skipEndGroup(Token token) {
+    token = token.next;
+    assert(optional('>=', token));
+    return splitGtEq(token);
+  }
+
+  Token updateEndGroup(BeginToken beginGroup, Token beforeEndGroup) {
+    Token endGroup = beforeEndGroup.next;
+    assert(optional('>=', endGroup));
+    beginGroup.endGroup = endGroup = splitGtEq(endGroup);
+    beforeEndGroup.setNext(endGroup);
+    return endGroup;
+  }
+}
+
+class SimpleTypeArgument1GtGt extends SimpleTypeArgument1 {
+  const SimpleTypeArgument1GtGt();
+
+  @override
+  TypeInfo get typeInfo => simpleTypeWith1ArgumentGtGt;
+
+  Token skipEndGroup(Token token) {
+    assert(optional('>>', token.next));
+    return token;
+  }
+
+  Token updateEndGroup(BeginToken beginGroup, Token beforeEndGroup) {
+    Token endGroup = beforeEndGroup.next;
+    if (!optional('>', endGroup)) {
+      endGroup = splitGtGt(endGroup);
+    }
+    beginGroup.endGroup = endGroup;
+    return endGroup;
+  }
+}
+
+class ComplexTypeParamOrArgInfo extends TypeParamOrArgInfo {
   /// The first token in the type var.
   final BeginToken start;
 
diff --git a/pkg/front_end/lib/src/fasta/parser/util.dart b/pkg/front_end/lib/src/fasta/parser/util.dart
index 4329810..7d57ecd 100644
--- a/pkg/front_end/lib/src/fasta/parser/util.dart
+++ b/pkg/front_end/lib/src/fasta/parser/util.dart
@@ -10,7 +10,8 @@
 
 import '../scanner.dart' show Token;
 
-import '../../scanner/token.dart' show BeginToken;
+import '../../scanner/token.dart'
+    show BeginToken, SimpleToken, SyntheticToken, TokenType;
 
 /// Returns true if [token] is the symbol or keyword [value].
 bool optional(String value, Token token) {
@@ -127,3 +128,44 @@
   }
   return token;
 }
+
+/// Split `>=` into two separate tokens.
+Token splitGtEq(Token token) {
+  assert(optional('>=', token));
+  return new SimpleToken(
+      TokenType.GT, token.charOffset, token.precedingComments)
+    ..setNext(new SimpleToken(TokenType.EQ, token.charOffset + 1)
+      ..setNext(token.next));
+}
+
+/// Split `>>` into two separate tokens.
+SimpleToken splitGtGt(Token token) {
+  assert(optional('>>', token));
+  return new SimpleToken(
+      TokenType.GT, token.charOffset, token.precedingComments)
+    ..setNext(new SimpleToken(TokenType.GT, token.charOffset + 1)
+      ..setNext(token.next));
+}
+
+/// Split `>>=` into three separate tokens.
+Token splitGtGtEq(Token token) {
+  assert(optional('>>=', token));
+  return new SimpleToken(
+      TokenType.GT, token.charOffset, token.precedingComments)
+    ..setNext(new SimpleToken(TokenType.GT, token.charOffset + 1)
+      ..setNext(new SimpleToken(TokenType.EQ, token.charOffset + 2)
+        ..setNext(token.next)));
+}
+
+/// Split `>>=` into two separate tokens... `>` followed by `>=`.
+Token splitGtFromGtGtEq(Token token) {
+  assert(optional('>>=', token));
+  return new SimpleToken(
+      TokenType.GT, token.charOffset, token.precedingComments)
+    ..setNext(new SimpleToken(TokenType.GT_EQ, token.charOffset + 1)
+      ..setNext(token.next));
+}
+
+Token syntheticGt(Token token) {
+  return new SyntheticToken(TokenType.GT, token.charOffset)..setNext(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 6cde453..453b0b0 100644
--- a/pkg/front_end/test/fasta/parser/type_info_test.dart
+++ b/pkg/front_end/test/fasta/parser/type_info_test.dart
@@ -580,7 +580,7 @@
 
   void test_computeType_nested() {
     expectNestedInfo(simpleType, '<T>');
-    expectNestedInfo(simpleTypeWith1Argument, '<T<S>>');
+    expectNestedInfo(simpleTypeWith1ArgumentGtGt, '<T<S>>');
     expectNestedComplexInfo('<T<S,R>>');
     expectNestedComplexInfo('<T<S Function()>>');
     expectNestedComplexInfo('<T<S Function()>>');
@@ -837,7 +837,18 @@
     Token t = start.next.next;
     expect(t.next.lexeme, '>>');
 
-    expect(simpleTypeArgument1.skip(start), t);
+    expect(simpleTypeArgument1GtGt.skip(start), t);
+  }
+
+  void test_simple_skip3() {
+    final Token start = scanString('before <T>= after').tokens;
+    Token t = start.next.next;
+    expect(t.next.lexeme, '>=');
+
+    Token skip = simpleTypeArgument1GtEq.skip(start);
+    expect(skip.lexeme, '>');
+    expect(skip.next.lexeme, '=');
+    expect(skip.next.next, t.next.next);
   }
 
   void test_simple_parseArguments() {
@@ -891,6 +902,10 @@
     expectTypeParamOrArg(simpleTypeArgument1, '<T>');
   }
 
+  void test_computeTypeParamOrArg_simple2() {
+    expectTypeParamOrArg(simpleTypeArgument1GtEq, '<T>=', inDeclaration: true);
+  }
+
   void test_computeTypeParamOrArg_simple_nested() {
     String source = '<C<T>>';
     Token start = scan(source).next.next;
@@ -899,7 +914,7 @@
     expect(gtgt.lexeme, '>>');
 
     TypeParamOrArgInfo typeVarInfo = computeTypeParamOrArg(start, false, gtgt);
-    expect(typeVarInfo, simpleTypeArgument1, reason: source);
+    expect(typeVarInfo, simpleTypeArgument1GtGt, reason: source);
   }
 
   void test_computeTypeArg_complex() {