CQ. Improvements to indexing and search around DirectSubtypeWithMembers.

Separate the subtype search entry points by result shape and caller
intent. Use `directSubtypeReferences` for reference locations in subtype
clauses, and use `directSubtypesWithMembersOfType` /
`directSubtypesWithMembersOfSubtype` for indexed direct subtypes with
their declared instance members.

Rename `SubtypeResult` to `DirectSubtypeWithMembers` so the model
matches what the search result actually represents. Update the server
member-collection logic to start from direct subtypes of the target type
and then walk direct subtypes by subtype id.

Introduce `SubtypeIndexElementId` as the single representation of
subtype index ids. This keeps id construction consistent between index
building and search, preserves the declaration file for declarations in
parts, and still exposes the element name separately for file-state
prefiltering.

Also make the subtype index expectations text-based so the expected
supertype id, subtype name, and member list are easier to read and
update.

Change-Id: Ice71d9b116aca83372bf595f6e58a8ce4aa9e9d1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/507780
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
Konstantin Shcheglov
2026-06-02 10:28:16 -07:00
parent 546bf07f7d
commit fb2c014693
6 changed files with 676 additions and 613 deletions
@@ -54,31 +54,35 @@ class SearchEngineImpl implements SearchEngine {
var visitedIds = <String>{};
var members = <String>{};
Future<void> addMembers(
InterfaceElement? type,
SubtypeResult? subtype,
) async {
if (subtype != null && !visitedIds.add(subtype.id)) {
Future<void> addSubtype(DirectSubtypeWithMembers subtype) async {
if (!visitedIds.add(subtype.id)) {
return;
}
hasSubtypes = true;
members.addAll(
subtype.library.resource == libraryFile
? subtype.members
: subtype.members.where((name) => !name.startsWith('_')),
);
for (var driver in drivers) {
var subtypes = await driver.search.subtypes(
type: type,
subtype: subtype,
);
for (var subtype in subtypes) {
hasSubtypes = true;
members.addAll(
subtype.library.resource == libraryFile
? subtype.members
: subtype.members.where((name) => !name.startsWith('_')),
);
await addMembers(null, subtype);
var directSubtypes = await driver.search
.directSubtypesWithMembersOfSubtype(subtype);
for (var directSubtype in directSubtypes) {
await addSubtype(directSubtype);
}
}
}
await addMembers(type, null);
for (var driver in drivers) {
var directSubtypes = await driver.search.directSubtypesWithMembersOfType(
type,
);
for (var directSubtype in directSubtypes) {
await addSubtype(directSubtype);
}
}
if (!hasSubtypes) {
return null;
@@ -199,8 +203,8 @@ class SearchEngineImpl implements SearchEngine {
_discoverAvailableFiles(drivers);
for (var driver in drivers) {
var results = await performance.runAsync(
'subTypes',
(_) => driver.search.subTypes(type),
'directSubtypeReferences',
(_) => driver.search.directSubtypeReferences(type),
);
allResults.addAll(results);
}
+36 -13
View File
@@ -146,6 +146,38 @@ class IndexElementInfo {
IndexElementInfo._(this.element, this.kind);
}
/// The identifier of an interface element in the subtype index.
///
/// It combines the library path, the path of the file containing the
/// declaration, and the element name. The name is kept separately because
/// search uses it as a file-state prefilter before matching the full [id].
class SubtypeIndexElementId {
final String name;
final String id;
SubtypeIndexElementId({
required Source librarySource,
required Source declarationSource,
required this.name,
}) : id =
'${librarySource.mustBeFile.path};'
'${declarationSource.mustBeFile.path};'
'$name';
static SubtypeIndexElementId? fromElement(InterfaceElement element) {
var name = element.name;
if (name == null) {
return null;
}
return SubtypeIndexElementId(
librarySource: element.library.firstFragment.source,
declarationSource: element.firstFragment.libraryFragment.source,
name: name,
);
}
}
/// Information about an element referenced in index.
class _ElementInfo {
/// The identifier of the [LibraryFragmentImpl] containing the first
@@ -1275,22 +1307,13 @@ class _IndexContributor extends GeneralizingAstVisitor {
List<String> supertypes = [];
List<String> members = [];
String getInterfaceElementId(InterfaceElement element) {
var libraryFile = element.library.firstFragment.source.mustBeFile;
var libraryPath = libraryFile.path;
var libraryFragment = element.firstFragment.libraryFragment;
var libraryFragmentFile = libraryFragment.source.mustBeFile;
var libraryFragmentPath = libraryFragmentFile.path;
return '$libraryPath;$libraryFragmentPath;${element.name}';
}
void addSupertype(NamedType? type) {
var element = type?.element;
if (element is InterfaceElement) {
var id = getInterfaceElementId(element);
supertypes.add(id);
var supertypeId = SubtypeIndexElementId.fromElement(element)?.id;
if (supertypeId != null) {
supertypes.add(supertypeId);
}
}
}
+106 -104
View File
@@ -168,6 +168,31 @@ enum DeclarationKind {
VARIABLE,
}
/// A direct subtype with its declared instance member names.
class DirectSubtypeWithMembers {
/// The library containing the subtype.
final FileState library;
/// The identifier of the subtype.
final String id;
/// The name of the subtype.
final String name;
/// The names of instance members declared in the class.
final List<String> members;
DirectSubtypeWithMembers({
required this.library,
required this.id,
required this.name,
required this.members,
});
@override
String toString() => id;
}
/// Searches through files known to [drivers] for declarations.
///
/// If files are known to multiple drivers, they will be searched only within
@@ -384,6 +409,50 @@ class Search {
return elements;
}
/// Returns references that declare direct subtypes of the given [type].
Future<List<SearchResult>> directSubtypeReferences(
InterfaceElement? type,
) async {
if (type == null) {
return const <SearchResult>[];
}
List<SearchResult> results = <SearchResult>[];
await _addResults(results, type, const {
IndexRelationKind.IS_EXTENDED_BY:
SearchResultKind.REFERENCE_IN_EXTENDS_CLAUSE,
IndexRelationKind.IS_MIXED_IN_BY:
SearchResultKind.REFERENCE_IN_WITH_CLAUSE,
IndexRelationKind.IS_IMPLEMENTED_BY:
SearchResultKind.REFERENCE_IN_IMPLEMENTS_CLAUSE,
IndexRelationKind.CONSTRAINS: SearchResultKind.REFERENCE_IN_ON_CLAUSE,
});
return results;
}
/// Return direct subtypes of [subtype] with their declared instance member
/// names.
Future<List<DirectSubtypeWithMembers>> directSubtypesWithMembersOfSubtype(
DirectSubtypeWithMembers subtype,
) async {
return _directSubtypesWithMembers(name: subtype.name, id: subtype.id);
}
/// Return direct subtypes of [type] with their declared instance member
/// names.
Future<List<DirectSubtypeWithMembers>> directSubtypesWithMembersOfType(
InterfaceElement type,
) async {
var typeElementId = SubtypeIndexElementId.fromElement(type);
if (typeElementId != null) {
return _directSubtypesWithMembers(
name: typeElementId.name,
id: typeElementId.id,
);
}
return [];
}
/// Return the prefixes used to reference the [element] in any of the
/// compilation units in the [library]. The returned set will include an empty
/// string if the element is referenced without a prefix.
@@ -485,68 +554,6 @@ class Search {
}).toList();
}
/// Returns subtypes of the given [type].
Future<List<SearchResult>> subTypes(InterfaceElement? type) async {
if (type == null) {
return const <SearchResult>[];
}
List<SearchResult> results = <SearchResult>[];
await _addResults(results, type, const {
IndexRelationKind.IS_EXTENDED_BY:
SearchResultKind.REFERENCE_IN_EXTENDS_CLAUSE,
IndexRelationKind.IS_MIXED_IN_BY:
SearchResultKind.REFERENCE_IN_WITH_CLAUSE,
IndexRelationKind.IS_IMPLEMENTED_BY:
SearchResultKind.REFERENCE_IN_IMPLEMENTS_CLAUSE,
IndexRelationKind.CONSTRAINS: SearchResultKind.REFERENCE_IN_ON_CLAUSE,
});
return results;
}
/// Return direct [SubtypeResult]s for either the [type] or [subtype].
Future<List<SubtypeResult>> subtypes({
InterfaceElement? type,
SubtypeResult? subtype,
}) async {
String name;
String id;
if (type != null) {
if (type.name case var elementName?) {
name = elementName;
var libraryFile = type.library.firstFragment.source.mustBeFile;
var fragmentFile = type.firstFragment.libraryFragment.source.mustBeFile;
id = '${libraryFile.path};${fragmentFile.path};$name';
} else {
return [];
}
} else {
name = subtype!.name;
id = subtype.id;
}
List<SubtypeResult> results = [];
_driver.discoverAvailableFiles();
var subtypingFiles = _driver.fsState.getFilesSubtypingName(name);
if (subtypingFiles != null) {
for (var file in _filesForSearch()) {
if (!subtypingFiles.contains(file)) {
continue;
}
var index = await _driver.getIndex(file.path);
if (index != null) {
var request = _IndexRequest(index);
request.addSubtypes(id, results, file);
}
}
}
return results;
}
/// Returns top-level elements with names matching the given [regExp].
Future<List<Element>> topLevelElements(RegExp regExp) async {
var elements = <Element>[];
@@ -726,6 +733,33 @@ class Search {
}
}
Future<List<DirectSubtypeWithMembers>> _directSubtypesWithMembers({
required String name,
required String id,
}) async {
List<DirectSubtypeWithMembers> results = [];
_driver.discoverAvailableFiles();
var subtypingFiles = _driver.fsState.getFilesSubtypingName(name);
if (subtypingFiles != null) {
for (var file in _filesForSearch()) {
if (!subtypingFiles.contains(file)) {
continue;
}
var index = await _driver.getIndex(file.path);
if (index != null) {
var request = _IndexRequest(index);
request.addDirectSubtypesWithMembers(id, results, file);
}
}
}
return results;
}
Iterable<FileState> _filesForSearch() {
_driver.discoverAvailableFiles();
@@ -1164,31 +1198,6 @@ enum SearchResultKind {
REFERENCE_IN_IMPLEMENTS_CLAUSE,
}
/// A single subtype of a type.
class SubtypeResult {
/// The library containing the subtype.
final FileState library;
/// The identifier of the subtype.
final String id;
/// The name of the subtype.
final String name;
/// The names of instance members declared in the class.
final List<String> members;
SubtypeResult({
required this.library,
required this.id,
required this.name,
required this.members,
});
@override
String toString() => id;
}
class WorkspaceSymbols {
final List<Declaration> declarations = [];
final List<String> files = [];
@@ -1521,9 +1530,9 @@ class _IndexRequest {
_IndexRequest(this.index);
void addSubtypes(
void addDirectSubtypesWithMembers(
String superIdString,
List<SubtypeResult> results,
List<DirectSubtypeWithMembers> results,
FileState file,
) {
var superId = index.getStringId(superIdString);
@@ -1549,12 +1558,16 @@ class _IndexRequest {
) {
var subtype = index.subtypes[superIndex];
var name = index.strings[subtype.name];
var subId = '${library.file.path};${file.path};$name';
var subtypeElementId = SubtypeIndexElementId(
librarySource: library.file.source,
declarationSource: file.source,
name: name,
);
results.add(
SubtypeResult(
DirectSubtypeWithMembers(
library: library.file,
id: subId,
name: name,
id: subtypeElementId.id,
name: subtypeElementId.name,
members: subtype.members.map((m) => index.strings[m]).toList(),
),
);
@@ -1862,14 +1875,3 @@ class _LocalReferencesVisitor extends RecursiveAstVisitor<void> {
class _MaxNumberOfDeclarationsError {
const _MaxNumberOfDeclarationsError();
}
extension _SourceExtension on Source {
/// Returns the [File] for this source.
///
/// This assumes that the source is a [FileSource], which is safe because
/// index and search are only supported in DAS, where all sources are file
/// based.
File get mustBeFile {
return (this as FileSource).file;
}
}
@@ -4,8 +4,6 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/source/file_source.dart';
import 'package:analyzer/source/source.dart';
import 'package:analyzer/src/dart/analysis/index.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/summary/idl.dart';
@@ -62,6 +60,15 @@ class IndexTest extends PubPackageResolutionTest {
}
}
void assertSubtypeIndexText(_IndexResult result, String expected) {
var actual = _toPosixPaths(_IndexTextBuilder(result).subtypes());
if (actual != expected) {
NodeTextExpectationsCollector.add(actual);
printPrettyDiff(expected, actual);
fail('See the difference above.');
}
}
test_analyzer_diagnosticCode() async {
var diagnosticFile = newFile('$testPackageLibPath/diagnostic.dart', r'''
const myDiagnosticCode = 0;
@@ -4179,16 +4186,15 @@ void useSetter() {
}
test_subtypes_classDeclaration() async {
var libFile = newFile('$testPackageLibPath/lib.dart', '''
newFile('$testPackageLibPath/a.dart', '''
class A {}
class B {}
class C {}
class D {}
class E {}
''');
String libP = '${libFile.path};${libFile.path}';
var result = await _indexTestCode('''
import 'lib.dart';
import 'a.dart';
class X extends A {
X();
@@ -4226,39 +4232,60 @@ class Z implements E, D {
}
''');
expect(result.index.supertypes, hasLength(6));
expect(result.index.subtypes, hasLength(6));
assertSubtypeIndexText(result, r'''
/home/test/lib/a.dart;/home/test/lib/a.dart;A -> X
field1
field2
getter1
method1
setter1
/home/test/lib/a.dart;/home/test/lib/a.dart;B -> Y
methodY
/home/test/lib/a.dart;/home/test/lib/a.dart;C -> Y
methodY
/home/test/lib/a.dart;/home/test/lib/a.dart;D -> Z
methodZ
/home/test/lib/a.dart;/home/test/lib/a.dart;E -> Z
methodZ
/sdk/lib/core/core.dart;/sdk/lib/core/core.dart;Object -> Y
methodY
''');
}
_assertSubtype(result, 0, '$libP;A', 'X', [
'field1',
'field2',
'getter1',
'method1',
'setter1',
]);
_assertSubtype(result, 1, '$libP;B', 'Y', ['methodY']);
_assertSubtype(result, 2, '$libP;C', 'Y', ['methodY']);
_assertSubtype(result, 3, '$libP;D', 'Z', ['methodZ']);
_assertSubtype(result, 4, '$libP;E', 'Z', ['methodZ']);
_assertSubtype(
result,
5,
_interfaceId(result.resolvedUnit.typeProvider.objectElement)!,
'Y',
['methodY'],
);
test_subtypes_classDeclaration_supertypeInPart() async {
newFile('$testPackageLibPath/a.dart', '''
part 'b.dart';
''');
newFile('$testPackageLibPath/b.dart', '''
part of 'a.dart';
class A {}
''');
var result = await _indexTestCode('''
import 'a.dart';
class X extends A {
void methodX() {}
}
''');
assertSubtypeIndexText(result, r'''
/home/test/lib/a.dart;/home/test/lib/b.dart;A -> X
methodX
''');
}
test_subtypes_classTypeAlias() async {
var libFile = newFile('$testPackageLibPath/lib.dart', '''
newFile('$testPackageLibPath/a.dart', '''
class A {}
class B {}
class C {}
class D {}
''');
String libP = '${libFile.path};${libFile.path}';
var result = await _indexTestCode('''
import 'lib.dart';
import 'a.dart';
class X = A with B, C;
// ^
@@ -4270,16 +4297,15 @@ class Y = A with B implements C, D;
// [diag.classUsedAsMixin] The class 'B' can't be used as a mixin because it's neither a mixin class nor a mixin.
''');
expect(result.index.supertypes, hasLength(7));
expect(result.index.subtypes, hasLength(7));
_assertSubtype(result, 0, '$libP;A', 'X', []);
_assertSubtype(result, 1, '$libP;A', 'Y', []);
_assertSubtype(result, 2, '$libP;B', 'X', []);
_assertSubtype(result, 3, '$libP;B', 'Y', []);
_assertSubtype(result, 4, '$libP;C', 'X', []);
_assertSubtype(result, 5, '$libP;C', 'Y', []);
_assertSubtype(result, 6, '$libP;D', 'Y', []);
assertSubtypeIndexText(result, r'''
/home/test/lib/a.dart;/home/test/lib/a.dart;A -> X
/home/test/lib/a.dart;/home/test/lib/a.dart;A -> Y
/home/test/lib/a.dart;/home/test/lib/a.dart;B -> X
/home/test/lib/a.dart;/home/test/lib/a.dart;B -> Y
/home/test/lib/a.dart;/home/test/lib/a.dart;C -> X
/home/test/lib/a.dart;/home/test/lib/a.dart;C -> Y
/home/test/lib/a.dart;/home/test/lib/a.dart;D -> Y
''');
}
test_subtypes_dynamic() async {
@@ -4291,12 +4317,11 @@ class X extends dynamic {
}
''');
expect(result.index.supertypes, isEmpty);
expect(result.index.subtypes, isEmpty);
assertSubtypeIndexText(result, r'''
''');
}
test_subtypes_enum_implements() async {
String libP = '${testFile.path};${testFile.path}';
var result = await _indexTestCode('''
class A {}
@@ -4306,12 +4331,13 @@ enum E implements A {
}
''');
expect(result.index.subtypes, hasLength(1));
_assertSubtype(result, 0, '$libP;A', 'E', ['foo']);
assertSubtypeIndexText(result, r'''
/home/test/lib/test.dart;/home/test/lib/test.dart;A -> E
foo
''');
}
test_subtypes_enum_with() async {
String libP = '${testFile.path};${testFile.path}';
var result = await _indexTestCode('''
mixin M {}
@@ -4321,20 +4347,21 @@ enum E with M {
}
''');
expect(result.index.subtypes, hasLength(1));
_assertSubtype(result, 0, '$libP;M', 'E', ['foo']);
assertSubtypeIndexText(result, r'''
/home/test/lib/test.dart;/home/test/lib/test.dart;M -> E
foo
''');
}
test_subtypes_extensionType_class() async {
var libFile = newFile('$testPackageLibPath/lib.dart', '''
newFile('$testPackageLibPath/a.dart', '''
class A {
void method1() {}
void method2() {}
}
''');
String libP = '${libFile.path};${libFile.path}';
var result = await _indexTestCode('''
import 'lib.dart';
import 'a.dart';
extension type X(A it) implements A {
void method1() {}
@@ -4342,22 +4369,22 @@ extension type X(A it) implements A {
}
''');
expect(result.index.supertypes, hasLength(1));
expect(result.index.subtypes, hasLength(1));
_assertSubtype(result, 0, '$libP;A', 'X', ['method1', 'method3']);
assertSubtypeIndexText(result, r'''
/home/test/lib/a.dart;/home/test/lib/a.dart;A -> X
method1
method3
''');
}
test_subtypes_extensionType_extensionType() async {
var libFile = newFile('$testPackageLibPath/lib.dart', '''
newFile('$testPackageLibPath/a.dart', '''
extension type A(int it) {
void method1() {}
void method2() {}
}
''');
String libP = '${libFile.path};${libFile.path}';
var result = await _indexTestCode('''
import 'lib.dart';
import 'a.dart';
extension type X(int it) implements A {
void method1() {}
@@ -4365,37 +4392,36 @@ extension type X(int it) implements A {
}
''');
expect(result.index.supertypes, hasLength(1));
expect(result.index.subtypes, hasLength(1));
_assertSubtype(result, 0, '$libP;A', 'X', ['method1', 'method3']);
assertSubtypeIndexText(result, r'''
/home/test/lib/a.dart;/home/test/lib/a.dart;A -> X
method1
method3
''');
}
test_subtypes_mixinDeclaration() async {
var libFile = newFile('$testPackageLibPath/lib.dart', '''
newFile('$testPackageLibPath/a.dart', '''
class A {}
class B {}
class C {}
class D {}
class E {}
''');
String libP = '${libFile.path};${libFile.path}';
var result = await _indexTestCode('''
import 'lib.dart';
import 'a.dart';
mixin X on A implements B, C {}
mixin Y on A, B implements C;
''');
expect(result.index.supertypes, hasLength(6));
expect(result.index.subtypes, hasLength(6));
_assertSubtype(result, 0, '$libP;A', 'X', []);
_assertSubtype(result, 1, '$libP;A', 'Y', []);
_assertSubtype(result, 2, '$libP;B', 'X', []);
_assertSubtype(result, 3, '$libP;B', 'Y', []);
_assertSubtype(result, 4, '$libP;C', 'X', []);
_assertSubtype(result, 5, '$libP;C', 'Y', []);
assertSubtypeIndexText(result, r'''
/home/test/lib/a.dart;/home/test/lib/a.dart;A -> X
/home/test/lib/a.dart;/home/test/lib/a.dart;A -> Y
/home/test/lib/a.dart;/home/test/lib/a.dart;B -> X
/home/test/lib/a.dart;/home/test/lib/a.dart;B -> Y
/home/test/lib/a.dart;/home/test/lib/a.dart;C -> X
/home/test/lib/a.dart;/home/test/lib/a.dart;C -> Y
''');
}
test_SuperFormalParameterElement_ofConstructor_optionalNamed() async {
@@ -4824,27 +4850,6 @@ void f() {
''');
}
void _assertSubtype(
_IndexResult result,
int i,
String superEncoded,
String subName,
List<String> members,
) {
var index = result.index;
expect(index.strings[index.supertypes[i]], superEncoded);
var subtype = index.subtypes[i];
expect(index.strings[subtype.name], subName);
expect(_decodeStringList(index, subtype.members), members);
}
List<String> _decodeStringList(
AnalysisDriverUnitIndex index,
List<int> stringIds,
) {
return stringIds.map((i) => index.strings[i]).toList();
}
Future<_IndexResult> _indexFileWithDiagnostics(File file, String code) async {
var unitResult = await resolveFileWithDiagnostics(file, code);
var indexBuilder = indexUnit(unitResult.unit);
@@ -4857,14 +4862,12 @@ void f() {
return _indexFileWithDiagnostics(testFile, code);
}
String? _interfaceId(InterfaceElement element) {
var libraryFile = element.library.firstFragment.source.mustBeFile;
var libraryPath = libraryFile.path;
var fragmentFile = element.firstFragment.libraryFragment.source.mustBeFile;
var fragmentPath = fragmentFile.path;
return '$libraryPath;$fragmentPath;${element.name}';
static String _toPosixPaths(String text) {
return text.replaceAllMapped(RegExp(r'C:\\([a-zA-Z0-9_.\\]+)'), (match) {
var path = match.group(1)!;
var posixPath = path.replaceAll(r'\', '/');
return '/$posixPath';
});
}
}
@@ -4983,6 +4986,23 @@ final class _IndexTextBuilder {
return buffer.toString();
}
String subtypes() {
var index = result.index;
expect(index.supertypes.length, index.subtypes.length);
var buffer = StringBuffer();
for (var i = 0; i < index.supertypes.length; i++) {
var supertypeId = index.strings[index.supertypes[i]];
var subtype = index.subtypes[i];
var subtypeName = index.strings[subtype.name];
buffer.writeln('$supertypeId -> $subtypeName');
for (var member in subtype.members) {
buffer.writeln(' ${index.strings[member]}');
}
}
return buffer.toString();
}
/// Return the [element] identifier in the result index, or `null`.
int? _findElementId(Element element) {
var index = result.index;
@@ -5089,14 +5109,3 @@ final class _IndexTextBuilder {
buffer.write('|$snippet|');
}
}
extension _SourceExtension on Source {
/// Returns the [File] for this source.
///
/// This assumes that the source is a [FileSource], which is safe because
/// index and search are only supported in DAS, where all sources are file
/// based.
File get mustBeFile {
return (this as FileSource).file;
}
}
@@ -90,6 +90,20 @@ class SearchTest extends PubPackageResolutionTest {
}
}
Future<void> assertDirectSubtypeReferencesText(
InterfaceElement element,
String expected,
) async {
var results = await driver.search.directSubtypeReferences(element);
var actual = _getSearchResultsText(results);
if (actual != expected) {
NodeTextExpectationsCollector.add(actual);
printPrettyDiff(expected, actual);
fail('See the difference above.');
}
expect(actual, expected);
}
Future<void> assertElementReferencesText(
Element element,
String expected,
@@ -130,20 +144,6 @@ class SearchTest extends PubPackageResolutionTest {
expect(actual, expected);
}
Future<void> assertSubTypesText(
InterfaceElement element,
String expected,
) async {
var results = await driver.search.subTypes(element);
var actual = _getSearchResultsText(results);
if (actual != expected) {
NodeTextExpectationsCollector.add(actual);
printPrettyDiff(expected, actual);
fail('See the difference above.');
}
expect(actual, expected);
}
Future<void> assertUnresolvedMemberReferencesText(
String name,
String expected,
@@ -885,6 +885,359 @@ testFile
);
}
test_directSubtypeReferences_class_discover2() async {
var aaaPackageRootPath = '$packagesRootPath/aaa';
var bbbPackageRootPath = '$packagesRootPath/bbb';
var cccPackageRootPath = '$packagesRootPath/ccc';
writeTestPackageConfig(
PackageConfigFileBuilder()
..add(name: 'aaa', rootFolder: getFolder(aaaPackageRootPath))
..add(name: 'bbb', rootFolder: getFolder(bbbPackageRootPath)),
);
addTestFile('class T implements List {}');
newFile('$aaaPackageRootPath/lib/a.dart', r'''
class A implements List {}
''');
newFile('$bbbPackageRootPath/lib/b.dart', r'''
class B implements List {}
''');
newFile('$cccPackageRootPath/lib/c.dart', r'''
class C implements List {}
''');
var coreLibResult =
await driver.getLibraryByUri('dart:core') as LibraryElementResult;
var listElement = coreLibResult.element.getClass('List')!;
var results = await driver.search.directSubtypeReferences(listElement);
void assertHasResult(String uriStr, String name, {bool not = false}) {
var matcher = contains(
predicate((SearchResult r) {
var element = r.enclosingFragment.element;
return element.library!.uri.toString() == uriStr &&
element.name == name;
}),
);
expect(results, not ? isNot(matcher) : matcher);
}
assertHasResult('package:test/test.dart', 'T');
assertHasResult('package:aaa/a.dart', 'A');
assertHasResult('package:bbb/b.dart', 'B');
assertHasResult('package:ccc/c.dart', 'C', not: true);
}
test_directSubtypesWithMembers_class() async {
var result = await resolveTestCode('''
class A {}
class B extends A {
void methodB() {}
}
class C extends Object with A {
void methodC() {}
}
class D implements A {
void methodD() {}
}
class E extends B {
void methodE() {}
}
class F {}
''');
var a = result.findElement.class_('A');
// Search by 'type'.
var directSubtypes = await driver.search.directSubtypesWithMembersOfType(a);
expect(directSubtypes, hasLength(3));
DirectSubtypeWithMembers b = directSubtypes.singleWhere(
(r) => r.name == 'B',
);
DirectSubtypeWithMembers c = directSubtypes.singleWhere(
(r) => r.name == 'C',
);
DirectSubtypeWithMembers d = directSubtypes.singleWhere(
(r) => r.name == 'D',
);
expect(b.library.resource, testFile);
expect(b.id, '${testFile.path};${testFile.path};B');
expect(b.members, ['methodB']);
expect(c.library.resource, testFile);
expect(c.id, '${testFile.path};${testFile.path};C');
expect(c.members, ['methodC']);
expect(d.library.resource, testFile);
expect(d.id, '${testFile.path};${testFile.path};D');
expect(d.members, ['methodD']);
// Search by 'id'.
{
var directSubtypes = await driver.search
.directSubtypesWithMembersOfSubtype(b);
expect(directSubtypes, hasLength(1));
DirectSubtypeWithMembers e = directSubtypes.singleWhere(
(r) => r.name == 'E',
);
expect(e.members, ['methodE']);
}
}
test_directSubtypesWithMembers_class_discover() async {
var aaaPackageRootPath = '$packagesRootPath/aaa';
var bbbPackageRootPath = '$packagesRootPath/bbb';
var aaaFilePath = convertPath('$aaaPackageRootPath/lib/a.dart');
var bbbFilePath = convertPath('$bbbPackageRootPath/lib/b.dart');
writeTestPackageConfig(
PackageConfigFileBuilder()
..add(name: 'aaa', rootFolder: getFolder(aaaPackageRootPath))
..add(name: 'bbb', rootFolder: getFolder(bbbPackageRootPath)),
);
var aUri = 'package:aaa/a.dart';
addTestFile(r'''
import 'package:aaa/a.dart';
class T1 extends A {
void method1() {}
}
class T2 extends A {
void method2() {}
}
''');
newFile(bbbFilePath, r'''
import 'package:aaa/a.dart';
class B extends A {
void method1() {}
}
''');
newFile(aaaFilePath, r'''
class A {
void method1() {}
void method2() {}
}
''');
var aLibraryResult =
await driver.getLibraryByUri(aUri) as LibraryElementResult;
var aClass = aLibraryResult.element.getClass('A')!;
// Search by 'type'.
var directSubtypes = await driver.search.directSubtypesWithMembersOfType(
aClass,
);
expect(directSubtypes, hasLength(3));
DirectSubtypeWithMembers t1 = directSubtypes.singleWhere(
(r) => r.name == 'T1',
);
DirectSubtypeWithMembers t2 = directSubtypes.singleWhere(
(r) => r.name == 'T2',
);
DirectSubtypeWithMembers b = directSubtypes.singleWhere(
(r) => r.name == 'B',
);
expect(t1.library.resource, testFile);
expect(t1.id, '${testFile.path};${testFile.path};T1');
expect(t1.members, ['method1']);
expect(t2.library.resource, testFile);
expect(t2.id, '${testFile.path};${testFile.path};T2');
expect(t2.members, ['method2']);
expect(b.library.resource, getFile(bbbFilePath));
expect(b.id, '$bbbFilePath;$bbbFilePath;B');
expect(b.members, ['method1']);
}
test_directSubtypesWithMembers_class_files() async {
String pathB = convertPath('$testPackageLibPath/b.dart');
String pathC = convertPath('$testPackageLibPath/c.dart');
newFile(pathB, r'''
import 'test.dart';
class B extends A {}
''');
newFile(pathC, r'''
import 'test.dart';
class C extends A {}
class D {}
''');
var result = await resolveTestCode('''
class A {}
''');
var a = result.findElement.class_('A');
var directSubtypes = await driver.search.directSubtypesWithMembersOfType(a);
expect(directSubtypes, hasLength(2));
DirectSubtypeWithMembers b = directSubtypes.singleWhere(
(r) => r.name == 'B',
);
DirectSubtypeWithMembers c = directSubtypes.singleWhere(
(r) => r.name == 'C',
);
expect(b.id, endsWith('b.dart;B'));
expect(c.id, endsWith('c.dart;C'));
}
test_directSubtypesWithMembers_class_missingName() async {
var result = await resolveTestCode('''
class {}
''');
var a = result.findElement.libraryElement.classes.single;
var directSubtypes = await driver.search.directSubtypesWithMembersOfType(a);
expect(directSubtypes, isEmpty);
}
test_directSubtypesWithMembers_enum() async {
var result = await resolveTestCode('''
class A {}
enum E1 implements A {
v;
void methodE1() {}
}
enum E2 with A {
v;
void methodE2() {}
}
class B {}
''');
var directSubtypes = await driver.search.directSubtypesWithMembersOfType(
result.findElement.class_('A'),
);
expect(directSubtypes, hasLength(2));
var resultE1 = directSubtypes.singleWhere((r) => r.name == 'E1');
var resultE2 = directSubtypes.singleWhere((r) => r.name == 'E2');
expect(resultE1.library.resource, testFile);
expect(resultE1.id, '${testFile.path};${testFile.path};E1');
expect(resultE1.members, ['methodE1']);
expect(resultE2.library.resource, testFile);
expect(resultE2.id, '${testFile.path};${testFile.path};E2');
expect(resultE2.members, ['methodE2']);
}
test_directSubtypesWithMembers_extensionType() async {
var result = await resolveTestCode('''
class A {}
extension type E1(A it) implements A {
void methodE1() {}
}
extension type E2(A it) implements A {
void methodE2() {}
}
''');
var directSubtypes = await driver.search.directSubtypesWithMembersOfType(
result.findElement.class_('A'),
);
expect(directSubtypes, hasLength(2));
var resultE1 = directSubtypes.singleWhere((r) => r.name == 'E1');
var resultE2 = directSubtypes.singleWhere((r) => r.name == 'E2');
expect(resultE1.library.resource, testFile);
expect(resultE1.id, '${testFile.path};${testFile.path};E1');
expect(resultE1.members, ['methodE1']);
expect(resultE2.library.resource, testFile);
expect(resultE2.id, '${testFile.path};${testFile.path};E2');
expect(resultE2.members, ['methodE2']);
}
test_directSubtypesWithMembers_extensionType2() async {
var result = await resolveTestCode('''
extension type A(int it) {}
extension type B(int it) implements A {
void methodB() {}
}
''');
var directSubtypes = await driver.search.directSubtypesWithMembersOfType(
result.findElement.extensionType('A'),
);
expect(directSubtypes, hasLength(1));
var B = directSubtypes.singleWhere((r) => r.name == 'B');
expect(B.library.resource, testFile);
expect(B.id, '${testFile.path};${testFile.path};B');
expect(B.members, ['methodB']);
}
test_directSubtypesWithMembers_mixin_superclassConstraints() async {
var result = await resolveTestCode('''
class A {
void methodA() {}
}
class B {
void methodB() {}
}
mixin M on A, B {
void methodA() {}
void methodM() {}
}
''');
var a = result.findElement.class_('A');
var b = result.findElement.class_('B');
{
var directSubtypes = await driver.search.directSubtypesWithMembersOfType(
a,
);
expect(directSubtypes, hasLength(1));
var m = directSubtypes.singleWhere((r) => r.name == 'M');
expect(m.library.resource, testFile);
expect(m.id, '${testFile.path};${testFile.path};M');
expect(m.members, ['methodA', 'methodM']);
}
{
var directSubtypes = await driver.search.directSubtypesWithMembersOfType(
b,
);
expect(directSubtypes, hasLength(1));
var m = directSubtypes.singleWhere((r) => r.name == 'M');
expect(m.library.resource, testFile);
expect(m.id, '${testFile.path};${testFile.path};M');
expect(m.members, ['methodA', 'methodM']);
}
}
test_issue49951_references_dontAddToKnown_unrelated() async {
var myRoot = newFolder('$workspaceRootPath/packages/my');
@@ -990,7 +1343,7 @@ class B_q extends p.A {}
79 6:21 |A| REFERENCE qualified
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> B@44
54 5:17 |A| REFERENCE_IN_EXTENDS_CLAUSE
<testLibraryFragment> B_q@65
@@ -1005,7 +1358,7 @@ class A {}
includedLibraryUris = {Uri.parse(testUriStr)};
var element = result.typeProvider.objectType.element;
await assertElementReferencesText(element, r'''''');
await assertSubTypesText(element, '');
await assertDirectSubtypeReferencesText(element, '');
}
test_scenario_ClassElement_hierarchy_class_implements() async {
@@ -1026,7 +1379,7 @@ class B_q implements p.A {}
85 6:24 |A| REFERENCE qualified
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> B@44
57 5:20 |A| REFERENCE_IN_IMPLEMENTS_CLAUSE
<testLibraryFragment> B_q@68
@@ -1052,7 +1405,7 @@ class D_q extends Object with p.A {}
103 6:33 |A| REFERENCE qualified
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> D@44
66 5:29 |A| REFERENCE_IN_WITH_CLAUSE
<testLibraryFragment> D_q@77
@@ -1078,7 +1431,7 @@ class D2_q = Object with p.A;
91 6:28 |A| REFERENCE qualified
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> D2@44
61 5:24 |A| REFERENCE_IN_WITH_CLAUSE
<testLibraryFragment> D2_q@70
@@ -1104,7 +1457,7 @@ enum E_q implements p.A { v }
86 6:23 |A| REFERENCE qualified
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> E@43
56 5:19 |A| REFERENCE_IN_IMPLEMENTS_CLAUSE
<testLibraryFragment> E_q@69
@@ -1134,7 +1487,7 @@ extension type E_q(A it) implements p.A {}
115 6:39 |A| REFERENCE qualified
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> E@53
72 5:35 |A| REFERENCE_IN_IMPLEMENTS_CLAUSE
<testLibraryFragment> E_q@92
@@ -1160,7 +1513,7 @@ mixin M_q implements p.A {}
85 6:24 |A| REFERENCE qualified
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> M@44
57 5:20 |A| REFERENCE_IN_IMPLEMENTS_CLAUSE
<testLibraryFragment> M_q@68
@@ -1186,7 +1539,7 @@ mixin M2_q on p.A {}
71 6:17 |A| REFERENCE qualified
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> M2@44
50 5:13 |A| REFERENCE_IN_ON_CLAUSE
<testLibraryFragment> M2_q@61
@@ -1211,7 +1564,7 @@ extension type B_q(int it) implements p.A {}
136 6:41 |A| REFERENCE qualified
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> B@70
91 5:37 |A| REFERENCE_IN_IMPLEMENTS_CLAUSE
<testLibraryFragment> B_q@111
@@ -1231,7 +1584,7 @@ class B implements A {}
30 2:20 |A| REFERENCE
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> B@17
30 2:20 |A| REFERENCE_IN_IMPLEMENTS_CLAUSE
''');
@@ -1249,7 +1602,7 @@ class B extends Object with A {}
39 2:29 |A| REFERENCE
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> B@17
39 2:29 |A| REFERENCE_IN_WITH_CLAUSE
''');
@@ -1267,7 +1620,7 @@ class B = Object with A;
33 2:23 |A| REFERENCE
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> B@17
33 2:23 |A| REFERENCE_IN_WITH_CLAUSE
''');
@@ -1287,7 +1640,7 @@ enum E implements A {
29 2:19 |A| REFERENCE
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> E@16
29 2:19 |A| REFERENCE_IN_IMPLEMENTS_CLAUSE
''');
@@ -1307,7 +1660,7 @@ enum E with A {
23 2:13 |A| REFERENCE
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> E@16
23 2:13 |A| REFERENCE_IN_WITH_CLAUSE
''');
@@ -1327,7 +1680,7 @@ extension type E(A it) implements A {}
45 2:35 |A| REFERENCE
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> E@26
45 2:35 |A| REFERENCE_IN_IMPLEMENTS_CLAUSE
''');
@@ -1345,7 +1698,7 @@ mixin M implements A {}
30 2:20 |A| REFERENCE
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> M@17
30 2:20 |A| REFERENCE_IN_IMPLEMENTS_CLAUSE
''');
@@ -1363,7 +1716,7 @@ mixin M on A {}
22 2:12 |A| REFERENCE
''');
await assertSubTypesText(element, r'''
await assertDirectSubtypeReferencesText(element, r'''
<testLibraryFragment> M@17
22 2:12 |A| REFERENCE_IN_ON_CLAUSE
''');
@@ -1384,7 +1737,7 @@ class C extends B {}
var aliasedClass = result.findElement.class_('A');
// TODO(scheglov): Subtypes for the aliased class should be reported.
await assertSubTypesText(aliasedClass, r'''
await assertDirectSubtypeReferencesText(aliasedClass, r'''
''');
}
@@ -1403,7 +1756,7 @@ class C implements B {}
var aliasedClass = result.findElement.class_('A');
// TODO(scheglov): Subtypes for the aliased class should be reported.
await assertSubTypesText(aliasedClass, r'''
await assertDirectSubtypeReferencesText(aliasedClass, r'''
''');
}
@@ -1422,7 +1775,7 @@ class C extends Object with B {}
var aliasedClass = result.findElement.class_('A');
// TODO(scheglov): Subtypes for the aliased class should be reported.
await assertSubTypesText(aliasedClass, r'''
await assertDirectSubtypeReferencesText(aliasedClass, r'''
''');
}
@@ -6946,334 +7299,6 @@ mixin B implements T {}
''');
}
test_subtypes_class() async {
var result = await resolveTestCode('''
class A {}
class B extends A {
void methodB() {}
}
class C extends Object with A {
void methodC() {}
}
class D implements A {
void methodD() {}
}
class E extends B {
void methodE() {}
}
class F {}
''');
var a = result.findElement.class_('A');
// Search by 'type'.
List<SubtypeResult> subtypes = await driver.search.subtypes(type: a);
expect(subtypes, hasLength(3));
SubtypeResult b = subtypes.singleWhere((r) => r.name == 'B');
SubtypeResult c = subtypes.singleWhere((r) => r.name == 'C');
SubtypeResult d = subtypes.singleWhere((r) => r.name == 'D');
expect(b.library.resource, testFile);
expect(b.id, '${testFile.path};${testFile.path};B');
expect(b.members, ['methodB']);
expect(c.library.resource, testFile);
expect(c.id, '${testFile.path};${testFile.path};C');
expect(c.members, ['methodC']);
expect(d.library.resource, testFile);
expect(d.id, '${testFile.path};${testFile.path};D');
expect(d.members, ['methodD']);
// Search by 'id'.
{
List<SubtypeResult> subtypes = await driver.search.subtypes(subtype: b);
expect(subtypes, hasLength(1));
SubtypeResult e = subtypes.singleWhere((r) => r.name == 'E');
expect(e.members, ['methodE']);
}
}
test_subTypes_class_discover() async {
var aaaPackageRootPath = '$packagesRootPath/aaa';
var bbbPackageRootPath = '$packagesRootPath/bbb';
var aaaFilePath = convertPath('$aaaPackageRootPath/lib/a.dart');
var bbbFilePath = convertPath('$bbbPackageRootPath/lib/b.dart');
writeTestPackageConfig(
PackageConfigFileBuilder()
..add(name: 'aaa', rootFolder: getFolder(aaaPackageRootPath))
..add(name: 'bbb', rootFolder: getFolder(bbbPackageRootPath)),
);
var aUri = 'package:aaa/a.dart';
addTestFile(r'''
import 'package:aaa/a.dart';
class T1 extends A {
void method1() {}
}
class T2 extends A {
void method2() {}
}
''');
newFile(bbbFilePath, r'''
import 'package:aaa/a.dart';
class B extends A {
void method1() {}
}
''');
newFile(aaaFilePath, r'''
class A {
void method1() {}
void method2() {}
}
''');
var aLibraryResult =
await driver.getLibraryByUri(aUri) as LibraryElementResult;
var aClass = aLibraryResult.element.getClass('A')!;
// Search by 'type'.
List<SubtypeResult> subtypes = await driver.search.subtypes(type: aClass);
expect(subtypes, hasLength(3));
SubtypeResult t1 = subtypes.singleWhere((r) => r.name == 'T1');
SubtypeResult t2 = subtypes.singleWhere((r) => r.name == 'T2');
SubtypeResult b = subtypes.singleWhere((r) => r.name == 'B');
expect(t1.library.resource, testFile);
expect(t1.id, '${testFile.path};${testFile.path};T1');
expect(t1.members, ['method1']);
expect(t2.library.resource, testFile);
expect(t2.id, '${testFile.path};${testFile.path};T2');
expect(t2.members, ['method2']);
expect(b.library.resource, getFile(bbbFilePath));
expect(b.id, '$bbbFilePath;$bbbFilePath;B');
expect(b.members, ['method1']);
}
test_subTypes_class_discover2() async {
var aaaPackageRootPath = '$packagesRootPath/aaa';
var bbbPackageRootPath = '$packagesRootPath/bbb';
var cccPackageRootPath = '$packagesRootPath/ccc';
writeTestPackageConfig(
PackageConfigFileBuilder()
..add(name: 'aaa', rootFolder: getFolder(aaaPackageRootPath))
..add(name: 'bbb', rootFolder: getFolder(bbbPackageRootPath)),
);
addTestFile('class T implements List {}');
newFile('$aaaPackageRootPath/lib/a.dart', r'''
class A implements List {}
''');
newFile('$bbbPackageRootPath/lib/b.dart', r'''
class B implements List {}
''');
newFile('$cccPackageRootPath/lib/c.dart', r'''
class C implements List {}
''');
var coreLibResult =
await driver.getLibraryByUri('dart:core') as LibraryElementResult;
var listElement = coreLibResult.element.getClass('List')!;
var results = await driver.search.subTypes(listElement);
void assertHasResult(String uriStr, String name, {bool not = false}) {
var matcher = contains(
predicate((SearchResult r) {
var element = r.enclosingFragment.element;
return element.library!.uri.toString() == uriStr &&
element.name == name;
}),
);
expect(results, not ? isNot(matcher) : matcher);
}
assertHasResult('package:test/test.dart', 'T');
assertHasResult('package:aaa/a.dart', 'A');
assertHasResult('package:bbb/b.dart', 'B');
assertHasResult('package:ccc/c.dart', 'C', not: true);
}
test_subtypes_class_files() async {
String pathB = convertPath('$testPackageLibPath/b.dart');
String pathC = convertPath('$testPackageLibPath/c.dart');
newFile(pathB, r'''
import 'test.dart';
class B extends A {}
''');
newFile(pathC, r'''
import 'test.dart';
class C extends A {}
class D {}
''');
var result = await resolveTestCode('''
class A {}
''');
var a = result.findElement.class_('A');
List<SubtypeResult> subtypes = await driver.search.subtypes(type: a);
expect(subtypes, hasLength(2));
SubtypeResult b = subtypes.singleWhere((r) => r.name == 'B');
SubtypeResult c = subtypes.singleWhere((r) => r.name == 'C');
expect(b.id, endsWith('b.dart;B'));
expect(c.id, endsWith('c.dart;C'));
}
test_subtypes_class_missingName() async {
var result = await resolveTestCode('''
class {}
''');
var a = result.findElement.libraryElement.classes.single;
var subtypes = await driver.search.subtypes(type: a);
expect(subtypes, isEmpty);
}
test_subtypes_enum() async {
var result = await resolveTestCode('''
class A {}
enum E1 implements A {
v;
void methodE1() {}
}
enum E2 with A {
v;
void methodE2() {}
}
class B {}
''');
var subtypes = await driver.search.subtypes(
type: result.findElement.class_('A'),
);
expect(subtypes, hasLength(2));
var resultE1 = subtypes.singleWhere((r) => r.name == 'E1');
var resultE2 = subtypes.singleWhere((r) => r.name == 'E2');
expect(resultE1.library.resource, testFile);
expect(resultE1.id, '${testFile.path};${testFile.path};E1');
expect(resultE1.members, ['methodE1']);
expect(resultE2.library.resource, testFile);
expect(resultE2.id, '${testFile.path};${testFile.path};E2');
expect(resultE2.members, ['methodE2']);
}
test_subtypes_extensionType() async {
var result = await resolveTestCode('''
class A {}
extension type E1(A it) implements A {
void methodE1() {}
}
extension type E2(A it) implements A {
void methodE2() {}
}
''');
var subtypes = await driver.search.subtypes(
type: result.findElement.class_('A'),
);
expect(subtypes, hasLength(2));
var resultE1 = subtypes.singleWhere((r) => r.name == 'E1');
var resultE2 = subtypes.singleWhere((r) => r.name == 'E2');
expect(resultE1.library.resource, testFile);
expect(resultE1.id, '${testFile.path};${testFile.path};E1');
expect(resultE1.members, ['methodE1']);
expect(resultE2.library.resource, testFile);
expect(resultE2.id, '${testFile.path};${testFile.path};E2');
expect(resultE2.members, ['methodE2']);
}
test_subtypes_extensionType2() async {
var result = await resolveTestCode('''
extension type A(int it) {}
extension type B(int it) implements A {
void methodB() {}
}
''');
var subtypes = await driver.search.subtypes(
type: result.findElement.extensionType('A'),
);
expect(subtypes, hasLength(1));
var B = subtypes.singleWhere((r) => r.name == 'B');
expect(B.library.resource, testFile);
expect(B.id, '${testFile.path};${testFile.path};B');
expect(B.members, ['methodB']);
}
test_subtypes_mixin_superclassConstraints() async {
var result = await resolveTestCode('''
class A {
void methodA() {}
}
class B {
void methodB() {}
}
mixin M on A, B {
void methodA() {}
void methodM() {}
}
''');
var a = result.findElement.class_('A');
var b = result.findElement.class_('B');
{
var subtypes = await driver.search.subtypes(type: a);
expect(subtypes, hasLength(1));
var m = subtypes.singleWhere((r) => r.name == 'M');
expect(m.library.resource, testFile);
expect(m.id, '${testFile.path};${testFile.path};M');
expect(m.members, ['methodA', 'methodM']);
}
{
var subtypes = await driver.search.subtypes(type: b);
expect(subtypes, hasLength(1));
var m = subtypes.singleWhere((r) => r.name == 'M');
expect(m.library.resource, testFile);
expect(m.id, '${testFile.path};${testFile.path};M');
expect(m.members, ['methodA', 'methodM']);
}
}
test_topLevelElements() async {
var result = await resolveTestCode('''
class A {}
@@ -248,7 +248,7 @@ class NodeTextExpectationsCollector {
),
_AssertMethod(
className: 'SearchTest',
methodName: 'assertSubTypesText',
methodName: 'assertDirectSubtypeReferencesText',
argument: _ArgumentIndex(1),
),
_AssertMethod(