Skip to content

Commit f5985ce

Browse files
authored
When accounting for direct implementors, skip over private ones (#2354)
Skip over private implementors when listing implementors
1 parent 01dab58 commit f5985ce

File tree

4 files changed

+72
-29
lines changed

4 files changed

+72
-29
lines changed

lib/src/model/class.dart

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,35 @@ class Class extends Container
166166
return '${package.baseHref}$filePath';
167167
}
168168

169-
/// Returns all the implementors of this class.
169+
/// Returns all the "immediate" public implementors of this class.
170+
///
171+
/// If this class has a private implementor, then that is counted as a proxy
172+
/// for any public implementors of that private class.
170173
Iterable<Class> get publicImplementors {
171-
return model_utils.filterNonPublic(
172-
model_utils.findCanonicalFor(packageGraph.implementors[href] ?? []));
174+
var result = <Class>{};
175+
var seen = <Class>{};
176+
177+
// Recursively adds [implementor] if public, or the impelentors of
178+
// [implementor] if not.
179+
void addToResult(Class implementor) {
180+
if (seen.contains(implementor)) return;
181+
seen.add(implementor);
182+
if (implementor.isPublic) {
183+
result.add(implementor);
184+
} else {
185+
model_utils
186+
.findCanonicalFor(packageGraph.implementors[implementor] ?? [])
187+
.forEach(addToResult);
188+
}
189+
}
190+
191+
model_utils
192+
.findCanonicalFor(packageGraph.implementors[this] ?? [])
193+
.forEach(addToResult);
194+
return result;
173195
}
174196

175-
/*lazy final*/
176-
List<Method> _inheritedMethods;
197+
/*lazy final*/ List<Method> _inheritedMethods;
177198

