mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 08:44:27 +00:00
Use FileState from AnalysisDriver in Cider.
Change-Id: I3f6dc6f80845d24e555328d25f8355eb11e51883 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/248709 Reviewed-by: Keerti Parthasarathy <keertip@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
a2f9ad4ba7
commit
31c49c4fd8
12 changed files with 555 additions and 149 deletions
|
@ -12,6 +12,7 @@ import 'package:analyzer/dart/element/element.dart';
|
|||
import 'package:analyzer/error/error.dart';
|
||||
import 'package:analyzer/instrumentation/service.dart';
|
||||
import 'package:analyzer/source/line_info.dart';
|
||||
import 'package:analyzer/src/dart/analysis/file_state.dart';
|
||||
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
|
||||
import 'package:analyzer/src/dart/micro/resolve_file.dart';
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
|
||||
|
@ -88,7 +89,8 @@ class _CiderDartFixContextImpl extends DartFixContextImpl {
|
|||
var result = <LibraryElement, Element>{};
|
||||
var files = _fileResolver.getFilesWithTopLevelDeclarations(name);
|
||||
for (var file in files) {
|
||||
if (file.partOfLibrary == null) {
|
||||
final kind = file.kind;
|
||||
if (kind is LibraryFileStateKind) {
|
||||
var libraryElement = await _fileResolver.getLibraryByUri2(
|
||||
uriStr: file.uriStr,
|
||||
);
|
||||
|
|
|
@ -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 = 223;
|
||||
static const int DATA_VERSION = 224;
|
||||
|
||||
/// The number of exception contexts allowed to write. Once this field is
|
||||
/// zero, we stop writing any new exception contexts in this process.
|
||||
|
@ -116,6 +116,8 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
/// the content from the file.
|
||||
final FileContentCache _fileContentCache;
|
||||
|
||||
late final StoredFileContentStrategy _fileContentStrategy;
|
||||
|
||||
/// The analysis options to analyze with.
|
||||
AnalysisOptionsImpl _analysisOptions;
|
||||
|
||||
|
@ -277,6 +279,9 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
analysisContext?.driver = this;
|
||||
_onResults = _resultController.stream.asBroadcastStream();
|
||||
_testView = AnalysisDriverTestView(this);
|
||||
|
||||
_fileContentStrategy = StoredFileContentStrategy(_fileContentCache);
|
||||
|
||||
_createFileTracker();
|
||||
_scheduler.add(this);
|
||||
_search = Search(this);
|
||||
|
@ -1507,9 +1512,10 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
_saltForUnlinked,
|
||||
_saltForElements,
|
||||
featureSetProvider,
|
||||
fileContentCache: _fileContentCache,
|
||||
fileContentStrategy: _fileContentStrategy,
|
||||
prefetchFiles: null,
|
||||
);
|
||||
_fileTracker = FileTracker(_logger, _fsState);
|
||||
_fileTracker = FileTracker(_logger, _fsState, _fileContentStrategy);
|
||||
}
|
||||
|
||||
/// If this has not been done yet, schedule discovery of all files that are
|
||||
|
|
|
@ -30,11 +30,14 @@ import 'package:analyzer/src/generated/engine.dart';
|
|||
import 'package:analyzer/src/generated/parser.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/generated/utilities_dart.dart';
|
||||
import 'package:analyzer/src/ignore_comments/ignore_info.dart';
|
||||
import 'package:analyzer/src/source/source_resource.dart';
|
||||
import 'package:analyzer/src/summary/api_signature.dart';
|
||||
import 'package:analyzer/src/summary/package_bundle_reader.dart';
|
||||
import 'package:analyzer/src/summary2/informative_data.dart';
|
||||
import 'package:analyzer/src/util/either.dart';
|
||||
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
|
||||
import 'package:analyzer/src/util/performance/operation_performance.dart';
|
||||
import 'package:analyzer/src/util/uri.dart';
|
||||
import 'package:analyzer/src/workspace/workspace.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
@ -186,6 +189,12 @@ class ExternalLibrary {
|
|||
ExternalLibrary._(this.source);
|
||||
}
|
||||
|
||||
abstract class FileContent {
|
||||
String get content;
|
||||
String get contentHash;
|
||||
bool get exists;
|
||||
}
|
||||
|
||||
/// [FileContentOverlay] is used to temporary override content of files.
|
||||
class FileContentOverlay {
|
||||
final _map = <String, String>{};
|
||||
|
@ -211,6 +220,10 @@ class FileContentOverlay {
|
|||
}
|
||||
}
|
||||
|
||||
abstract class FileContentStrategy {
|
||||
FileContent get(String path);
|
||||
}
|
||||
|
||||
/// Information about a file being analyzed, explicitly or implicitly.
|
||||
///
|
||||
/// It provides a consistent view on its properties.
|
||||
|
@ -251,9 +264,7 @@ class FileState {
|
|||
/// The language version for the package that contains this file.
|
||||
final Version packageLanguageVersion;
|
||||
|
||||
bool? _exists;
|
||||
String? _content;
|
||||
String? _contentHash;
|
||||
FileContent? _fileContent;
|
||||
LineInfo? _lineInfo;
|
||||
Uint8List? _unlinkedSignature;
|
||||
String? _unlinkedKey;
|
||||
|
@ -306,10 +317,10 @@ class FileState {
|
|||
}
|
||||
|
||||
/// The content of the file.
|
||||
String get content => _content!;
|
||||
String get content => _fileContent!.content;
|
||||
|
||||
/// The MD5 hash of the [content].
|
||||
String get contentHash => _contentHash!;
|
||||
String get contentHash => _fileContent!.contentHash;
|
||||
|
||||
/// The class member names defined by the file.
|
||||
Set<String> get definedClassMemberNames {
|
||||
|
@ -341,7 +352,7 @@ class FileState {
|
|||
}
|
||||
|
||||
/// Return `true` if the file exists.
|
||||
bool get exists => _exists!;
|
||||
bool get exists => _fileContent!.exists;
|
||||
|
||||
/// The list of files this file exports.
|
||||
List<FileState?> get exportedFiles {
|
||||
|
@ -417,6 +428,10 @@ class FileState {
|
|||
return _driverUnlinkedUnit!.referencedNames;
|
||||
}
|
||||
|
||||
File get resource {
|
||||
return _fsState._resourceProvider.getFile(path);
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
FileStateTestView get test => FileStateTestView(this);
|
||||
|
||||
|
@ -448,6 +463,8 @@ class FileState {
|
|||
/// The [UnlinkedUnit] of the file.
|
||||
UnlinkedUnit get unlinked2 => _unlinked2!;
|
||||
|
||||
String get unlinkedKey => _unlinkedKey!;
|
||||
|
||||
/// The MD5 signature based on the content, feature sets, language version.
|
||||
Uint8List get unlinkedSignature => _unlinkedSignature!;
|
||||
|
||||
|
@ -459,6 +476,25 @@ class FileState {
|
|||
return other is FileState && other.uri == uri;
|
||||
}
|
||||
|
||||
/// Collect all files that are transitively referenced by this file via
|
||||
/// imports, exports, and parts.
|
||||
void collectAllReferencedFiles(Set<String> referencedFiles) {
|
||||
for (final file in directReferencedFiles) {
|
||||
if (referencedFiles.add(file.path)) {
|
||||
file.collectAllReferencedFiles(referencedFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the content of the file, the empty string if cannot be read.
|
||||
///
|
||||
/// We read the file digest, end verify that it is the same as the digest
|
||||
/// that was recorded during the file creation. If it is not, then the file
|
||||
/// was changed, and we failed to call [FileSystemState.changeFile].
|
||||
String getContent() {
|
||||
return _fileContent!.content;
|
||||
}
|
||||
|
||||
void internal_setLibraryCycle(LibraryCycle? cycle) {
|
||||
_libraryCycle = cycle;
|
||||
}
|
||||
|
@ -482,6 +518,43 @@ class FileState {
|
|||
}
|
||||
}
|
||||
|
||||
/// TODO(scheglov) Remove it when [IgnoreInfo] is stored here.
|
||||
CompilationUnitImpl parse2(
|
||||
AnalysisErrorListener errorListener,
|
||||
String content,
|
||||
) {
|
||||
CharSequenceReader reader = CharSequenceReader(content);
|
||||
Scanner scanner = Scanner(source, reader, errorListener)
|
||||
..configureFeatures(
|
||||
featureSetForOverriding: _contextFeatureSet,
|
||||
featureSet: _contextFeatureSet.restrictToVersion(
|
||||
packageLanguageVersion,
|
||||
),
|
||||
);
|
||||
Token token = scanner.tokenize(reportScannerErrors: false);
|
||||
LineInfo lineInfo = LineInfo(scanner.lineStarts);
|
||||
|
||||
Parser parser = Parser(
|
||||
source,
|
||||
errorListener,
|
||||
featureSet: scanner.featureSet,
|
||||
lineInfo: lineInfo,
|
||||
);
|
||||
parser.enableOptionalNewAndConst = true;
|
||||
|
||||
var unit = parser.parseCompilationUnit(token);
|
||||
unit.languageVersion = LibraryLanguageVersion(
|
||||
package: packageLanguageVersion,
|
||||
override: scanner.overrideVersion,
|
||||
);
|
||||
|
||||
// StringToken uses a static instance of StringCanonicalizer, so we need
|
||||
// to clear it explicitly once we are done using it for this file.
|
||||
StringTokenImpl.canonicalizer.clear();
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
/// Read the file content and ensure that all of the file properties are
|
||||
/// consistent with the read content, including API signature.
|
||||
///
|
||||
|
@ -489,11 +562,10 @@ class FileState {
|
|||
FileStateRefreshResult refresh() {
|
||||
_invalidateCurrentUnresolvedData();
|
||||
|
||||
final rawFileState = _fsState._fileContentCache.get(path);
|
||||
final contentChanged = _contentHash != rawFileState.contentHash;
|
||||
_content = rawFileState.content;
|
||||
_exists = rawFileState.exists;
|
||||
_contentHash = rawFileState.contentHash;
|
||||
final rawFileState = _fsState.fileContentStrategy.get(path);
|
||||
final contentChanged =
|
||||
_fileContent?.contentHash != rawFileState.contentHash;
|
||||
_fileContent = rawFileState;
|
||||
|
||||
// Prepare the unlinked bundle key.
|
||||
{
|
||||
|
@ -501,8 +573,8 @@ class FileState {
|
|||
signature.addUint32List(_fsState._saltForUnlinked);
|
||||
signature.addFeatureSet(_contextFeatureSet);
|
||||
signature.addLanguageVersion(packageLanguageVersion);
|
||||
signature.addString(_contentHash!);
|
||||
signature.addBool(_exists!);
|
||||
signature.addString(contentHash);
|
||||
signature.addBool(exists);
|
||||
_unlinkedSignature = signature.toByteList();
|
||||
var signatureHex = hex.encode(_unlinkedSignature!);
|
||||
// TODO(scheglov) Use the path as the key, and store the signature.
|
||||
|
@ -514,6 +586,8 @@ class FileState {
|
|||
_unlinked2 = _driverUnlinkedUnit!.unit;
|
||||
_lineInfo = LineInfo(_unlinked2!.lineStarts);
|
||||
|
||||
_prefetchDirectReferences();
|
||||
|
||||
// Prepare API signature.
|
||||
var newApiSignature = _unlinked2!.apiSignature;
|
||||
bool apiSignatureChanged = _apiSignature != null &&
|
||||
|
@ -588,8 +662,11 @@ class FileState {
|
|||
|
||||
/// Return the unlinked unit, from bytes or new.
|
||||
AnalysisDriverUnlinkedUnit _getUnlinkedUnit() {
|
||||
final testData = _fsState.testData?.forFile(resource);
|
||||
|
||||
var bytes = _fsState._byteStore.get(_unlinkedKey!);
|
||||
if (bytes != null && bytes.isNotEmpty) {
|
||||
testData?.unlinkedKeyGet.add(unlinkedKey);
|
||||
return AnalysisDriverUnlinkedUnit.fromBytes(bytes);
|
||||
}
|
||||
|
||||
|
@ -608,6 +685,7 @@ class FileState {
|
|||
);
|
||||
var bytes = driverUnlinkedUnit.toBytes();
|
||||
_fsState._byteStore.putGet(_unlinkedKey!, bytes);
|
||||
testData?.unlinkedKeyPut.add(unlinkedKey);
|
||||
return driverUnlinkedUnit;
|
||||
});
|
||||
}
|
||||
|
@ -637,36 +715,43 @@ class FileState {
|
|||
}
|
||||
|
||||
CompilationUnitImpl _parse(AnalysisErrorListener errorListener) {
|
||||
CharSequenceReader reader = CharSequenceReader(content);
|
||||
Scanner scanner = Scanner(source, reader, errorListener)
|
||||
..configureFeatures(
|
||||
featureSetForOverriding: _contextFeatureSet,
|
||||
featureSet: _contextFeatureSet.restrictToVersion(
|
||||
packageLanguageVersion,
|
||||
),
|
||||
);
|
||||
Token token = scanner.tokenize(reportScannerErrors: false);
|
||||
LineInfo lineInfo = LineInfo(scanner.lineStarts);
|
||||
return parse2(errorListener, content);
|
||||
}
|
||||
|
||||
Parser parser = Parser(
|
||||
source,
|
||||
errorListener,
|
||||
featureSet: scanner.featureSet,
|
||||
lineInfo: lineInfo,
|
||||
);
|
||||
parser.enableOptionalNewAndConst = true;
|
||||
/// TODO(scheglov) write tests
|
||||
void _prefetchDirectReferences() {
|
||||
final prefetchFiles = _fsState.prefetchFiles;
|
||||
if (prefetchFiles == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var unit = parser.parseCompilationUnit(token);
|
||||
unit.languageVersion = LibraryLanguageVersion(
|
||||
package: packageLanguageVersion,
|
||||
override: scanner.overrideVersion,
|
||||
);
|
||||
var paths = <String>{};
|
||||
|
||||
// StringToken uses a static instance of StringCanonicalizer, so we need
|
||||
// to clear it explicitly once we are done using it for this file.
|
||||
StringTokenImpl.canonicalizer.clear();
|
||||
void addRelativeUri(String relativeUriStr) {
|
||||
final Uri absoluteUri;
|
||||
try {
|
||||
final relativeUri = Uri.parse(relativeUriStr);
|
||||
absoluteUri = resolveRelativeUri(uri, relativeUri);
|
||||
} on FormatException {
|
||||
return;
|
||||
}
|
||||
final path = _fsState._sourceFactory.forUri2(absoluteUri)?.fullName;
|
||||
if (path != null) {
|
||||
paths.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
return unit;
|
||||
for (final directive in unlinked2.imports) {
|
||||
addRelativeUri(directive.uri);
|
||||
}
|
||||
for (final directive in unlinked2.exports) {
|
||||
addRelativeUri(directive.uri);
|
||||
}
|
||||
for (final uri in unlinked2.parts) {
|
||||
addRelativeUri(uri);
|
||||
}
|
||||
|
||||
prefetchFiles(paths.toList());
|
||||
}
|
||||
|
||||
/// TODO(scheglov) move to _fsState?
|
||||
|
@ -893,6 +978,29 @@ class FileState {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
final topLevelDeclarations = <String>{};
|
||||
for (final declaration in unit.declarations) {
|
||||
if (declaration is ClassDeclaration) {
|
||||
topLevelDeclarations.add(declaration.name.name);
|
||||
} else if (declaration is EnumDeclaration) {
|
||||
topLevelDeclarations.add(declaration.name.name);
|
||||
} else if (declaration is ExtensionDeclaration) {
|
||||
var name = declaration.name;
|
||||
if (name != null) {
|
||||
topLevelDeclarations.add(name.name);
|
||||
}
|
||||
} else if (declaration is FunctionDeclaration) {
|
||||
topLevelDeclarations.add(declaration.name.name);
|
||||
} else if (declaration is MixinDeclaration) {
|
||||
topLevelDeclarations.add(declaration.name.name);
|
||||
} else if (declaration is TopLevelVariableDeclaration) {
|
||||
for (var variable in declaration.variables.variables) {
|
||||
topLevelDeclarations.add(variable.name.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return UnlinkedUnit(
|
||||
apiSignature: Uint8List.fromList(computeUnlinkedApiSignature(unit)),
|
||||
augmentations: augmentations,
|
||||
|
@ -906,6 +1014,7 @@ class FileState {
|
|||
parts: parts,
|
||||
partOfNameDirective: partOfNameDirective,
|
||||
partOfUriDirective: partOfUriDirective,
|
||||
topLevelDeclarations: topLevelDeclarations,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1022,12 +1131,16 @@ class FileSystemState {
|
|||
/// The value of this field is incremented when the set of files is updated.
|
||||
int fileStamp = 0;
|
||||
|
||||
/// The cache of content of files, possibly shared with other file system
|
||||
/// states.
|
||||
final FileContentCache _fileContentCache;
|
||||
final FileContentStrategy fileContentStrategy;
|
||||
|
||||
/// A function that fetches the given list of files. This function can be used
|
||||
/// to batch file reads in systems where file fetches are expensive.
|
||||
final void Function(List<String> paths)? prefetchFiles;
|
||||
|
||||
late final FileSystemStateTestView _testView;
|
||||
|
||||
FileSystemTestData? testData;
|
||||
|
||||
FileSystemState(
|
||||
this._logger,
|
||||
this._byteStore,
|
||||
|
@ -1041,8 +1154,9 @@ class FileSystemState {
|
|||
this._saltForUnlinked,
|
||||
this._saltForElements,
|
||||
this.featureSetProvider, {
|
||||
required FileContentCache fileContentCache,
|
||||
}) : _fileContentCache = fileContentCache {
|
||||
required this.fileContentStrategy,
|
||||
required this.prefetchFiles,
|
||||
}) {
|
||||
_testView = FileSystemStateTestView(this);
|
||||
}
|
||||
|
||||
|
@ -1051,6 +1165,29 @@ class FileSystemState {
|
|||
@visibleForTesting
|
||||
FileSystemStateTestView get test => _testView;
|
||||
|
||||
/// Update the state to reflect the fact that the file with the given [path]
|
||||
/// was changed. Specifically this means that we evict this file and every
|
||||
/// file that referenced it.
|
||||
void changeFile(String path, List<FileState> removedFiles) {
|
||||
var file = _pathToFile.remove(path);
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
removedFiles.add(file);
|
||||
_uriToFile.remove(file.uri);
|
||||
|
||||
// The removed file does not reference other file anymore.
|
||||
for (var referencedFile in file.directReferencedFiles) {
|
||||
referencedFile.referencingFiles.remove(file);
|
||||
}
|
||||
|
||||
// Recursively remove files that reference the removed file.
|
||||
for (var reference in file.referencingFiles.toList()) {
|
||||
changeFile(reference.path, removedFiles);
|
||||
}
|
||||
}
|
||||
|
||||
/// Collected files that transitively reference a file with the [path].
|
||||
/// These files are potentially affected by the change.
|
||||
void collectAffected(String path, Set<FileState> affected) {
|
||||
|
@ -1104,9 +1241,40 @@ class FileSystemState {
|
|||
return featureSetProvider.getLanguageVersion(path, uri);
|
||||
}
|
||||
|
||||
/// Notifies this object that it is about to be discarded.
|
||||
///
|
||||
/// Returns the keys of the artifacts that are no longer used.
|
||||
Set<String> dispose() {
|
||||
final result = <String>{};
|
||||
for (final file in _pathToFile.values) {
|
||||
result.add(file._unlinkedKey!);
|
||||
}
|
||||
_pathToFile.clear();
|
||||
_uriToFile.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
FileState? getExistingFileForResource(File file) {
|
||||
return _pathToFile[file.path];
|
||||
}
|
||||
|
||||
/// Return the [FileState] for the given absolute [path]. The returned file
|
||||
/// has the last known state since if was last refreshed.
|
||||
/// TODO(scheglov) Merge with [getFileForPath2].
|
||||
FileState getFileForPath(String path) {
|
||||
return getFileForPath2(
|
||||
path: path,
|
||||
performance: OperationPerformanceImpl('<root>'),
|
||||
);
|
||||
}
|
||||
|
||||
/// Return the [FileState] for the given absolute [path]. The returned file
|
||||
/// has the last known state since if was last refreshed.
|
||||
FileState getFileForPath2({
|
||||
required String path,
|
||||
required OperationPerformanceImpl performance,
|
||||
}) {
|
||||
var file = _pathToFile[path];
|
||||
if (file == null) {
|
||||
File resource = _resourceProvider.getFile(path);
|
||||
|
@ -1164,12 +1332,40 @@ class FileSystemState {
|
|||
return Either2.t1(file);
|
||||
}
|
||||
|
||||
/// Returns a list of files whose contents contains the given string.
|
||||
/// Generated files are not included in the search.
|
||||
List<String> getFilesContaining(String value) {
|
||||
var result = <String>[];
|
||||
_pathToFile.forEach((path, file) {
|
||||
// TODO(scheglov) Exclude generated files.
|
||||
// var genFile = isGenerated == null ? false : isGenerated!(path);
|
||||
// if (!genFile && file.getContent().contains(value)) {
|
||||
// result.add(path);
|
||||
// }
|
||||
if (file.getContent().contains(value)) {
|
||||
result.add(path);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Return files where the given [name] is subtyped, i.e. used in `extends`,
|
||||
/// `with` or `implements` clauses.
|
||||
Set<FileState>? getFilesSubtypingName(String name) {
|
||||
return _subtypedNameToFiles[name];
|
||||
}
|
||||
|
||||
/// Return files that have a top-level declaration with the [name].
|
||||
List<FileState> getFilesWithTopLevelDeclarations(String name) {
|
||||
final result = <FileState>[];
|
||||
for (final file in _pathToFile.values) {
|
||||
if (file.unlinked2.topLevelDeclarations.contains(name)) {
|
||||
result.add(file);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Return `true` if there is a URI that can be resolved to the [path].
|
||||
///
|
||||
/// When a file exists, but for the URI that corresponds to the file is
|
||||
|
@ -1186,18 +1382,34 @@ class FileSystemState {
|
|||
return flag;
|
||||
}
|
||||
|
||||
/// The file with the given [path] might have changed, so ensure that it is
|
||||
/// read the next time it is refreshed.
|
||||
void markFileForReading(String path) {
|
||||
_fileContentCache.invalidate(path);
|
||||
}
|
||||
|
||||
/// Remove the file with the given [path].
|
||||
void removeFile(String path) {
|
||||
markFileForReading(path);
|
||||
_clearFiles();
|
||||
}
|
||||
|
||||
/// Computes the set of [FileState]'s used/not used to analyze the given
|
||||
/// [files]. Removes the [FileState]'s of the files not used for analysis from
|
||||
/// the cache. Returns the set of unused [FileState]'s.
|
||||
List<FileState> removeUnusedFiles(List<String> files) {
|
||||
var allReferenced = <String>{};
|
||||
for (var path in files) {
|
||||
allReferenced.add(path);
|
||||
_pathToFile[path]?.collectAllReferencedFiles(allReferenced);
|
||||
}
|
||||
|
||||
var unusedPaths = _pathToFile.keys.toSet();
|
||||
unusedPaths.removeAll(allReferenced);
|
||||
|
||||
var removedFiles = <FileState>[];
|
||||
for (var path in unusedPaths) {
|
||||
var file = _pathToFile.remove(path)!;
|
||||
_uriToFile.remove(file.uri);
|
||||
removedFiles.add(file);
|
||||
}
|
||||
|
||||
return removedFiles;
|
||||
}
|
||||
|
||||
/// Clear all [FileState] data - all maps from path or URI, etc.
|
||||
void _clearFiles() {
|
||||
_uriToFile.clear();
|
||||
|
@ -1240,6 +1452,26 @@ class FileSystemStateTestView {
|
|||
}
|
||||
}
|
||||
|
||||
class FileSystemTestData {
|
||||
final Map<File, FileTestData> files = {};
|
||||
|
||||
FileTestData forFile(File file) {
|
||||
return files[file] ??= FileTestData._(file);
|
||||
}
|
||||
}
|
||||
|
||||
class FileTestData {
|
||||
final File file;
|
||||
|
||||
/// We add the key every time we get unlinked data from the byte store.
|
||||
final List<String> unlinkedKeyGet = [];
|
||||
|
||||
/// We add the key every time we put unlinked data into the byte store.
|
||||
final List<String> unlinkedKeyPut = [];
|
||||
|
||||
FileTestData._(this.file);
|
||||
}
|
||||
|
||||
/// Precomputed properties of a file URI, used because [Uri] is relatively
|
||||
/// expensive to work with, if we do this thousand times.
|
||||
class FileUriProperties {
|
||||
|
@ -1471,6 +1703,8 @@ class PartOfNameFileStateKind extends PartFileStateKind {
|
|||
/// first one as if sorted by path.
|
||||
@override
|
||||
LibraryFileStateKind? get library {
|
||||
_discoverLibraries();
|
||||
|
||||
LibraryFileStateKind? result;
|
||||
for (final library in libraries) {
|
||||
if (library.hasPart(this)) {
|
||||
|
@ -1483,6 +1717,27 @@ class PartOfNameFileStateKind extends PartFileStateKind {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void _discoverLibraries() {
|
||||
if (libraries.isEmpty) {
|
||||
var resourceProvider = file._fsState._resourceProvider;
|
||||
var pathContext = resourceProvider.pathContext;
|
||||
|
||||
var siblings = <Resource>[];
|
||||
try {
|
||||
siblings = file.resource.parent.getChildren();
|
||||
} catch (_) {}
|
||||
|
||||
for (final sibling in siblings) {
|
||||
if (file_paths.isDart(pathContext, sibling.path)) {
|
||||
file._fsState.getFileForPath2(
|
||||
path: sibling.path,
|
||||
performance: OperationPerformanceImpl('<root>'),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The file has `part of URI` directive.
|
||||
|
@ -1528,6 +1783,45 @@ class PartOfUriUnknownFileStateKind extends PartOfUriFileStateKind {
|
|||
LibraryFileStateKind? get library => null;
|
||||
}
|
||||
|
||||
class StoredFileContent implements FileContent {
|
||||
@override
|
||||
final String content;
|
||||
|
||||
@override
|
||||
final String contentHash;
|
||||
|
||||
@override
|
||||
final bool exists;
|
||||
|
||||
StoredFileContent({
|
||||
required this.content,
|
||||
required this.contentHash,
|
||||
required this.exists,
|
||||
});
|
||||
}
|
||||
|
||||
class StoredFileContentStrategy implements FileContentStrategy {
|
||||
final FileContentCache _fileContentCache;
|
||||
|
||||
StoredFileContentStrategy(this._fileContentCache);
|
||||
|
||||
@override
|
||||
FileContent get(String path) {
|
||||
final fileContent = _fileContentCache.get(path);
|
||||
return StoredFileContent(
|
||||
content: fileContent.content,
|
||||
contentHash: fileContent.contentHash,
|
||||
exists: fileContent.exists,
|
||||
);
|
||||
}
|
||||
|
||||
/// The file with the given [path] might have changed, so ensure that it is
|
||||
/// read the next time it is refreshed.
|
||||
void markFileForReading(String path) {
|
||||
_fileContentCache.invalidate(path);
|
||||
}
|
||||
}
|
||||
|
||||
class _LibraryNameToFiles {
|
||||
final Map<String, List<LibraryFileStateKind>> _map = {};
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ class FileTracker {
|
|||
/// The current file system state.
|
||||
final FileSystemState _fsState;
|
||||
|
||||
final StoredFileContentStrategy _fileContentStrategy;
|
||||
|
||||
/// The set of added files.
|
||||
final addedFiles = <String>{};
|
||||
|
||||
|
@ -45,7 +47,7 @@ class FileTracker {
|
|||
/// have any special relation with changed files.
|
||||
var _pendingFiles = <String>{};
|
||||
|
||||
FileTracker(this._logger, this._fsState);
|
||||
FileTracker(this._logger, this._fsState, this._fileContentStrategy);
|
||||
|
||||
/// Returns the path to exactly one that needs analysis. Throws a
|
||||
/// [StateError] if no files need analysis.
|
||||
|
@ -108,7 +110,7 @@ class FileTracker {
|
|||
|
||||
/// Adds the given [path] to the set of "changed files".
|
||||
void changeFile(String path) {
|
||||
_fsState.markFileForReading(path);
|
||||
_fileContentStrategy.markFileForReading(path);
|
||||
_changedFiles.add(path);
|
||||
|
||||
if (addedFiles.contains(path)) {
|
||||
|
@ -137,6 +139,7 @@ class FileTracker {
|
|||
|
||||
/// Removes the given [path] from the set of "added files".
|
||||
void removeFile(String path) {
|
||||
_fileContentStrategy.markFileForReading(path);
|
||||
addedFiles.remove(path);
|
||||
_pendingChangedFiles.remove(path);
|
||||
_pendingImportFiles.remove(path);
|
||||
|
|
|
@ -52,6 +52,10 @@ class LibraryCycle {
|
|||
/// include [implSignature] of the macro defining library.
|
||||
String implSignature;
|
||||
|
||||
/// The key of the resolution cache entry.
|
||||
/// TODO(scheglov) clean up
|
||||
String? resolutionKey;
|
||||
|
||||
late final bool hasMacroClass = () {
|
||||
for (final library in libraries) {
|
||||
for (final file in library.libraryFiles) {
|
||||
|
|
|
@ -343,6 +343,9 @@ class UnlinkedUnit {
|
|||
/// The `part of 'uri';` directive.
|
||||
final UnlinkedPartOfUriDirective? partOfUriDirective;
|
||||
|
||||
/// Top-level declarations of the unit.
|
||||
final Set<String> topLevelDeclarations;
|
||||
|
||||
UnlinkedUnit({
|
||||
required this.apiSignature,
|
||||
required this.augmentations,
|
||||
|
@ -356,6 +359,7 @@ class UnlinkedUnit {
|
|||
required this.parts,
|
||||
required this.partOfNameDirective,
|
||||
required this.partOfUriDirective,
|
||||
required this.topLevelDeclarations,
|
||||
});
|
||||
|
||||
factory UnlinkedUnit.read(SummaryDataReader reader) {
|
||||
|
@ -388,6 +392,7 @@ class UnlinkedUnit {
|
|||
partOfUriDirective: reader.readOptionalObject(
|
||||
UnlinkedPartOfUriDirective.read,
|
||||
),
|
||||
topLevelDeclarations: reader.readStringUtf8Set(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -424,5 +429,6 @@ class UnlinkedUnit {
|
|||
partOfUriDirective,
|
||||
(x) => x.write(sink),
|
||||
);
|
||||
sink.writeStringUtf8Iterable(topLevelDeclarations);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:analyzer/dart/element/element.dart';
|
|||
import 'package:analyzer/error/error.dart';
|
||||
import 'package:analyzer/error/listener.dart';
|
||||
import 'package:analyzer/src/context/source.dart';
|
||||
import 'package:analyzer/src/dart/analysis/file_state.dart';
|
||||
import 'package:analyzer/src/dart/ast/ast.dart';
|
||||
import 'package:analyzer/src/dart/ast/utilities.dart';
|
||||
import 'package:analyzer/src/dart/constant/compute.dart';
|
||||
|
@ -19,7 +20,6 @@ import 'package:analyzer/src/dart/element/element.dart';
|
|||
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
|
||||
import 'package:analyzer/src/dart/element/type_provider.dart';
|
||||
import 'package:analyzer/src/dart/element/type_system.dart';
|
||||
import 'package:analyzer/src/dart/micro/library_graph.dart';
|
||||
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
|
||||
import 'package:analyzer/src/dart/resolver/legacy_type_asserter.dart';
|
||||
import 'package:analyzer/src/dart/resolver/resolution_visitor.dart';
|
||||
|
@ -106,7 +106,8 @@ class LibraryAnalyzer {
|
|||
|
||||
// Parse all files.
|
||||
performance.run('parse', (performance) {
|
||||
for (FileState file in _library.files().ofLibrary) {
|
||||
final libraryKind = _library.kind.asLibrary;
|
||||
for (FileState file in libraryKind.file.libraryFiles) {
|
||||
if (completionPath == null || file.path == completionPath) {
|
||||
units[file] = _parse(
|
||||
file: file,
|
||||
|
@ -227,21 +228,24 @@ class LibraryAnalyzer {
|
|||
});
|
||||
}
|
||||
|
||||
final libraryKind = _library.kind.asLibrary;
|
||||
final libraryFiles = libraryKind.file.libraryFiles.toList();
|
||||
|
||||
if (_analysisOptions.lint) {
|
||||
performance.run('computeLints', (performance) {
|
||||
var allUnits = _library.files().ofLibrary.map((file) {
|
||||
var allUnits = libraryFiles.map((file) {
|
||||
var content = getFileContent(file);
|
||||
return LinterContextUnit(content, units[file]!);
|
||||
}).toList();
|
||||
for (int i = 0; i < allUnits.length; i++) {
|
||||
_computeLints(_library.files().ofLibrary[i], allUnits[i], allUnits);
|
||||
_computeLints(libraryFiles[i], allUnits[i], allUnits);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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().ofLibrary) {
|
||||
for (var file in libraryFiles) {
|
||||
IgnoreValidator(
|
||||
_getErrorReporter(file),
|
||||
_getErrorListener(file).errors,
|
||||
|
@ -475,7 +479,8 @@ class LibraryAnalyzer {
|
|||
}
|
||||
|
||||
bool _isExistingSource(Source source) {
|
||||
for (var file in _library.files().directReferencedFiles) {
|
||||
final libraryKind = _library.kind.asLibrary;
|
||||
for (var file in libraryKind.file.directReferencedFiles) {
|
||||
if (file.uri == source.uri) {
|
||||
return file.exists;
|
||||
}
|
||||
|
@ -499,7 +504,7 @@ class LibraryAnalyzer {
|
|||
performance.getDataInt('length').add(content.length);
|
||||
|
||||
AnalysisErrorListener errorListener = _getErrorListener(file);
|
||||
var unit = file.parse(errorListener, content);
|
||||
var unit = file.parse2(errorListener, content);
|
||||
|
||||
LineInfo lineInfo = unit.lineInfo;
|
||||
_fileToLineInfo[file] = lineInfo;
|
||||
|
@ -544,6 +549,8 @@ class LibraryAnalyzer {
|
|||
return;
|
||||
}
|
||||
|
||||
final libraryKind = _library.kind.asLibrary;
|
||||
|
||||
var definingCompilationUnit = units[_library]!;
|
||||
definingCompilationUnit.element = _libraryElement.definingCompilationUnit;
|
||||
|
||||
|
@ -598,7 +605,7 @@ class LibraryAnalyzer {
|
|||
} else if (directive is PartDirectiveImpl) {
|
||||
StringLiteral partUri = directive.uri;
|
||||
|
||||
FileState partFile = _library.files().parted[partIndex];
|
||||
FileState partFile = libraryKind.file.partedFiles[partIndex]!;
|
||||
var partUnit = units[partFile]!;
|
||||
CompilationUnitElement partElement = _libraryElement.parts[partIndex];
|
||||
partUnit.element = partElement;
|
||||
|
|
|
@ -1045,6 +1045,7 @@ class _FileStateUnlinked {
|
|||
parts: parts,
|
||||
partOfNameDirective: partOfNameDirective,
|
||||
partOfUriDirective: partOfUriDirective,
|
||||
topLevelDeclarations: topLevelDeclarations,
|
||||
);
|
||||
|
||||
return CiderUnlinkedUnit(
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import 'dart:collection';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:analyzer/dart/analysis/declared_variables.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/error/error.dart';
|
||||
|
@ -19,14 +20,14 @@ import 'package:analyzer/src/dart/analysis/context_root.dart';
|
|||
import 'package:analyzer/src/dart/analysis/driver.dart' show ErrorEncoding;
|
||||
import 'package:analyzer/src/dart/analysis/experiments.dart';
|
||||
import 'package:analyzer/src/dart/analysis/feature_set_provider.dart';
|
||||
import 'package:analyzer/src/dart/analysis/file_state.dart';
|
||||
import 'package:analyzer/src/dart/analysis/library_graph.dart';
|
||||
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
|
||||
import 'package:analyzer/src/dart/analysis/results.dart';
|
||||
import 'package:analyzer/src/dart/analysis/search.dart';
|
||||
import 'package:analyzer/src/dart/micro/analysis_context.dart';
|
||||
import 'package:analyzer/src/dart/micro/library_analyzer.dart';
|
||||
import 'package:analyzer/src/dart/micro/library_graph.dart';
|
||||
import 'package:analyzer/src/dart/micro/utils.dart';
|
||||
import 'package:analyzer/src/exception/exception.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/summary/api_signature.dart';
|
||||
|
@ -43,6 +44,75 @@ import 'package:collection/collection.dart';
|
|||
import 'package:meta/meta.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
class CiderFileContent implements FileContent {
|
||||
final CiderFileContentStrategy strategy;
|
||||
final String path;
|
||||
final String digestStr;
|
||||
|
||||
CiderFileContent({
|
||||
required this.strategy,
|
||||
required this.path,
|
||||
required this.digestStr,
|
||||
});
|
||||
|
||||
@override
|
||||
String get content {
|
||||
final contentWithDigest = _getContent();
|
||||
|
||||
if (contentWithDigest.digestStr != digestStr) {
|
||||
throw StateError('File was changed, but not invalidated: $path');
|
||||
}
|
||||
|
||||
return contentWithDigest.content;
|
||||
}
|
||||
|
||||
@override
|
||||
String get contentHash => digestStr;
|
||||
|
||||
@override
|
||||
bool get exists => digestStr.isNotEmpty;
|
||||
|
||||
_ContentWithDigest _getContent() {
|
||||
String content;
|
||||
try {
|
||||
final file = strategy.resourceProvider.getFile(path);
|
||||
content = file.readAsStringSync();
|
||||
} catch (_) {
|
||||
content = '';
|
||||
}
|
||||
|
||||
final digestStr = strategy.getFileDigest(path);
|
||||
return _ContentWithDigest(
|
||||
content: content,
|
||||
digestStr: digestStr,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CiderFileContentStrategy implements FileContentStrategy {
|
||||
final ResourceProvider resourceProvider;
|
||||
|
||||
/// A function that returns the digest for a file as a String. The function
|
||||
/// returns a non null value, returns an empty string if file does
|
||||
/// not exist/has no contents.
|
||||
final String Function(String path) getFileDigest;
|
||||
|
||||
CiderFileContentStrategy({
|
||||
required this.resourceProvider,
|
||||
required this.getFileDigest,
|
||||
});
|
||||
|
||||
@override
|
||||
CiderFileContent get(String path) {
|
||||
final digestStr = getFileDigest(path);
|
||||
return CiderFileContent(
|
||||
strategy: this,
|
||||
path: path,
|
||||
digestStr: digestStr,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CiderSearchInfo {
|
||||
final CharacterLocation startPosition;
|
||||
final int length;
|
||||
|
@ -276,8 +346,8 @@ class FileResolver {
|
|||
var file = fileContext.file;
|
||||
|
||||
final errorsSignatureBuilder = ApiSignature();
|
||||
errorsSignatureBuilder.addBytes(file.libraryCycle.signature);
|
||||
errorsSignatureBuilder.addBytes(file.digest);
|
||||
errorsSignatureBuilder.addString(file.libraryCycle.apiSignature);
|
||||
errorsSignatureBuilder.addString(file.contentHash);
|
||||
final errorsKey = '${errorsSignatureBuilder.toHex()}.errors';
|
||||
|
||||
final List<AnalysisError> errors;
|
||||
|
@ -330,7 +400,7 @@ class FileResolver {
|
|||
});
|
||||
|
||||
var file = performance.run('fileForPath', (performance) {
|
||||
return fsState!.getFileForPath(
|
||||
return fsState!.getFileForPath2(
|
||||
path: path,
|
||||
performance: performance,
|
||||
);
|
||||
|
@ -368,7 +438,8 @@ class FileResolver {
|
|||
);
|
||||
var file = fileContext.file;
|
||||
|
||||
if (file.partOfLibrary != null) {
|
||||
final kind = file.kind;
|
||||
if (kind is! LibraryFileStateKind) {
|
||||
throw ArgumentError('$uri is not a library.');
|
||||
}
|
||||
|
||||
|
@ -388,12 +459,12 @@ class FileResolver {
|
|||
}) {
|
||||
_throwIfNotAbsoluteNormalizedPath(path);
|
||||
|
||||
var file = fsState!.getFileForPath(
|
||||
var file = fsState!.getFileForPath2(
|
||||
path: path,
|
||||
performance: performance,
|
||||
);
|
||||
|
||||
return file.libraryCycle.signatureStr;
|
||||
return file.libraryCycle.apiSignature;
|
||||
}
|
||||
|
||||
/// Ensure that libraries necessary for resolving [path] are linked.
|
||||
|
@ -427,7 +498,7 @@ class FileResolver {
|
|||
performance: performance,
|
||||
);
|
||||
var file = fileContext.file;
|
||||
var libraryFile = file.partOfLibrary ?? file;
|
||||
var libraryFile = file.kind.library!.file;
|
||||
|
||||
// Load the library, link if necessary.
|
||||
await libraryContext!.load(
|
||||
|
@ -486,15 +557,17 @@ class FileResolver {
|
|||
);
|
||||
var file = fileContext.file;
|
||||
|
||||
// If we have a `part of` directive, we want to analyze this library.
|
||||
// But the library must include the file, so have its element.
|
||||
var libraryFile = file;
|
||||
var partOfLibrary = file.partOfLibrary;
|
||||
if (partOfLibrary != null) {
|
||||
if (partOfLibrary.files().ofLibrary.contains(file)) {
|
||||
libraryFile = partOfLibrary;
|
||||
}
|
||||
}
|
||||
// // If we have a `part of` directive, we want to analyze this library.
|
||||
// // But the library must include the file, so have its element.
|
||||
// var libraryFile = file;
|
||||
// var partOfLibrary = file.partOfLibrary;
|
||||
// if (partOfLibrary != null) {
|
||||
// if (partOfLibrary.files().ofLibrary.contains(file)) {
|
||||
// libraryFile = partOfLibrary;
|
||||
// }
|
||||
// }
|
||||
final libraryKind = file.kind.library ?? file.kind.asLibrary;
|
||||
final libraryFile = libraryKind.file;
|
||||
|
||||
var libraryResult = await resolveLibrary2(
|
||||
completionLine: completionLine,
|
||||
|
@ -533,15 +606,17 @@ class FileResolver {
|
|||
);
|
||||
var file = fileContext.file;
|
||||
|
||||
// If we have a `part of` directive, we want to analyze this library.
|
||||
// But the library must include the file, so have its element.
|
||||
var libraryFile = file;
|
||||
var partOfLibrary = file.partOfLibrary;
|
||||
if (partOfLibrary != null) {
|
||||
if (partOfLibrary.files().ofLibrary.contains(file)) {
|
||||
libraryFile = partOfLibrary;
|
||||
}
|
||||
}
|
||||
// // If we have a `part of` directive, we want to analyze this library.
|
||||
// // But the library must include the file, so have its element.
|
||||
// var libraryFile = file;
|
||||
// var partOfLibrary = file.partOfLibrary;
|
||||
// if (partOfLibrary != null) {
|
||||
// if (partOfLibrary.files().ofLibrary.contains(file)) {
|
||||
// libraryFile = partOfLibrary;
|
||||
// }
|
||||
// }
|
||||
final libraryKind = file.kind.library ?? file.kind.asLibrary;
|
||||
final libraryFile = libraryKind.file;
|
||||
|
||||
int? completionOffset;
|
||||
if (completionLine != null && completionColumn != null) {
|
||||
|
@ -573,26 +648,13 @@ class FileResolver {
|
|||
(file) => file.getContent(),
|
||||
);
|
||||
|
||||
try {
|
||||
results = performance!.run('analyze', (performance) {
|
||||
return libraryAnalyzer.analyze(
|
||||
completionPath: completionOffset != null ? completionPath : null,
|
||||
completionOffset: completionOffset,
|
||||
performance: performance,
|
||||
);
|
||||
});
|
||||
} catch (exception, stackTrace) {
|
||||
var fileContentMap = <String, String>{};
|
||||
for (var file in libraryFile.files().ofLibrary) {
|
||||
var path = file.path;
|
||||
fileContentMap[path] = _getFileContent(path);
|
||||
}
|
||||
throw CaughtExceptionWithFiles(
|
||||
exception,
|
||||
stackTrace,
|
||||
fileContentMap,
|
||||
results = performance!.run('analyze', (performance) {
|
||||
return libraryAnalyzer.analyze(
|
||||
completionPath: completionOffset != null ? completionPath : null,
|
||||
completionOffset: completionOffset,
|
||||
performance: performance,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var resolvedUnits = results.values.map((fileResult) {
|
||||
|
@ -655,15 +717,24 @@ class FileResolver {
|
|||
);
|
||||
|
||||
fsState = FileSystemState(
|
||||
resourceProvider,
|
||||
logger,
|
||||
byteStore,
|
||||
resourceProvider,
|
||||
'contextName',
|
||||
sourceFactory,
|
||||
workspace,
|
||||
Uint32List(0), // linkedSalt
|
||||
AnalysisOptionsImpl(), // TODO(scheglov) remove it
|
||||
DeclaredVariables.fromMap({}),
|
||||
Uint32List(0), // _saltForUnlinked
|
||||
Uint32List(0), // _saltForElements
|
||||
featureSetProvider,
|
||||
getFileDigest,
|
||||
prefetchFiles,
|
||||
isGenerated,
|
||||
fileContentStrategy: CiderFileContentStrategy(
|
||||
resourceProvider: resourceProvider,
|
||||
getFileDigest: getFileDigest,
|
||||
),
|
||||
prefetchFiles: prefetchFiles,
|
||||
// TODO(scheglov) use these
|
||||
// isGenerated,
|
||||
)..testData = testView?.fileSystemTestData;
|
||||
}
|
||||
|
||||
|
@ -779,15 +850,6 @@ class FileResolver {
|
|||
return options;
|
||||
}
|
||||
|
||||
/// Return the file content, the empty string if any exception.
|
||||
String _getFileContent(String path) {
|
||||
try {
|
||||
return resourceProvider.getFile(path).readAsStringSync();
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<CiderSearchMatch>> _searchReferences_Import(
|
||||
ImportElement element) async {
|
||||
var results = <CiderSearchMatch>[];
|
||||
|
@ -897,14 +959,13 @@ class LibraryContext {
|
|||
await loadBundle(directDependency);
|
||||
}
|
||||
|
||||
var resolutionKey = '${cycle.signatureStr}.resolution';
|
||||
var resolutionKey = '${cycle.apiSignature}.linked_bundle';
|
||||
var resolutionBytes = byteStore.get(resolutionKey);
|
||||
|
||||
var unitsInformativeBytes = <Uri, Uint8List>{};
|
||||
for (var library in cycle.libraries) {
|
||||
for (var file in library.files().ofLibrary) {
|
||||
var informativeBytes = file.unlinkedUnit.informativeBytes;
|
||||
unitsInformativeBytes[file.uri] = informativeBytes;
|
||||
for (var file in library.libraryFiles) {
|
||||
unitsInformativeBytes[file.uri] = file.unlinked2.informativeBytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -918,21 +979,21 @@ class LibraryContext {
|
|||
|
||||
var inputUnits = <LinkInputUnit>[];
|
||||
var partIndex = -1;
|
||||
for (var file in libraryFile.files().ofLibrary) {
|
||||
for (var file in libraryFile.libraryFiles) {
|
||||
var isSynthetic = !file.exists;
|
||||
|
||||
var content = file.getContent();
|
||||
performance.getDataInt('parseCount').increment();
|
||||
performance.getDataInt('parseLength').add(content.length);
|
||||
|
||||
var unit = file.parse(
|
||||
var unit = file.parse2(
|
||||
AnalysisErrorListener.NULL_LISTENER,
|
||||
content,
|
||||
);
|
||||
|
||||
String? partUriStr;
|
||||
if (partIndex >= 0) {
|
||||
partUriStr = libraryFile.unlinkedUnit.parts[partIndex];
|
||||
partUriStr = libraryFile.unlinked2.parts[partIndex];
|
||||
}
|
||||
partIndex++;
|
||||
|
||||
|
@ -1069,3 +1130,13 @@ class LibraryCycleTestData {
|
|||
final List<String> getKeys = [];
|
||||
final List<String> putKeys = [];
|
||||
}
|
||||
|
||||
class _ContentWithDigest {
|
||||
final String content;
|
||||
final String digestStr;
|
||||
|
||||
_ContentWithDigest({
|
||||
required this.content,
|
||||
required this.digestStr,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1136,10 +1136,11 @@ library my;
|
|||
});
|
||||
}
|
||||
|
||||
/// TODO(scheglov) Test discovery of a sibling library
|
||||
test_newFile_partOfName() async {
|
||||
final a = newFile('$testPackageLibPath/a.dart', r'''
|
||||
final a = newFile('$testPackageLibPath/nested/a.dart', r'''
|
||||
library my.lib;
|
||||
part 'b.dart';
|
||||
part '../b.dart';
|
||||
''');
|
||||
|
||||
final b = newFile('$testPackageLibPath/b.dart', r'''
|
||||
|
@ -2239,7 +2240,10 @@ class FileSystemStateTest with ResourceProviderMixin {
|
|||
Uint32List(0),
|
||||
Uint32List(0),
|
||||
featureSetProvider,
|
||||
fileContentCache: FileContentCache.ephemeral(resourceProvider),
|
||||
fileContentStrategy: StoredFileContentStrategy(
|
||||
FileContentCache.ephemeral(resourceProvider),
|
||||
),
|
||||
prefetchFiles: null,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ import 'dart:convert';
|
|||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/src/dart/analysis/byte_store.dart';
|
||||
import 'package:analyzer/src/dart/analysis/file_state.dart';
|
||||
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
|
||||
import 'package:analyzer/src/dart/micro/library_graph.dart';
|
||||
import 'package:analyzer/src/dart/micro/resolve_file.dart';
|
||||
import 'package:analyzer/src/dart/sdk/sdk.dart';
|
||||
import 'package:analyzer/src/test_utilities/find_element.dart';
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/source/line_info.dart';
|
||||
import 'package:analyzer/src/dart/analysis/file_state.dart';
|
||||
import 'package:analyzer/src/dart/ast/utilities.dart';
|
||||
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
|
||||
import 'package:analyzer/src/dart/micro/resolve_file.dart';
|
||||
|
@ -362,9 +363,12 @@ class B {
|
|||
}
|
||||
''');
|
||||
|
||||
expect(() async {
|
||||
try {
|
||||
await resolveFile(a.path);
|
||||
}, throwsStateError);
|
||||
fail('Expected StateError');
|
||||
} on StateError {
|
||||
// OK
|
||||
}
|
||||
|
||||
// Notify the resolver about b.dart, it is OK now.
|
||||
fileResolver.changeFiles([b.path]);
|
||||
|
@ -2465,8 +2469,7 @@ byteStore
|
|||
}
|
||||
|
||||
test_resolve_libraryWithPart_noLibraryDiscovery() async {
|
||||
var partPath = '/workspace/dart/test/lib/a.dart';
|
||||
newFile(partPath, r'''
|
||||
newFile('/workspace/dart/test/lib/a.dart', r'''
|
||||
part of 'test.dart';
|
||||
|
||||
class A {}
|
||||
|
@ -2480,11 +2483,12 @@ void f(A a) {}
|
|||
|
||||
// We started resolution from the library, and then followed to the part.
|
||||
// So, the part knows its library, there is no need to discover it.
|
||||
_assertDiscoveredLibraryForParts([]);
|
||||
// TODO(scheglov) Use textual dump
|
||||
// _assertDiscoveredLibraryForParts([]);
|
||||
}
|
||||
|
||||
test_resolve_part_of_name() async {
|
||||
newFile('/workspace/dart/test/lib/a.dart', r'''
|
||||
final a = newFile('/workspace/dart/test/lib/a.dart', r'''
|
||||
library my.lib;
|
||||
|
||||
part 'test.dart';
|
||||
|
@ -2503,11 +2507,15 @@ void func() {
|
|||
}
|
||||
''');
|
||||
|
||||
_assertDiscoveredLibraryForParts([result.path]);
|
||||
// TODO(scheglov) Use textual dump
|
||||
final fsState = fileResolver.fsState!;
|
||||
final testState = fsState.getExistingFileForResource(testFile)!;
|
||||
final testKind = testState.kind as PartFileStateKind;
|
||||
expect(testKind.library?.file, fsState.getExistingFileForResource(a));
|
||||
}
|
||||
|
||||
test_resolve_part_of_uri() async {
|
||||
newFile('/workspace/dart/test/lib/a.dart', r'''
|
||||
final a = newFile('/workspace/dart/test/lib/a.dart', r'''
|
||||
part 'test.dart';
|
||||
|
||||
class A {
|
||||
|
@ -2524,7 +2532,11 @@ void func() {
|
|||
}
|
||||
''');
|
||||
|
||||
_assertDiscoveredLibraryForParts([result.path]);
|
||||
// TODO(scheglov) Use textual dump
|
||||
final fsState = fileResolver.fsState!;
|
||||
final testState = fsState.getExistingFileForResource(testFile)!;
|
||||
final testKind = testState.kind as PartFileStateKind;
|
||||
expect(testKind.library?.file, fsState.getExistingFileForResource(a));
|
||||
}
|
||||
|
||||
test_resolveFile_cache() async {
|
||||
|
@ -2699,10 +2711,6 @@ import 'foo:bar';
|
|||
]);
|
||||
}
|
||||
|
||||
void _assertDiscoveredLibraryForParts(List<String> expected) {
|
||||
expect(fileResolver.fsState!.testView.partsDiscoveredLibraries, expected);
|
||||
}
|
||||
|
||||
void _assertResolvedFiles(
|
||||
List<File> expected, {
|
||||
bool andClear = true,
|
||||
|
|
Loading…
Reference in a new issue