Skip to content

Generic metadata and type parameters fixup #2649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
May 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 12 additions & 56 deletions lib/src/element_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,10 @@ abstract class DefinedElementType extends ElementType {
}

/// Any callable ElementType will mix-in this class, whether anonymous or not.
abstract class CallableElementTypeMixin implements ElementType {
Iterable<ElementType> _typeArguments;
mixin CallableElementTypeMixin implements ElementType {
@override
// TODO(jcollins-g): remove after dart-lang/dartdoc#2648 is fixed.
String get linkedName;

ModelElement get returnElement => returnType is DefinedElementType
? (returnType as DefinedElementType).modelElement
Expand All @@ -400,50 +402,12 @@ abstract class CallableElementTypeMixin implements ElementType {
@override
FunctionType get type => _type;

// TODO(jcollins-g): Rewrite this and improve object model so this doesn't
// require type checking everywhere.
Iterable<ElementType> get typeArguments {
if (_typeArguments == null) {
Iterable<DartType> dartTypeArguments;
if (returnedFrom is FunctionTypeElementType) {
if (type.typeFormals.isEmpty) {
dartTypeArguments = type.aliasArguments;
} else {
dartTypeArguments = type.typeFormals.map(_legacyTypeParameterType);
}
} else {
if (type.typeFormals.isEmpty) {
dartTypeArguments = type.aliasArguments;
} else if (returnedFrom != null &&
returnedFrom.type.element is GenericFunctionTypeElement) {
_typeArguments = (returnedFrom as DefinedElementType).typeArguments;
} else {
dartTypeArguments = type.typeFormals.map(_legacyTypeParameterType);
}
}
if (dartTypeArguments != null) {
_typeArguments = dartTypeArguments
.map((f) => ElementType.from(f, library, packageGraph))
.toList();
}
}
return _typeArguments;
}

/// Return the [TypeParameterType] with the legacy nullability for the given
/// type parameter [element].
///
/// TODO(scheglov): This method is a work around that fact that DartDoc
/// currently represents both type formals and uses of them as actual types,
/// as [TypeParameterType]s. This was not perfect, but worked before Null
/// safety. With Null safety, types have nullability suffixes, but type
/// formals should not. Eventually we should separate models for type formals
/// and types.
static TypeParameterType _legacyTypeParameterType(
TypeParameterElement element,
) {
return element.instantiate(nullabilitySuffix: NullabilitySuffix.star);
}
Iterable<ElementType> _typeArguments;
Iterable<ElementType> get typeArguments =>
_typeArguments ??= type.aliasArguments
?.map((f) => ElementType.from(f, library, packageGraph))
?.toList() ??
[];
}

/// A callable type that may or may not be backed by a declaration using the generic
Expand All @@ -455,16 +419,8 @@ class CallableElementType extends ParameterizedElementType
: super(t, library, packageGraph, element, returnedFrom);

@override
String get linkedName {
if (_linkedName == null) {
if (name != null && name.isNotEmpty) {
_linkedName = super.linkedName;
} else {
_linkedName = _renderer.renderLinkedName(this);
}
}
return _linkedName;
}
String get name =>
super.name != null && super.name.isNotEmpty ? super.name : 'Function';

@override
ElementTypeRenderer<CallableElementType> get _renderer =>
Expand Down
137 changes: 70 additions & 67 deletions lib/src/generator/templates.renderers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,26 @@ class _Renderer_CallableElementTypeMixin
_propertyMapCache.putIfAbsent(
CT_,
() => {
..._Renderer_Object.propertyMap<CT_>(),
'linkedName': Property(
getValue: (CT_ c) => c.linkedName,
renderVariable:
(CT_ c, Property<CT_> self, List<String> remainingNames) {
if (remainingNames.isEmpty) {
return self.getValue(c).toString();
}
var name = remainingNames.first;
var nextProperty =
_Renderer_String.propertyMap().getValue(name);
return nextProperty.renderVariable(self.getValue(c),
nextProperty, [...remainingNames.skip(1)]);
},
isNullValue: (CT_ c) => c.linkedName == null,
renderValue:
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
return _render_String(c.linkedName, ast, r.template,
parent: r);
},
),
'returnElement': Property(
getValue: (CT_ c) => c.returnElement,
renderVariable:
Expand Down Expand Up @@ -6042,60 +6061,35 @@ String _render_FunctionTypedef(

class _Renderer_FunctionTypedef extends RendererBase<FunctionTypedef> {
static final Map<Type, Object> _propertyMapCache = {};
static Map<String, Property<CT_>> propertyMap<
CT_ extends FunctionTypedef>() =>
_propertyMapCache.putIfAbsent(
CT_,
() => {
..._Renderer_Typedef.propertyMap<CT_>(),
'aliasedType': Property(
getValue: (CT_ c) => c.aliasedType,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(
c, remainingNames, 'FunctionType'),
isNullValue: (CT_ c) => c.aliasedType == null,
renderValue:
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
return renderSimple(c.aliasedType, ast, r.template,
parent: r);
},
),
'genericTypeParameters': Property(
getValue: (CT_ c) => c.genericTypeParameters,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(
c, remainingNames, 'List<TypeParameterElement>'),
renderIterable:
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
return c.genericTypeParameters.map(
(e) => renderSimple(e, ast, r.template, parent: r));
},
),
'modelType': Property(
getValue: (CT_ c) => c.modelType,
renderVariable:
(CT_ c, Property<CT_> self, List<String> remainingNames) {
if (remainingNames.isEmpty) {
return self.getValue(c).toString();
}
var name = remainingNames.first;
var nextProperty =
_Renderer_CallableElementTypeMixin.propertyMap()
.getValue(name);
return nextProperty.renderVariable(self.getValue(c),
nextProperty, [...remainingNames.skip(1)]);
},
isNullValue: (CT_ c) => c.modelType == null,
renderValue:
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
return _render_CallableElementTypeMixin(
c.modelType, ast, r.template,
parent: r);
},
),
});
static Map<String, Property<CT_>>
propertyMap<CT_ extends FunctionTypedef>() =>
_propertyMapCache.putIfAbsent(
CT_,
() => {
..._Renderer_Typedef.propertyMap<CT_>(),
'modelType': Property(
getValue: (CT_ c) => c.modelType,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) {
if (remainingNames.isEmpty) {
return self.getValue(c).toString();
}
var name = remainingNames.first;
var nextProperty =
_Renderer_CallableElementTypeMixin.propertyMap()
.getValue(name);
return nextProperty.renderVariable(self.getValue(c),
nextProperty, [...remainingNames.skip(1)]);
},
isNullValue: (CT_ c) => c.modelType == null,
renderValue: (CT_ c, RendererBase<CT_> r,
List<MustachioNode> ast) {
return _render_CallableElementTypeMixin(
c.modelType, ast, r.template,
parent: r);
},
),
});

_Renderer_FunctionTypedef(
FunctionTypedef context, RendererBase<Object> parent, Template template)
Expand Down Expand Up @@ -13914,18 +13908,6 @@ class _Renderer_Typedef extends RendererBase<Typedef> {
parent: r);
},
),
'genericTypeParameters': Property(
getValue: (CT_ c) => c.genericTypeParameters,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(
c, remainingNames, 'List<TypeParameterElement>'),
renderIterable:
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
return c.genericTypeParameters.map(
(e) => renderSimple(e, ast, r.template, parent: r));
},
),
'href': Property(
getValue: (CT_ c) => c.href,
renderVariable:
Expand Down Expand Up @@ -13971,6 +13953,27 @@ class _Renderer_Typedef extends RendererBase<Typedef> {
return _render_String(c.kind, ast, r.template, parent: r);
},
),
'linkedGenericParameters': Property(
getValue: (CT_ c) => c.linkedGenericParameters,
renderVariable:
(CT_ c, Property<CT_> self, List<String> remainingNames) {
if (remainingNames.isEmpty) {
return self.getValue(c).toString();
}
var name = remainingNames.first;
var nextProperty =
_Renderer_String.propertyMap().getValue(name);
return nextProperty.renderVariable(self.getValue(c),
nextProperty, [...remainingNames.skip(1)]);
},
isNullValue: (CT_ c) => c.linkedGenericParameters == null,
renderValue:
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
return _render_String(
c.linkedGenericParameters, ast, r.template,
parent: r);
},
),
'modelType': Property(
getValue: (CT_ c) => c.modelType,
renderVariable:
Expand Down
20 changes: 3 additions & 17 deletions lib/src/model/typedef.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ class Typedef extends ModelElement
@override
String get genericParameters => _renderer.renderGenericParameters(this);

List<TypeParameterElement> get genericTypeParameters =>
element.typeParameters;
@override
String get linkedGenericParameters =>
_renderer.renderLinkedGenericParameters(this);

@override
String get filePath => '${library.dirName}/$fileName';
Expand Down Expand Up @@ -87,21 +88,6 @@ class FunctionTypedef extends Typedef {
TypeAliasElement element, Library library, PackageGraph packageGraph)
: super(element, library, packageGraph);

@override
FunctionType get aliasedType => super.aliasedType;

@override
List<TypeParameterElement> get genericTypeParameters {
var aliasedTypeElement = aliasedType.aliasElement;
if (aliasedTypeElement is FunctionTypedElement) {
return aliasedTypeElement.typeParameters;
}
if (aliasedType.typeFormals.isNotEmpty == true) {
return aliasedType.typeFormals;
}
return super.genericTypeParameters;
}

@override
CallableElementTypeMixin get modelType => super.modelType;
}
32 changes: 32 additions & 0 deletions lib/src/render/element_type_renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ class CallableElementTypeRendererHtml
buf.write(elementType.returnType.linkedName);
return wrapNullabilityParens(elementType, buf.toString());
}

