diff --git a/lib/src/model/canonicalization.dart b/lib/src/model/canonicalization.dart index e51805ff11..761f4a5b81 100644 --- a/lib/src/model/canonicalization.dart +++ b/lib/src/model/canonicalization.dart @@ -74,6 +74,11 @@ final class Canonicalization { scoredCandidate._alterScore(1.0, _Reason.packageName); } + // Same idea as the above, for the Dart SDK. + if (library.name == 'dart:core') { + scoredCandidate._alterScore(0.9, _Reason.packageName); + } + // Give a tiny boost for libraries with long names, assuming they're // more specific (and therefore more likely to be the owner of this symbol). scoredCandidate._alterScore( diff --git a/lib/src/model/container.dart b/lib/src/model/container.dart index a4ace6017e..de5076d723 100644 --- a/lib/src/model/container.dart +++ b/lib/src/model/container.dart @@ -238,7 +238,7 @@ abstract class Container extends ModelElement String get sidebarPath; @override - String get aboveSidebarPath => library.sidebarPath; + String get aboveSidebarPath => canonicalLibraryOrThrow.sidebarPath; @override String get belowSidebarPath => sidebarPath; diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart index 0af2b4ce24..f5495ff52f 100644 --- a/lib/src/model/model_element.dart +++ b/lib/src/model/model_element.dart @@ -394,6 +394,22 @@ abstract class ModelElement !(enclosingElement as Extension).isPublic) { return false; } + + if (element case LibraryElement(:var identifier, :var source)) { + // Private Dart SDK libraries are not public. + if (identifier.startsWith('dart:_') || + identifier.startsWith('dart:nativewrappers/') || + 'dart:nativewrappers' == identifier) { + return false; + } + // Package-private libraries are not public. + var elementUri = source.uri; + if (elementUri.scheme == 'package' && + elementUri.pathSegments[1] == 'src') { + return false; + } + } + return !element.hasPrivateName && !hasNodoc; }(); @@ -518,6 +534,7 @@ abstract class ModelElement final candidateLibraries = thisAndExported.where((l) { if (!l.isPublic) return false; if (l.package.documentedWhere == DocumentLocation.missing) return false; + if (this is Library) return true; var lookup = l.element.exportNamespace.definedNames[topLevelElementName]; return topLevelElement == (lookup is PropertyAccessorElement ? lookup.variable2 : lookup); diff --git a/lib/src/model/model_function.dart b/lib/src/model/model_function.dart index 255e1c319f..733b28f95b 100644 --- a/lib/src/model/model_function.dart +++ b/lib/src/model/model_function.dart @@ -51,7 +51,7 @@ class ModelFunctionTyped extends ModelElement with TypeParameters { String get filePath => '${canonicalLibrary?.dirName}/$fileName'; @override - String get aboveSidebarPath => enclosingElement.sidebarPath; + String get aboveSidebarPath => canonicalLibraryOrThrow.sidebarPath; @override String? get belowSidebarPath => null; diff --git a/lib/src/model/package_graph.dart b/lib/src/model/package_graph.dart index 5d67e3b68a..9a23180882 100644 --- a/lib/src/model/package_graph.dart +++ b/lib/src/model/package_graph.dart @@ -195,6 +195,7 @@ class PackageGraph with CommentReferable, Nameable { // that might not _have_ a canonical [ModelElement], too. if (element.isCanonical || element.canonicalModelElement == null || + element is Library || element.enclosingElement!.isCanonical) { for (var d in element.documentationFrom .where((d) => d.hasDocumentationComment)) { @@ -771,6 +772,8 @@ class PackageGraph with CommentReferable, Nameable { if (canonicalClass != null) preferredClass = canonicalClass; } var lib = modelElement.canonicalLibrary; + if (modelElement is Library) return lib; + if (lib == null && preferredClass != null) { lib = preferredClass.canonicalLibrary; } diff --git a/lib/src/model/top_level_variable.dart b/lib/src/model/top_level_variable.dart index cc10f58daa..0b577342ce 100644 --- a/lib/src/model/top_level_variable.dart +++ b/lib/src/model/top_level_variable.dart @@ -45,7 +45,7 @@ class TopLevelVariable extends ModelElement String get filePath => '${canonicalLibraryOrThrow.dirName}/$fileName'; @override - String get aboveSidebarPath => enclosingElement.sidebarPath; + String get aboveSidebarPath => canonicalLibraryOrThrow.sidebarPath; @override String? get belowSidebarPath => null; diff --git a/lib/src/model/typedef.dart b/lib/src/model/typedef.dart index e245371fb2..1de5d2b157 100644 --- a/lib/src/model/typedef.dart +++ b/lib/src/model/typedef.dart @@ -35,7 +35,7 @@ abstract class Typedef extends ModelElement String get filePath => '${canonicalLibraryOrThrow.dirName}/$fileName'; @override - String get aboveSidebarPath => enclosingElement.sidebarPath; + String get aboveSidebarPath => canonicalLibraryOrThrow.sidebarPath; @override String? get belowSidebarPath => null; diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index 9d73320dd0..e770369b7f 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -72,19 +72,6 @@ extension ElementExtension on Element { return true; } } - if (self is LibraryElement) { - if (self.identifier.startsWith('dart:_') || - self.identifier.startsWith('dart:nativewrappers/') || - 'dart:nativewrappers' == self.identifier) { - return true; - } - var elementUri = self.source.uri; - // TODO(jcollins-g): Implement real cross package detection. - if (elementUri.scheme == 'package' && - elementUri.pathSegments[1] == 'src') { - return true; - } - } return false; } } diff --git a/test/dartdoc_test_base.dart b/test/dartdoc_test_base.dart index da629b2cd8..d6d974ebb6 100644 --- a/test/dartdoc_test_base.dart +++ b/test/dartdoc_test_base.dart @@ -36,6 +36,9 @@ abstract class DartdocTestBase { String get linkPrefix => '$placeholder$libraryName'; + String get dartAsyncUrlPrefix => + 'https://api.dart.dev/stable/3.2.0/dart-async'; + String get dartCoreUrlPrefix => 'https://api.dart.dev/stable/3.2.0/dart-core'; String get sdkConstraint => '>=3.3.0 <4.0.0'; @@ -99,20 +102,21 @@ analyzer: /// Creates a single library named [libraryName], with optional preamble /// [libraryPreamble]. Optionally, pass [extraFiles] such as /// `dartdoc_options.yaml`. - Future bootPackageWithLibrary(String libraryContent, - {String libraryPreamble = '', - Iterable extraFiles = const [], - List additionalArguments = const []}) async { + Future bootPackageWithLibrary( + String libraryContent, { + String libraryFilePath = 'lib/library.dart', + String libraryPreamble = '', + Iterable extraFiles = const [], + List additionalArguments = const [], + }) async { return (await bootPackageFromFiles([ - d.dir('lib', [ - d.file('lib.dart', ''' + d.file(libraryFilePath, ''' $libraryPreamble library $libraryName; $libraryContent '''), - ]), - ...extraFiles + ...extraFiles, ], additionalArguments: additionalArguments)) .libraries .named(libraryName); diff --git a/test/libraries_test.dart b/test/libraries_test.dart index 1593104d8d..3c1e1158d9 100644 --- a/test/libraries_test.dart +++ b/test/libraries_test.dart @@ -5,13 +5,19 @@ import 'package:analyzer/file_system/memory_file_system.dart'; import 'package:dartdoc/src/model/model.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'; -// TODO(srawlins): Migrate to test_reflective_loader tests. - void main() { + defineReflectiveSuite(() { + defineReflectiveTests(LibrariesTest); + }); + + // TODO(srawlins): Migrate to test_reflective_loader tests. + test('A named library', () async { var packageMetaProvider = testPackageMetaProvider; @@ -169,3 +175,35 @@ A doc comment. '${htmlBasePlaceholder}dart-async/dart-async-library.html'); }, onPlatform: {'windows': Skip('Test does not work on Windows (#2446)')}); } + +@reflectiveTest +class LibrariesTest extends DartdocTestBase { + @override + String get libraryName => 'libraries'; + + void test_publicLibrary() async { + var library = await bootPackageWithLibrary( + 'var x = 1;', + libraryFilePath: 'lib/library.dart', + ); + + expect(library.qualifiedName, 'libraries'); + expect(library.href, '${placeholder}libraries/libraries-library.html'); + } + + void test_exportedLibrary() async { + var library = await bootPackageWithLibrary( + 'var x = 1;', + libraryFilePath: 'lib/src/library.dart', + extraFiles: [ + d.dir('lib', [ + d.file('public.dart', ''' +export 'src/library.dart'; +''') + ]), + ], + ); + expect(library.qualifiedName, 'libraries'); + expect(library.href, '${placeholder}public/public-library.html'); + } +} diff --git a/test/prefixes_test.dart b/test/prefixes_test.dart index f7d10c26bc..6ce9b05252 100644 --- a/test/prefixes_test.dart +++ b/test/prefixes_test.dart @@ -29,11 +29,14 @@ int x = 0; ''', additionalArguments: ['--link-to-remote'], ); - var f = library.properties.named('x'); - // There is no link, but also no wrong link or crash. - expect(f.documentationAsHtml, '

