Move the generation of type parameter suggestions

Change-Id: I3ab850747ab9c12360ffcccd80120b7723af8420
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/336840
Reviewed-by: Keerti Parthasarathy <keertip@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/candidate_suggestion.dart b/pkg/analysis_server/lib/src/services/completion/dart/candidate_suggestion.dart
index 25d2dda..319ad80 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/candidate_suggestion.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/candidate_suggestion.dart
@@ -219,6 +219,18 @@
   String get completion => element.name;
 }
 
+/// The information about a candidate suggestion based on a type parameter.
+final class TypeParameterSuggestion extends CandidateSuggestion {
+  /// The element on which the suggestion is based.
+  final TypeParameterElement element;
+
+  /// Initialize a newly created candidate suggestion to suggest the [element].
+  TypeParameterSuggestion(this.element);
+
+  @override
+  String get completion => element.name;
+}
+
 extension SuggestionBuilderExtension on SuggestionBuilder {
   // TODO(brianwilkerson): Move these to `SuggestionBuilder`, possibly as part
   //  of splitting it into a legacy builder and an LSP builder.
@@ -273,6 +285,8 @@
           suggestion.element,
           inheritanceDistance: inheritanceDistance,
         );
+      case TypeParameterSuggestion():
+        suggestTypeParameter(suggestion.element);
     }
   }
 
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/declaration_helper.dart b/pkg/analysis_server/lib/src/services/completion/dart/declaration_helper.dart
index 4277743..7268c9d 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/declaration_helper.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/declaration_helper.dart
@@ -33,12 +33,16 @@
   final bool mustBeConstant;
 
   /// A flag indicating whether suggestions should be limited to only include
+  /// methods with a non-`void` return type.
+  final bool mustBeNonVoid;
+
+  /// A flag indicating whether suggestions should be limited to only include
   /// static members.
   final bool mustBeStatic;
 
   /// A flag indicating whether suggestions should be limited to only include
-  /// methods with a non-`void` return type.
-  final bool mustBeNonVoid;
+  /// types.
+  final bool mustBeType;
 
   /// A flag indicating whether suggestions should be tear-offs rather than
   /// invocations where possible.
@@ -58,8 +62,9 @@
       {required this.collector,
       required this.offset,
       required this.mustBeConstant,
-      required this.mustBeStatic,
       required this.mustBeNonVoid,
+      required this.mustBeStatic,
+      required this.mustBeType,
       required this.preferNonInvocation});
 
   /// Return the suggestion kind that should be used for executable elements.
@@ -108,28 +113,55 @@
 
   /// Add any declarations that are visible at the completion location,
   /// given that the completion location is within the [node]. This includes
-  /// local variables, local functions, and parameters.
+  /// local variables, local functions, parameters, members of the enclosing
+  /// declaration, and top-level declarations in the enclosing library.
   void addLexicalDeclarations(AstNode node) {
-    var containingMember = _addLocalDeclarations(node);
+    var containingMember =
+        mustBeType ? _addLocalTypes(node) : _addLocalDeclarations(node);
     if (containingMember == null) {
       return;
     }
     var parent = containingMember.parent;
-    _addMembersOf(parent, containingMember);
+    if (parent is ClassMember) {
+      assert(node is CommentReference);
+      parent = parent.parent;
+    } else if (parent is CompilationUnit) {
+      parent = containingMember;
+    }
+    if (parent is CompilationUnitMember) {
+      _addMembersOf(parent, containingMember);
+      parent = parent.parent;
+    }
+    if (parent is CompilationUnit) {
+      var library = parent.declaredElement?.library;
+      if (library != null) {
+        _addTopLevelDeclarations(library);
+        _addImportedDeclarations(library);
+      }
+    }
   }
 
   void addMembersOfType(DartType type) {
     // TODO(brianwilkerson): Implement this.
   }
 
+  /// Add suggestions for any top-level declarations that are visible within the
+  /// [library].
+  void _addImportedDeclarations(LibraryElement library) {
+    // TODO(brianwilkerson): Implement this.
+    // for (var importedLibrary in library.importedLibraries) {}
+  }
+
   /// Add suggestions for any local declarations that are visible at the
   /// completion location, given that the completion location is within the
   /// [node].
   ///
