blob: d104ed3fcbdefdb70c038a98afde1e74bca96afb [file] [log] [blame]
// Copyright (c) 2018, 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:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/test_utilities/function_ast_visitor.dart';
import 'package:collection/src/iterable_extensions.dart';
/// Helper for finding elements declared in the resolved [unit].
class FindElement extends _FindElementBase {
final CompilationUnit unit;
FindElement(this.unit);
LibraryElement get libraryElement => unitElement.library;
@override
CompilationUnitElement get unitElement => unit.declaredElement!;
ExportElement2 export(String targetUri) {
ExportElement2? result;
for (var export in libraryElement.exports2) {
var exportedUri = export.exportedLibrary?.source.uri.toString();
if (exportedUri == targetUri) {
if (result != null) {
throw StateError('Not unique: $targetUri');
}
result = export;
}
}
if (result != null) {
return result;
}
throw StateError('Not found: $targetUri');
}
FieldFormalParameterElement fieldFormalParameter(String name) {
return parameter(name) as FieldFormalParameterElement;
}
FunctionElement function(String name) {
for (var function in unitElement.functions) {
if (function.name == name) {
return function;
}
}
throw StateError('Not found: $name');
}
ImportElement2 import(String targetUri, {bool mustBeUnique = true}) {
ImportElement2? importElement;
for (var import in libraryElement.imports2) {
var importedUri = import.importedLibrary?.source.uri.toString();
if (importedUri == targetUri) {
if (importElement == null) {
importElement = import;
} else if (mustBeUnique) {
throw StateError('Not unique: $targetUri');
}
}
}
if (importElement != null) {
return importElement;
}
throw StateError('Not found: $targetUri');
}
ImportFindElement importFind(String targetUri, {bool mustBeUnique = true}) {
var import = this.import(targetUri, mustBeUnique: mustBeUnique);
return ImportFindElement(import);
}
LabelElement label(String name) {
LabelElement? result;
void updateResult(Element element) {
if (element is LabelElement && element.name == name) {
if (result != null) {
throw StateError('Not unique: $name');
}
result = element;
}
}
unit.accept(FunctionAstVisitor(
label: (node) {
updateResult(node.label.staticElement!);
},
));
if (result == null) {
throw StateError('Not found: $name');
}
return result!;
}
FunctionElement localFunction(String name) {
FunctionElement? result;
unit.accept(FunctionAstVisitor(
functionDeclarationStatement: (node) {
var element = node.functionDeclaration.declaredElement;
if (element is FunctionElement && element.name == name) {
if (result != null) {
throw StateError('Not unique: $name');
}
result = element;
}
},
));
if (result == null) {
throw StateError('Not found: $name');
}
return result!;
}
LocalVariableElement localVar(String name) {
LocalVariableElement? result;
void updateResult(Element element) {
if (element is LocalVariableElement && element.name == name) {
if (result != null) {
throw StateError('Not unique: $name');
}
result = element;
}
}
unit.accept(FunctionAstVisitor(
declaredIdentifier: (node) {
updateResult(node.declaredElement!);
},
simpleIdentifier: (node) {
if (node.parent is CatchClause) {
updateResult(node.staticElement!);
}
},
variableDeclaration: (node) {
updateResult(node.declaredElement!);
},
));
if (result == null) {
throw StateError('Not found: $name');
}
return result!;
}
@override
ParameterElement parameter(String name) {
ParameterElement? result;
void findIn(List<ParameterElement> parameters) {
for (var parameter in parameters) {
if (parameter.name == name) {
if (result != null) {
throw StateError('Not unique: $name');
}
result = parameter;
}
}
}
void findInExecutables(List<ExecutableElement> executables) {
for (var executable in executables) {
findIn(executable.parameters);
}
}
void findInClasses(List<ClassElement> classes) {
for (var class_ in classes) {
findInExecutables(class_.accessors);
findInExecutables(class_.constructors);
findInExecutables(class_.methods);
}
}
findInExecutables(unitElement.accessors);
findInExecutables(unitElement.functions);
findInClasses(unitElement.classes);
findInClasses(unitElement.enums);
findInClasses(unitElement.mixins);
for (var extension_ in unitElement.extensions) {
findInExecutables(extension_.accessors);
findInExecutables(extension_.methods);
}
for (var alias in unitElement.typeAliases) {
var aliasedElement = alias.aliasedElement;
if (aliasedElement is GenericFunctionTypeElement) {
findIn(aliasedElement.parameters);
}
}
unit.accept(
FunctionAstVisitor(functionExpression: (node, local) {
if (local) {
var functionElement = node.declaredElement!;
findIn(functionElement.parameters);
}
}),
);
if (result != null) {
return result!;
}
throw StateError('Not found: $name');
}
CompilationUnitElement part(String targetUri) {
CompilationUnitElement? result;
for (final partElement in libraryElement.parts2) {
final uri = partElement.uri;
if (uri is DirectiveUriWithUnit) {
final unitElement = uri.unit;
if ('${unitElement.source.uri}' == targetUri) {
if (result != null) {
throw StateError('Not unique: $targetUri');
}
result = unitElement;
}
}
}
if (result != null) {
return result;
}
throw StateError('Not found: $targetUri');
}
PartFindElement partFind(String targetUri) {
var part = this.part(targetUri);
return PartFindElement(part);
}
PrefixElement prefix(String name) {
for (var import_ in libraryElement.imports2) {
var prefix = import_.prefix?.element;
if (prefix != null && prefix.name == name) {
return prefix;
}
}
throw StateError('Not found: $name');
}
TypeParameterElement typeParameter(String name) {
TypeParameterElement? result;
void findIn(List<TypeParameterElement> typeParameters) {
for (var typeParameter in typeParameters) {
if (typeParameter.name == name) {
if (result != null) {
throw StateError('Not unique: $name');
}
result = typeParameter;
}
}
}
void findInClass(ClassElement class_) {
findIn(class_.typeParameters);
for (var method in class_.methods) {
findIn(method.typeParameters);
}
}
for (var type in unitElement.functions) {
findIn(type.typeParameters);
}
for (var alias in unitElement.typeAliases) {
findIn(alias.typeParameters);
var aliasedElement = alias.aliasedElement;
if (aliasedElement is GenericFunctionTypeElement) {
findIn(aliasedElement.typeParameters);
}
}
for (var class_ in unitElement.classes) {
findInClass(class_);
}
for (var enum_ in unitElement.enums) {
findInClass(enum_);
}
for (var extension_ in unitElement.extensions) {
findIn(extension_.typeParameters);
}
for (var mixin in unitElement.mixins) {
findInClass(mixin);
}
if (result != null) {
return result!;
}
throw StateError('Not found: $name');
}
}
/// Helper for searching imported elements.
class ImportFindElement extends _FindElementBase {
final ImportElement2 import;
ImportFindElement(this.import);
LibraryElement get importedLibrary => import.importedLibrary!;
PrefixElement? get prefix => import.prefix?.element;
@override
CompilationUnitElement get unitElement {
return importedLibrary.definingCompilationUnit;
}
}
class PartFindElement extends _FindElementBase {
@override
final CompilationUnitElement unitElement;
PartFindElement(this.unitElement);
}
abstract class _FindElementBase {
CompilationUnitElement get unitElement;
ClassElement class_(String name) {
for (var class_ in unitElement.classes) {
if (class_.name == name) {
return class_;
}
}
throw StateError('Not found: $name');
}
ClassElement classOrMixin(String name) {
for (var class_ in unitElement.classes) {
if (class_.name == name) {
return class_;
}
}
for (var mixin in unitElement.mixins) {
if (mixin.name == name) {
return mixin;
}
}
throw StateError('Not found: $name');
}
ConstructorElement constructor(String name, {String? of}) {
assert(name != '');
ConstructorElement? result;
void findIn(List<ConstructorElement> constructors) {
for (var constructor in constructors) {
if (constructor.name == name) {
if (result != null) {
throw StateError('Not unique: $name');
}
result = constructor;
}
}
}
for (var class_ in unitElement.classes) {
if (of == null || class_.name == of) {
findIn(class_.constructors);
}
}
for (var enum_ in unitElement.enums) {
if (of == null || enum_.name == of) {
findIn(enum_.constructors);
}
}
if (result != null) {
return result!;
}
throw StateError('Not found: $name');
}
ClassElement enum_(String name) {
for (var enum_ in unitElement.enums) {
if (enum_.name == name) {
return enum_;
}
}
throw StateError('Not found: $name');
}
ExtensionElement extension_(String name) {
for (var extension_ in unitElement.extensions) {
if (extension_.name == name) {
return extension_;
}
}
throw StateError('Not found: $name');
}
FieldElement field(String name, {String? of}) {
return _findInClassesLike(
className: of,
fromClass: (element) => element.getField(name),
fromExtension: (element) => element.getField(name),
);
}
PropertyAccessorElement getter(String name, {String? of}) {
return _findInClassesLike(
className: of,
fromClass: (element) => element.getGetter(name),
fromExtension: (element) => element.getGetter(name),
);
}
MethodElement method(String name, {String? of}) {
return _findInClassesLike(
className: of,
fromClass: (element) => element.getMethod(name),
fromExtension: (element) => element.getMethod(name),
);
}
ClassElement mixin(String name) {
for (var mixin in unitElement.mixins) {
if (mixin.name == name) {
return mixin;
}
}
throw StateError('Not found: $name');
}
ParameterElement parameter(String name) {
ParameterElement? result;
for (var class_ in unitElement.classes) {
for (var constructor in class_.constructors) {
for (var parameter in constructor.parameters) {
if (parameter.name == name) {
if (result != null) {
throw StateError('Not unique: $name');
}
result = parameter;
}
}
}
}
if (result != null) {
return result;
}
throw StateError('Not found: $name');
}
PropertyAccessorElement setter(String name, {String? of}) {
return _findInClassesLike(
className: of,
fromClass: (element) => element.getSetter(name),
fromExtension: (element) => element.getSetter(name),
);
}
FunctionElement topFunction(String name) {
for (var function in unitElement.functions) {
if (function.name == name) {
return function;
}
}
throw StateError('Not found: $name');
}
PropertyAccessorElement topGet(String name) {
return topVar(name).getter!;
}
PropertyAccessorElement topSet(String name) {
return topVar(name).setter!;
}
TopLevelVariableElement topVar(String name) {
for (var variable in unitElement.topLevelVariables) {
if (variable.name == name) {
return variable;
}
}
throw StateError('Not found: $name');
}
TypeAliasElement typeAlias(String name) {
for (var element in unitElement.typeAliases) {
if (element.name == name) {
return element;
}
}
throw StateError('Not found: $name');
}
ConstructorElement unnamedConstructor(String name) {
return _findInClassesLike(
className: name,
fromClass: (e) => e.unnamedConstructor,
fromExtension: (_) => null,
);
}
T _findInClassesLike<T extends Element>({
required String? className,
required T? Function(ClassElement element) fromClass,
required T? Function(ExtensionElement element) fromExtension,
}) {
bool filter(Element element) {
return className == null || element.name == className;
}
var classes = [
...unitElement.classes,
...unitElement.enums,
...unitElement.mixins,
];
var results = [
...classes.where(filter).map(fromClass),
...unitElement.extensions.where(filter).map(fromExtension),
].whereNotNull().toList();
var result = results.singleOrNull;
if (result != null) {
return result;
}
if (results.isEmpty) {
throw StateError('Not found');
} else {
throw StateError('Not unique');
}
}
}
extension ExecutableElementExtensions on ExecutableElement {
ParameterElement parameter(String name) {
for (var parameter in parameters) {
if (parameter.name == name) {
return parameter;
}
}
throw StateError('Not found: $name');
}
SuperFormalParameterElement superFormalParameter(String name) {
for (var parameter in parameters) {
if (parameter is SuperFormalParameterElement && parameter.name == name) {
return parameter;
}
}
throw StateError('Not found: $name');
}
}