From 067b70fadbb8cb90157f6c7afb9831ec3b529af0 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 30 Sep 2024 12:43:27 -0700 Subject: [PATCH 1/2] Support for enhanced parts There isn't _much_ for dartdoc to be concerned with. Dartdoc basically must be aware of an exports foud in a part, and there might be reasons to be concerned with imports coming from a part. In particular, dartdoc used to skip resolving all part files when searching for libraries, just as an optimization. Can't do that any more. This might not be a full implementation of enhanced parts support, but it sets up the basics. --- lib/src/model/package_builder.dart | 30 ++++++++++--- lib/src/model/package_graph.dart | 7 ++- test/dartdoc_test_base.dart | 2 +- test/packages_test.dart | 71 ++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 9 deletions(-) diff --git a/lib/src/model/package_builder.dart b/lib/src/model/package_builder.dart index 1631382a62..e6fe631550 100644 --- a/lib/src/model/package_builder.dart +++ b/lib/src/model/package_builder.dart @@ -283,6 +283,9 @@ class PubPackageBuilder implements PackageBuilder { continue; } newFiles.addFilesReferencedBy(resolvedLibrary.element); + for (var unit in resolvedLibrary.units) { + newFiles.addFilesReferencedBy(unit.declaredElement); + } if (processedLibraries.contains(resolvedLibrary.element)) { continue; } @@ -548,24 +551,37 @@ class DartDocResolvedLibrary { extension on Set { /// Adds [element]'s path and all of its part files' paths to `this`, and /// recursively adds the paths of all imported and exported libraries. - void addFilesReferencedBy(LibraryOrAugmentationElement? element) { + /// + /// [element] must be a [LibraryElement] or [CompilationUnitElement]. + void addFilesReferencedBy(Element? element) { if (element == null) return; var path = element.source?.fullName; if (path == null) return; if (add(path)) { - for (var import in element.libraryImports) { + var libraryImports = switch (element) { + LibraryElement(:var libraryImports) || + CompilationUnitElement(:var libraryImports) => + libraryImports, + _ => const [], + }; + for (var import in libraryImports) { addFilesReferencedBy(import.importedLibrary); } - for (var export in element.libraryExports) { + + var libraryExports = switch (element) { + LibraryElement(:var libraryExports) || + CompilationUnitElement(:var libraryExports) => + libraryExports, + _ => const [], + }; + for (var export in libraryExports) { addFilesReferencedBy(export.exportedLibrary); } if (element is LibraryElement) { - for (var part in element.parts - .map((e) => e.uri) - .whereType()) { - add(part.source.fullName); + for (var unit in element.units) { + addFilesReferencedBy(unit); } } } diff --git a/lib/src/model/package_graph.dart b/lib/src/model/package_graph.dart index 8d388c4c35..8aca17a701 100644 --- a/lib/src/model/package_graph.dart +++ b/lib/src/model/package_graph.dart @@ -554,7 +554,12 @@ class PackageGraph with CommentReferable, Nameable { alreadyTagged.add(key); // Mark that `publicLibrary` exports `libraryElement`. _libraryExports.putIfAbsent(libraryElement, () => {}).add(publicLibrary); - for (var exportedElement in libraryElement.libraryExports) { + + var exported = [ + ...libraryElement.libraryExports, + for (var unit in libraryElement.units) ...unit.libraryExports, + ]; + for (var exportedElement in exported) { var exportedLibrary = exportedElement.exportedLibrary; if (exportedLibrary != null) { // Follow the exports down; as `publicLibrary` exports `libraryElement`, diff --git a/test/dartdoc_test_base.dart b/test/dartdoc_test_base.dart index 15b2af7786..606a1fb78b 100644 --- a/test/dartdoc_test_base.dart +++ b/test/dartdoc_test_base.dart @@ -43,7 +43,7 @@ abstract class DartdocTestBase { String get sdkConstraint => '>=3.6.0 <4.0.0'; - List get experiments => ['wildcard-variables']; + List get experiments => ['enhanced-parts', 'wildcard-variables']; bool get skipUnreachableSdkLibraries => true; diff --git a/test/packages_test.dart b/test/packages_test.dart index 4961ae0545..e0bd408996 100644 --- a/test/packages_test.dart +++ b/test/packages_test.dart @@ -5,16 +5,26 @@ import 'package:analyzer/file_system/file_system.dart'; import 'package:analyzer/file_system/memory_file_system.dart'; import 'package:dartdoc/src/dartdoc_options.dart'; +import 'package:dartdoc/src/model/class.dart'; import 'package:dartdoc/src/model/documentable.dart'; import 'package:dartdoc/src/model/kind.dart'; import 'package:dartdoc/src/package_config_provider.dart'; import 'package:dartdoc/src/package_meta.dart'; import 'package:dartdoc/src/special_elements.dart'; import 'package:test/test.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; +import 'dartdoc_test_base.dart'; +import 'src/test_descriptor_utils.dart' as d; import 'src/utils.dart' as utils; void main() { + defineReflectiveSuite(() { + defineReflectiveTests(PackagesTest); + }); + + // TODO(srawlins): Migrate to test_reflective_loader tests. + late MemoryResourceProvider resourceProvider; late PackageMetaProvider packageMetaProvider; late FakePackageConfigProvider packageConfigProvider; @@ -103,6 +113,8 @@ int x; projectPath, packageMetaProvider, packageConfigProvider); expect(packageGraph.localPublicLibraries, hasLength(1)); + var library = packageGraph.libraries.named('a'); + expect(library.isDocumented, true); }); test('has private libraries', () async { @@ -489,3 +501,62 @@ int x; }); }); } + +@reflectiveTest +class PackagesTest extends DartdocTestBase { + @override + String get libraryName => 'lib'; + + void test_exportedElements() async { + var graph = await bootPackageFromFiles( + [ + d.dir('lib', [ + d.file('lib.dart', "export 'src/impl.dart';"), + d.dir('src', [ + d.file('impl.dart', 'class C {}'), + ]), + ]), + ], + ); + var library = graph.libraries.named('lib'); + expect(library.classes.single.name, 'C'); + } + + void test_exportedElements_indirectlyExported() async { + var graph = await bootPackageFromFiles( + [ + d.dir('lib', [ + d.file('lib.dart', "export 'src/impl.dart';"), + d.dir('src', [ + d.file('impl.dart', "export 'impl2.dart';"), + d.file('impl2.dart', 'class C {}'), + ]), + ]), + ], + ); + var library = graph.libraries.named('lib'); + expect(library.classes.single.name, 'C'); + } + + void test_exportedElements_fromPart() async { + var graph = await bootPackageFromFiles( + [ + d.dir('lib', [ + d.file('lib.dart', "part 'part.dart';"), + d.file('part.dart', ''' +part of 'lib.dart'; +export 'src/impl.dart'; +'''), + d.dir('src', [ + d.file('impl.dart', 'class C {}'), + ]), + ]), + ], + ); + var library = graph.libraries.named('lib'); + expect(library.qualifiedName, 'lib'); + expect(library.classes, isNotEmpty); + expect(library.classes.single, + isA().having((c) => c.name, 'name', 'C')); + } +} From 042d9c7a0539007d624df5bdf8a0755e9cfb9f14 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 30 Sep 2024 14:01:38 -0700 Subject: [PATCH 2/2] feedback --- lib/src/model/package_graph.dart | 7 +------ test/packages_test.dart | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/src/model/package_graph.dart b/lib/src/model/package_graph.dart index 8aca17a701..8d388c4c35 100644 --- a/lib/src/model/package_graph.dart +++ b/lib/src/model/package_graph.dart @@ -554,12 +554,7 @@ class PackageGraph with CommentReferable, Nameable { alreadyTagged.add(key); // Mark that `publicLibrary` exports `libraryElement`. _libraryExports.putIfAbsent(libraryElement, () => {}).add(publicLibrary); - - var exported = [ - ...libraryElement.libraryExports, - for (var unit in libraryElement.units) ...unit.libraryExports, - ]; - for (var exportedElement in exported) { + for (var exportedElement in libraryElement.libraryExports) { var exportedLibrary = exportedElement.exportedLibrary; if (exportedLibrary != null) { // Follow the exports down; as `publicLibrary` exports `libraryElement`, diff --git a/test/packages_test.dart b/test/packages_test.dart index e0bd408996..60db22a1b7 100644 --- a/test/packages_test.dart +++ b/test/packages_test.dart @@ -546,6 +546,32 @@ class PackagesTest extends DartdocTestBase { d.file('part.dart', ''' part of 'lib.dart'; export 'src/impl.dart'; +'''), + d.dir('src', [ + d.file('impl.dart', 'class C {}'), + ]), + ]), + ], + ); + var library = graph.libraries.named('lib'); + expect(library.qualifiedName, 'lib'); + expect(library.classes, isNotEmpty); + expect(library.classes.single, + isA().having((c) => c.name, 'name', 'C')); + } + + void test_exportedElements_fromPartOfPart() async { + var graph = await bootPackageFromFiles( + [ + d.dir('lib', [ + d.file('lib.dart', "part 'part1.dart';"), + d.file('part1.dart', ''' +part of 'lib.dart'; +part 'part2.dart'; +'''), + d.file('part2.dart', ''' +part of 'part1.dart'; +export 'src/impl.dart'; '''), d.dir('src', [ d.file('impl.dart', 'class C {}'),