Skip to content

Commit c1c0854

Browse files
authored
Improve support for package-with-macro-application (#3687)
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 b28ee1f commit c1c0854

8 files changed

+117
-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/generator/templates.runtime_renderers.dart

+1
Original file line numberDiff line numberDiff line change
@@ -16851,6 +16851,7 @@ const _invisibleGetters = {
1685116851
'allLibraries',
1685216852
'allLibrariesAdded',
1685316853
'allLocalModelElements',
16854+
'analysisContext',
1685416855
'breadcrumbName',
1685516856
'config',
1685616857
'dartCoreObject',

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 {
@@ -68,6 +108,7 @@ class PubPackageBuilder implements PackageBuilder {
68108
_sdk,
69109
_embedderSdkUris.isNotEmpty,
70110
_packageMetaProvider,
111+
_analysisContext,
71112
);
72113
await _getLibraries(newGraph);
73114
runtimeStats.endPerfTask();
@@ -84,6 +125,12 @@ class PubPackageBuilder implements PackageBuilder {
84125
return newGraph;
85126
}
86127

128+
@override
129+
Future<void> dispose() async {
130+
// Shutdown macro support.
131+
await _contextCollection.dispose();
132+
}
133+
87134
late final DartSdk _sdk = _packageMetaProvider.defaultSdk ??
88135
FolderBasedDartSdk(
89136
_resourceProvider, _resourceProvider.getFolder(_config.sdkDir));
@@ -123,22 +170,6 @@ class PubPackageBuilder implements PackageBuilder {
123170

124171
late final Map<String, List<Folder>> _packageMap;
125172

126-
late final _contextCollection = AnalysisContextCollectionImpl(
127-
includedPaths: [_config.inputDir],
128-
// TODO(jcollins-g): should we pass excluded directories here instead of
129-
// handling it ourselves?
130-
resourceProvider: _resourceProvider,
131-
sdkPath: _config.sdkDir,
132-
updateAnalysisOptions2: ({
133-
required AnalysisOptionsImpl analysisOptions,
134-
required ContextRoot contextRoot,
135-
required DartSdk sdk,
136-
}) =>
137-
analysisOptions
138-
..warning = false
139-
..lint = false,
140-
);
141-
142173
List<String> get _sdkFilesToDocument => [
143174
for (var sdkLib in _sdk.sdkLibraries)
144175
_sdk.mapDartUri(sdkLib.shortName)!.fullName,
@@ -236,6 +267,8 @@ class PubPackageBuilder implements PackageBuilder {
236267
}
237268
var resolvedLibrary = await _resolveLibrary(file);
238269
if (resolvedLibrary == null) {
270+
// `file` did not resolve to a _library_; could be a part, an
271+
// augmentation, or some other invalid result.
239272
_knownParts.add(file);
240273
continue;
241274
}
@@ -450,8 +483,6 @@ class PubPackageBuilder implements PackageBuilder {
450483
specialFiles.difference(files),
451484
addingSpecials: true,
452485
);
453-
// Shutdown macro support.
454-
await _contextCollection.dispose();
455486
}
456487

457488
/// Throws an exception if any configured-to-be-included files were not found
@@ -502,22 +533,29 @@ class DartDocResolvedLibrary {
502533
extension on Set<String> {
503534
/// Adds [element]'s path and all of its part files' paths to `this`, and
504535
/// recursively adds the paths of all imported and exported libraries.
505-
void addFilesReferencedBy(LibraryElement? element) {
506-
if (element != null) {
507-
var path = element.source.fullName;
508-
if (add(path)) {
509-
for (var import in element.libraryImports) {
510-
addFilesReferencedBy(import.importedLibrary);
511-
}
512-
for (var export in element.libraryExports) {
513-
addFilesReferencedBy(export.exportedLibrary);
514-
}
536+
void addFilesReferencedBy(LibraryOrAugmentationElement? element) {
537+
if (element == null) return;
538+
539+
var path = element.source?.fullName;
540+
if (path == null) return;
541+
542+
if (add(path)) {
543+
for (var import in element.libraryImports) {
544+
addFilesReferencedBy(import.importedLibrary);
545+
}
546+
for (var export in element.libraryExports) {
547+
addFilesReferencedBy(export.exportedLibrary);
548+
}
549+
if (element is LibraryElement) {
515550
for (var part in element.parts
516551
.map((e) => e.uri)
517552
.whereType<DirectiveUriWithUnit>()) {
518553
add(part.source.fullName);
519554
}
520555
}
556+
for (var augmentation in element.augmentationImports) {
557+
addFilesReferencedBy(augmentation.importedAugmentation);
558+
}
521559
}
522560
}
523561
}

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)