Allow FunctionTypes to work in place of InterfaceType in applicability check (#2109)
* Fix FunctionTypeImpl crash
* Add bug number to comment
* Remove unnecessary comment
* Add TODO for bad cast
diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart
index 3e9e618..86df0c7 100644
--- a/lib/src/element_type.dart
+++ b/lib/src/element_type.dart
@@ -205,10 +205,11 @@
String get nameWithGenerics => name;
@override
- ClassElement get _boundClassElement => interfaceType.element;
+ ClassElement get _boundClassElement => type.element;
@override
- InterfaceType get interfaceType => (type as TypeParameterType).bound;
+ // TODO(jcollins-g): This is wrong; bound is not always an InterfaceType.
+ InterfaceType get _interfaceType => (type as TypeParameterType).bound;
}
/// An [ElementType] associated with an [Element].
@@ -271,18 +272,19 @@
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 +415,7 @@
}
return _returnType;
}
+
+ @override
+ DartType get instantiatedType => type;
}
diff --git a/lib/src/model/class.dart b/lib/src/model/class.dart
index a647d29..cf9d1d5 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<DefinedElementType> mixins;
DefinedElementType supertype;
@@ -51,22 +52,6 @@
return _defaultConstructor;
}
- bool get hasPotentiallyApplicableExtensions =>
- potentiallyApplicableExtensions.isNotEmpty;
-
- List<Extension> _potentiallyApplicableExtensions;
-
- Iterable<Extension> get potentiallyApplicableExtensions {
- if (_potentiallyApplicableExtensions == null) {
- _potentiallyApplicableExtensions = model_utils
- .filterNonDocumented(packageGraph.extensions)
- .where((e) => e.couldApplyTo(this))
- .toList(growable: false)
- ..sort(byName);
- }
- return _potentiallyApplicableExtensions;
- }
-
Iterable<Method> get allInstanceMethods =>
quiver.concat([instanceMethods, inheritedMethods]);
@@ -223,6 +208,7 @@
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 dd9bd06..546eb08 100644
--- a/lib/src/model/extension.dart
+++ b/lib/src/model/extension.dart
@@ -8,6 +8,7 @@
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 @@
ElementType.from(_extension.extendedType, library, packageGraph);
}
- bool couldApplyTo(Class c) => _couldApplyTo(c.modelType);
+ bool couldApplyTo<T extends ExtensionTarget>(T c) =>
+ _couldApplyTo(c.modelType);
/// Return true if this extension could apply to [t].
bool _couldApplyTo(DefinedElementType t) {
@@ -40,13 +42,14 @@
.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<InterfaceType> visited) {
+ bool _isBoundSupertypeTo(DartType superType, HashSet<DartType> 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 0000000..a88c0e5
--- /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 (#2701).
+mixin ExtensionTarget on ModelElement {
+ bool get hasModifiers;
+
+ bool get hasPotentiallyApplicableExtensions =>
+ potentiallyApplicableExtensions.isNotEmpty;
+
+ List<Extension> _potentiallyApplicableExtensions;
+
+ Iterable<Extension> 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 83043bb..cc8d51d 100644
--- a/lib/src/model/package_graph.dart
+++ b/lib/src/model/package_graph.dart
@@ -152,13 +152,21 @@
return _implementors;
}
+ List<Extension> _documentedExtensions;
+ Iterable<Extension> get documentedExtensions {
+ if (_documentedExtensions == null) {
+ _documentedExtensions =
+ utils.filterNonDocumented(extensions).toList(growable: false);
+ }
+ return _documentedExtensions;
+ }
+
Iterable<Extension> get extensions {
assert(allExtensionsAdded);
return _extensions;
}
Map<String, Set<ModelElement>> _findRefElementCache;
-
Map<String, Set<ModelElement>> 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 a195bfb..94ff87e 100644
--- a/testing/test_package/lib/fake.dart
+++ b/testing/test_package/lib/fake.dart
@@ -1117,3 +1117,18 @@
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, R>(A a);
+typedef R Function2<A, B, R>(A a, B b);
+
+extension DoSomething2X<A, B, R> on Function1<A, Function1<B, R>> {
+ Function2<A, B, R> something() => (A first, B second) => this(first)(second);
+}
\ No newline at end of file