Skip to content

Commit

Permalink
analyzer: Support doc-imports with prefixes
Browse files Browse the repository at this point in the history
Change-Id: I91f7b992e6425a6bf76ee32a68e09b2b03ee4447
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/387861
Reviewed-by: Konstantin Shcheglov <[email protected]>
Commit-Queue: Samuel Rawlins <[email protected]>
  • Loading branch information
srawlins authored and Commit Queue committed Jan 6, 2025
1 parent a8b8df2 commit 292987f
Show file tree
Hide file tree
Showing 20 changed files with 979 additions and 114 deletions.
17 changes: 12 additions & 5 deletions pkg/_fe_analyzer_shared/lib/src/util/dependency_walker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:meta/meta.dart';

/// An instance of [DependencyWalker] contains the core algorithms for
/// walking a dependency graph and evaluating nodes in a safe order.
abstract class DependencyWalker<NodeType extends Node<NodeType>> {
/// Called by [walk] to evaluate a single non-cyclical node, after
/// all that node's dependencies have been evaluated.
/// Evaluates a single non-cyclical node, after all that node's dependencies
/// have been evaluated.
///
/// Should only be called by [walk].
@visibleForOverriding
void evaluate(NodeType v);

/// Called by [walk] to evaluate a strongly connected component
/// containing one or more nodes. All dependencies of the strongly
/// connected component have been evaluated.
/// Evaluates a strongly connected component containing one or more nodes.
///
/// All dependencies of the strongly connected component have been evaluated.
/// Should only be called by [walk].
@visibleForOverriding
void evaluateScc(List<NodeType> scc);

/// Walk the dependency graph starting at [startingPoint], finding
Expand Down
13 changes: 1 addition & 12 deletions pkg/analyzer/lib/src/dart/analysis/driver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ import 'package:meta/meta.dart';
// TODO(scheglov): Clean up the list of implicitly analyzed files.
class AnalysisDriver {
/// The version of data format, should be incremented on every format change.
static const int DATA_VERSION = 424;
static const int DATA_VERSION = 425;

/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.
Expand Down Expand Up @@ -1374,17 +1374,6 @@ class AnalysisDriver {
},
);

for (var import in library.docImports) {
if (import is LibraryImportWithFile) {
if (import.importedLibrary case var libraryFileKind?) {
await libraryContext.load(
targetLibrary: libraryFileKind,
performance: OperationPerformanceImpl('<root>'),
);
}
}
}

var analysisOptions = file.analysisOptions;
var libraryElement =
libraryContext.elementFactory.libraryOfUri2(library.file.uri);
Expand Down
12 changes: 6 additions & 6 deletions pkg/analyzer/lib/src/dart/analysis/file_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ abstract class FileKind {
List<LibraryExportState>? _libraryExports;
List<LibraryImportState>? _libraryImports;
List<PartIncludeState>? _partIncludes;
List<LibraryImportState>? _docImports;
List<LibraryImportState>? _docLibraryImports;

FileKind({
required this.file,
Expand All @@ -223,12 +223,12 @@ abstract class FileKind {
}

/// The import states of each `@docImport` on the library directive.
List<LibraryImportState> get docImports {
if (_docImports case var existing?) {
List<LibraryImportState> get docLibraryImports {
if (_docLibraryImports case var existing?) {
return existing;
}

return _docImports =
return _docLibraryImports =
_unlinkedDocImports.map(_buildLibraryImportState).toFixedList();
}

Expand Down Expand Up @@ -382,7 +382,7 @@ abstract class FileKind {
libraryExports;
libraryImports;
partIncludes;
docImports;
docLibraryImports;
}

@mustCallSuper
Expand All @@ -392,7 +392,7 @@ abstract class FileKind {
_libraryExports?.disposeAll();
_libraryImports?.disposeAll();
_partIncludes?.disposeAll();
_docImports?.disposeAll();
_docLibraryImports?.disposeAll();
}

/// Dispose the containing [LibraryFileKind] cycle.
Expand Down
19 changes: 9 additions & 10 deletions pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,13 @@ class LibraryAnalyzer {

// TODO(scheglov): We don't need to do this for the whole unit.
parsedUnit.accept(ScopeResolverVisitor(
_libraryElement, file.source, _typeProvider, errorListener,
nameScope: unitElement.scope));
_libraryElement,
file.source,
_typeProvider,
errorListener,
nameScope: unitElement.scope,
unitElement: unitElement,
));

FlowAnalysisHelper flowAnalysisHelper = FlowAnalysisHelper(
_testingData != null, _libraryElement.featureSet,
Expand Down Expand Up @@ -817,7 +822,7 @@ class LibraryAnalyzer {
for (var i = 0; i < docImports.length; i++) {
_resolveLibraryDocImportDirective(
directive: docImports[i].import as ImportDirectiveImpl,
state: fileKind.docImports[i],
state: fileKind.docLibraryImports[i],
errorReporter: containerErrorReporter,
);
}
Expand Down Expand Up @@ -851,19 +856,13 @@ class LibraryAnalyzer {
_testingData?.recordTypeConstraintGenerationDataForTesting(
fileAnalysis.file.uri, inferenceDataForTesting!);

var docImportLibraries = [
for (var import in _library.docImports)
if (import is LibraryImportWithFile)
_libraryElement.session.elementFactory
.libraryOfUri2(import.importedFile.uri)
];
unit.accept(ScopeResolverVisitor(
_libraryElement,
source,
_typeProvider,
errorListener,
nameScope: unitElement.scope,
docImportLibraries: docImportLibraries,
unitElement: unitElement,
));

// Nothing for RESOLVED_UNIT8?
Expand Down
3 changes: 3 additions & 0 deletions pkg/analyzer/lib/src/dart/analysis/library_graph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ class _LibraryNode extends graph.Node<_LibraryNode> {
...fileKind.libraryExports
.whereType<LibraryExportWithFile>()
.map((export) => export.exportedLibrary),
...fileKind.docLibraryImports
.whereType<LibraryImportWithFile>()
.map((import) => import.importedLibrary),
];
})
.flattenedToList
Expand Down
71 changes: 63 additions & 8 deletions pkg/analyzer/lib/src/dart/element/element.dart
Original file line number Diff line number Diff line change
Expand Up @@ -781,12 +781,19 @@ class CompilationUnitElementImpl extends UriReferencedElementImpl
List<LibraryImportElementImpl> _libraryImports =
_Sentinel.libraryImportElement;

/// The libraries imported by this unit with a `@docImport`.
List<LibraryImportElementImpl> _docLibraryImports =
_Sentinel.libraryImportElement;

/// The cached list of prefixes from [libraryImports].
List<PrefixElementImpl>? _libraryImportPrefixes;

/// The cached list of prefixes from [prefixes].
List<PrefixElementImpl2>? _libraryImportPrefixes2;

/// The cached list of prefixes from [docLibraryImports].
List<PrefixElementImpl2>? _docLibraryImportPrefixes;

/// The parts included by this unit.
List<PartElementImpl> _parts = const <PartElementImpl>[];

Expand Down Expand Up @@ -899,6 +906,23 @@ class CompilationUnitElementImpl extends UriReferencedElementImpl
@override
List<ClassFragment> get classes2 => classes.cast<ClassFragment>();

List<PrefixElementImpl2> get docLibraryImportPrefixes {
return _docLibraryImportPrefixes ??= _buildDocLibraryImportPrefixes();
}

List<LibraryImportElementImpl> get docLibraryImports {
linkedData?.read(this);
return _docLibraryImports;
}

set docLibraryImports(List<LibraryImportElementImpl> imports) {
_docLibraryImports = imports;
}

List<LibraryImportElementImpl> get docLibraryImports_unresolved {
return _docLibraryImports;
}

@override
LibraryElementImpl get element => library;

Expand Down Expand Up @@ -1299,6 +1323,17 @@ class CompilationUnitElementImpl extends UriReferencedElementImpl
);
}

List<PrefixElementImpl2> _buildDocLibraryImportPrefixes() {
var prefixes = <PrefixElementImpl2>{};
for (var import in docLibraryImports) {
var prefix = import.prefix2?.element;
if (prefix is PrefixElementImpl2) {
prefixes.add(prefix);
}
}
return prefixes.toFixedList();
}

List<PrefixElementImpl> _buildLibraryImportPrefixes() {
var prefixes = <PrefixElementImpl>{};
for (var import in libraryImports) {
Expand Down Expand Up @@ -9377,9 +9412,13 @@ class PrefixElementImpl extends _ExistingElementImpl implements PrefixElement {
/// The scope of this prefix, `null` if not set yet.
PrefixScope? _scope;

final bool _isDocLibraryImport;

/// Initialize a newly created method element to have the given [name] and
/// [nameOffset].
PrefixElementImpl(String super.name, super.nameOffset, {super.reference});
PrefixElementImpl(String super.name, super.nameOffset,
{super.reference, required bool isDocLibraryImport})
: _isDocLibraryImport = isDocLibraryImport;

@override
List<Element2> get children2 => const [];
Expand All @@ -9388,9 +9427,15 @@ class PrefixElementImpl extends _ExistingElementImpl implements PrefixElement {
String get displayName => name;

PrefixElementImpl2 get element2 {
return enclosingElement3.prefixes.firstWhere((element) {
return (element.name3 ?? '') == name;
});
if (_isDocLibraryImport) {
return enclosingElement3.docLibraryImportPrefixes.firstWhere((element) {
return (element.name3 ?? '') == name;
});
} else {
return enclosingElement3.prefixes.firstWhere((element) {
return (element.name3 ?? '') == name;
});
}
}

@override
Expand Down Expand Up @@ -9448,10 +9493,14 @@ class PrefixElementImpl2 extends ElementImpl2 implements PrefixElement2 {

PrefixFragmentImpl lastFragment;

final bool _isDocLibraryImport;

PrefixElementImpl2({
required this.reference,
required this.firstFragment,
}) : lastFragment = firstFragment {
required bool isDocLibraryImport,
}) : lastFragment = firstFragment,
_isDocLibraryImport = isDocLibraryImport {
reference.element2 = this;
}

Expand All @@ -9473,9 +9522,15 @@ class PrefixElementImpl2 extends ElementImpl2 implements PrefixElement2 {

@override
List<LibraryImportElementImpl> get imports {
return firstFragment.enclosingFragment.libraryImports
.where((import) => import.prefix2?.element == this)
.toList();
if (_isDocLibraryImport) {
return firstFragment.enclosingFragment.docLibraryImports
.where((import) => import.prefix2?.element == this)
.toList();
} else {
return firstFragment.enclosingFragment.libraryImports
.where((import) => import.prefix2?.element == this)
.toList();
}
}

@override
Expand Down
Loading

0 comments on commit 292987f

Please sign in to comment.