Skip to content

Commit c06d53d

Browse files
committed
Improve support for package-with-macro-application
The previous fix is no good; it was concerned with _augmentations_, assuming they cannot be read from disk. This is not the case. Only a macro-generated augmentation cannot be read from disk. Instead we change tactics and say we _can_ include source code from a macro (maybe we shouldn't), and just ask analyzer for all source files, from the AnalysisContext. Other changes to facilitate this: * Do not dispose the AnalysisContextCollection right away; we should wait until all docs are generated. * Pass the AnalysisContext to ModelNode, and no longer pass the ResourceProvider. * Remove the file cache thing in model_utils; instead ModelNode.sourceCode only relies on the AnalysisContext. Theoretically there is already caching over that at analyzer? But I benchmarked with Flutter and saw no time-to-document or max-RSS changes. * Do not ask the AnalysisContextCollection for the AnalysisContext _for every file_! Cache it. To facilitate that, change `PubPackageBuilder()` into a factory. * Sort the fields instantiated by the `PackageGraph.uninitialized` constructor above it. * In `addFilesReferencedBy`, also look at augmentation imports.
1 parent d8e7f99 commit c06d53d

7 files changed

+116
-108
lines changed

lib/src/dartdoc.dart

+1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ class Dartdoc {
217217
if (config.showStats) {
218218
logInfo(runtimeStats.buildReport());
219219
}
220+
await packageBuilder.dispose();
220221
return DartdocResults(config.topLevelPackageMeta, packageGraph, _outputDir);
221222
}
222223

lib/src/model/model_node.dart

+22-15
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@
44

55
import 'dart:convert';
66

7+
import 'package:analyzer/dart/analysis/analysis_context.dart';
8+
import 'package:analyzer/dart/analysis/results.dart';
79
import 'package:analyzer/dart/ast/ast.dart';
810
import 'package:analyzer/dart/element/element.dart';
9-
import 'package:analyzer/file_system/file_system.dart';
10-
import 'package:dartdoc/src/model_utils.dart' as model_utils;
1111
import 'package:meta/meta.dart';
1212

1313
/// Stripped down information derived from [AstNode] containing only information
1414
/// needed to resurrect the source code of [element].
1515
class ModelNode {
1616
final Element _element;
17-
final ResourceProvider _resourceProvider;
17+
final AnalysisContext _analysisContext;
1818
final int _sourceEnd;
1919
final int _sourceOffset;
2020

2121
factory ModelNode(
22-
AstNode? sourceNode, Element element, ResourceProvider resourceProvider) {
22+
AstNode? sourceNode, Element element, AnalysisContext analysisContext) {
2323
if (sourceNode == null) {
24-
return ModelNode._(element, resourceProvider,
24+
return ModelNode._(element, analysisContext,
2525
sourceEnd: -1, sourceOffset: -1);
2626
} else {
2727
// Get a node higher up the syntax tree that includes the semicolon.
@@ -32,12 +32,12 @@ class ModelNode {
3232
assert(sourceNode is FieldDeclaration ||
3333
sourceNode is TopLevelVariableDeclaration);
3434
}
35-
return ModelNode._(element, resourceProvider,
35+
return ModelNode._(element, analysisContext,
3636
sourceEnd: sourceNode.end, sourceOffset: sourceNode.offset);
3737
}
3838
}
3939

40-
ModelNode._(this._element, this._resourceProvider,
40+
ModelNode._(this._element, this._analysisContext,
4141
{required int sourceEnd, required int sourceOffset})
4242
: _sourceEnd = sourceEnd,
4343
_sourceOffset = sourceOffset;
@@ -46,14 +46,21 @@ class ModelNode {
4646

4747
/// The text of the source code of this node, stripped of the leading
4848
/// indentation, and stripped of the doc comments.
49-
late final String sourceCode = _isSynthetic
50-
? ''
51-
: model_utils
52-
.getFileContentsFor(_element, _resourceProvider)
53-
.substringFromLineStart(_sourceOffset, _sourceEnd)
54-
.stripIndent
55-
.stripDocComments
56-
.trim();
49+
late final String sourceCode = () {
50+
if (_isSynthetic) return '';
51+
52+
var path = _element.source?.fullName;
53+
if (path == null) return '';
54+
55+
var fileResult = _analysisContext.currentSession.getFile(path);
56+
if (fileResult is! FileResult) return '';
57+
58+
return fileResult.content
59+
.substringFromLineStart(_sourceOffset, _sourceEnd)
60+
.stripIndent
61+
.stripDocComments
62+
.trim();
63+
}();
5764
}
5865

5966
@visibleForTesting

lib/src/model/package_builder.dart

+70-32
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:async';
66

7+
import 'package:analyzer/dart/analysis/analysis_context.dart';
78
import 'package:analyzer/dart/analysis/context_root.dart';
89
import 'package:analyzer/dart/analysis/results.dart';
910
import 'package:analyzer/dart/ast/ast.dart';
@@ -36,6 +37,8 @@ import 'package:path/path.dart' as p show Context;
3637
abstract class PackageBuilder {
3738
// Builds package graph to be used by documentation generator.
3839
Future<PackageGraph> buildPackageGraph();
40+
41+
Future<void> dispose();
3942
}
4043

4144
/// A package builder that understands pub package format.
@@ -44,12 +47,49 @@ class PubPackageBuilder implements PackageBuilder {
4447
final PackageMetaProvider _packageMetaProvider;
4548
final PackageConfigProvider _packageConfigProvider;
4649

47-
PubPackageBuilder(
50+
final AnalysisContextCollectionImpl _contextCollection;
51+
final AnalysisContext _analysisContext;
52+
53+
factory PubPackageBuilder(
54+
DartdocOptionContext config,
55+
PackageMetaProvider packageMetaProvider,
56+
PackageConfigProvider packageConfigProvider, {
57+
@visibleForTesting bool skipUnreachableSdkLibraries = false,
58+
}) {
59+
var contextCollection = AnalysisContextCollectionImpl(
60+
includedPaths: [config.inputDir],
61+
// TODO(jcollins-g): should we pass excluded directories here instead
62+
// of handling it ourselves?
63+
resourceProvider: packageMetaProvider.resourceProvider,
64+
sdkPath: config.sdkDir,
65+
updateAnalysisOptions2: ({
66+
required AnalysisOptionsImpl analysisOptions,
67+
required ContextRoot contextRoot,
68+
required DartSdk sdk,
69+
}) =>
70+
analysisOptions
71+
..warning = false
72+
..lint = false,
73+
);
74+
return PubPackageBuilder._(
75+
config,
76+
packageMetaProvider,
77+
packageConfigProvider,
78+
contextCollection,
79+
analysisContext: contextCollection.contextFor(config.inputDir),
80+
skipUnreachableSdkLibraries: skipUnreachableSdkLibraries,
81+
);
82+
}
83+
84+
PubPackageBuilder._(
4885
this._config,
4986
this._packageMetaProvider,
50-
this._packageConfigProvider, {
51-
@visibleForTesting bool skipUnreachableSdkLibraries = false,
52-
}) : _skipUnreachableSdkLibraries = skipUnreachableSdkLibraries;
87+
this._packageConfigProvider,
88+
this._contextCollection, {
89+
required AnalysisContext analysisContext,
90+
required bool skipUnreachableSdkLibraries,
91+
}) : _analysisContext = analysisContext,
92+
_skipUnreachableSdkLibraries = skipUnreachableSdkLibraries;
5393

5494
@override
5595
Future<PackageGraph> buildPackageGraph() async {
@@ -77,6 +117,7 @@ class PubPackageBuilder implements PackageBuilder {
77117
_sdk,
78118
_embedderSdkUris.isNotEmpty,
79119
_packageMetaProvider,
120+
_analysisContext,
80121
);
81122
await _getLibraries(newGraph);
82123
runtimeStats.endPerfTask();
@@ -93,6 +134,12 @@ class PubPackageBuilder implements PackageBuilder {
93134
return newGraph;
94135
}
95136

137+
@override
138+
Future<void> dispose() async {
139+
// Shutdown macro support.
140+
await _contextCollection.dispose();
141+
}
142+
96143
late final DartSdk _sdk = _packageMetaProvider.defaultSdk ??
97144
FolderBasedDartSdk(
98145
_resourceProvider, _resourceProvider.getFolder(_config.sdkDir));
@@ -132,22 +179,6 @@ class PubPackageBuilder implements PackageBuilder {
132179

133180
late final Map<String, List<Folder>> _packageMap;
134181

135-
late final _contextCollection = AnalysisContextCollectionImpl(
136-
includedPaths: [_config.inputDir],
137-
// TODO(jcollins-g): should we pass excluded directories here instead of
138-
// handling it ourselves?
139-
resourceProvider: _resourceProvider,
140-
sdkPath: _config.sdkDir,
141-
updateAnalysisOptions2: ({
142-
required AnalysisOptionsImpl analysisOptions,
143-
required ContextRoot contextRoot,
144-
required DartSdk sdk,
145-
}) =>
146-
analysisOptions
147-
..warning = false
148-
..lint = false,
149-
);
150-
151182
List<String> get _sdkFilesToDocument => [
152183
for (var sdkLib in _sdk.sdkLibraries)
153184
_sdk.mapDartUri(sdkLib.shortName)!.fullName,
@@ -245,6 +276,8 @@ class PubPackageBuilder implements PackageBuilder {
245276
}
246277
var resolvedLibrary = await _resolveLibrary(file);
247278
if (resolvedLibrary == null) {
279+
// `file` did not resolve to a _library_; could be a part, an
280+
// augmentation, or some other invalid result.
248281
_knownParts.add(file);
249282
continue;
250283
}
@@ -459,8 +492,6 @@ class PubPackageBuilder implements PackageBuilder {
459492
specialFiles.difference(files),
460493
addingSpecials: true,
461494
);
462-
// Shutdown macro support.
463-
await _contextCollection.dispose();
464495
}
465496

466497
/// Throws an exception if any configured-to-be-included files were not found
@@ -511,22 +542,29 @@ class DartDocResolvedLibrary {
511542
extension on Set<String> {
512543
/// Adds [element]'s path and all of its part files' paths to `this`, and
513544
/// recursively adds the paths of all imported and exported libraries.
514-
void addFilesReferencedBy(LibraryElement? element) {
515-
if (element != null) {
516-
var path = element.source.fullName;
517-
if (add(path)) {
518-
for (var import in element.libraryImports) {
519-
addFilesReferencedBy(import.importedLibrary);
520-
}
521-
for (var export in element.libraryExports) {
522-
addFilesReferencedBy(export.exportedLibrary);
523-
}
545+
void addFilesReferencedBy(LibraryOrAugmentationElement? element) {
546+
if (element == null) return;
547+
548+
var path = element.source?.fullName;
549+
if (path == null) return;
550+
551+
if (add(path)) {
552+
for (var import in element.libraryImports) {
553+
addFilesReferencedBy(import.importedLibrary);
554+
}
555+
for (var export in element.libraryExports) {
556+
addFilesReferencedBy(export.exportedLibrary);
557+
}
558+
if (element is LibraryElement) {
524559
for (var part in element.parts
525560
.map((e) => e.uri)
526561
.whereType<DirectiveUriWithUnit>()) {
527562
add(part.source.fullName);
528563
}
529564
}
565+
for (var augmentation in element.augmentationImports) {
566+
addFilesReferencedBy(augmentation.importedAugmentation);
567+
}
530568
}
531569
}
532570
}

lib/src/model/package_graph.dart

+22-18
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:collection';
66

7+
import 'package:analyzer/dart/analysis/analysis_context.dart';
78
import 'package:analyzer/dart/ast/ast.dart';
89
import 'package:analyzer/dart/element/element.dart';
910
import 'package:analyzer/file_system/file_system.dart';
@@ -32,11 +33,29 @@ import 'package:dartdoc/src/warnings.dart';
3233
import 'package:meta/meta.dart';
3334

3435
class PackageGraph with CommentReferable, Nameable {
36+
/// Dartdoc's configuration flags.
37+
final DartdocOptionContext config;
38+
39+
final bool hasEmbedderSdk;
40+
41+
/// [PackageMeta] provider for building [PackageMeta]s.
42+
final PackageMetaProvider packageMetaProvider;
43+
44+
final InheritanceManager3 inheritanceManager = InheritanceManager3();
45+
46+
final AnalysisContext analysisContext;
47+
48+
/// PackageMeta for the default package.
49+
final PackageMeta packageMeta;
50+
51+
final Map<Source?, SdkLibrary> sdkLibrarySources;
52+
3553
PackageGraph.uninitialized(
3654
this.config,
3755
DartSdk sdk,
3856
this.hasEmbedderSdk,
3957
this.packageMetaProvider,
58+
this.analysisContext,
4059
) : packageMeta = config.topLevelPackageMeta,
4160
sdkLibrarySources = {
4261
for (var lib in sdk.sdkLibraries) sdk.mapDartUri(lib.shortName): lib
@@ -46,10 +65,6 @@ class PackageGraph with CommentReferable, Nameable {
4665
Package.fromPackageMeta(packageMeta, this);
4766
}
4867

49-
final InheritanceManager3 inheritanceManager = InheritanceManager3();
50-
51-
final Map<Source?, SdkLibrary> sdkLibrarySources;
52-
5368
void dispose() {
5469
// Clear out any cached tool snapshots and temporary directories.
5570
// TODO(jcollins-g): Consider ownership change for these objects
@@ -260,7 +275,7 @@ class PackageGraph with CommentReferable, Nameable {
260275
for (var field in fields) {
261276
var element = field.declaredElement!;
262277
_modelNodes.putIfAbsent(
263-
element, () => ModelNode(field, element, resourceProvider));
278+
element, () => ModelNode(field, element, analysisContext));
264279
}
265280
return;
266281
}
@@ -269,13 +284,13 @@ class PackageGraph with CommentReferable, Nameable {
269284
for (var field in fields) {
270285
var element = field.declaredElement!;
271286
_modelNodes.putIfAbsent(
272-
element, () => ModelNode(field, element, resourceProvider));
287+
element, () => ModelNode(field, element, analysisContext));
273288
}
274289
return;
275290
}
276291
var element = declaration.declaredElement!;
277292
_modelNodes.putIfAbsent(
278-
element, () => ModelNode(declaration, element, resourceProvider));
293+
element, () => ModelNode(declaration, element, analysisContext));
279294
}
280295

281296
ModelNode? getModelNodeFor(Element element) => _modelNodes[element];
@@ -338,23 +353,12 @@ class PackageGraph with CommentReferable, Nameable {
338353
/// A list of extensions that exist in the package graph.
339354
final List<Extension> _extensions = [];
340355

341-
/// PackageMeta for the default package.
342-
final PackageMeta packageMeta;
343-
344356
/// Name of the default package.
345357
String get defaultPackageName => packageMeta.name;
346358

347-
/// Dartdoc's configuration flags.
348-
final DartdocOptionContext config;
349-
350-
/// PackageMeta Provider for building [PackageMeta]s.
351-
final PackageMetaProvider packageMetaProvider;
352-
353359
late final Package defaultPackage =
354360
Package.fromPackageMeta(packageMeta, this);
355361

356-
final bool hasEmbedderSdk;
357-
358362
bool get hasFooterVersion => !config.excludeFooterVersion;
359363

360364
@override

lib/src/model/source_code_mixin.dart

+1-17
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ mixin SourceCode implements Documentable {
1313

1414
Element? get element;
1515

16-
bool get hasSourceCode =>
17-
config.includeSource && !element.isAugmentation && sourceCode.isNotEmpty;
16+
bool get hasSourceCode => config.includeSource && sourceCode.isNotEmpty;
1817

1918
Library? get library;
2019

@@ -23,18 +22,3 @@ mixin SourceCode implements Documentable {
2322
return modelNode == null ? '' : modelNode.sourceCode;
2423
}
2524
}
26-
27-
extension on Element? {
28-
/// Whether `this` is an augmentation method or property.
29-
///
30-
/// This property should only be referenced for elements whose source code we
31-
/// may wish to refer to.
32-
bool get isAugmentation {
33-
final self = this;
34-
return switch (self) {
35-
ExecutableElement() => self.augmentationTarget != null,
36-
PropertyInducingElement() => self.augmentationTarget != null,
37-
_ => false,
38-
};
39-
}
40-
}

0 commit comments

Comments
 (0)