blob: cc9796f11e2d63e4cc3a139e3aef2aba4336e732 [file] [log] [blame]
// 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.name == name) {
children.add(element);
}
return false;
});
return children;
}
/// Returns direct non-synthetic children of the given [InterfaceElement].
///
/// Includes: fields, accessors and methods.
/// Excludes: constructors and synthetic elements.
List<Element> getClassMembers(InterfaceElement clazz, [String? name]) {
var members = <Element>[];
visitChildren(clazz, (Element element) {
if (element.isSynthetic) {
return false;
}
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,
SearchEngineCache searchEngineCache,
) async {
var matches = await searchEngine.searchSubtypes(seed, searchEngineCache);
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]) {
var members = <Element>[];
visitChildren(extension, (element) {
if (element.isSynthetic) {
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 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 ConstructorElement():
members.add(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 and synthetic elements.
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.isSynthetic) {
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;
}
}