|
3 | 3 | // BSD-style license that can be found in the LICENSE file.
|
4 | 4 |
|
5 | 5 | import 'dart:async';
|
| 6 | +import 'dart:collection'; |
6 | 7 |
|
7 | 8 | import 'package:analyzer/dart/ast/ast.dart';
|
8 | 9 | import 'package:analyzer/dart/element/element.dart';
|
@@ -79,7 +80,7 @@ class PackageGraph {
|
79 | 80 | for (var package in documentedPackages) {
|
80 | 81 | package.libraries.sort((a, b) => compareNatural(a.name, b.name));
|
81 | 82 | for (var library in package.libraries) {
|
82 |
| - library.allClasses.forEach(_addToImplementors); |
| 83 | + _addToImplementors(library.allClasses); |
83 | 84 | _extensions.addAll(library.extensions);
|
84 | 85 | }
|
85 | 86 | }
|
@@ -202,7 +203,9 @@ class PackageGraph {
|
202 | 203 | allInheritableElements = {};
|
203 | 204 |
|
204 | 205 | /// A mapping of the list of classes which implement each class.
|
205 |
| - final Map<Class, List<Class>> _implementors = {}; |
| 206 | + final Map<Class, List<Class>> _implementors = LinkedHashMap( |
| 207 | + equals: (Class a, Class b) => a.definingClass == b.definingClass, |
| 208 | + hashCode: (Class class_) => class_.definingClass.hashCode); |
206 | 209 |
|
207 | 210 | /// A list of extensions that exist in the package graph.
|
208 | 211 | final List<Extension> _extensions = [];
|
@@ -576,26 +579,46 @@ class PackageGraph {
|
576 | 579 | return hrefMap;
|
577 | 580 | }
|
578 | 581 |
|
579 |
| - void _addToImplementors(Class class_) { |
| 582 | + void _addToImplementors(Iterable<Class> classes) { |
580 | 583 | assert(!allImplementorsAdded);
|
581 |
| - _implementors.putIfAbsent(class_, () => []); |
582 |
| - void checkAndAddClass(Class key) { |
583 |
| - _implementors.putIfAbsent(key, () => []); |
584 |
| - var list = _implementors[key]; |
585 | 584 |
|
586 |
| - if (!list.any((l) => l.element == class_.element)) { |
587 |
| - list.add(class_); |
| 585 | + // Private classes may not be included in [classes], but may still be |
| 586 | + // necessary links in the implementation chain. They are added here as they |
| 587 | + // are found, then processed after [classes]. |
| 588 | + var privates = <Class>[]; |
| 589 | + |
| 590 | + void checkAndAddClass(Class implemented, Class implementor) { |
| 591 | + if (!implemented.isPublic) { |
| 592 | + privates.add(implemented); |
| 593 | + } |
| 594 | + implemented = implemented.canonicalModelElement ?? implemented; |
| 595 | + _implementors.putIfAbsent(implemented, () => []); |
| 596 | + var list = _implementors[implemented]; |
| 597 | + // TODO(srawlins): This would be more efficient if we created a |
| 598 | + // SplayTreeSet keyed off of `.element`. |
| 599 | + if (!list.any((l) => l.element == implementor.element)) { |
| 600 | + list.add(implementor); |
588 | 601 | }
|
589 | 602 | }
|
590 | 603 |
|
591 |
| - for (var type in class_.mixins) { |
592 |
| - checkAndAddClass(type.element); |
593 |
| - } |
594 |
| - if (class_.supertype != null) { |
595 |
| - checkAndAddClass(class_.supertype.element); |
| 604 | + void addImplementor(Class class_) { |
| 605 | + for (var type in class_.mixins) { |
| 606 | + checkAndAddClass(type.element, class_); |
| 607 | + } |
| 608 | + if (class_.supertype != null) { |
| 609 | + checkAndAddClass(class_.supertype.element, class_); |
| 610 | + } |
| 611 | + for (var type in class_.interfaces) { |
| 612 | + checkAndAddClass(type.element, class_); |
| 613 | + } |
596 | 614 | }
|
597 |
| - for (var type in class_.interfaces) { |
598 |
| - checkAndAddClass(type.element); |
| 615 | + |
| 616 | + classes.forEach(addImplementor); |
| 617 | + |
| 618 | + // [privates] may grow while processing; use a for loop, rather than a |
| 619 | + // for-each loop, to avoid concurrent modification errors. |
| 620 | + for (var i = 0; i < privates.length; i++) { |
| 621 | + addImplementor(privates[i]); |
599 | 622 | }
|
600 | 623 | }
|
601 | 624 |
|
|
0 commit comments