diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart
index 00a8cbe27a..037ec674a8 100644
--- a/lib/src/html/html_generator_instance.dart
+++ b/lib/src/html/html_generator_instance.dart
@@ -171,7 +171,7 @@ class HtmlGeneratorInstance {
}
}
- for (var extension in filterNonPublic(lib.extensions)) {
+ for (var extension in filterNonDocumented(lib.extensions)) {
generateExtension(_packageGraph, lib, extension);
for (var constant in filterNonDocumented(extension.constants)) {
diff --git a/lib/src/model.dart b/lib/src/model.dart
index a215e14a9c..d5b6d623c7 100644
--- a/lib/src/model.dart
+++ b/lib/src/model.dart
@@ -2415,8 +2415,6 @@ class Library extends ModelElement with Categorization, TopLevelContainer {
return _allOriginalModelElementNames;
}
- List get allClasses => _allClasses;
-
@override
CharacterLocation get characterLocation {
if (element.nameOffset == -1) {
@@ -2430,19 +2428,30 @@ class Library extends ModelElement with Categorization, TopLevelContainer {
CompilationUnitElement get compilationUnitElement => (element as LibraryElement).definingCompilationUnit;
@override
- Iterable get classes {
- return _allClasses
- .where((c) => !c.isErrorOrException)
- .toList(growable: false);
- }
+ Iterable get classes => allClasses.where((c) => !c.isErrorOrException);
@override
Iterable get extensions {
- if (_extensions != null) return _extensions;
- _extensions = _libraryElement.definingCompilationUnit.extensions
- .map((e) => ModelElement.from(e, this, packageGraph) as Extension)
- .toList(growable: false)
- ..sort(byName);
+ if (_extensions == null) {
+ // De-dupe extensions coming from multiple exported libraries at once.
+ Set extensionElements = Set();
+ extensionElements.addAll(_libraryElement.definingCompilationUnit.extensions);
+ for (CompilationUnitElement cu in _libraryElement.parts) {
+ extensionElements.addAll(cu.extensions);
+ }
+ for (LibraryElement le in _libraryElement.exportedLibraries) {
+ extensionElements.addAll(le.definingCompilationUnit.extensions
+ .where((t) => _exportedNamespace.definedNames.values.contains(t.name)));
+ }
+
+ extensionElements.addAll(_exportedNamespace.definedNames.values
+ .whereType());
+
+ _extensions = extensionElements
+ .map((e) => ModelElement.from(e, this, packageGraph) as Extension)
+ .toList(growable: false)
+ ..sort(byName);
+ }
return _extensions;
}
@@ -2628,10 +2637,10 @@ class Library extends ModelElement with Categorization, TopLevelContainer {
@override
List get exceptions {
- return _allClasses
- .where((c) => c.isErrorOrException)
- .toList(growable: false)
- ..sort(byName);
+ if (_exceptions == null) {
+ _exceptions = allClasses.where((c) => c.isErrorOrException).toList(growable: false);
+ }
+ return _exceptions;
}
@override
@@ -2752,9 +2761,10 @@ class Library extends ModelElement with Categorization, TopLevelContainer {
return _typedefs;
}
- List get _allClasses {
+ List get allClasses {
if (_classes != null) return _classes;
+ // De-dupe classes coming from multiple exported libraries at once.
Set types = Set();
types.addAll(_libraryElement.definingCompilationUnit.types);
for (CompilationUnitElement cu in _libraryElement.parts) {
@@ -2762,14 +2772,12 @@ class Library extends ModelElement with Categorization, TopLevelContainer {
}
for (LibraryElement le in _libraryElement.exportedLibraries) {
types.addAll(le.definingCompilationUnit.types
- .where((t) => _exportedNamespace.definedNames.values.contains(t.name))
- .toList());
+ .where((t) => _exportedNamespace.definedNames.values.contains(t.name)));
}
types.addAll(_exportedNamespace.definedNames.values
- .where((e) => e is ClassElement && !e.isMixin)
- .cast()
- .where((element) => !element.isEnum));
+ .whereType()
+ .where((e) => !e.isMixin && !e.isEnum));
_classes = types
.map((e) => ModelElement.from(e, this, packageGraph) as Class)
@@ -2783,7 +2791,7 @@ class Library extends ModelElement with Categorization, TopLevelContainer {
LibraryElement get _libraryElement => (element as LibraryElement);
Class getClassByName(String name) {
- return _allClasses.firstWhere((it) => it.name == name, orElse: () => null);
+ return allClasses.firstWhere((it) => it.name == name, orElse: () => null);
}
List _getVariables() {
@@ -5026,7 +5034,7 @@ class PackageGraph {
documentedPackages.toList().forEach((package) {
package._libraries.sort((a, b) => compareNatural(a.name, b.name));
package._libraries.forEach((library) {
- library._allClasses.forEach(_addToImplementors);
+ library.allClasses.forEach(_addToImplementors);
});
});
_implementors.values.forEach((l) => l.sort());
diff --git a/test/model_test.dart b/test/model_test.dart
index e441d08283..c2a076e218 100644
--- a/test/model_test.dart
+++ b/test/model_test.dart
@@ -2114,11 +2114,20 @@ void main() {
group('Extension', () {
Extension ext, fancyList;
+ Extension documentOnceReexportOne, documentOnceReexportTwo;
+ Library reexportOneLib, reexportTwoLib;
Class extensionReferencer;
Method doSomeStuff, doStuff, s;
List extensions;
setUpAll(() {
+ reexportOneLib = packageGraph.libraries
+ .firstWhere((lib) => lib.name == 'reexport_one');
+ reexportTwoLib = packageGraph.libraries
+ .firstWhere((lib) => lib.name == 'reexport_two');
+ documentOnceReexportOne = reexportOneLib.extensions.firstWhere((e) => e.name == 'DocumentThisExtensionOnce');
+ documentOnceReexportTwo = reexportTwoLib.extensions.firstWhere((e) => e.name == 'DocumentThisExtensionOnce');
+
ext = exLibrary.extensions.firstWhere((e) => e.name == 'AppleExtension');
extensionReferencer = exLibrary.classes.firstWhere((c) => c.name == 'ExtensionReferencer');
fancyList = exLibrary.extensions.firstWhere((e) => e.name == 'FancyList');
@@ -2129,6 +2138,12 @@ void main() {
extensions = exLibrary.publicExtensions.toList();
});
+ test('basic canonicalization for extensions', () {
+ expect(documentOnceReexportOne.isCanonical, isFalse);
+ expect(documentOnceReexportOne.href, equals(documentOnceReexportTwo.href));
+ expect(documentOnceReexportTwo.isCanonical, isTrue);
+ });
+
// TODO(jcollins-g): implement feature and update tests
test('documentation links do not crash in base cases', () {
packageGraph.packageWarningCounter.hasWarning(doStuff, PackageWarning.notImplemented,
@@ -2143,6 +2158,7 @@ void main() {
expect(extensionReferencer.documentationAsHtml, contains('_Shhh
'));
expect(extensionReferencer.documentationAsHtml, contains('FancyList'));
expect(extensionReferencer.documentationAsHtml, contains('AnExtension.call'));
+ expect(extensionReferencer.documentationAsHtml, contains('DocumentThisExtensionOnce'));
});
test('has a fully qualified name', () {
diff --git a/testing/test_package/lib/example.dart b/testing/test_package/lib/example.dart
index 8b233189ca..b1874b67ac 100644
--- a/testing/test_package/lib/example.dart
+++ b/testing/test_package/lib/example.dart
@@ -677,4 +677,5 @@ extension on Object {
/// This class has nothing to do with [_Shhh], [FancyList], or [AnExtension.call],
/// but should not crash because we referenced them.
+/// We should be able to find [DocumentThisExtensionOnce], too.
class ExtensionReferencer {}
\ No newline at end of file
diff --git a/testing/test_package/lib/reexport_two.dart b/testing/test_package/lib/reexport_two.dart
index e3fddc2419..e922e8aed6 100644
--- a/testing/test_package/lib/reexport_two.dart
+++ b/testing/test_package/lib/reexport_two.dart
@@ -1,6 +1,7 @@
/// {@canonicalFor reexport.somelib.SomeClass}
/// {@canonicalFor reexport.somelib.AUnicornClass}
/// {@canonicalFor something.ThatDoesntExist}
+/// {@canonicalFor reexport.somelib.DocumentThisExtensionOnce}
/// {@category Unreal}
library reexport_two;
diff --git a/testing/test_package/lib/src/somelib.dart b/testing/test_package/lib/src/somelib.dart
index c5243040c8..075745e6f5 100644
--- a/testing/test_package/lib/src/somelib.dart
+++ b/testing/test_package/lib/src/somelib.dart
@@ -7,3 +7,19 @@ class SomeOtherClass {}
class YetAnotherClass {}
class AUnicornClass {}
+
+
+/// A private extension.
+extension _Unseen on Object {
+ void doYouSeeMe() { }
+}
+
+/// An extension without a name
+extension on List {
+ void somethingNew() { }
+}
+
+/// [_Unseen] is not seen, but [DocumentMe] is.
+extension DocumentThisExtensionOnce on String {
+ String get reportOnString => '$this is wonderful';
+}
\ No newline at end of file