Skip to content

Commit 47b60b4

Browse files
authored
Canonicalization overhaul (#1368)
Fix some structural problems with elements in dartdoc leading to canonicalization bugs, see CHANGELOG.md
1 parent 0819d42 commit 47b60b4

File tree

154 files changed

+1968
-17447
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

154 files changed

+1968
-17447
lines changed

CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,37 @@
1+
## 0.10.0
2+
3+
* fix canonicalization problems and related issues introduced or not addressed
4+
in 0.9.11, including:
5+
* (#1361), (#1232), (#1239 (partial))- Broken links in enums
6+
* (#1345) and (#1090)- Reexports have wrong links in many places
7+
* (#1341), (#1197 (partial)) - Duplicate docs still in some cases
8+
* (#1334) - Some classes don't list their subclasses
9+
* Inheritable class members had incorrect canonicalization in many cases
10+
* ... and many other unfiled bugs relating to inheritance and duplicate files.
11+
* Dartdoc no longer creates documentation for a given identifier more than once.
12+
This means dartdoc is 20-30% faster on complex packages.
13+
* --auto-include-dependencies is now recursive past one layer (#589) It now drills
14+
all the way down and will dive into the SDK and other packages.
15+
* Change display of warnings to be more consistent; warnings now always
16+
go to stderr and are printed on their own line.
17+
* Dartdoc now warns when it is unable to find a canonical object to link to
18+
* Dartdoc now warns if a package exports an identifier so that it is
19+
ambiguous which one should be treated as canonical
20+
* Dartdoc now has a number of asserts in checked mode for issues solved
21+
and as-yet-unsolved, including (#1367) or canonicalization problems; try
22+
running in checked mode if you see structural problems in generated docs and
23+
see if an assert fires.
24+
* Dartdoc internals have changed significantly:
25+
* Package now owns the calculation of recursive dependencies with a factory
26+
constructor, Package.withAutoincludedDependencies.
27+
* ModelElements and Libraries now have Package-scoped caches.
28+
* ModelElements and their subclasses now must be constructed from a single
29+
factory, ModelElements.from
30+
* Package has new methods to assist canonicalization, including
31+
findCanonicalLibraryFor and findCanonicalModelElementFor.
32+
* New mixin "Inheritable" helps class members calculate canonicalization
33+
for inheritable members
34+
135
## 0.9.13
236

337
* fix grind check-links and check-sdk-links (#1360)

bin/dartdoc.dart

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ main(List<String> arguments) async {
4141

4242
Directory sdkDir = getSdkDir();
4343
if (sdkDir == null) {
44-
print("Error: unable to locate the Dart SDK.");
44+
stderr.write(" Error: unable to locate the Dart SDK.");
4545
exit(1);
4646
}
4747

@@ -56,13 +56,15 @@ main(List<String> arguments) async {
5656

5757
var readme = args['sdk-readme'];
5858
if (readme != null && !(new File(readme).existsSync())) {
59-
print("Error: unable to locate the SDK description file at $readme.");
59+
stderr
60+
.write(" Error: unable to locate the SDK description file at $readme.");
6061
exit(1);
6162
}
6263

6364
Directory inputDir = new Directory(args['input']);
6465
if (!inputDir.existsSync()) {
65-
print("Error: unable to locate the input directory at ${inputDir.path}.");
66+
stderr.write(
67+
" Error: unable to locate the input directory at ${inputDir.path}.");
6668
exit(1);
6769
}
6870

@@ -75,15 +77,15 @@ main(List<String> arguments) async {
7577
args['footer'].map(_resolveTildePath).toList() as List<String>;
7678
for (String footerFilePath in footerFilePaths) {
7779
if (!new File(footerFilePath).existsSync()) {
78-
print("Error: unable to locate footer file: ${footerFilePath}.");
80+
stderr.write(" Error: unable to locate footer file: ${footerFilePath}.");
7981
exit(1);
8082
}
8183
}
8284
List<String> headerFilePaths =
8385
args['header'].map(_resolveTildePath).toList() as List<String>;
8486
for (String headerFilePath in footerFilePaths) {
8587
if (!new File(headerFilePath).existsSync()) {
86-
print("Error: unable to locate header file: ${headerFilePath}.");
88+
stderr.write(" Error: unable to locate header file: ${headerFilePath}.");
8789
exit(1);
8890
}
8991
}
@@ -96,7 +98,8 @@ main(List<String> arguments) async {
9698

9799
if (args.rest.isNotEmpty) {
98100
var unknownArgs = args.rest.join(' ');
99-
print('Error: detected unknown command-line argument(s): $unknownArgs');
101+
stderr.write(
102+
'Error: detected unknown command-line argument(s): $unknownArgs');
100103
_printUsageAndExit(parser, exitCode: 1);
101104
}
102105

lib/dartdoc.dart

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export 'src/package_meta.dart';
4040

4141
const String name = 'dartdoc';
4242
// Update when pubspec version changes.
43-
const String version = '0.9.13';
43+
const String version = '0.10.0';
4444

4545
final String defaultOutDir = path.join('doc', 'api');
4646

@@ -134,6 +134,7 @@ class DartDoc {
134134
? const []
135135
: findFilesToDocumentInPackage(rootDir.path).toList();
136136

137+
// TODO(jcollins-g): seems like most of this belongs in the Package constructor
137138
List<LibraryElement> libraries = _parseLibraries(files, includeExternals);
138139

139140
if (includes != null && includes.isNotEmpty) {
@@ -154,21 +155,27 @@ class DartDoc {
154155
});
155156
}
156157

157-
if (includes.isNotEmpty || excludes.isNotEmpty) {
158-
print('generating docs for libraries ${libraries.join(', ')}\n');
159-
}
160-
161-
Package package = new Package(libraries, packageMeta);
162-
158+
Package package;
163159
if (config != null && config.autoIncludeDependencies) {
164-
final newLibraryElements =
165-
_buildLibrariesWithAutoincludedDependencies(package);
166-
Library.clearLibraryMap();
167-
package = new Package(newLibraryElements, packageMeta);
160+
package = Package.withAutoIncludedDependencies(libraries, packageMeta);
161+
libraries = package.libraries.map((l) => l.element).toList();
162+
// remove excluded libraries again, in case they are picked up through
163+
// dependencies.
164+
excludes.forEach((pattern) {
165+
libraries.removeWhere((lib) {
166+
return lib.name.startsWith(pattern) || lib.name == pattern;
167+
});
168+
});
168169
}
170+
package = new Package(libraries, packageMeta);
171+
172+
print(
173+
'generating docs for libraries ${package.libraries.map((Library l) => l.name).join(', ')}\n');
169174

170175
// Go through docs of every model element in package to prebuild the macros index
171-
package.allModelElements.forEach((m) => m.documentation);
176+
// TODO(jcollins-g): move index building into a cached-on-demand generation
177+
// like most other bits in [Package].
178+
package.allCanonicalModelElements.forEach((m) => m.documentation);
172179

173180
// Create the out directory.
174181
if (!outputDir.existsSync()) outputDir.createSync(recursive: true);
@@ -179,11 +186,11 @@ class DartDoc {
179186

180187
double seconds = _stopwatch.elapsedMilliseconds / 1000.0;
181188
print(
182-
"\nDocumented ${libraries.length} librar${libraries.length == 1 ? 'y' : 'ies'} "
189+
"\nDocumented ${package.libraries.length} librar${package.libraries.length == 1 ? 'y' : 'ies'} "
183190
"in ${seconds.toStringAsFixed(1)} seconds.");
184191

185-
if (libraries.isEmpty) {
186-
print(
192+
if (package.libraries.isEmpty) {
193+
stderr.write(
187194
"\ndartdoc could not find any libraries to document. Run `pub get` and try again.");
188195
}
189196

@@ -192,7 +199,7 @@ class DartDoc {
192199

193200
List<LibraryElement> _parseLibraries(
194201
List<String> files, List<String> includeExternals) {
195-
List<LibraryElement> libraries = [];
202+
Set<LibraryElement> libraries = new Set();
196203
DartSdk sdk = new FolderBasedDartSdk(PhysicalResourceProvider.INSTANCE,
197204
PhysicalResourceProvider.INSTANCE.getFolder(sdkDir.path));
198205
List<UriResolver> resolvers = [];
@@ -257,7 +264,7 @@ class DartDoc {
257264
sources.add(source);
258265
if (context.computeKindOf(source) == SourceKind.LIBRARY) {
259266
LibraryElement library = context.computeLibraryElement(source);
260-
libraries.add(library);
267+
if (!isPrivate(library)) libraries.add(library);
261268
}
262269
}
263270

@@ -281,11 +288,35 @@ class DartDoc {
281288
LibraryElement library = context.computeLibraryElement(source);
282289
String libraryName = Library.getLibraryName(library);
283290
var fullPath = source.fullName;
291+
284292
if (includeExternals.any((string) => fullPath.endsWith(string))) {
285293
if (libraries.map(Library.getLibraryName).contains(libraryName)) {
286294
continue;
287295
}
288296
libraries.add(library);
297+
} else if (config != null &&
298+
config.autoIncludeDependencies &&
299+
libraryName != '') {
300+
File searchFile = new File(fullPath);
301+
searchFile =
302+
new File(path.join(searchFile.parent.path, 'pubspec.yaml'));
303+
bool foundLibSrc = false;
304+
while (!foundLibSrc && searchFile.parent != null) {
305+
if (searchFile.existsSync()) break;
306+
List<String> pathParts = path.split(searchFile.parent.path);
307+
// This is a pretty intensely hardcoded convention, but there seems to
308+
// to be no other way to identify what might be a "top level" library
309+
// here. If lib/src is in the path between the file and the pubspec,
310+
// assume that this is supposed to be private.
311+
if (pathParts.length < 2) break;
312+
pathParts = pathParts.sublist(pathParts.length - 2, pathParts.length);
313+
foundLibSrc =
314+
path.join(pathParts[0], pathParts[1]) == path.join('lib', 'src');
315+
searchFile = new File(
316+
path.join(searchFile.parent.parent.path, 'pubspec.yaml'));
317+
}
318+
if (foundLibSrc) continue;
319+
libraries.add(library);
289320
}
290321
}
291322

@@ -376,26 +407,3 @@ class _Error implements Comparable<_Error> {
376407
@override
377408
String toString() => '[${severityName}] ${description}';
378409
}
379-
380-
Iterable<LibraryElement> _buildLibrariesWithAutoincludedDependencies(
381-
Package package) {
382-
final List<LibraryElement> newLibraryElements = []
383-
..addAll(package.libraries.map((l) => l.element as LibraryElement));
384-
385-
package.allModelElements.forEach((modelElement) {
386-
modelElement.usedElements.forEach((used) {
387-
if (used != null && used.modelType != null) {
388-
final ModelElement modelTypeElement = used.modelType.element;
389-
final library = package.findLibraryFor(modelTypeElement.element);
390-
if (library == null && modelTypeElement.library != null) {
391-
if (!newLibraryElements.contains(modelTypeElement.library.element) &&
392-
!modelTypeElement.library.name.startsWith("dart:")) {
393-
newLibraryElements.add(modelTypeElement.library.element);
394-
}
395-
}
396-
}
397-
});
398-
});
399-
400-
return newLibraryElements;
401-
}

lib/src/element_type.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ class ElementType {
6363
String get name => _type.name;
6464

6565
ModelElement get returnElement {
66-
Element e = _returnTypeCore.element;
66+
Element e;
67+
if (_type is FunctionType)
68+
e = _returnTypeCore.element;
69+
else
70+
e = _type.element;
6771
if (e == null || e.library == null) {
6872
return null;
6973
}

lib/src/export_graph.dart

Lines changed: 0 additions & 76 deletions
This file was deleted.

0 commit comments

Comments
 (0)