Version 2.14.0-66.0.dev
Merge commit '9d015feeaf5617205dc431568c024fed7eec23d9' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index ca2f072..7109004 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
"constraint, update this by running tools/generate_package_config.dart."
],
"configVersion": 2,
- "generated": "2021-04-29T10:51:01.320402",
+ "generated": "2021-04-29T17:48:25.665083",
"generator": "tools/generate_package_config.dart",
"packages": [
{
diff --git a/DEPS b/DEPS
index bbac264..78974f4 100644
--- a/DEPS
+++ b/DEPS
@@ -73,7 +73,7 @@
# Revisions of /third_party/* dependencies.
"args_rev": "d8fea36c10ef96797be02e3d132d572445cd86f4",
- "async_rev": "06774f59a7cf9780e08148298d438c1043e2b063",
+ "async_rev": "cde00b89bd3c19e877b21de2c02282d4d311d0a5",
"bazel_worker_rev": "0885637b037979afbf5bcd05fd748b309fd669c0",
"benchmark_harness_rev": "c546dbd9f639f75cd2f75de8df2eb9f8ea15e8e7",
"boolean_selector_rev": "665e6921ab246569420376f827bff4585dff0b14",
diff --git a/pkg/analysis_server/bin/server.dart b/pkg/analysis_server/bin/server.dart
index 7810101..a873eb1 100644
--- a/pkg/analysis_server/bin/server.dart
+++ b/pkg/analysis_server/bin/server.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart = 2.9
+
import 'package:analysis_server/starter.dart';
/// Create and run an analysis server.
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index 4afc13e..7f10b84 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -104,9 +104,13 @@
/// - parameter
static const label = SemanticTokenModifiers('label');
+ /// A modifier applied to constructors to allow colouring them differently
+ /// to class names that are not constructors.
+ static const constructor = SemanticTokenModifiers('constructor');
+
/// All custom semantic token modifiers, used to populate the LSP Legend which must
/// include all used modifiers.
- static const values = [control, label];
+ static const values = [control, label, constructor];
}
abstract class CustomSemanticTokenTypes {
diff --git a/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart b/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
index 66e4b26..c83e229 100644
--- a/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
@@ -15,6 +15,7 @@
HighlightRegionType.COMMENT_DOCUMENTATION: {
SemanticTokenModifiers.documentation
},
+ HighlightRegionType.CONSTRUCTOR: {CustomSemanticTokenModifiers.constructor},
HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_DECLARATION: {
SemanticTokenModifiers.declaration
},
diff --git a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
index 6fcc641..20d0bfc 100644
--- a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
+++ b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
@@ -82,6 +82,46 @@
expect(decoded, equals(expected));
}
+ Future<void> test_class_constructors() async {
+ final content = '''
+ class MyClass {
+ MyClass();
+ MyClass.named();
+ }
+
+ final a = MyClass();
+ final b = MyClass.named();
+ ''';
+
+ final expected = [
+ _Token('class', SemanticTokenTypes.keyword),
+ _Token('MyClass', SemanticTokenTypes.class_),
+ _Token('MyClass', SemanticTokenTypes.class_),
+ _Token('MyClass', SemanticTokenTypes.class_),
+ _Token('named', SemanticTokenTypes.class_,
+ [CustomSemanticTokenModifiers.constructor]),
+ _Token('final', SemanticTokenTypes.keyword),
+ _Token('a', SemanticTokenTypes.variable,
+ [SemanticTokenModifiers.declaration]),
+ _Token('MyClass', SemanticTokenTypes.class_,
+ [CustomSemanticTokenModifiers.constructor]),
+ _Token('final', SemanticTokenTypes.keyword),
+ _Token('b', SemanticTokenTypes.variable,
+ [SemanticTokenModifiers.declaration]),
+ _Token('MyClass', SemanticTokenTypes.class_,
+ [CustomSemanticTokenModifiers.constructor]),
+ _Token('named', SemanticTokenTypes.class_,
+ [CustomSemanticTokenModifiers.constructor])
+ ];
+
+ await initialize();
+ await openFile(mainFileUri, withoutMarkers(content));
+
+ final tokens = await getSemanticTokens(mainFileUri);
+ final decoded = decodeSemanticTokens(content, tokens);
+ expect(decoded, equals(expected));
+ }
+
Future<void> test_class_fields() async {
final content = '''
class MyClass {
@@ -119,7 +159,8 @@
_Token('final', SemanticTokenTypes.keyword),
_Token('a', SemanticTokenTypes.variable,
[SemanticTokenModifiers.declaration]),
- _Token('MyClass', SemanticTokenTypes.class_),
+ _Token('MyClass', SemanticTokenTypes.class_,
+ [CustomSemanticTokenModifiers.constructor]),
_Token('print', SemanticTokenTypes.function),
_Token('a', SemanticTokenTypes.variable),
_Token('myField', SemanticTokenTypes.property),
@@ -197,7 +238,8 @@
_Token('final', SemanticTokenTypes.keyword),
_Token('a', SemanticTokenTypes.variable,
[SemanticTokenModifiers.declaration]),
- _Token('MyClass', SemanticTokenTypes.class_),
+ _Token('MyClass', SemanticTokenTypes.class_,
+ [CustomSemanticTokenModifiers.constructor]),
_Token('print', SemanticTokenTypes.function),
_Token('a', SemanticTokenTypes.variable),
_Token('myGetter', SemanticTokenTypes.property),
@@ -255,7 +297,8 @@
_Token('final', SemanticTokenTypes.keyword),
_Token('a', SemanticTokenTypes.variable,
[SemanticTokenModifiers.declaration]),
- _Token('MyClass', SemanticTokenTypes.class_),
+ _Token('MyClass', SemanticTokenTypes.class_,
+ [CustomSemanticTokenModifiers.constructor]),
_Token('a', SemanticTokenTypes.variable),
_Token('myMethod', SemanticTokenTypes.method),
_Token('MyClass', SemanticTokenTypes.class_),
@@ -443,7 +486,8 @@
_Token('a', SemanticTokenTypes.variable,
[SemanticTokenModifiers.declaration]),
_Token('new', SemanticTokenTypes.keyword),
- _Token('Object', SemanticTokenTypes.class_),
+ _Token('Object', SemanticTokenTypes.class_,
+ [CustomSemanticTokenModifiers.constructor]),
_Token('await', SemanticTokenTypes.keyword,
[CustomSemanticTokenModifiers.control]),
_Token('null', SemanticTokenTypes.keyword),
diff --git a/pkg/analyzer/lib/src/dart/micro/library_graph.dart b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
index 2c10c45..0d6ea0b 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
@@ -518,6 +518,10 @@
/// to batch file reads in systems where file fetches are expensive.
final void Function(List<String> paths)? prefetchFiles;
+ /// A function that returns true if the given file path is likely to be that
+ /// of a file that is generated.
+ final bool Function(String path)? isGenerated;
+
final FileSystemStateTimers timers2 = FileSystemStateTimers();
final FileSystemStateTestView testView = FileSystemStateTestView();
@@ -533,6 +537,7 @@
this.featureSetProvider,
this.getFileDigest,
this.prefetchFiles,
+ this.isGenerated,
);
/// Update the state to reflect the fact that the file with the given [path]
@@ -662,6 +667,19 @@
return file;
}
+ /// Returns a list of files whose contents contains the given string.
+ /// Generated files are not included in the search.
+ List<String> getFilesContaining(String value) {
+ var result = <String>[];
+ _pathToFile.forEach((path, file) {
+ var genFile = isGenerated == null ? false : isGenerated!(path);
+ if (!genFile && file.getContent().contains(value)) {
+ result.add(path);
+ }
+ });
+ return result;
+ }
+
String? getPathForUri(Uri uri) {
var source = _sourceFactory.forUri2(uri);
if (source == null) {
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index e7dff9b..c24aa37 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -18,10 +18,12 @@
import 'package:analyzer/src/dart/analysis/feature_set_provider.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:analyzer/src/dart/analysis/results.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/micro/analysis_context.dart';
import 'package:analyzer/src/dart/micro/cider_byte_store.dart';
import 'package:analyzer/src/dart/micro/library_analyzer.dart';
import 'package:analyzer/src/dart/micro/library_graph.dart';
+import 'package:analyzer/src/dart/micro/utils.dart';
import 'package:analyzer/src/exception/exception.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
import 'package:analyzer/src/generated/source.dart';
@@ -36,11 +38,25 @@
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer/src/workspace/workspace.dart';
+import 'package:collection/collection.dart';
import 'package:yaml/yaml.dart';
const M = 1024 * 1024 /*1 MiB*/;
const memoryCacheSize = 200 * M;
+class CiderSearchMatch {
+ final String path;
+ final List<int> offsets;
+
+ CiderSearchMatch(this.path, this.offsets);
+
+ @override
+ bool operator ==(Object object) =>
+ object is CiderSearchMatch &&
+ path == object.path &&
+ const ListEquality<int>().equals(offsets, object.offsets);
+}
+
class FileContext {
final AnalysisOptionsImpl analysisOptions;
final FileState file;
@@ -54,13 +70,15 @@
CiderByteStore byteStore;
final SourceFactory sourceFactory;
- /*
- * A function that returns the digest for a file as a String. The function
- * returns a non null value, can return an empty string if file does
- * not exist/has no contents.
- */
+ /// A function that returns the digest for a file as a String. The function
+ /// returns a non null value, can return an empty string if file does
+ /// not exist/has no contents.
final String Function(String path) getFileDigest;
+ /// A function that returns true if the given file path is likely to be that
+ /// of a file that is generated.
+ final bool Function(String path)? isGenerated;
+
/// A function that fetches the given list of files. This function can be used
/// to batch file reads in systems where file fetches are expensive.
final void Function(List<String> paths)? prefetchFiles;
@@ -96,6 +114,7 @@
String Function(String path) getFileDigest,
void Function(List<String> paths)? prefetchFiles, {
required Workspace workspace,
+ bool Function(String path)? isGenerated,
@deprecated Duration? libraryContextResetTimeout,
}) : this.from(
logger: logger,
@@ -104,6 +123,8 @@
getFileDigest: getFileDigest,
prefetchFiles: prefetchFiles,
workspace: workspace,
+ isGenerated: isGenerated,
+
// ignore: deprecated_member_use_from_same_package
libraryContextResetTimeout: libraryContextResetTimeout,
);
@@ -115,6 +136,7 @@
required String Function(String path) getFileDigest,
required void Function(List<String> paths)? prefetchFiles,
required Workspace workspace,
+ bool Function(String path)? isGenerated,
CiderByteStore? byteStore,
@deprecated Duration? libraryContextResetTimeout,
}) : logger = logger,
@@ -123,6 +145,7 @@
getFileDigest = getFileDigest,
prefetchFiles = prefetchFiles,
workspace = workspace,
+ isGenerated = isGenerated,
byteStore = byteStore ?? CiderCachedByteStore(memoryCacheSize);
/// Update the resolver to reflect the fact that the file with the given
@@ -164,6 +187,31 @@
@deprecated
void dispose() {}
+ /// Looks for references to the Element at the given offset and path. All the
+ /// files currently cached by the resolver are searched, generated files are
+ /// ignored.
+ List<CiderSearchMatch> findReferences(int offset, String path,
+ {OperationPerformanceImpl? performance}) {
+ var references = <CiderSearchMatch>[];
+ var unit = resolve(path: path);
+ var node = NodeLocator(offset).searchWithin(unit.unit);
+ var element = getElementOfNode(node);
+ if (element != null) {
+ // TODO(keertip): check if element is named constructor.
+ var result = fsState!.getFilesContaining(element.displayName);
+ result.forEach((filePath) {
+ var resolved = resolve(path: filePath);
+ var collector = ReferencesCollector(element);
+ resolved.unit?.accept(collector);
+ var offsets = collector.offsets;
+ if (offsets.isNotEmpty) {
+ references.add(CiderSearchMatch(filePath, offsets));
+ }
+ });
+ }
+ return references;
+ }
+
ErrorsResult getErrors({
required String path,
OperationPerformanceImpl? performance,
@@ -555,6 +603,7 @@
featureSetProvider,
getFileDigest,
prefetchFiles,
+ isGenerated,
);
}
diff --git a/pkg/analyzer/lib/src/dart/micro/utils.dart b/pkg/analyzer/lib/src/dart/micro/utils.dart
new file mode 100644
index 0000000..9afec39b
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/micro/utils.dart
@@ -0,0 +1,176 @@
+// Copyright (c) 2021, 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/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/ast/element_locator.dart';
+
+/// Return the [Element] of the given [node], or `null` if [node] is `null` or
+/// does not have an element.
+Element? getElementOfNode(AstNode? node) {
+ if (node == null) {
+ return null;
+ }
+ if (node is SimpleIdentifier && node.parent is LibraryIdentifier) {
+ node = node.parent;
+ }
+ if (node is LibraryIdentifier) {
+ node = node.parent;
+ }
+ if (node is StringLiteral && node.parent is UriBasedDirective) {
+ return null;
+ }
+ var element = ElementLocator.locate(node);
+ if (node is SimpleIdentifier && element is PrefixElement) {
+ var parent = node.parent;
+ if (parent is ImportDirective) {
+ element = parent.element;
+ } else {
+ element = _getImportElementInfo(node);
+ }
+ }
+ return element;
+}
+
+/// Return the [ImportElement] that declared [prefix] and imports [element].
+///
+/// [libraryElement] - the [LibraryElement] where reference is.
+/// [prefix] - the import prefix, maybe `null`.
+/// [element] - the referenced element.
+/// [importElementsMap] - the cache of [Element]s imported by [ImportElement]s.
+ImportElement? _getImportElement(LibraryElement libraryElement, String prefix,
+ Element element, Map<ImportElement, Set<Element>> importElementsMap) {
+ if (element.enclosingElement is! CompilationUnitElement) {
+ return null;
+ }
+ var usedLibrary = element.library;
+ // find ImportElement that imports used library with used prefix
+ List<ImportElement>? candidates;
+ for (var importElement in libraryElement.imports) {
+ // required library
+ if (importElement.importedLibrary != usedLibrary) {
+ continue;
+ }
+ // required prefix
+ var prefixElement = importElement.prefix;
+ if (prefixElement == null) {
+ continue;
+ }
+ if (prefix != prefixElement.name) {
+ continue;
+ }
+ // no combinators => only possible candidate
+ if (importElement.combinators.isEmpty) {
+ return importElement;
+ }
+ // OK, we have candidate
+ candidates ??= [];
+ candidates.add(importElement);
+ }
+ // no candidates, probably element is defined in this library
+ if (candidates == null) {
+ return null;
+ }
+ // one candidate
+ if (candidates.length == 1) {
+ return candidates[0];
+ }
+ // ensure that each ImportElement has set of elements
+ for (var importElement in candidates) {
+ if (importElementsMap.containsKey(importElement)) {
+ continue;
+ }
+ var namespace = importElement.namespace;
+ var elements = Set<Element>.from(namespace.definedNames.values);
+ importElementsMap[importElement] = elements;
+ }
+ // use import namespace to choose correct one
+ for (var entry in importElementsMap.entries) {
+ var importElement = entry.key;
+ var elements = entry.value;
+ if (elements.contains(element)) {
+ return importElement;
+ }
+ }
+ // not found
+ return null;
+}
+
+/// Returns the [ImportElement] that is referenced by [prefixNode] with a
+/// [PrefixElement], maybe `null`.
+ImportElement? _getImportElementInfo(SimpleIdentifier prefixNode) {
+ // prepare environment
+ var parent = prefixNode.parent;
+ var unit = prefixNode.thisOrAncestorOfType<CompilationUnit>();
+ var libraryElement = unit?.declaredElement?.library;
+ if (libraryElement == null) {
+ return null;
+ }
+ // prepare used element
+ Element? usedElement;
+ if (parent is PrefixedIdentifier) {
+ var prefixed = parent;
+ if (prefixed.prefix == prefixNode) {
+ usedElement = prefixed.staticElement;
+ }
+ } else if (parent is MethodInvocation) {
+ var invocation = parent;
+ if (invocation.target == prefixNode) {
+ usedElement = invocation.methodName.staticElement;
+ }
+ }
+ // we need used Element
+ if (usedElement == null) {
+ return null;
+ }
+ // find ImportElement
+ var prefix = prefixNode.name;
+ var importElementsMap = <ImportElement, Set<Element>>{};
+ return _getImportElement(
+ libraryElement, prefix, usedElement, importElementsMap);
+}
+
+class ReferencesCollector extends GeneralizingAstVisitor<void> {
+ final Element element;
+ final List<int> offsets = [];
+
+ ReferencesCollector(this.element);
+
+ @override
+ void visitAssignmentExpression(AssignmentExpression node) {
+ if (node.writeElement != null &&
+ node.writeElement is PropertyAccessorElement) {
+ var property = node.writeElement as PropertyAccessorElement;
+ if (property.variable == element || property == element) {
+ if (node.leftHandSide is SimpleIdentifier) {
+ offsets.add(node.leftHandSide.offset);
+ } else if (node.leftHandSide is PrefixedIdentifier) {
+ var prefixIdentifier = node.leftHandSide as PrefixedIdentifier;
+ offsets.add(prefixIdentifier.identifier.offset);
+ } else if (node.leftHandSide is PropertyAccess) {
+ var accessor = node.leftHandSide as PropertyAccess;
+ offsets.add(accessor.propertyName.offset);
+ }
+ }
+ }
+ if (node.readElement != null &&
+ node.readElement is PropertyAccessorElement) {
+ var property = node.readElement as PropertyAccessorElement;
+ if (property.variable == element) {
+ offsets.add(node.rightHandSide.offset);
+ }
+ }
+ }
+
+ @override
+ void visitSimpleIdentifier(SimpleIdentifier node) {
+ var e = node.staticElement;
+ if (e == element) {
+ offsets.add(node.offset);
+ } else if (e is PropertyAccessorElement && e.variable == element) {
+ offsets.add(node.offset);
+ }
+ }
+}
diff --git a/pkg/analyzer/test/src/dart/micro/file_resolution.dart b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
index 55d7031..ca6326b 100644
--- a/pkg/analyzer/test/src/dart/micro/file_resolution.dart
+++ b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
@@ -54,6 +54,7 @@
getFileDigest: (String path) => _getDigest(path),
workspace: workspace,
prefetchFiles: null,
+ isGenerated: null,
);
fileResolver.testView = FileResolverTestView();
}
diff --git a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
index e82b7ca..a994e7d 100644
--- a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
+++ b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
@@ -4,6 +4,7 @@
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/dart/micro/cider_byte_store.dart';
+import 'package:analyzer/src/dart/micro/resolve_file.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/lint/registry.dart';
import 'package:test/test.dart';
@@ -346,6 +347,289 @@
(fileResolver.byteStore as CiderCachedByteStore).testView!.length);
}
+ test_findReferences_class() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+ newFile(aPath, content: r'''
+class A {
+ int foo;
+}
+''');
+
+ var bPath = convertPath('/workspace/dart/test/lib/b.dart');
+ newFile(bPath, content: r'''
+import 'a.dart';
+
+void func() {
+ var a = A();
+ print(a.foo);
+}
+''');
+
+ await resolveFile(bPath);
+ var result = fileResolver.findReferences(6, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [6]),
+ CiderSearchMatch(bPath, [42])
+ ];
+ expect(result, unorderedEquals(expected));
+ }
+
+ test_findReferences_field() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+ newFile(aPath, content: r'''
+class A {
+ int foo = 0;
+
+ void func(int bar) {
+ foo = bar;
+ }
+}
+''');
+
+ await resolveFile(aPath);
+ var result = fileResolver.findReferences(16, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [16, 53])
+ ];
+ expect(result, unorderedEquals(expected));
+ }
+
+ test_findReferences_function() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+ newFile(aPath, content: r'''
+main() {
+ foo('Hello');
+}
+
+foo(String str) {}
+''');
+
+ await resolveFile(aPath);
+ var result = fileResolver.findReferences(11, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [11, 28])
+ ];
+ expect(result, unorderedEquals(expected));
+ }
+
+ test_findReferences_getter() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+ newFile(aPath, content: r'''
+class A {
+ int get foo => 6;
+}
+''');
+ var bPath = convertPath('/workspace/dart/test/lib/b.dart');
+ newFile(bPath, content: r'''
+import 'a.dart';
+
+main() {
+ var a = A();
+ var bar = a.foo;
+}
+''');
+
+ await resolveFile(bPath);
+ var result = fileResolver.findReferences(20, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [20]),
+ CiderSearchMatch(bPath, [56])
+ ];
+ expect(result, unorderedEquals(expected));
+ }
+
+ test_findReferences_local_variable() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+ newFile(aPath, content: r'''
+class A {
+ void func(int n) {
+ var foo = bar+1;
+ print(foo);
+ }
+}
+''');
+ await resolveFile(aPath);
+ var result = fileResolver.findReferences(39, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [39, 62])
+ ];
+ expect(result, unorderedEquals(expected));
+ }
+
+ test_findReferences_method() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+ newFile(aPath, content: r'''
+class A {
+ void func() {
+ print('hello');
+ }
+
+ void func2() {
+ func();
+ }
+}
+''');
+
+ var bPath = convertPath('/workspace/dart/test/lib/b.dart');
+ newFile(bPath, content: r'''
+import 'a.dart';
+
+main() {
+ var a = A();
+ a.func();
+}
+''');
+
+ await resolveFile(bPath);
+ var result = fileResolver.findReferences(17, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [17, 69]),
+ CiderSearchMatch(bPath, [46])
+ ];
+ expect(result, unorderedEquals(expected));
+ }
+
+ test_findReferences_setter() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+ newFile(aPath, content: r'''
+class A {
+ void set value(int m){ };
+}
+''');
+ var bPath = convertPath('/workspace/dart/test/lib/b.dart');
+ newFile(bPath, content: r'''
+import 'a.dart';
+
+main() {
+ var a = A();
+ a.value = 6;
+}
+''');
+
+ await resolveFile(bPath);
+ var result = fileResolver.findReferences(21, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [21]),
+ CiderSearchMatch(bPath, [46])
+ ];
+ expect(result, unorderedEquals(expected));
+ }
+
+ test_findReferences_top_level_getter() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+
+ newFile(aPath, content: r'''
+int _foo;
+
+int get foo => _foo;
+''');
+
+ var bPath = convertPath('/workspace/dart/test/lib/b.dart');
+ newFile(bPath, content: r'''
+import 'a.dart';
+
+main() {
+ var bar = foo;
+}
+''');
+
+ await resolveFile(bPath);
+ var result = fileResolver.findReferences(19, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [19]),
+ CiderSearchMatch(bPath, [39])
+ ];
+ expect(result, unorderedEquals(expected));
+ }
+
+ test_findReferences_top_level_setter() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+
+ newFile(aPath, content: r'''
+int _foo;
+
+void set foo(int bar) { _foo = bar; }
+''');
+
+ var bPath = convertPath('/workspace/dart/test/lib/b.dart');
+ newFile(bPath, content: r'''
+import 'a.dart';
+
+main() {
+ foo = 6;
+}
+''');
+
+ await resolveFile(bPath);
+ var result = fileResolver.findReferences(20, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [20]),
+ CiderSearchMatch(bPath, [29])
+ ];
+ expect(result, unorderedEquals(expected));
+ }
+
+ test_findReferences_top_level_variable() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+
+ newFile(aPath, content: r'''
+const int C = 42;
+
+void func() {
+ print(C);
+}
+''');
+
+ await resolveFile(aPath);
+ var result = fileResolver.findReferences(10, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [10, 43])
+ ];
+ expect(result, unorderedEquals(expected));
+ }
+
+ test_findReferences_type_parameter() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+ newFile(aPath, content: r'''
+class Foo<T> {
+ List<T> l;
+
+ void bar(T t) {}
+}
+''');
+ await resolveFile(aPath);
+ var result = fileResolver.findReferences(10, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [10, 22, 42])
+ ];
+ expect(result.map((e) => e.path),
+ unorderedEquals(expected.map((e) => e.path)));
+ expect(result.map((e) => e.offsets),
+ unorderedEquals(expected.map((e) => e.offsets)));
+ }
+
+ test_findReferences_typedef() async {
+ var aPath = convertPath('/workspace/dart/test/lib/a.dart');
+ newFile(aPath, content: r'''
+typedef func = int Function(int);
+
+''');
+ var bPath = convertPath('/workspace/dart/test/lib/b.dart');
+ newFile(bPath, content: r'''
+import 'a.dart';
+
+void f(func o) {}
+''');
+
+ await resolveFile(bPath);
+ var result = fileResolver.findReferences(8, aPath);
+ var expected = <CiderSearchMatch>[
+ CiderSearchMatch(aPath, [8]),
+ CiderSearchMatch(bPath, [25])
+ ];
+ expect(result, unorderedEquals(expected));
+ }
+
test_getErrors() {
addTestFile(r'''
var a = b;
diff --git a/tools/VERSION b/tools/VERSION
index 4cfe1b9..48a84ad 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 65
+PRERELEASE 66
PRERELEASE_PATCH 0
\ No newline at end of file