-  /// This includes local variables, local functions, and parameters. Return the
-  /// member containing the local declarations that were added, or `null` if
-  /// there is an error such as the AST being malformed or we encountered an AST
-  /// structure that isn't handled correctly.
+  /// This includes local variables, local functions, parameters, and type
+  /// parameters defined on local functions.
+  ///
+  /// Return the member containing the local declarations that were added, or
+  /// `null` if there is an error such as the AST being malformed or we
+  /// encountered an AST structure that isn't handled correctly.
   ///
   /// The returned member can be either a [ClassMember] or a
   /// [CompilationUnitMember].
@@ -169,12 +201,14 @@
           }
         case FunctionExpression():
           _visitParameterList(currentNode.parameters);
+          _visitTypeParameterList(currentNode.typeParameters);
         case IfElement():
           _visitIfElement(currentNode);
         case IfStatement():
           _visitIfStatement(currentNode);
         case MethodDeclaration():
           _visitParameterList(currentNode.parameters);
+          _visitTypeParameterList(currentNode.typeParameters);
           return currentNode;
         case SwitchCase():
           _visitStatements(currentNode.statements, previousNode);
@@ -186,6 +220,8 @@
           _visitSwitchPatternCase(currentNode, previousNode);
         case VariableDeclarationList():
           _visitVariableDeclarationList(currentNode, previousNode);
+        case CompilationUnitMember():
+          return currentNode;
       }
       previousNode = currentNode;
       currentNode = currentNode.parent;
@@ -193,6 +229,47 @@
     return currentNode;
   }
 
