mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
Backport AnalysisSession.getResolvedLibrary()/ByElement().
Change-Id: I83c1dbe9d758be12212fcd892bb57ae69407d8be Reviewed-on: https://dart-review.googlesource.com/c/85147 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
db89c2d1ac
commit
232aa553fb
|
@ -91,6 +91,21 @@ abstract class AnalysisSession {
|
|||
*/
|
||||
Future<ResolveResult> getResolvedAst(String path);
|
||||
|
||||
/// Return a future that will complete with information about the results of
|
||||
/// resolving all of the files in the library with the given absolute,
|
||||
/// normalized [path].
|
||||
///
|
||||
/// Throw [ArgumentError] if the given [path] is not the defining compilation
|
||||
/// unit for a library (that is, is a part of a library).
|
||||
Future<ResolvedLibraryResult> getResolvedLibrary(String path);
|
||||
|
||||
/// Return a future that will complete with information about the results of
|
||||
/// resolving all of the files in the library with the library [element].
|
||||
///
|
||||
/// Throw [ArgumentError] if the [element] was not produced by this session.
|
||||
Future<ResolvedLibraryResult> getResolvedLibraryByElement(
|
||||
LibraryElement element);
|
||||
|
||||
/**
|
||||
* Return a future that will complete with the source kind of the file with
|
||||
* the given absolute, normalized [path]. If the path does not represent a
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'dart:typed_data';
|
|||
|
||||
import 'package:analyzer/dart/analysis/declared_variables.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart' as results;
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer/dart/analysis/session.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/element/element.dart'
|
||||
|
@ -24,6 +25,7 @@ import 'package:analyzer/src/dart/analysis/index.dart';
|
|||
import 'package:analyzer/src/dart/analysis/library_analyzer.dart';
|
||||
import 'package:analyzer/src/dart/analysis/library_context.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/analysis/session.dart';
|
||||
import 'package:analyzer/src/dart/analysis/status.dart';
|
||||
|
@ -186,6 +188,13 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
*/
|
||||
final _requestedFiles = <String, List<Completer<AnalysisResult>>>{};
|
||||
|
||||
/**
|
||||
* The mapping from the files for which analysis was requested using
|
||||
* [getResolvedLibrary] to the [Completer]s to report the result.
|
||||
*/
|
||||
final _requestedLibraries =
|
||||
<String, List<Completer<ResolvedLibraryResult>>>{};
|
||||
|
||||
/**
|
||||
* The task that discovers available files. If this field is not `null`,
|
||||
* and the task is not completed, it should be performed and completed
|
||||
|
@ -468,6 +477,9 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
if (_requestedFiles.isNotEmpty) {
|
||||
return AnalysisDriverPriority.interactive;
|
||||
}
|
||||
if (_requestedLibraries.isNotEmpty) {
|
||||
return AnalysisDriverPriority.interactive;
|
||||
}
|
||||
if (_discoverAvailableFilesTask != null &&
|
||||
!_discoverAvailableFilesTask.isCompleted) {
|
||||
return AnalysisDriverPriority.interactive;
|
||||
|
@ -725,6 +737,83 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
return unitResult.element.library;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a [Future] that completes with a [ResolvedLibraryResult] for the
|
||||
* Dart library file with the given [path]. If the file is not a Dart file
|
||||
* or cannot be analyzed, the [Future] completes with `null`.
|
||||
*
|
||||
* Throw [ArgumentError] if the given [path] is not the defining compilation
|
||||
* unit for a library (that is, is a part of a library).
|
||||
*
|
||||
* The [path] must be absolute and normalized.
|
||||
*
|
||||
* The [path] can be any file - explicitly or implicitly analyzed, or neither.
|
||||
*
|
||||
* Invocation of this method causes the analysis state to transition to
|
||||
* "analyzing" (if it is not in that state already), the driver will produce
|
||||
* the resolution result for it, which is consistent with the current file
|
||||
* state (including new states of the files previously reported using
|
||||
* [changeFile]), prior to the next time the analysis state transitions
|
||||
* to "idle".
|
||||
*/
|
||||
Future<ResolvedLibraryResult> getResolvedLibrary(String path) {
|
||||
_throwIfNotAbsolutePath(path);
|
||||
if (!_fsState.hasUri(path)) {
|
||||
return new Future.value();
|
||||
}
|
||||
|
||||
FileState file = _fsState.getFileForPath(path);
|
||||
|
||||
if (file.isExternalLibrary) {
|
||||
return Future.value(
|
||||
ResolvedLibraryResultImpl.external(currentSession, file.uri),
|
||||
);
|
||||
}
|
||||
|
||||
if (file.isPart) {
|
||||
throw ArgumentError('Is a part: $path');
|
||||
}
|
||||
|
||||
// Schedule analysis.
|
||||
var completer = new Completer<ResolvedLibraryResult>();
|
||||
_requestedLibraries
|
||||
.putIfAbsent(path, () => <Completer<ResolvedLibraryResult>>[])
|
||||
.add(completer);
|
||||
_scheduler.notify(this);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a [Future] that completes with a [ResolvedLibraryResult] for the
|
||||
* Dart library file with the given [uri].
|
||||
*
|
||||
* Throw [ArgumentError] if the given [uri] is not the defining compilation
|
||||
* unit for a library (that is, is a part of a library).
|
||||
*
|
||||
* Invocation of this method causes the analysis state to transition to
|
||||
* "analyzing" (if it is not in that state already), the driver will produce
|
||||
* the resolution result for it, which is consistent with the current file
|
||||
* state (including new states of the files previously reported using
|
||||
* [changeFile]), prior to the next time the analysis state transitions
|
||||
* to "idle".
|
||||
*/
|
||||
Future<ResolvedLibraryResult> getResolvedLibraryByUri(Uri uri) {
|
||||
FileState file = _fsState.getFileForUri(uri);
|
||||
|
||||
if (file.isExternalLibrary) {
|
||||
return Future.value(
|
||||
ResolvedLibraryResultImpl.external(currentSession, file.uri),
|
||||
);
|
||||
}
|
||||
|
||||
if (file.isPart) {
|
||||
throw ArgumentError('Is a part: $uri');
|
||||
}
|
||||
|
||||
// The file is a local file, we can get the result.
|
||||
return getResolvedLibrary(file.path);
|
||||
}
|
||||
|
||||
ApiSignature getResolvedUnitKeyByPath(String path) {
|
||||
_throwIfNotAbsolutePath(path);
|
||||
ApiSignature signature = getUnitKeyByPath(path);
|
||||
|
@ -959,6 +1048,22 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
return;
|
||||
}
|
||||
|
||||
// Analyze a requested library.
|
||||
if (_requestedLibraries.isNotEmpty) {
|
||||
String path = _requestedLibraries.keys.first;
|
||||
try {
|
||||
var result = _computeResolvedLibrary(path);
|
||||
_requestedLibraries.remove(path).forEach((completer) {
|
||||
completer.complete(result);
|
||||
});
|
||||
} catch (exception, stackTrace) {
|
||||
_requestedLibraries.remove(path).forEach((completer) {
|
||||
completer.completeError(exception, stackTrace);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Process an index request.
|
||||
if (_indexRequestedFiles.isNotEmpty) {
|
||||
String path = _indexRequestedFiles.keys.first;
|
||||
|
@ -1256,7 +1361,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
if (!_fsState.getFileForUri(Uri.parse('dart:async')).exists) {
|
||||
return _newMissingDartLibraryResult(file, 'dart:async');
|
||||
}
|
||||
libraryContext = await _createLibraryContext(library);
|
||||
libraryContext = _createLibraryContext(library);
|
||||
|
||||
LibraryAnalyzer analyzer = new LibraryAnalyzer(
|
||||
analysisOptions,
|
||||
|
@ -1267,7 +1372,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
libraryContext.resynthesizer,
|
||||
library,
|
||||
_resourceProvider);
|
||||
Map<FileState, UnitAnalysisResult> results = await analyzer.analyze();
|
||||
Map<FileState, UnitAnalysisResult> results = analyzer.analyze();
|
||||
|
||||
List<int> bytes;
|
||||
CompilationUnit resolvedUnit;
|
||||
|
@ -1319,6 +1424,62 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
return analysisResult._index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the newly computed resolution result of the library with the
|
||||
* given [path].
|
||||
*/
|
||||
ResolvedLibraryResultImpl _computeResolvedLibrary(String path) {
|
||||
FileState library = _fsState.getFileForPath(path);
|
||||
|
||||
return _logger.run('Compute resolved library $path', () {
|
||||
_testView.numOfAnalyzedLibraries++;
|
||||
var libraryContext = _createLibraryContext(library);
|
||||
|
||||
LibraryAnalyzer analyzer = new LibraryAnalyzer(
|
||||
analysisOptions,
|
||||
declaredVariables,
|
||||
sourceFactory,
|
||||
libraryContext.isLibraryUri,
|
||||
libraryContext.analysisContext,
|
||||
libraryContext.resynthesizer,
|
||||
library,
|
||||
_resourceProvider);
|
||||
Map<FileState, UnitAnalysisResult> unitResults = analyzer.analyze();
|
||||
var resolvedUnits = <ResolvedUnitResult>[];
|
||||
|
||||
for (var unitFile in unitResults.keys) {
|
||||
if (unitFile.path != null) {
|
||||
var unitResult = unitResults[unitFile];
|
||||
resolvedUnits.add(
|
||||
new AnalysisResult(
|
||||
this,
|
||||
_sourceFactory,
|
||||
unitFile.path,
|
||||
unitFile.uri,
|
||||
unitFile.exists,
|
||||
unitFile.content,
|
||||
unitFile.lineInfo,
|
||||
unitFile.isPart,
|
||||
null,
|
||||
unitResult.unit,
|
||||
unitResult.errors,
|
||||
null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new ResolvedLibraryResultImpl(
|
||||
currentSession,
|
||||
library.path,
|
||||
library.uri,
|
||||
resolvedUnits.first.libraryElement,
|
||||
libraryContext.analysisContext.typeProvider,
|
||||
resolvedUnits,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<UnitElementResult> _computeUnitElement(String path,
|
||||
{bool asIsIfPartWithoutLibrary: false}) async {
|
||||
// TODO(brianwilkerson) Determine whether this await is necessary.
|
||||
|
@ -1335,7 +1496,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
}
|
||||
}
|
||||
|
||||
LibraryContext libraryContext = await _createLibraryContext(library);
|
||||
LibraryContext libraryContext = _createLibraryContext(library);
|
||||
try {
|
||||
CompilationUnitElement element =
|
||||
libraryContext.computeUnitElement(library.source, file.source);
|
||||
|
@ -1389,9 +1550,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
/**
|
||||
* Return the context in which the [library] should be analyzed.
|
||||
*/
|
||||
Future<LibraryContext> _createLibraryContext(FileState library) async {
|
||||
// TODO(brianwilkerson) Determine whether this await is necessary.
|
||||
await null;
|
||||
LibraryContext _createLibraryContext(FileState library) {
|
||||
_testView.numOfCreatedLibraryContexts++;
|
||||
return new LibraryContext.forSingleLibrary(
|
||||
library,
|
||||
|
@ -1955,7 +2114,7 @@ class AnalysisDriverTestView {
|
|||
FileState library = driver.fsState.getFileForPath(libraryPath);
|
||||
// TODO(brianwilkerson) Determine whether this await is necessary.
|
||||
await null;
|
||||
LibraryContext libraryContext = await driver._createLibraryContext(library);
|
||||
LibraryContext libraryContext = driver._createLibraryContext(library);
|
||||
try {
|
||||
return libraryContext.store;
|
||||
} finally {
|
||||
|
@ -1975,7 +2134,7 @@ class AnalysisDriverTestView {
|
|||
* Every result is independent, and is not guaranteed to be consistent with
|
||||
* any previously returned result, even inside of the same library.
|
||||
*/
|
||||
class AnalysisResult extends FileResult implements results.ResolveResult {
|
||||
class AnalysisResult extends FileResult implements results.ResolvedUnitResult {
|
||||
static final _UNCHANGED = new AnalysisResult(
|
||||
null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
|
||||
|
|
|
@ -213,11 +213,26 @@ class FileState {
|
|||
*/
|
||||
List<FileState> get importedFiles => _importedFiles;
|
||||
|
||||
/**
|
||||
* Return `true` if the file is a stub created for a library in the provided
|
||||
* external summary store.
|
||||
*/
|
||||
bool get isExternalLibrary {
|
||||
return _fsState.externalSummaries != null &&
|
||||
_fsState.externalSummaries.linkedMap.containsKey(uriStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the file does not have a `library` directive, and has a
|
||||
* `part of` directive, so is probably a part.
|
||||
*/
|
||||
bool get isPart => _unlinked.libraryNameOffset == 0 && _unlinked.isPartOf;
|
||||
bool get isPart {
|
||||
if (_fsState.externalSummaries != null &&
|
||||
_fsState.externalSummaries.unlinkedMap.containsKey(uriStr)) {
|
||||
return !_fsState.externalSummaries.linkedMap.containsKey(uriStr);
|
||||
}
|
||||
return _unlinked.libraryNameOffset == 0 && _unlinked.isPartOf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the file is the "unresolved" file, which does not have
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analyzer/dart/analysis/declared_variables.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/visitor.dart';
|
||||
|
@ -94,10 +92,8 @@ class LibraryAnalyzer {
|
|||
/**
|
||||
* Compute analysis results for all units of the library.
|
||||
*/
|
||||
Future<Map<FileState, UnitAnalysisResult>> analyze() async {
|
||||
// TODO(brianwilkerson) Determine whether this await is necessary.
|
||||
await null;
|
||||
return PerformanceStatistics.analysis.makeCurrentWhileAsync(() async {
|
||||
Map<FileState, UnitAnalysisResult> analyze() {
|
||||
return PerformanceStatistics.analysis.makeCurrentWhile(() {
|
||||
return analyzeSync();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -143,9 +143,6 @@ class ResolvedLibraryResultImpl extends AnalysisResultImpl
|
|||
@override
|
||||
final LibraryElement element;
|
||||
|
||||
@override
|
||||
final ResultState state = ResultState.VALID;
|
||||
|
||||
@override
|
||||
final TypeProvider typeProvider;
|
||||
|
||||
|
@ -156,8 +153,23 @@ class ResolvedLibraryResultImpl extends AnalysisResultImpl
|
|||
this.element, this.typeProvider, this.units)
|
||||
: super(session, path, uri);
|
||||
|
||||
ResolvedLibraryResultImpl.external(AnalysisSession session, Uri uri)
|
||||
: this(session, null, uri, null, null, null);
|
||||
|
||||
@override
|
||||
ResultState get state {
|
||||
if (path == null) {
|
||||
return ResultState.NOT_A_FILE;
|
||||
}
|
||||
return ResultState.VALID;
|
||||
}
|
||||
|
||||
@override
|
||||
ElementDeclarationResult getElementDeclaration(Element element) {
|
||||
if (state != ResultState.VALID) {
|
||||
throw StateError('The result is not valid: $state');
|
||||
}
|
||||
|
||||
var elementPath = element.source.fullName;
|
||||
var unitResult = units.firstWhere(
|
||||
(r) => r.path == elementPath,
|
||||
|
|
|
@ -129,6 +129,20 @@ class AnalysisSessionImpl implements AnalysisSession {
|
|||
return _driver.getResult(path);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ResolvedLibraryResult> getResolvedLibrary(String path) {
|
||||
_checkConsistency();
|
||||
return _driver.getResolvedLibrary(path);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ResolvedLibraryResult> getResolvedLibraryByElement(
|
||||
LibraryElement element) {
|
||||
_checkConsistency();
|
||||
_checkElementOfThisSession(element);
|
||||
return _driver.getResolvedLibraryByUri(element.source.uri);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SourceKind> getSourceKind(String path) {
|
||||
_checkConsistency();
|
||||
|
@ -163,4 +177,13 @@ class AnalysisSessionImpl implements AnalysisSession {
|
|||
throw new InconsistentAnalysisException();
|
||||
}
|
||||
}
|
||||
|
||||
void _checkElementOfThisSession(Element element) {
|
||||
// TODO(scheglov) Requires 2.2 implementation
|
||||
// if (element.session != this) {
|
||||
// throw new ArgumentError(
|
||||
// '(${element.runtimeType}) $element was not produced by '
|
||||
// 'this session.');
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analyzer/dart/analysis/results.dart' as results;
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/standard_resolution_map.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
|
@ -1572,6 +1573,89 @@ class Test {}
|
|||
expect(coreLibrary.getType('Object'), isNotNull);
|
||||
}
|
||||
|
||||
test_getResolvedLibrary_external() async {
|
||||
var a1 = _p('/aaa/lib/a1.dart');
|
||||
var a2 = _p('/aaa/lib/a2.dart');
|
||||
|
||||
var a1UriStr = 'package:aaa/a1.dart';
|
||||
var a2UriStr = 'package:aaa/a2.dart';
|
||||
|
||||
provider.newFile(a1, "part 'a2.dart'; class A {}");
|
||||
provider.newFile(a2, "part of 'a1.dart';");
|
||||
|
||||
// Build the store with the library.
|
||||
var store = await createAnalysisDriver().test.getSummaryStore(a1);
|
||||
expect(store.unlinkedMap.keys, contains(a1UriStr));
|
||||
expect(store.unlinkedMap.keys, contains(a2UriStr));
|
||||
expect(store.linkedMap.keys, contains(a1UriStr));
|
||||
|
||||
var driver = createAnalysisDriver(externalSummaries: store);
|
||||
var libraryElement = await driver.getLibraryByUri(a1UriStr);
|
||||
var classA = libraryElement.library.getType('A');
|
||||
|
||||
var resolvedLibrary = await driver.getResolvedLibrary(a1);
|
||||
expect(resolvedLibrary, isNotNull);
|
||||
expect(resolvedLibrary.state, results.ResultState.NOT_A_FILE);
|
||||
expect(() {
|
||||
resolvedLibrary.getElementDeclaration(classA);
|
||||
}, throwsStateError);
|
||||
|
||||
// It is an error to ask for a library when we know that it is a part.
|
||||
expect(() async {
|
||||
await driver.getResolvedLibrary(a2);
|
||||
}, throwsArgumentError);
|
||||
}
|
||||
|
||||
test_getResolvedLibraryByUri_external() async {
|
||||
var a1 = _p('/aaa/lib/a1.dart');
|
||||
var a2 = _p('/aaa/lib/a2.dart');
|
||||
|
||||
var a1UriStr = 'package:aaa/a1.dart';
|
||||
var a2UriStr = 'package:aaa/a2.dart';
|
||||
|
||||
var a1Uri = Uri.parse(a1UriStr);
|
||||
var a2Uri = Uri.parse(a2UriStr);
|
||||
|
||||
provider.newFile(a1, "part 'a2.dart'; class A {}");
|
||||
provider.newFile(a2, "part of 'a1.dart';");
|
||||
|
||||
// Build the store with the library.
|
||||
var store = await createAnalysisDriver().test.getSummaryStore(a1);
|
||||
expect(store.unlinkedMap.keys, contains(a1UriStr));
|
||||
expect(store.unlinkedMap.keys, contains(a2UriStr));
|
||||
expect(store.linkedMap.keys, contains(a1UriStr));
|
||||
|
||||
var driver = createAnalysisDriver(externalSummaries: store);
|
||||
var libraryElement = await driver.getLibraryByUri(a1UriStr);
|
||||
var classA = libraryElement.library.getType('A');
|
||||
|
||||
{
|
||||
var resolvedLibrary = await driver.getResolvedLibraryByUri(a1Uri);
|
||||
expect(resolvedLibrary, isNotNull);
|
||||
expect(resolvedLibrary.state, results.ResultState.NOT_A_FILE);
|
||||
expect(() {
|
||||
resolvedLibrary.getElementDeclaration(classA);
|
||||
}, throwsStateError);
|
||||
}
|
||||
|
||||
// We can also get the result from the session.
|
||||
{
|
||||
var session = driver.currentSession;
|
||||
var resolvedLibrary =
|
||||
await session.getResolvedLibraryByElement(libraryElement);
|
||||
expect(resolvedLibrary, isNotNull);
|
||||
expect(resolvedLibrary.state, results.ResultState.NOT_A_FILE);
|
||||
expect(() {
|
||||
resolvedLibrary.getElementDeclaration(classA);
|
||||
}, throwsStateError);
|
||||
}
|
||||
|
||||
// It is an error to ask for a library when we know that it is a part.
|
||||
expect(() async {
|
||||
await driver.getResolvedLibraryByUri(a2Uri);
|
||||
}, throwsArgumentError);
|
||||
}
|
||||
|
||||
test_getResult() async {
|
||||
String content = 'int f() => 42;';
|
||||
addTestFile(content, priority: true);
|
||||
|
|
|
@ -2,23 +2,18 @@
|
|||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analyzer/dart/analysis/session.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/file_system/memory_file_system.dart';
|
||||
import 'package:analyzer/src/dart/analysis/driver.dart';
|
||||
import 'package:analyzer/dart/analysis/analysis_context.dart';
|
||||
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
|
||||
import 'package:analyzer/src/dart/analysis/session.dart';
|
||||
import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:analyzer/src/dart/element/type.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart'
|
||||
show AnalysisOptions, AnalysisOptionsImpl;
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../../context/mock_sdk.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(AnalysisSessionImplTest);
|
||||
|
@ -26,214 +21,281 @@ main() {
|
|||
}
|
||||
|
||||
@reflectiveTest
|
||||
class AnalysisSessionImplTest {
|
||||
MockAnalysisDriver driver;
|
||||
class AnalysisSessionImplTest with ResourceProviderMixin {
|
||||
AnalysisContextCollection contextCollection;
|
||||
AnalysisContext context;
|
||||
AnalysisSessionImpl session;
|
||||
|
||||
String testContextPath;
|
||||
String aaaContextPath;
|
||||
String bbbContextPath;
|
||||
|
||||
String testPath;
|
||||
|
||||
void setUp() {
|
||||
driver = new MockAnalysisDriver();
|
||||
session = new AnalysisSessionImpl(driver);
|
||||
driver.currentSession = session;
|
||||
MockSdk(resourceProvider: resourceProvider);
|
||||
|
||||
testContextPath = newFolder('/home/test').path;
|
||||
aaaContextPath = newFolder('/home/aaa').path;
|
||||
bbbContextPath = newFolder('/home/bbb').path;
|
||||
|
||||
newFile('/home/test/.packages', content: r'''
|
||||
test:lib/
|
||||
''');
|
||||
|
||||
contextCollection = AnalysisContextCollectionImpl(
|
||||
includedPaths: [testContextPath, aaaContextPath, bbbContextPath],
|
||||
resourceProvider: resourceProvider,
|
||||
sdkPath: convertPath(sdkRoot),
|
||||
);
|
||||
context = contextCollection.contextFor(testContextPath);
|
||||
session = context.currentSession;
|
||||
|
||||
testPath = convertPath('/home/test/lib/test.dart');
|
||||
}
|
||||
|
||||
test_getErrors() async {
|
||||
ErrorsResult result = new ErrorsResult(null, null, null, null, null, null);
|
||||
driver.errorsResult = result;
|
||||
expect(await session.getErrors('path'), result);
|
||||
newFile(testPath, content: 'class C {');
|
||||
var errorsResult = await session.getErrors(testPath);
|
||||
expect(errorsResult.session, session);
|
||||
expect(errorsResult.path, testPath);
|
||||
expect(errorsResult.errors, isNotEmpty);
|
||||
}
|
||||
|
||||
test_getLibraryByUri() async {
|
||||
String uri = 'uri';
|
||||
newFile(testPath, content: r'''
|
||||
class A {}
|
||||
class B {}
|
||||
''');
|
||||
|
||||
var source = new _SourceMock(Uri.parse(uri));
|
||||
var unit = new CompilationUnitElementImpl()
|
||||
..librarySource = source
|
||||
..source = source;
|
||||
var library = new LibraryElementImpl(null, null, null, null)
|
||||
..definingCompilationUnit = unit;
|
||||
|
||||
driver.libraryMap[uri] = library;
|
||||
expect(await session.getLibraryByUri(uri), library);
|
||||
var library = await session.getLibraryByUri('package:test/test.dart');
|
||||
expect(library.getType('A'), isNotNull);
|
||||
expect(library.getType('B'), isNotNull);
|
||||
expect(library.getType('C'), isNull);
|
||||
}
|
||||
|
||||
test_getParsedAst() async {
|
||||
ParseResult result =
|
||||
new ParseResult(null, null, null, null, null, null, null, null);
|
||||
driver.parseResult = result;
|
||||
expect(await session.getParsedAst('path'), result);
|
||||
test_getParsedAstSync() async {
|
||||
newFile(testPath, content: r'''
|
||||
class A {}
|
||||
class B {}
|
||||
''');
|
||||
|
||||
var unitResult = session.getParsedAstSync(testPath);
|
||||
expect(unitResult.session, session);
|
||||
expect(unitResult.path, testPath);
|
||||
expect(unitResult.uri, Uri.parse('package:test/test.dart'));
|
||||
expect(unitResult.unit.declarations, hasLength(2));
|
||||
}
|
||||
|
||||
test_getResolvedAst() async {
|
||||
AnalysisResult result = new AnalysisResult(driver, null, null, null, null,
|
||||
null, null, null, null, null, null, null);
|
||||
driver.result = result;
|
||||
expect(await session.getResolvedAst('path'), result);
|
||||
newFile(testPath, content: r'''
|
||||
class A {}
|
||||
class B {}
|
||||
''');
|
||||
|
||||
var unitResult = await session.getResolvedAst(testPath);
|
||||
expect(unitResult.session, session);
|
||||
expect(unitResult.path, testPath);
|
||||
expect(unitResult.uri, Uri.parse('package:test/test.dart'));
|
||||
expect(unitResult.unit.declarations, hasLength(2));
|
||||
expect(unitResult.typeProvider, isNotNull);
|
||||
expect(unitResult.libraryElement, isNotNull);
|
||||
}
|
||||
|
||||
test_getResolvedLibrary() async {
|
||||
var a = convertPath('/home/test/lib/a.dart');
|
||||
var b = convertPath('/home/test/lib/b.dart');
|
||||
|
||||
var aContent = r'''
|
||||
part 'b.dart';
|
||||
|
||||
class A /*a*/ {}
|
||||
''';
|
||||
newFile(a, content: aContent);
|
||||
|
||||
var bContent = r'''
|
||||
part of 'a.dart';
|
||||
|
||||
class B /*b*/ {}
|
||||
class B2 extends X {}
|
||||
''';
|
||||
newFile(b, content: bContent);
|
||||
|
||||
var resolvedLibrary = await session.getResolvedLibrary(a);
|
||||
expect(resolvedLibrary.session, session);
|
||||
expect(resolvedLibrary.path, a);
|
||||
expect(resolvedLibrary.uri, Uri.parse('package:test/a.dart'));
|
||||
|
||||
var typeProvider = resolvedLibrary.typeProvider;
|
||||
expect(typeProvider.intType.element.name, 'int');
|
||||
|
||||
var libraryElement = resolvedLibrary.element;
|
||||
expect(libraryElement, isNotNull);
|
||||
|
||||
var aClass = libraryElement.getType('A');
|
||||
expect(aClass, isNotNull);
|
||||
|
||||
var bClass = libraryElement.getType('B');
|
||||
expect(bClass, isNotNull);
|
||||
|
||||
var aUnitResult = resolvedLibrary.units[0];
|
||||
expect(aUnitResult.path, a);
|
||||
expect(aUnitResult.uri, Uri.parse('package:test/a.dart'));
|
||||
expect(aUnitResult.content, aContent);
|
||||
expect(aUnitResult.unit, isNotNull);
|
||||
expect(aUnitResult.unit.directives, hasLength(1));
|
||||
expect(aUnitResult.unit.declarations, hasLength(1));
|
||||
expect(aUnitResult.errors, isEmpty);
|
||||
|
||||
var bUnitResult = resolvedLibrary.units[1];
|
||||
expect(bUnitResult.path, b);
|
||||
expect(bUnitResult.uri, Uri.parse('package:test/b.dart'));
|
||||
expect(bUnitResult.content, bContent);
|
||||
expect(bUnitResult.unit, isNotNull);
|
||||
expect(bUnitResult.unit.directives, hasLength(1));
|
||||
expect(bUnitResult.unit.declarations, hasLength(2));
|
||||
expect(bUnitResult.errors, isNotEmpty);
|
||||
|
||||
var aDeclaration = resolvedLibrary.getElementDeclaration(aClass);
|
||||
ClassDeclaration aNode = aDeclaration.node;
|
||||
expect(aNode.name.name, 'A');
|
||||
expect(aNode.offset, 16);
|
||||
expect(aNode.length, 16);
|
||||
expect(aNode.name.staticElement.name, 'A');
|
||||
|
||||
var bDeclaration = resolvedLibrary.getElementDeclaration(bClass);
|
||||
ClassDeclaration bNode = bDeclaration.node;
|
||||
expect(bNode.name.name, 'B');
|
||||
expect(bNode.offset, 19);
|
||||
expect(bNode.length, 16);
|
||||
expect(bNode.name.staticElement.name, 'B');
|
||||
}
|
||||
|
||||
test_getResolvedLibrary_getElementDeclaration_notThisLibrary() async {
|
||||
newFile(testPath, content: '');
|
||||
|
||||
var resolvedLibrary = await session.getResolvedLibrary(testPath);
|
||||
|
||||
expect(() {
|
||||
var intClass = resolvedLibrary.typeProvider.intType.element;
|
||||
resolvedLibrary.getElementDeclaration(intClass);
|
||||
}, throwsArgumentError);
|
||||
}
|
||||
|
||||
test_getResolvedLibrary_getElementDeclaration_synthetic() async {
|
||||
newFile(testPath, content: r'''
|
||||
int foo = 0;
|
||||
''');
|
||||
|
||||
var resolvedLibrary = await session.getResolvedLibrary(testPath);
|
||||
var unitElement = resolvedLibrary.element.definingCompilationUnit;
|
||||
|
||||
var fooElement = unitElement.topLevelVariables[0];
|
||||
expect(fooElement.name, 'foo');
|
||||
|
||||
// We can get the variable element declaration.
|
||||
var fooDeclaration = resolvedLibrary.getElementDeclaration(fooElement);
|
||||
VariableDeclaration fooNode = fooDeclaration.node;
|
||||
expect(fooNode.name.name, 'foo');
|
||||
expect(fooNode.offset, 4);
|
||||
expect(fooNode.length, 7);
|
||||
expect(fooNode.name.staticElement.name, 'foo');
|
||||
|
||||
// Synthetic elements don't have nodes.
|
||||
expect(resolvedLibrary.getElementDeclaration(fooElement.getter), isNull);
|
||||
expect(resolvedLibrary.getElementDeclaration(fooElement.setter), isNull);
|
||||
}
|
||||
|
||||
test_getResolvedLibrary_invalidPartUri() async {
|
||||
newFile(testPath, content: r'''
|
||||
part 'a.dart';
|
||||
part ':[invalid uri].dart';
|
||||
part 'c.dart';
|
||||
''');
|
||||
|
||||
var resolvedLibrary = await session.getResolvedLibrary(testPath);
|
||||
|
||||
expect(resolvedLibrary.units, hasLength(3));
|
||||
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_notLibrary() async {
|
||||
newFile(testPath, content: 'part of "a.dart";');
|
||||
|
||||
expect(() {
|
||||
session.getResolvedLibrary(testPath);
|
||||
}, throwsArgumentError);
|
||||
}
|
||||
|
||||
test_getResolvedLibraryByElement() async {
|
||||
newFile(testPath, content: '');
|
||||
|
||||
var element = await session.getLibraryByUri('package:test/test.dart');
|
||||
|
||||
var resolvedLibrary = await session.getResolvedLibraryByElement(element);
|
||||
expect(resolvedLibrary.session, session);
|
||||
expect(resolvedLibrary.path, testPath);
|
||||
expect(resolvedLibrary.uri, Uri.parse('package:test/test.dart'));
|
||||
expect(resolvedLibrary.units, hasLength(1));
|
||||
expect(resolvedLibrary.units[0].unit.declaredElement, isNotNull);
|
||||
}
|
||||
|
||||
test_getSourceKind() async {
|
||||
SourceKind kind = SourceKind.LIBRARY;
|
||||
driver.sourceKind = kind;
|
||||
expect(await session.getSourceKind('path'), kind);
|
||||
newFile(testPath, content: 'class C {}');
|
||||
|
||||
var kind = await session.getSourceKind(testPath);
|
||||
expect(kind, SourceKind.LIBRARY);
|
||||
}
|
||||
|
||||
test_getTopLevelDeclarations() async {
|
||||
List<TopLevelDeclarationInSource> declarations = [];
|
||||
driver.topLevelDeclarations = declarations;
|
||||
expect(await session.getTopLevelDeclarations('path'), declarations);
|
||||
test_getSourceKind_part() async {
|
||||
newFile(testPath, content: 'part of "a.dart";');
|
||||
|
||||
var kind = await session.getSourceKind(testPath);
|
||||
expect(kind, SourceKind.PART);
|
||||
}
|
||||
|
||||
test_getUnitElement() async {
|
||||
UnitElementResult result =
|
||||
new UnitElementResult(null, null, null, null, null);
|
||||
driver.unitElementResult = result;
|
||||
expect(await session.getUnitElement('path'), result);
|
||||
newFile(testPath, content: r'''
|
||||
class A {}
|
||||
class B {}
|
||||
''');
|
||||
|
||||
var unitResult = await session.getUnitElement(testPath);
|
||||
expect(unitResult.session, session);
|
||||
expect(unitResult.path, testPath);
|
||||
expect(unitResult.uri, Uri.parse('package:test/test.dart'));
|
||||
expect(unitResult.element.types, hasLength(2));
|
||||
|
||||
var signature = await session.getUnitElementSignature(testPath);
|
||||
expect(unitResult.signature, signature);
|
||||
}
|
||||
|
||||
test_getUnitElementSignature() async {
|
||||
String signature = 'xyzzy';
|
||||
driver.unitElementSignature = signature;
|
||||
expect(await session.getUnitElementSignature('path'), signature);
|
||||
}
|
||||
|
||||
test_resourceProvider() {
|
||||
ResourceProvider resourceProvider = new MemoryResourceProvider();
|
||||
driver.resourceProvider = resourceProvider;
|
||||
test_resourceProvider() async {
|
||||
expect(session.resourceProvider, resourceProvider);
|
||||
}
|
||||
|
||||
test_sourceFactory() {
|
||||
SourceFactory sourceFactory = new SourceFactory([]);
|
||||
driver.sourceFactory = sourceFactory;
|
||||
expect(session.sourceFactory, sourceFactory);
|
||||
}
|
||||
|
||||
test_typeProvider() async {
|
||||
_initializeSDK();
|
||||
expect(await session.typeProvider, isNotNull);
|
||||
var typeProvider = await session.typeProvider;
|
||||
expect(typeProvider.intType.element.name, 'int');
|
||||
}
|
||||
|
||||
test_typeSystem() async {
|
||||
_initializeSDK();
|
||||
expect(await session.typeSystem, isNotNull);
|
||||
}
|
||||
|
||||
void _initializeSDK() {
|
||||
CompilationUnitElementImpl newUnit(String name) {
|
||||
CompilationUnitElementImpl unit = new CompilationUnitElementImpl();
|
||||
unit.accessors = [];
|
||||
unit.enums = [];
|
||||
unit.functions = [];
|
||||
unit.typeAliases = [];
|
||||
return unit;
|
||||
}
|
||||
|
||||
ClassElementImpl newClass(String name) {
|
||||
TypeParameterElementImpl param = new TypeParameterElementImpl('E', 0);
|
||||
param.type = new TypeParameterTypeImpl(param);
|
||||
ClassElementImpl element = new ClassElementImpl(name, 0);
|
||||
element.typeParameters = [param];
|
||||
return element;
|
||||
}
|
||||
|
||||
{
|
||||
CompilationUnitElementImpl coreUnit = newUnit('dart.core');
|
||||
coreUnit.types = <ClassElement>[newClass('Iterable')];
|
||||
LibraryElementImpl core = new LibraryElementImpl(null, null, null, null);
|
||||
core.definingCompilationUnit = coreUnit;
|
||||
driver.libraryMap['dart:core'] = core;
|
||||
}
|
||||
{
|
||||
CompilationUnitElementImpl asyncUnit = newUnit('dart.async');
|
||||
asyncUnit.types = <ClassElement>[
|
||||
newClass('Future'),
|
||||
newClass('FutureOr'),
|
||||
newClass('Stream')
|
||||
];
|
||||
LibraryElementImpl async = new LibraryElementImpl(null, null, null, null);
|
||||
async.definingCompilationUnit = asyncUnit;
|
||||
driver.libraryMap['dart:async'] = async;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MockAnalysisDriver implements AnalysisDriver {
|
||||
@override
|
||||
AnalysisSession currentSession;
|
||||
|
||||
ErrorsResult errorsResult;
|
||||
Map<String, LibraryElement> libraryMap = <String, LibraryElement>{};
|
||||
ParseResult parseResult;
|
||||
ResourceProvider resourceProvider;
|
||||
AnalysisResult result;
|
||||
SourceFactory sourceFactory;
|
||||
SourceKind sourceKind;
|
||||
List<TopLevelDeclarationInSource> topLevelDeclarations;
|
||||
UnitElementResult unitElementResult;
|
||||
String unitElementSignature;
|
||||
|
||||
AnalysisOptions get analysisOptions => new AnalysisOptionsImpl();
|
||||
|
||||
@override
|
||||
Future<ErrorsResult> getErrors(String path) async {
|
||||
return errorsResult;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LibraryElement> getLibraryByUri(String uri) async {
|
||||
return libraryMap[uri];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<AnalysisResult> getResult(String path,
|
||||
{bool sendCachedToStream: false}) async {
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SourceKind> getSourceKind(String path) async {
|
||||
return sourceKind;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TopLevelDeclarationInSource>> getTopLevelNameDeclarations(
|
||||
String name) async {
|
||||
return topLevelDeclarations;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<UnitElementResult> getUnitElement(String path) async {
|
||||
return unitElementResult;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getUnitElementSignature(String path) async {
|
||||
return unitElementSignature;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) {
|
||||
fail('Unexpected invocation of ${invocation.memberName}');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ParseResult> parseFile(String path) async {
|
||||
return parseResult;
|
||||
}
|
||||
|
||||
@override
|
||||
ParseResult parseFileSync(String path) {
|
||||
return parseResult;
|
||||
}
|
||||
}
|
||||
|
||||
class _SourceMock implements Source {
|
||||
@override
|
||||
final Uri uri;
|
||||
|
||||
_SourceMock(this.uri);
|
||||
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) {
|
||||
throw new StateError('Unexpected invocation of ${invocation.memberName}');
|
||||
var typeSystem = await session.typeSystem;
|
||||
var typeProvider = typeSystem.typeProvider;
|
||||
expect(
|
||||
typeSystem.isSubtypeOf(typeProvider.intType, typeProvider.numType),
|
||||
isTrue,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue