Chromium Code Reviews| Index: pkg/analyzer/lib/src/dart/analysis/search.dart |
| diff --git a/pkg/analyzer/lib/src/dart/analysis/search.dart b/pkg/analyzer/lib/src/dart/analysis/search.dart |
| index 719f93db81925a2994ff30d68aa497367db72103..98087e0a854fec548608e20aef4c37c49a3f3d05 100644 |
| --- a/pkg/analyzer/lib/src/dart/analysis/search.dart |
| +++ b/pkg/analyzer/lib/src/dart/analysis/search.dart |
| @@ -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) { |
|
Brian Wilkerson
2016/11/28 18:52:08
Not that it matters much because they are both pri
|
| + 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)); |
| } |
| } |