[analysis_server] Fix relationships for subtypes that provide type arguments

Change-Id: I655f7bff036a17ee79a1d0f9f163125112766f2b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/264001
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Danny Tuppeny 2022-10-13 18:07:19 +00:00 committed by Commit Queue
parent 3e7a1703bb
commit 54d7847787
2 changed files with 56 additions and 10 deletions

View file

@ -94,18 +94,23 @@ class DartLazyTypeHierarchyComputer {
InterfaceElement target, SearchEngine searchEngine) async {
final targetType = target.thisType;
/// Helper to check whether [type] refers to the same class as [target]
/// irrespective of any concrete type arguments.
bool isTargetClass(InterfaceType? type) =>
type?.element == targetType.element;
/// Helper to convert an [InterfaceElement] to a [TypeHierarchyRelatedItem].
TypeHierarchyRelatedItem toHierarchyItem(InterfaceElement element) {
final type = element.thisType;
if (element is MixinElement &&
element.superclassConstraints.contains(targetType)) {
element.superclassConstraints.any(isTargetClass)) {
return TypeHierarchyRelatedItem.constrainedTo(type);
} else if (element.supertype == targetType) {
} else if (isTargetClass(element.supertype)) {
return TypeHierarchyRelatedItem.extends_(type);
} else if (element.interfaces.contains(targetType)) {
} else if (element.interfaces.any(isTargetClass)) {
return TypeHierarchyRelatedItem.implements(type);
} else if (element.mixins.contains(targetType)) {
return TypeHierarchyRelatedItem.with_(type);
} else if (element.mixins.any(isTargetClass)) {
return TypeHierarchyRelatedItem.mixesIn(type);
} else {
assert(false, 'Subtype found with unknown relationship type');
return TypeHierarchyRelatedItem.unknown(type);
@ -136,7 +141,7 @@ class DartLazyTypeHierarchyComputer {
if (supertype != null) TypeHierarchyRelatedItem.extends_(supertype),
...?superclassConstraints?.map(TypeHierarchyRelatedItem.constrainedTo),
...?interfaces?.map(TypeHierarchyRelatedItem.implements),
...?mixins?.map(TypeHierarchyRelatedItem.with_),
...?mixins?.map(TypeHierarchyRelatedItem.mixesIn),
];
}
@ -213,14 +218,14 @@ class TypeHierarchyRelatedItem extends TypeHierarchyItem {
: this._forElement(type.element,
relationship: TypeHierarchyItemRelationship.implements);
TypeHierarchyRelatedItem.mixesIn(InterfaceType type)
: this._forElement(type.element,
relationship: TypeHierarchyItemRelationship.mixesIn);
TypeHierarchyRelatedItem.unknown(InterfaceType type)
: this._forElement(type.element,
relationship: TypeHierarchyItemRelationship.unknown);
TypeHierarchyRelatedItem.with_(InterfaceType type)
: this._forElement(type.element,
relationship: TypeHierarchyItemRelationship.mixesIn);
TypeHierarchyRelatedItem._forElement(super.element,
{required this.relationship})
: super.forElement();

View file

@ -112,6 +112,26 @@ class TypeHierarchyComputerFindSubtypesTest extends AbstractTypeHierarchyTest {
]);
}
Future<void> test_class_generic() async {
final content = '''
class My^Class1<T> {}
/*[0*/class /*[1*/MyClass2/*1]*/ implements MyClass1<String> {}/*0]*/
''';
addTestSource(content);
final target = await findTarget();
final supertypes = await findSubtypes(target!);
expect(supertypes, [
_isRelatedItem(
'MyClass2',
testFile,
relationship: TypeHierarchyItemRelationship.implements,
codeRange: code.ranges[0].sourceRange,
nameRange: code.ranges[1].sourceRange,
),
]);
}
Future<void> test_class_interfaces() async {
final content = '''
class ^MyClass1 {}
@ -300,6 +320,27 @@ class ^MyClass2 extends MyClass1 {}
]);
}
Future<void> test_class_generic() async {
final content = '''
/*[0*/class /*[1*/MyClass1/*1]*/<T> {}/*0]*/
class ^MyClass2 implements MyClass1<String> {}
''';
addTestSource(content);
final target = await findTarget();
final supertypes = await findSupertypes(target!);
expect(supertypes, [
_isObject,
_isRelatedItem(
'MyClass1',
testFile,
relationship: TypeHierarchyItemRelationship.implements,
codeRange: code.ranges[0].sourceRange,
nameRange: code.ranges[1].sourceRange,
),
]);
}
Future<void> test_class_interfaces() async {
final content = '''
/*[0*/class /*[1*/MyClass1/*1]*/ {}/*0]*/