From b2663eadb4338588ad3f1705a53cdcb45ea8b45f Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Thu, 2 Jan 2020 15:30:51 -0800 Subject: [PATCH 1/7] Fix FunctionTypeImpl crash --- lib/src/element_type.dart | 17 +++++++++++------ lib/src/model/class.dart | 20 +++----------------- lib/src/model/extension.dart | 11 +++++++---- lib/src/model/extension_target.dart | 26 ++++++++++++++++++++++++++ lib/src/model/package_graph.dart | 10 +++++++++- testing/test_package/lib/fake.dart | 15 +++++++++++++++ 6 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 lib/src/model/extension_target.dart diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart index 3e9e618f8a..a5ed703eb5 100644 --- a/lib/src/element_type.dart +++ b/lib/src/element_type.dart @@ -205,10 +205,10 @@ class TypeParameterElementType extends DefinedElementType { String get nameWithGenerics => name; @override - ClassElement get _boundClassElement => interfaceType.element; + ClassElement get _boundClassElement => type.element; @override - InterfaceType get interfaceType => (type as TypeParameterType).bound; + InterfaceType get _interfaceType => (type as TypeParameterType).bound; } /// An [ElementType] associated with an [Element]. @@ -271,18 +271,19 @@ abstract class DefinedElementType extends ElementType { ClassElement get _boundClassElement => (element.element as ClassElement); Class get boundClass => ModelElement.fromElement(_boundClassElement, packageGraph); - InterfaceType get interfaceType => type; + + InterfaceType get _interfaceType => type; InterfaceType _instantiatedType; /// Return this type, instantiated to bounds if it isn't already. DartType get instantiatedType { if (_instantiatedType == null) { - if (!interfaceType.typeArguments.every((t) => t is InterfaceType)) { + if (!_interfaceType.typeArguments.every((t) => t is InterfaceType)) { var typeSystem = library.element.typeSystem as TypeSystemImpl; - _instantiatedType = typeSystem.instantiateToBounds(interfaceType); + _instantiatedType = typeSystem.instantiateToBounds(_interfaceType); } else { - _instantiatedType = interfaceType; + _instantiatedType = _interfaceType; } } return _instantiatedType; @@ -413,4 +414,8 @@ class CallableGenericTypeAliasElementType extends ParameterizedElementType } return _returnType; } + + /// Return this type, instantiated to bounds if it isn't already. + @override + DartType get instantiatedType => type; } diff --git a/lib/src/model/class.dart b/lib/src/model/class.dart index a647d29c48..cf9d1d5e6c 100644 --- a/lib/src/model/class.dart +++ b/lib/src/model/class.dart @@ -5,12 +5,13 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:dartdoc/src/element_type.dart'; +import 'package:dartdoc/src/model/extension_target.dart'; import 'package:dartdoc/src/model/model.dart'; import 'package:dartdoc/src/model_utils.dart' as model_utils; import 'package:quiver/iterables.dart' as quiver; class Class extends Container - with TypeParameters, Categorization + with TypeParameters, Categorization, ExtensionTarget implements EnclosedElement { List mixins; DefinedElementType supertype; @@ -51,22 +52,6 @@ class Class extends Container return _defaultConstructor; } - bool get hasPotentiallyApplicableExtensions => - potentiallyApplicableExtensions.isNotEmpty; - - List _potentiallyApplicableExtensions; - - Iterable get potentiallyApplicableExtensions { - if (_potentiallyApplicableExtensions == null) { - _potentiallyApplicableExtensions = model_utils - .filterNonDocumented(packageGraph.extensions) - .where((e) => e.couldApplyTo(this)) - .toList(growable: false) - ..sort(byName); - } - return _potentiallyApplicableExtensions; - } - Iterable get allInstanceMethods => quiver.concat([instanceMethods, inheritedMethods]); @@ -223,6 +208,7 @@ class Class extends Container bool get hasPublicMixins => publicMixins.isNotEmpty; + @override bool get hasModifiers => hasPublicMixins || hasAnnotations || diff --git a/lib/src/model/extension.dart b/lib/src/model/extension.dart index dd9bd06968..546eb08585 100644 --- a/lib/src/model/extension.dart +++ b/lib/src/model/extension.dart @@ -8,6 +8,7 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/src/dart/element/element.dart'; import 'package:dartdoc/src/element_type.dart'; +import 'package:dartdoc/src/model/extension_target.dart'; import 'package:dartdoc/src/model/model.dart'; import 'package:quiver/iterables.dart' as quiver; @@ -24,7 +25,8 @@ class Extension extends Container ElementType.from(_extension.extendedType, library, packageGraph); } - bool couldApplyTo(Class c) => _couldApplyTo(c.modelType); + bool couldApplyTo(T c) => + _couldApplyTo(c.modelType); /// Return true if this extension could apply to [t]. bool _couldApplyTo(DefinedElementType t) { @@ -40,13 +42,14 @@ class Extension extends Container .isSubtypeOf(extendedType.instantiatedType, t.instantiatedType); bool isBoundSupertypeTo(DefinedElementType t) => - _isBoundSupertypeTo(t.type, HashSet()); + _isBoundSupertypeTo(t.instantiatedType, HashSet()); /// Returns true if at least one supertype (including via mixins and /// interfaces) is equivalent to or a subtype of [extendedType] when /// instantiated to bounds. - bool _isBoundSupertypeTo( - InterfaceType superType, HashSet visited) { + bool _isBoundSupertypeTo(DartType superType, HashSet visited) { + // Only InterfaceTypes can have superTypes. + if (superType is! InterfaceType) return false; ClassElement superClass = superType?.element; if (visited.contains(superType)) return false; visited.add(superType); diff --git a/lib/src/model/extension_target.dart b/lib/src/model/extension_target.dart new file mode 100644 index 0000000000..9930f39071 --- /dev/null +++ b/lib/src/model/extension_target.dart @@ -0,0 +1,26 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:dartdoc/src/model/model.dart'; + +// TODO(jcollins-g): Mix-in ExtensionTarget on Method, ModelFunction, Typedef, +// and other possible documented symbols that could be extended. +mixin ExtensionTarget on ModelElement { + bool get hasModifiers; + + bool get hasPotentiallyApplicableExtensions => + potentiallyApplicableExtensions.isNotEmpty; + + List _potentiallyApplicableExtensions; + + Iterable get potentiallyApplicableExtensions { + if (_potentiallyApplicableExtensions == null) { + _potentiallyApplicableExtensions = packageGraph.documentedExtensions + .where((e) => e.couldApplyTo(this)) + .toList(growable: false) + ..sort(byName); + } + return _potentiallyApplicableExtensions; + } +} diff --git a/lib/src/model/package_graph.dart b/lib/src/model/package_graph.dart index 1025e27861..bcb3771c65 100644 --- a/lib/src/model/package_graph.dart +++ b/lib/src/model/package_graph.dart @@ -152,13 +152,21 @@ class PackageGraph { return _implementors; } + List _documentedExtensions; + Iterable get documentedExtensions { + if (_documentedExtensions == null) { + _documentedExtensions = + utils.filterNonDocumented(extensions).toList(growable: false); + } + return _documentedExtensions; + } + Iterable get extensions { assert(allExtensionsAdded); return _extensions; } Map> _findRefElementCache; - Map> get findRefElementCache { if (_findRefElementCache == null) { assert(packageGraph.allLibrariesAdded); diff --git a/testing/test_package/lib/fake.dart b/testing/test_package/lib/fake.dart index a195bfb0c1..94ff87e57b 100644 --- a/testing/test_package/lib/fake.dart +++ b/testing/test_package/lib/fake.dart @@ -1117,3 +1117,18 @@ abstract class CanonicalPrivateInheritedToolUser print('hello, tool world'); } } + +/* + * Complex extension methods case. + * + * TODO(jcollins-g): add unit tests around behavior when #2701 is implemented. + * Until #2701 is fixed we mostly are testing that we don't crash because + * DoSomething2X is declared. + */ + +typedef R Function1(A a); +typedef R Function2(A a, B b); + +extension DoSomething2X on Function1> { + Function2 something() => (A first, B second) => this(first)(second); +} \ No newline at end of file From 7511f3cf804c5c367496fa76274409d30fc4e299 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Thu, 2 Jan 2020 15:33:38 -0800 Subject: [PATCH 2/7] Add bug number to comment --- lib/src/model/extension_target.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/model/extension_target.dart b/lib/src/model/extension_target.dart index 9930f39071..a88c0e5a59 100644 --- a/lib/src/model/extension_target.dart +++ b/lib/src/model/extension_target.dart @@ -5,7 +5,7 @@ import 'package:dartdoc/src/model/model.dart'; // TODO(jcollins-g): Mix-in ExtensionTarget on Method, ModelFunction, Typedef, -// and other possible documented symbols that could be extended. +// and other possible documented symbols that could be extended (#2701). mixin ExtensionTarget on ModelElement { bool get hasModifiers; From 98254621462771da4584b3d175362e910dbef246 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Thu, 2 Jan 2020 15:37:14 -0800 Subject: [PATCH 3/7] Remove unnecessary comment --- lib/src/element_type.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart index a5ed703eb5..39750bdefa 100644 --- a/lib/src/element_type.dart +++ b/lib/src/element_type.dart @@ -415,7 +415,6 @@ class CallableGenericTypeAliasElementType extends ParameterizedElementType return _returnType; } - /// Return this type, instantiated to bounds if it isn't already. @override DartType get instantiatedType => type; } From 365e0349fded06c8bf893550f46108a274e44eb0 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Fri, 3 Jan 2020 08:01:54 -0800 Subject: [PATCH 4/7] Add TODO for bad cast --- lib/src/element_type.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart index 39750bdefa..86df0c75ea 100644 --- a/lib/src/element_type.dart +++ b/lib/src/element_type.dart @@ -208,6 +208,7 @@ class TypeParameterElementType extends DefinedElementType { ClassElement get _boundClassElement => type.element; @override + // TODO(jcollins-g): This is wrong; bound is not always an InterfaceType. InterfaceType get _interfaceType => (type as TypeParameterType).bound; } From d12800a71ba48e3d4012e5225316f07f9c405c9f Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Fri, 3 Jan 2020 09:55:40 -0800 Subject: [PATCH 5/7] First attempt to fix crash --- lib/src/element_type.dart | 31 ++++++ lib/src/model/extension.dart | 166 +++++++++++++++++++++++------ testing/test_package/lib/fake.dart | 19 +++- 3 files changed, 182 insertions(+), 34 deletions(-) diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart index 86df0c75ea..9d9bb3c248 100644 --- a/lib/src/element_type.dart +++ b/lib/src/element_type.dart @@ -10,6 +10,7 @@ import 'dart:collection'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/nullability_suffix.dart'; import 'package:analyzer/dart/element/type.dart'; +import 'package:analyzer/src/dart/element/element.dart' show ClassElementImpl; import 'package:analyzer/src/generated/type_system.dart'; import 'package:dartdoc/src/model/model.dart'; import 'package:dartdoc/src/render/element_type_renderer.dart'; @@ -289,6 +290,36 @@ abstract class DefinedElementType extends ElementType { } return _instantiatedType; } + + /// The instantiated to bounds type of this type is a subtype of + /// [t]. + bool isSubtypeOf(DefinedElementType t) => + library.typeSystem.isSubtypeOf(instantiatedType, t.instantiatedType); + + /// Returns true if at least one supertype (including via mixins and + /// interfaces) is equivalent to or a subtype of [this] when + /// instantiated to bounds. + bool isBoundSupertypeTo(DefinedElementType t) => + _isBoundSupertypeTo(t.instantiatedType, HashSet()); + + bool _isBoundSupertypeTo(DartType superType, HashSet visited) { + // Only InterfaceTypes can have superTypes. + if (superType is! InterfaceType) return false; + ClassElement superClass = superType?.element; + if (visited.contains(superType)) return false; + visited.add(superType); + if (superClass == type.element && + (superType == instantiatedType || + library.typeSystem.isSubtypeOf(superType, instantiatedType))) { + return true; + } + List supertypes = []; + ClassElementImpl.collectAllSupertypes(supertypes, superType, null); + for (InterfaceType toVisit in supertypes) { + if (_isBoundSupertypeTo(toVisit, visited)) return true; + } + return false; + } } /// Any callable ElementType will mix-in this class, whether anonymous or not. diff --git a/lib/src/model/extension.dart b/lib/src/model/extension.dart index 546eb08585..da08abc7c2 100644 --- a/lib/src/model/extension.dart +++ b/lib/src/model/extension.dart @@ -16,7 +16,7 @@ import 'package:quiver/iterables.dart' as quiver; class Extension extends Container with TypeParameters, Categorization implements EnclosedElement { - DefinedElementType extendedType; + ElementType extendedType; Extension( ExtensionElement element, Library library, PackageGraph packageGraph) @@ -30,43 +30,144 @@ class Extension extends Container /// Return true if this extension could apply to [t]. bool _couldApplyTo(DefinedElementType t) { - return t.instantiatedType == extendedType.instantiatedType || - (t.instantiatedType.element == extendedType.instantiatedType.element && - isSubtypeOf(t)) || - isBoundSupertypeTo(t); + if (extendedType is UndefinedElementType) { + assert(extendedType.type.isDynamic || extendedType.type.isVoid); + return true; + } + { + DefinedElementType extendedType = this.extendedType; + return t.instantiatedType == extendedType.instantiatedType || + (t.instantiatedType.element == + extendedType.instantiatedType.element && + extendedType.isSubtypeOf(t)) || + extendedType.isBoundSupertypeTo(t); + } } - /// The instantiated to bounds [extendedType] of this extension is a subtype of - /// [t]. - bool isSubtypeOf(DefinedElementType t) => library.typeSystem - .isSubtypeOf(extendedType.instantiatedType, t.instantiatedType); - - bool isBoundSupertypeTo(DefinedElementType t) => - _isBoundSupertypeTo(t.instantiatedType, HashSet()); - - /// Returns true if at least one supertype (including via mixins and - /// interfaces) is equivalent to or a subtype of [extendedType] when - /// instantiated to bounds. - bool _isBoundSupertypeTo(DartType superType, HashSet visited) { - // Only InterfaceTypes can have superTypes. - if (superType is! InterfaceType) return false; - ClassElement superClass = superType?.element; - if (visited.contains(superType)) return false; - visited.add(superType); - if (superClass == extendedType.type.element && - (superType == extendedType.instantiatedType || - library.typeSystem - .isSubtypeOf(superType, extendedType.instantiatedType))) { - return true; + @override + ModelElement get enclosingElement => library; + + ExtensionElement get _extension => (element as ExtensionElement); + + @override + String get kind => 'extension'; + + List _methods; + + @override + List get methods { + if (_methods == null) { + _methods = _extension.methods.map((e) { + return ModelElement.from(e, library, packageGraph) as Method; + }).toList(growable: false) + ..sort(byName); } - List supertypes = []; - ClassElementImpl.collectAllSupertypes(supertypes, superType, null); - for (InterfaceType toVisit in supertypes) { - if (_isBoundSupertypeTo(toVisit, visited)) return true; + return _methods; + } + + List _fields; + + @override + List get allFields { + if (_fields == null) { + _fields = _extension.fields.map((f) { + Accessor getter, setter; + if (f.getter != null) { + getter = ContainerAccessor(f.getter, library, packageGraph); + } + if (f.setter != null) { + setter = ContainerAccessor(f.setter, library, packageGraph); + } + return ModelElement.from(f, library, packageGraph, + getter: getter, setter: setter) as Field; + }).toList(growable: false) + ..sort(byName); } - return false; + return _fields; } + List _typeParameters; + + // a stronger hash? + @override + List get typeParameters { + if (_typeParameters == null) { + _typeParameters = _extension.typeParameters.map((f) { + var lib = Library(f.enclosingElement.library, packageGraph); + return ModelElement.from(f, lib, packageGraph) as TypeParameter; + }).toList(); + } + return _typeParameters; + } + + @override + ParameterizedElementType get modelType => super.modelType; + + List _allModelElements; + + List get allModelElements { + if (_allModelElements == null) { + _allModelElements = List.from( + quiver.concat([ + instanceMethods, + allInstanceFields, + allAccessors, + allOperators, + constants, + staticMethods, + staticProperties, + typeParameters, + ]), + growable: false); + } + return _allModelElements; + } + + @override + String get filePath => '${library.dirName}/$fileName'; + + @override + String get href { + if (!identical(canonicalModelElement, this)) { + return canonicalModelElement?.href; + } + assert(canonicalLibrary != null); + assert(canonicalLibrary == library); + return '${package.baseHref}$filePath'; + } +} +/* +/// Extension methods +class ExtensionOnUndefined extends Container + with TypeParameters, Categorization + implements EnclosedElement { + UndefinedElementType extendedType; + + ExtensionOnUndefined( + ExtensionElement element, Library library, PackageGraph packageGraph) + : super(element, library, packageGraph) { + extendedType = + ElementType.from(_extension.extendedType, library, packageGraph); + } + + /// Return true if this extension could apply to [t]. + bool _couldApplyTo(DefinedElementType t) { + assert(extendedType.type.isDynamic || extendedType.type.isVoid); + return true; + } + + /// The instantiated to bounds [extendedType] of this extension is a subtype of + /// [t]. + bool isSubtypeOf(DefinedElementType t) => throw UnimplementedError; + bool isBoundSupertypeTo(DefinedElementType t) => throw UnimplementedError; + bool _isBoundSupertypeTo(DartType superType, HashSet visited) => throw UnimplementedError; + + + + bool couldApplyTo(T c) => + _couldApplyTo(c.modelType); + + @override ModelElement get enclosingElement => library; @@ -159,3 +260,4 @@ class Extension extends Container return '${package.baseHref}$filePath'; } } + */ diff --git a/testing/test_package/lib/fake.dart b/testing/test_package/lib/fake.dart index 94ff87e57b..fd90e29eb0 100644 --- a/testing/test_package/lib/fake.dart +++ b/testing/test_package/lib/fake.dart @@ -1119,7 +1119,7 @@ abstract class CanonicalPrivateInheritedToolUser } /* - * Complex extension methods case. + * Complex extension methods + typedefs case. * * TODO(jcollins-g): add unit tests around behavior when #2701 is implemented. * Until #2701 is fixed we mostly are testing that we don't crash because @@ -1131,4 +1131,19 @@ typedef R Function2(A a, B b); extension DoSomething2X on Function1> { Function2 something() => (A first, B second) => this(first)(second); -} \ No newline at end of file +} + + +/// Extensions might exist on types defined by the language. +extension ExtensionOnDynamic on dynamic { + void youCanAlwaysCallMe() {} +} + +extension ExtensionOnVoid on void { + void youCanStillAlwaysCallMe() {} +} + +extension ExtensionOnNull on Null { + void youCanOnlyCallMeOnNulls() {} +} + From 209beac0283928e08e95474f82e2981cd85d481e Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Mon, 6 Jan 2020 10:16:08 -0800 Subject: [PATCH 6/7] Clean up for always-applying extensions --- lib/src/model/extension.dart | 135 ++-------------------------- lib/src/model/extension_target.dart | 6 ++ test/model_test.dart | 25 ++++++ 3 files changed, 37 insertions(+), 129 deletions(-) diff --git a/lib/src/model/extension.dart b/lib/src/model/extension.dart index da08abc7c2..457a2081bf 100644 --- a/lib/src/model/extension.dart +++ b/lib/src/model/extension.dart @@ -2,11 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'dart:collection'; - import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/src/dart/element/element.dart'; import 'package:dartdoc/src/element_type.dart'; import 'package:dartdoc/src/model/extension_target.dart'; import 'package:dartdoc/src/model/model.dart'; @@ -25,6 +21,12 @@ class Extension extends Container ElementType.from(_extension.extendedType, library, packageGraph); } + /// Detect if this extension applies to every object. + bool get alwaysApplies => + extendedType.type.isDynamic || + extendedType.type.isVoid || + extendedType.type.isObject; + bool couldApplyTo(T c) => _couldApplyTo(c.modelType); @@ -136,128 +138,3 @@ class Extension extends Container return '${package.baseHref}$filePath'; } } -/* -/// Extension methods -class ExtensionOnUndefined extends Container - with TypeParameters, Categorization - implements EnclosedElement { - UndefinedElementType extendedType; - - ExtensionOnUndefined( - ExtensionElement element, Library library, PackageGraph packageGraph) - : super(element, library, packageGraph) { - extendedType = - ElementType.from(_extension.extendedType, library, packageGraph); - } - - /// Return true if this extension could apply to [t]. - bool _couldApplyTo(DefinedElementType t) { - assert(extendedType.type.isDynamic || extendedType.type.isVoid); - return true; - } - - /// The instantiated to bounds [extendedType] of this extension is a subtype of - /// [t]. - bool isSubtypeOf(DefinedElementType t) => throw UnimplementedError; - bool isBoundSupertypeTo(DefinedElementType t) => throw UnimplementedError; - bool _isBoundSupertypeTo(DartType superType, HashSet visited) => throw UnimplementedError; - - - - bool couldApplyTo(T c) => - _couldApplyTo(c.modelType); - - - @override - ModelElement get enclosingElement => library; - - ExtensionElement get _extension => (element as ExtensionElement); - - @override - String get kind => 'extension'; - - List _methods; - - @override - List get methods { - if (_methods == null) { - _methods = _extension.methods.map((e) { - return ModelElement.from(e, library, packageGraph) as Method; - }).toList(growable: false) - ..sort(byName); - } - return _methods; - } - - List _fields; - - @override - List get allFields { - if (_fields == null) { - _fields = _extension.fields.map((f) { - Accessor getter, setter; - if (f.getter != null) { - getter = ContainerAccessor(f.getter, library, packageGraph); - } - if (f.setter != null) { - setter = ContainerAccessor(f.setter, library, packageGraph); - } - return ModelElement.from(f, library, packageGraph, - getter: getter, setter: setter) as Field; - }).toList(growable: false) - ..sort(byName); - } - return _fields; - } - - List _typeParameters; - - // a stronger hash? - @override - List get typeParameters { - if (_typeParameters == null) { - _typeParameters = _extension.typeParameters.map((f) { - var lib = Library(f.enclosingElement.library, packageGraph); - return ModelElement.from(f, lib, packageGraph) as TypeParameter; - }).toList(); - } - return _typeParameters; - } - - @override - ParameterizedElementType get modelType => super.modelType; - - List _allModelElements; - - List get allModelElements { - if (_allModelElements == null) { - _allModelElements = List.from( - quiver.concat([ - instanceMethods, - allInstanceFields, - allAccessors, - allOperators, - constants, - staticMethods, - staticProperties, - typeParameters, - ]), - growable: false); - } - return _allModelElements; - } - - @override - String get filePath => '${library.dirName}/$fileName'; - - @override - String get href { - if (!identical(canonicalModelElement, this)) { - return canonicalModelElement?.href; - } - assert(canonicalLibrary != null); - assert(canonicalLibrary == library); - return '${package.baseHref}$filePath'; - } -} - */ diff --git a/lib/src/model/extension_target.dart b/lib/src/model/extension_target.dart index a88c0e5a59..a6fa138260 100644 --- a/lib/src/model/extension_target.dart +++ b/lib/src/model/extension_target.dart @@ -14,9 +14,15 @@ mixin ExtensionTarget on ModelElement { List _potentiallyApplicableExtensions; + /// The set of potentiallyApplicableExtensions, for display in templates. + /// + /// This is defined as those extensions where an instantiation of the type + /// defined by [element] can exist where this extension applies, not including + /// any extension that applies to every type. Iterable get potentiallyApplicableExtensions { if (_potentiallyApplicableExtensions == null) { _potentiallyApplicableExtensions = packageGraph.documentedExtensions + .where((e) => !e.alwaysApplies) .where((e) => e.couldApplyTo(this)) .toList(growable: false) ..sort(byName); diff --git a/test/model_test.dart b/test/model_test.dart index edae9ae3bc..7e59c2e09d 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -13,6 +13,7 @@ import 'package:dartdoc/src/render/enum_field_renderer.dart'; import 'package:dartdoc/src/render/model_element_renderer.dart'; import 'package:dartdoc/src/render/parameter_renderer.dart'; import 'package:dartdoc/src/render/typedef_renderer.dart'; +import 'package:dartdoc/src/special_elements.dart'; import 'package:dartdoc/src/warnings.dart'; import 'package:test/test.dart'; @@ -1821,6 +1822,30 @@ void main() { orderedEquals([uphill])); }); + test('extensions on special types work', () { + Extension extensionOnDynamic, extensionOnVoid, extensionOnNull; + Class object = packageGraph.specialClasses[SpecialClass.object]; + Extension getExtension(String name) => + fakeLibrary.extensions.firstWhere((e) => e.name == name); + + extensionOnDynamic = getExtension('ExtensionOnDynamic'); + extensionOnNull = getExtension('ExtensionOnNull'); + extensionOnVoid = getExtension('ExtensionOnVoid'); + + expect(extensionOnDynamic.couldApplyTo(object), isTrue); + expect(extensionOnVoid.couldApplyTo(object), isTrue); + expect(extensionOnNull.couldApplyTo(object), isFalse); + + expect(extensionOnDynamic.alwaysApplies, isTrue); + expect(extensionOnVoid.alwaysApplies, isTrue); + expect(extensionOnNull.alwaysApplies, isFalse); + + // Even though it does have extensions that could apply to it, + // extensions that apply to [Object] should always be hidden from + // documentation. + expect(object.hasPotentiallyApplicableExtensions, isFalse); + }); + test('applicableExtensions include those from implements & mixins', () { Extension extensionCheckLeft, extensionCheckRight, From 65a5fac8f1addf4f20f3c918a1395c118a93fee7 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 8 Jan 2020 09:32:53 -0800 Subject: [PATCH 7/7] Double timeout factor on generator test --- test/html_generator_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/html_generator_test.dart b/test/html_generator_test.dart index 6cfdee0dd8..460c5dce19 100644 --- a/test/html_generator_test.dart +++ b/test/html_generator_test.dart @@ -123,7 +123,7 @@ void main() { packageGraph.localPublicLibraries, anyElement((l) => packageGraph.packageWarningCounter .hasWarning(l, PackageWarning.duplicateFile, expectedPath))); - }, timeout: Timeout.factor(2)); + }, timeout: Timeout.factor(4)); }); }); }