+  /// Add suggestions for any local types that are visible at the completion
+  /// location, given that the completion location is within the [node].
+  ///
+  /// This includes only type parameters.
+  ///
+  /// Return the member containing the local declarations that were added, or
+  /// `null` if there is an error such as the AST being malformed or we
+  /// encountered an AST structure that isn't handled correctly.
+  ///
+  /// The returned member can be either a [ClassMember] or a
+  /// [CompilationUnitMember].
+  AstNode? _addLocalTypes(AstNode node) {
+    AstNode? currentNode = node;
+    while (currentNode != null) {
+      switch (currentNode) {
+        case CommentReference():
+          return currentNode;
+        case ConstructorDeclaration():
+          _visitParameterList(currentNode.parameters);
+          return currentNode;
+        case FieldDeclaration():
+          return currentNode;
+        case FunctionDeclaration(:var parent):
+          if (parent is! FunctionDeclarationStatement) {
+            return currentNode;
+          }
+        case FunctionExpression():
+          _visitTypeParameterList(currentNode.typeParameters);
+        case GenericFunctionType():
+          _visitTypeParameterList(currentNode.typeParameters);
+        case MethodDeclaration():
+          _visitTypeParameterList(currentNode.typeParameters);
+          return currentNode;
+        case CompilationUnitMember():
+          return currentNode;
+      }
+      currentNode = currentNode.parent;
+    }
+    return currentNode;
+  }
+
   /// Add suggestions for the [members] of the [containingElement].
   void _addMembers(Element containingElement, NodeList<ClassMember> members) {
     for (var member in members) {
@@ -226,44 +303,65 @@
 
   /// Add suggestions for any members of the [parent].
   ///
-  /// The [parent] is expected to be either a [CompilationUnitMember] or a
-  /// [CompilationUnit]. The [containingMember] is the member within the
-  /// [parent] in which completion was requested.
-  void _addMembersOf(AstNode? parent, AstNode containingMember) {
-    while (parent != null) {
-      switch (parent) {
-        case ClassDeclaration():
-          var classElement = parent.declaredElement;
-          if (classElement != null) {
+  /// The [containingMember] is the member within the [parent] in which
+  /// completion was requested.
+  void _addMembersOf(CompilationUnitMember parent, AstNode containingMember) {
+    switch (parent) {
+      case ClassDeclaration():
+        var classElement = parent.declaredElement;
+        if (classElement != null) {
+          if (!mustBeType) {
             _addMembers(classElement, parent.members);
           }
-        case CompilationUnit():
-          var library = parent.declaredElement?.library;
-          if (library != null) {
-            _addTopLevelDeclarations(library);
-          }
-        case EnumDeclaration():
-          var enumElement = parent.declaredElement;
-          if (enumElement != null) {
+          _suggestTypeParameters(classElement.typeParameters);
+        }
+      case EnumDeclaration():
+        var enumElement = parent.declaredElement;
+        if (enumElement != null) {
+          if (!mustBeType) {
             _addMembers(enumElement, parent.members);
           }
-        case ExtensionDeclaration():
-          var extensionElement = parent.declaredElement;
-          if (extensionElement != null) {
+          _suggestTypeParameters(enumElement.typeParameters);
+        }
+      case ExtensionDeclaration():
+        var extensionElement = parent.declaredElement;
+        if (extensionElement != null) {
+          if (!mustBeType) {
             _addMembers(extensionElement, parent.members);
           }
-        case ExtensionTypeDeclaration():
-          var extensionTypeElement = parent.declaredElement;
-          if (extensionTypeElement != null) {
+          _suggestTypeParameters(extensionElement.typeParameters);
+        }
+      case ExtensionTypeDeclaration():
+        var extensionTypeElement = parent.declaredElement;
+        if (extensionTypeElement != null) {
+          if (!mustBeType) {
             _addMembers(extensionTypeElement, parent.members);
           }
-        case MixinDeclaration():
-          var mixinElement = parent.declaredElement;
-          if (mixinElement != null) {
+          _suggestTypeParameters(extensionTypeElement.typeParameters);
+        }
+      case MixinDeclaration():
+        var mixinElement = parent.declaredElement;
+        if (mixinElement != null) {
+          if (!mustBeType) {
             _addMembers(mixinElement, parent.members);
           }
-      }
-      parent = parent.parent;
+          _suggestTypeParameters(mixinElement.typeParameters);
+        }
+      case ClassTypeAlias():
+        var aliasElement = parent.declaredElement;
+        if (aliasElement != null) {
+          _suggestTypeParameters(aliasElement.typeParameters);
+        }
+      case FunctionTypeAlias():
+        var aliasElement = parent.declaredElement;
+        if (aliasElement != null) {
+          _suggestTypeParameters(aliasElement.typeParameters);
+        }
+      case GenericTypeAlias():
+        var aliasElement = parent.declaredElement;
+        if (aliasElement is TypeAliasElement) {
+          _suggestTypeParameters(aliasElement.typeParameters);
+        }
     }
   }
 
@@ -349,6 +447,21 @@
     }
   }
 
+  /// Add a suggestion for the type parameter represented by the [element].
+  void _suggestTypeParameter(TypeParameterElement element) {
+    if (visibilityTracker.isVisible(element)) {
+      var suggestion = TypeParameterSuggestion(element);
+      collector.addSuggestion(suggestion);
+    }
+  }
+
+  /// Suggest each of the [typeParameters].
+  void _suggestTypeParameters(List<TypeParameterElement> typeParameters) {
+    for (var parameter in typeParameters) {
+      _suggestTypeParameter(parameter);
+    }
+  }
+
   /// Add a suggestion for the local variable represented by the [element].
   void _suggestVariable(LocalVariableElement element) {
     if (mustBeConstant && !element.isConst) {
@@ -379,11 +492,15 @@
       case ConstructorDeclaration():
         _visitParameterList(member.parameters);
       case FunctionDeclaration():
-        _visitParameterList(member.functionExpression.parameters);
+        var functionExpression = member.functionExpression;
+        _visitParameterList(functionExpression.parameters);
+        _visitTypeParameterList(functionExpression.typeParameters);
       case FunctionExpression():
         _visitParameterList(member.parameters);
+        _visitTypeParameterList(member.typeParameters);
       case MethodDeclaration():
         _visitParameterList(member.parameters);
+        _visitTypeParameterList(member.typeParameters);
     }
     return comment;
   }
@@ -567,6 +684,17 @@
     }
   }
 
+  void _visitTypeParameterList(TypeParameterList? typeParameters) {
+    if (typeParameters != null) {
+      for (var typeParameter in typeParameters.typeParameters) {
+        var element = typeParameter.declaredElement;
+        if (element != null) {
+          _suggestTypeParameter(element);
+        }
+      }
+    }
+  }
+
   void _visitVariableDeclarationList(
       VariableDeclarationList node, AstNode? child) {
     var variables = node.variables;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart b/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
index 8e85fcc..ad2e398 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
@@ -102,7 +102,9 @@
   /// Return the helper used to suggest declarations that are in scope.
   DeclarationHelper declarationHelper(
       {bool mustBeConstant = false,
+      bool mustBeNonVoid = false,
       bool mustBeStatic = false,
+      bool mustBeType = false,
       bool preferNonInvocation = false}) {
     // Ensure that we aren't attempting to create multiple declaration helpers
     // with inconsistent states.
@@ -110,7 +112,9 @@
       var helper = _declarationHelper;
       return helper == null ||
           (helper.mustBeConstant == mustBeConstant &&
+              helper.mustBeNonVoid == mustBeNonVoid &&
               helper.mustBeStatic == mustBeStatic &&
+              helper.mustBeType == mustBeType &&
               helper.preferNonInvocation == preferNonInvocation);
     }());
     return _declarationHelper ??= DeclarationHelper(
@@ -118,7 +122,8 @@
         offset: offset,
         mustBeConstant: mustBeConstant,
         mustBeStatic: mustBeStatic,
-        mustBeNonVoid: false,
+        mustBeNonVoid: mustBeNonVoid,
+        mustBeType: mustBeType,
         preferNonInvocation: preferNonInvocation);
   }
 
@@ -279,7 +284,7 @@
       if (offset <= onKeyword.end) {
         keywordHelper.addKeyword(Keyword.ON);
       } else if (catchKeyword != null && offset < catchKeyword.offset) {
-        _forTypeAnnotation();
+        _forTypeAnnotation(node);
       }
     }
     if (catchKeyword != null &&
@@ -309,7 +314,7 @@
     }
     if (offset >= node.leftBracket.end && offset <= node.rightBracket.offset) {
       collector.completionLocation = 'ClassDeclaration_member';
-      _forClassMember();
+      _forClassMember(node);
       var element = node.members.elementBefore(offset);
       if (element is MethodDeclaration) {
         var body = element.body;
@@ -644,7 +649,7 @@
     }
 
     keywordHelper.addFormalParameterKeywords(node);
-    _forTypeAnnotation();
+    _forTypeAnnotation(node);
   }
 
   @override
@@ -708,7 +713,7 @@
     if ((returnType == null || returnType.beginToken == returnType.endToken) &&
         offset <= node.name.offset) {
       collector.completionLocation = 'FunctionDeclaration_returnType';
-      _forTypeAnnotation();
+      _forTypeAnnotation(node);
     }
   }
 
@@ -750,9 +755,9 @@
     var returnType = node.returnType;
     if (returnType != null && offset <= returnType.end) {
       keywordHelper.addFormalParameterKeywords(node.parentFormalParameterList);
-      _forTypeAnnotation();
+      _forTypeAnnotation(node);
     } else if (returnType == null && offset < node.name.offset) {
-      _forTypeAnnotation();
+      _forTypeAnnotation(node);
     }
   }
 
@@ -761,7 +766,7 @@
     if (node.typedefKeyword.coversOffset(offset)) {
       keywordHelper.addKeyword(Keyword.TYPEDEF);
     } else if (offset >= node.equals.end && offset <= node.semicolon.offset) {
-      _forTypeAnnotation();
+      _forTypeAnnotation(node);
     }
   }
 
