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>
This commit is contained in:
committed by
Commit Bot
parent
1e7bc687a7
commit
8ded7af5f5
@@ -86,7 +86,7 @@ import 'package:meta/meta.dart';
|
||||
/// 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.
|
||||
|
||||
@@ -85,6 +85,75 @@ class AugmentationUnknownFileStateKind extends AugmentationFileStateKind {
|
||||
});
|
||||
}
|
||||
|
||||
/// 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 @@ class FileState {
|
||||
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 @@ class FileState {
|
||||
imports.add(
|
||||
UnlinkedNamespaceDirective(
|
||||
configurations: [],
|
||||
isSyntheticDartCoreImport: true,
|
||||
uri: 'dart:core',
|
||||
),
|
||||
);
|
||||
@@ -1193,6 +1264,77 @@ class FileUriProperties {
|
||||
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 @@ class LibraryFileStateKind extends LibraryOrAugmentationFileKind {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -159,12 +159,15 @@ class UnlinkedNamespaceDirective {
|
||||
/// 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 @@ class UnlinkedNamespaceDirective {
|
||||
() => UnlinkedNamespaceDirectiveConfiguration.read(reader),
|
||||
),
|
||||
uri: reader.readStringUtf8(),
|
||||
isSyntheticDartCoreImport: reader.readBool(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -185,6 +189,7 @@ class UnlinkedNamespaceDirective {
|
||||
},
|
||||
);
|
||||
sink.writeStringUtf8(uri);
|
||||
sink.writeBool(isSyntheticDartCoreImport);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -975,6 +975,7 @@ class _FileStateUnlinked {
|
||||
imports.add(
|
||||
UnlinkedNamespaceDirective(
|
||||
configurations: [],
|
||||
isSyntheticDartCoreImport: true,
|
||||
uri: 'dart:core',
|
||||
),
|
||||
);
|
||||
|
||||
@@ -65,7 +65,13 @@ class InSummarySource extends BasicSource {
|
||||
/// 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 @@ class InSummarySource extends BasicSource {
|
||||
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 @@ class InSummaryUriResolver extends UriResolver {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -43,11 +43,10 @@ abstract class SimpleWorkspace extends Workspace {
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'dart:typed_data';
|
||||
|
||||
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 @@ import 'package:analyzer/src/generated/engine.dart'
|
||||
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 @@ class FileSystemState_BazelWorkspaceTest extends BazelWorkspaceResolutionTest {
|
||||
|
||||
@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 @@ import augment 'c.dart';
|
||||
});
|
||||
}
|
||||
|
||||
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 @@ class A2 {}
|
||||
}).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 PartOfNameFileStateKind {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -129,6 +129,12 @@ abstract class ContextResolutionTest
|
||||
/// 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 @@ abstract class ContextResolutionTest
|
||||
resourceProvider: resourceProvider,
|
||||
retainDataForTesting: retainDataForTesting,
|
||||
sdkPath: sdkRoot.path,
|
||||
sdkSummaryPath: sdkSummaryFile?.path,
|
||||
librarySummaryPaths: librarySummaryFiles?.map((e) => e.path).toList(),
|
||||
updateAnalysisOptions: updateAnalysisOptions,
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
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 @@ main() {
|
||||
api_signature.main();
|
||||
elements.main();
|
||||
flat_buffers.main();
|
||||
in_summary_source.main();
|
||||
macro.main();
|
||||
top_level_inference.main();
|
||||
}, name: 'summary');
|
||||
|
||||
@@ -901,7 +901,11 @@ class BazelWorkspacePackageTest with ResourceProviderMixin {
|
||||
|
||||
Source _inSummarySource(String uriStr) {
|
||||
var uri = Uri.parse(uriStr);
|
||||
return InSummarySource(uri, '');
|
||||
return InSummarySource(
|
||||
uri: uri,
|
||||
summaryPath: '',
|
||||
kind: InSummarySourceKind.library,
|
||||
);
|
||||
}
|
||||
|
||||
void _setUpPackage() {
|
||||
|
||||
@@ -11,6 +11,12 @@ extension EqualityExtension<T> on CheckTarget<T> {
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -29,6 +29,14 @@ void main() {
|
||||
_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 @@ void main() {
|
||||
});
|
||||
});
|
||||
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>());
|
||||
|
||||
Reference in New Issue
Block a user