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>(&quot;hello there&quot;)');
+      expect(
+        aName.constantValue,
+        'const <a href="${htmlBasePlaceholder}ex/ExtendedShortName/ExtendedShortName.html">ExtendedShortName</a>(&quot;hello there&quot;)',
+      );
     });
 
     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.'),
     ]);
   }