From e178d8a936d10dd0d551c7ec00ce62bc0e63d132 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Mon, 30 Aug 2021 10:22:39 -0700 Subject: [PATCH 1/3] flatten --- lib/src/model/model_element.dart | 2 +- lib/src/model/typedef.dart | 49 ++++++++++++++----- test/end2end/model_special_cases_test.dart | 10 ++++ .../lib/constructor_tearoffs.dart | 2 +- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart index f01ce12963..f47112e1e6 100644 --- a/lib/src/model/model_element.dart +++ b/lib/src/model/model_element.dart @@ -315,7 +315,7 @@ abstract class ModelElement extends Canonicalization if (e.aliasedType is FunctionType) { return FunctionTypedef(e, library, packageGraph); } - return Typedef(e, library, packageGraph); + return ClassTypedef(e, library, packageGraph); } if (e is ConstructorElement) { return Constructor(e, library, packageGraph); diff --git a/lib/src/model/typedef.dart b/lib/src/model/typedef.dart index 93358152d6..e7be81e305 100644 --- a/lib/src/model/typedef.dart +++ b/lib/src/model/typedef.dart @@ -9,7 +9,7 @@ import 'package:dartdoc/src/model/comment_referable.dart'; import 'package:dartdoc/src/model/model.dart'; import 'package:dartdoc/src/render/typedef_renderer.dart'; -class Typedef extends ModelElement +abstract class Typedef extends ModelElement with TypeParameters, Categorization implements EnclosedElement { Typedef(TypeAliasElement element, Library library, PackageGraph packageGraph) @@ -40,6 +40,8 @@ class Typedef extends ModelElement @override String get filePath => '${library.dirName}/$fileName'; + /// Helper for mustache templates, which can't do casting themselves + /// without this. FunctionTypedef get asCallable => this as FunctionTypedef; @override @@ -65,34 +67,57 @@ class Typedef extends ModelElement TypedefRenderer get _renderer => packageGraph.rendererFactory.typedefRenderer; - Map _referenceChildren; + @override + Iterable get referenceParents => [definingLibrary]; +} + +/// A typedef referring to a non-function type. +class ClassTypedef extends Typedef { + ClassTypedef( + TypeAliasElement element, Library library, PackageGraph packageGraph + ) : super(element, library, packageGraph) { + assert(!isCallable); + } + @override + DefinedElementType get modelType => super.modelType; + + Map _referenceChildren; @override Map get referenceChildren { if (_referenceChildren == null) { _referenceChildren = {}; - - // Only consider parameters if this is a function typedef. - if (isCallable) { - _referenceChildren - .addEntriesIfAbsent(parameters.explicitOnCollisionWith(this)); - } _referenceChildren .addEntriesIfAbsent(typeParameters.explicitOnCollisionWith(this)); + _referenceChildren + .addEntriesIfAbsent(modelType.modelElement.referenceChildren.entries); } return _referenceChildren; } - - @override - Iterable get referenceParents => [definingLibrary]; } /// A typedef referring to a function type. class FunctionTypedef extends Typedef { FunctionTypedef( TypeAliasElement element, Library library, PackageGraph packageGraph) - : super(element, library, packageGraph); + : super(element, library, packageGraph) { + assert(isCallable); + } @override Callable get modelType => super.modelType; + + Map _referenceChildren; + @override + Map get referenceChildren { + if (_referenceChildren == null) { + _referenceChildren = {}; + _referenceChildren + .addEntriesIfAbsent(parameters.explicitOnCollisionWith(this)); + + _referenceChildren + .addEntriesIfAbsent(typeParameters.explicitOnCollisionWith(this)); + } + return _referenceChildren; + } } diff --git a/test/end2end/model_special_cases_test.dart b/test/end2end/model_special_cases_test.dart index 0e2af4366b..531c5f914d 100644 --- a/test/end2end/model_special_cases_test.dart +++ b/test/end2end/model_special_cases_test.dart @@ -186,6 +186,16 @@ void main() { expect(referenceLookup(constructorTearoffs, 'F.new'), equals(MatchingLinkResult(Fnew))); }); + + test('.new works on typedefs', () { + expect(referenceLookup(constructorTearoffs, 'At.new'), equals(MatchingLinkResult(Anew))); + expect(referenceLookup(constructorTearoffs, 'Bt.new'), equals(MatchingLinkResult(Bnew))); + expect(referenceLookup(constructorTearoffs, 'Ct.new'), equals(MatchingLinkResult(Cnew))); + expect(referenceLookup(constructorTearoffs, 'Dt.new'), equals(MatchingLinkResult(Dnew))); + expect(referenceLookup(constructorTearoffs, 'Et.new'), equals(MatchingLinkResult(Enew))); + expect(referenceLookup(constructorTearoffs, 'Fstring.new'), equals(MatchingLinkResult(Fnew))); + expect(referenceLookup(constructorTearoffs, 'Ft.new'), equals(MatchingLinkResult(Fnew))); + }); }, skip: !_constructorTearoffsAllowed.allows(utils.platformVersion)); }); diff --git a/testing/test_package_experiments/lib/constructor_tearoffs.dart b/testing/test_package_experiments/lib/constructor_tearoffs.dart index 6c3e87dd34..722f4e4bde 100644 --- a/testing/test_package_experiments/lib/constructor_tearoffs.dart +++ b/testing/test_package_experiments/lib/constructor_tearoffs.dart @@ -58,4 +58,4 @@ typedef NotAClass = Function; /// Mixins don't have constructors either, so disallow `M.new`. mixin M on C { -} \ No newline at end of file +} From 74a27447e25582ec8ac7651e42d636c54d5aae84 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Mon, 30 Aug 2021 14:30:05 -0700 Subject: [PATCH 2/3] Fix up typedef implementation to handle generic non-class correctly --- lib/src/model/model_element.dart | 5 ++- lib/src/model/typedef.dart | 40 +++++++++++++++------- test/end2end/model_special_cases_test.dart | 21 ++++++++---- test/end2end/model_test.dart | 8 +++++ 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart index f47112e1e6..9d834dd151 100644 --- a/lib/src/model/model_element.dart +++ b/lib/src/model/model_element.dart @@ -315,7 +315,10 @@ abstract class ModelElement extends Canonicalization if (e.aliasedType is FunctionType) { return FunctionTypedef(e, library, packageGraph); } - return ClassTypedef(e, library, packageGraph); + if (e.aliasedType.element is ClassElement) { + return ClassTypedef(e, library, packageGraph); + } + return GeneralizedTypedef(e, library, packageGraph); } if (e is ConstructorElement) { return Constructor(e, library, packageGraph); diff --git a/lib/src/model/typedef.dart b/lib/src/model/typedef.dart index e7be81e305..d4be7b2433 100644 --- a/lib/src/model/typedef.dart +++ b/lib/src/model/typedef.dart @@ -69,26 +69,46 @@ abstract class Typedef extends ModelElement @override Iterable get referenceParents => [definingLibrary]; + + Map _referenceChildren; + @override + Map get referenceChildren { + if (_referenceChildren == null) { + _referenceChildren = {}; + _referenceChildren + .addEntriesIfAbsent(typeParameters.explicitOnCollisionWith(this)); + } + return _referenceChildren; + } +} + +/// A typedef referring to a non-function typedef that is nevertheless not +/// referring to a defined class. An example is a typedef alias for `void` or +/// for `Function` itself. +class GeneralizedTypedef extends Typedef { + GeneralizedTypedef( + TypeAliasElement element, Library library, PackageGraph packageGraph) + : super(element, library, packageGraph) { + assert(!isCallable); + } } -/// A typedef referring to a non-function type. +/// A typedef referring to a non-function, defined type. class ClassTypedef extends Typedef { ClassTypedef( - TypeAliasElement element, Library library, PackageGraph packageGraph - ) : super(element, library, packageGraph) { + TypeAliasElement element, Library library, PackageGraph packageGraph) + : super(element, library, packageGraph) { assert(!isCallable); + assert(modelType.modelElement is Class); } @override DefinedElementType get modelType => super.modelType; - Map _referenceChildren; @override Map get referenceChildren { if (_referenceChildren == null) { - _referenceChildren = {}; - _referenceChildren - .addEntriesIfAbsent(typeParameters.explicitOnCollisionWith(this)); + _referenceChildren = super.referenceChildren; _referenceChildren .addEntriesIfAbsent(modelType.modelElement.referenceChildren.entries); } @@ -107,16 +127,12 @@ class FunctionTypedef extends Typedef { @override Callable get modelType => super.modelType; - Map _referenceChildren; @override Map get referenceChildren { if (_referenceChildren == null) { - _referenceChildren = {}; + _referenceChildren = super.referenceChildren; _referenceChildren .addEntriesIfAbsent(parameters.explicitOnCollisionWith(this)); - - _referenceChildren - .addEntriesIfAbsent(typeParameters.explicitOnCollisionWith(this)); } return _referenceChildren; } diff --git a/test/end2end/model_special_cases_test.dart b/test/end2end/model_special_cases_test.dart index 531c5f914d..50c4d54978 100644 --- a/test/end2end/model_special_cases_test.dart +++ b/test/end2end/model_special_cases_test.dart @@ -188,13 +188,20 @@ void main() { }); test('.new works on typedefs', () { - expect(referenceLookup(constructorTearoffs, 'At.new'), equals(MatchingLinkResult(Anew))); - expect(referenceLookup(constructorTearoffs, 'Bt.new'), equals(MatchingLinkResult(Bnew))); - expect(referenceLookup(constructorTearoffs, 'Ct.new'), equals(MatchingLinkResult(Cnew))); - expect(referenceLookup(constructorTearoffs, 'Dt.new'), equals(MatchingLinkResult(Dnew))); - expect(referenceLookup(constructorTearoffs, 'Et.new'), equals(MatchingLinkResult(Enew))); - expect(referenceLookup(constructorTearoffs, 'Fstring.new'), equals(MatchingLinkResult(Fnew))); - expect(referenceLookup(constructorTearoffs, 'Ft.new'), equals(MatchingLinkResult(Fnew))); + expect(referenceLookup(constructorTearoffs, 'At.new'), + equals(MatchingLinkResult(Anew))); + expect(referenceLookup(constructorTearoffs, 'Bt.new'), + equals(MatchingLinkResult(Bnew))); + expect(referenceLookup(constructorTearoffs, 'Ct.new'), + equals(MatchingLinkResult(Cnew))); + expect(referenceLookup(constructorTearoffs, 'Dt.new'), + equals(MatchingLinkResult(Dnew))); + expect(referenceLookup(constructorTearoffs, 'Et.new'), + equals(MatchingLinkResult(Enew))); + expect(referenceLookup(constructorTearoffs, 'Fstring.new'), + equals(MatchingLinkResult(Fnew))); + expect(referenceLookup(constructorTearoffs, 'Ft.new'), + equals(MatchingLinkResult(Fnew))); }); }, skip: !_constructorTearoffsAllowed.allows(utils.platformVersion)); }); diff --git a/test/end2end/model_test.dart b/test/end2end/model_test.dart index bc153c1398..72b2f94861 100644 --- a/test/end2end/model_test.dart +++ b/test/end2end/model_test.dart @@ -2237,6 +2237,7 @@ void main() { Library generalizedTypedefs; Typedef T0, T2, T5, T8; Class C2; + Field C1a; setUpAll(() { generalizedTypedefs = packageGraph.libraries @@ -2246,6 +2247,7 @@ void main() { T5 = generalizedTypedefs.typedefs.firstWhere((a) => a.name == 'T5'); T8 = generalizedTypedefs.typedefs.firstWhere((a) => a.name == 'T8'); C2 = generalizedTypedefs.classes.firstWhere((c) => c.name == 'C2'); + C1a = C2.allFields.firstWhere((f) => f.name == 'a'); }); test('Verify basic ability to link anything', () { @@ -2266,6 +2268,12 @@ void main() { var T5name = T5.parameters.firstWhere((t) => t.name == 'name'); expect(referenceLookup(T5, 'name'), equals(MatchingLinkResult(T5name))); }); + + test('Verify ability to link to class members of aliased classes', () { + expect(referenceLookup(generalizedTypedefs, 'T8.a'), + equals(MatchingLinkResult(C1a))); + expect(referenceLookup(T8, 'a'), equals(MatchingLinkResult(C1a))); + }); }); group('Linking for complex inheritance and reexport cases', () { From a23c49be0cfe6dab089801685fa418421e8e3279 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Mon, 30 Aug 2021 15:23:12 -0700 Subject: [PATCH 3/3] oops, forgot a bit --- test/end2end/model_test.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/end2end/model_test.dart b/test/end2end/model_test.dart index 72b2f94861..d4ee8867d4 100644 --- a/test/end2end/model_test.dart +++ b/test/end2end/model_test.dart @@ -2236,7 +2236,7 @@ void main() { group('Linking for generalized typedef cases works', () { Library generalizedTypedefs; Typedef T0, T2, T5, T8; - Class C2; + Class C1, C2; Field C1a; setUpAll(() { @@ -2246,8 +2246,9 @@ void main() { T2 = generalizedTypedefs.typedefs.firstWhere((a) => a.name == 'T2'); T5 = generalizedTypedefs.typedefs.firstWhere((a) => a.name == 'T5'); T8 = generalizedTypedefs.typedefs.firstWhere((a) => a.name == 'T8'); + C1 = generalizedTypedefs.classes.firstWhere((c) => c.name == 'C1'); C2 = generalizedTypedefs.classes.firstWhere((c) => c.name == 'C2'); - C1a = C2.allFields.firstWhere((f) => f.name == 'a'); + C1a = C1.allFields.firstWhere((f) => f.name == 'a'); }); test('Verify basic ability to link anything', () {