Remove parser reliance on "<".endGroup/endToken

This replaces calls to lt.endGroup when parsing type arg/param
with calls to computeTypeParamOrArg. This is a step towards
removing lt.endGroup everywhere and making type arg/param
parsing more resiliant and streamlined.

Change-Id: I5b3374769367f601c8910afa18116a2edd3f80c0
Reviewed-on: https://dart-review.googlesource.com/70000
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index 9ea2973..3f2075a 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -1320,9 +1320,11 @@
       next = token.next;
     }
     Token beforeInlineFunctionType;
+    TypeParamOrArgInfo typeParam = noTypeParamOrArg;
     if (optional("<", next)) {
-      Token closer = next.endGroup;
-      if (closer != null) {
+      typeParam = computeTypeParamOrArg(token);
+      if (typeParam != noTypeParamOrArg) {
+        Token closer = typeParam.skip(token);
         if (optional("(", closer.next)) {
           if (varFinalOrConst != null) {
             reportRecoverableError(
@@ -1350,8 +1352,8 @@
 
     Token endInlineFunctionType;
     if (beforeInlineFunctionType != null) {
-      endInlineFunctionType = computeTypeParamOrArg(beforeInlineFunctionType)
-          .parseVariables(beforeInlineFunctionType, this);
+      endInlineFunctionType =
+          typeParam.parseVariables(beforeInlineFunctionType, this);
       listener.beginFunctionTypedFormalParameter(beforeInlineFunctionType.next);
       token = typeInfo.parseType(beforeType, this);
       endInlineFunctionType = parseFormalParametersRequiredOpt(
@@ -2152,17 +2154,15 @@
   }
 
   Token parseMethodTypeVar(Token name) {
+    if (!optional('<', name.next)) {
+      return noTypeParamOrArg.parseVariables(name, this);
+    }
     TypeParamOrArgInfo typeVar = computeTypeParamOrArg(name, true);
-    Token token;
-    if (typeVar == noTypeParamOrArg || name.next.endGroup != null) {
-      token = typeVar.parseVariables(name, this);
-    } else {
+    Token token = typeVar.parseVariables(name, this);
+    if (optional('=', token.next)) {
       // Recovery
-      token = typeVar.parseVariables(name, this);
-      if (optional('=', token.next)) {
-        token = token.next;
-        reportRecoverableErrorWithToken(token, fasta.templateUnexpectedToken);
-      }
+      token = token.next;
+      reportRecoverableErrorWithToken(token, fasta.templateUnexpectedToken);
     }
     return token;
   }
@@ -2642,9 +2642,10 @@
         // Fall through to continue parsing `factory` as an identifier.
       } else if (identical(value, 'operator')) {
         Token next2 = next.next;
+        TypeParamOrArgInfo typeParam = computeTypeParamOrArg(next);
         // `operator` can be used as an identifier as in
         // `int operator<T>()` or `int operator = 2`
-        if (next2.isUserDefinableOperator && next2.endGroup == null) {
+        if (next2.isUserDefinableOperator && typeParam == noTypeParamOrArg) {
           token = parseMethod(
               beforeStart,
               externalToken,
@@ -2916,7 +2917,7 @@
     assert(optional('operator', token));
     Token next = token.next;
     if (next.isUserDefinableOperator) {
-      if (next.endGroup != null) {
+      if (computeTypeParamOrArg(token) != noTypeParamOrArg) {
         // `operator` is being used as an identifier.
         // For example: `int operator<T>(foo) => 0;`
         listener.handleIdentifier(token, IdentifierContext.methodDeclaration);
@@ -2957,11 +2958,13 @@
   }
 
   Token parseFunctionLiteral(
-      final Token start, TypeInfo typeInfo, IdentifierContext context) {
-    Token beforeName = typeInfo.skipType(start);
-    Token name = beforeName.next;
-    assert(name.isIdentifier);
-    Token formals = computeTypeParamOrArg(name).parseVariables(name, this);
+      Token start,
+      Token beforeName,
+      Token name,
+      TypeInfo typeInfo,
+      TypeParamOrArgInfo typeParam,
+      IdentifierContext context) {
+    Token formals = typeParam.parseVariables(name, this);
     listener.beginNamedFunctionExpression(start.next);
     typeInfo.parseType(start, this);
     return parseNamedFunctionRest(beforeName, start.next, formals, true);
@@ -3022,13 +3025,14 @@
     return token;
   }
 
-  Token parseConstructorReference(Token token) {
+  Token parseConstructorReference(Token token, [TypeParamOrArgInfo typeArg]) {
     Token start =
         ensureIdentifier(token, IdentifierContext.constructorReference);
     listener.beginConstructorReference(start);
     token = parseQualifiedRestOpt(
         start, IdentifierContext.constructorReferenceContinuation);
-    token = computeTypeParamOrArg(token).parseArguments(token, this);
+    typeArg ??= computeTypeParamOrArg(token);
+    token = typeArg.parseArguments(token, this);
     Token period = null;
     if (optional('.', token.next)) {
       period = token.next;
@@ -3703,11 +3707,12 @@
       if (identifier.isIdentifier) {
         // Looking at `identifier ('.' identifier)?`.
         if (optional("<", identifier.next)) {
-          BeginToken typeArguments = identifier.next;
-          Token endTypeArguments = typeArguments.endGroup;
-          if (endTypeArguments != null &&
-              optional(".", endTypeArguments.next)) {
-            return parseImplicitCreationExpression(token);
+          TypeParamOrArgInfo typeArg = computeTypeParamOrArg(identifier);
+          if (typeArg != noTypeParamOrArg) {
+            Token endTypeArguments = typeArg.skip(identifier);
+            if (optional(".", endTypeArguments.next)) {
+              return parseImplicitCreationExpression(token, typeArg);
+            }
           }
         }
       }
@@ -4126,10 +4131,23 @@
       return parseSend(token, context);
     }
     TypeInfo typeInfo = computeType(token, false);
-    if (!looksLikeFunctionDeclaration(typeInfo.skipType(token).next)) {
-      return parseSend(token, context);
+    Token beforeName = typeInfo.skipType(token);
+    Token name = beforeName.next;
+    if (name.isIdentifier) {
+      TypeParamOrArgInfo typeParam = computeTypeParamOrArg(name);
+      Token next = name;
+      if (typeParam != noTypeParamOrArg) {
+        next = typeParam.skip(next);
+      }
+      next = next.next;
+      if (optional('(', next)) {
+        if (looksLikeFunctionBody(next.endGroup.next)) {
+          return parseFunctionLiteral(
+              token, beforeName, name, typeInfo, typeParam, context);
+        }
+      }
     }
-    return parseFunctionLiteral(token, typeInfo, context);
+    return parseSend(token, context);
   }
 
   Token parseRequiredArguments(Token token) {
@@ -4158,10 +4176,11 @@
     return token;
   }
 
-  Token parseImplicitCreationExpression(Token token) {
+  Token parseImplicitCreationExpression(
+      Token token, TypeParamOrArgInfo typeArg) {
     Token begin = token;
     listener.beginImplicitCreationExpression(token);
-    token = parseConstructorReference(token);
+    token = parseConstructorReference(token, typeArg);
     token = parseRequiredArguments(token);
     listener.endImplicitCreationExpression(begin);
     return token;
@@ -4519,14 +4538,14 @@
   /// without a return type.
   bool looksLikeLocalFunction(Token token) {
     if (token.isIdentifier) {
-      token = token.next;
-      if (optional('<', token)) {
-        Token closeBrace = token.endGroup;
-        if (closeBrace == null) {
+      if (optional('<', token.next)) {
+        TypeParamOrArgInfo typeParam = computeTypeParamOrArg(token);
+        if (typeParam == noTypeParamOrArg) {
           return false;
         }
-        token = closeBrace.next;
+        token = typeParam.skip(token);
       }
+      token = token.next;
       if (optional('(', token)) {
         token = token.endGroup.next;
         return optional('{', token) ||
@@ -4549,24 +4568,6 @@
         optional('sync', token);
   }
 
-  /// Returns true if [token] could be the start of a function declaration
-  /// without a return type.
-  bool looksLikeFunctionDeclaration(Token token) {
-    if (!token.isIdentifier) {
-      return false;
-    }
-    token = token.next;
-    if (optional('<', token)) {
-      Token closeBrace = token.endGroup;
-      if (closeBrace == null) return false;
-      token = closeBrace.next;
-    }
-    if (optional('(', token)) {
-      return looksLikeFunctionBody(token.endGroup.next);
-    }
-    return false;
-  }
-
   Token parseExpressionStatementOrConstDeclaration(final Token start) {
     Token constToken = start.next;
     assert(optional('const', constToken));
@@ -4651,22 +4652,24 @@
     Token token = typeInfo.skipType(beforeType);
     Token next = token.next;
 
-    if (!onlyParseVariableDeclarationStart && looksLikeLocalFunction(next)) {
-      // Parse a local function declaration.
-      if (varFinalOrConst != null) {
-        reportRecoverableErrorWithToken(
-            varFinalOrConst, fasta.templateExtraneousModifier);
+    if (!onlyParseVariableDeclarationStart) {
+      if (looksLikeLocalFunction(next)) {
+        // Parse a local function declaration.
+        if (varFinalOrConst != null) {
+          reportRecoverableErrorWithToken(
+              varFinalOrConst, fasta.templateExtraneousModifier);
+        }
+        if (!optional('@', start.next)) {
+          listener.beginMetadataStar(start.next);
+          listener.endMetadataStar(0);
+        }
+        Token beforeFormals =
+            computeTypeParamOrArg(next).parseVariables(next, this);
+        listener.beginLocalFunctionDeclaration(start.next);
+        token = typeInfo.parseType(beforeType, this);
+        next = token.next;
+        return parseNamedFunctionRest(token, start.next, beforeFormals, false);
       }
-      if (!optional('@', start.next)) {
-        listener.beginMetadataStar(start.next);
-        listener.endMetadataStar(0);
-      }
-      Token beforeFormals =
-          computeTypeParamOrArg(next).parseVariables(next, this);
-      listener.beginLocalFunctionDeclaration(start.next);
-      token = typeInfo.parseType(beforeType, this);
-      next = token.next;
-      return parseNamedFunctionRest(token, start.next, beforeFormals, false);
     }
 
     if (token == start) {
diff --git a/pkg/front_end/testcases/regress/issue_31155.dart.direct.expect b/pkg/front_end/testcases/regress/issue_31155.dart.direct.expect
index 0f54478..d785e40 100644
--- a/pkg/front_end/testcases/regress/issue_31155.dart.direct.expect
+++ b/pkg/front_end/testcases/regress/issue_31155.dart.direct.expect
@@ -21,19 +21,6 @@
 // pkg/front_end/testcases/regress/issue_31155.dart:11:23: Error: Expected a class member, but got ';'.
 //   var f = Map<A, B> {};
 //                       ^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:11: Error: A function expression can't have a name.
-//   var f = Map<A, B> {};
-//           ^^^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:14: Error: A function declaration needs an explicit list of parameters.
-// Try adding a parameter list to the function declaration.
-//   var f = Map<A, B> {};
-//              ^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:14: Error: Expected a function body, but got '<'.
-//   var f = Map<A, B> {};
-//              ^
 
 library;
 import self as self;
diff --git a/pkg/front_end/testcases/regress/issue_31155.dart.direct.transformed.expect b/pkg/front_end/testcases/regress/issue_31155.dart.direct.transformed.expect
index 0f54478..d785e40 100644
--- a/pkg/front_end/testcases/regress/issue_31155.dart.direct.transformed.expect
+++ b/pkg/front_end/testcases/regress/issue_31155.dart.direct.transformed.expect
@@ -21,19 +21,6 @@
 // pkg/front_end/testcases/regress/issue_31155.dart:11:23: Error: Expected a class member, but got ';'.
 //   var f = Map<A, B> {};
 //                       ^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:11: Error: A function expression can't have a name.
-//   var f = Map<A, B> {};
-//           ^^^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:14: Error: A function declaration needs an explicit list of parameters.
-// Try adding a parameter list to the function declaration.
-//   var f = Map<A, B> {};
-//              ^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:14: Error: Expected a function body, but got '<'.
-//   var f = Map<A, B> {};
-//              ^
 
 library;
 import self as self;
diff --git a/pkg/front_end/testcases/regress/issue_31155.dart.strong.expect b/pkg/front_end/testcases/regress/issue_31155.dart.strong.expect
index f805c54..3b1594e 100644
--- a/pkg/front_end/testcases/regress/issue_31155.dart.strong.expect
+++ b/pkg/front_end/testcases/regress/issue_31155.dart.strong.expect
@@ -21,19 +21,6 @@
 // pkg/front_end/testcases/regress/issue_31155.dart:11:23: Error: Expected a class member, but got ';'.
 //   var f = Map<A, B> {};
 //                       ^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:11: Error: A function expression can't have a name.
-//   var f = Map<A, B> {};
-//           ^^^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:14: Error: A function declaration needs an explicit list of parameters.
-// Try adding a parameter list to the function declaration.
-//   var f = Map<A, B> {};
-//              ^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:14: Error: Expected a function body, but got '<'.
-//   var f = Map<A, B> {};
-//              ^
 
 library;
 import self as self;
diff --git a/pkg/front_end/testcases/regress/issue_31155.dart.strong.transformed.expect b/pkg/front_end/testcases/regress/issue_31155.dart.strong.transformed.expect
index e7de89b..868d241 100644
--- a/pkg/front_end/testcases/regress/issue_31155.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/regress/issue_31155.dart.strong.transformed.expect
@@ -21,19 +21,6 @@
 // pkg/front_end/testcases/regress/issue_31155.dart:11:23: Error: Expected a class member, but got ';'.
 //   var f = Map<A, B> {};
 //                       ^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:11: Error: A function expression can't have a name.
-//   var f = Map<A, B> {};
-//           ^^^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:14: Error: A function declaration needs an explicit list of parameters.
-// Try adding a parameter list to the function declaration.
-//   var f = Map<A, B> {};
-//              ^
-//
-// pkg/front_end/testcases/regress/issue_31155.dart:11:14: Error: Expected a function body, but got '<'.
-//   var f = Map<A, B> {};
-//              ^
 
 library;
 import self as self;