Migrate Constructor element model (#3987)
diff --git a/lib/src/generator/templates.runtime_renderers.dart b/lib/src/generator/templates.runtime_renderers.dart index 803a73f..3203d95 100644 --- a/lib/src/generator/templates.runtime_renderers.dart +++ b/lib/src/generator/templates.runtime_renderers.dart
@@ -3571,6 +3571,38 @@ ); }, ), + '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: (
diff --git a/lib/src/model/constructor.dart b/lib/src/model/constructor.dart index 343daba..f8ce976 100644 --- a/lib/src/model/constructor.dart +++ b/lib/src/model/constructor.dart
@@ -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'; @@ -17,7 +15,8 @@ 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; @@ -26,7 +25,7 @@ @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. @@ -39,11 +38,11 @@ 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)) { @@ -62,7 +61,12 @@ @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; @@ -77,39 +81,32 @@ } @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'; @@ -117,7 +114,7 @@ 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; } @@ -126,17 +123,17 @@ @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, @@ -146,5 +143,5 @@ @override String get referenceName => - isUnnamedConstructor ? enclosingElement.name : element.name; + isUnnamedConstructor ? enclosingElement.name : element2.name3!; }
diff --git a/lib/src/model/getter_setter_combo.dart b/lib/src/model/getter_setter_combo.dart index ace35bb..0475c1d 100644 --- a/lib/src/model/getter_setter_combo.dart +++ b/lib/src/model/getter_setter_combo.dart
@@ -83,7 +83,6 @@ 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; @@ -93,8 +92,11 @@ // 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}');
diff --git a/lib/src/model/inheriting_container.dart b/lib/src/model/inheriting_container.dart index 7cd552f..0f8c0ec 100644 --- a/lib/src/model/inheriting_container.dart +++ b/lib/src/model/inheriting_container.dart
@@ -28,7 +28,7 @@ @override late final List<Constructor> publicConstructorsSorted = - constructors.wherePublic.toList(growable: false)..sort(); + constructors.wherePublic.toList(growable: false)..sort(byName); @override @visibleForOverriding
diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart index 2c7f3f8..97e4075 100644 --- a/lib/src/model/model_element.dart +++ b/lib/src/model/model_element.dart
@@ -228,9 +228,9 @@ 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. @@ -676,7 +676,14 @@ @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 || @@ -689,12 +696,16 @@ 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();
diff --git a/lib/src/model/nameable.dart b/lib/src/model/nameable.dart index a7fc9c4..423948f 100644 --- a/lib/src/model/nameable.dart +++ b/lib/src/model/nameable.dart
@@ -12,6 +12,7 @@ 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'; @@ -121,7 +122,7 @@ 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. @@ -155,6 +156,12 @@ 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;
diff --git a/test/constructors_test.dart b/test/constructors_test.dart index 81f7449..2e2d74c 100644 --- a/test/constructors_test.dart +++ b/test/constructors_test.dart
@@ -27,9 +27,8 @@ } '''); var c = library.classes.named('C').constructors.first; - expect(c.name, equals('C')); - // TODO(srawlins): This should be `constructors.C.new`. - expect(c.fullyQualifiedName, 'constructors.C.C'); + expect(c.name, equals('C.new')); + expect(c.fullyQualifiedName, 'constructors.C.new'); expect(c.isPublic, isTrue); expect(c.documentationAsHtml, '<p>Constructor.</p>'); } @@ -42,9 +41,8 @@ } '''); var c = library.classes.named('C').constructors.first; - expect(c.name, equals('C')); - // TODO(srawlins): This should be `constructors.C.new`. - expect(c.fullyQualifiedName, 'constructors.C.C'); + expect(c.name, equals('C.new')); + expect(c.fullyQualifiedName, 'constructors.C.new'); expect(c.isPublic, isFalse); expect(c.documentationAsHtml, '<p>Constructor.</p>'); } @@ -57,9 +55,8 @@ } '''); var c = library.classes.named('C').constructors.first; - expect(c.name, equals('C')); - // TODO(srawlins): This should be `constructors.C.new`. - expect(c.fullyQualifiedName, 'constructors.C.C'); + expect(c.name, equals('C.new')); + expect(c.fullyQualifiedName, 'constructors.C.new'); expect(c.isPublic, isFalse); expect(c.documentationAsHtml, '<p>Constructor.</p>'); } @@ -86,7 +83,7 @@ } '''); var c = library.classes.named('_C').constructors.first; - expect(c.name, equals('_C')); + expect(c.name, equals('_C.new')); expect(c.isPublic, isFalse); expect(c.documentationAsHtml, '<p>Constructor.</p>'); } @@ -96,7 +93,7 @@ class C {} '''); var c = library.classes.named('C').constructors.first; - expect(c.name, equals('C')); + expect(c.name, equals('C.new')); expect(c.isPublic, isTrue); expect(c.documentationAsHtml, ''); } @@ -123,7 +120,7 @@ } '''); var c = library.classes.named('C').constructors.first; - expect(c.name, equals('C')); + expect(c.name, equals('C.new')); expect(c.isPublic, isTrue); expect(c.documentationAsHtml, '<p>Constructor.</p>'); } @@ -136,9 +133,8 @@ } '''); var c = library.classes.named('C').constructors.first; - expect(c.name, equals('C')); - // TODO(srawlins): This should be `constructors.C.new`. - expect(c.fullyQualifiedName, 'constructors.C.C'); + expect(c.name, equals('C.new')); + expect(c.fullyQualifiedName, 'constructors.C.new'); expect(c.isPublic, isTrue); expect(c.documentationAsHtml, '<p>Constructor.</p>'); } @@ -151,7 +147,7 @@ } '''); var c = library.classes.named('C').constructors.first; - expect(c.name, equals('C')); + expect(c.name, equals('C.new')); expect(c.isPublic, isFalse); expect(c.documentationAsHtml, '<p>Constructor.</p>'); } @@ -180,9 +176,8 @@ } '''); var e = library.enums.named('E').constructors.first; - expect(e.name, equals('E')); - // TODO(srawlins): This should be `constructors.E.new`. - expect(e.fullyQualifiedName, 'constructors.E.E'); + expect(e.name, equals('E.new')); + expect(e.fullyQualifiedName, 'constructors.E.new'); expect(e.isPublic, isFalse); expect(e.documentationAsHtml, '<p>Constructor.</p>'); } @@ -196,7 +191,6 @@ '''); var etNamed = library.extensionTypes.named('ET').constructors.named('ET.named'); - expect(etNamed.name, equals('ET.named')); expect(etNamed.fullyQualifiedName, 'constructors.ET.named'); expect(etNamed.isPublic, isTrue); expect(etNamed.documentationAsHtml, '<p>Constructor.</p>'); @@ -208,7 +202,6 @@ '''); var etNamed = library.extensionTypes.named('ET').constructors.named('ET.named'); - expect(etNamed.name, equals('ET.named')); expect(etNamed.fullyQualifiedName, 'constructors.ET.named'); expect(etNamed.isPublic, isTrue); } @@ -217,10 +210,8 @@ var library = await bootPackageWithLibrary(''' extension type ET(int it) {} '''); - var et = library.extensionTypes.named('ET').constructors.named('ET'); - expect(et.name, equals('ET')); - // TODO(srawlins): This should be `constructors.ET.new`. - expect(et.fullyQualifiedName, 'constructors.ET.ET'); + var et = library.extensionTypes.named('ET').constructors.named('ET.new'); + expect(et.fullyQualifiedName, 'constructors.ET.new'); expect(et.isPublic, isTrue); } @@ -231,10 +222,9 @@ ET(this.it); } '''); - var etNamed = library.extensionTypes.named('ET').constructors.named('ET'); - expect(etNamed.name, equals('ET')); - // TODO(srawlins): This should be `constructors.ET.new`. - expect(etNamed.fullyQualifiedName, 'constructors.ET.ET'); + var etNamed = + library.extensionTypes.named('ET').constructors.named('ET.new'); + expect(etNamed.fullyQualifiedName, 'constructors.ET.new'); expect(etNamed.isPublic, isTrue); expect(etNamed.documentationAsHtml, '<p>Constructor.</p>'); }
diff --git a/test/end2end/model_test.dart b/test/end2end/model_test.dart index b8745b7..b49f7f9 100644 --- a/test/end2end/model_test.dart +++ b/test/end2end/model_test.dart
@@ -2414,7 +2414,7 @@ aNonDefaultConstructor = baseForDocComments.constructors .named('BaseForDocComments.aNonDefaultConstructor'); defaultConstructor = - baseForDocComments.constructors.named('BaseForDocComments'); + baseForDocComments.constructors.named('BaseForDocComments.new'); somethingShadowyParameter = defaultConstructor.parameters.named('somethingShadowy'); initializeMe = baseForDocComments.allFields.named('initializeMe'); @@ -2473,7 +2473,7 @@ anotherConstructor = FactoryConstructorThings.constructors .named('FactoryConstructorThings.anotherConstructor'); factoryConstructorThingsDefault = FactoryConstructorThings.constructors - .named('FactoryConstructorThings'); + .named('FactoryConstructorThings.new'); aName = anotherName.parameters.named('aName'); anotherNameParameter = anotherName.parameters.named('anotherName'); @@ -3974,8 +3974,10 @@ }); test('substrings of the constant values type are not linked (#1535)', () { - expect(aName.constantValue, - 'const <a href="${htmlBasePlaceholder}ex/ExtendedShortName/ExtendedShortName.html">ExtendedShortName</a>("hello there")'); + expect( + aName.constantValue, + 'const <a href="${htmlBasePlaceholder}ex/ExtendedShortName/ExtendedShortName.html">ExtendedShortName</a>("hello there")', + ); }); test('constant field values are escaped', () { @@ -4040,10 +4042,10 @@ constCat = exLibrary.classes.named('ConstantCat'); constructorTester = fakeLibrary.classes.named('ConstructorTester'); constCatConstructor = constCat.constructors.first; - appleDefaultConstructor = apple.constructors.named('Apple'); + appleDefaultConstructor = apple.constructors.named('Apple.new'); appleConstructorFromString = apple.constructors.named('Apple.fromString'); constructorTesterDefault = - constructorTester.constructors.named('ConstructorTester'); + constructorTester.constructors.named('ConstructorTester.new'); constructorTesterFromSomething = constructorTester.constructors .named('ConstructorTester.fromSomething'); referToADefaultConstructor = @@ -4095,8 +4097,8 @@ test('has constructor', () { expect(appleDefaultConstructor, isNotNull); - expect(appleDefaultConstructor.name, equals('Apple')); - expect(appleDefaultConstructor.shortName, equals('Apple')); + expect(appleDefaultConstructor.name, equals('Apple.new')); + expect(appleDefaultConstructor.shortName, equals('new')); }); test('title has factory qualifier', () {
diff --git a/test/parameters_test.dart b/test/parameters_test.dart index f43a636..d9c3908 100644 --- a/test/parameters_test.dart +++ b/test/parameters_test.dart
@@ -251,7 +251,7 @@ C(this.p); } '''); - var cConstructor = library.classes.named('C').constructors.named('C'); + var cConstructor = library.classes.named('C').constructors.named('C.new'); // There is no link, but also no wrong link or crash. expect(cConstructor.documentationAsHtml, '<p>Text <code>p</code>.</p>'); } @@ -264,7 +264,7 @@ C(this._); } '''); - var cConstructor = library.classes.named('C').constructors.named('C'); + var cConstructor = library.classes.named('C').constructors.named('C.new'); // There is no link, but also no wrong link or crash. expect(cConstructor.documentationAsHtml, '<p>Text <code>_</code>.</p>'); } @@ -279,7 +279,7 @@ D(super._) {} } '''); - var dConstructor = library.classes.named('D').constructors.named('D'); + var dConstructor = library.classes.named('D').constructors.named('D.new'); // There is no link, but also no wrong link or crash. expect(dConstructor.documentationAsHtml, '<p>Text <code>_</code>.</p>'); }
diff --git a/test/templates/class_test.dart b/test/templates/class_test.dart index 2e46a46..c70ef2b 100644 --- a/test/templates/class_test.dart +++ b/test/templates/class_test.dart
@@ -218,7 +218,7 @@ htmlLines.expectMainContentContainsAllInOrder([ matches('<h2>Constructors</h2>'), - matches('<a href="../lib/C/C.html">C</a>'), + matches('<a href="../lib/C/C.html">C.new</a>'), matches('An unnamed constructor.'), ]); }
diff --git a/test/templates/extension_type_test.dart b/test/templates/extension_type_test.dart index 5c9a7cb..4d50a24 100644 --- a/test/templates/extension_type_test.dart +++ b/test/templates/extension_type_test.dart
@@ -166,9 +166,8 @@ htmlLines.expectMainContentContainsAllInOrder([ matches('<h2>Constructors</h2>'), - matches('<a href="../lib/One/One.html">One</a>'), - matches('<a href="../lib/One/One.named.html">' - 'One.named</a>'), + matches('<a href="../lib/One/One.html">One.new</a>'), + matches('<a href="../lib/One/One.named.html">One.named</a>'), matches('A named constructor.'), ]); }