diff --git a/lib/src/model/annotation.dart b/lib/src/model/annotation.dart
index 63576d3ce2..f29178c31c 100644
--- a/lib/src/model/annotation.dart
+++ b/lib/src/model/annotation.dart
@@ -5,6 +5,7 @@
import 'dart:convert';
import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
import 'package:dartdoc/src/element_type.dart';
import 'package:dartdoc/src/model/attribute.dart';
import 'package:dartdoc/src/model/class.dart';
@@ -36,14 +37,47 @@ final class Annotation extends Attribute {
var parameterText =
source.substring(startIndex == -1 ? source.length : startIndex);
- return '@$linkedName${const HtmlEscape().convert(parameterText)}';
+ var escapedParameterText = const HtmlEscape().convert(parameterText);
+ return '@$linkedName$_linkedTypeArguments$escapedParameterText';
}
@override
- String get linkedName => _annotation.element is PropertyAccessorElement
- ? _packageGraph.getModelForElement(_annotation.element!).linkedName
- // TODO(jcollins-g): consider linking to constructor instead of type?
- : _modelType.linkedName;
+ String get linkedName => switch (_annotation.element) {
+ PropertyAccessorElement element =>
+ _packageGraph.getModelForElement(element).linkedName,
+ ConstructorElement element =>
+ _packageGraph.getModelForElement(element).linkedName,
+ _ => _modelType.linkedName
+ };
+
+ /// The linked type argument text, with `<` and `>`, if there are any type
+ /// arguments.
+ String get _linkedTypeArguments {
+ if (_annotation.element is PropertyAccessorElement) {
+ return '';
+ }
+
+ var type = _modelType.type;
+ if (type is! InterfaceType) {
+ return '';
+ }
+
+ var typeArguments = type.typeArguments;
+ if (typeArguments.isEmpty) {
+ return '';
+ }
+
+ var buffer = StringBuffer();
+ buffer.write('<');
+ for (var t in typeArguments) {
+ buffer.write(_packageGraph.getTypeFor(t, _library).linkedName);
+ if (t != typeArguments.last) {
+ buffer.write(', ');
+ }
+ }
+ buffer.write('>');
+ return buffer.toString();
+ }
late final ElementType _modelType = switch (_annotation.element) {
ConstructorElement(:var returnType) =>
diff --git a/lib/src/model/constructor.dart b/lib/src/model/constructor.dart
index 0503b76978..8152901345 100644
--- a/lib/src/model/constructor.dart
+++ b/lib/src/model/constructor.dart
@@ -92,8 +92,7 @@ class Constructor extends ModelElement with ContainerMember, TypeParameters {
@override
Kind get kind => Kind.constructor;
- late final Callable modelType =
- getTypeFor(element.type, library) as Callable;
+ late final Callable modelType = getTypeFor(element.type, library) as Callable;
@override
String get name =>
@@ -102,6 +101,11 @@ class Constructor extends ModelElement with ContainerMember, TypeParameters {
// code there and elsewhere with simple references to the name.
'${enclosingElement.name}.${element.name}';
+ @override
+ String get displayName => isUnnamedConstructor
+ ? enclosingElement.name
+ : '${enclosingElement.name}.${element.name}';
+
@override
String get nameWithGenerics {
var constructorName = element.name!;
diff --git a/test/annotations_test.dart b/test/annotations_test.dart
index 69f63dfc9b..c972de574d 100644
--- a/test/annotations_test.dart
+++ b/test/annotations_test.dart
@@ -48,11 +48,11 @@ int value = 0;
var annotation = valueVariable.annotations.single;
expect(
annotation.linkedName,
- 'Deprecated',
+ 'Deprecated',
);
expect(
annotation.linkedNameWithParameters,
- '@Deprecated'
+ '@Deprecated'
'('text')',
);
}
@@ -97,16 +97,40 @@ int value = 0;
var annotation = valueVariable.annotations.single;
expect(
annotation.linkedName,
- ''
+ ''
'MyAnnotation',
);
expect(
annotation.linkedNameWithParameters,
- '@'
+ '@'
'MyAnnotation(true)',
);
}
+ void test_locallyDeclaredConstructorCall_named() async {
+ var library = await bootPackageWithLibrary('''
+class MyAnnotation {
+ const MyAnnotation.named(bool b);
+}
+
+@MyAnnotation.named(true)
+int value = 0;
+''');
+ var valueVariable = library.properties.named('value');
+ expect(valueVariable.hasAnnotations, true);
+ var annotation = valueVariable.annotations.single;
+ expect(
+ annotation.linkedName,
+ ''
+ 'MyAnnotation.named',
+ );
+ expect(
+ annotation.linkedNameWithParameters,
+ '@'
+ 'MyAnnotation.named(true)',
+ );
+ }
+
void test_genericConstructorCall() async {
var library = await bootPackageWithLibrary('''
class Ann {
@@ -122,15 +146,13 @@ int value = 0;
var annotation = valueVariable.annotations.single;
expect(
annotation.linkedName,
- 'Ann'
- '<'
- 'bool>',
+ 'Ann',
);
expect(
annotation.linkedNameWithParameters,
- '@Ann'
- '<'
- 'bool>(true)',
+ '@Ann'
+ '<bool>'
+ '(true)',
);
}
}
diff --git a/test/end2end/model_test.dart b/test/end2end/model_test.dart
index 4f6fe4034f..bcda54076c 100644
--- a/test/end2end/model_test.dart
+++ b/test/end2end/model_test.dart
@@ -190,7 +190,9 @@ void main() async {
test('Verify type arguments on annotations renders, including parameters',
() {
var ab0 =
- '@A<B>(0)';
+ '@A'
+ '<B>'
+ '(0)';
expect(genericMetadata.annotations.first.linkedNameWithParameters,
equals(ab0));
diff --git a/test/enums_test.dart b/test/enums_test.dart
index 224cbe02c8..80fb8514c2 100644
--- a/test/enums_test.dart
+++ b/test/enums_test.dart
@@ -207,7 +207,7 @@ enum E { one, two, three }
expect(eEnum.hasAnnotations, true);
expect(eEnum.annotations, hasLength(1));
expect(eEnum.annotations.single.linkedName,
- 'C');
+ 'C');
}
void test_hasDocComment() async {
diff --git a/test/templates/class_test.dart b/test/templates/class_test.dart
index c70ef2bde0..2e46a46a88 100644
--- a/test/templates/class_test.dart
+++ b/test/templates/class_test.dart
@@ -218,7 +218,7 @@ class C {
htmlLines.expectMainContentContainsAllInOrder([
matches('Constructors
'),
- matches('C.new'),
+ matches('C'),
matches('An unnamed constructor.'),
]);
}
diff --git a/test/templates/enum_test.dart b/test/templates/enum_test.dart
index 594714b50c..23a75439b9 100644
--- a/test/templates/enum_test.dart
+++ b/test/templates/enum_test.dart
@@ -180,7 +180,7 @@ extension Ext on E {}
matches('Annotations'),
matches(''),
matches(
- r'- @C\('message'\)
'),
+ r'- @C<dynamic>\('message'\)
'),
matches('
'),
]);
});
diff --git a/test/templates/extension_type_test.dart b/test/templates/extension_type_test.dart
index 4d50a24844..c15eee8a0e 100644
--- a/test/templates/extension_type_test.dart
+++ b/test/templates/extension_type_test.dart
@@ -166,7 +166,7 @@ extension type One(int it) {
htmlLines.expectMainContentContainsAllInOrder([
matches('Constructors
'),
- matches('One.new'),
+ matches('One'),
matches('One.named'),
matches('A named constructor.'),
]);
diff --git a/test/templates/field_test.dart b/test/templates/field_test.dart
index e23f791054..32e8a76014 100644
--- a/test/templates/field_test.dart
+++ b/test/templates/field_test.dart
@@ -54,7 +54,7 @@ class A {
matches(''),
matches('- @deprecated
'),
matches(
- r'- @A\('message'\)
'),
+ r'- @A\('message'\)
'),
matches('
'),
],
);
diff --git a/test/templates/method_test.dart b/test/templates/method_test.dart
index a87be07beb..41d887789b 100644
--- a/test/templates/method_test.dart
+++ b/test/templates/method_test.dart
@@ -6,7 +6,6 @@ import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../src/utils.dart';
-
import 'template_test_base.dart';
void main() async {
@@ -53,7 +52,7 @@ class C {
matches(''),
matches('- @deprecated
'),
matches(
- r'- @A\('message'\)
'),
+ r'- @A\('message'\)
'),
matches('
'),
],
);