Add unresolved export warning to dartdoc (#1748)
* Add unresolved export warning
* Simplification
* Split test package docs into dev and stable versions
* Make stable and dev trees separate for Dart compare_output_test
diff --git a/analysis_options.yaml b/analysis_options.yaml
index d0fd7f3..8a654ac 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -8,6 +8,7 @@
- 'pub.dartlang.org/**'
- 'testing/**'
- 'testing/test_package_flutter_plugin/**'
+ - 'testing/test_package_export_error/**'
linter:
rules:
- annotate_overrides
diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart
index 48d89ae..2b9d476 100644
--- a/lib/dartdoc.dart
+++ b/lib/dartdoc.dart
@@ -133,7 +133,7 @@
/// [DartdocResults] is returned if dartdoc succeeds. [DartdocFailure] is
/// thrown if dartdoc fails in an expected way, for example if there is an
/// analysis error in the code.
- Future<DartdocResults> generateDocs() async {
+ Future<DartdocResults> generateDocsBase() async {
Stopwatch _stopwatch = new Stopwatch()..start();
double seconds;
packageGraph = await buildPackageGraph();
@@ -167,18 +167,22 @@
logInfo(
"Documented ${packageGraph.publicLibraries.length} public librar${packageGraph.publicLibraries.length == 1 ? 'y' : 'ies'} "
"in ${seconds.toStringAsFixed(1)} seconds");
+ return new DartdocResults(
+ config.topLevelPackageMeta, packageGraph, outputDir);
+ }
- if (packageGraph.publicLibraries.isEmpty) {
+ Future<DartdocResults> generateDocs() async {
+ DartdocResults dartdocResults = await generateDocsBase();
+ if (dartdocResults.packageGraph.publicLibraries.isEmpty) {
throw new DartdocFailure(
"dartdoc could not find any libraries to document. Run `pub get` and try again.");
}
- if (packageGraph.packageWarningCounter.errorCount > 0) {
+ if (dartdocResults.packageGraph.packageWarningCounter.errorCount > 0) {
throw new DartdocFailure("dartdoc encountered errors while processing");
}
- return new DartdocResults(
- config.topLevelPackageMeta, packageGraph, outputDir);
+ return dartdocResults;
}
/// Warn on file paths.
diff --git a/lib/src/model.dart b/lib/src/model.dart
index b84633d..8a9de85 100644
--- a/lib/src/model.dart
+++ b/lib/src/model.dart
@@ -4357,6 +4357,9 @@
case PackageWarning.deprecated:
warningMessage = 'deprecated dartdoc usage: ${message}';
break;
+ case PackageWarning.unresolvedExport:
+ warningMessage = 'unresolved export uri: ${message}';
+ break;
}
List<String> messageParts = [warningMessage];
@@ -4425,11 +4428,26 @@
Map<LibraryElement, Set<Library>> _libraryElementReexportedBy = new Map();
void _tagReexportsFor(
- final Library tll, final LibraryElement libraryElement) {
+ final Library topLevelLibrary,
+ final LibraryElement libraryElement,
+ [ExportElement lastExportedElement]) {
+ if (libraryElement == null) {
+ // The first call to _tagReexportFor should not have a null libraryElement.
+ assert(lastExportedElement != null);
+ warnOnElement(
+ findOrCreateLibraryFor(lastExportedElement.enclosingElement),
+ PackageWarning.unresolvedExport,
+ message: '"${lastExportedElement.uri}"',
+ referredFrom: <Locatable>[topLevelLibrary]);
+ return;
+ }
_libraryElementReexportedBy.putIfAbsent(libraryElement, () => new Set());
- _libraryElementReexportedBy[libraryElement].add(tll);
+ _libraryElementReexportedBy[libraryElement].add(topLevelLibrary);
for (ExportElement exportedElement in libraryElement.exports) {
- _tagReexportsFor(tll, exportedElement.exportedLibrary);
+ _tagReexportsFor(
+ topLevelLibrary,
+ exportedElement.exportedLibrary,
+ exportedElement);
}
}
@@ -5674,6 +5692,7 @@
for (PackageWarning kind in PackageWarning.values) {
switch (kind) {
case PackageWarning.invalidParameter:
+ case PackageWarning.unresolvedExport:
warningOptions.error(kind);
break;
default:
diff --git a/lib/src/warnings.dart b/lib/src/warnings.dart
index 62857c7..9d350c6 100644
--- a/lib/src/warnings.dart
+++ b/lib/src/warnings.dart
@@ -94,6 +94,10 @@
PackageWarning.deprecated,
"deprecated",
"A dartdoc directive has a deprecated format."),
+ PackageWarning.unresolvedExport: const PackageWarningHelpText(
+ PackageWarning.unresolvedExport,
+ "unresolvedExport",
+ "An export refers to a URI that can not be resolved."),
};
/// Something that package warnings can be called on. Optionally associated
@@ -135,6 +139,7 @@
typeAsHtml,
invalidParameter,
deprecated,
+ unresolvedExport,
}
/// Warnings it is OK to skip if we can determine the warnable isn't documented.
@@ -158,6 +163,7 @@
PackageWarningOptions(this.verboseWarnings) {
asWarnings.addAll(PackageWarning.values);
ignore(PackageWarning.typeAsHtml);
+ error(PackageWarning.unresolvedExport);
}
void _assertInvariantsOk() {
@@ -194,7 +200,7 @@
}
class PackageWarningCounter {
- final _countedWarnings =
+ final countedWarnings =
new Map<Element, Set<Tuple2<PackageWarning, String>>>();
final _warningCounts = new Map<PackageWarning, int>();
final PackageWarningOptions options;
@@ -249,8 +255,8 @@
/// Returns true if we've already warned for this.
bool hasWarning(Warnable element, PackageWarning kind, String message) {
Tuple2<PackageWarning, String> warningData = new Tuple2(kind, message);
- if (_countedWarnings.containsKey(element?.element)) {
- return _countedWarnings[element?.element].contains(warningData);
+ if (countedWarnings.containsKey(element?.element)) {
+ return countedWarnings[element?.element].contains(warningData);
}
return false;
}
@@ -263,8 +269,8 @@
Tuple2<PackageWarning, String> warningData = new Tuple2(kind, message);
_warningCounts.putIfAbsent(kind, () => 0);
_warningCounts[kind] += 1;
- _countedWarnings.putIfAbsent(element?.element, () => new Set());
- _countedWarnings[element?.element].add(warningData);
+ countedWarnings.putIfAbsent(element?.element, () => new Set());
+ countedWarnings[element?.element].add(warningData);
_writeWarning(kind, element?.fullyQualifiedName, fullMessage);
}
diff --git a/test/dartdoc_test.dart b/test/dartdoc_test.dart
index f4c43a6..dab6e7d 100644
--- a/test/dartdoc_test.dart
+++ b/test/dartdoc_test.dart
@@ -9,6 +9,8 @@
import 'package:dartdoc/dartdoc.dart';
import 'package:dartdoc/src/model.dart';
+import 'package:dartdoc/src/tuple.dart';
+import 'package:dartdoc/src/warnings.dart';
import 'package:path/path.dart' as pathLib;
import 'package:test/test.dart';
@@ -33,9 +35,25 @@
argv..addAll(['--input', packageRoot.path])..addAll(outputParam)));
}
+ test('with broken reexport chain', () async {
+ Dartdoc dartdoc = await buildDartdoc([], testPackageImportExportError);
+ DartdocResults results = await dartdoc.generateDocsBase();
+ PackageGraph p = results.packageGraph;
+ Iterable<String> unresolvedExportWarnings = p
+ .packageWarningCounter.countedWarnings.values
+ .expand<String>((Set<Tuple2<PackageWarning, String>> s) => s
+ .where((Tuple2<PackageWarning, String> t) =>
+ t.item1 == PackageWarning.unresolvedExport)
+ .map<String>((Tuple2<PackageWarning, String> t) => t.item2));
+
+ expect(unresolvedExportWarnings.length, equals(1));
+ expect(unresolvedExportWarnings.first,
+ equals('"package:not_referenced_in_pubspec/library3.dart"'));
+ });
+
group('include/exclude parameters', () {
test('with config file', () async {
- Dartdoc dartdoc = await buildDartdoc([], testPackageImportExport);
+ Dartdoc dartdoc = await buildDartdoc([], testPackageIncludeExclude);
DartdocResults results = await dartdoc.generateDocs();
PackageGraph p = results.packageGraph;
expect(p.localPublicLibraries.map((l) => l.name),
@@ -44,7 +62,7 @@
test('with include command line argument', () async {
Dartdoc dartdoc = await buildDartdoc(
- ['--include', 'another_included'], testPackageImportExport);
+ ['--include', 'another_included'], testPackageIncludeExclude);
DartdocResults results = await dartdoc.generateDocs();
PackageGraph p = results.packageGraph;
expect(p.localPublicLibraries.length, equals(1));
@@ -53,7 +71,7 @@
test('with exclude command line argument', () async {
Dartdoc dartdoc = await buildDartdoc(
- ['--exclude', 'more_included'], testPackageImportExport);
+ ['--exclude', 'more_included'], testPackageIncludeExclude);
DartdocResults results = await dartdoc.generateDocs();
PackageGraph p = results.packageGraph;
expect(p.localPublicLibraries.length, equals(1));
diff --git a/test/src/utils.dart b/test/src/utils.dart
index 688677c..c96c47d 100644
--- a/test/src/utils.dart
+++ b/test/src/utils.dart
@@ -27,8 +27,10 @@
new Directory('testing/test_package_embedder_yaml');
final Directory testPackageWithNoReadme =
new Directory('testing/test_package_small');
-final Directory testPackageImportExport =
+final Directory testPackageIncludeExclude =
new Directory('testing/test_package_include_exclude');
+final Directory testPackageImportExportError =
+ new Directory('testing/test_package_import_export_error');
/// Convenience factory to build a [DartdocGeneratorOptionContext] and associate
/// it with a [DartdocOptionSet] based on the current working directory and/or
diff --git a/testing/test_package_export_error/lib/library1.dart b/testing/test_package_export_error/lib/library1.dart
new file mode 100644
index 0000000..a179dd1
--- /dev/null
+++ b/testing/test_package_export_error/lib/library1.dart
@@ -0,0 +1,5 @@
+library library1;
+
+/// An invalid reexport - a non-existent library3.dart from 'not_referenced_in_pubspec'
+/// package.
+export 'package:not_referenced_in_pubspec/library3.dart' show Lib3Class;
diff --git a/testing/test_package_export_error/lib/library2.dart b/testing/test_package_export_error/lib/library2.dart
new file mode 100644
index 0000000..2da7059
--- /dev/null
+++ b/testing/test_package_export_error/lib/library2.dart
@@ -0,0 +1,5 @@
+library library2;
+
+export 'package:test_package_export_error/library1.dart';
+
+class Lib2Class {}
diff --git a/testing/test_package_export_error/pubspec.yaml b/testing/test_package_export_error/pubspec.yaml
new file mode 100644
index 0000000..377e320
--- /dev/null
+++ b/testing/test_package_export_error/pubspec.yaml
@@ -0,0 +1,5 @@
+name: test_package_export_error
+version: 0.0.1
+description: A simple console application.
+environment:
+ sdk: <=3.0.0
diff --git a/testing/test_package_import_export_error/lib/main.dart b/testing/test_package_import_export_error/lib/main.dart
new file mode 100644
index 0000000..2849af2
--- /dev/null
+++ b/testing/test_package_import_export_error/lib/main.dart
@@ -0,0 +1,6 @@
+library main;
+
+export 'package:test_package_export_error/library2.dart';
+
+/// This is an important class.
+class BugFreeClass {}
diff --git a/testing/test_package_import_export_error/pubspec.yaml b/testing/test_package_import_export_error/pubspec.yaml
new file mode 100644
index 0000000..4f75cf4
--- /dev/null
+++ b/testing/test_package_import_export_error/pubspec.yaml
@@ -0,0 +1,9 @@
+name: test_package_import_export_error
+version: 0.0.1
+description: A simple console application.
+environment:
+ sdk: <=3.0.0
+
+dependencies:
+ test_package_export_error:
+ path: ../test_package_export_error