@@ -879,8 +884,7 @@
     } else if (offset < isOperator.offset) {
       _forExpression(node);
     } else if (offset > isOperator.end) {
-      // TODO(brianwilkerson): Suggest the types available in the current scope.
-      // declarationHelper.addTypes();
+      declarationHelper(mustBeType: true).addLexicalDeclarations(node);
     }
   }
 
@@ -955,7 +959,7 @@
   void visitMethodDeclaration(MethodDeclaration node) {
     if (offset >= node.firstTokenAfterCommentAndMetadata.previous!.offset &&
         offset <= node.name.end) {
-      _forTypeAnnotation();
+      _forTypeAnnotation(node);
       // If the cursor is at the beginning of the declaration, include the class
       // member keywords.  See dartbug.com/41039.
       keywordHelper.addClassMemberKeywords();
@@ -1012,7 +1016,7 @@
 
   @override
   void visitNamedType(NamedType node) {
-    _forTypeAnnotation();
+    _forTypeAnnotation(node);
   }
 
   @override
@@ -1169,16 +1173,16 @@
         if (name.keyword == Keyword.REQUIRED && node.covariantKeyword == null) {
           keywordHelper.addKeyword(Keyword.COVARIANT);
         }
-        _forTypeAnnotation();
+        _forTypeAnnotation(node);
         return;
       } else if (name.isSynthetic) {
         keywordHelper
             .addFormalParameterKeywords(node.parentFormalParameterList);
-        _forTypeAnnotation();
+        _forTypeAnnotation(node);
       } else {
         keywordHelper
             .addFormalParameterKeywords(node.parentFormalParameterList);
-        _forTypeAnnotation();
+        _forTypeAnnotation(node);
       }
     }
     var type = node.type;
