mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +00:00
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:
parent
f4baeb20d7
commit
27a3275495
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue