mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 10:33:28 +00:00
Revert 'Import Library' quick fix changes.
This basically reverts https://dart-review.googlesource.com/c/sdk/+/103921 because internally IntelliJ does not provide module dependencies yet. Change-Id: I7717b2841bf3d6391b991875a594c6df9e246ff1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106482 Reviewed-by: Ari Aye <ariaye@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
4adaa1fd56
commit
143e5ef556
|
@ -3,7 +3,6 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
|
||||
|
||||
|
@ -22,10 +21,4 @@ abstract class DartFixContext implements FixContext {
|
|||
* The workspace in which the fix contributor operates.
|
||||
*/
|
||||
ChangeWorkspace get workspace;
|
||||
|
||||
/**
|
||||
* Return top-level declarations with the [name] in libraries that are
|
||||
* available to this context.
|
||||
*/
|
||||
List<TopLevelDeclaration> getTopLevelDeclarations(String name);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import 'package:analysis_server/src/services/correction/assist_internal.dart';
|
|||
import 'package:analysis_server/src/services/correction/change_workspace.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix/analysis_options/fix_generator.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix/manifest/fix_generator.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix/pubspec/fix_generator.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix_internal.dart';
|
||||
|
@ -635,16 +634,7 @@ class EditDomainHandler extends AbstractRequestHandler {
|
|||
int errorLine = lineInfo.getLocation(error.offset).lineNumber;
|
||||
if (errorLine == requestLine) {
|
||||
var workspace = DartChangeWorkspace(server.currentSessions);
|
||||
var context =
|
||||
new DartFixContextImpl(workspace, result, error, (name) {
|
||||
var tracker = server.declarationsTracker;
|
||||
var provider = TopLevelDeclarationsProvider(tracker);
|
||||
return provider.get(
|
||||
result.session.analysisContext,
|
||||
result.path,
|
||||
name,
|
||||
);
|
||||
});
|
||||
var context = new DartFixContextImpl(workspace, result, error);
|
||||
List<Fix> fixes =
|
||||
await new DartFixContributor().computeFixes(context);
|
||||
if (fixes.isNotEmpty) {
|
||||
|
|
|
@ -28,12 +28,7 @@ class FixErrorTask {
|
|||
|
||||
Future<void> fixError(ResolvedUnitResult result, AnalysisError error) async {
|
||||
final workspace = DartChangeWorkspace(listener.server.currentSessions);
|
||||
final dartContext = new DartFixContextImpl(
|
||||
workspace,
|
||||
result,
|
||||
error,
|
||||
(name) => [],
|
||||
);
|
||||
final dartContext = new DartFixContextImpl(workspace, result, error);
|
||||
final processor = new FixProcessor(dartContext);
|
||||
Fix fix = await processor.computeFix();
|
||||
final location = listener.locationFor(result, error.offset, error.length);
|
||||
|
|
|
@ -18,7 +18,6 @@ import 'package:analysis_server/src/services/correction/assist.dart';
|
|||
import 'package:analysis_server/src/services/correction/assist_internal.dart';
|
||||
import 'package:analysis_server/src/services/correction/change_workspace.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix_internal.dart';
|
||||
import 'package:analysis_server/src/services/refactoring/refactoring.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
|
@ -195,14 +194,7 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
|
|||
int errorLine = lineInfo.getLocation(error.offset).lineNumber - 1;
|
||||
if (errorLine >= range.start.line && errorLine <= range.end.line) {
|
||||
var workspace = DartChangeWorkspace(server.currentSessions);
|
||||
var context = new DartFixContextImpl(workspace, unit, error, (name) {
|
||||
var tracker = server.declarationsTracker;
|
||||
return TopLevelDeclarationsProvider(tracker).get(
|
||||
unit.session.analysisContext,
|
||||
unit.path,
|
||||
name,
|
||||
);
|
||||
});
|
||||
var context = new DartFixContextImpl(workspace, unit, error);
|
||||
final fixes = await fixContributor.computeFixes(context);
|
||||
if (fixes.isNotEmpty) {
|
||||
fixes.sort(Fix.SORT_BY_RELEVANCE);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix_internal.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer/error/error.dart';
|
||||
|
@ -120,16 +119,7 @@ class DartFixContextImpl implements DartFixContext {
|
|||
@override
|
||||
final AnalysisError error;
|
||||
|
||||
final List<TopLevelDeclaration> Function(String name)
|
||||
getTopLevelDeclarationsFunction;
|
||||
|
||||
DartFixContextImpl(this.workspace, this.resolveResult, this.error,
|
||||
this.getTopLevelDeclarationsFunction);
|
||||
|
||||
@override
|
||||
List<TopLevelDeclaration> getTopLevelDeclarations(String name) {
|
||||
return getTopLevelDeclarationsFunction(name);
|
||||
}
|
||||
DartFixContextImpl(this.workspace, this.resolveResult, this.error);
|
||||
}
|
||||
|
||||
/// An enumeration of quick fix kinds found in a Dart file.
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analyzer/dart/analysis/analysis_context.dart';
|
||||
import 'package:analyzer/src/services/available_declarations.dart';
|
||||
|
||||
/// Information about a single top-level declaration.
|
||||
class TopLevelDeclaration {
|
||||
/// The path of the library that exports this declaration.
|
||||
final String path;
|
||||
|
||||
/// The URI of the library that exports this declaration.
|
||||
final Uri uri;
|
||||
|
||||
final TopLevelDeclarationKind kind;
|
||||
|
||||
final String name;
|
||||
|
||||
/// Is `true` if the declaration is exported, not declared in the [path].
|
||||
final bool isExported;
|
||||
|
||||
TopLevelDeclaration(
|
||||
this.path,
|
||||
this.uri,
|
||||
this.kind,
|
||||
this.name,
|
||||
this.isExported,
|
||||
);
|
||||
|
||||
@override
|
||||
String toString() => '($path, $uri, $kind, $name, $isExported)';
|
||||
}
|
||||
|
||||
/// Kind of a top-level declaration.
|
||||
///
|
||||
/// We don't need it to be precise, just enough to support quick fixes.
|
||||
enum TopLevelDeclarationKind { type, function, variable }
|
||||
|
||||
class TopLevelDeclarationsProvider {
|
||||
final DeclarationsTracker tracker;
|
||||
|
||||
TopLevelDeclarationsProvider(this.tracker);
|
||||
|
||||
void doTrackerWork() {
|
||||
while (tracker.hasWork) {
|
||||
tracker.doWork();
|
||||
}
|
||||
}
|
||||
|
||||
List<TopLevelDeclaration> get(
|
||||
AnalysisContext analysisContext,
|
||||
String path,
|
||||
String name,
|
||||
) {
|
||||
var declarations = <TopLevelDeclaration>[];
|
||||
|
||||
void addDeclarations(Library library) {
|
||||
for (var declaration in library.declarations) {
|
||||
if (declaration.name != name) continue;
|
||||
|
||||
var kind = _getTopKind(declaration.kind);
|
||||
if (kind == null) continue;
|
||||
|
||||
declarations.add(
|
||||
TopLevelDeclaration(
|
||||
library.path,
|
||||
library.uri,
|
||||
kind,
|
||||
name,
|
||||
declaration.locationLibraryUri != library.uri,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var declarationsContext = tracker.getContext(analysisContext);
|
||||
var libraries = declarationsContext.getLibraries(path);
|
||||
libraries.context.forEach(addDeclarations);
|
||||
libraries.dependencies.forEach(addDeclarations);
|
||||
libraries.sdk.forEach(addDeclarations);
|
||||
|
||||
return declarations;
|
||||
}
|
||||
|
||||
TopLevelDeclarationKind _getTopKind(DeclarationKind kind) {
|
||||
switch (kind) {
|
||||
case DeclarationKind.CLASS:
|
||||
case DeclarationKind.CLASS_TYPE_ALIAS:
|
||||
case DeclarationKind.ENUM:
|
||||
case DeclarationKind.FUNCTION_TYPE_ALIAS:
|
||||
case DeclarationKind.MIXIN:
|
||||
return TopLevelDeclarationKind.type;
|
||||
break;
|
||||
case DeclarationKind.FUNCTION:
|
||||
return TopLevelDeclarationKind.function;
|
||||
break;
|
||||
case DeclarationKind.GETTER:
|
||||
case DeclarationKind.SETTER:
|
||||
case DeclarationKind.VARIABLE:
|
||||
return TopLevelDeclarationKind.variable;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,6 @@ import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
|
|||
import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/utilities.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
|
||||
import 'package:analysis_server/src/services/correction/levenshtein.dart';
|
||||
import 'package:analysis_server/src/services/correction/namespace.dart';
|
||||
import 'package:analysis_server/src/services/correction/strings.dart';
|
||||
|
@ -27,6 +26,7 @@ import 'package:analyzer/dart/element/type.dart';
|
|||
import 'package:analyzer/error/error.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/src/dart/analysis/session_helper.dart';
|
||||
import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
|
||||
import 'package:analyzer/src/dart/ast/ast.dart';
|
||||
import 'package:analyzer/src/dart/ast/token.dart';
|
||||
import 'package:analyzer/src/dart/ast/utilities.dart';
|
||||
|
@ -105,11 +105,7 @@ class DartFixContributor implements FixContributor {
|
|||
// For each fix, put the fix into the HashMap.
|
||||
for (int i = 0; i < allAnalysisErrors.length; i++) {
|
||||
final FixContext fixContextI = new DartFixContextImpl(
|
||||
context.workspace,
|
||||
context.resolveResult,
|
||||
allAnalysisErrors[i],
|
||||
(name) => [],
|
||||
);
|
||||
context.workspace, context.resolveResult, allAnalysisErrors[i]);
|
||||
final FixProcessor processorI = new FixProcessor(fixContextI);
|
||||
final List<Fix> fixesListI = await processorI.compute();
|
||||
for (Fix f in fixesListI) {
|
||||
|
@ -2416,7 +2412,7 @@ class FixProcessor {
|
|||
}
|
||||
// may be there is an existing import,
|
||||
// but it is with prefix and we don't use this prefix
|
||||
var alreadyImportedWithPrefix = new Set<String>();
|
||||
Set<Source> alreadyImportedWithPrefix = new Set<Source>();
|
||||
for (ImportElement imp in unitLibraryElement.imports) {
|
||||
// prepare element
|
||||
LibraryElement libraryElement = imp.importedLibrary;
|
||||
|
@ -2458,7 +2454,7 @@ class FixProcessor {
|
|||
libraryName = libraryElement.source.shortName;
|
||||
}
|
||||
// don't add this library again
|
||||
alreadyImportedWithPrefix.add(libraryElement.source.fullName);
|
||||
alreadyImportedWithPrefix.add(libraryElement.source);
|
||||
// update library
|
||||
String newShowCode = 'show ${showNames.join(', ')}';
|
||||
int offset = showCombinator.offset;
|
||||
|
@ -2477,21 +2473,25 @@ class FixProcessor {
|
|||
}
|
||||
// Find new top-level declarations.
|
||||
{
|
||||
var declarations = await context.getTopLevelDeclarations(name);
|
||||
for (var declaration in declarations) {
|
||||
var declarations = await session.getTopLevelDeclarations(name);
|
||||
for (TopLevelDeclarationInSource declaration in declarations) {
|
||||
// Check the kind.
|
||||
if (!kinds2.contains(declaration.kind)) {
|
||||
if (!kinds2.contains(declaration.declaration.kind)) {
|
||||
continue;
|
||||
}
|
||||
// Check the source.
|
||||
if (alreadyImportedWithPrefix.contains(declaration.path)) {
|
||||
Source librarySource = declaration.source;
|
||||
if (alreadyImportedWithPrefix.contains(librarySource)) {
|
||||
continue;
|
||||
}
|
||||
if (!_isSourceVisibleToLibrary(librarySource)) {
|
||||
continue;
|
||||
}
|
||||
// Compute the fix kind.
|
||||
FixKind fixKind;
|
||||
if (declaration.uri.isScheme('dart')) {
|
||||
if (librarySource.isInSystemLibrary) {
|
||||
fixKind = DartFixKind.IMPORT_LIBRARY_SDK;
|
||||
} else if (_isLibSrcPath(declaration.path)) {
|
||||
} else if (_isLibSrcPath(librarySource.fullName)) {
|
||||
// Bad: non-API.
|
||||
fixKind = DartFixKind.IMPORT_LIBRARY_PROJECT3;
|
||||
} else if (declaration.isExported) {
|
||||
|
@ -2503,8 +2503,8 @@ class FixProcessor {
|
|||
}
|
||||
// Add the fix.
|
||||
var relativeURI =
|
||||
_getRelativeURIFromLibrary(unitLibraryElement, declaration.path);
|
||||
await _addFix_importLibrary(fixKind, declaration.uri, relativeURI);
|
||||
_getRelativeURIFromLibrary(unitLibraryElement, librarySource);
|
||||
await _addFix_importLibrary(fixKind, librarySource.uri, relativeURI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4246,20 +4246,21 @@ class FixProcessor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the relative uri from the passed [library] to the given [path].
|
||||
* If the [path] is not in the LibraryElement, `null` is returned.
|
||||
* Return the relative uri from the passed [library] to the passed
|
||||
* [source]. If the [source] is not in the LibraryElement, `null` is returned.
|
||||
*/
|
||||
String _getRelativeURIFromLibrary(LibraryElement library, String path) {
|
||||
String _getRelativeURIFromLibrary(LibraryElement library, Source source) {
|
||||
var librarySource = library?.librarySource;
|
||||
if (librarySource == null) {
|
||||
return null;
|
||||
}
|
||||
var pathCtx = resourceProvider.pathContext;
|
||||
var libraryDirectory = pathCtx.dirname(librarySource.fullName);
|
||||
var sourceDirectory = pathCtx.dirname(path);
|
||||
if (pathCtx.isWithin(libraryDirectory, path) ||
|
||||
var sourceDirectory = pathCtx.dirname(source.fullName);
|
||||
if (pathCtx.isWithin(libraryDirectory, source.fullName) ||
|
||||
pathCtx.isWithin(sourceDirectory, libraryDirectory)) {
|
||||
String relativeFile = pathCtx.relative(path, from: libraryDirectory);
|
||||
String relativeFile =
|
||||
pathCtx.relative(source.fullName, from: libraryDirectory);
|
||||
return pathCtx.split(relativeFile).join('/');
|
||||
}
|
||||
return null;
|
||||
|
@ -4468,6 +4469,30 @@ class FixProcessor {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the [source] can be imported into current library.
|
||||
*/
|
||||
bool _isSourceVisibleToLibrary(Source source) {
|
||||
String path = source.fullName;
|
||||
|
||||
var contextRoot = context.resolveResult.session.analysisContext.contextRoot;
|
||||
if (contextRoot == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We don't want to use private libraries of other packages.
|
||||
if (source.uri.isScheme('package') && _isLibSrcPath(path)) {
|
||||
return contextRoot.root.contains(path);
|
||||
}
|
||||
|
||||
// We cannot use relative URIs to reference files outside of our package.
|
||||
if (source.uri.isScheme('file')) {
|
||||
return contextRoot.root.contains(path);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _isToListMethodElement(MethodElement method) {
|
||||
if (method.name != 'toList') {
|
||||
return false;
|
||||
|
|
|
@ -135,12 +135,6 @@ class AbstractAnalysisTest with ResourceProviderMixin {
|
|||
handleSuccessfulRequest(request, handler: analysisHandler);
|
||||
}
|
||||
|
||||
void doAllDeclarationsTrackerWork() {
|
||||
while (server.declarationsTracker.hasWork) {
|
||||
server.declarationsTracker.doWork();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset of [search] in [testCode].
|
||||
* Fails if not found.
|
||||
|
|
|
@ -40,7 +40,6 @@ main() {
|
|||
}
|
||||
''');
|
||||
await waitForTasksFinished();
|
||||
doAllDeclarationsTrackerWork();
|
||||
List<AnalysisErrorFixes> errorFixes =
|
||||
await _getFixesAt('Completer<String>');
|
||||
expect(errorFixes, hasLength(1));
|
||||
|
@ -144,6 +143,9 @@ dependencies:
|
|||
bbb: any
|
||||
''');
|
||||
|
||||
// Ensure that the target is analyzed as an implicit source.
|
||||
newFile('/aaa/lib/foo.dart', content: 'import "package:bbb/target.dart";');
|
||||
|
||||
newFolder('/bbb');
|
||||
newFile('/bbb/.packages', content: '''
|
||||
bbb:${toUri('/bbb/lib')}
|
||||
|
@ -161,7 +163,6 @@ bbb:${toUri('/bbb/lib')}
|
|||
_addOverlay(testFile, testCode);
|
||||
|
||||
await waitForTasksFinished();
|
||||
doAllDeclarationsTrackerWork();
|
||||
|
||||
List<String> fixes = (await _getFixesAt('Foo()'))
|
||||
.single
|
||||
|
|
|
@ -7,12 +7,9 @@ import 'dart:async';
|
|||
import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
|
||||
import 'package:analysis_server/src/services/correction/change_workspace.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix_internal.dart';
|
||||
import 'package:analyzer/error/error.dart';
|
||||
import 'package:analyzer/src/dart/analysis/byte_store.dart';
|
||||
import 'package:analyzer/src/dart/error/lint_codes.dart';
|
||||
import 'package:analyzer/src/services/available_declarations.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_common.dart'
|
||||
hide AnalysisError;
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
|
||||
|
@ -253,19 +250,7 @@ abstract class FixProcessorTest extends AbstractSingleUnitTest {
|
|||
|
||||
/// Computes fixes for the given [error] in [testUnit].
|
||||
Future<List<Fix>> _computeFixes(AnalysisError error) async {
|
||||
var tracker = DeclarationsTracker(MemoryByteStore(), resourceProvider);
|
||||
tracker.addContext(driver.analysisContext);
|
||||
|
||||
var context = new DartFixContextImpl(
|
||||
workspace,
|
||||
testAnalysisResult,
|
||||
error,
|
||||
(name) {
|
||||
var provider = TopLevelDeclarationsProvider(tracker);
|
||||
provider.doTrackerWork();
|
||||
return provider.get(driver.analysisContext, testFile, name);
|
||||
},
|
||||
);
|
||||
var context = new DartFixContextImpl(workspace, testAnalysisResult, error);
|
||||
return await new DartFixContributor().computeFixes(context);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:analyzer/dart/analysis/uri_converter.dart';
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/exception/exception.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
|
||||
|
@ -121,6 +122,11 @@ abstract class AnalysisSession {
|
|||
/// complete with [SourceKind.UNKNOWN].
|
||||
Future<SourceKind> getSourceKind(String path);
|
||||
|
||||
/// Return a future that will complete with a list of the top-level
|
||||
/// declarations with the given [name] in all known libraries.
|
||||
Future<List<TopLevelDeclarationInSource>> getTopLevelDeclarations(
|
||||
String name);
|
||||
|
||||
/// Return a future that will complete with information about the results of
|
||||
/// building the element model for the file with the given absolute,
|
||||
/// normalized[path].
|
||||
|
|
|
@ -28,6 +28,7 @@ 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';
|
||||
import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
|
||||
import 'package:analyzer/src/error/codes.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart'
|
||||
show
|
||||
|
@ -215,6 +216,11 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
*/
|
||||
final _referencingNameTasks = <_FilesReferencingNameTask>[];
|
||||
|
||||
/**
|
||||
* The list of tasks to compute top-level declarations of a name.
|
||||
*/
|
||||
final _topLevelNameDeclarationsTasks = <_TopLevelNameDeclarationsTask>[];
|
||||
|
||||
/**
|
||||
* The mapping from the files for which the index was requested using
|
||||
* [getIndex] to the [Completer]s to report the result.
|
||||
|
@ -513,6 +519,9 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
if (_unitElementRequestedFiles.isNotEmpty) {
|
||||
return AnalysisDriverPriority.interactive;
|
||||
}
|
||||
if (_topLevelNameDeclarationsTasks.isNotEmpty) {
|
||||
return AnalysisDriverPriority.interactive;
|
||||
}
|
||||
if (_priorityFiles.isNotEmpty) {
|
||||
for (String path in _priorityFiles) {
|
||||
if (_fileTracker.isFilePending(path)) {
|
||||
|
@ -961,6 +970,19 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a [Future] that completes with top-level declarations with the
|
||||
* given [name] in all known libraries.
|
||||
*/
|
||||
Future<List<TopLevelDeclarationInSource>> getTopLevelNameDeclarations(
|
||||
String name) {
|
||||
_discoverAvailableFiles();
|
||||
var task = new _TopLevelNameDeclarationsTask(this, name);
|
||||
_topLevelNameDeclarationsTasks.add(task);
|
||||
_scheduler.notify(this);
|
||||
return task.completer.future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a [Future] that completes with the [UnitElementResult] for the
|
||||
* file with the given [path], or with `null` if the file cannot be analyzed.
|
||||
|
@ -1186,6 +1208,16 @@ class AnalysisDriver implements AnalysisDriverGeneric {
|
|||
return;
|
||||
}
|
||||
|
||||
// Compute top-level declarations.
|
||||
if (_topLevelNameDeclarationsTasks.isNotEmpty) {
|
||||
_TopLevelNameDeclarationsTask task = _topLevelNameDeclarationsTasks.first;
|
||||
bool isDone = task.perform();
|
||||
if (isDone) {
|
||||
_topLevelNameDeclarationsTasks.remove(task);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Analyze a priority file.
|
||||
if (_priorityFiles.isNotEmpty) {
|
||||
for (String path in _priorityFiles) {
|
||||
|
@ -2515,3 +2547,66 @@ class _FilesReferencingNameTask {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Task that computes top-level declarations for a certain name in all
|
||||
* known libraries.
|
||||
*/
|
||||
class _TopLevelNameDeclarationsTask {
|
||||
final AnalysisDriver driver;
|
||||
final String name;
|
||||
final Completer<List<TopLevelDeclarationInSource>> completer =
|
||||
new Completer<List<TopLevelDeclarationInSource>>();
|
||||
|
||||
final List<TopLevelDeclarationInSource> libraryDeclarations =
|
||||
<TopLevelDeclarationInSource>[];
|
||||
final Set<String> checkedFiles = new Set<String>();
|
||||
final List<String> filesToCheck = <String>[];
|
||||
|
||||
_TopLevelNameDeclarationsTask(this.driver, this.name);
|
||||
|
||||
/**
|
||||
* Perform a single piece of work, and either complete the [completer] and
|
||||
* return `true` to indicate that the task is done, return `false` to indicate
|
||||
* that the task should continue to be run.
|
||||
*/
|
||||
bool perform() {
|
||||
// Prepare files to check.
|
||||
if (filesToCheck.isEmpty) {
|
||||
filesToCheck.addAll(driver.addedFiles.difference(checkedFiles));
|
||||
filesToCheck.addAll(driver.knownFiles.difference(checkedFiles));
|
||||
}
|
||||
|
||||
// If no more files to check, complete and done.
|
||||
if (filesToCheck.isEmpty) {
|
||||
completer.complete(libraryDeclarations);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the next file.
|
||||
String path = filesToCheck.removeLast();
|
||||
if (checkedFiles.add(path)) {
|
||||
FileState file = driver._fsState.getFileForPath(path);
|
||||
if (!file.isPart) {
|
||||
bool isExported = false;
|
||||
|
||||
TopLevelDeclaration declaration;
|
||||
for (FileState part in file.libraryFiles) {
|
||||
declaration ??= part.topLevelDeclarations[name];
|
||||
}
|
||||
|
||||
if (declaration == null) {
|
||||
declaration = file.exportedTopLevelDeclarations[name];
|
||||
isExported = true;
|
||||
}
|
||||
if (declaration != null) {
|
||||
libraryDeclarations.add(new TopLevelDeclarationInSource(
|
||||
file.source, declaration, isExported));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We're not done yet.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'package:analyzer/src/dart/analysis/library_graph.dart';
|
|||
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
|
||||
import 'package:analyzer/src/dart/analysis/referenced_names.dart';
|
||||
import 'package:analyzer/src/dart/analysis/unlinked_api_signature.dart';
|
||||
import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
|
||||
import 'package:analyzer/src/dart/scanner/reader.dart';
|
||||
import 'package:analyzer/src/dart/scanner/scanner.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
|
@ -84,6 +85,11 @@ class FileContentOverlay {
|
|||
* should be called.
|
||||
*/
|
||||
class FileState {
|
||||
/**
|
||||
* The next value for [_exportDeclarationsId].
|
||||
*/
|
||||
static int _exportDeclarationsNextId = 0;
|
||||
|
||||
final FileSystemState _fsState;
|
||||
|
||||
/**
|
||||
|
@ -136,6 +142,10 @@ class FileState {
|
|||
String _transitiveSignature;
|
||||
String _transitiveSignatureLinked;
|
||||
|
||||
Map<String, TopLevelDeclaration> _topLevelDeclarations;
|
||||
Map<String, TopLevelDeclaration> _exportedTopLevelDeclarations;
|
||||
int _exportDeclarationsId = 0;
|
||||
|
||||
/**
|
||||
* The flag that shows whether the file has an error or warning that
|
||||
* might be fixed by a change to another file.
|
||||
|
@ -206,6 +216,18 @@ class FileState {
|
|||
*/
|
||||
List<FileState> get exportedFiles => _exportedFiles;
|
||||
|
||||
/**
|
||||
* Return [TopLevelDeclaration]s exported from the this library file. The
|
||||
* keys to the map are names of declarations.
|
||||
*/
|
||||
Map<String, TopLevelDeclaration> get exportedTopLevelDeclarations {
|
||||
if (AnalysisDriver.useSummary2) {
|
||||
return <String, TopLevelDeclaration>{};
|
||||
}
|
||||
_exportDeclarationsNextId = 1;
|
||||
return _computeExportedDeclarations().declarations;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => uri.hashCode;
|
||||
|
||||
|
@ -303,6 +325,63 @@ class FileState {
|
|||
@visibleForTesting
|
||||
FileStateTestView get test => new FileStateTestView(this);
|
||||
|
||||
/**
|
||||
* Return public top-level declarations declared in the file. The keys to the
|
||||
* map are names of declarations.
|
||||
*/
|
||||
Map<String, TopLevelDeclaration> get topLevelDeclarations {
|
||||
if (AnalysisDriver.useSummary2) {
|
||||
return <String, TopLevelDeclaration>{};
|
||||
}
|
||||
|
||||
if (_topLevelDeclarations == null) {
|
||||
_topLevelDeclarations = <String, TopLevelDeclaration>{};
|
||||
|
||||
void addDeclaration(TopLevelDeclarationKind kind, String name) {
|
||||
if (!name.startsWith('_')) {
|
||||
_topLevelDeclarations[name] = new TopLevelDeclaration(kind, name);
|
||||
}
|
||||
}
|
||||
|
||||
// Add types.
|
||||
for (UnlinkedClass type in unlinked.classes) {
|
||||
addDeclaration(TopLevelDeclarationKind.type, type.name);
|
||||
}
|
||||
for (UnlinkedEnum type in unlinked.enums) {
|
||||
addDeclaration(TopLevelDeclarationKind.type, type.name);
|
||||
}
|
||||
for (UnlinkedClass type in unlinked.mixins) {
|
||||
addDeclaration(TopLevelDeclarationKind.type, type.name);
|
||||
}
|
||||
for (UnlinkedTypedef type in unlinked.typedefs) {
|
||||
addDeclaration(TopLevelDeclarationKind.type, type.name);
|
||||
}
|
||||
// Add functions and variables.
|
||||
Set<String> addedVariableNames = new Set<String>();
|
||||
for (UnlinkedExecutable executable in unlinked.executables) {
|
||||
String name = executable.name;
|
||||
if (executable.kind == UnlinkedExecutableKind.functionOrMethod) {
|
||||
addDeclaration(TopLevelDeclarationKind.function, name);
|
||||
} else if (executable.kind == UnlinkedExecutableKind.getter ||
|
||||
executable.kind == UnlinkedExecutableKind.setter) {
|
||||
if (executable.kind == UnlinkedExecutableKind.setter) {
|
||||
name = name.substring(0, name.length - 1);
|
||||
}
|
||||
if (addedVariableNames.add(name)) {
|
||||
addDeclaration(TopLevelDeclarationKind.variable, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (UnlinkedVariable variable in unlinked.variables) {
|
||||
String name = variable.name;
|
||||
if (addedVariableNames.add(name)) {
|
||||
addDeclaration(TopLevelDeclarationKind.variable, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _topLevelDeclarations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of transitive files - the file itself and all of the
|
||||
* directly or indirectly referenced files.
|
||||
|
@ -472,6 +551,10 @@ class FileState {
|
|||
library.libraryCycle?.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
for (FileState file in _fsState._uriToFile.values) {
|
||||
file._exportedTopLevelDeclarations = null;
|
||||
}
|
||||
}
|
||||
|
||||
// This file is potentially not a library for its previous parts anymore.
|
||||
|
@ -538,6 +621,69 @@ class FileState {
|
|||
@override
|
||||
String toString() => path ?? '<unresolved>';
|
||||
|
||||
/**
|
||||
* Compute the full or partial map of exported declarations for this library.
|
||||
*/
|
||||
_ExportedDeclarations _computeExportedDeclarations() {
|
||||
// If we know exported declarations, return them.
|
||||
if (_exportedTopLevelDeclarations != null) {
|
||||
return new _ExportedDeclarations(0, _exportedTopLevelDeclarations);
|
||||
}
|
||||
|
||||
// If we are already computing exported declarations for this library,
|
||||
// report that we found a cycle.
|
||||
if (_exportDeclarationsId != 0) {
|
||||
return new _ExportedDeclarations(_exportDeclarationsId, null);
|
||||
}
|
||||
|
||||
var declarations = <String, TopLevelDeclaration>{};
|
||||
|
||||
// Give each library a unique identifier.
|
||||
_exportDeclarationsId = _exportDeclarationsNextId++;
|
||||
|
||||
// Append the exported declarations.
|
||||
int firstCycleId = 0;
|
||||
for (int i = 0; i < _exportedFiles.length; i++) {
|
||||
var exported = _exportedFiles[i]._computeExportedDeclarations();
|
||||
if (exported.declarations != null) {
|
||||
for (TopLevelDeclaration t in exported.declarations.values) {
|
||||
if (_exportFilters[i].accepts(t.name)) {
|
||||
declarations[t.name] = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exported.firstCycleId > 0) {
|
||||
if (firstCycleId == 0 || firstCycleId > exported.firstCycleId) {
|
||||
firstCycleId = exported.firstCycleId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this library is the first component of the cycle, then we are at
|
||||
// the beginning of this cycle, and combination of partial export
|
||||
// namespaces of other exported libraries and declarations of this library
|
||||
// is the full export namespace of this library.
|
||||
if (firstCycleId != 0 && firstCycleId == _exportDeclarationsId) {
|
||||
firstCycleId = 0;
|
||||
}
|
||||
|
||||
// We're done with this library, successfully or not.
|
||||
_exportDeclarationsId = 0;
|
||||
|
||||
// Append the library declarations.
|
||||
for (FileState file in libraryFiles) {
|
||||
declarations.addAll(file.topLevelDeclarations);
|
||||
}
|
||||
|
||||
// Record the declarations only if it is the full result.
|
||||
if (firstCycleId == 0) {
|
||||
_exportedTopLevelDeclarations = declarations;
|
||||
}
|
||||
|
||||
// Return the full or partial result.
|
||||
return new _ExportedDeclarations(firstCycleId, declarations);
|
||||
}
|
||||
|
||||
CompilationUnit _createEmptyCompilationUnit(FeatureSet featureSet) {
|
||||
var token = new Token.eof(0);
|
||||
return astFactory.compilationUnit2(
|
||||
|
@ -573,6 +719,7 @@ class FileState {
|
|||
_definedTopLevelNames = null;
|
||||
_definedClassMemberNames = null;
|
||||
_referencedNames = null;
|
||||
_topLevelDeclarations = null;
|
||||
|
||||
if (_driverUnlinkedUnit != null) {
|
||||
for (var name in _driverUnlinkedUnit.subtypedNames) {
|
||||
|
@ -687,6 +834,10 @@ class FileState {
|
|||
library.libraryCycle?.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
for (FileState file in _fsState._uriToFile.values) {
|
||||
file._exportedTopLevelDeclarations = null;
|
||||
}
|
||||
}
|
||||
|
||||
// This file is potentially not a library for its previous parts anymore.
|
||||
|
@ -1109,6 +1260,23 @@ class FileSystemStateTestView {
|
|||
.where((f) => f._libraryCycle == null)
|
||||
.toSet();
|
||||
}
|
||||
|
||||
Set<FileState> get librariesWithComputedExportedDeclarations {
|
||||
return state._uriToFile.values
|
||||
.where((f) => !f.isPart && f._exportedTopLevelDeclarations != null)
|
||||
.toSet();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of computing exported top-level declarations.
|
||||
* It can be full (when [firstCycleId] is zero), or partial (when a cycle)
|
||||
*/
|
||||
class _ExportedDeclarations {
|
||||
final int firstCycleId;
|
||||
final Map<String, TopLevelDeclaration> declarations;
|
||||
|
||||
_ExportedDeclarations(this.firstCycleId, this.declarations);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:analyzer/dart/analysis/uri_converter.dart';
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/src/dart/analysis/driver.dart' as driver;
|
||||
import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
|
||||
import 'package:analyzer/src/dart/analysis/uri_converter.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
|
@ -157,6 +158,13 @@ class AnalysisSessionImpl implements AnalysisSession {
|
|||
return _driver.getSourceKind(path);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TopLevelDeclarationInSource>> getTopLevelDeclarations(
|
||||
String name) {
|
||||
_checkConsistency();
|
||||
return _driver.getTopLevelNameDeclarations(name);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<UnitElementResult> getUnitElement(String path) {
|
||||
_checkConsistency();
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
|
||||
/**
|
||||
* Information about a single top-level declaration.
|
||||
*/
|
||||
class TopLevelDeclaration {
|
||||
final TopLevelDeclarationKind kind;
|
||||
final String name;
|
||||
|
||||
TopLevelDeclaration(this.kind, this.name);
|
||||
|
||||
@override
|
||||
String toString() => '($kind, $name)';
|
||||
}
|
||||
|
||||
/**
|
||||
* A declaration in a source.
|
||||
*/
|
||||
class TopLevelDeclarationInSource {
|
||||
/**
|
||||
* The declaring source.
|
||||
*/
|
||||
final Source source;
|
||||
|
||||
/**
|
||||
* The declaration.
|
||||
*/
|
||||
final TopLevelDeclaration declaration;
|
||||
|
||||
/**
|
||||
* Is `true` if the [declaration] is exported, not declared in the [source].
|
||||
*/
|
||||
final bool isExported;
|
||||
|
||||
TopLevelDeclarationInSource(this.source, this.declaration, this.isExported);
|
||||
|
||||
@override
|
||||
String toString() => '($source, $declaration, $isExported)';
|
||||
}
|
||||
|
||||
/**
|
||||
* Kind of a top-level declaration.
|
||||
*
|
||||
* We don't need it to be precise, just enough to support quick fixes.
|
||||
*/
|
||||
enum TopLevelDeclarationKind { type, function, variable }
|
|
@ -15,6 +15,7 @@ import 'package:analyzer/src/dart/analysis/driver.dart';
|
|||
import 'package:analyzer/src/dart/analysis/file_state.dart';
|
||||
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
|
||||
import 'package:analyzer/src/dart/analysis/status.dart';
|
||||
import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
|
||||
import 'package:analyzer/src/dart/constant/evaluation.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:analyzer/src/error/codes.dart';
|
||||
|
@ -2227,6 +2228,102 @@ var A2 = B1;
|
|||
expect(await driver.getSourceKind(path), SourceKind.PART);
|
||||
}
|
||||
|
||||
test_getTopLevelNameDeclarations() async {
|
||||
var a = convertPath('/test/lib/a.dart');
|
||||
var b = convertPath('/test/lib/b.dart');
|
||||
var c = convertPath('/test/lib/c.dart');
|
||||
var d = convertPath('/test/lib/d.dart');
|
||||
|
||||
newFile(a, content: 'class A {}');
|
||||
newFile(b, content: 'export "a.dart"; class B {}');
|
||||
newFile(c, content: 'import "d.dart"; class C {}');
|
||||
newFile(d, content: 'class D {}');
|
||||
|
||||
driver.addFile(a);
|
||||
driver.addFile(b);
|
||||
driver.addFile(c);
|
||||
// Don't add d.dart, it is referenced implicitly.
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('A'), [a, b], [false, true]);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('B'), [b], [false]);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('C'), [c], [false]);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('D'), [d], [false]);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('X'), [], []);
|
||||
}
|
||||
|
||||
test_getTopLevelNameDeclarations_discover() async {
|
||||
var t = convertPath('/test/lib/test.dart');
|
||||
var a1 = convertPath('/aaa/lib/a1.dart');
|
||||
var a2 = convertPath('/aaa/lib/src/a2.dart');
|
||||
var b = convertPath('/bbb/lib/b.dart');
|
||||
var c = convertPath('/ccc/lib/c.dart');
|
||||
|
||||
newFile(t, content: 'class T {}');
|
||||
newFile(a1, content: 'class A1 {}');
|
||||
newFile(a2, content: 'class A2 {}');
|
||||
newFile(b, content: 'class B {}');
|
||||
newFile(c, content: 'class C {}');
|
||||
|
||||
driver.addFile(t);
|
||||
// Don't add a1.dart, a2.dart, or b.dart - they should be discovered.
|
||||
// And c.dart is not in .packages, so should not be discovered.
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('T'), [t], [false]);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('A1'), [a1], [false]);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('A2'), [a2], [false]);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('B'), [b], [false]);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('C'), [], []);
|
||||
}
|
||||
|
||||
test_getTopLevelNameDeclarations_parts() async {
|
||||
var a = convertPath('/test/lib/a.dart');
|
||||
var b = convertPath('/test/lib/b.dart');
|
||||
var c = convertPath('/test/lib/c.dart');
|
||||
|
||||
newFile(a, content: r'''
|
||||
library lib;
|
||||
part 'b.dart';
|
||||
part 'c.dart';
|
||||
class A {}
|
||||
''');
|
||||
newFile(b, content: 'part of lib; class B {}');
|
||||
newFile(c, content: 'part of lib; class C {}');
|
||||
|
||||
driver.addFile(a);
|
||||
driver.addFile(b);
|
||||
driver.addFile(c);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('A'), [a], [false]);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('B'), [a], [false]);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('C'), [a], [false]);
|
||||
|
||||
_assertTopLevelDeclarations(
|
||||
await driver.getTopLevelNameDeclarations('X'), [], []);
|
||||
}
|
||||
|
||||
test_getUnitElement() async {
|
||||
String content = r'''
|
||||
foo(int p) {}
|
||||
|
@ -3416,6 +3513,20 @@ var v = 0
|
|||
}
|
||||
}
|
||||
|
||||
void _assertTopLevelDeclarations(
|
||||
List<TopLevelDeclarationInSource> declarations,
|
||||
List<String> expectedFiles,
|
||||
List<bool> expectedIsExported) {
|
||||
expect(expectedFiles, hasLength(expectedIsExported.length));
|
||||
for (int i = 0; i < expectedFiles.length; i++) {
|
||||
expect(declarations,
|
||||
contains(predicate((TopLevelDeclarationInSource declaration) {
|
||||
return declaration.source.fullName == expectedFiles[i] &&
|
||||
declaration.isExported == expectedIsExported[i];
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
void _expectCircularityError(EvaluationResultImpl evaluationResult) {
|
||||
expect(evaluationResult, isNotNull);
|
||||
expect(evaluationResult.value, isNull);
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:analyzer/src/dart/analysis/driver.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/top_level_declaration.dart';
|
||||
import 'package:analyzer/src/file_system/file_system.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart'
|
||||
show AnalysisOptions, AnalysisOptionsImpl;
|
||||
|
@ -104,6 +105,249 @@ var G, H;
|
|||
unorderedEquals(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']));
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_cycle() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String b = convertPath('/aaa/lib/b.dart');
|
||||
String c = convertPath('/aaa/lib/c.dart');
|
||||
newFile(a, content: r'''
|
||||
export 'b.dart';
|
||||
class A {}
|
||||
''');
|
||||
newFile(b, content: r'''
|
||||
export 'c.dart';
|
||||
class B {}
|
||||
''');
|
||||
newFile(c, content: r'''
|
||||
export 'a.dart';
|
||||
class C {}
|
||||
''');
|
||||
_assertExportedTopLevelDeclarations(a, ['A', 'B', 'C']);
|
||||
|
||||
// We asked for 'a', and it was computed.
|
||||
// But 'b' and 'c' are not computed, because we detect that there is
|
||||
// cycle with 'a', so we cannot get all exported declarations of 'a'.
|
||||
_assertHasComputedExportedDeclarations([a]);
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_cycle_anotherOutsideCycle() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String b = convertPath('/aaa/lib/b.dart');
|
||||
String c = convertPath('/aaa/lib/c.dart');
|
||||
String d = convertPath('/aaa/lib/d.dart');
|
||||
newFile(a, content: r'''
|
||||
export 'b.dart';
|
||||
class A {}
|
||||
''');
|
||||
newFile(b, content: r'''
|
||||
export 'c.dart';
|
||||
class B {}
|
||||
''');
|
||||
newFile(c, content: r'''
|
||||
export 'b.dart';
|
||||
export 'd.dart';
|
||||
class C {}
|
||||
''');
|
||||
newFile(d, content: r'''
|
||||
class D {}
|
||||
''');
|
||||
_assertExportedTopLevelDeclarations(a, ['A', 'B', 'C', 'D']);
|
||||
|
||||
// To compute 'a' we compute 'b'.
|
||||
// But 'c' is not computed, because of the cycle [b, c].
|
||||
// However 'd' is not a part of a cycle, so it is computed too.
|
||||
_assertHasComputedExportedDeclarations([a, b, d]);
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_cycle_onSequence() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String b = convertPath('/aaa/lib/b.dart');
|
||||
String c = convertPath('/aaa/lib/c.dart');
|
||||
String d = convertPath('/aaa/lib/d.dart');
|
||||
String e = convertPath('/aaa/lib/e.dart');
|
||||
newFile(a, content: r'''
|
||||
export 'b.dart';
|
||||
class A {}
|
||||
''');
|
||||
newFile(b, content: r'''
|
||||
export 'c.dart';
|
||||
class B {}
|
||||
''');
|
||||
newFile(c, content: r'''
|
||||
export 'd.dart';
|
||||
class C {}
|
||||
''');
|
||||
newFile(d, content: r'''
|
||||
export 'e.dart';
|
||||
class D {}
|
||||
''');
|
||||
newFile(e, content: r'''
|
||||
export 'c.dart';
|
||||
class E {}
|
||||
''');
|
||||
// We compute 'a'.
|
||||
// To compute it we also compute 'b' and 'c'.
|
||||
// But 'd' and 'e' are not computed, because of the cycle [c, d, e].
|
||||
_assertExportedTopLevelDeclarations(a, ['A', 'B', 'C', 'D', 'E']);
|
||||
_assertHasComputedExportedDeclarations([a, b, c]);
|
||||
|
||||
// We compute 'd', and try to compute 'e', because 'd' needs 'e'; 'e' can
|
||||
// be computed because 'c' is ready, so the cycle [c, d, e] is broken.
|
||||
_assertExportedTopLevelDeclarations(d, ['C', 'D', 'E']);
|
||||
_assertHasComputedExportedDeclarations([a, b, c, d, e]);
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_export() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String b = convertPath('/aaa/lib/b.dart');
|
||||
newFile(a, content: r'''
|
||||
class A {}
|
||||
''');
|
||||
newFile(b, content: r'''
|
||||
export 'a.dart';
|
||||
class B {}
|
||||
''');
|
||||
_assertExportedTopLevelDeclarations(b, ['A', 'B']);
|
||||
_assertHasComputedExportedDeclarations([a, b]);
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_export2_show() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String b = convertPath('/aaa/lib/b.dart');
|
||||
String c = convertPath('/aaa/lib/c.dart');
|
||||
newFile(a, content: r'''
|
||||
class A1 {}
|
||||
class A2 {}
|
||||
class A3 {}
|
||||
''');
|
||||
newFile(b, content: r'''
|
||||
export 'a.dart' show A1, A2;
|
||||
class B1 {}
|
||||
class B2 {}
|
||||
''');
|
||||
newFile(c, content: r'''
|
||||
export 'b.dart' show A2, A3, B1;
|
||||
class C {}
|
||||
''');
|
||||
_assertExportedTopLevelDeclarations(c, ['A2', 'B1', 'C']);
|
||||
_assertHasComputedExportedDeclarations([a, b, c]);
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_export_flushOnChange() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String b = convertPath('/aaa/lib/b.dart');
|
||||
newFile(a, content: r'''
|
||||
class A {}
|
||||
''');
|
||||
newFile(b, content: r'''
|
||||
export 'a.dart';
|
||||
class B {}
|
||||
''');
|
||||
|
||||
// Initial exported declarations.
|
||||
_assertExportedTopLevelDeclarations(b, ['A', 'B']);
|
||||
|
||||
// Update a.dart, so a.dart and b.dart exported declarations are flushed.
|
||||
newFile(a, content: 'class A {} class A2 {}');
|
||||
fileSystemState.getFileForPath(a).refresh();
|
||||
_assertExportedTopLevelDeclarations(b, ['A', 'A2', 'B']);
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_export_hide() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String b = convertPath('/aaa/lib/b.dart');
|
||||
newFile(a, content: r'''
|
||||
class A1 {}
|
||||
class A2 {}
|
||||
class A3 {}
|
||||
''');
|
||||
newFile(b, content: r'''
|
||||
export 'a.dart' hide A2;
|
||||
class B {}
|
||||
''');
|
||||
_assertExportedTopLevelDeclarations(b, ['A1', 'A3', 'B']);
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_export_preferLocal() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String b = convertPath('/aaa/lib/b.dart');
|
||||
newFile(a, content: r'''
|
||||
class V {}
|
||||
''');
|
||||
newFile(b, content: r'''
|
||||
export 'a.dart';
|
||||
int V;
|
||||
''');
|
||||
FileState file = fileSystemState.getFileForPath(b);
|
||||
Map<String, TopLevelDeclaration> declarations =
|
||||
file.exportedTopLevelDeclarations;
|
||||
expect(declarations.keys, unorderedEquals(['V']));
|
||||
expect(declarations['V'].kind, TopLevelDeclarationKind.variable);
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_export_show() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String b = convertPath('/aaa/lib/b.dart');
|
||||
newFile(a, content: r'''
|
||||
class A1 {}
|
||||
class A2 {}
|
||||
''');
|
||||
newFile(b, content: r'''
|
||||
export 'a.dart' show A2;
|
||||
class B {}
|
||||
''');
|
||||
_assertExportedTopLevelDeclarations(b, ['A2', 'B']);
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_export_show2() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String b = convertPath('/aaa/lib/b.dart');
|
||||
String c = convertPath('/aaa/lib/c.dart');
|
||||
String d = convertPath('/aaa/lib/d.dart');
|
||||
newFile(a, content: r'''
|
||||
export 'b.dart' show Foo;
|
||||
export 'c.dart' show Bar;
|
||||
''');
|
||||
newFile(b, content: r'''
|
||||
export 'd.dart';
|
||||
''');
|
||||
newFile(c, content: r'''
|
||||
export 'd.dart';
|
||||
''');
|
||||
newFile(d, content: r'''
|
||||
class Foo {}
|
||||
class Bar {}
|
||||
''');
|
||||
_assertExportedTopLevelDeclarations(a, ['Foo', 'Bar']);
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_import() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String b = convertPath('/aaa/lib/b.dart');
|
||||
newFile(a, content: r'''
|
||||
class A {}
|
||||
''');
|
||||
newFile(b, content: r'''
|
||||
import 'a.dart';
|
||||
class B {}
|
||||
''');
|
||||
_assertExportedTopLevelDeclarations(b, ['B']);
|
||||
}
|
||||
|
||||
test_exportedTopLevelDeclarations_parts() {
|
||||
String a = convertPath('/aaa/lib/a.dart');
|
||||
String a2 = convertPath('/aaa/lib/a2.dart');
|
||||
newFile(a, content: r'''
|
||||
library lib;
|
||||
part 'a2.dart';
|
||||
class A1 {}
|
||||
''');
|
||||
newFile(a2, content: r'''
|
||||
part of lib;
|
||||
class A2 {}
|
||||
''');
|
||||
_assertExportedTopLevelDeclarations(a, ['A1', 'A2']);
|
||||
}
|
||||
|
||||
test_getFileForPath_doesNotExist() {
|
||||
String path = convertPath('/aaa/lib/a.dart');
|
||||
FileState file = fileSystemState.getFileForPath(path);
|
||||
|
@ -597,6 +841,52 @@ class Z implements C, D {}
|
|||
expect(file.referencedNames, unorderedEquals(['A', 'B', 'C', 'D']));
|
||||
}
|
||||
|
||||
test_topLevelDeclarations() {
|
||||
String path = convertPath('/aaa/lib/a.dart');
|
||||
newFile(path, content: r'''
|
||||
class C {}
|
||||
typedef F();
|
||||
enum E {E1, E2}
|
||||
mixin M {}
|
||||
void f() {}
|
||||
var V1;
|
||||
get V2 => null;
|
||||
set V3(_) {}
|
||||
get V4 => null;
|
||||
set V4(_) {}
|
||||
|
||||
class _C {}
|
||||
typedef _F();
|
||||
enum _E {E1, E2}
|
||||
mixin _M {}
|
||||
void _f() {}
|
||||
var _V1;
|
||||
get _V2 => null;
|
||||
set _V3(_) {}
|
||||
''');
|
||||
FileState file = fileSystemState.getFileForPath(path);
|
||||
|
||||
Map<String, TopLevelDeclaration> declarations = file.topLevelDeclarations;
|
||||
|
||||
void assertHas(String name, TopLevelDeclarationKind kind) {
|
||||
expect(declarations[name]?.kind, kind);
|
||||
}
|
||||
|
||||
expect(
|
||||
declarations.keys,
|
||||
unorderedEquals(['C', 'F', 'E', 'M', 'f', 'V1', 'V2', 'V3', 'V4']),
|
||||
);
|
||||
assertHas('C', TopLevelDeclarationKind.type);
|
||||
assertHas('F', TopLevelDeclarationKind.type);
|
||||
assertHas('E', TopLevelDeclarationKind.type);
|
||||
assertHas('M', TopLevelDeclarationKind.type);
|
||||
assertHas('f', TopLevelDeclarationKind.function);
|
||||
assertHas('V1', TopLevelDeclarationKind.variable);
|
||||
assertHas('V2', TopLevelDeclarationKind.variable);
|
||||
assertHas('V3', TopLevelDeclarationKind.variable);
|
||||
assertHas('V4', TopLevelDeclarationKind.variable);
|
||||
}
|
||||
|
||||
test_transitiveSignature() {
|
||||
String pa = convertPath('/aaa/lib/a.dart');
|
||||
String pb = convertPath('/aaa/lib/b.dart');
|
||||
|
@ -657,11 +947,24 @@ part of 'a.dart';
|
|||
expect(bSignature, isNot(aSignature));
|
||||
}
|
||||
|
||||
void _assertExportedTopLevelDeclarations(String path, List<String> expected) {
|
||||
FileState file = fileSystemState.getFileForPath(path);
|
||||
Map<String, TopLevelDeclaration> declarations =
|
||||
file.exportedTopLevelDeclarations;
|
||||
expect(declarations.keys, unorderedEquals(expected));
|
||||
}
|
||||
|
||||
void _assertFilesWithoutLibraryCycle(List<FileState> expected) {
|
||||
var actual = fileSystemState.test.filesWithoutLibraryCycle;
|
||||
expect(_excludeSdk(actual), unorderedEquals(expected));
|
||||
}
|
||||
|
||||
void _assertHasComputedExportedDeclarations(List<String> expectedPathList) {
|
||||
FileSystemStateTestView test = fileSystemState.test;
|
||||
expect(test.librariesWithComputedExportedDeclarations.map((f) => f.path),
|
||||
unorderedEquals(expectedPathList));
|
||||
}
|
||||
|
||||
void _assertIsUnresolvedFile(FileState file) {
|
||||
expect(file.path, isNull);
|
||||
expect(file.uri, isNull);
|
||||
|
|
Loading…
Reference in a new issue