From 232aa553fb97dbc2790b19363d85e2a7b74e6e27 Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Tue, 27 Nov 2018 22:53:24 +0000 Subject: [PATCH] Backport AnalysisSession.getResolvedLibrary()/ByElement(). Change-Id: I83c1dbe9d758be12212fcd892bb57ae69407d8be Reviewed-on: https://dart-review.googlesource.com/c/85147 Commit-Queue: Konstantin Shcheglov Reviewed-by: Brian Wilkerson Reviewed-by: Paul Berry --- pkg/analyzer/lib/dart/analysis/session.dart | 15 + .../lib/src/dart/analysis/driver.dart | 175 ++++++- .../lib/src/dart/analysis/file_state.dart | 17 +- .../src/dart/analysis/library_analyzer.dart | 8 +- .../lib/src/dart/analysis/results.dart | 18 +- .../lib/src/dart/analysis/session.dart | 23 + .../test/src/dart/analysis/driver_test.dart | 84 ++++ .../test/src/dart/analysis/session_test.dart | 440 ++++++++++-------- 8 files changed, 573 insertions(+), 207 deletions(-) diff --git a/pkg/analyzer/lib/dart/analysis/session.dart b/pkg/analyzer/lib/dart/analysis/session.dart index 1760f2a4fae..b945ed612a4 100644 --- a/pkg/analyzer/lib/dart/analysis/session.dart +++ b/pkg/analyzer/lib/dart/analysis/session.dart @@ -91,6 +91,21 @@ abstract class AnalysisSession { */ Future 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 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 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 diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart index 000fcae9319..6f2d3ec420d 100644 --- a/pkg/analyzer/lib/src/dart/analysis/driver.dart +++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart @@ -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 = >>{}; + /** + * The mapping from the files for which analysis was requested using + * [getResolvedLibrary] to the [Completer]s to report the result. + */ + final _requestedLibraries = + >>{}; + /** * 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 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(); + _requestedLibraries + .putIfAbsent(path, () => >[]) + .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 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 results = await analyzer.analyze(); + Map results = analyzer.analyze(); List 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 unitResults = analyzer.analyze(); + var resolvedUnits = []; + + 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 _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 _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); diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart index f9da8aa35ca..e627abbfabe 100644 --- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart +++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart @@ -213,11 +213,26 @@ class FileState { */ List 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 diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart index 6c421b8ec2f..ed454748154 100644 --- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart +++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart @@ -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> analyze() async { - // TODO(brianwilkerson) Determine whether this await is necessary. - await null; - return PerformanceStatistics.analysis.makeCurrentWhileAsync(() async { + Map analyze() { + return PerformanceStatistics.analysis.makeCurrentWhile(() { return analyzeSync(); }); } diff --git a/pkg/analyzer/lib/src/dart/analysis/results.dart b/pkg/analyzer/lib/src/dart/analysis/results.dart index 2f1eb6c97f2..2b9ac079050 100644 --- a/pkg/analyzer/lib/src/dart/analysis/results.dart +++ b/pkg/analyzer/lib/src/dart/analysis/results.dart @@ -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, diff --git a/pkg/analyzer/lib/src/dart/analysis/session.dart b/pkg/analyzer/lib/src/dart/analysis/session.dart index 982f3bb0adf..b44d556ee33 100644 --- a/pkg/analyzer/lib/src/dart/analysis/session.dart +++ b/pkg/analyzer/lib/src/dart/analysis/session.dart @@ -129,6 +129,20 @@ class AnalysisSessionImpl implements AnalysisSession { return _driver.getResult(path); } + @override + Future getResolvedLibrary(String path) { + _checkConsistency(); + return _driver.getResolvedLibrary(path); + } + + @override + Future getResolvedLibraryByElement( + LibraryElement element) { + _checkConsistency(); + _checkElementOfThisSession(element); + return _driver.getResolvedLibraryByUri(element.source.uri); + } + @override Future 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.'); +// } + } } diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart index 1afe2138d09..0345738a625 100644 --- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart +++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart @@ -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); diff --git a/pkg/analyzer/test/src/dart/analysis/session_test.dart b/pkg/analyzer/test/src/dart/analysis/session_test.dart index 0c007943bcb..a5b9b87a889 100644 --- a/pkg/analyzer/test/src/dart/analysis/session_test.dart +++ b/pkg/analyzer/test/src/dart/analysis/session_test.dart @@ -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 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 = [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 = [ - 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 libraryMap = {}; - ParseResult parseResult; - ResourceProvider resourceProvider; - AnalysisResult result; - SourceFactory sourceFactory; - SourceKind sourceKind; - List topLevelDeclarations; - UnitElementResult unitElementResult; - String unitElementSignature; - - AnalysisOptions get analysisOptions => new AnalysisOptionsImpl(); - - @override - Future getErrors(String path) async { - return errorsResult; - } - - @override - Future getLibraryByUri(String uri) async { - return libraryMap[uri]; - } - - @override - Future getResult(String path, - {bool sendCachedToStream: false}) async { - return result; - } - - @override - Future getSourceKind(String path) async { - return sourceKind; - } - - @override - Future> getTopLevelNameDeclarations( - String name) async { - return topLevelDeclarations; - } - - @override - Future getUnitElement(String path) async { - return unitElementResult; - } - - @override - Future getUnitElementSignature(String path) async { - return unitElementSignature; - } - - @override - dynamic noSuchMethod(Invocation invocation) { - fail('Unexpected invocation of ${invocation.memberName}'); - } - - @override - Future 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, + ); } }