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