@@ -1186,11 +1190,11 @@
       if (type.beginToken.coversOffset(offset)) {
         keywordHelper
             .addFormalParameterKeywords(node.parentFormalParameterList);
-        _forTypeAnnotation();
+        _forTypeAnnotation(node);
       } else if (type is GenericFunctionType &&
           offset < type.functionKeyword.offset &&
           type.returnType == null) {
-        _forTypeAnnotation();
+        _forTypeAnnotation(node);
       }
     }
   }
@@ -1382,7 +1386,7 @@
 
   @override
   void visitTypeArgumentList(TypeArgumentList node) {
-    _forTypeAnnotation();
+    _forTypeAnnotation(node);
   }
 
   @override
@@ -1430,7 +1434,7 @@
           keywordHelper.addKeyword(Keyword.VAR);
         }
         if (keyword == null || keyword.keyword != Keyword.VAR) {
-          _forTypeAnnotation();
+          _forTypeAnnotation(node);
         }
       }
       if (grandparent is FieldDeclaration) {
@@ -1487,8 +1491,9 @@
     var variables = node.variables;
     if (variables.isNotEmpty && offset <= variables[0].name.end) {
       var type = node.type;
-      if (type == null && keyword?.keyword != Keyword.VAR) {
-        _forTypeAnnotation();
+      if ((type == null || type.coversOffset(offset)) &&
+          keyword?.keyword != Keyword.VAR) {
+        _forTypeAnnotation(node);
       } else if (type is RecordTypeAnnotation) {
         // This might be a record pattern that happens to look like a type, in
         // which case the user might be typing `in`.
@@ -1546,9 +1551,9 @@
 
   /// Add the suggestions that are appropriate when the selection is at the
   /// beginning of a class member.
-  void _forClassMember() {
+  void _forClassMember(ClassDeclaration node) {
     keywordHelper.addClassMemberKeywords();
-    // TODO(brianwilkerson): Suggest type names.
+    declarationHelper(mustBeType: true).addLexicalDeclarations(node);
   }
 
   /// Add the suggestions that are appropriate when the selection is at the
@@ -1726,11 +1731,15 @@
 
   /// Add the suggestions that are appropriate when the selection is at the
   /// beginning of a type annotation.
-  void _forTypeAnnotation() {
+  void _forTypeAnnotation(AstNode node) {
     keywordHelper.addKeyword(Keyword.DYNAMIC);
     keywordHelper.addKeyword(Keyword.VOID);
-    // TODO(brianwilkerson): Suggest the types available in the current scope.
-    // _addTypesInScope();
+    if (node is NamedType && node.importPrefix != null) {
+      // TODO(brianwilkerson): Figure out a better way to handle prefixed
+      //  identifiers.
+      return;
+    }
+    declarationHelper(mustBeType: true).addLexicalDeclarations(node);
   }
 
   /// Add the suggestions that are appropriate when the selection is at the
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
index 9ddba66..3cf17f7 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
@@ -321,16 +321,6 @@
   }
 
   @override
-  void declaredTypeParameter(TypeParameter node) {
-    var declaredElement = node.declaredElement;
-    if (declaredElement != null &&
-        visibilityTracker.isVisible(declaredElement) &&
-        opType.includeTypeNameSuggestions) {
-      builder.suggestTypeParameter(declaredElement);
-    }
-  }
-
-  @override
   void visitExtendsClause(ExtendsClause node) {
     inExtendsClause = true;
     super.visitExtendsClause(node);
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_test.dart b/pkg/analysis_server/test/services/completion/dart/completion_test.dart
index 82db258..a2c22fe 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_test.dart
@@ -6387,13 +6387,27 @@
     await computeSuggestions('''
 class Bar<T extends Foo> {const Bar(T k);T m(T a, T b){}final T^ f = null;}
 ''');
-    assertResponse(r'''
+    if (isProtocolVersion2) {
+      assertResponse(r'''
 replacement
   left: 1
 suggestions
   T
     kind: typeParameter
 ''');
+    } else {
+      assertResponse(r'''
+replacement
+  left: 1
+suggestions
+  T
+    kind: typeParameter
+  dynamic
+    kind: keyword
+  void
+    kind: keyword
+''');
+    }
   }
 
   Future<void> test_commentSnippets031_1() async {
@@ -13111,10 +13125,14 @@
   left: 1
   right: 2
 suggestions
+  dynamic
+    kind: keyword
   num
     kind: class
   num
     kind: class
+  void
+    kind: keyword
 ''');
     }
   }
diff --git a/pkg/analysis_server/test/services/completion/dart/shadowing_test.dart b/pkg/analysis_server/test/services/completion/dart/shadowing_test.dart
index 9eabbb5..c7886ad 100644
--- a/pkg/analysis_server/test/services/completion/dart/shadowing_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/shadowing_test.dart
@@ -373,7 +373,6 @@
 ''');
   }
 
-  @failingTest
   Future<void> test_typeParameter_localVariable() async {
     await computeSuggestions('''
 void f() {
@@ -391,7 +390,6 @@
 ''');
   }
 
-  @failingTest
   Future<void> test_typeParameter_parameter() async {
     await computeSuggestions('''
 void f(int p0) {
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 49feac3..25ea723 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -12145,6 +12145,7 @@
     ..addToken('propertyKeyword', propertyKeyword)
     ..addToken('operatorKeyword', operatorKeyword)
     ..addToken('name', name)
+    ..addNode('typeParameters', typeParameters)
     ..addNode('parameters', parameters)
     ..addNode('body', body);
 
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart
index 2bed2e4..5029fa8 100644
--- a/pkg/analyzer/test/generated/strong_mode_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart
@@ -4347,6 +4347,13 @@
     element: T@26
     type: T
   name: f
+  typeParameters: TypeParameterList
+    leftBracket: <
+    typeParameters
+      TypeParameter
+        name: T
+        declaredElement: T@26
+    rightBracket: >
   parameters: FormalParameterList
     leftParenthesis: (
     parameter: SimpleFormalParameter
@@ -4376,6 +4383,13 @@
     element: T@26
     type: T*
   name: f
+  typeParameters: TypeParameterList
+    leftBracket: <
+    typeParameters
+      TypeParameter
+        name: T
+        declaredElement: T@26
+    rightBracket: >
   parameters: FormalParameterList
     leftParenthesis: (
     parameter: SimpleFormalParameter
@@ -5457,6 +5471,13 @@
     element: T@61
     type: T
   name: f
+  typeParameters: TypeParameterList
+    leftBracket: <
+    typeParameters
+      TypeParameter
+        name: T
+        declaredElement: T@61
+    rightBracket: >
   parameters: FormalParameterList
     leftParenthesis: (
     parameter: SimpleFormalParameter
@@ -5485,6 +5506,13 @@
     element: T@61
     type: T*
   name: f
+  typeParameters: TypeParameterList
+    leftBracket: <
+    typeParameters
+      TypeParameter
+        name: T
+        declaredElement: T@61
+    rightBracket: >
   parameters: FormalParameterList
     leftParenthesis: (
     parameter: SimpleFormalParameter
diff --git a/pkg/analyzer/test/src/dart/resolution/enum_test.dart b/pkg/analyzer/test/src/dart/resolution/enum_test.dart
index 5b20cd7..3df5ce8 100644
--- a/pkg/analyzer/test/src/dart/resolution/enum_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/enum_test.dart
@@ -435,6 +435,13 @@
     element: dart:core::@class::int
     type: int
   name: foo
+  typeParameters: TypeParameterList
+    leftBracket: <
+    typeParameters
+      TypeParameter
+        name: U
+        declaredElement: U@27
+    rightBracket: >
   parameters: FormalParameterList
     leftParenthesis: (
     parameter: SimpleFormalParameter
diff --git a/pkg/analyzer/test/src/dart/resolution/extension_type_test.dart b/pkg/analyzer/test/src/dart/resolution/extension_type_test.dart
index 275decc..728a198 100644
--- a/pkg/analyzer/test/src/dart/resolution/extension_type_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/extension_type_test.dart
@@ -238,6 +238,13 @@
     element: <null>
     type: void
   name: foo
+  typeParameters: TypeParameterList
+    leftBracket: <
+    typeParameters
+      TypeParameter
+        name: U
+        declaredElement: U@41
+    rightBracket: >
   parameters: FormalParameterList
     leftParenthesis: (
     parameter: SimpleFormalParameter