Add LibraryOrAugmentationFileKind.exports/imports
These will replace eventually FileState.exported/importedFiles,
because only libraries or augmentation can have them. Also, we will
build Export/ImportElement(s) from them, when google3 switches to
using AnalysisDriver.buildPackageBundle(), so we are free to make
changes to the way linker works.
Change-Id: I66674dc7720aa704cfc36d16fc78e17dd05a8b93
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/246681
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 17a79a5..3aa510f 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -86,7 +86,7 @@
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
class AnalysisDriver implements AnalysisDriverGeneric {
/// The version of data format, should be incremented on every format change.
- static const int DATA_VERSION = 221;
+ static const int DATA_VERSION = 222;
/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index a6763f8..66b5ac0 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -85,6 +85,75 @@
});
}
+/// Information about a single `import` directive.
+class ExportDirectiveState {
+ final UnlinkedNamespaceDirective directive;
+
+ ExportDirectiveState({
+ required this.directive,
+ });
+
+ /// If [exportedSource] corresponds to a library, returns it.
+ Source? get exportedLibrarySource => null;
+
+ /// Returns a [Source] that is referenced by this directive. If the are
+ /// configurations, selects the one which satisfies the conditions.
+ ///
+ /// Returns `null` if the selected URI is not valid, or cannot be resolved
+ /// into a [Source].
+ Source? get exportedSource => null;
+}
+
+/// [ExportDirectiveState] that has a valid URI that references a file.
+class ExportDirectiveWithFile extends ExportDirectiveState {
+ final FileState exportedFile;
+
+ ExportDirectiveWithFile({
+ required super.directive,
+ required this.exportedFile,
+ });
+
+ /// Returns [exportedFile] if it is a library.
+ LibraryFileStateKind? get exportedLibrary {
+ final kind = exportedFile.kind;
+ if (kind is LibraryFileStateKind) {
+ return kind;
+ }
+ return null;
+ }
+
+ @override
+ Source? get exportedLibrarySource {
+ if (exportedFile.kind is LibraryFileStateKind) {
+ return exportedSource;
+ }
+ return null;
+ }
+
+ @override
+ Source get exportedSource => exportedFile.source;
+}
+
+/// [ExportDirectiveState] with a URI that resolves to [InSummarySource].
+class ExportDirectiveWithInSummarySource extends ExportDirectiveState {
+ @override
+ final InSummarySource exportedSource;
+
+ ExportDirectiveWithInSummarySource({
+ required super.directive,
+ required this.exportedSource,
+ });
+
+ @override
+ Source? get exportedLibrarySource {
+ if (exportedSource.kind == InSummarySourceKind.library) {
+ return exportedSource;
+ } else {
+ return null;
+ }
+ }
+}
+
/// A library from [SummaryDataStore].
class ExternalLibrary {
final InSummarySource source;
@@ -587,6 +656,7 @@
return unit;
}
+ /// TODO(scheglov) move to _fsState?
String _selectRelativeUri(UnlinkedNamespaceDirective directive) {
for (var configuration in directive.configurations) {
var name = configuration.name;
@@ -805,6 +875,7 @@
imports.add(
UnlinkedNamespaceDirective(
configurations: [],
+ isSyntheticDartCoreImport: true,
uri: 'dart:core',
),
);
@@ -1193,6 +1264,77 @@
bool get isSrc => (_flags & _isSrc) != 0;
}
+/// Information about a single `import` directive.
+class ImportDirectiveState {
+ final UnlinkedNamespaceDirective directive;
+
+ ImportDirectiveState({
+ required this.directive,
+ });
+
+ /// If [importedSource] corresponds to a library, returns it.
+ Source? get importedLibrarySource => null;
+
+ /// Returns a [Source] that is referenced by this directive. If the are
+ /// configurations, selects the one which satisfies the conditions.
+ ///
+ /// Returns `null` if the selected URI is not valid, or cannot be resolved
+ /// into a [Source].
+ Source? get importedSource => null;
+
+ bool get isSyntheticDartCoreImport => directive.isSyntheticDartCoreImport;
+}
+
+/// [ImportDirectiveState] that has a valid URI that references a file.
+class ImportDirectiveWithFile extends ImportDirectiveState {
+ final FileState importedFile;
+
+ ImportDirectiveWithFile({
+ required super.directive,
+ required this.importedFile,
+ });
+
+ /// Returns [importedFile] if it is a library.
+ LibraryFileStateKind? get importedLibrary {
+ final kind = importedFile.kind;
+ if (kind is LibraryFileStateKind) {
+ return kind;
+ }
+ return null;
+ }
+
+ @override
+ Source? get importedLibrarySource {
+ if (importedFile.kind is LibraryFileStateKind) {
+ return importedSource;
+ }
+ return null;
+ }
+
+ @override
+ Source get importedSource => importedFile.source;
+}
+
+/// [ImportDirectiveState] with a URI that resolves to [InSummarySource].
+class ImportDirectiveWithInSummarySource extends ImportDirectiveState {
+ @override
+ final InSummarySource importedSource;
+
+ ImportDirectiveWithInSummarySource({
+ required super.directive,
+ required this.importedSource,
+ });
+
+ @override
+ Source? get importedLibrarySource {
+ if (importedSource.kind == InSummarySourceKind.library) {
+ return importedSource;
+ } else {
+ return null;
+ }
+ }
+}
+
class LibraryFileStateKind extends LibraryOrAugmentationFileKind {
/// The name of the library from the `library` directive.
/// Or `null` if no `library` directive.
@@ -1209,10 +1351,67 @@
}
abstract class LibraryOrAugmentationFileKind extends FileStateKind {
+ List<ExportDirectiveState>? _exports;
+ List<ImportDirectiveState>? _imports;
+
LibraryOrAugmentationFileKind({
required super.file,
});
+ List<ExportDirectiveState> get exports {
+ return _exports ??= file.unlinked2.exports.map((directive) {
+ final uriStr = file._selectRelativeUri(directive);
+ return file._fileForRelativeUri(uriStr).map(
+ (refFile) {
+ if (refFile != null) {
+ refFile.referencingFiles.add(file);
+ return ExportDirectiveWithFile(
+ directive: directive,
+ exportedFile: refFile,
+ );
+ } else {
+ return ExportDirectiveState(
+ directive: directive,
+ );
+ }
+ },
+ (externalLibrary) {
+ return ExportDirectiveWithInSummarySource(
+ directive: directive,
+ exportedSource: externalLibrary.source,
+ );
+ },
+ );
+ }).toList();
+ }
+
+ List<ImportDirectiveState> get imports {
+ return _imports ??= file.unlinked2.imports.map((directive) {
+ final uriStr = file._selectRelativeUri(directive);
+ return file._fileForRelativeUri(uriStr).map(
+ (refFile) {
+ if (refFile != null) {
+ refFile.referencingFiles.add(file);
+ return ImportDirectiveWithFile(
+ directive: directive,
+ importedFile: refFile,
+ );
+ } else {
+ return ImportDirectiveState(
+ directive: directive,
+ );
+ }
+ },
+ (externalLibrary) {
+ return ImportDirectiveWithInSummarySource(
+ directive: directive,
+ importedSource: externalLibrary.source,
+ );
+ },
+ );
+ }).toList();
+ }
+
bool hasAugmentation(AugmentationFileStateKind augmentation) {
return file.augmentationFiles.contains(augmentation.file);
}
diff --git a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
index 605461c..d9a7714 100644
--- a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
@@ -159,12 +159,15 @@
/// The configurations that control which library will actually be used.
final List<UnlinkedNamespaceDirectiveConfiguration> configurations;
+ final bool isSyntheticDartCoreImport;
+
/// The URI referenced by this directive, nad used by default when none
/// of the [configurations] matches.
final String uri;
UnlinkedNamespaceDirective({
required this.configurations,
+ this.isSyntheticDartCoreImport = false,
required this.uri,
});
@@ -174,6 +177,7 @@
() => UnlinkedNamespaceDirectiveConfiguration.read(reader),
),
uri: reader.readStringUtf8(),
+ isSyntheticDartCoreImport: reader.readBool(),
);
}
@@ -185,6 +189,7 @@
},
);
sink.writeStringUtf8(uri);
+ sink.writeBool(isSyntheticDartCoreImport);
}
}
diff --git a/pkg/analyzer/lib/src/dart/micro/library_graph.dart b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
index f560b03..4a51ff1 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
@@ -975,6 +975,7 @@
imports.add(
UnlinkedNamespaceDirective(
configurations: [],
+ isSyntheticDartCoreImport: true,
uri: 'dart:core',
),
);
diff --git a/pkg/analyzer/lib/src/summary/package_bundle_reader.dart b/pkg/analyzer/lib/src/summary/package_bundle_reader.dart
index 34780d5..8648d55 100644
--- a/pkg/analyzer/lib/src/summary/package_bundle_reader.dart
+++ b/pkg/analyzer/lib/src/summary/package_bundle_reader.dart
@@ -65,7 +65,13 @@
/// The summary file where this source was defined.
final String summaryPath;
- InSummarySource(super.uri, this.summaryPath);
+ final InSummarySourceKind kind;
+
+ InSummarySource({
+ required Uri uri,
+ required this.summaryPath,
+ required this.kind,
+ }) : super(uri);
@override
TimestampedData<String> get contents => TimestampedData<String>(0, '');
@@ -77,6 +83,8 @@
String toString() => uri.toString();
}
+enum InSummarySourceKind { library, part }
+
/// The [UriResolver] that knows about sources that are served from their
/// summaries.
class InSummaryUriResolver extends UriResolver {
@@ -92,7 +100,13 @@
String uriString = uri.toString();
String? summaryPath = _dataStore.uriToSummaryPath[uriString];
if (summaryPath != null) {
- return InSummarySource(uri, summaryPath);
+ final isLibrary = _dataStore._libraryUris.contains(uriString);
+ return InSummarySource(
+ uri: uri,
+ summaryPath: summaryPath,
+ kind:
+ isLibrary ? InSummarySourceKind.library : InSummarySourceKind.part,
+ );
}
return null;
}
diff --git a/pkg/analyzer/lib/src/workspace/simple.dart b/pkg/analyzer/lib/src/workspace/simple.dart
index dc9b694..6430e61 100644
--- a/pkg/analyzer/lib/src/workspace/simple.dart
+++ b/pkg/analyzer/lib/src/workspace/simple.dart
@@ -43,11 +43,10 @@
DartSdk? sdk,
SummaryDataStore? summaryData,
) {
- if (summaryData != null) {
- throw UnsupportedError(
- 'Summary files are not supported in a Pub workspace.');
- }
List<UriResolver> resolvers = <UriResolver>[];
+ if (summaryData != null) {
+ resolvers.add(InSummaryUriResolver(summaryData));
+ }
if (sdk != null) {
resolvers.add(DartUriResolver(sdk));
}
diff --git a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
index c0b0ac1..8dd3040 100644
--- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
@@ -7,6 +7,7 @@
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/sdk/build_sdk_summary.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/context/packages.dart';
import 'package:analyzer/src/dart/analysis/byte_store.dart';
@@ -21,12 +22,15 @@
show AnalysisOptions, AnalysisOptionsImpl;
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/source/package_map_resolver.dart';
+import 'package:analyzer/src/summary/package_bundle_reader.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:analyzer/src/util/either.dart';
import 'package:analyzer/src/workspace/basic.dart';
+import 'package:analyzer_utilities/check/check.dart';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
+import 'package:meta/meta.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -121,10 +125,31 @@
@reflectiveTest
class FileSystemState_PubPackageTest extends PubPackageResolutionTest {
+ FileState get _dartAsyncState {
+ return fileStateForUriStr('dart:async');
+ }
+
+ FileState get _dartCoreState {
+ return fileStateForUriStr('dart:core');
+ }
+
+ FileState get _dartMathState {
+ return fileStateForUriStr('dart:math');
+ }
+
FileState fileStateFor(File file) {
return fsStateFor(file).getFileForPath(file.path);
}
+ FileState fileStateForUri(Uri uri) {
+ return fsStateFor(testFile).getFileForUri(uri).t1!;
+ }
+
+ FileState fileStateForUriStr(String uriStr) {
+ final uri = Uri.parse(uriStr);
+ return fileStateForUri(uri);
+ }
+
FileSystemState fsStateFor(File file) {
return driverFor(file.path).fsState;
}
@@ -539,6 +564,487 @@
});
}
+ test_newFile_library_exports_dart() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+export 'dart:async';
+export 'dart:math';
+''');
+
+ final aState = fileStateFor(a);
+ aState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.exports).matches([
+ (export) => export.isLibrary(_dartAsyncState),
+ (export) => export.isLibrary(_dartMathState),
+ ]);
+ });
+ }
+
+ test_newFile_library_exports_inSummary_library() async {
+ // Prepare a bundle where `package:foo/foo.dart` is a library.
+ final librarySummaryFiles = <File>[];
+ {
+ final fooRoot = getFolder('$workspaceRootPath/foo');
+
+ newFile('${fooRoot.path}/lib/foo.dart', 'class F {}');
+
+ final fooPackageConfigFile = getFile(
+ '${fooRoot.path}/.dart_tool/package_config.json',
+ );
+
+ writePackageConfig(
+ fooPackageConfigFile.path,
+ PackageConfigFileBuilder()..add(name: 'foo', rootPath: fooRoot.path),
+ );
+
+ final analysisDriver = driverFor(fooRoot.path);
+ final bundleBytes = await analysisDriver.buildPackageBundle(
+ uriList: [
+ Uri.parse('package:foo/foo.dart'),
+ ],
+ );
+
+ final bundleFile =
+ resourceProvider.getFile('/home/summaries/packages.sum');
+ bundleFile.writeAsBytesSync(bundleBytes);
+
+ librarySummaryFiles.add(bundleFile);
+
+ // Delete, so it is not available as a file.
+ // We don't have a package config for it anyway, but just to be sure.
+ fooRoot.delete();
+ }
+
+ // Prepare for recreating the collection, with summaries.
+ sdkSummaryFile = await _writeSdkSummary();
+ this.librarySummaryFiles = librarySummaryFiles;
+
+ disposeAnalysisContextCollection();
+
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+export 'dart:async';
+export 'package:foo/foo.dart';
+export 'b.dart';
+''');
+
+ final b = getFile('$testPackageLibPath/b.dart');
+
+ final aState = fileStateFor(a);
+ final bState = fileStateFor(b);
+
+ aState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.exports).matches([
+ (import) {
+ final expected = Uri.parse('dart:async');
+ import.withInSummaryLibrary(expected);
+ },
+ (import) {
+ final expected = Uri.parse('package:foo/foo.dart');
+ import.withInSummaryLibrary(expected);
+ },
+ (import) => import.isLibrary(bState),
+ ]);
+ });
+ }
+
+ test_newFile_library_exports_inSummary_part() async {
+ // Prepare a bundle where `package:foo/foo2.dart` is a part.
+ final librarySummaryFiles = <File>[];
+ {
+ final fooRoot = getFolder('$workspaceRootPath/foo');
+
+ newFile('${fooRoot.path}/lib/foo.dart', r'''
+part 'foo2.dart';
+''');
+
+ newFile('${fooRoot.path}/lib/foo2.dart', r'''
+part of 'foo.dart';
+''');
+
+ final fooPackageConfigFile = getFile(
+ '${fooRoot.path}/.dart_tool/package_config.json',
+ );
+
+ writePackageConfig(
+ fooPackageConfigFile.path,
+ PackageConfigFileBuilder()..add(name: 'foo', rootPath: fooRoot.path),
+ );
+
+ final analysisDriver = driverFor(fooRoot.path);
+ final bundleBytes = await analysisDriver.buildPackageBundle(
+ uriList: [
+ Uri.parse('package:foo/foo.dart'),
+ ],
+ );
+
+ final bundleFile =
+ resourceProvider.getFile('/home/summaries/packages.sum');
+ bundleFile.writeAsBytesSync(bundleBytes);
+
+ librarySummaryFiles.add(bundleFile);
+
+ // Delete, so it is not available as a file.
+ // We don't have a package config for it anyway, but just to be sure.
+ fooRoot.delete();
+ }
+
+ // Prepare for recreating the collection, with summaries.
+ sdkSummaryFile = await _writeSdkSummary();
+ this.librarySummaryFiles = librarySummaryFiles;
+
+ disposeAnalysisContextCollection();
+
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+export 'package:foo/foo2.dart';
+export 'b.dart';
+''');
+
+ final b = getFile('$testPackageLibPath/b.dart');
+
+ final aState = fileStateFor(a);
+ final bState = fileStateFor(b);
+
+ aState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.exports).matches([
+ (export) {
+ final expected = Uri.parse('package:foo/foo2.dart');
+ export.withInSummaryNotLibrary(expected);
+ },
+ (export) => export.isLibrary(bState),
+ ]);
+ });
+ }
+
+ test_newFile_library_exports_invalidRelativeUri() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+export '::net';
+''');
+
+ final aState = fileStateFor(a);
+ aState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.exports).matches([
+ (export) => export.isNotFile(),
+ ]);
+ });
+ }
+
+ test_newFile_library_exports_package() async {
+ final a = newFile('$testPackageLibPath/a.dart', '');
+ final b = newFile('$testPackageLibPath/b.dart', '');
+
+ final c = newFile('$testPackageLibPath/c.dart', r'''
+export 'a.dart';
+export 'package:test/b.dart';
+''');
+
+ final aState = fileStateFor(a);
+ final bState = fileStateFor(b);
+ final cState = fileStateFor(c);
+
+ cState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.exports).matches([
+ (export) => export.isLibrary(aState),
+ (export) => export.isLibrary(bState),
+ ]);
+ });
+ }
+
+ test_newFile_library_exports_part() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+part of my.lib;
+''');
+
+ final b = newFile('$testPackageLibPath/b.dart', r'''
+export 'a.dart';
+''');
+
+ final aState = fileStateFor(a);
+ final bState = fileStateFor(b);
+
+ bState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.exports).matches([
+ (export) => export.isFile(aState),
+ ]);
+ });
+ }
+
+ test_newFile_library_imports_library_augmentation() async {
+ final b = newFile('$testPackageLibPath/b.dart', r'''
+library augment 'a.dart';
+''');
+
+ final c = newFile('$testPackageLibPath/c.dart', r'''
+import 'b.dart';
+''');
+
+ final bState = fileStateFor(b);
+ final cState = fileStateFor(c);
+
+ cState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.imports).matches([
+ (import) => import.isFile(bState),
+ (import) => import
+ ..isLibrary(_dartCoreState)
+ ..isSyntheticDartCoreImport.isTrue,
+ ]);
+ });
+ }
+
+ test_newFile_library_imports_library_dart() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+import 'dart:async';
+import 'dart:math';
+''');
+
+ final aState = fileStateFor(a);
+ aState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.imports).matches([
+ (import) => import.isLibrary(_dartAsyncState),
+ (import) => import.isLibrary(_dartMathState),
+ (import) => import
+ ..isLibrary(_dartCoreState)
+ ..isSyntheticDartCoreImport.isTrue,
+ ]);
+ });
+ }
+
+ test_newFile_library_imports_library_dart_explicitDartCore() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+import 'dart:core';
+import 'dart:math';
+''');
+
+ final aState = fileStateFor(a);
+ aState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.imports).matches([
+ (import) => import
+ ..isLibrary(_dartCoreState)
+ ..isSyntheticDartCoreImport.isFalse,
+ (import) => import.isLibrary(_dartMathState),
+ ]);
+ });
+ }
+
+ test_newFile_library_imports_library_inSummary_library() async {
+ // Prepare a bundle where `package:foo/foo.dart` is a library.
+ final librarySummaryFiles = <File>[];
+ {
+ final fooRoot = getFolder('$workspaceRootPath/foo');
+
+ newFile('${fooRoot.path}/lib/foo.dart', 'class F {}');
+
+ final fooPackageConfigFile = getFile(
+ '${fooRoot.path}/.dart_tool/package_config.json',
+ );
+
+ writePackageConfig(
+ fooPackageConfigFile.path,
+ PackageConfigFileBuilder()..add(name: 'foo', rootPath: fooRoot.path),
+ );
+
+ final analysisDriver = driverFor(fooRoot.path);
+ final bundleBytes = await analysisDriver.buildPackageBundle(
+ uriList: [
+ Uri.parse('package:foo/foo.dart'),
+ ],
+ );
+
+ final bundleFile =
+ resourceProvider.getFile('/home/summaries/packages.sum');
+ bundleFile.writeAsBytesSync(bundleBytes);
+
+ librarySummaryFiles.add(bundleFile);
+
+ // Delete, so it is not available as a file.
+ // We don't have a package config for it anyway, but just to be sure.
+ fooRoot.delete();
+ }
+
+ // Prepare for recreating the collection, with summaries.
+ sdkSummaryFile = await _writeSdkSummary();
+ this.librarySummaryFiles = librarySummaryFiles;
+
+ disposeAnalysisContextCollection();
+
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+import 'dart:async';
+import 'package:foo/foo.dart';
+import 'b.dart';
+''');
+
+ final b = getFile('$testPackageLibPath/b.dart');
+
+ final aState = fileStateFor(a);
+ final bState = fileStateFor(b);
+
+ aState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.imports).matches([
+ (import) {
+ final expected = Uri.parse('dart:async');
+ import.withInSummaryLibrary(expected);
+ },
+ (import) {
+ final expected = Uri.parse('package:foo/foo.dart');
+ import.withInSummaryLibrary(expected);
+ },
+ (import) => import.isLibrary(bState),
+ (import) {
+ final expected = Uri.parse('dart:core');
+ import
+ ..withInSummaryLibrary(expected)
+ ..isSyntheticDartCoreImport.isTrue;
+ },
+ ]);
+ });
+ }
+
+ test_newFile_library_imports_library_inSummary_part() async {
+ // Prepare a bundle where `package:foo/foo2.dart` is a part.
+ final librarySummaryFiles = <File>[];
+ {
+ final fooRoot = getFolder('$workspaceRootPath/foo');
+
+ newFile('${fooRoot.path}/lib/foo.dart', r'''
+part 'foo2.dart';
+''');
+
+ newFile('${fooRoot.path}/lib/foo2.dart', r'''
+part of 'foo.dart';
+''');
+
+ final fooPackageConfigFile = getFile(
+ '${fooRoot.path}/.dart_tool/package_config.json',
+ );
+
+ writePackageConfig(
+ fooPackageConfigFile.path,
+ PackageConfigFileBuilder()..add(name: 'foo', rootPath: fooRoot.path),
+ );
+
+ final analysisDriver = driverFor(fooRoot.path);
+ final bundleBytes = await analysisDriver.buildPackageBundle(
+ uriList: [
+ Uri.parse('package:foo/foo.dart'),
+ ],
+ );
+
+ final bundleFile =
+ resourceProvider.getFile('/home/summaries/packages.sum');
+ bundleFile.writeAsBytesSync(bundleBytes);
+
+ librarySummaryFiles.add(bundleFile);
+
+ // Delete, so it is not available as a file.
+ // We don't have a package config for it anyway, but just to be sure.
+ fooRoot.delete();
+ }
+
+ // Prepare for recreating the collection, with summaries.
+ sdkSummaryFile = await _writeSdkSummary();
+ this.librarySummaryFiles = librarySummaryFiles;
+
+ disposeAnalysisContextCollection();
+
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+import 'package:foo/foo2.dart';
+import 'b.dart';
+''');
+
+ final b = getFile('$testPackageLibPath/b.dart');
+
+ final aState = fileStateFor(a);
+ final bState = fileStateFor(b);
+
+ aState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.imports).matches([
+ (export) {
+ final expected = Uri.parse('package:foo/foo2.dart');
+ export.withInSummaryNotLibrary(expected);
+ },
+ (export) => export.isLibrary(bState),
+ (export) {
+ final expected = Uri.parse('dart:core');
+ export
+ ..withInSummaryLibrary(expected)
+ ..isSyntheticDartCoreImport.isTrue;
+ },
+ ]);
+ });
+ }
+
+ test_newFile_library_imports_library_invalidRelativeUri() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+import '::net';
+''');
+
+ final aState = fileStateFor(a);
+ aState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.imports).matches([
+ (import) => import.isNotFile(),
+ (import) => import.isLibrary(_dartCoreState),
+ ]);
+ });
+ }
+
+ test_newFile_library_imports_library_package() async {
+ final a = newFile('$testPackageLibPath/a.dart', '');
+ final b = newFile('$testPackageLibPath/b.dart', '');
+
+ final c = newFile('$testPackageLibPath/c.dart', r'''
+import 'a.dart';
+import 'package:test/b.dart';
+''');
+
+ final aState = fileStateFor(a);
+ final bState = fileStateFor(b);
+ final cState = fileStateFor(c);
+
+ cState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.imports).matches([
+ (import) => import.isLibrary(aState),
+ (import) => import.isLibrary(bState),
+ (import) => import
+ ..isLibrary(_dartCoreState)
+ ..isSyntheticDartCoreImport.isTrue,
+ ]);
+ });
+ }
+
+ test_newFile_library_imports_library_part() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+part of my.lib;
+''');
+
+ final b = newFile('$testPackageLibPath/b.dart', r'''
+import 'a.dart';
+''');
+
+ final aState = fileStateFor(a);
+ final bState = fileStateFor(b);
+
+ bState.assertKind((kind) {
+ kind as LibraryFileStateKind;
+ check(kind.imports).matches([
+ (import) => import.isFile(aState),
+ (import) => import
+ ..isLibrary(_dartCoreState)
+ ..isSyntheticDartCoreImport.isTrue,
+ ]);
+ });
+ }
+
test_newFile_library_includePart_withoutPartOf() async {
final a = newFile('$testPackageLibPath/a.dart', r'''
part 'b.dart';
@@ -1609,6 +2115,16 @@
}).toList();
expect(actualFiles, expected);
}
+
+ Future<File> _writeSdkSummary() async {
+ final file = resourceProvider.getFile('/home/summaries/sdk.sum');
+ final bytes = await buildSdkSummary2(
+ resourceProvider: resourceProvider,
+ sdkPath: sdkRoot.path,
+ );
+ file.writeAsBytesSync(bytes);
+ return file;
+ }
}
@reflectiveTest
@@ -2466,6 +2982,188 @@
}
}
+extension on CheckTarget<ImportDirectiveState> {
+ @useResult
+ CheckTarget<Source?> get importedLibrarySource {
+ return nest(
+ value.importedLibrarySource,
+ (selected) => 'importedLibrarySource ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
+ CheckTarget<bool> get isSyntheticDartCoreImport {
+ return nest(
+ value.isSyntheticDartCoreImport,
+ (selected) => 'isSyntheticDartCoreImport ${valueStr(selected)}',
+ );
+ }
+
+ /// Is [ImportDirectiveWithFile], but not a library.
+ void isFile(FileState expected) {
+ this.isA<ImportDirectiveWithFile>()
+ ..importedFile.isIdenticalTo(expected)
+ ..importedSource.uri.isEqualTo(expected.uri)
+ ..importedLibrary.isNull
+ ..importedLibrarySource.isNull;
+ }
+
+ /// Is [ImportDirectiveWithFile], and is a library.
+ void isLibrary(FileState expected) {
+ final expectedKind = expected.kind as LibraryFileStateKind;
+ this.isA<ImportDirectiveWithFile>()
+ ..importedFile.isIdenticalTo(expected)
+ ..importedSource.uri.isEqualTo(expected.uri)
+ ..importedLibrary.isNotNull.isIdenticalTo(expectedKind)
+ ..importedLibrarySource.isNotNull.uri.isEqualTo(expected.uri);
+ }
+
+ /// Exactly [ImportDirectiveState], even the file is not known.
+ void isNotFile() {
+ hasExactType<ImportDirectiveState>();
+ }
+
+ void withInSummaryLibrary(Uri expected) {
+ this.isA<ImportDirectiveWithInSummarySource>()
+ ..importedSource.uri.isEqualTo(expected)
+ ..importedLibrarySource.isNotNull.uri.isEqualTo(expected);
+ }
+
+ void withInSummaryNotLibrary(Uri expected) {
+ this.isA<ImportDirectiveWithInSummarySource>()
+ ..importedSource.uri.isEqualTo(expected)
+ ..importedLibrarySource.isNull;
+ }
+}
+
+extension on CheckTarget<ExportDirectiveState> {
+ @useResult
+ CheckTarget<Source?> get exportedLibrarySource {
+ return nest(
+ value.exportedLibrarySource,
+ (selected) => 'exportedLibrarySource ${valueStr(selected)}',
+ );
+ }
+
+ /// Is [ExportDirectiveWithFile], but not a library.
+ void isFile(FileState expected) {
+ this.isA<ExportDirectiveWithFile>()
+ ..exportedFile.isIdenticalTo(expected)
+ ..exportedSource.uri.isEqualTo(expected.uri)
+ ..exportedLibrary.isNull
+ ..exportedLibrarySource.isNull;
+ }
+
+ /// Is [ExportDirectiveWithFile], and is a library.
+ void isLibrary(FileState expected) {
+ final expectedKind = expected.kind as LibraryFileStateKind;
+ this.isA<ExportDirectiveWithFile>()
+ ..exportedFile.isIdenticalTo(expected)
+ ..exportedSource.uri.isEqualTo(expected.uri)
+ ..exportedLibrary.isIdenticalTo(expectedKind)
+ ..exportedLibrarySource.isNotNull.uri.isEqualTo(expected.uri);
+ }
+
+ /// Exactly [ExportDirectiveState], even the file is not known.
+ void isNotFile() {
+ hasExactType<ExportDirectiveState>();
+ }
+
+ void withInSummaryLibrary(Uri expected) {
+ this.isA<ExportDirectiveWithInSummarySource>()
+ ..exportedSource.uri.isEqualTo(expected)
+ ..exportedLibrarySource.isNotNull.uri.isEqualTo(expected);
+ }
+
+ void withInSummaryNotLibrary(Uri expected) {
+ this.isA<ExportDirectiveWithInSummarySource>()
+ ..exportedSource.uri.isEqualTo(expected)
+ ..exportedLibrarySource.isNull;
+ }
+}
+
+extension on CheckTarget<ImportDirectiveWithFile> {
+ @useResult
+ CheckTarget<FileState> get importedFile {
+ return nest(
+ value.importedFile,
+ (selected) => 'importedFile ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
+ CheckTarget<LibraryFileStateKind?> get importedLibrary {
+ return nest(
+ value.importedLibrary,
+ (selected) => 'importedLibrary ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
+ CheckTarget<Source> get importedSource {
+ return nest(
+ value.importedSource,
+ (selected) => 'importedSource ${valueStr(selected)}',
+ );
+ }
+}
+
+extension on CheckTarget<ExportDirectiveWithFile> {
+ @useResult
+ CheckTarget<FileState> get exportedFile {
+ return nest(
+ value.exportedFile,
+ (selected) => 'exportedFile ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
+ CheckTarget<LibraryFileStateKind?> get exportedLibrary {
+ return nest(
+ value.exportedLibrary,
+ (selected) => 'exportedLibrary ${valueStr(selected)}',
+ );
+ }
+
+ @useResult
+ CheckTarget<Source> get exportedSource {
+ return nest(
+ value.exportedSource,
+ (selected) => 'exportedSource ${valueStr(selected)}',
+ );
+ }
+}
+
+extension on CheckTarget<ImportDirectiveWithInSummarySource> {
+ @useResult
+ CheckTarget<InSummarySource> get importedSource {
+ return nest(
+ value.importedSource,
+ (selected) => 'importedSource ${valueStr(selected)}',
+ );
+ }
+}
+
+extension on CheckTarget<ExportDirectiveWithInSummarySource> {
+ @useResult
+ CheckTarget<InSummarySource> get exportedSource {
+ return nest(
+ value.exportedSource,
+ (selected) => 'exportedSource ${valueStr(selected)}',
+ );
+ }
+}
+
+extension on CheckTarget<Source> {
+ @useResult
+ CheckTarget<Uri> get uri {
+ return nest(
+ value.uri,
+ (selected) => 'uri ${valueStr(selected)}',
+ );
+ }
+}
+
extension _Either2Extension<T1, T2> on Either2<T1, T2> {
T1 get t1 {
late T1 result;
diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
index 8773df4..c2aa845 100644
--- a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
@@ -129,6 +129,12 @@
/// to this path, instead of the given path.
String? pathForContextSelection;
+ /// Optional Dart SDK summary file, to be used instead of [sdkRoot].
+ File? sdkSummaryFile;
+
+ /// Optional summaries to provide for the collection.
+ List<File>? librarySummaryFiles;
+
List<MockSdkLibrary> get additionalMockSdkLibraries => [];
List<String> get collectionIncludedPaths;
@@ -259,6 +265,8 @@
resourceProvider: resourceProvider,
retainDataForTesting: retainDataForTesting,
sdkPath: sdkRoot.path,
+ sdkSummaryPath: sdkSummaryFile?.path,
+ librarySummaryPaths: librarySummaryFiles?.map((e) => e.path).toList(),
updateAnalysisOptions: updateAnalysisOptions,
);
diff --git a/pkg/analyzer/test/src/summary/in_summary_source_test.dart b/pkg/analyzer/test/src/summary/in_summary_source_test.dart
deleted file mode 100644
index 0587667..0000000
--- a/pkg/analyzer/test/src/summary/in_summary_source_test.dart
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
-// 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:analyzer/src/generated/source.dart';
-import 'package:analyzer/src/summary/package_bundle_reader.dart';
-import 'package:test/test.dart';
-import 'package:test_reflective_loader/test_reflective_loader.dart';
-
-main() {
- defineReflectiveSuite(() {
- defineReflectiveTests(InSummarySourceTest);
- });
-}
-
-@reflectiveTest
-class InSummarySourceTest {
- test_InSummarySource() {
- var sourceFactory = SourceFactory([
- InSummaryUriResolver(
- MockSummaryDataStore.fake({
- 'package:foo/foo.dart': 'foo.sum',
- 'package:foo/src/foo_impl.dart': 'foo.sum',
- 'package:bar/baz.dart': 'bar.sum',
- }),
- )
- ]);
-
- var source =
- sourceFactory.forUri('package:foo/foo.dart') as InSummarySource;
- expect(source, isNotNull);
- expect(source.summaryPath, 'foo.sum');
-
- source = sourceFactory.forUri('package:foo/src/foo_impl.dart')
- as InSummarySource;
- expect(source, isNotNull);
- expect(source.summaryPath, 'foo.sum');
-
- source = sourceFactory.forUri('package:bar/baz.dart') as InSummarySource;
- expect(source, isNotNull);
- expect(source.summaryPath, 'bar.sum');
- }
-}
-
-class MockSummaryDataStore implements SummaryDataStore {
- @override
- final Map<String, String> uriToSummaryPath;
-
- MockSummaryDataStore(this.uriToSummaryPath);
-
- factory MockSummaryDataStore.fake(Map<String, String> uriToSummary) {
- return MockSummaryDataStore(uriToSummary);
- }
-
- @override
- noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
diff --git a/pkg/analyzer/test/src/summary/test_all.dart b/pkg/analyzer/test/src/summary/test_all.dart
index 41786a1..9c87739 100644
--- a/pkg/analyzer/test/src/summary/test_all.dart
+++ b/pkg/analyzer/test/src/summary/test_all.dart
@@ -7,7 +7,6 @@
import 'api_signature_test.dart' as api_signature;
import 'elements_test.dart' as elements;
import 'flat_buffers_test.dart' as flat_buffers;
-import 'in_summary_source_test.dart' as in_summary_source;
import 'macro_test.dart' as macro;
import 'top_level_inference_test.dart' as top_level_inference;
@@ -16,7 +15,6 @@
api_signature.main();
elements.main();
flat_buffers.main();
- in_summary_source.main();
macro.main();
top_level_inference.main();
}, name: 'summary');
diff --git a/pkg/analyzer/test/src/workspace/bazel_test.dart b/pkg/analyzer/test/src/workspace/bazel_test.dart
index a79b85e..784c9b8 100644
--- a/pkg/analyzer/test/src/workspace/bazel_test.dart
+++ b/pkg/analyzer/test/src/workspace/bazel_test.dart
@@ -901,7 +901,11 @@
Source _inSummarySource(String uriStr) {
var uri = Uri.parse(uriStr);
- return InSummarySource(uri, '');
+ return InSummarySource(
+ uri: uri,
+ summaryPath: '',
+ kind: InSummarySourceKind.library,
+ );
}
void _setUpPackage() {
diff --git a/pkg/analyzer_utilities/lib/check/equality.dart b/pkg/analyzer_utilities/lib/check/equality.dart
index 2d265b8..9701bb3 100644
--- a/pkg/analyzer_utilities/lib/check/equality.dart
+++ b/pkg/analyzer_utilities/lib/check/equality.dart
@@ -11,6 +11,12 @@
}
}
+ void isIdenticalTo(Object? other) {
+ if (!identical(value, other)) {
+ fail('is not identical to $other');
+ }
+ }
+
void isNotEqualTo(Object? other) {
if (value == other) {
fail('is equal to $other');
diff --git a/pkg/analyzer_utilities/lib/check/type.dart b/pkg/analyzer_utilities/lib/check/type.dart
index ea28b9e..c261e2a 100644
--- a/pkg/analyzer_utilities/lib/check/type.dart
+++ b/pkg/analyzer_utilities/lib/check/type.dart
@@ -5,6 +5,15 @@
import 'package:analyzer_utilities/check/check.dart';
extension IsExtension<T> on CheckTarget<T> {
+ CheckTarget<U> hasExactType<U extends T>() {
+ final value = this.value;
+ if (value.runtimeType == U) {
+ return nest(value as U, (_) => 'is of type $U');
+ } else {
+ fail('is not of type $U');
+ }
+ }
+
CheckTarget<U> isA<U extends T>() {
final value = this.value;
if (value is U) {
diff --git a/pkg/analyzer_utilities/test/check/check_test.dart b/pkg/analyzer_utilities/test/check/check_test.dart
index 9e5ee83..0f9bda1 100644
--- a/pkg/analyzer_utilities/test/check/check_test.dart
+++ b/pkg/analyzer_utilities/test/check/check_test.dart
@@ -29,6 +29,14 @@
_fails(() => check(false).isTrue);
});
});
+ group('equality', () {
+ test('isIdenticalTo', () {
+ final a = Object();
+ final b = Object();
+ check(a).isIdenticalTo(a);
+ _fails(() => check(a).isIdenticalTo(b));
+ });
+ });
group('int', () {
test('isEqualTo', () {
check(0).isEqualTo(0);
@@ -332,6 +340,10 @@
});
});
group('type', () {
+ test('hasExactType', () {
+ check(42).hasExactType<int>();
+ _fails(() => check(42 as dynamic).hasExactType<num>());
+ });
test('isA', () {
check(0).isA<int>();
_fails(() => check('abc' as dynamic).isA<int>());