Text async.

'); + var x = library.properties.named('x'); + expect( + x.documentationAsHtml, + '

Text async.

', + ); } + @FailingTest(issue: 'https://github.com/dart-lang/dartdoc/issues/3769') void test_referenced_wildcard() async { var library = await bootPackageWithLibrary( ''' @@ -44,8 +47,8 @@ int x = 0; ''', additionalArguments: ['--link-to-remote'], ); - var f = library.properties.named('x'); + var x = library.properties.named('x'); // There is no link, but also no wrong link or crash. - expect(f.documentationAsHtml, '

Text _.

'); + expect(x.documentationAsHtml, '

Text _.

'); } } diff --git a/test/src/test_descriptor_utils.dart b/test/src/test_descriptor_utils.dart index 498b5e904e..b98005529f 100644 --- a/test/src/test_descriptor_utils.dart +++ b/test/src/test_descriptor_utils.dart @@ -131,7 +131,8 @@ extension on d.FileDescriptor { Future createInMemory( MemoryResourceProvider resourceProvider, String parent) async { var content = await readAsBytes().transform(utf8.decoder).join(''); - var fullPath = resourceProvider.pathContext.join(parent, name); + var fullPath = resourceProvider + .convertPath(resourceProvider.pathContext.join(parent, name)); resourceProvider.newFile(fullPath, content); return fullPath; }