Use ImportDirectiveState and others in LibraryAnalyzer.

Change-Id: I9cdeedcb8f3e9b35814bb457547bb52ac85f7f4c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/250263
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-07-02 03:06:30 +00:00 committed by Commit Bot
parent ffb598f3b5
commit 1ed8748dcd
9 changed files with 647 additions and 499 deletions

View file

@ -1330,7 +1330,6 @@ class AnalysisDriver implements AnalysisDriverGeneric {
var results = LibraryAnalyzer(
analysisOptions as AnalysisOptionsImpl,
declaredVariables,
sourceFactory,
libraryContext.elementFactory.libraryOfUri2(library.file.uri),
libraryContext.elementFactory.analysisSession.inheritanceManager,
library,
@ -1397,7 +1396,6 @@ class AnalysisDriver implements AnalysisDriverGeneric {
var unitResults = LibraryAnalyzer(
analysisOptions as AnalysisOptionsImpl,
declaredVariables,
sourceFactory,
libraryContext.elementFactory.libraryOfUri2(library.file.uri),
libraryContext.elementFactory.analysisSession.inheritanceManager,
library,
@ -1771,7 +1769,6 @@ class AnalysisDriver implements AnalysisDriverGeneric {
var analysisResult = LibraryAnalyzer(
analysisOptions as AnalysisOptionsImpl,
declaredVariables,
sourceFactory,
libraryContext.elementFactory.libraryOfUri2(library.file.uri),
libraryContext.elementFactory.analysisSession.inheritanceManager,
library,

View file

@ -131,12 +131,82 @@ class DirectiveState {
void dispose() {}
}
/// Information about a single `import` directive.
class ExportDirectiveState extends DirectiveState {
/// Meaning of a URI referenced in a directive.
class DirectiveUri {
Source? get source => null;
}
/// [DirectiveUriWithUri] with URI that resolves to a [FileState].
class DirectiveUriWithFile extends DirectiveUriWithUri {
final FileState file;
DirectiveUriWithFile({
required super.relativeUriStr,
required super.relativeUri,
required this.file,
});
@override
Source? get source => file.source;
@override
String toString() => '$file';
}
/// [DirectiveUriWithUri] with URI that resolves to a [InSummarySource].
class DirectiveUriWithInSummarySource extends DirectiveUriWithUri {
@override
final InSummarySource source;
DirectiveUriWithInSummarySource({
required super.relativeUriStr,
required super.relativeUri,
required this.source,
});
@override
String toString() => '$source';
}
/// [DirectiveUri] for which we can get its relative URI string.
class DirectiveUriWithString extends DirectiveUri {
final String relativeUriStr;
DirectiveUriWithString({
required this.relativeUriStr,
});
@override
String toString() => relativeUriStr;
}
/// [DirectiveUriWithString] that can be parsed into a relative URI.
class DirectiveUriWithUri extends DirectiveUriWithString {
final Uri relativeUri;
DirectiveUriWithUri({
required super.relativeUriStr,
required this.relativeUri,
});
bool get isValid {
return relativeUri.path.isNotEmpty;
}
@override
String toString() => '$relativeUri';
}
/// Information about a single `export` directive.
class ExportDirectiveState<U extends DirectiveUri> extends DirectiveState {
final UnlinkedNamespaceDirective directive;
final U selectedUri;
final NamespaceDirectiveUris uris;
ExportDirectiveState({
required this.directive,
required this.selectedUri,
required this.uris,
});
/// If [exportedSource] corresponds to a library, returns it.
@ -151,19 +221,21 @@ class ExportDirectiveState extends DirectiveState {
}
/// [ExportDirectiveWithUri] that has a valid URI that references a file.
class ExportDirectiveWithFile extends ExportDirectiveWithUri {
class ExportDirectiveWithFile
extends ExportDirectiveWithUri<DirectiveUriWithFile> {
final LibraryOrAugmentationFileKind container;
final FileState exportedFile;
ExportDirectiveWithFile({
required this.container,
required super.directive,
required this.exportedFile,
required super.selectedUriStr,
required super.selectedUri,
required super.uris,
}) {
exportedFile.referencingFiles.add(container.file);
}
FileState get exportedFile => selectedUri.file;
/// Returns [exportedFile] if it is a library.
LibraryFileStateKind? get exportedLibrary {
final kind = exportedFile.kind;
@ -191,14 +263,12 @@ class ExportDirectiveWithFile extends ExportDirectiveWithUri {
}
/// [ExportDirectiveWithUri] with a URI that resolves to [InSummarySource].
class ExportDirectiveWithInSummarySource extends ExportDirectiveWithUri {
@override
final InSummarySource exportedSource;
class ExportDirectiveWithInSummarySource
extends ExportDirectiveWithUri<DirectiveUriWithInSummarySource> {
ExportDirectiveWithInSummarySource({
required super.directive,
required this.exportedSource,
required super.selectedUriStr,
required super.selectedUri,
required super.uris,
});
@override
@ -209,15 +279,18 @@ class ExportDirectiveWithInSummarySource extends ExportDirectiveWithUri {
return null;
}
}
@override
InSummarySource get exportedSource => selectedUri.source;
}
/// [ExportDirectiveState] that has a valid URI string.
class ExportDirectiveWithUri extends ExportDirectiveState {
final String selectedUriStr;
/// [ExportDirectiveState] that has a valid URI.
class ExportDirectiveWithUri<U extends DirectiveUriWithUri>
extends ExportDirectiveState<U> {
ExportDirectiveWithUri({
required super.directive,
required this.selectedUriStr,
required super.selectedUri,
required super.uris,
});
}
@ -593,6 +666,70 @@ class FileState {
return '$uri = $path';
}
DirectiveUri _buildDirectiveUri(String? relativeUriStr) {
if (relativeUriStr == null) {
return DirectiveUri();
}
final relativeUri = Uri.tryParse(relativeUriStr);
if (relativeUri == null) {
return DirectiveUriWithString(
relativeUriStr: relativeUriStr,
);
}
final absoluteUri = resolveRelativeUri(uri, relativeUri);
return _fsState.getFileForUri(absoluteUri).map(
(file) {
if (file != null) {
return DirectiveUriWithFile(
relativeUriStr: relativeUriStr,
relativeUri: relativeUri,
file: file,
);
} else {
return DirectiveUriWithUri(
relativeUriStr: relativeUriStr,
relativeUri: relativeUri,
);
}
},
(externalLibrary) {
return DirectiveUriWithInSummarySource(
relativeUriStr: relativeUriStr,
relativeUri: relativeUri,
source: externalLibrary.source,
);
},
);
}
/// TODO(scheglov) move to _fsState?
NamespaceDirectiveUris _buildNamespaceDirectiveUris(
UnlinkedNamespaceDirective directive,
) {
final primaryUri = _buildDirectiveUri(directive.uri);
final configurationUris = <DirectiveUri>[];
DirectiveUri? selectedConfigurationUri;
for (final configuration in directive.configurations) {
final configurationUri = _buildDirectiveUri(configuration.uri);
configurationUris.add(configurationUri);
// Maybe select this URI.
final name = configuration.name;
final value = configuration.valueOrTrue;
if (_fsState._declaredVariables.get(name) == value) {
selectedConfigurationUri ??= configurationUri;
}
}
return NamespaceDirectiveUris(
primary: primaryUri,
configurations: configurationUris,
selected: selectedConfigurationUri ?? primaryUri,
);
}
/// Return the [FileState] for the given [relativeUri], or `null` if the
/// URI cannot be parsed, cannot correspond any file, etc.
Either2<FileState?, ExternalLibrary> _fileForRelativeUri(
@ -1092,17 +1229,13 @@ class FileSystemState {
_uriToFile.remove(file.uri);
// The removed file does not reference other file anymore.
for (var referencedFile in file.directReferencedFiles) {
referencedFile.referencingFiles.remove(file);
}
// The removed file does not reference other files anymore.
file._kind?.dispose();
// Recursively remove files that reference the removed file.
for (var reference in file.referencingFiles.toList()) {
changeFile(reference.path, removedFiles);
}
file._kind?.dispose();
}
/// Collected files that transitively reference a file with the [path].
@ -1454,11 +1587,14 @@ class FileUriProperties {
}
/// Information about a single `import augment` directive.
class ImportAugmentationDirectiveState extends DirectiveState {
class ImportAugmentationDirectiveState<U extends DirectiveUri>
extends DirectiveState {
final UnlinkedImportAugmentationDirective directive;
final U uri;
ImportAugmentationDirectiveState({
required this.directive,
required this.uri,
});
/// Returns a [Source] that is referenced by this directive.
@ -1469,14 +1605,13 @@ class ImportAugmentationDirectiveState extends DirectiveState {
/// [ImportAugmentationWithUri] that has a valid URI that references a file.
class ImportAugmentationDirectiveWithFile
extends ImportAugmentationDirectiveState {
extends ImportAugmentationWithUri<DirectiveUriWithFile> {
final LibraryOrAugmentationFileKind container;
final FileState importedFile;
ImportAugmentationDirectiveWithFile({
required this.container,
required super.directive,
required this.importedFile,
required super.uri,
}) {
importedFile.referencingFiles.add(container.file);
}
@ -1491,6 +1626,8 @@ class ImportAugmentationDirectiveWithFile
return null;
}
FileState get importedFile => uri.file;
@override
Source? get importedSource => importedFile.source;
@ -1501,21 +1638,24 @@ class ImportAugmentationDirectiveWithFile
}
/// [ImportAugmentationDirectiveState] that has a valid URI.
class ImportAugmentationWithUri extends ImportAugmentationDirectiveState {
final String uriStr;
class ImportAugmentationWithUri<U extends DirectiveUriWithUri>
extends ImportAugmentationDirectiveState<U> {
ImportAugmentationWithUri({
required super.directive,
required this.uriStr,
required super.uri,
});
}
/// Information about a single `import` directive.
class ImportDirectiveState extends DirectiveState {
class ImportDirectiveState<U extends DirectiveUri> extends DirectiveState {
final UnlinkedNamespaceDirective directive;
final U selectedUri;
final NamespaceDirectiveUris uris;
ImportDirectiveState({
required this.directive,
required this.selectedUri,
required this.uris,
});
/// If [importedSource] corresponds to a library, returns it.
@ -1532,19 +1672,21 @@ class ImportDirectiveState extends DirectiveState {
}
/// [ImportDirectiveWithUri] that has a valid URI that references a file.
class ImportDirectiveWithFile extends ImportDirectiveWithUri {
class ImportDirectiveWithFile
extends ImportDirectiveWithUri<DirectiveUriWithFile> {
final LibraryOrAugmentationFileKind container;
final FileState importedFile;
ImportDirectiveWithFile({
required this.container,
required super.directive,
required this.importedFile,
required super.selectedUriStr,
required super.selectedUri,
required super.uris,
}) {
importedFile.referencingFiles.add(container.file);
}
FileState get importedFile => selectedUri.file;
/// Returns [importedFile] if it is a library.
LibraryFileStateKind? get importedLibrary {
final kind = importedFile.kind;
@ -1572,14 +1714,12 @@ class ImportDirectiveWithFile extends ImportDirectiveWithUri {
}
/// [ImportDirectiveWithUri] with a URI that resolves to [InSummarySource].
class ImportDirectiveWithInSummarySource extends ImportDirectiveWithUri {
@override
final InSummarySource importedSource;
class ImportDirectiveWithInSummarySource
extends ImportDirectiveWithUri<DirectiveUriWithInSummarySource> {
ImportDirectiveWithInSummarySource({
required super.directive,
required this.importedSource,
required super.selectedUriStr,
required super.selectedUri,
required super.uris,
});
@override
@ -1590,15 +1730,18 @@ class ImportDirectiveWithInSummarySource extends ImportDirectiveWithUri {
return null;
}
}
@override
InSummarySource get importedSource => selectedUri.source;
}
/// [ImportDirectiveState] that has a valid URI.
class ImportDirectiveWithUri extends ImportDirectiveState {
final String selectedUriStr;
class ImportDirectiveWithUri<U extends DirectiveUriWithUri>
extends ImportDirectiveState<U> {
ImportDirectiveWithUri({
required super.directive,
required this.selectedUriStr,
required super.selectedUri,
required super.uris,
});
}
@ -1658,37 +1801,24 @@ class LibraryFileStateKind extends LibraryOrAugmentationFileKind {
List<PartDirectiveState> get parts {
return _parts ??= file.unlinked2.parts.map((directive) {
final uriStr = directive.uri;
if (uriStr != null) {
return file._fileForRelativeUri(uriStr).map(
(refFile) {
if (refFile != null) {
return PartDirectiveWithFile(
library: this,
directive: directive,
includedFile: refFile,
uriStr: uriStr,
);
} else {
return PartDirectiveWithUri(
library: this,
directive: directive,
uriStr: uriStr,
);
}
},
(externalLibrary) {
return PartDirectiveWithUri(
library: this,
directive: directive,
uriStr: uriStr,
);
},
final uri = file._buildDirectiveUri(directive.uri);
if (uri is DirectiveUriWithFile) {
return PartDirectiveWithFile(
library: this,
directive: directive,
uri: uri,
);
} else if (uri is DirectiveUriWithUri) {
return PartDirectiveWithUri(
library: this,
directive: directive,
uri: uri,
);
} else {
return PartDirectiveState(
library: this,
directive: directive,
uri: uri,
);
}
}).toList();
@ -1750,105 +1880,90 @@ abstract class LibraryOrAugmentationFileKind extends FileStateKind {
List<ImportAugmentationDirectiveState> get augmentations {
return _augmentations ??= file.unlinked2.augmentations.map((directive) {
final uriStr = directive.uri;
if (uriStr != null) {
return file._fileForRelativeUri(uriStr).map(
(refFile) {
if (refFile != null) {
return ImportAugmentationDirectiveWithFile(
container: this,
directive: directive,
importedFile: refFile,
);
} else {
return ImportAugmentationWithUri(
directive: directive,
uriStr: uriStr,
);
}
},
(externalLibrary) {
return ImportAugmentationWithUri(
directive: directive,
uriStr: uriStr,
);
},
final uri = file._buildDirectiveUri(directive.uri);
if (uri is DirectiveUriWithFile) {
return ImportAugmentationDirectiveWithFile(
container: this,
directive: directive,
uri: uri,
);
} else if (uri is DirectiveUriWithUri) {
return ImportAugmentationWithUri(
directive: directive,
uri: uri,
);
} else {
return ImportAugmentationDirectiveState(
directive: directive,
uri: uri,
);
}
}).toList();
}
List<ExportDirectiveState> get exports {
return _exports ??= file.unlinked2.exports.map((directive) {
final uriStr = file._selectRelativeUri(directive);
if (uriStr != null) {
return file._fileForRelativeUri(uriStr).map(
(refFile) {
if (refFile != null) {
return ExportDirectiveWithFile(
container: this,
directive: directive,
exportedFile: refFile,
selectedUriStr: uriStr,
);
} else {
return ExportDirectiveWithUri(
directive: directive,
selectedUriStr: uriStr,
);
}
},
(externalLibrary) {
return ExportDirectiveWithInSummarySource(
directive: directive,
exportedSource: externalLibrary.source,
selectedUriStr: uriStr,
);
},
return _exports ??=
file.unlinked2.exports.map<ExportDirectiveState>((directive) {
final uris = file._buildNamespaceDirectiveUris(directive);
final selectedUri = uris.selected;
if (selectedUri is DirectiveUriWithFile) {
return ExportDirectiveWithFile(
container: this,
directive: directive,
selectedUri: selectedUri,
uris: uris,
);
} else if (selectedUri is DirectiveUriWithInSummarySource) {
return ExportDirectiveWithInSummarySource(
directive: directive,
selectedUri: selectedUri,
uris: uris,
);
} else if (selectedUri is DirectiveUriWithUri) {
return ExportDirectiveWithUri(
directive: directive,
selectedUri: selectedUri,
uris: uris,
);
} else {
return ExportDirectiveState(
directive: directive,
selectedUri: selectedUri,
uris: uris,
);
}
}).toList();
}
List<ImportDirectiveState> get imports {
return _imports ??= file.unlinked2.imports.map((directive) {
final uriStr = file._selectRelativeUri(directive);
if (uriStr != null) {
return file._fileForRelativeUri(uriStr).map(
(refFile) {
if (refFile != null) {
return ImportDirectiveWithFile(
container: this,
directive: directive,
importedFile: refFile,
selectedUriStr: uriStr,
);
} else {
return ImportDirectiveWithUri(
directive: directive,
selectedUriStr: uriStr,
);
}
},
(externalLibrary) {
return ImportDirectiveWithInSummarySource(
directive: directive,
importedSource: externalLibrary.source,
selectedUriStr: uriStr,
);
},
return _imports ??=
file.unlinked2.imports.map<ImportDirectiveState>((directive) {
final uris = file._buildNamespaceDirectiveUris(directive);
final selectedUri = uris.selected;
if (selectedUri is DirectiveUriWithFile) {
return ImportDirectiveWithFile(
container: this,
directive: directive,
selectedUri: selectedUri,
uris: uris,
);
} else if (selectedUri is DirectiveUriWithInSummarySource) {
return ImportDirectiveWithInSummarySource(
directive: directive,
selectedUri: selectedUri,
uris: uris,
);
} else if (selectedUri is DirectiveUriWithUri) {
return ImportDirectiveWithUri(
directive: directive,
selectedUri: selectedUri,
uris: uris,
);
} else {
return ImportDirectiveState(
directive: directive,
selectedUri: selectedUri,
uris: uris,
);
}
}).toList();
@ -1912,14 +2027,28 @@ abstract class LibraryOrAugmentationFileKind extends FileStateKind {
}
}
class NamespaceDirectiveUris {
final DirectiveUri primary;
final List<DirectiveUri> configurations;
final DirectiveUri selected;
NamespaceDirectiveUris({
required this.primary,
required this.configurations,
required this.selected,
});
}
/// Information about a single `part` directive.
class PartDirectiveState extends DirectiveState {
class PartDirectiveState<U extends DirectiveUri> extends DirectiveState {
final LibraryFileStateKind library;
final UnlinkedPartDirective directive;
final U uri;
PartDirectiveState({
required this.library,
required this.directive,
required this.uri,
});
/// Returns a [Source] that is referenced by this directive.
@ -1929,18 +2058,17 @@ class PartDirectiveState extends DirectiveState {
}
/// [PartDirectiveWithUri] that has a valid URI that references a file.
class PartDirectiveWithFile extends PartDirectiveWithUri {
final FileState includedFile;
class PartDirectiveWithFile extends PartDirectiveWithUri<DirectiveUriWithFile> {
PartDirectiveWithFile({
required super.library,
required super.directive,
required super.uriStr,
required this.includedFile,
required super.uri,
}) {
includedFile.referencingFiles.add(library.file);
}
FileState get includedFile => uri.file;
/// If [includedFile] is a [PartFileStateKind], and it confirms that it
/// is a part of the [library], returns the [includedFile].
PartFileStateKind? get includedPart {
@ -1961,13 +2089,12 @@ class PartDirectiveWithFile extends PartDirectiveWithUri {
}
/// [PartDirectiveState] that has a valid URI.
class PartDirectiveWithUri extends PartDirectiveState {
final String uriStr;
class PartDirectiveWithUri<U extends DirectiveUriWithUri>
extends PartDirectiveState<U> {
PartDirectiveWithUri({
required super.library,
required super.directive,
required this.uriStr,
required super.uri,
});
}

View file

@ -41,16 +41,13 @@ import 'package:analyzer/src/generated/error_verifier.dart';
import 'package:analyzer/src/generated/ffi_verifier.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/hint/sdk_constraint_verifier.dart';
import 'package:analyzer/src/ignore_comments/ignore_info.dart';
import 'package:analyzer/src/lint/linter.dart';
import 'package:analyzer/src/lint/linter_visitor.dart';
import 'package:analyzer/src/services/lint.dart';
import 'package:analyzer/src/summary/package_bundle_reader.dart';
import 'package:analyzer/src/task/strong/checker.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer/src/util/uri.dart';
class AnalysisForCompletionResult {
final CompilationUnit parsedUnit;
@ -66,7 +63,6 @@ class AnalysisForCompletionResult {
class LibraryAnalyzer {
final AnalysisOptionsImpl _analysisOptions;
final DeclaredVariables _declaredVariables;
final SourceFactory _sourceFactory;
final LibraryFileStateKind _library;
final InheritanceManager3 _inheritance;
@ -79,13 +75,8 @@ class LibraryAnalyzer {
final Map<FileState, ErrorReporter> _errorReporters = {};
final TestingData? _testingData;
LibraryAnalyzer(
this._analysisOptions,
this._declaredVariables,
this._sourceFactory,
this._libraryElement,
this._inheritance,
this._library,
LibraryAnalyzer(this._analysisOptions, this._declaredVariables,
this._libraryElement, this._inheritance, this._library,
{TestingData? testingData})
: _testingData = testingData;
@ -296,13 +287,17 @@ class LibraryAnalyzer {
// This must happen after all other diagnostics have been computed but
// before the list of diagnostics has been filtered.
for (var file in _library.files) {
IgnoreValidator(
_getErrorReporter(file),
_getErrorListener(file).errors,
_fileToIgnoreInfo[file]!,
_fileToLineInfo[file]!,
_analysisOptions.unignorableNames,
).reportErrors();
final ignoreInfo = _fileToIgnoreInfo[file];
// TODO(scheglov) make it safer
if (ignoreInfo != null) {
IgnoreValidator(
_getErrorReporter(file),
_getErrorListener(file).errors,
ignoreInfo,
_fileToLineInfo[file]!,
_analysisOptions.unignorableNames,
).reportErrors();
}
}
}
@ -439,11 +434,6 @@ class LibraryAnalyzer {
checker.visitCompilationUnit(unit);
}
//
// Validate the directives.
//
_validateUriBasedDirectives(file, unit);
//
// Use the ConstantVerifier to compute errors.
//
@ -515,42 +505,6 @@ class LibraryAnalyzer {
});
}
/// Return the name of the library that the given part is declared to be a
/// part of, or `null` if the part does not contain a part-of directive.
_NameOrSource? _getPartLibraryNameOrUri(Source partSource,
CompilationUnit partUnit, List<Directive> directivesToResolve) {
for (Directive directive in partUnit.directives) {
if (directive is PartOfDirective) {
directivesToResolve.add(directive);
LibraryIdentifier? libraryName = directive.libraryName;
if (libraryName != null) {
return _NameOrSource(libraryName.name, null);
}
String? uri = directive.uri?.stringValue;
if (uri != null) {
Source? librarySource = _sourceFactory.resolveUri(partSource, uri);
if (librarySource != null) {
return _NameOrSource(null, librarySource);
}
}
}
}
return null;
}
bool _isExistingSource(Source source) {
if (source is InSummarySource) {
return true;
}
for (var file in _library.file.directReferencedFiles) {
if (file.uri == source.uri) {
return file.exists;
}
}
// A library can refer to itself with an empty URI.
return source == _library.file.source;
}
/// Return a new parsed unresolved [CompilationUnit].
CompilationUnitImpl _parse(FileState file) {
AnalysisErrorListener errorListener = _getErrorListener(file);
@ -565,21 +519,28 @@ class LibraryAnalyzer {
/// Parse and resolve all files in [_library].
Map<FileState, CompilationUnitImpl> _parseAndResolve() {
var units = <FileState, CompilationUnitImpl>{};
// Parse all files.
for (FileState file in _library.files) {
units[file] = _parse(file);
final libraryFile = _library.file;
final libraryUnit = _parse(libraryFile);
final units = <FileState, CompilationUnitImpl>{
libraryFile: libraryUnit,
};
for (final part in _library.parts) {
if (part is PartDirectiveWithFile) {
final partFile = part.includedPart?.file;
if (partFile != null) {
units[partFile] = _parse(partFile);
}
}
}
// Resolve URIs in directives to corresponding sources.
FeatureSet featureSet = units[_library.file]!.featureSet;
final featureSet = _libraryElement.featureSet;
units.forEach((file, unit) {
_validateFeatureSet(unit, featureSet);
_resolveUriBasedDirectives(file, unit);
});
_resolveDirectives(units);
_resolveDirectives(units, libraryUnit);
units.forEach((file, unit) {
_resolveFile(file, unit);
@ -590,136 +551,56 @@ class LibraryAnalyzer {
return units;
}
void _resolveDirectives(Map<FileState, CompilationUnitImpl> units) {
var definingCompilationUnit = units[_library.file]!;
definingCompilationUnit.element = _libraryElement.definingCompilationUnit;
bool matchNodeElement(Directive node, Element element) {
return node.keyword.offset == element.nameOffset;
}
void _resolveDirectives(
Map<FileState, CompilationUnitImpl> units,
CompilationUnitImpl libraryUnit,
) {
libraryUnit.element = _libraryElement.definingCompilationUnit;
ErrorReporter libraryErrorReporter = _getErrorReporter(_library.file);
var importIndex = 0;
var exportIndex = 0;
LibraryIdentifier? libraryNameNode;
var seenPartSources = <Source>{};
var directivesToResolve = <DirectiveImpl>[];
int partDirectiveIndex = 0;
int partElementIndex = 0;
for (Directive directive in definingCompilationUnit.directives) {
final partIndexes = _PartDirectiveIndexes();
for (Directive directive in libraryUnit.directives) {
if (directive is LibraryDirectiveImpl) {
libraryNameNode = directive.name;
directivesToResolve.add(directive);
} else if (directive is ImportDirectiveImpl) {
// TODO(scheglov) Rewrite to iterating `ImportDirectiveState`.
for (var index = 0; index < _libraryElement.imports.length; index++) {
final importElement = _libraryElement.imports[index];
if (matchNodeElement(directive, importElement)) {
directive.element = importElement;
directive.prefix?.staticElement = importElement.prefix;
final importDirectiveState = _library.imports[index];
// TODO(scheglov) rewrite
if (importDirectiveState is ImportDirectiveWithUri) {
if (importDirectiveState.importedSource != null) {
if (importDirectiveState.importedLibrarySource == null) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY,
directive.uri,
[importDirectiveState.selectedUriStr],
);
}
}
}
}
if (directive.augmentKeyword != null) {
// TODO(scheglov) implement
throw UnimplementedError();
} else {
_resolveImportDirective(
directive: directive,
importElement: _libraryElement.imports[importIndex],
importState: _library.imports[importIndex],
libraryErrorReporter: libraryErrorReporter,
);
importIndex++;
}
} else if (directive is ExportDirectiveImpl) {
// TODO(scheglov) Rewrite to iterating `ExportDirectiveState`.
for (var index = 0; index < _libraryElement.exports.length; index++) {
final exportElement = _libraryElement.exports[index];
if (matchNodeElement(directive, exportElement)) {
directive.element = exportElement;
final exportDirectiveState = _library.exports[index];
// TODO(scheglov) rewrite
if (exportDirectiveState is ExportDirectiveWithUri) {
if (exportDirectiveState.exportedSource != null) {
if (exportDirectiveState.exportedLibrarySource == null) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
directive.uri,
[exportDirectiveState.selectedUriStr],
);
}
}
}
}
}
_resolveExportDirective(
directive: directive,
exportElement: _libraryElement.exports[exportIndex],
exportState: _library.exports[exportIndex],
libraryErrorReporter: libraryErrorReporter,
);
exportIndex++;
} else if (directive is PartDirectiveImpl) {
StringLiteral partUri = directive.uri;
if (partElementIndex >= _libraryElement.parts.length) {
continue;
}
final partState = _library.parts[partDirectiveIndex++];
if (partState is! PartDirectiveWithFile) {
continue;
}
final partFile = partState.includedFile;
var partUnit = units[partFile]!;
var partElement = _libraryElement.parts[partElementIndex++];
partUnit.element = partElement;
directive.element = partElement;
Source? partSource = directive.uriSource;
if (partSource == null) {
continue;
}
//
// Validate that the part source is unique in the library.
//
if (!seenPartSources.add(partSource)) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.DUPLICATE_PART, partUri, [partSource.uri]);
}
//
// Validate that the part contains a part-of directive with the same
// name or uri as the library.
//
if (_isExistingSource(partSource)) {
_NameOrSource? nameOrSource = _getPartLibraryNameOrUri(
partSource, partUnit, directivesToResolve);
if (nameOrSource == null) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.PART_OF_NON_PART,
partUri,
[partUri.toSource()]);
} else {
String? name = nameOrSource.name;
if (name != null) {
if (libraryNameNode == null) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.PART_OF_UNNAMED_LIBRARY,
partUri,
[name]);
} else if (libraryNameNode.name != name) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.PART_OF_DIFFERENT_LIBRARY,
partUri,
[libraryNameNode.name, name]);
}
} else {
Source source = nameOrSource.source!;
if (source != _library.file.source) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.PART_OF_DIFFERENT_LIBRARY,
partUri,
[_library.file.uriStr, source.uri]);
}
}
}
}
_resolvePartDirective(
directive: directive,
partIndexes: partIndexes,
libraryErrorReporter: libraryErrorReporter,
libraryNameNode: libraryNameNode,
units: units,
directivesToResolve: directivesToResolve,
seenPartSources: seenPartSources,
);
}
}
@ -734,6 +615,62 @@ class LibraryAnalyzer {
}
}
void _resolveExportDirective({
required ExportDirectiveImpl directive,
required ExportElement exportElement,
required ExportDirectiveState exportState,
required ErrorReporter libraryErrorReporter,
}) {
directive.element = exportElement;
_resolveNamespaceDirective(
directive: directive,
primaryUriNode: directive.uri,
primaryUriState: exportState.uris.primary,
configurationNodes: directive.configurations,
configurationUris: exportState.uris.configurations,
selectedUriState: exportState.selectedUri,
);
if (exportState is ExportDirectiveWithUri) {
final selectedUriStr = exportState.selectedUri.relativeUriStr;
if (selectedUriStr.startsWith('dart-ext:')) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.USE_OF_NATIVE_EXTENSION,
directive.uri,
);
} else if (exportState.exportedSource == null) {
final errorCode = exportState.selectedUri.isValid
? CompileTimeErrorCode.URI_DOES_NOT_EXIST
: CompileTimeErrorCode.INVALID_URI;
libraryErrorReporter.reportErrorForNode(
errorCode,
directive.uri,
[selectedUriStr],
);
} else if (exportState is ExportDirectiveWithFile &&
!exportState.exportedFile.exists) {
final errorCode = isGeneratedSource(exportState.exportedSource)
? CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED
: CompileTimeErrorCode.URI_DOES_NOT_EXIST;
libraryErrorReporter.reportErrorForNode(
errorCode,
directive.uri,
[selectedUriStr],
);
} else if (exportState.exportedLibrarySource == null) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
directive.uri,
[selectedUriStr],
);
}
} else {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.URI_WITH_INTERPOLATION,
directive.uri,
);
}
}
void _resolveFile(FileState file, CompilationUnit unit) {
var source = file.source;
RecordingErrorListener errorListener = _getErrorListener(file);
@ -772,86 +709,202 @@ class LibraryAnalyzer {
featureSet: unit.featureSet, flowAnalysisHelper: flowAnalysisHelper));
}
Uri? _resolveRelativeUri(String relativeUriStr) {
Uri relativeUri;
try {
relativeUri = Uri.parse(relativeUriStr);
} on FormatException {
return null;
void _resolveImportDirective({
required ImportDirectiveImpl directive,
required ImportElement importElement,
required ImportDirectiveState importState,
required ErrorReporter libraryErrorReporter,
}) {
directive.element = importElement;
directive.prefix?.staticElement = importElement.prefix;
_resolveNamespaceDirective(
directive: directive,
primaryUriNode: directive.uri,
primaryUriState: importState.uris.primary,
configurationNodes: directive.configurations,
configurationUris: importState.uris.configurations,
selectedUriState: importState.selectedUri,
);
if (importState is ImportDirectiveWithUri) {
final selectedUriStr = importState.selectedUri.relativeUriStr;
if (selectedUriStr.startsWith('dart-ext:')) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.USE_OF_NATIVE_EXTENSION,
directive.uri,
);
} else if (importState.importedSource == null) {
final errorCode = importState.selectedUri.isValid
? CompileTimeErrorCode.URI_DOES_NOT_EXIST
: CompileTimeErrorCode.INVALID_URI;
libraryErrorReporter.reportErrorForNode(
errorCode,
directive.uri,
[selectedUriStr],
);
} else if (importState is ImportDirectiveWithFile &&
!importState.importedFile.exists) {
final errorCode = isGeneratedSource(importState.importedSource)
? CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED
: CompileTimeErrorCode.URI_DOES_NOT_EXIST;
libraryErrorReporter.reportErrorForNode(
errorCode,
directive.uri,
[selectedUriStr],
);
} else if (importState.importedLibrarySource == null) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY,
directive.uri,
[selectedUriStr],
);
}
} else {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.URI_WITH_INTERPOLATION,
directive.uri,
);
}
var absoluteUri = resolveRelativeUri(_library.file.uri, relativeUri);
return rewriteToCanonicalUri(_sourceFactory, absoluteUri);
}
/// Return the result of resolve the given [uriContent], reporting errors
/// against the [uriLiteral].
Source? _resolveUri(FileState file, bool isImport, StringLiteral uriLiteral,
String? uriContent) {
UriValidationCode? code =
UriBasedDirectiveImpl.validateUri(isImport, uriLiteral, uriContent);
if (code == null) {
try {
Uri.parse(uriContent!);
} on FormatException {
return null;
}
return _sourceFactory.resolveUri(file.source, uriContent);
} else if (code == UriValidationCode.URI_WITH_INTERPOLATION) {
_getErrorReporter(file).reportErrorForNode(
CompileTimeErrorCode.URI_WITH_INTERPOLATION, uriLiteral);
return null;
} else if (code == UriValidationCode.INVALID_URI) {
// It is safe to assume [uriContent] is non-null because the only way for
// it to be null is if the string literal contained an interpolation, and
// in that case the validation code would have been
// UriValidationCode.URI_WITH_INTERPOLATION.
assert(uriContent != null);
_getErrorReporter(file).reportErrorForNode(
CompileTimeErrorCode.INVALID_URI, uriLiteral, [uriContent!]);
return null;
void _resolveNamespaceDirective({
required NamespaceDirectiveImpl directive,
required StringLiteralImpl primaryUriNode,
required DirectiveUri primaryUriState,
required DirectiveUri selectedUriState,
required List<Configuration> configurationNodes,
required List<DirectiveUri> configurationUris,
}) {
for (var i = 0; i < configurationNodes.length; i++) {
final configurationNode = configurationNodes[i];
configurationNode as ConfigurationImpl;
configurationNode.uriSource = configurationUris[i].source;
}
if (primaryUriState is DirectiveUriWithString) {
directive.uriContent = primaryUriState.relativeUriStr;
directive.uriSource = primaryUriState.source;
}
if (selectedUriState is DirectiveUriWithString) {
directive.selectedUriContent = selectedUriState.relativeUriStr;
directive.selectedSource = selectedUriState.source;
}
return null;
}
void _resolveUriBasedDirectives(FileState file, CompilationUnit unit) {
for (var directive in unit.directives) {
if (directive is UriBasedDirectiveImpl) {
StringLiteral uriLiteral = directive.uri;
String? uriContent = uriLiteral.stringValue?.trim();
directive.uriContent = uriContent;
Source? defaultSource = _resolveUri(
file, directive is ImportDirective, uriLiteral, uriContent);
directive.uriSource = defaultSource;
void _resolvePartDirective({
required PartDirectiveImpl directive,
required _PartDirectiveIndexes partIndexes,
required ErrorReporter libraryErrorReporter,
required LibraryIdentifier? libraryNameNode,
required Map<FileState, CompilationUnitImpl> units,
required List<DirectiveImpl> directivesToResolve,
required Set<Source> seenPartSources,
}) {
StringLiteral partUri = directive.uri;
final partState = _library.parts[partIndexes.directive++];
directive.uriSource = partState.includedSource;
if (partState is! PartDirectiveWithUri) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.URI_WITH_INTERPOLATION,
directive.uri,
);
return;
}
// TODO(scheglov) This should not be necessary if we build `PartElement`
// for every `part` directive.
if (partIndexes.element >= _libraryElement.parts.length) {
final errorCode = partState.uri.isValid
? CompileTimeErrorCode.URI_DOES_NOT_EXIST
: CompileTimeErrorCode.INVALID_URI;
libraryErrorReporter.reportErrorForNode(
errorCode,
directive.uri,
[partState.uri.relativeUriStr],
);
return;
}
if (partState is! PartDirectiveWithFile) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.URI_DOES_NOT_EXIST,
directive.uri,
[partState.uri.relativeUriStr],
);
return;
}
final includedFile = partState.includedFile;
final includedKind = includedFile.kind;
if (includedKind is! PartFileStateKind) {
if (includedFile.exists) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.PART_OF_NON_PART,
partUri,
[partUri.toSource()],
);
} else {
final errorCode = isGeneratedSource(includedFile.source)
? CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED
: CompileTimeErrorCode.URI_DOES_NOT_EXIST;
libraryErrorReporter.reportErrorForNode(
errorCode,
directive.uri,
[partUri.toSource()],
);
}
if (directive is NamespaceDirectiveImpl) {
var relativeUriStr = _selectRelativeUri(directive);
directive.selectedUriContent = relativeUriStr;
var absoluteUri = _resolveRelativeUri(relativeUriStr);
if (absoluteUri != null) {
directive.selectedSource = _sourceFactory.forUri2(absoluteUri);
}
for (var configuration in directive.configurations) {
configuration as ConfigurationImpl;
var uriLiteral = configuration.uri;
String? uriContent = uriLiteral.stringValue?.trim();
Source? defaultSource = _resolveUri(
file, directive is ImportDirective, uriLiteral, uriContent);
configuration.uriSource = defaultSource;
return;
}
if (includedKind is PartOfNameFileStateKind) {
if (!includedKind.libraries.contains(_library)) {
final name = includedKind.directive.name;
if (libraryNameNode == null) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.PART_OF_UNNAMED_LIBRARY,
partUri,
[name],
);
} else {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.PART_OF_DIFFERENT_LIBRARY,
partUri,
[libraryNameNode.name, name],
);
}
return;
}
} else if (includedKind.library != _library) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.PART_OF_DIFFERENT_LIBRARY,
partUri,
[_library.file.uriStr, includedFile.uriStr],
);
return;
}
}
String _selectRelativeUri(NamespaceDirective directive) {
for (var configuration in directive.configurations) {
var name = configuration.name.components.join('.');
var value = configuration.value?.stringValue ?? 'true';
if (_declaredVariables.get(name) == value) {
return configuration.uri.stringValue ?? '';
var partUnit = units[includedFile]!;
var partElement = _libraryElement.parts[partIndexes.element++];
partUnit.element = partElement;
directive.element = partElement;
final partSource = includedKind.file.source;
directive.uriSource = partSource;
for (final directive in partUnit.directives) {
if (directive is PartOfDirectiveImpl) {
directivesToResolve.add(directive);
}
}
return directive.uri.stringValue ?? '';
//
// Validate that the part source is unique in the library.
//
if (!seenPartSources.add(partSource)) {
libraryErrorReporter.reportErrorForNode(
CompileTimeErrorCode.DUPLICATE_PART, partUri, [partSource.uri]);
}
}
/// Validate that the feature set associated with the compilation [unit] is
@ -863,61 +916,6 @@ class LibraryAnalyzer {
}
}
/// Check the given [directive] to see if the referenced source exists and
/// report an error if it does not.
void _validateUriBasedDirective(
FileState file, UriBasedDirectiveImpl directive) {
String? uriContent;
Source? source;
if (directive is NamespaceDirectiveImpl) {
uriContent = directive.selectedUriContent;
source = directive.selectedSource;
} else {
uriContent = directive.uriContent;
source = directive.uriSource;
}
if (source != null) {
if (_isExistingSource(source)) {
return;
}
} else {
// Don't report errors already reported by ParseDartTask.resolveDirective
// TODO(scheglov) we don't use this task here
if (directive.validate() != null) {
return;
}
}
if (uriContent != null && uriContent.startsWith('dart-ext:')) {
_getErrorReporter(file).reportErrorForNode(
CompileTimeErrorCode.USE_OF_NATIVE_EXTENSION,
directive.uri,
);
return;
}
CompileTimeErrorCode errorCode = CompileTimeErrorCode.URI_DOES_NOT_EXIST;
if (isGeneratedSource(source)) {
errorCode = CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED;
}
// It is safe to assume that [uriContent] is non-null because the only way
// for it to be null is if the string literal contained an interpolation,
// and in that case the call to `directive.validate()` above would have
// returned a non-null validation code.
_getErrorReporter(file)
.reportErrorForNode(errorCode, directive.uri, [uriContent!]);
}
/// Check each directive in the given [unit] to see if the referenced source
/// exists and report an error if it does not.
void _validateUriBasedDirectives(FileState file, CompilationUnit unit) {
for (Directive directive in unit.directives) {
if (directive is UriBasedDirectiveImpl) {
_validateUriBasedDirective(file, directive);
}
}
}
/// Find constants in [unit] to compute.
static List<ConstantEvaluationTarget> _findConstants(CompilationUnit unit) {
ConstantFinder constantFinder = ConstantFinder();
@ -1016,10 +1014,7 @@ class UnitAnalysisResult {
UnitAnalysisResult(this.file, this.unit, this.errors);
}
/// Either the name or the source associated with a part-of directive.
class _NameOrSource {
final String? name;
final Source? source;
_NameOrSource(this.name, this.source);
class _PartDirectiveIndexes {
int directive = 0;
int element = 0;
}

View file

@ -221,6 +221,14 @@ class UnlinkedNamespaceDirectiveConfiguration {
);
}
String get valueOrTrue {
if (value.isEmpty) {
return 'true';
} else {
return value;
}
}
void write(BufferedSink sink) {
sink.writeStringUtf8(name);
sink.writeOptionalStringUtf8(uri);

View file

@ -581,7 +581,6 @@ class FileResolver {
var libraryAnalyzer = LibraryAnalyzer(
fileContext.analysisOptions,
contextObjects!.declaredVariables,
sourceFactory,
elementFactory.libraryOfUri2(libraryKind.file.uri),
analysisSession.inheritanceManager,
libraryKind,
@ -647,7 +646,6 @@ class FileResolver {
var libraryAnalyzer = LibraryAnalyzer(
fileContext.analysisOptions,
contextObjects!.declaredVariables,
sourceFactory,
libraryContext!.elementFactory.libraryOfUri2(libraryKind.file.uri),
libraryContext!.elementFactory.analysisSession.inheritanceManager,
libraryKind,

View file

@ -111,7 +111,7 @@ class AnalyzerStatePrinter {
}
sink.writeln();
} else if (import is ImportAugmentationWithUri) {
final uriStr = _stringOfUriStr(import.uriStr);
final uriStr = _stringOfUriStr(import.uri.relativeUriStr);
_writelnWithIndent('uri: $uriStr');
} else {
_writelnWithIndent('noUri');
@ -204,7 +204,7 @@ class AnalyzerStatePrinter {
}
sink.writeln();
} else if (export is ExportDirectiveWithUri) {
final uriStr = _stringOfUriStr(export.selectedUriStr);
final uriStr = _stringOfUriStr(export.selectedUri.relativeUriStr);
_writelnWithIndent('uri: $uriStr');
} else {
_writelnWithIndent('noUri');
@ -255,8 +255,9 @@ class AnalyzerStatePrinter {
}
sink.writeln();
} else if (import is ImportDirectiveWithUri) {
final uriStr = _stringOfUriStr(import.selectedUri.relativeUriStr);
sink.write(_indent);
sink.write('uri: ${_stringOfUriStr(import.selectedUriStr)}');
sink.write('uri: $uriStr');
if (import.isSyntheticDartCoreImport) {
sink.write(' synthetic');
}
@ -553,7 +554,7 @@ class AnalyzerStatePrinter {
}
sink.writeln();
} else if (part is PartDirectiveWithUri) {
final uriStr = _stringOfUriStr(part.uriStr);
final uriStr = _stringOfUriStr(part.uri.relativeUriStr);
_writelnWithIndent('uri: $uriStr');
} else {
_writelnWithIndent('noUri');

View file

@ -670,19 +670,11 @@ part 'c.dart';
var session = contextFor(testFilePath).currentSession;
var resolvedLibrary = await session.getResolvedLibraryValid(test.path);
expect(resolvedLibrary.units, hasLength(3));
expect(resolvedLibrary.units, hasLength(1));
expect(
resolvedLibrary.units[0].path,
convertPath('/home/test/lib/test.dart'),
);
expect(
resolvedLibrary.units[1].path,
convertPath('/home/test/lib/a.dart'),
);
expect(
resolvedLibrary.units[2].path,
convertPath('/home/test/lib/c.dart'),
);
}
test_getResolvedLibrary_invalidPath_notAbsolute() async {

View file

@ -34,11 +34,19 @@ export 'unknown.dart';
]);
}
test_export_cannotResolve() async {
await assertErrorsInCode(r'''
export 'dart:foo';
''', [
error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 7, 10),
]);
}
test_export_dart() async {
await assertErrorsInCode('''
export 'dart:foo/bar.dart';
export 'dart:math/bar.dart';
''', [
error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 7, 19),
error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 7, 20),
]);
}
@ -72,11 +80,19 @@ import 'target.dart';
]);
}
test_import_cannotResolve() async {
await assertErrorsInCode(r'''
import 'dart:foo';
''', [
error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 7, 10),
]);
}
test_import_dart() async {
await assertErrorsInCode('''
import 'dart:foo/bar.dart';
import 'dart:math/bar.dart';
''', [
error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 7, 19),
error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 7, 20),
]);
}
@ -107,4 +123,12 @@ part 'unknown.dart';
error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 18, 14),
]);
}
test_part_cannotResolve() async {
await assertErrorsInCode(r'''
part 'dart:foo';
''', [
error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 5, 10),
]);
}
}

View file

@ -15,21 +15,27 @@ main() {
@reflectiveTest
class UriWithInterpolationTest extends PubPackageResolutionTest {
test_constant() async {
await assertErrorsInCode('''
import 'stuff_\$platform.dart';
test_export() async {
await assertErrorsInCode(r'''
export '${'foo'}.dart';
''', [
error(CompileTimeErrorCode.URI_WITH_INTERPOLATION, 7, 22),
error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 15, 8),
error(CompileTimeErrorCode.URI_WITH_INTERPOLATION, 7, 15),
]);
}
test_nonConstant() async {
test_import() async {
await assertErrorsInCode(r'''
library lib;
part '${'a'}.dart';
import '${'foo'}.dart';
''', [
error(CompileTimeErrorCode.URI_WITH_INTERPOLATION, 18, 13),
error(CompileTimeErrorCode.URI_WITH_INTERPOLATION, 7, 15),
]);
}
test_part() async {
await assertErrorsInCode(r'''
part '${'foo'}.dart';
''', [
error(CompileTimeErrorCode.URI_WITH_INTERPOLATION, 5, 15),
]);
}
}