Skip to content

Commit 0577ca1

Browse files
authored
Improve logic of calculating a "fully qualified name" (#3836)
For improvements that I'm working on in another CL, it made sense to tidy up this code. The `_buildFullyQualifiedName` function was unnecessarily recursive and complex. And then the `_fullyQualifiedNameWithoutLibrary` function (a) has a complex name and (b) also worked _further_ to do undo some work done in `_buildFullyQualifiedName`. This CL tidies these two "fields". * Combine `fullyQualifiedName` and `_buildFullyQualifiedName` into one getter (removing one field). * Combine `fullyQualifiedNameWithoutLibrary` and `_fullyQualifiedNameWithoutLibrary` into one late final field, remove recursion, and simplify name to `qualifiedName`. Also mark as `@visibleForOverriding`. It should be private but because of the mixin hierarchy, it is declared in one file, and defined in another. * Improve documentation.
1 parent 271c051 commit 0577ca1

11 files changed

+107
-123
lines changed

lib/src/generator/templates.runtime_renderers.dart

Lines changed: 23 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3974,30 +3974,6 @@ class _Renderer_DocumentationComment
39743974
getters: _invisibleGetters['Documentation']!);
39753975
},
39763976
),
3977-
'fullyQualifiedNameWithoutLibrary': Property(
3978-
getValue: (CT_ c) => c.fullyQualifiedNameWithoutLibrary,
3979-
renderVariable:
3980-
(CT_ c, Property<CT_> self, List<String> remainingNames) {
3981-
if (remainingNames.isEmpty) {
3982-
return self.getValue(c).toString();
3983-
}
3984-
var name = remainingNames.first;
3985-
var nextProperty =
3986-
_Renderer_String.propertyMap().getValue(name);
3987-
return nextProperty.renderVariable(
3988-
self.getValue(c) as String,
3989-
nextProperty,
3990-
[...remainingNames.skip(1)]);
3991-
},
3992-
isNullValue: (CT_ c) =>
3993-
c.fullyQualifiedNameWithoutLibrary == null,
3994-
renderValue: (CT_ c, RendererBase<CT_> r,
3995-
List<MustachioNode> ast, StringSink sink) {
3996-
_render_String(c.fullyQualifiedNameWithoutLibrary!, ast,
3997-
r.template, sink,
3998-
parent: r);
3999-
},
4000-
),
40013977
'hasDocumentationComment': Property(
40023978
getValue: (CT_ c) => c.hasDocumentationComment,
40033979
renderVariable: (CT_ c, Property<CT_> self,
@@ -10313,29 +10289,6 @@ class _Renderer_ModelElement extends RendererBase<ModelElement> {
1031310289
parent: r);
1031410290
},
1031510291
),
10316-
'fullyQualifiedNameWithoutLibrary': Property(
10317-
getValue: (CT_ c) => c.fullyQualifiedNameWithoutLibrary,
10318-
renderVariable:
10319-
(CT_ c, Property<CT_> self, List<String> remainingNames) {
10320-
if (remainingNames.isEmpty) {
10321-
return self.getValue(c).toString();
10322-
}
10323-
var name = remainingNames.first;
10324-
var nextProperty =
10325-
_Renderer_String.propertyMap().getValue(name);
10326-
return nextProperty.renderVariable(
10327-
self.getValue(c) as String,
10328-
nextProperty,
10329-
[...remainingNames.skip(1)]);
10330-
},
10331-
isNullValue: (CT_ c) => false,
10332-
renderValue: (CT_ c, RendererBase<CT_> r,
10333-
List<MustachioNode> ast, StringSink sink) {
10334-
_render_String(c.fullyQualifiedNameWithoutLibrary, ast,
10335-
r.template, sink,
10336-
parent: r);
10337-
},
10338-
),
1033910292
'hasAnnotations': Property(
1034010293
getValue: (CT_ c) => c.hasAnnotations,
1034110294
renderVariable: (CT_ c, Property<CT_> self,
@@ -10783,6 +10736,28 @@ class _Renderer_ModelElement extends RendererBase<ModelElement> {
1078310736
parent: r, getters: _invisibleGetters['Context']!);
1078410737
},
1078510738
),
10739+
'qualifiedName': Property(
10740+
getValue: (CT_ c) => c.qualifiedName,
10741+
renderVariable:
10742+
(CT_ c, Property<CT_> self, List<String> remainingNames) {
10743+
if (remainingNames.isEmpty) {
10744+
return self.getValue(c).toString();
10745+
}
10746+
var name = remainingNames.first;
10747+
var nextProperty =
10748+
_Renderer_String.propertyMap().getValue(name);
10749+
return nextProperty.renderVariable(
10750+
self.getValue(c) as String,
10751+
nextProperty,
10752+
[...remainingNames.skip(1)]);
10753+
},
10754+
isNullValue: (CT_ c) => false,
10755+
renderValue: (CT_ c, RendererBase<CT_> r,
10756+
List<MustachioNode> ast, StringSink sink) {
10757+
_render_String(c.qualifiedName, ast, r.template, sink,
10758+
parent: r);
10759+
},
10760+
),
1078610761
'sourceCode': Property(
1078710762
getValue: (CT_ c) => c.sourceCode,
1078810763
renderVariable:
@@ -15888,11 +15863,11 @@ const _invisibleGetters = {
1588815863
'documentationLocal',
1588915864
'element',
1589015865
'elementDocumentation',
15891-
'fullyQualifiedNameWithoutLibrary',
1589215866
'hasDocumentationComment',
1589315867
'hasNodoc',
1589415868
'needsPrecache',
1589515869
'pathContext',
15870+
'qualifiedName',
1589615871
'sourceFileName'
1589715872
},
1589815873
'Element': {

lib/src/model/documentation_comment.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ mixin DocumentationComment
142142

143143
String? get sourceFileName;
144144

145-
String? get fullyQualifiedNameWithoutLibrary;
145+
/// The name of this element, qualified with any enclosing element(s), up to
146+
/// but not including an enclosing library.
147+
@visibleForOverriding
148+
String get qualifiedName;
146149

147150
p.Context get pathContext;
148151

@@ -279,7 +282,7 @@ mixin DocumentationComment
279282
'PACKAGE_PATH': package.packagePath,
280283
'PACKAGE_NAME': package.name,
281284
'LIBRARY_NAME': library?.fullyQualifiedName,
282-
'ELEMENT_NAME': fullyQualifiedNameWithoutLibrary,
285+
'ELEMENT_NAME': qualifiedName,
283286
'INVOCATION_INDEX': invocationIndex.toString(),
284287
'PACKAGE_INVOCATION_INDEX': (package.toolInvocationIndex++).toString(),
285288
};

lib/src/model/model_element.dart

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/// The models used to represent Dart code.
66
library;
77

8+
import 'dart:collection';
89
import 'dart:convert';
910

1011
import 'package:analyzer/dart/element/element.dart';
@@ -571,18 +572,21 @@ abstract class ModelElement
571572
/// documented.
572573
String get filePath;
573574

574-
/// Returns the fully qualified name.
575-
///
576-
/// For example: 'libraryName.className.methodName'
577575
@override
578-
late final String fullyQualifiedName = _buildFullyQualifiedName(this, name);
579-
580-
late final String _fullyQualifiedNameWithoutLibrary =
581-
fullyQualifiedName.replaceFirst('${library.fullyQualifiedName}.', '');
576+
String get fullyQualifiedName =>
577+
this is Library ? qualifiedName : '${library.name}.$qualifiedName';
582578

583579
@override
584-
String get fullyQualifiedNameWithoutLibrary =>
585-
_fullyQualifiedNameWithoutLibrary;
580+
late final String qualifiedName = () {
581+
var enclosingElement = this.enclosingElement;
582+
583+
var result = name;
584+
while (enclosingElement != null && enclosingElement is! Library) {
585+
result = '${enclosingElement.name}.$result';
586+
enclosingElement = enclosingElement.enclosingElement;
587+
}
588+
return result;
589+
}();
586590

587591
@override
588592
String get sourceFileName => element.source!.fullName;
@@ -783,14 +787,6 @@ abstract class ModelElement
783787
@override
784788
String toString() => '$runtimeType $name';
785789

786-
String _buildFullyQualifiedName(ModelElement e, String fullyQualifiedName) {
787-
final enclosingElement = e.enclosingElement;
788-
return enclosingElement == null
789-
? fullyQualifiedName
790-
: _buildFullyQualifiedName(
791-
enclosingElement, '${enclosingElement.name}.$fullyQualifiedName');
792-
}
793-
794790
@internal
795791
@override
796792
CommentReferable get definingCommentReferable {

lib/src/model/nameable.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,16 @@ import 'package:dartdoc/src/model/package_graph.dart';
1616
mixin Nameable {
1717
String get name;
1818

19-
/// A qualified name, mostly for use in the web search functionality, and for
20-
/// warnings printed in the terminal; not for display use in rendered HTML.
19+
/// A "fully" qualified name, mostly for use in the web search functionality,
20+
/// and for warnings printed in the terminal; not for display use in rendered
21+
/// HTML.
22+
///
23+
/// "Fully" means the name is qualified through the library. For example, a
24+
/// method named 'baz' in a class named 'Bar' in a library named 'foo' has a
25+
/// fully qualified name of 'foo.Bar.baz'.
26+
///
27+
/// As dartdoc can document multiple packages at once, note that such
28+
/// qualifying names may not be unique across all documented packages.
2129
String get fullyQualifiedName => name;
2230

2331
/// The name to use as text in the rendered documentation.

test/constant_values_test.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import 'src/utils.dart';
1010

1111
void main() {
1212
defineReflectiveSuite(() {
13-
defineReflectiveTests(ConstantValuesWithConstructorTearoffsTest);
13+
defineReflectiveTests(ConstantValuesTest);
1414
if (namedArgumentsAnywhereAllowed) {
1515
defineReflectiveTests(ConstantValuesWithNamedArgumentsAnywhereTest);
1616
}
@@ -22,16 +22,17 @@ void main() {
2222
// test_reflective_loader migration.
2323

2424
@reflectiveTest
25-
class ConstantValuesWithConstructorTearoffsTest extends DartdocTestBase {
25+
class ConstantValuesTest extends DartdocTestBase {
2626
@override
27-
String get libraryName => 'constructor_tearoffs';
27+
String get libraryName => 'constant_values';
2828

2929
void test_nonGenericFunctionReference() async {
3030
var library = await bootPackageWithLibrary('''
3131
void func() {}
3232
const aFunc = func;
3333
''');
3434
var aFuncConstant = library.constants.named('aFunc');
35+
expect(aFuncConstant.fullyQualifiedName, equals('constant_values.aFunc'));
3536
expect(aFuncConstant.constantValue, equals('func'));
3637
}
3738

test/constructors_test.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ abstract final class C {
2828
''');
2929
var c = library.classes.named('C').constructors.first;
3030
expect(c.name, equals('C'));
31+
// TODO(srawlins): This should be `constructors.C.new`.
32+
expect(c.fullyQualifiedName, 'constructors.C.C');
3133
expect(c.isPublic, isTrue);
3234
expect(c.documentationAsHtml, '<p>Constructor.</p>');
3335
}
@@ -41,6 +43,8 @@ abstract final class C {
4143
''');
4244
var c = library.classes.named('C').constructors.first;
4345
expect(c.name, equals('C'));
46+
// TODO(srawlins): This should be `constructors.C.new`.
47+
expect(c.fullyQualifiedName, 'constructors.C.C');
4448
expect(c.isPublic, isFalse);
4549
expect(c.documentationAsHtml, '<p>Constructor.</p>');
4650
}
@@ -54,6 +58,8 @@ abstract interface class C {
5458
''');
5559
var c = library.classes.named('C').constructors.first;
5660
expect(c.name, equals('C'));
61+
// TODO(srawlins): This should be `constructors.C.new`.
62+
expect(c.fullyQualifiedName, 'constructors.C.C');
5763
expect(c.isPublic, isFalse);
5864
expect(c.documentationAsHtml, '<p>Constructor.</p>');
5965
}
@@ -67,6 +73,7 @@ class C {
6773
''');
6874
var c = library.classes.named('C').constructors.first;
6975
expect(c.name, equals('C._'));
76+
expect(c.fullyQualifiedName, 'constructors.C._');
7077
expect(c.isPublic, isFalse);
7178
expect(c.documentationAsHtml, '<p>Constructor.</p>');
7279
}
@@ -103,6 +110,7 @@ class C {
103110
''');
104111
var c = library.classes.named('C').constructors.first;
105112
expect(c.name, equals('C.named'));
113+
expect(c.fullyQualifiedName, 'constructors.C.named');
106114
expect(c.isPublic, isTrue);
107115
expect(c.documentationAsHtml, '<p>Constructor.</p>');
108116
}
@@ -129,6 +137,8 @@ class C {
129137
''');
130138
var c = library.classes.named('C').constructors.first;
131139
expect(c.name, equals('C'));
140+
// TODO(srawlins): This should be `constructors.C.new`.
141+
expect(c.fullyQualifiedName, 'constructors.C.C');
132142
expect(c.isPublic, isTrue);
133143
expect(c.documentationAsHtml, '<p>Constructor.</p>');
134144
}
@@ -156,6 +166,7 @@ enum E {
156166
''');
157167
var e = library.enums.named('E').constructors.first;
158168
expect(e.name, equals('E.named'));
169+
expect(e.fullyQualifiedName, 'constructors.E.named');
159170
expect(e.isPublic, isFalse);
160171
expect(e.documentationAsHtml, '<p>Constructor.</p>');
161172
}
@@ -170,6 +181,8 @@ enum E {
170181
''');
171182
var e = library.enums.named('E').constructors.first;
172183
expect(e.name, equals('E'));
184+
// TODO(srawlins): This should be `constructors.E.new`.
185+
expect(e.fullyQualifiedName, 'constructors.E.E');
173186
expect(e.isPublic, isFalse);
174187
expect(e.documentationAsHtml, '<p>Constructor.</p>');
175188
}
@@ -184,6 +197,7 @@ extension type ET(int it) {
184197
var etNamed =
185198
library.extensionTypes.named('ET').constructors.named('ET.named');
186199
expect(etNamed.name, equals('ET.named'));
200+
expect(etNamed.fullyQualifiedName, 'constructors.ET.named');
187201
expect(etNamed.isPublic, isTrue);
188202
expect(etNamed.documentationAsHtml, '<p>Constructor.</p>');
189203
}
@@ -195,6 +209,7 @@ extension type ET.named(int it) {}
195209
var etNamed =
196210
library.extensionTypes.named('ET').constructors.named('ET.named');
197211
expect(etNamed.name, equals('ET.named'));
212+
expect(etNamed.fullyQualifiedName, 'constructors.ET.named');
198213
expect(etNamed.isPublic, isTrue);
199214
}
200215

@@ -204,6 +219,8 @@ extension type ET(int it) {}
204219
''');
205220
var et = library.extensionTypes.named('ET').constructors.named('ET');
206221
expect(et.name, equals('ET'));
222+
// TODO(srawlins): This should be `constructors.ET.new`.
223+
expect(et.fullyQualifiedName, 'constructors.ET.ET');
207224
expect(et.isPublic, isTrue);
208225
}
209226

@@ -216,6 +233,8 @@ extension type ET.named(int it) {
216233
''');
217234
var etNamed = library.extensionTypes.named('ET').constructors.named('ET');
218235
expect(etNamed.name, equals('ET'));
236+
// TODO(srawlins): This should be `constructors.ET.new`.
237+
expect(etNamed.fullyQualifiedName, 'constructors.ET.ET');
219238
expect(etNamed.isPublic, isTrue);
220239
expect(etNamed.documentationAsHtml, '<p>Constructor.</p>');
221240
}

0 commit comments

Comments
 (0)