178199
Iterable<Method> get inheritedMethods {
179200
if (_inheritedMethods == null) {
@@ -201,8 +222,7 @@ class Class extends Container
201222

202223
bool get hasPublicInheritedMethods => publicInheritedMethods.isNotEmpty;
203224

204-
/*lazy final*/
205-
List<Operator> _inheritedOperators;
225+
/*lazy final*/ List<Operator> _inheritedOperators;
206226

207227
Iterable<Operator> get inheritedOperators {
208228
if (_inheritedOperators == null) {

lib/src/model/package_graph.dart

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ class PackageGraph {
151151
/// is true.
152152
bool allExtensionsAdded = false;
153153

154-
Map<String, List<Class>> get implementors {
154+
Map<Class, List<Class>> get implementors {
155155
assert(allImplementorsAdded);
156156
return _implementors;
157157
}
@@ -201,8 +201,8 @@ class PackageGraph {
201201
final Map<Tuple2<Element, Library>, Set<ModelElement>>
202202
allInheritableElements = {};
203203

204-
/// Map of Class.href to a list of classes implementing that class
205-
final Map<String, List<Class>> _implementors = {};
204+
/// A mapping of the list of classes which implement each class.
205+
final Map<Class, List<Class>> _implementors = {};
206206

207207
/// A list of extensions that exist in the package graph.
208208
final List<Extension> _extensions = [];
@@ -576,26 +576,26 @@ class PackageGraph {
576576
return hrefMap;
577577
}
578578

579-
void _addToImplementors(Class c) {
579+
void _addToImplementors(Class class_) {
580580
assert(!allImplementorsAdded);
581-
_implementors.putIfAbsent(c.href, () => []);
582-
void _checkAndAddClass(Class key, Class implClass) {
583-
_implementors.putIfAbsent(key.href, () => []);
584-
var list = _implementors[key.href];
581+
_implementors.putIfAbsent(class_, () => []);
582+
void checkAndAddClass(Class key) {
583+
_implementors.putIfAbsent(key, () => []);
584+
var list = _implementors[key];
585585

586-
if (!list.any((l) => l.element == c.element)) {
587-
list.add(implClass);
586+
if (!list.any((l) => l.element == class_.element)) {
587+
list.add(class_);
588588
}
589589
}
590590

591-
for (var type in c.mixins) {
592-
_checkAndAddClass(type.element, c);
591+
for (var type in class_.mixins) {
592+
checkAndAddClass(type.element);
593593
}
594-
if (c.supertype != null) {
595-
_checkAndAddClass(c.supertype.element, c);
594+
if (class_.supertype != null) {
595+
checkAndAddClass(class_.supertype.element);
596596
}
597-
for (var type in c.interfaces) {
598-
_checkAndAddClass(type.element, c);
597+
for (var type in class_.interfaces) {
598+
checkAndAddClass(type.element);
599599
}
600600
}
601601

test/end2end/model_test.dart

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,12 +1328,12 @@ void main() {
13281328
expect(GenericMixin.characterLocation, isNotNull);
13291329
});
13301330

1331-
test(('Verify mixin member is available in findRefElementCache'), () {
1331+
test('Verify mixin member is available in findRefElementCache', () {
13321332
expect(packageGraph.findRefElementCache['GenericMixin.mixinMember'],
13331333
isNotEmpty);
13341334
});
13351335

1336-
test(('Verify inheritance/mixin structure and type inference'), () {
1336+
test('Verify inheritance/mixin structure and type inference', () {
13371337
expect(
13381338
TypeInferenceMixedIn.mixins
13391339
.map<String>((DefinedElementType t) => t.element.name),
@@ -1356,7 +1356,7 @@ void main() {
13561356
orderedEquals(['int']));
13571357
});
13581358

1359-
test(('Verify non-overridden members have right canonical classes'), () {
1359+
test('Verify non-overridden members have right canonical classes', () {
13601360
var member = TypeInferenceMixedIn.instanceFields
13611361
.firstWhere((f) => f.name == 'member');
13621362
var modifierMember = TypeInferenceMixedIn.instanceFields
@@ -1368,7 +1368,7 @@ void main() {
13681368
expect(mixinMember.canonicalEnclosingContainer, equals(GenericMixin));
13691369
});
13701370

1371-
test(('Verify overrides & documentation inheritance work as intended'), () {
1371+
test('Verify overrides & documentation inheritance work as intended', () {
13721372
expect(overrideByEverything.canonicalEnclosingContainer,
13731373
equals(TypeInferenceMixedIn));
13741374
expect(overrideByGenericMixin.canonicalEnclosingContainer,
@@ -1398,8 +1398,7 @@ void main() {
13981398
.getter));
13991399
});
14001400

1401-
test(('Verify that documentation for mixin applications contains links'),
1402-
() {
1401+
test('Verify that documentation for mixin applications contains links', () {
14031402
expect(
14041403
overrideByModifierClass.oneLineDoc,
14051404
contains(
@@ -3582,6 +3581,18 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
35823581
.toList();
35833582
});
35843583

3584+
test('private classes do not break the implementor chain', () {
3585+
var Super1 = fakeLibrary.classes.firstWhere((c) => c.name == 'Super1');
3586+
var publicImplementors = Super1.publicImplementors.map((i) => i.name);
3587+
expect(publicImplementors, hasLength(3));
3588+
// A direct implementor.
3589+
expect(publicImplementors, contains('Super4'));
3590+
// An implementor through _Super2.
3591+
expect(publicImplementors, contains('Super3'));
3592+
// An implementor through _Super5 and _Super2.
3593+
expect(publicImplementors, contains('Super6'));
3594+
});
3595+
35853596
test('the first class is Apple', () {
35863597
expect(apple.name, equals('Apple'));
35873598
});

testing/test_package/lib/fake.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,3 +1160,15 @@ extension ExtensionOnNull on Null {
11601160
extension ExtensionOnTypeParameter<T> on T {
11611161
T aFunctionReturningT(T other) => other;
11621162
}
1163+
1164+
class Super1 {}
1165+
1166+
class _Super2 implements Super1 {}
1167+
1168+
class Super3 implements _Super2 {}
1169+
1170+
class Super4 implements Super1 {}
1171+
1172+
class _Super5 implements _Super2 {}
1173+
1174+
class Super6 implements _Super5 {}

0 commit comments

Comments
 (0)