Search for top-level elements and class members.

No search for imports, import prefixes, libraries and units yet.

R=brianwilkerson@google.com, paulberry@google.com
BUG=

Review URL: https://codereview.chromium.org/2532203002 .
This commit is contained in:
Konstantin Shcheglov 2016-11-28 11:08:57 -08:00
parent f4baeb20d7
commit 27a3275495
3 changed files with 814 additions and 69 deletions

View file

@ -9,8 +9,18 @@ import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/visitor.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/dart/analysis/index.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:collection/collection.dart';
Element _getEnclosingElement(CompilationUnitElement unitElement, int offset) {
var finder = new _ContainingElementFinder(offset);
unitElement.accept(finder);
return finder.containingElement;
}
/**
* Search support for an [AnalysisDriver].
@ -29,14 +39,27 @@ class Search {
}
ElementKind kind = element.kind;
if (kind == ElementKind.FUNCTION || kind == ElementKind.METHOD) {
if (kind == ElementKind.CLASS ||
kind == ElementKind.COMPILATION_UNIT ||
kind == ElementKind.CONSTRUCTOR ||
kind == ElementKind.FUNCTION_TYPE_ALIAS ||
kind == ElementKind.SETTER) {
return _searchReferences(element);
} else if (kind == ElementKind.GETTER) {
return _searchReferences_Getter(element);
} else if (kind == ElementKind.FIELD ||
kind == ElementKind.TOP_LEVEL_VARIABLE) {
return _searchReferences_Field(element);
} else if (kind == ElementKind.FUNCTION || kind == ElementKind.METHOD) {
if (element.enclosingElement is ExecutableElement) {
return _searchReferences_Local(element, (n) => n is Block);
}
// return _searchReferences_Function(element);
return _searchReferences_Function(element);
} else if (kind == ElementKind.LABEL ||
kind == ElementKind.LOCAL_VARIABLE) {
return _searchReferences_Local(element, (n) => n is Block);
} else if (kind == ElementKind.PARAMETER) {
return _searchReferences_Parameter(element);
} else if (kind == ElementKind.TYPE_PARAMETER) {
return _searchReferences_Local(
element, (n) => n.parent is CompilationUnit);
@ -45,6 +68,85 @@ class Search {
return const <SearchResult>[];
}
Future<Null> _addResults(List<SearchResult> results, Element element,
IndexRelationKind relationKind, SearchResultKind resultKind) async {
// TODO(scheglov) optimize for private elements
String name = element.displayName;
// Prepare the list of files that reference the element name.
List<String> files = await _driver.getFilesReferencingName(name);
String path = element.source.fullName;
if (!files.contains(path)) {
files.add(path);
}
// Check the index of every file that references the element name.
for (String file in files) {
IndexResult result = await _driver.getIndex(file);
_IndexRequest request = new _IndexRequest(result.index);
int elementId = request.findElementId(element);
if (elementId != -1) {
CompilationUnitElement unitElement = result.unitElement;
List<SearchResult> fileResults = request.getRelations(
elementId, relationKind, resultKind, unitElement);
results.addAll(fileResults);
}
}
}
Future<List<SearchResult>> _searchReferences(Element element) async {
List<SearchResult> results = <SearchResult>[];
await _addResults(results, element, IndexRelationKind.IS_REFERENCED_BY,
SearchResultKind.REFERENCE);
return results;
}
Future<List<SearchResult>> _searchReferences_Field(
PropertyInducingElement field) async {
List<SearchResult> results = <SearchResult>[];
PropertyAccessorElement getter = field.getter;
PropertyAccessorElement setter = field.setter;
if (!field.isSynthetic) {
await _addResults(results, field, IndexRelationKind.IS_WRITTEN_BY,
SearchResultKind.WRITE);
await _addResults(results, field, IndexRelationKind.IS_REFERENCED_BY,
SearchResultKind.REFERENCE);
}
if (getter != null) {
await _addResults(results, getter, IndexRelationKind.IS_REFERENCED_BY,
SearchResultKind.READ);
await _addResults(results, getter, IndexRelationKind.IS_INVOKED_BY,
SearchResultKind.INVOCATION);
}
if (setter != null) {
await _addResults(results, setter, IndexRelationKind.IS_REFERENCED_BY,
SearchResultKind.WRITE);
}
return results;
}
Future<List<SearchResult>> _searchReferences_Function(Element element) async {
if (element is Member) {
element = (element as Member).baseElement;
}
List<SearchResult> results = <SearchResult>[];
await _addResults(results, element, IndexRelationKind.IS_REFERENCED_BY,
SearchResultKind.REFERENCE);
await _addResults(results, element, IndexRelationKind.IS_INVOKED_BY,
SearchResultKind.INVOCATION);
return results;
}
Future<List<SearchResult>> _searchReferences_Getter(
PropertyAccessorElement getter) async {
List<SearchResult> results = <SearchResult>[];
await _addResults(results, getter, IndexRelationKind.IS_REFERENCED_BY,
SearchResultKind.REFERENCE);
await _addResults(results, getter, IndexRelationKind.IS_INVOKED_BY,
SearchResultKind.INVOCATION);
return results;
}
Future<List<SearchResult>> _searchReferences_Local(
Element element, bool isRootNode(AstNode n)) async {
String path = element.source.fullName;
@ -77,6 +179,17 @@ class Search {
enclosingNode.accept(visitor);
return visitor.results;
}
Future<List<SearchResult>> _searchReferences_Parameter(
ParameterElement parameter) async {
List<SearchResult> results = <SearchResult>[];
results.addAll(await _searchReferences(parameter));
results.addAll(await _searchReferences_Local(parameter, (AstNode node) {
AstNode parent = node.parent;
return parent is ClassDeclaration || parent is CompilationUnit;
}));
return results;
}
}
/**
@ -126,6 +239,8 @@ class SearchResult {
StringBuffer buffer = new StringBuffer();
buffer.write("SearchResult(kind=");
buffer.write(kind);
buffer.write(", enclosingElement=");
buffer.write(enclosingElement);
buffer.write(", offset=");
buffer.write(offset);
buffer.write(", length=");
@ -134,8 +249,6 @@ class SearchResult {
buffer.write(isResolved);
buffer.write(", isQualified=");
buffer.write(isQualified);
buffer.write(", enclosingElement=");
buffer.write(enclosingElement);
buffer.write(")");
return buffer.toString();
}
@ -167,6 +280,189 @@ class _ContainingElementFinder extends GeneralizingElementVisitor {
}
}
class _IndexRequest {
final AnalysisDriverUnitIndex index;
_IndexRequest(this.index);
/**
* Return the [element]'s identifier in the [index] or `-1` if the
* [element] is not referenced in the [index].
*/
int findElementId(Element element) {
IndexElementInfo info = new IndexElementInfo(element);
element = info.element;
// Find the id of the element's unit.
int unitId = getUnitId(element);
if (unitId == -1) {
return -1;
}
// Prepare information about the element.
int unitMemberId = getElementUnitMemberId(element);
if (unitMemberId == -1) {
return -1;
}
int classMemberId = getElementClassMemberId(element);
if (classMemberId == -1) {
return -1;
}
int parameterId = getElementParameterId(element);
if (parameterId == -1) {
return -1;
}
// Try to find the element id using classMemberId, parameterId, and kind.
int elementId =
_findFirstOccurrence(index.elementNameUnitMemberIds, unitMemberId);
if (elementId == -1) {
return -1;
}
for (;
elementId < index.elementNameUnitMemberIds.length &&
index.elementNameUnitMemberIds[elementId] == unitMemberId;
elementId++) {
if (index.elementUnits[elementId] == unitId &&
index.elementNameClassMemberIds[elementId] == classMemberId &&
index.elementNameParameterIds[elementId] == parameterId &&
index.elementKinds[elementId] == info.kind) {
return elementId;
}
}
return -1;
}
/**
* Return the [element]'s class member name identifier, `null` is not a class
* member, or `-1` if the [element] is not referenced in the [index].
*/
int getElementClassMemberId(Element element) {
for (; element != null; element = element.enclosingElement) {
if (element.enclosingElement is ClassElement) {
return getStringId(element.name);
}
}
return index.nullStringId;
}
/**
* Return the [element]'s class member name identifier, `null` is not a class
* member, or `-1` if the [element] is not referenced in the [index].
*/
int getElementParameterId(Element element) {
for (; element != null; element = element.enclosingElement) {
if (element is ParameterElement) {
return getStringId(element.name);
}
}
return index.nullStringId;
}
/**
* Return the [element]'s top-level name identifier, `0` is the unit, or
* `-1` if the [element] is not referenced in the [index].
*/
int getElementUnitMemberId(Element element) {
for (; element != null; element = element.enclosingElement) {
if (element.enclosingElement is CompilationUnitElement) {
return getStringId(element.name);
}
}
return index.nullStringId;
}
/**
* Return a list of results where an element with the given [elementId] has
* relation of the given [indexKind].
*/
List<SearchResult> getRelations(
int elementId,
IndexRelationKind indexKind,
SearchResultKind searchKind,
CompilationUnitElement enclosingUnitElement) {
// Find the first usage of the element.
int i = _findFirstOccurrence(index.usedElements, elementId);
if (i == -1) {
return const <SearchResult>[];
}
// Create locations for every usage of the element.
List<SearchResult> results = <SearchResult>[];
for (;
i < index.usedElements.length && index.usedElements[i] == elementId;
i++) {
if (index.usedElementKinds[i] == indexKind) {
int offset = index.usedElementOffsets[i];
Element enclosingElement =
_getEnclosingElement(enclosingUnitElement, offset);
results.add(new SearchResult._(
null,
enclosingElement,
searchKind,
offset,
index.usedElementLengths[i],
true,
index.usedElementIsQualifiedFlags[i]));
}
}
return results;
}
/**
* Return the identifier of [str] in the [index] or `-1` if [str] is not
* used in the [index].
*/
int getStringId(String str) {
return binarySearch(index.strings, str);
}
/**
* Return the identifier of the [CompilationUnitElement] containing the
* [element] in the [index] or `-1` if not found.
*/
int getUnitId(Element element) {
CompilationUnitElement unitElement = getUnitElement(element);
int libraryUriId = getUriId(unitElement.library.source.uri);
if (libraryUriId == -1) {
return -1;
}
int unitUriId = getUriId(unitElement.source.uri);
if (unitUriId == -1) {
return -1;
}
for (int i = 0; i < index.unitLibraryUris.length; i++) {
if (index.unitLibraryUris[i] == libraryUriId &&
index.unitUnitUris[i] == unitUriId) {
return i;
}
}
return -1;
}
/**
* Return the identifier of the [uri] in the [index] or `-1` if the [uri] is
* not used in the [index].
*/
int getUriId(Uri uri) {
String str = uri.toString();
return getStringId(str);
}
/**
* Return the index of the first occurrence of the [value] in the [sortedList],
* or `-1` if the [value] is not in the list.
*/
int _findFirstOccurrence(List<int> sortedList, int value) {
// Find an occurrence.
int i = binarySearch(sortedList, value);
if (i == -1) {
return -1;
}
// Find the first occurrence.
while (i > 0 && sortedList[i - 1] == value) {
i--;
}
return i;
}
}
/**
* Visitor that adds [SearchResult]s for local elements of a block, method,
* class or a library - labels, local functions, local variables and parameters,
@ -213,9 +509,9 @@ class _LocalReferencesVisitor extends RecursiveAstVisitor {
void _addResult(AstNode node, SearchResultKind kind) {
bool isQualified = node.parent is Label;
var finder = new _ContainingElementFinder(node.offset);
enclosingUnitElement.accept(finder);
results.add(new SearchResult._(element, finder.containingElement, kind,
node.offset, node.length, true, isQualified));
Element enclosingElement =
_getEnclosingElement(enclosingUnitElement, node.offset);
results.add(new SearchResult._(element, enclosingElement, kind, node.offset,
node.length, true, isQualified));
}
}

View file

@ -1049,7 +1049,7 @@ class ClassElementImpl extends AbstractClassElementImpl
@override
void visitChildren(ElementVisitor visitor) {
super.visitChildren(visitor);
safelyVisitChildren(_constructors, visitor);
safelyVisitChildren(constructors, visitor);
safelyVisitChildren(methods, visitor);
safelyVisitChildren(typeParameters, visitor);
}

View file

@ -9,6 +9,7 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/dart/analysis/search.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@ -21,14 +22,14 @@ main() {
}
class ExpectedResult {
final List<String> enclosingComponents;
final Element enclosingElement;
final SearchResultKind kind;
final int offset;
final int length;
final bool isResolved;
final bool isQualified;
ExpectedResult(this.enclosingComponents, this.kind, this.offset, this.length,
ExpectedResult(this.enclosingElement, this.kind, this.offset, this.length,
{this.isResolved: true, this.isQualified: false});
bool operator ==(Object result) {
@ -37,24 +38,7 @@ class ExpectedResult {
result.isResolved == this.isResolved &&
result.isQualified == this.isQualified &&
result.offset == this.offset &&
hasExpectedComponents(result.enclosingElement);
}
bool hasExpectedComponents(Element element) {
for (int i = enclosingComponents.length - 1; i >= 0; i--) {
if (element == null) {
return false;
}
if (element is CompilationUnitElement) {
if (element.source.uri.toString() != enclosingComponents[0]) {
return false;
}
} else if (element.name != enclosingComponents[i]) {
return false;
}
element = element.enclosingElement;
}
return true;
result.enclosingElement == this.enclosingElement;
}
@override
@ -62,8 +46,8 @@ class ExpectedResult {
StringBuffer buffer = new StringBuffer();
buffer.write("ExpectedResult(kind=");
buffer.write(kind);
buffer.write(", enclosingComponents=");
buffer.write(enclosingComponents);
buffer.write(", enclosingElement=");
buffer.write(enclosingElement);
buffer.write(", offset=");
buffer.write(offset);
buffer.write(", length=");
@ -81,16 +65,233 @@ class ExpectedResult {
class SearchTest extends BaseAnalysisDriverTest {
static const testUri = 'package:test/test.dart';
CompilationUnit testUnit;
CompilationUnitElement testUnitElement;
LibraryElement testLibraryElement;
test_searchReferences_ClassElement_definedInside() async {
await _resolveTestUnit('''
class A {};
main(A p) {
A v;
}
class B1 extends A {} // extends
class B2 implements A {} // implements
class B3 extends Object with A {} // with
List<A> v2 = null;
''');
ClassElement element = _findElementAtString('A {}');
Element p = _findElement('p');
Element main = _findElement('main');
Element b1 = _findElement('B1');
Element b2 = _findElement('B2');
Element b3 = _findElement('B3');
Element v2 = _findElement('v2');
var expected = [
_expectId(p, SearchResultKind.REFERENCE, 'A p'),
_expectId(main, SearchResultKind.REFERENCE, 'A v'),
_expectId(b1, SearchResultKind.REFERENCE, 'A {} // extends'),
_expectId(b2, SearchResultKind.REFERENCE, 'A {} // implements'),
_expectId(b3, SearchResultKind.REFERENCE, 'A {} // with'),
_expectId(v2, SearchResultKind.REFERENCE, 'A> v2'),
];
await _verifyReferences(element, expected);
}
test_searchReferences_ClassElement_definedOutside() async {
provider.newFile(
_p('$testProject/lib.dart'),
r'''
class A {};
''');
await _resolveTestUnit('''
import 'lib.dart';
main(A p) {
A v;
}
''');
ClassElement element = _findElementAtString('A p');
Element p = _findElement('p');
Element main = _findElement('main');
var expected = [
_expectId(p, SearchResultKind.REFERENCE, 'A p'),
_expectId(main, SearchResultKind.REFERENCE, 'A v')
];
await _verifyReferences(element, expected);
}
@failingTest
test_searchReferences_CompilationUnitElement() async {
provider.newFile(
_p('$testProject/my_part.dart'),
'''
part of lib;
''');
await _resolveTestUnit('''
library lib;
part 'my_part.dart';
''');
CompilationUnitElement element = _findElementAtString('my_part');
var expected = [
_expectIdQ(element.library.definingCompilationUnit,
SearchResultKind.REFERENCE, "'my_part.dart'",
length: "'my_part.dart'".length)
];
await _verifyReferences(element, expected);
}
test_searchReferences_ConstructorElement() async {
await _resolveTestUnit('''
class A {
A.named() {}
}
main() {
new A.named();
}
''');
ConstructorElement element = _findElement('named');
Element mainElement = _findElement('main');
var expected = [
_expectIdQ(mainElement, SearchResultKind.REFERENCE, '.named();',
length: 6)
];
await _verifyReferences(element, expected);
}
test_searchReferences_ConstructorElement_synthetic() async {
await _resolveTestUnit('''
class A {
}
main() {
new A();
}
''');
ClassElement classElement = _findElement('A');
ConstructorElement element = classElement.unnamedConstructor;
Element mainElement = _findElement('main');
var expected = [
_expectIdQ(mainElement, SearchResultKind.REFERENCE, '();', length: 0)
];
await _verifyReferences(element, expected);
}
test_searchReferences_FieldElement() async {
await _resolveTestUnit('''
class A {
var field;
A({this.field});
main() {
new A(field: 1);
// getter
print(field); // ref-nq
print(this.field); // ref-q
field(); // inv-nq
this.field(); // inv-q
// setter
field = 2; // ref-nq;
this.field = 3; // ref-q;
}
}
''');
FieldElement element = _findElement('field', ElementKind.FIELD);
Element main = _findElement('main');
Element fieldParameter = _findElement('field', ElementKind.PARAMETER);
var expected = [
_expectIdQ(fieldParameter, SearchResultKind.WRITE, 'field}'),
_expectIdQ(main, SearchResultKind.REFERENCE, 'field: 1'),
_expectId(main, SearchResultKind.READ, 'field); // ref-nq'),
_expectIdQ(main, SearchResultKind.READ, 'field); // ref-q'),
_expectId(main, SearchResultKind.INVOCATION, 'field(); // inv-nq'),
_expectIdQ(main, SearchResultKind.INVOCATION, 'field(); // inv-q'),
_expectId(main, SearchResultKind.WRITE, 'field = 2; // ref-nq'),
_expectIdQ(main, SearchResultKind.WRITE, 'field = 3; // ref-q'),
];
await _verifyReferences(element, expected);
}
test_searchReferences_FieldElement_ofEnum() async {
await _resolveTestUnit('''
enum MyEnum {
A, B, C
}
main() {
print(MyEnum.A.index);
print(MyEnum.values);
print(MyEnum.A);
print(MyEnum.B);
}
''');
ClassElement enumElement = _findElement('MyEnum');
Element mainElement = _findElement('main');
await _verifyReferences(enumElement.getField('index'),
[_expectIdQ(mainElement, SearchResultKind.READ, 'index);')]);
await _verifyReferences(enumElement.getField('values'),
[_expectIdQ(mainElement, SearchResultKind.READ, 'values);')]);
await _verifyReferences(enumElement.getField('A'), [
_expectIdQ(mainElement, SearchResultKind.READ, 'A.index);'),
_expectIdQ(mainElement, SearchResultKind.READ, 'A);')
]);
await _verifyReferences(enumElement.getField('B'),
[_expectIdQ(mainElement, SearchResultKind.READ, 'B);')]);
}
test_searchReferences_FieldElement_synthetic() async {
await _resolveTestUnit('''
class A {
get field => null;
set field(x) {}
main() {
// getter
print(field); // ref-nq
print(this.field); // ref-q
field(); // inv-nq
this.field(); // inv-q
// setter
field = 2; // ref-nq;
this.field = 3; // ref-q;
}
}
''');
FieldElement element = _findElement('field', ElementKind.FIELD);
Element main = _findElement('main');
var expected = [
_expectId(main, SearchResultKind.READ, 'field); // ref-nq'),
_expectIdQ(main, SearchResultKind.READ, 'field); // ref-q'),
_expectId(main, SearchResultKind.INVOCATION, 'field(); // inv-nq'),
_expectIdQ(main, SearchResultKind.INVOCATION, 'field(); // inv-q'),
_expectId(main, SearchResultKind.WRITE, 'field = 2; // ref-nq'),
_expectIdQ(main, SearchResultKind.WRITE, 'field = 3; // ref-q'),
];
await _verifyReferences(element, expected);
}
test_searchReferences_FunctionElement() async {
await _resolveTestUnit('''
test() {}
main() {
test();
print(test);
}
''');
FunctionElement element = _findElement('test');
Element mainElement = _findElement('main');
var expected = [
_expectId(mainElement, SearchResultKind.INVOCATION, 'test();'),
_expectId(mainElement, SearchResultKind.REFERENCE, 'test);')
];
await _verifyReferences(element, expected);
}
test_searchReferences_FunctionElement_local() async {
addTestFile('''
await _resolveTestUnit('''
main() {
test() {}
test();
print(test);
}
''');
FunctionElement element = await _findElement('test');
List<String> main = [testUri, 'main'];
FunctionElement element = _findElement('test');
Element main = _findElement('main');
var expected = [
_expectId(main, SearchResultKind.INVOCATION, 'test();'),
_expectId(main, SearchResultKind.REFERENCE, 'test);')
@ -99,7 +300,7 @@ main() {
}
test_searchReferences_LabelElement() async {
addTestFile('''
await _resolveTestUnit('''
main() {
label:
while (true) {
@ -110,8 +311,8 @@ label:
}
}
''');
Element element = await _findElement('label');
List<String> main = [testUri, 'main'];
Element element = _findElement('label');
Element main = _findElement('main');
var expected = [
_expectId(main, SearchResultKind.REFERENCE, 'label; // 1'),
_expectId(main, SearchResultKind.REFERENCE, 'label; // 2')
@ -120,7 +321,7 @@ label:
}
test_searchReferences_LocalVariableElement() async {
addTestFile(r'''
await _resolveTestUnit(r'''
main() {
var v;
v = 1;
@ -129,8 +330,8 @@ main() {
v();
}
''');
Element element = await _findElement('v');
List<String> main = [testUri, 'main'];
Element element = _findElement('v');
Element main = _findElement('main');
var expected = [
_expectId(main, SearchResultKind.WRITE, 'v = 1;'),
_expectId(main, SearchResultKind.READ_WRITE, 'v += 2;'),
@ -141,7 +342,7 @@ main() {
}
test_searchReferences_localVariableElement_inForEachLoop() async {
addTestFile('''
await _resolveTestUnit('''
main() {
for (var v in []) {
v = 1;
@ -151,8 +352,8 @@ main() {
}
}
''');
Element element = await _findElementAtString('v in []');
List<String> main = [testUri, 'main'];
Element element = _findElementAtString('v in []');
Element main = _findElement('main');
var expected = [
_expectId(main, SearchResultKind.WRITE, 'v = 1;'),
_expectId(main, SearchResultKind.READ_WRITE, 'v += 2;'),
@ -162,90 +363,338 @@ main() {
await _verifyReferences(element, expected);
}
test_searchReferences_MethodElement() async {
await _resolveTestUnit('''
class A {
m() {}
main() {
m(); // 1
this.m(); // 2
print(m); // 3
print(this.m); // 4
}
}
''');
MethodElement method = _findElement('m');
Element mainElement = _findElement('main');
var expected = [
_expectId(mainElement, SearchResultKind.INVOCATION, 'm(); // 1'),
_expectIdQ(mainElement, SearchResultKind.INVOCATION, 'm(); // 2'),
_expectId(mainElement, SearchResultKind.REFERENCE, 'm); // 3'),
_expectIdQ(mainElement, SearchResultKind.REFERENCE, 'm); // 4')
];
await _verifyReferences(method, expected);
}
test_searchReferences_MethodMember() async {
await _resolveTestUnit('''
class A<T> {
T m() => null;
}
main(A<int> a) {
a.m(); // ref
}
''');
MethodMember method = _findElementAtString('m(); // ref');
Element mainElement = _findElement('main');
var expected = [
_expectIdQ(mainElement, SearchResultKind.INVOCATION, 'm(); // ref')
];
await _verifyReferences(method, expected);
}
test_searchReferences_ParameterElement_ofConstructor() async {
await _resolveTestUnit('''
class C {
var f;
C({p}) : f = p + 1 {
p = 2;
p += 3;
print(p);
p();
}
}
main() {
new C(p: 42);
}
''');
ParameterElement element = _findElement('p');
ClassElement classC = _findElement('C');
ConstructorElement constructorA = classC.unnamedConstructor;
Element mainElement = _findElement('main');
var expected = [
_expectId(constructorA, SearchResultKind.READ, 'p + 1 {'),
_expectId(constructorA, SearchResultKind.WRITE, 'p = 2;'),
_expectId(constructorA, SearchResultKind.READ_WRITE, 'p += 3;'),
_expectId(constructorA, SearchResultKind.READ, 'p);'),
_expectId(constructorA, SearchResultKind.INVOCATION, 'p();'),
_expectIdQ(mainElement, SearchResultKind.REFERENCE, 'p: 42')
];
await _verifyReferences(element, expected);
}
test_searchReferences_ParameterElement_ofLocalFunction() async {
await _resolveTestUnit('''
main() {
foo({p}) {
p = 1;
p += 2;
print(p);
p();
}
foo(p: 42);
}
''');
ParameterElement element = _findElement('p');
Element fooElement = _findElement('foo');
Element mainElement = _findElement('main');
var expected = [
_expectId(fooElement, SearchResultKind.WRITE, 'p = 1;'),
_expectId(fooElement, SearchResultKind.READ_WRITE, 'p += 2;'),
_expectId(fooElement, SearchResultKind.READ, 'p);'),
_expectId(fooElement, SearchResultKind.INVOCATION, 'p();'),
_expectIdQ(mainElement, SearchResultKind.REFERENCE, 'p: 42')
];
await _verifyReferences(element, expected);
}
test_searchReferences_ParameterElement_ofMethod() async {
await _resolveTestUnit('''
class C {
foo({p}) {
p = 1;
p += 2;
print(p);
p();
}
}
main(C c) {
c.foo(p: 42);
}
''');
ParameterElement element = _findElement('p');
Element fooElement = _findElement('foo');
Element mainElement = _findElement('main');
var expected = [
_expectId(fooElement, SearchResultKind.WRITE, 'p = 1;'),
_expectId(fooElement, SearchResultKind.READ_WRITE, 'p += 2;'),
_expectId(fooElement, SearchResultKind.READ, 'p);'),
_expectId(fooElement, SearchResultKind.INVOCATION, 'p();'),
_expectIdQ(mainElement, SearchResultKind.REFERENCE, 'p: 42')
];
await _verifyReferences(element, expected);
}
test_searchReferences_ParameterElement_ofTopLevelFunction() async {
await _resolveTestUnit('''
foo({p}) {
p = 1;
p += 2;
print(p);
p();
}
main() {
foo(p: 42);
}
''');
ParameterElement element = _findElement('p');
Element fooElement = _findElement('foo');
Element mainElement = _findElement('main');
var expected = [
_expectId(fooElement, SearchResultKind.WRITE, 'p = 1;'),
_expectId(fooElement, SearchResultKind.READ_WRITE, 'p += 2;'),
_expectId(fooElement, SearchResultKind.READ, 'p);'),
_expectId(fooElement, SearchResultKind.INVOCATION, 'p();'),
_expectIdQ(mainElement, SearchResultKind.REFERENCE, 'p: 42')
];
await _verifyReferences(element, expected);
}
test_searchReferences_PropertyAccessorElement_getter() async {
await _resolveTestUnit('''
class A {
get ggg => null;
main() {
print(ggg); // ref-nq
print(this.ggg); // ref-q
ggg(); // inv-nq
this.ggg(); // inv-q
}
}
''');
PropertyAccessorElement element = _findElement('ggg', ElementKind.GETTER);
Element main = _findElement('main');
var expected = [
_expectId(main, SearchResultKind.REFERENCE, 'ggg); // ref-nq'),
_expectIdQ(main, SearchResultKind.REFERENCE, 'ggg); // ref-q'),
_expectId(main, SearchResultKind.INVOCATION, 'ggg(); // inv-nq'),
_expectIdQ(main, SearchResultKind.INVOCATION, 'ggg(); // inv-q'),
];
await _verifyReferences(element, expected);
}
test_searchReferences_PropertyAccessorElement_setter() async {
await _resolveTestUnit('''
class A {
set s(x) {}
main() {
s = 1;
this.s = 2;
}
}
''');
PropertyAccessorElement element = _findElement('s=');
Element mainElement = _findElement('main');
var expected = [
_expectId(mainElement, SearchResultKind.REFERENCE, 's = 1'),
_expectIdQ(mainElement, SearchResultKind.REFERENCE, 's = 2')
];
await _verifyReferences(element, expected);
}
test_searchReferences_TopLevelVariableElement() async {
provider.newFile(
_p('$testProject/lib.dart'),
'''
library lib;
var V;
''');
await _resolveTestUnit('''
import 'lib.dart' show V; // imp
import 'lib.dart' as pref;
main() {
pref.V = 1; // q
print(pref.V); // q
pref.V(); // q
V = 1; // nq
print(V); // nq
V(); // nq
}
''');
ImportElement importElement = testLibraryElement.imports[0];
CompilationUnitElement impUnit =
importElement.importedLibrary.definingCompilationUnit;
TopLevelVariableElement variable = impUnit.topLevelVariables[0];
Element main = _findElement('main');
var expected = [
_expectIdQ(testUnitElement, SearchResultKind.REFERENCE, 'V; // imp'),
_expectIdQ(main, SearchResultKind.WRITE, 'V = 1; // q'),
_expectIdQ(main, SearchResultKind.READ, 'V); // q'),
_expectIdQ(main, SearchResultKind.INVOCATION, 'V(); // q'),
_expectId(main, SearchResultKind.WRITE, 'V = 1; // nq'),
_expectId(main, SearchResultKind.READ, 'V); // nq'),
_expectId(main, SearchResultKind.INVOCATION, 'V(); // nq'),
];
await _verifyReferences(variable, expected);
}
test_searchReferences_TypeParameterElement_ofClass() async {
addTestFile('''
await _resolveTestUnit('''
class A<T> {
foo(T a) {}
bar(T b) {}
}
''');
TypeParameterElement element = await _findElement('T');
TypeParameterElement element = _findElement('T');
Element a = _findElement('a');
Element b = _findElement('b');
var expected = [
_expectId([testUri, 'A', 'foo', 'a'], SearchResultKind.REFERENCE, 'T a'),
_expectId([testUri, 'A', 'bar', 'b'], SearchResultKind.REFERENCE, 'T b'),
_expectId(a, SearchResultKind.REFERENCE, 'T a'),
_expectId(b, SearchResultKind.REFERENCE, 'T b'),
];
await _verifyReferences(element, expected);
}
test_searchReferences_TypeParameterElement_ofLocalFunction() async {
addTestFile('''
await _resolveTestUnit('''
main() {
void foo<T>(T a) {
void bar(T b) {}
}
}
''');
TypeParameterElement element = await _findElement('T');
TypeParameterElement element = _findElement('T');
Element a = _findElement('a');
Element b = _findElement('b');
var expected = [
_expectId(
[testUri, 'main', 'foo', 'a'], SearchResultKind.REFERENCE, 'T a'),
_expectId([testUri, 'main', 'foo', 'bar', 'b'],
SearchResultKind.REFERENCE, 'T b'),
_expectId(a, SearchResultKind.REFERENCE, 'T a'),
_expectId(b, SearchResultKind.REFERENCE, 'T b'),
];
await _verifyReferences(element, expected);
}
test_searchReferences_TypeParameterElement_ofMethod() async {
addTestFile('''
await _resolveTestUnit('''
class A {
foo<T>(T p) {}
}
''');
TypeParameterElement element = await _findElement('T');
TypeParameterElement element = _findElement('T');
Element p = _findElement('p');
var expected = [
_expectId([testUri, 'A', 'foo', 'p'], SearchResultKind.REFERENCE, 'T p'),
_expectId(p, SearchResultKind.REFERENCE, 'T p'),
];
await _verifyReferences(element, expected);
}
test_searchReferences_TypeParameterElement_ofTopLevelFunction() async {
addTestFile('''
await _resolveTestUnit('''
foo<T>(T a) {
bar(T b) {}
}
''');
TypeParameterElement element = await _findElement('T');
TypeParameterElement element = _findElement('T');
Element a = _findElement('a');
Element b = _findElement('b');
var expected = [
_expectId([testUri, 'foo', 'a'], SearchResultKind.REFERENCE, 'T a'),
_expectId(
[testUri, 'foo', 'bar', 'b'], SearchResultKind.REFERENCE, 'T b'),
_expectId(a, SearchResultKind.REFERENCE, 'T a'),
_expectId(b, SearchResultKind.REFERENCE, 'T b'),
];
await _verifyReferences(element, expected);
}
ExpectedResult _expectId(
List<String> enclosingComponents, SearchResultKind kind, String search,
Element enclosingElement, SearchResultKind kind, String search,
{int length, bool isResolved: true, bool isQualified: false}) {
int offset = findOffset(search);
if (length == null) {
length = getLeadingIdentifierLength(search);
}
return new ExpectedResult(enclosingComponents, kind, offset, length,
return new ExpectedResult(enclosingElement, kind, offset, length,
isResolved: isResolved, isQualified: isQualified);
}
Future<Element> _findElement(String name) async {
AnalysisResult result = await driver.getResult(testFile);
return findChildElement(result.unit.element, name);
/**
* Create [ExpectedResult] for a qualified and resolved match.
*/
ExpectedResult _expectIdQ(
Element element, SearchResultKind kind, String search,
{int length, bool isResolved: true}) {
return _expectId(element, kind, search, isQualified: true, length: length);
}
Future<Element> _findElementAtString(String search) async {
AnalysisResult result = await driver.getResult(testFile);
Element _findElement(String name, [ElementKind kind]) {
return findChildElement(testUnit.element, name, kind);
}
Element _findElementAtString(String search) {
int offset = findOffset(search);
AstNode node = new NodeLocator(offset).searchWithin(result.unit);
AstNode node = new NodeLocator(offset).searchWithin(testUnit);
return ElementLocator.locate(node);
}
String _p(String path) => provider.convertPath(path);
Future<Null> _resolveTestUnit(String code) async {
addTestFile(code);
if (testUnit == null) {
AnalysisResult result = await driver.getResult(testFile);
testUnit = result.unit;
testUnitElement = testUnit.element;
testLibraryElement = testUnitElement.library;
}
}
Future _verifyReferences(
Element element, List<ExpectedResult> expectedMatches) async {
List<SearchResult> results = await driver.search.references(element);