Skip to content

Commit 711e52c

Browse files
authored
Prefer package-local canonicalization and warn on reexported private API across packages (#1684)
1 parent 1cf5f34 commit 711e52c

File tree

3 files changed

+72
-30
lines changed

3 files changed

+72
-30
lines changed

lib/src/model.dart

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2941,6 +2941,21 @@ abstract class ModelElement extends Canonicalization
29412941
if (topLevelElement == lookup) return true;
29422942
return false;
29432943
}).toList();
2944+
2945+
// Avoid claiming canonicalization for elements outside of this element's
2946+
// defining package.
2947+
// TODO(jcollins-g): Make the else block unconditional.
2948+
if (!candidateLibraries.isEmpty &&
2949+
!candidateLibraries
2950+
.any((l) => l.package == definingLibrary.package)) {
2951+
warn(PackageWarning.reexportedPrivateApiAcrossPackages,
2952+
message: definingLibrary.package.fullyQualifiedName,
2953+
referredFrom: candidateLibraries);
2954+
} else {
2955+
candidateLibraries
2956+
.removeWhere((l) => l.package != definingLibrary.package);
2957+
}
2958+
29442959
// Start with our top-level element.
29452960
ModelElement warnable =
29462961
new ModelElement.fromElement(topLevelElement, packageGraph);
@@ -2972,7 +2987,8 @@ abstract class ModelElement extends Canonicalization
29722987
} else {
29732988
_canonicalLibrary = definingLibrary;
29742989
}
2975-
if (this is Inheritable) {
2990+
// Only pretend when not linking to remote packages.
2991+
if (this is Inheritable && !config.linkToRemote) {
29762992
if ((this as Inheritable).isInherited &&
29772993
_canonicalLibrary == null &&
29782994
packageGraph.publicLibraries.contains(library)) {
@@ -4054,8 +4070,9 @@ class PackageGraph extends Canonicalization
40544070
case PackageWarning.noCanonicalFound:
40554071
// Fix these warnings by adding libraries with --include, or by using
40564072
// --auto-include-dependencies.
4057-
// TODO(jcollins-g): add a dartdoc flag to enable external website linking for non-canonical elements, using .packages for versioning
4058-
// TODO(jcollins-g): support documenting multiple packages at once and linking between them
4073+
// TODO(jcollins-g): pipeline references through linkedName for error
4074+
// messages and warn for non-public canonicalization
4075+
// errors.
40594076
warningMessage =
40604077
"no canonical library found for ${warnableName}, not linking";
40614078
break;
@@ -4081,6 +4098,10 @@ class PackageGraph extends Canonicalization
40814098
warningMessage =
40824099
"--package-order gives invalid package name: '${message}'";
40834100
break;
4101+
case PackageWarning.reexportedPrivateApiAcrossPackages:
4102+
warningMessage =
4103+
"private API of ${message} is reexported by libraries in other packages: ";
4104+
break;
40844105
case PackageWarning.unresolvedDocReference:
40854106
warningMessage = "unresolved doc reference [${message}]";
40864107
if (referredFrom == null) {

lib/src/warnings.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ final Map<PackageWarning, PackageWarningHelpText> packageWarningText = const {
5454
PackageWarning.packageOrderGivesMissingPackageName,
5555
"category-order-gives-missing-package-name",
5656
"The category-order flag on the command line was given the name of a nonexistent package"),
57+
PackageWarning.reexportedPrivateApiAcrossPackages: const PackageWarningHelpText(
58+
PackageWarning.reexportedPrivateApiAcrossPackages,
59+
"reexported-private-api-across-packages",
60+
"One or more libraries reexports private API members from outside its own package"),
5761
PackageWarning.unresolvedDocReference: const PackageWarningHelpText(
5862
PackageWarning.unresolvedDocReference,
5963
"unresolved-doc-reference",
@@ -113,6 +117,7 @@ enum PackageWarning {
113117
noCanonicalFound,
114118
noLibraryLevelDocs,
115119
packageOrderGivesMissingPackageName,
120+
reexportedPrivateApiAcrossPackages,
116121
unresolvedDocReference,
117122
unknownMacro,
118123
brokenLink,

tool/grind.dart

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ expectFileContains(String path, List<Pattern> items) {
3333
}
3434
}
3535

36+
/// The pub cache inherited by grinder.
37+
final String defaultPubCache =
38+
Platform.environment['PUB_CACHE'] ?? resolveTildePath('~/.pub-cache');
39+
3640
/// Run no more than the number of processors available in parallel.
3741
final MultiFutureTracker testFutures = new MultiFutureTracker(
3842
Platform.environment.containsKey('TRAVIS')
@@ -183,11 +187,11 @@ Future buildSdkDocs() async {
183187

184188
class WarningsCollection {
185189
final String tempDir;
186-
final Map<String, int> _warningKeyCounts;
190+
final Map<String, int> warningKeyCounts;
187191
final String branch;
188192
final String pubCachePath;
189193
WarningsCollection(this.tempDir, this.pubCachePath, this.branch)
190-
: this._warningKeyCounts = new Map() {}
194+
: this.warningKeyCounts = new Map() {}
191195

192196
static const String kPubCachePathReplacement = '_xxxPubDirectoryxxx_';
193197
static const String kTempDirReplacement = '_xxxTempDirectoryxxx_';
@@ -208,8 +212,8 @@ class WarningsCollection {
208212

209213
void add(String text) {
210214
String key = _toKey(text);
211-
_warningKeyCounts.putIfAbsent(key, () => 0);
212-
_warningKeyCounts[key]++;
215+
warningKeyCounts.putIfAbsent(key, () => 0);
216+
warningKeyCounts[key]++;
213217
}
214218

215219
/// Output formatter for comparing warnings. [this] is the original.
@@ -220,19 +224,19 @@ class WarningsCollection {
220224
Set<String> onlyCurrent = new Set();
221225
Set<String> identical = new Set();
222226
Set<String> allKeys = new Set.from([]
223-
..addAll(_warningKeyCounts.keys)
224-
..addAll(current._warningKeyCounts.keys));
227+
..addAll(warningKeyCounts.keys)
228+
..addAll(current.warningKeyCounts.keys));
225229

226230
for (String key in allKeys) {
227-
if (_warningKeyCounts.containsKey(key) &&
228-
!current._warningKeyCounts.containsKey(key)) {
231+
if (warningKeyCounts.containsKey(key) &&
232+
!current.warningKeyCounts.containsKey(key)) {
229233
onlyOriginal.add(key);
230-
} else if (!_warningKeyCounts.containsKey(key) &&
231-
current._warningKeyCounts.containsKey(key)) {
234+
} else if (!warningKeyCounts.containsKey(key) &&
235+
current.warningKeyCounts.containsKey(key)) {
232236
onlyCurrent.add(key);
233-
} else if (_warningKeyCounts.containsKey(key) &&
234-
current._warningKeyCounts.containsKey(key) &&
235-
_warningKeyCounts[key] != current._warningKeyCounts[key]) {
237+
} else if (warningKeyCounts.containsKey(key) &&
238+
current.warningKeyCounts.containsKey(key) &&
239+
warningKeyCounts[key] != current.warningKeyCounts[key]) {
236240
quantityChangedOuts.add(key);
237241
} else {
238242
identical.add(key);
@@ -253,7 +257,7 @@ class WarningsCollection {
253257
printBuffer.writeln('*** $title : Identical warning quantity changed');
254258
for (String key in quantityChangedOuts) {
255259
printBuffer.writeln(
256-
"* Appeared ${_warningKeyCounts[key]} times in $branch, ${current._warningKeyCounts[key]} in ${current.branch}:");
260+
"* Appeared ${warningKeyCounts[key]} times in $branch, ${current.warningKeyCounts[key]} in ${current.branch}:");
257261
printBuffer.writeln(current._fromKey(key));
258262
}
259263
}
@@ -813,26 +817,38 @@ Future serveDartdocFlutterPluginDocs() async {
813817
pluginPackageDocsDir.path, 8005, 'serve-dartdoc-flutter-plugin-docs');
814818
}
815819

816-
@Task('Build docs for a package that requires flutter with remote linking')
817-
buildDartdocFlutterPluginDocs() async {
820+
Future<WarningsCollection> _buildDartdocFlutterPluginDocs() async {
818821
FlutterRepo flutterRepo = await FlutterRepo.fromExistingFlutterRepo(
819822
await cleanFlutterRepo, 'docs-flutter-plugin');
820823

821-
await flutterRepo.launcher.runStreamed(
822-
Platform.resolvedExecutable,
823-
[
824-
'--checked',
825-
pathLib.join(Directory.current.path, 'bin', 'dartdoc.dart'),
826-
'--link-to-remote',
827-
'--output',
828-
pluginPackageDocsDir.path
829-
],
830-
workingDirectory: pluginPackage.path);
824+
return jsonMessageIterableToWarnings(
825+
await flutterRepo.launcher.runStreamed(
826+
Platform.resolvedExecutable,
827+
[
828+
'--checked',
829+
pathLib.join(Directory.current.path, 'bin', 'dartdoc.dart'),
830+
'--json',
831+
'--link-to-remote',
832+
'--output',
833+
pluginPackageDocsDir.path
834+
],
835+
workingDirectory: pluginPackage.path),
836+
pluginPackageDocsDir.path,
837+
defaultPubCache,
838+
'HEAD');
839+
}
840+
841+
@Task('Build docs for a package that requires flutter with remote linking')
842+
buildDartdocFlutterPluginDocs() async {
843+
await _buildDartdocFlutterPluginDocs();
831844
}
832845

833846
@Task('Verify docs for a package that requires flutter with remote linking')
834-
@Depends(buildDartdocFlutterPluginDocs)
835847
testDartdocFlutterPlugin() async {
848+
WarningsCollection warnings = await _buildDartdocFlutterPluginDocs();
849+
if (!warnings.warningKeyCounts.isEmpty) {
850+
fail('No warnings should exist in : ${warnings.warningKeyCounts}');
851+
}
836852
// Verify that links to Dart SDK and Flutter SDK go to the flutter site.
837853
expectFileContains(
838854
pathLib.join(

0 commit comments

Comments
 (0)