Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mixins with superclass constraints, to the 'implementers' list. #3844

Merged
merged 1 commit into from
Aug 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions lib/src/generator/templates.runtime_renderers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9568,17 +9568,17 @@ class _Renderer_MixedInTypes extends RendererBase<MixedInTypes> {
self.renderSimpleVariable(c, remainingNames, 'bool'),
getBool: (CT_ c) => c.hasPublicMixedInTypes,
),
'mixedInElements': Property(
getValue: (CT_ c) => c.mixedInElements,
'mixedInTypes': Property(
getValue: (CT_ c) => c.mixedInTypes,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(
c, remainingNames, 'List<InheritingContainer>'),
c, remainingNames, 'List<DefinedElementType>'),
renderIterable: (CT_ c, RendererBase<CT_> r,
List<MustachioNode> ast, StringSink sink) {
return c.mixedInElements.map((e) =>
_render_InheritingContainer(e, ast, r.template, sink,
parent: r));
return c.mixedInTypes.map((e) => _render_DefinedElementType(
e, ast, r.template, sink,
parent: r));
},
),
'publicMixedInTypes': Property(
Expand Down
2 changes: 1 addition & 1 deletion lib/src/model/class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Class extends InheritingContainer with Constructable, MixedInTypes {
this,

// Caching should make this recursion a little less painful.
for (var container in mixedInElements.reversed)
for (var container in mixedInTypes.modelElements.reversed)
...container.inheritanceChain,

for (var container in superChain.modelElements)
Expand Down
2 changes: 1 addition & 1 deletion lib/src/model/enum.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Enum extends InheritingContainer with Constructable, MixedInTypes {
@override
late final List<InheritingContainer> inheritanceChain = [
this,
for (var container in mixedInElements.reversed)
for (var container in mixedInTypes.modelElements.reversed)
...container.inheritanceChain,
for (var container in superChain.modelElements)
...container.inheritanceChain,
Expand Down
12 changes: 4 additions & 8 deletions lib/src/model/inheriting_container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,8 @@ abstract class InheritingContainer extends Container {

/// All the "immediate" public implementers of this container.
///
/// For a [Mixin], this is actually the mixin applications using the [Mixin].
/// For a [Mixin], this is actually the mixin applications that use the
/// [Mixin].
///
/// If this container has a private implementer, then that is counted as a
/// proxy for any public implementers of that private container.
Expand Down Expand Up @@ -580,15 +581,10 @@ abstract class InheritingContainer extends Container {

/// Add the ability to support mixed-in types to an [InheritingContainer].
mixin MixedInTypes on InheritingContainer {
@visibleForTesting
late final List<DefinedElementType> mixedInTypes = element.mixins
.map((f) => getTypeFor(f, library) as DefinedElementType)
.toList(growable: false);

List<InheritingContainer> get mixedInElements => [
for (var t in mixedInTypes) t.modelElement as InheritingContainer,
];

@override
bool get hasModifiers => super.hasModifiers || hasPublicMixedInTypes;

Expand All @@ -604,8 +600,8 @@ extension on InterfaceElement {

extension DefinedElementTypeIterableExtension on Iterable<DefinedElementType> {
/// The [ModelElement] for each element.
Iterable<InheritingContainer> get modelElements =>
map((e) => e.modelElement as InheritingContainer);
List<InheritingContainer> get modelElements =>
map((e) => e.modelElement as InheritingContainer).toList();
}

extension InheritingContainerIterableExtension
Expand Down
9 changes: 3 additions & 6 deletions lib/src/model/package_graph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -643,14 +643,11 @@ class PackageGraph with CommentReferable, Nameable {
supertype.modelElement as InheritingContainer, container);
}
if (container is Class) {
for (var element in container.mixedInElements) {
for (var element in container.mixedInTypes.modelElements) {
checkAndAddContainer(element, container);
}
for (var element in container.interfaceElements) {
checkAndAddContainer(element, container);
}
} else if (container is ExtensionType) {
for (var element in container.interfaceElements) {
} else if (container is Mixin) {
for (var element in container.superclassConstraints.modelElements) {
checkAndAddContainer(element, container);
}
}
Expand Down
56 changes: 4 additions & 52 deletions test/end2end/model_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1763,7 +1763,7 @@ void main() async {

test('Verify inheritance/mixin structure and type inference', () {
expect(
TypeInferenceMixedIn.mixedInElements
TypeInferenceMixedIn.mixedInTypes
.map<String>((element) => element.name),
orderedEquals(['GenericMixin']));
expect(
Expand Down Expand Up @@ -1928,11 +1928,11 @@ void main() async {
});

test('mixins', () {
expect(Apple.mixedInElements, hasLength(0));
expect(Apple.mixedInTypes, hasLength(0));
});

test('mixins private', () {
expect(F.mixedInElements, hasLength(1));
expect(F.mixedInTypes, hasLength(1));
});

test('interfaces', () {
Expand Down Expand Up @@ -4487,30 +4487,6 @@ String? topLevelFunction(int param1, bool param2, Cool coolBeans,
});

group('Implementors', () {
late final Class apple;
late final Class b;
late final List<InheritingContainer> implA, implC;

setUpAll(() {
apple = exLibrary.classes.named('Apple');
b = exLibrary.classes.named('B');
implA = apple.publicImplementersSorted;
implC = exLibrary.classes.named('Cat').publicImplementersSorted;
});

test('private classes do not break the implementor chain', () {
var Super1 = fakeLibrary.classes.named('Super1');
var publicImplementors =
Super1.publicImplementersSorted.map((i) => i.name);
expect(publicImplementors, hasLength(3));
// A direct implementor.
expect(publicImplementors, contains('Super4'));
// An implementor through _Super2.
expect(publicImplementors, contains('Super3'));
// An implementor through _Super5 and _Super2.
expect(publicImplementors, contains('Super6'));
});

test(
'private classes in internal libraries do not break the implementor chain',
() {
Expand All @@ -4535,31 +4511,6 @@ String? topLevelFunction(int param1, bool param2, Cool coolBeans,
expect(publicImplementors, hasLength(1));
expect(publicImplementors, contains('GenericSuperInt'));
});

test('the first class is Apple', () {
expect(apple.name, equals('Apple'));
});

test('apple has some implementors', () {
expect(apple.hasPublicImplementers, isTrue);
expect(implA, isNotNull);
expect(implA, hasLength(1));
expect(implA[0].name, equals('B'));
});

test('Cat has implementors', () {
expect(implC, hasLength(3));
var implementors = <String>['B', 'Dog', 'ConstantCat'];
expect(implementors, contains(implC[0].name));
expect(implementors, contains(implC[1].name));
expect(implementors, contains(implC[2].name));
});

test('B does not have implementors', () {
expect(b, isNotNull);
expect(b.name, equals('B'));
expect(b.publicImplementersSorted, hasLength(0));
});
});

group('Errors and exceptions', () {
Expand All @@ -4569,6 +4520,7 @@ String? topLevelFunction(int param1, bool param2, Cool coolBeans,
'MyErrorImplements',
'MyExceptionImplements'
];

test('library has the exact errors/exceptions we expect', () {
expect(exLibrary.exceptions.map((e) => e.name),
unorderedEquals(expectedNames));
Expand Down
7 changes: 5 additions & 2 deletions test/enums_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,11 @@ enum E<T> with M<T>, N { one, two, three; }
''');
var eEnum = library.enums.named('E');

expect(eEnum.mixedInElements, hasLength(2));
expect(eEnum.mixedInElements.map((e) => e.name), equals(['M', 'N']));
expect(eEnum.mixedInTypes.modelElements, hasLength(2));
expect(
eEnum.mixedInTypes.modelElements.map((e) => e.name),
equals(['M', 'N']),
);
}

void test_operatorsAreDocumented() async {
Expand Down
29 changes: 24 additions & 5 deletions test/templates/class_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class ClassTest extends TemplateTestBase {
void test_implementers_class_extends() async {
await createPackageWithLibrary('''
class Base {}
class Foo extends Base {}
class _Foo extends Base {}
class Foo extends _Foo {}
''');
var baseLines = readLines(['lib', 'Base-class.html']);

Expand All @@ -40,7 +41,8 @@ class Foo extends Base {}
void test_implementers_class_implements() async {
await createPackageWithLibrary('''
class Base {}
class Foo implements Base {}
class _Foo implements Base {}
class Foo implements _Foo {}
''');
var baseLines = readLines(['lib', 'Base-class.html']);

Expand Down Expand Up @@ -82,6 +84,22 @@ class Foo implements Base<int> {}
]);
}

void test_implementers_class_mixesIn() async {
await createPackageWithLibrary('''
class Base {}
class _Foo with Base {}
class Foo with _Foo {}
''');
var baseLines = readLines(['lib', 'Base-class.html']);

baseLines.expectMainContentContainsAllInOrder([
matches('<dt>Implementers</dt>'),
matches('<dd><ul class="comma-separated clazz-relationships">'),
matches('<li><a href="../lib/Foo-class.html">Foo</a></li>'),
matches('</ul></dd>'),
]);
}

void test_implementers_extensionType_implements() async {
await createPackageWithLibrary('''
class Base1 {}
Expand All @@ -102,7 +120,8 @@ extension type ET(Base2 base) implements Base1 {}
void test_implementers_mixin_implements() async {
await createPackageWithLibrary('''
class Base {}
mixin M implements Base {}
mixin _M implements Base {}
mixin M implements _M {}
''');
var baseLines = readLines(['lib', 'Base-class.html']);

Expand All @@ -114,11 +133,11 @@ mixin M implements Base {}
]);
}

@FailingTest(reason: 'Not implemented yet; should be?')
void test_implementers_mixin_superclassConstraint() async {
await createPackageWithLibrary('''
class Base {}
mixin M on Base {}
mixin _M on Base {}
mixin M on _M {}
''');
var baseLines = readLines(['lib', 'Base-class.html']);

Expand Down
12 changes: 0 additions & 12 deletions testing/test_package/lib/fake.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1214,18 +1214,6 @@ extension ExtensionOnTypeParameter<T> on T {
T aFunctionReturningT(T other) => other;
}

class Super1 {}

class _Super2 implements Super1 {}

class Super3 implements _Super2 {}

class Super4 implements Super1 {}

class _Super5 implements _Super2 {}

class Super6 implements _Super5 {}

abstract class IntermediateAbstract extends Object {
/// This is an override.
@override
Expand Down