@override
String renderNameWithGenerics(CallableElementType elementType) {
var buf = StringBuffer();
buf.write(elementType.name);
if (elementType.typeArguments != null) {
if (elementType.typeArguments.isNotEmpty &&
!elementType.typeArguments.every((t) => t.name == 'dynamic')) {
buf.write('&lt;');
buf.writeAll(
elementType.typeArguments.map((t) => t.nameWithGenerics), ', ');
buf.write('>');
}
}
return wrapNullability(elementType, buf.toString());
}
}

// Markdown implementations
Expand Down Expand Up @@ -255,4 +271,20 @@ class CallableElementTypeRendererMd
buf.write(elementType.returnType.linkedName);
return wrapNullabilityParens(elementType, buf.toString());
}

@override
String renderNameWithGenerics(CallableElementType elementType) {
var buf = StringBuffer();
buf.write(elementType.name);
if (elementType.typeArguments != null) {
if (elementType.typeArguments.isNotEmpty &&
!elementType.typeArguments.every((t) => t.name == 'dynamic')) {
buf.write('&lt;');
buf.writeAll(
elementType.typeArguments.map((t) => t.nameWithGenerics), ', ');
buf.write('>');
}
}
return wrapNullability(elementType, buf.toString());
}
}
25 changes: 20 additions & 5 deletions lib/src/render/type_parameters_renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import 'package:dartdoc/src/model/type_parameter.dart';

