Skip to content
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

Migrate Constructor element model #3987

Merged
merged 2 commits into from
Feb 5, 2025
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
32 changes: 32 additions & 0 deletions lib/src/generator/templates.runtime_renderers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3571,6 +3571,38 @@ class _Renderer_Constructor extends RendererBase<Constructor> {
);
},
),
'fileName': Property(
getValue: (CT_ c) => c.fileName,
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) as String,
nextProperty,
[...remainingNames.skip(1)],
);
},

isNullValue: (CT_ c) => false,

renderValue: (
CT_ c,
RendererBase<CT_> r,
List<MustachioNode> ast,
StringSink sink,
) {
_render_String(c.fileName, ast, r.template, sink, parent: r);
},
),
'fullKind': Property(
getValue: (CT_ c) => c.fullKind,
renderVariable: (
Expand Down
71 changes: 34 additions & 37 deletions lib/src/model/constructor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// 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.

// ignore_for_file: analyzer_use_new_elements

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/source/line_info.dart';
Expand All @@ -17,7 +15,8 @@ import 'package:dartdoc/src/model_utils.dart';

class Constructor extends ModelElement with ContainerMember, TypeParameters {
@override
ConstructorElement get element => element2.asElement;
// ignore: analyzer_use_new_elements
ConstructorElement get element => element2.asElement;

@override
final ConstructorElement2 element2;
Expand All @@ -26,7 +25,7 @@ class Constructor extends ModelElement with ContainerMember, TypeParameters {

@override
CharacterLocation? get characterLocation {
if (element.isSynthetic) {
if (element2.isSynthetic) {
// Make warnings for a synthetic constructor refer to somewhere reasonable
// since a synthetic constructor has no definition independent of the
// parent class.
Expand All @@ -39,11 +38,11 @@ class Constructor extends ModelElement with ContainerMember, TypeParameters {
bool get isPublic {
if (!super.isPublic) return false;
if (element2.hasPrivateName) return false;
var class_ = element.enclosingElement3;
var class_ = element2.enclosingElement2;
// Enums cannot be explicitly constructed or extended.
if (class_ is EnumElement) return false;
if (class_ is ClassElement) {
if (element.isFactory) return true;
if (class_ is EnumElement2) return false;
if (class_ is ClassElement2) {
if (element2.isFactory) return true;
if (class_.isSealed ||
(class_.isAbstract && class_.isFinal) ||
(class_.isAbstract && class_.isInterface)) {
Expand All @@ -62,7 +61,12 @@ class Constructor extends ModelElement with ContainerMember, TypeParameters {

@override
Container get enclosingElement =>
getModelFor(element.enclosingElement3, library) as Container;
getModelFor2(element2.enclosingElement2, library) as Container;

@override
String get fileName =>
// TODO(srawlins): It would be great to use `new.html` as the file name.
isUnnamedConstructor ? '${enclosingElement.name}.html' : '$name.html';

@override
String get aboveSidebarPath => enclosingElement.sidebarPath;
Expand All @@ -77,47 +81,40 @@ class Constructor extends ModelElement with ContainerMember, TypeParameters {
}

@override
String get fullyQualifiedName {
if (isUnnamedConstructor) return super.fullyQualifiedName;
return '${library.name}.$name';
}
String get fullyQualifiedName => '${library.name}.$name';

@override
bool get isConst => element.isConst;
bool get isConst => element2.isConst;

bool get isUnnamedConstructor => name == enclosingElement.name;
bool get isUnnamedConstructor => element2.name3 == 'new';

bool get isFactory => element.isFactory;
bool get isFactory => element2.isFactory;

@override
Kind get kind => Kind.constructor;

late final Callable modelType = getTypeFor(element.type, library) as Callable;
late final Callable modelType =
getTypeFor(element2.type, library) as Callable;

@override
String get name {
// TODO(jcollins-g): After the old lookup code is retired, rationalize
// [name] around the conventions used in referenceChildren and replace
// code there and elsewhere with simple references to the name.
var constructorName = element.name;
if (constructorName.isEmpty) {
return enclosingElement.name;
}
return '${enclosingElement.name}.$constructorName';
}
String get name =>
// TODO(jcollins-g): After the old lookup code is retired, rationalize
// [name] around the conventions used in referenceChildren and replace
// code there and elsewhere with simple references to the name.
'${enclosingElement.name}.${element2.name3}';

@override
String get nameWithGenerics {
var constructorName = element.name;
if (constructorName.isEmpty) {
var constructorName = element2.name3!;
if (constructorName == 'new') {
return '${enclosingElement.name}$genericParameters';
}
return '${enclosingElement.name}$genericParameters.$constructorName';
}

String? get shortName {
if (name.contains('.')) {
return name.substring(element.enclosingElement3.name.length + 1);
return name.substring(element2.enclosingElement2.name3!.length + 1);
} else {
return name;
}
Expand All @@ -126,17 +123,17 @@ class Constructor extends ModelElement with ContainerMember, TypeParameters {
@override
late final Map<String, CommentReferable> referenceChildren = () {
// Find the element that [parameter] is _really_ referring to.
Element? dereferenceParameter(ParameterElement? parameter) =>
Element2? dereferenceParameter(FormalParameterElement? parameter) =>
switch (parameter) {
FieldFormalParameterElement() => parameter.field,
SuperFormalParameterElement() =>
dereferenceParameter(parameter.superConstructorParameter),
FieldFormalParameterElement2() => parameter.field2,
SuperFormalParameterElement2() =>
dereferenceParameter(parameter.superConstructorParameter2),
_ => parameter
};

var parameterElements = parameters.map((parameter) {
var element = dereferenceParameter(parameter.element);
return element == null ? parameter : getModelForElement(element);
var element = dereferenceParameter(parameter.element2);
return element == null ? parameter : getModelForElement2(element);
});
return {
for (var element in parameterElements) element.referenceName: element,
Expand All @@ -146,5 +143,5 @@ class Constructor extends ModelElement with ContainerMember, TypeParameters {

@override
String get referenceName =>
isUnnamedConstructor ? enclosingElement.name : element.name;
isUnnamedConstructor ? enclosingElement.name : element2.name3!;
}
8 changes: 5 additions & 3 deletions lib/src/model/getter_setter_combo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ mixin GetterSetterCombo on ModelElement {
String linkifyConstantValue(String original) {
if (_constantInitializer is! InstanceCreationExpression) return original;

var constructorName = _constantInitializer.constructorName.toString();
var element = _constantInitializer.constructorName.element;
if (element == null) return original;

Expand All @@ -93,8 +92,11 @@ mixin GetterSetterCombo on ModelElement {

// TODO(jcollins-g): this logic really should be integrated into
// `Constructor`, but that's not trivial because of `linkedName`'s usage.
if (enclosingElement.name == target.name) {
return original.replaceAll(constructorName, target.linkedName);
if (target.isUnnamedConstructor) {
var parts = target.linkedNameParts;
// We don't want the `.new` representation of an unnamed constructor.
var linkedName = '${parts.tag}${enclosingElement.name}${parts.endTag}';
return original.replaceAll(enclosingElement.name, linkedName);
}
return original.replaceAll('${enclosingElement.name}.${target.name}',
'${enclosingElement.linkedName}.${target.linkedName}');
Expand Down
2 changes: 1 addition & 1 deletion lib/src/model/inheriting_container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ mixin Constructable implements InheritingContainer {

@override
late final List<Constructor> publicConstructorsSorted =
constructors.wherePublic.toList(growable: false)..sort();
constructors.wherePublic.toList(growable: false)..sort(byName);

@override
@visibleForOverriding
Expand Down
21 changes: 16 additions & 5 deletions lib/src/model/model_element.dart
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,9 @@ abstract class ModelElement
if (e is ExecutableMember) {
originalMember = e;
e = e.baseElement;
} else if (e is FieldMember){
} else if (e is FieldMember) {
originalMember = e;
e = e.baseElement;
e = e.baseElement;
}

// Return the cached ModelElement if it exists.
Expand Down Expand Up @@ -676,7 +676,14 @@ abstract class ModelElement
@override
Library get library => _library;

/// The name of this element, wrapped in an HTML link (an `<a>` tag) if [href]
/// is non-`null`.
late final String linkedName = () {
var parts = linkedNameParts;
return '${parts.tag}${parts.text}${parts.endTag}';
}();

({String tag, String text, String endTag}) get linkedNameParts {
// If `name` is empty, we probably have the wrong Element association or
// there's an analyzer issue.
assert(name.isNotEmpty ||
Expand All @@ -689,12 +696,16 @@ abstract class ModelElement
if (isPublicAndPackageDocumented) {
warn(PackageWarning.noCanonicalFound);
}
return htmlEscape.convert(name);
return (tag: '', text: htmlEscape.convert(name), endTag: '');
}

var cssClass = isDeprecated ? ' class="deprecated"' : '';
return '<a$cssClass href="$href">$displayName</a>';
}();
return (
tag: '<a$cssClass href="$href">',
text: displayName,
endTag: '</a>'
);
}

ParameterRenderer get _parameterRenderer => const ParameterRendererHtml();

Expand Down
9 changes: 8 additions & 1 deletion lib/src/model/nameable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:analyzer/src/utilities/extensions/element.dart';
import 'package:collection/collection.dart';
import 'package:dartdoc/src/element_type.dart';
import 'package:dartdoc/src/model/accessor.dart';
import 'package:dartdoc/src/model/constructor.dart';
import 'package:dartdoc/src/model/container.dart';
import 'package:dartdoc/src/model/library.dart';
import 'package:dartdoc/src/model/model_element.dart';
Expand Down Expand Up @@ -121,7 +122,7 @@ mixin Nameable {
enclosingContainer: enclosingContainer,
);

/// Returns the [ModelElement] for [element], instantiating it if needed.
/// Returns the [ModelElement] for [element], instantiating it if needed.
///
/// A convenience method for [ModelElement.forPropertyInducingElement], see
/// its documentation.
Expand Down Expand Up @@ -155,6 +156,12 @@ int byName(Nameable a, Nameable b) {
return compareAsciiLowerCaseNatural(a.displayName, b.displayName);
}

if (a is Constructor && b is Constructor) {
var aName = a.name.replaceFirst('.new', '');
var bName = b.name.replaceFirst('.new', '');
return compareAsciiLowerCaseNatural(aName, bName);
}

var stringCompare = compareAsciiLowerCaseNatural(a.name, b.name);
if (stringCompare != 0) {
return stringCompare;
Expand Down
Loading