diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart index 3bb9529bb5..d279c3866d 100644 --- a/lib/src/element_type.dart +++ b/lib/src/element_type.dart @@ -33,9 +33,16 @@ abstract class ElementType extends Privacy { return UndefinedElementType(f, library, packageGraph, returnedFrom); } else { var element = ModelElement.fromElement(f.element, packageGraph); + // [TypeAliasElement.aliasElement] has different implications. + // In that case it is an actual type alias of some kind (generic + // or otherwise. Here however aliasElement signals that this is a + // type referring to an alias. + if (f is! TypeAliasElement && f.aliasElement != null) { + return AliasedElementType( + f, library, packageGraph, element, returnedFrom); + } assert(f is ParameterizedType || f is TypeParameterType); - // TODO(jcollins-g): after analyzer 1.2.0 implement InterfaceType - // alias references and strip out all the cruft that's accumulated + // TODO(jcollins-g): strip out all the cruft that's accumulated // here for non-generic type aliases. var isGenericTypeAlias = f.aliasElement != null && f is! InterfaceType; if (f is FunctionType) { @@ -211,10 +218,32 @@ class ParameterizedElementType extends DefinedElementType { return _nameWithGenerics; } - ElementTypeRenderer get _renderer => + ElementTypeRenderer get _renderer => packageGraph.rendererFactory.parameterizedElementTypeRenderer; } +class AliasedElementType extends ParameterizedElementType { + AliasedElementType(ParameterizedType type, Library library, + PackageGraph packageGraph, ModelElement element, ElementType returnedFrom) + : super(type, library, packageGraph, element, returnedFrom) { + assert(type.aliasElement != null); + } + + ModelElement _aliasElement; + ModelElement get aliasElement => _aliasElement ??= + ModelElement.fromElement(type.aliasElement, packageGraph); + + Iterable _aliasArguments; + Iterable get aliasArguments => + _aliasArguments ??= type.aliasArguments + .map((f) => ElementType.from(f, library, packageGraph)) + .toList(growable: false); + + @override + ElementTypeRenderer get _renderer => + packageGraph.rendererFactory.aliasedElementTypeRenderer; +} + class TypeParameterElementType extends DefinedElementType { TypeParameterElementType(TypeParameterType type, Library library, PackageGraph packageGraph, ModelElement element, ElementType returnedFrom) diff --git a/lib/src/render/element_type_renderer.dart b/lib/src/render/element_type_renderer.dart index bd474c1d09..bbe708f5eb 100644 --- a/lib/src/render/element_type_renderer.dart +++ b/lib/src/render/element_type_renderer.dart @@ -83,6 +83,39 @@ class ParameterizedElementTypeRendererHtml } } +class AliasedElementTypeRendererHtml + extends ElementTypeRenderer { + @override + String renderLinkedName(AliasedElementType elementType) { + var buf = StringBuffer(); + buf.write(elementType.aliasElement.linkedName); + if (elementType.aliasArguments.isNotEmpty && + !elementType.aliasArguments.every((t) => t.name == 'dynamic')) { + buf.write(''); + buf.write('<'); + buf.writeAll(elementType.aliasArguments.map((t) => t.linkedName), + ', '); + buf.write('>'); + buf.write(''); + } + return wrapNullability(elementType, buf.toString()); + } + + @override + String renderNameWithGenerics(AliasedElementType elementType) { + var buf = StringBuffer(); + buf.write(elementType.aliasElement.name); + if (elementType.aliasArguments.isNotEmpty && + !elementType.aliasArguments.every((t) => t.name == 'dynamic')) { + buf.write('<'); + buf.writeAll(elementType.aliasArguments.map((t) => t.nameWithGenerics), + ', '); + buf.write('>'); + } + return wrapNullability(elementType, buf.toString()); + } +} + class CallableElementTypeRendererHtml extends ElementTypeRenderer { @override @@ -160,6 +193,37 @@ class ParameterizedElementTypeRendererMd } } +class AliasedElementTypeRendererMd + extends ElementTypeRenderer { + @override + String renderLinkedName(AliasedElementType elementType) { + var buf = StringBuffer(); + buf.write(elementType.aliasElement.linkedName); + if (elementType.aliasArguments.isNotEmpty && + !elementType.aliasArguments.every((t) => t.name == 'dynamic')) { + buf.write('<'); + buf.writeAll(elementType.aliasArguments.map((t) => t.linkedName), ', '); + buf.write('>'); + } + return wrapNullability(elementType, buf.toString()); + } + + @override + String renderNameWithGenerics(AliasedElementType elementType) { + var buf = StringBuffer(); + buf.write(elementType.aliasElement.name); + if (elementType.aliasArguments.isNotEmpty && + !elementType.aliasArguments.every((t) => t.name == 'dynamic')) { + buf.write('<'); + buf.writeAll( + elementType.aliasArguments.map((t) => t.nameWithGenerics), ', '); + buf.write('>'); + } + buf.write(elementType.nullabilitySuffix); + return wrapNullability(elementType, buf.toString()); + } +} + class CallableElementTypeRendererMd extends ElementTypeRenderer { @override diff --git a/lib/src/render/renderer_factory.dart b/lib/src/render/renderer_factory.dart index cfe07b3b4f..1521b67156 100644 --- a/lib/src/render/renderer_factory.dart +++ b/lib/src/render/renderer_factory.dart @@ -46,6 +46,8 @@ abstract class RendererFactory { ElementTypeRenderer get parameterizedElementTypeRenderer; + ElementTypeRenderer get aliasedElementTypeRenderer; + ElementTypeRenderer get callableElementTypeRenderer; EnumFieldRenderer get enumFieldRenderer; @@ -90,6 +92,10 @@ class HtmlRenderFactory extends RendererFactory { get parameterizedElementTypeRenderer => ParameterizedElementTypeRendererHtml(); + @override + ElementTypeRenderer get aliasedElementTypeRenderer => + AliasedElementTypeRendererHtml(); + @override EnumFieldRenderer get enumFieldRenderer => EnumFieldRendererHtml(); @@ -146,6 +152,10 @@ class MdRenderFactory extends RendererFactory { get parameterizedElementTypeRenderer => ParameterizedElementTypeRendererMd(); + @override + ElementTypeRenderer get aliasedElementTypeRenderer => + AliasedElementTypeRendererMd(); + @override EnumFieldRenderer get enumFieldRenderer => EnumFieldRendererMd(); diff --git a/pubspec.yaml b/pubspec.yaml index 3f1a58110b..084b86eaad 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ environment: sdk: '>=2.11.99 <3.0.0' dependencies: - analyzer: ^1.1.0 + analyzer: ^1.2.0 args: ^2.0.0 charcode: ^1.2.0 collection: ^1.2.0 diff --git a/test/end2end/model_special_cases_test.dart b/test/end2end/model_special_cases_test.dart index 893589921c..b5f8819d2e 100644 --- a/test/end2end/model_special_cases_test.dart +++ b/test/end2end/model_special_cases_test.dart @@ -107,25 +107,35 @@ void main() { orderedEquals(genericParameters)); } + void expectAliasedTypeName(AliasedElementType n, expected) { + expect(n.aliasElement.name, expected); + } + test('typedef references display aliases', () { - var f = C.allFields.firstWhere((f) => f.name == 'f'); var g = C.instanceMethods.firstWhere((m) => m.name == 'g'); - var a = generalizedTypedefs.properties.firstWhere((p) => p.name == 'a'); - var b = C2.allFields.firstWhere((f) => f.name == 'b'); + var c = C2.allFields.firstWhere((f) => f.name == 'c'); var d = C2.instanceMethods.firstWhere((f) => f.name == 'd'); - expect(a.modelType.name, equals('T0')); - expect(b.modelType.name, equals('T0')); - expect(c.modelType.name, equals('T1')); - expect(d.modelType.returnType.name, equals('T2')); - expect(d.parameters.first.modelType.name, equals('T3')); - expect(d.parameters.last.modelType.name, equals('T4')); - - expect(f.modelType.name, equals('T0')); - expect(g.modelType.returnType.name, equals('T1')); - expect(g.modelType.parameters.first.modelType.name, equals('T2')); - expect(g.modelType.parameters.last.modelType.name, equals('T3')); + expectAliasedTypeName(c.modelType, equals('T1')); + expectAliasedTypeName(d.modelType.returnType, equals('T2')); + expectAliasedTypeName(d.parameters.first.modelType, equals('T3')); + expectAliasedTypeName(d.parameters.last.modelType, equals('T4')); + + expectAliasedTypeName(g.modelType.returnType, equals('T1')); + expectAliasedTypeName( + g.modelType.parameters.first.modelType, equals('T2')); + expectAliasedTypeName( + g.modelType.parameters.last.modelType, equals('T3')); + }); + + test('typedef references to special types work', () { + var a = generalizedTypedefs.properties.firstWhere((p) => p.name == 'a'); + var b = C2.allFields.firstWhere((f) => f.name == 'b'); + var f = C.allFields.firstWhere((f) => f.name == 'f'); + expectAliasedTypeName(a.modelType, equals('T0')); + expectAliasedTypeName(b.modelType, equals('T0')); + expectAliasedTypeName(f.modelType, equals('T0')); }, skip: 'dart-lang/sdk#45921'); test('basic non-function typedefs work', () {