Skip to content

Commit f92ae00

Browse files
authored
Fix a reexport case for comment reference linking to extension methods (#3393)
* recommit * this works. but did it break anything else * fix up test * cleanup * period instead of slash
1 parent 8e8b36e commit f92ae00

File tree

4 files changed

+185
-9
lines changed

4 files changed

+185
-9
lines changed

lib/src/model/container_member.dart

+2-5
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,8 @@ mixin ContainerMember on ModelElement implements EnclosedElement {
4343
as Container?;
4444
}
4545
// TODO(jcollins-g): move Extension specific code to [Extendable]
46-
if (enclosingElement.isDocumented) {
47-
return packageGraph.findCanonicalModelElementFor(enclosingElement.element)
48-
as Container?;
49-
}
50-
return null;
46+
return packageGraph.findCanonicalModelElementFor(enclosingElement.element)
47+
as Container?;
5148
}
5249

5350
@override

lib/src/model/inheritable.dart

-4
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,6 @@ mixin Inheritable on ContainerMember {
4848
if (canonicalEnclosingContainer == null) {
4949
return null;
5050
}
51-
// TODO(jcollins-g): factor out extension logic into [Extendable]
52-
if (canonicalEnclosingContainer is Extension) {
53-
return this;
54-
}
5551
return canonicalEnclosingContainer.allCanonicalModelElements
5652
.firstWhereOrNull((m) =>
5753
m.name == name &&

test/dartdoc_test_base.dart

+68
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ import 'package:meta/meta.dart';
1111
import 'src/test_descriptor_utils.dart' as d;
1212
import 'src/utils.dart';
1313

14+
/// Exception thrown for invalid use of [DartdocTestBase]'s api.
15+
class DartdocTestBaseFailure implements Exception {
16+
final String message;
17+
18+
DartdocTestBaseFailure(this.message);
19+
20+
@override
21+
String toString() => message;
22+
}
23+
1424
abstract class DartdocTestBase {
1525
late final PackageMetaProvider packageMetaProvider;
1626
late final MemoryResourceProvider resourceProvider;
@@ -65,6 +75,64 @@ analyzer:
6575
d.file('lib.dart', '''
6676
library $libraryName;
6777
78+
$libraryContent
79+
'''),
80+
]).createInMemory(resourceProvider, packagePath);
81+
82+
var packageGraph = await bootBasicPackage(
83+
packagePath,
84+
packageMetaProvider,
85+
packageConfigProvider,
86+
);
87+
return packageGraph.libraries.named(libraryName);
88+
}
89+
90+
/// Similar to [bootPackageWithLibrary], but allows for more complex
91+
/// cases to test the edges of canonicalization.
92+
///
93+
/// - Puts [reexportedContent] in a library named [libraryName]_src in
94+
/// `lib/src` (if [reexportPrivate] is true), or 'lib/subdir'.
95+
/// - Creates a reexporting library named [libraryName]_lib in `lib` that
96+
/// reexports [libraryName]_src.
97+
/// - Creates [libraryName] containing [libraryContent] that can optionally
98+
/// import 'lib.dart' to import the reexporting library.
99+
///
100+
/// Optionally, specify [show] or [hide] to change whether the reexport
101+
/// gives access to the full namespace.
102+
Future<Library> bootPackageWithReexportedLibrary(
103+
String reexportedContent, String libraryContent,
104+
{bool reexportPrivate = false,
105+
List<String> show = const [],
106+
List<String> hide = const []}) async {
107+
final subdir = reexportPrivate ? 'src' : 'subdir';
108+
await d.dir('lib', [
109+
d.dir(subdir, [
110+
d.file('lib.dart', '''
111+
library ${libraryName}_src;
112+
113+
$reexportedContent
114+
'''),
115+
])
116+
]).createInMemory(resourceProvider, packagePath);
117+
118+
if (show.isNotEmpty && hide.isNotEmpty) {
119+
throw DartdocTestBaseFailure('Can not specify show and hide');
120+
}
121+
122+
final showHideString = '${show.isNotEmpty ? 'show ${show.join(', ')}' : ''}'
123+
'${hide.isNotEmpty ? 'hide ${hide.join(', ')}' : ''}';
124+
await d.dir('lib', [
125+
d.file('lib.dart', '''
126+
library ${libraryName}_lib;
127+
128+
export '$subdir/lib.dart' $showHideString;
129+
'''),
130+
]).createInMemory(resourceProvider, packagePath);
131+
132+
await d.dir('lib', [
133+
d.file('importing_lib.dart', '''
134+
library $libraryName;
135+
68136
$libraryContent
69137
'''),
70138
]).createInMemory(resourceProvider, packagePath);

test/extension_methods_test.dart

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:dartdoc/src/markdown_processor.dart';
6+
import 'package:dartdoc/src/model/extension.dart';
7+
import 'package:dartdoc/src/model/method.dart';
8+
import 'package:dartdoc/src/model/model_element.dart';
9+
import 'package:test/test.dart';
10+
import 'package:test_reflective_loader/test_reflective_loader.dart';
11+
12+
import 'dartdoc_test_base.dart';
13+
import 'src/utils.dart';
14+
15+
void main() {
16+
defineReflectiveSuite(() {
17+
if (classModifiersAllowed) {
18+
defineReflectiveTests(ExtensionMethodsTest);
19+
}
20+
});
21+
}
22+
23+
@reflectiveTest
24+
class ExtensionMethodsTest extends DartdocTestBase {
25+
@override
26+
String get libraryName => 'extension_methods';
27+
28+
static const String reexportedContent = '''
29+
class AClassNotNeedingExtending {}
30+
31+
class AClassNeedingExtending {}
32+
33+
extension AnExtension on AClassNeedingExtending {
34+
void aMethod() {}
35+
}
36+
''';
37+
38+
static const String libraryContent = '''
39+
/// This is an amazing public function.
40+
var aPublicFunction() {}
41+
''';
42+
43+
void expectReferenceValid(
44+
ModelElement reference, ModelElement expected, String href) {
45+
expect(identical(reference.canonicalModelElement, expected), isTrue);
46+
expect(expected.isCanonical, isTrue);
47+
expect(expected.href, endsWith(href));
48+
}
49+
50+
void test_reexportWithShow() async {
51+
var library = await bootPackageWithReexportedLibrary(
52+
reexportedContent, libraryContent,
53+
reexportPrivate: true, show: ['AClassNeedingExtending', 'AnExtension']);
54+
var aPublicFunction = library.functions.named('aPublicFunction');
55+
var anExtension = library.package.publicLibraries
56+
.named('${libraryName}_lib')
57+
.extensions
58+
.named('AnExtension');
59+
var anExtensionMethod = anExtension.instanceMethods.named('aMethod');
60+
var anExtensionReference =
61+
getMatchingLinkElement('AnExtension', aPublicFunction).commentReferable
62+
as Extension;
63+
expectReferenceValid(anExtensionReference, anExtension,
64+
'%extension_methods_lib/AnExtension.html');
65+
var anExtensionMethodReference =
66+
getMatchingLinkElement('AnExtension.aMethod', aPublicFunction)
67+
.commentReferable as Method;
68+
expectReferenceValid(anExtensionMethodReference, anExtensionMethod,
69+
'%extension_methods_lib/AnExtension/aMethod.html');
70+
}
71+
72+
void test_reexportWithHide() async {
73+
var library = await bootPackageWithReexportedLibrary(
74+
reexportedContent, libraryContent,
75+
reexportPrivate: true, hide: ['AClassNotNeedingExtending']);
76+
var aPublicFunction = library.functions.named('aPublicFunction');
77+
var anExtension = library.package.publicLibraries
78+
.named('${libraryName}_lib')
79+
.extensions
80+
.named('AnExtension');
81+
var anExtensionMethod = anExtension.instanceMethods.named('aMethod');
82+
var anExtensionReference =
83+
getMatchingLinkElement('AnExtension', aPublicFunction).commentReferable
84+
as Extension;
85+
expectReferenceValid(anExtensionReference, anExtension,
86+
'%extension_methods_lib/AnExtension.html');
87+
var anExtensionMethodReference =
88+
getMatchingLinkElement('AnExtension.aMethod', aPublicFunction)
89+
.commentReferable as Method;
90+
expectReferenceValid(anExtensionMethodReference, anExtensionMethod,
91+
'%extension_methods_lib/AnExtension/aMethod.html');
92+
}
93+
94+
void test_reexportFull() async {
95+
var library = await bootPackageWithReexportedLibrary(
96+
reexportedContent, libraryContent,
97+
reexportPrivate: true);
98+
var aPublicFunction = library.functions.named('aPublicFunction');
99+
var anExtension = library.package.publicLibraries
100+
.named('${libraryName}_lib')
101+
.extensions
102+
.named('AnExtension');
103+
var anExtensionMethod = anExtension.instanceMethods.named('aMethod');
104+
var anExtensionReference =
105+
getMatchingLinkElement('AnExtension', aPublicFunction).commentReferable
106+
as Extension;
107+
expectReferenceValid(anExtensionReference, anExtension,
108+
'%extension_methods_lib/AnExtension.html');
109+
var anExtensionMethodReference =
110+
getMatchingLinkElement('AnExtension.aMethod', aPublicFunction)
111+
.commentReferable as Method;
112+
expectReferenceValid(anExtensionMethodReference, anExtensionMethod,
113+
'%extension_methods_lib/AnExtension/aMethod.html');
114+
}
115+
}

0 commit comments

Comments
 (0)