abstract class TypeParametersRenderer {
const TypeParametersRenderer();

String renderGenericParameters(TypeParameters typeParameters);

String renderLinkedGenericParameters(TypeParameters typeParameters);
Expand All @@ -19,7 +21,10 @@ class TypeParametersRendererHtml implements TypeParametersRenderer {
return '';
}
var joined = typeParameters.typeParameters
.map((t) => t.name)
.map((t) => [
...t.annotations.map((a) => a.linkedNameWithParameters),
t.name
].join(' '))
.join('</span>, <span class="type-parameter">');
return '&lt;<wbr><span class="type-parameter">$joined</span>&gt;';
}
Expand All @@ -30,7 +35,10 @@ class TypeParametersRendererHtml implements TypeParametersRenderer {
return '';
}
var joined = typeParameters.typeParameters
.map((t) => t.linkedName)
.map((t) => [
...t.annotations.map((a) => a.linkedNameWithParameters),
t.linkedName
].join(' '))
.join('</span>, <span class="type-parameter">');
return '<span class="signature">&lt;<wbr><span class="type-parameter">$joined</span>&gt;</span>';
}
Expand All @@ -40,12 +48,19 @@ class TypeParametersRendererMd implements TypeParametersRenderer {
const TypeParametersRendererMd();

@override
String renderGenericParameters(TypeParameters typeParameters) =>
_compose(typeParameters.typeParameters, (t) => t.name);
String renderGenericParameters(TypeParameters typeParameters) => _compose(
typeParameters.typeParameters,
(t) => [...t.annotations.map((a) => a.linkedNameWithParameters), t.name]
.join(' '));

@override
String renderLinkedGenericParameters(TypeParameters typeParameters) =>
_compose(typeParameters.typeParameters, (t) => t.linkedName);
_compose(
typeParameters.typeParameters,
(t) => [
...t.annotations.map((a) => a.linkedNameWithParameters),
t.linkedName
].join(' '));

String _compose(List<TypeParameter> typeParameters,
String Function(TypeParameter) mapfn) {
Expand Down
Loading