| // Copyright (c) 2014, 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:analysis_server/src/services/search/element_visitors.dart'; | |
| import 'package:analysis_server/src/services/search/search_engine.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:analyzer/src/util/performance/operation_performance.dart'; | |
| /// Expand [formalParameters] to include all chains of super formals. | |
| Future<void> addNamedSuperFormalParameters( | |
| SearchEngine searchEngine, | |
| List<FormalParameterElement> formalParameters, | |
| ) async { | |
| // Indexed-based loop to allow modification while iterating. | |
| for (var i = 0; i < formalParameters.length; i++) { | |
| var formalParameter = formalParameters[i]; | |
| if (formalParameter.isNamed) { | |
| var references = await searchEngine.searchReferences(formalParameter); | |
| formalParameters.addAll( | |
| references | |
| .map((match) => match.element) | |
| .whereType<SuperFormalParameterElement>(), | |
| ); | |
| } | |
| } | |
| } | |
| /// Returns direct children of [parent]. | |
| List<Element> getChildren(Element parent, [String? name]) { | |
| var children = <Element>[]; | |
| visitChildren(parent, (element) { | |
| if (name == null || element.lookupName == name) { | |
| children.add(element); | |
| } | |
| return false; | |
| }); | |
| return children; | |
| } | |
| /// Returns direct non-synthetic children of the given [InterfaceElement]. | |
| /// | |
| /// Includes: fields, accessors and methods. | |
| /// Excludes: constructors. | |
| List<Element> getClassMembers(InterfaceElement clazz, [String? name]) { | |
| var members = <Element>[]; | |
| visitChildren(clazz, (Element element) { | |
| if (element is ConstructorElement) { | |
| return false; | |
| } | |
| if (name != null && element.displayName != name) { | |
| return false; | |
| } | |
| if (element is ExecutableElement) { | |
| members.add(element); | |
| } | |
| if (element is FieldElement) { | |
| members.add(element); | |
| } | |
| return false; | |
| }); | |
| return members; | |
| } | |
| /// Returns a [Set] with direct subclasses of [seed]. | |
| /// | |
| /// The given [searchEngineCache] will be used or filled out as needed | |
| /// so subsequent calls can utilize it to speed up the computation. | |
| Future<Set<InterfaceElement>> getDirectSubClasses( | |
| SearchEngine searchEngine, | |
| InterfaceElement seed, | |
| ) async { | |
| var matches = await searchEngine.searchSubtypes(seed); | |
| return matches.map((match) => match.element).cast<InterfaceElement>().toSet(); | |
| } | |
| /// Return the non-synthetic children of the given [extension]. This includes | |
| /// fields, accessors and methods, but excludes synthetic elements. | |
| List<Element> getExtensionMembers(ExtensionElement extension, [String? name]) { | |
| return [ | |
| ...extension.fields.where((e) => e.isOriginDeclaration), | |
| ...extension.getters.where((e) => e.isOriginDeclaration), | |
| ...extension.setters.where((e) => e.isOriginDeclaration), | |
| ...extension.methods, | |
| ].where((element) { | |
| return name == null || element.displayName == name; | |
| }).toList(); | |
| } | |
| /// Returns all implementations of the given [member], including in its | |
| /// superclasses and their subclasses. | |
| Future<Set<Element>> getHierarchyMembers( | |
| SearchEngine searchEngine, | |
| Element member, { | |
| OperationPerformanceImpl? performance, | |
| }) async { | |
| var (members, _) = await getHierarchyMembersAndParameters( | |
| searchEngine, | |
| member, | |
| performance: performance, | |
| ); | |
| return members; | |
| } | |
| /// Returns all implementations of the given [member2], including in its | |
| /// superclasses and their subclasses. | |
| /// | |
| /// If [includeParametersForFields] is true and [member2] is a [FieldElement], | |
| /// any [FieldFormalParameterElement]s for the member will also be provided | |
| /// (otherwise, the parameter set will be empty in the result). | |
| Future<(Set<Element>, Set<FormalParameterElement>)> | |
| getHierarchyMembersAndParameters( | |
| SearchEngine searchEngine, | |
| Element member2, { | |
| OperationPerformanceImpl? performance, | |
| bool includeParametersForFields = false, | |
| }) async { | |
| performance ??= OperationPerformanceImpl('<root>'); | |
| var members = <Element>{}; | |
| var parameters = <FormalParameterElement>{}; | |
| // extension member | |
| var enclosingElement = member2.enclosingElement; | |
| if (enclosingElement is ExtensionElement) { | |
| members.add(member2); | |
| return (members, parameters); | |
| } | |
| // static elements | |
| switch (member2) { | |
| case ConstructorElement(): | |
| case FieldElement(isStatic: true): | |
| case MethodElement(isStatic: true): | |
| members.add(member2); | |
| return (members, parameters); | |
| } | |
| // method, field, etc | |
| if (enclosingElement is InterfaceElement) { | |
| var name = member2.displayName; | |
| var superElementsToSearch = enclosingElement.allSupertypes | |
| .map((superType) => superType.element) | |
| .where((interface) { | |
| return member2.isPublic || interface.library == member2.library; | |
| }) | |
| .toList(); | |
| var searchClasses = [...superElementsToSearch, enclosingElement]; | |
| var subClasses = <InterfaceElement>{}; | |
| for (var superClass in searchClasses) { | |
| // ignore if super- class does not declare member | |
| if (getClassMembers(superClass, name).isEmpty) { | |
| continue; | |
| } | |
| // check all sub- classes | |
| await performance.runAsync( | |
| 'appendAllSubtypes', | |
| (performance) => | |
| searchEngine.appendAllSubtypes(superClass, subClasses, performance), | |
| ); | |
| subClasses.add(superClass); | |
| } | |
| if (member2.isPrivate) { | |
| subClasses.removeWhere((subClass) => subClass.library != member2.library); | |
| } | |
| for (var subClass in subClasses) { | |
| var subClassMembers = getChildren(subClass, name); | |
| for (var member in subClassMembers) { | |
| switch (member) { | |
| case FieldElement(): | |
| members.add(member); | |
| case MethodElement(): | |
| members.add(member); | |
| } | |
| } | |
| if (includeParametersForFields && member2 is FieldElement) { | |
| for (var constructor in subClass.constructors) { | |
| for (var parameter in constructor.formalParameters) { | |
| if (parameter is FieldFormalParameterElement && | |
| parameter.field == member2) { | |
| parameters.add(parameter); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return (members, parameters); | |
| } | |
| return (members, parameters); | |
| } | |
| /// If the [element] is a named parameter in a [MethodElement], return all | |
| /// corresponding named parameters in the method hierarchy. | |
| Future<List<FormalParameterElement>> getHierarchyNamedParameters( | |
| SearchEngine searchEngine, | |
| FormalParameterElement element, | |
| ) async { | |
| if (element.isNamed) { | |
| var method = element.enclosingElement; | |
| if (method is MethodElement) { | |
| var hierarchyParameters = <FormalParameterElement>[]; | |
| var hierarchyMembers = await getHierarchyMembers(searchEngine, method); | |
| for (var hierarchyMethod in hierarchyMembers) { | |
| if (hierarchyMethod is MethodElement) { | |
| for (var hierarchyParameter in hierarchyMethod.formalParameters) { | |
| if (hierarchyParameter.isNamed && | |
| hierarchyParameter.name == element.name) { | |
| hierarchyParameters.add(hierarchyParameter); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| return hierarchyParameters; | |
| } | |
| } | |
| return [element]; | |
| } | |
| Future<List<FormalParameterElement>> getHierarchyPositionalParameters( | |
| SearchEngine searchEngine, | |
| FormalParameterElement element, | |
| ) async { | |
| if (element.isPositional) { | |
| var method = element.enclosingElement; | |
| if (method is MethodElement) { | |
| var index = method.parameterIndex(element); | |
| // Should not ever happen but this means we can't find the index. | |
| if (index == null) { | |
| return [element]; | |
| } | |
| var hierarchyParameters = <FormalParameterElement>[]; | |
| var hierarchyMembers = await getHierarchyMembers(searchEngine, method); | |
| for (var hierarchyMethod in hierarchyMembers) { | |
| if (hierarchyMethod is MethodElement) { | |
| for (var hierarchyParameter in hierarchyMethod.formalParameters) { | |
| if (hierarchyParameter.isPositional && | |
| hierarchyMethod.parameterIndex(hierarchyParameter) == index) { | |
| hierarchyParameters.add(hierarchyParameter); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| return hierarchyParameters; | |
| } | |
| } | |
| return [element]; | |
| } | |
| /// Returns non-synthetic members of the given [InterfaceElement] and its super | |
| /// classes. | |
| /// | |
| /// Includes: fields, accessors and methods. | |
| /// | |
| /// Excludes: constructors. | |
| List<Element> getMembers(InterfaceElement clazz) { | |
| var classElements = [...clazz.allSupertypes.map((e) => e.element), clazz]; | |
| var members = <Element>[]; | |
| for (var superClass in classElements) { | |
| members.addAll(getClassMembers(superClass)); | |
| } | |
| return members; | |
| } | |
| /// If the given [element] is a synthetic [PropertyAccessorElement] returns | |
| /// its variable, otherwise returns [element]. | |
| Element getSyntheticAccessorVariable(Element element) { | |
| if (element is PropertyAccessorElement) { | |
| if (element.isOriginVariable) { | |
| return element.variable; | |
| } | |
| } | |
| return element; | |
| } | |
| extension on MethodElement { | |
| int? parameterIndex(FormalParameterElement parameter) { | |
| var index = 0; | |
| for (var positionalParameter in formalParameters.where( | |
| (p) => p.isPositional, | |
| )) { | |
| if (positionalParameter == parameter) { | |
| return index; | |
| } | |
| index++; | |
| } | |
| return null; | |
| } | |
| } |