| // 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/dart/element/type.dart'; | |
| import 'package:analyzer/src/dart/element/element.dart'; | |
| import 'package:analyzer/src/test_utilities/function_ast_visitor.dart'; | |
| import 'package:analyzer/src/utilities/extensions/element.dart'; | |
| import 'package:collection/collection.dart'; | |
| /// Helper for finding elements declared in the resolved [unit]. | |
| class FindElement extends _FindElementBase { | |
| final CompilationUnit unit; | |
| new(this.unit); | |
| @override | |
| LibraryFragment get libraryFragment => unit.declaredFragment!; | |
| LibraryExportImpl export(String targetUri) { | |
| LibraryExport? result; | |
| for (var export in libraryFragment.libraryExports) { | |
| var exportedUri = export.exportedLibrary?.uri.toString(); | |
| if (exportedUri == targetUri) { | |
| if (result != null) { | |
| throw StateError('Not unique: $targetUri'); | |
| } | |
| result = export; | |
| } | |
| } | |
| if (result != null) { | |
| return result as LibraryExportImpl; | |
| } | |
| throw StateError('Not found: $targetUri'); | |
| } | |
| FieldFormalParameterElement fieldFormalParameter(String name) { | |
| return parameter(name) as FieldFormalParameterElement; | |
| } | |
| TopLevelFunctionElement function(String name) { | |
| for (var function in libraryElement.topLevelFunctions) { | |
| if (function.name == name) { | |
| return function; | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| LibraryImportImpl import(String targetUri, {bool mustBeUnique = true}) { | |
| LibraryImport? importElement; | |
| for (var libraryFragment in libraryFragment.withEnclosing2) { | |
| for (var import in libraryFragment.libraryImports) { | |
| var importedUri = import.importedLibrary?.uri.toString(); | |
| if (importedUri == targetUri) { | |
| if (importElement == null) { | |
| importElement = import; | |
| } else if (mustBeUnique) { | |
| throw StateError('Not unique: $targetUri'); | |
| } | |
| } | |
| } | |
| } | |
| if (importElement != null) { | |
| return importElement as LibraryImportImpl; | |
| } | |
| 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.accept2( | |
| FunctionAstVisitor( | |
| label: (node) { | |
| updateResult(node.declaredFragment!.element); | |
| }, | |
| ), | |
| ); | |
| if (result == null) { | |
| throw StateError('Not found: $name'); | |
| } | |
| return result!; | |
| } | |
| LibraryFragmentImpl libraryFragmentByUri(String targetUri) { | |
| LibraryFragment? result; | |
| for (var fragment in libraryElement.fragments) { | |
| if ('${fragment.source.uri}' == targetUri) { | |
| if (result != null) { | |
| throw StateError('Not unique: $targetUri'); | |
| } | |
| result = fragment; | |
| } | |
| } | |
| if (result != null) { | |
| return result as LibraryFragmentImpl; | |
| } | |
| throw StateError('Not found: $targetUri'); | |
| } | |
| LocalFunctionElement localFunction(String name) { | |
| LocalFunctionElement? result; | |
| unit.accept2( | |
| FunctionAstVisitor( | |
| localFunctionDeclaration: (node) { | |
| var element = node.declaredFragment?.element; | |
| if (element != null && 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.accept2( | |
| FunctionAstVisitor( | |
| catchClauseParameter: (node) { | |
| updateResult(node.declaredFragment!.element); | |
| }, | |
| declaredIdentifier: (node) { | |
| updateResult(node.declaredFragment!.element); | |
| }, | |
| declaredVariablePattern: (node) { | |
| updateResult(node.declaredFragment!.element); | |
| }, | |
| localVariable: (node) { | |
| updateResult(node.declaredFragment!.element); | |
| }, | |
| ), | |
| ); | |
| if (result == null) { | |
| throw StateError('Not found: $name'); | |
| } | |
| return result!; | |
| } | |
| @override | |
| // TODO(fshcheglov): rename to formalParameter() | |
| FormalParameterElement parameter(String name) { | |
| FormalParameterElement? result; | |
| void findIn(List<FormalParameterElement> formalParameters) { | |
| for (var formalParameter in formalParameters) { | |
| if (formalParameter.name == name) { | |
| if (result != null) { | |
| throw StateError('Not unique: $name'); | |
| } | |
| result = formalParameter; | |
| } | |
| } | |
| } | |
| void findInExecutables(List<ExecutableElement> executables) { | |
| for (var executable in executables) { | |
| findIn(executable.formalParameters); | |
| } | |
| } | |
| void findInClasses(List<InterfaceElement> classes) { | |
| for (var class_ in classes) { | |
| findInExecutables(class_.getters); | |
| findInExecutables(class_.setters); | |
| findInExecutables(class_.constructors); | |
| findInExecutables(class_.methods); | |
| } | |
| } | |
| findInExecutables(libraryElement.getters); | |
| findInExecutables(libraryElement.setters); | |
| findInExecutables(libraryElement.topLevelFunctions); | |
| findInClasses(libraryElement.classes); | |
| findInClasses(libraryElement.enums); | |
| findInClasses(libraryElement.extensionTypes); | |
| findInClasses(libraryElement.mixins); | |
| for (var extension_ in libraryElement.extensions) { | |
| findInExecutables(extension_.getters); | |
| findInExecutables(extension_.setters); | |
| findInExecutables(extension_.methods); | |
| } | |
| for (var typeAlias in libraryElement.typeAliases) { | |
| var aliasedType = typeAlias.aliasedType; | |
| if (aliasedType is FunctionType) { | |
| findIn(aliasedType.formalParameters); | |
| } | |
| } | |
| unit.accept2( | |
| FunctionAstVisitor( | |
| functionExpression: (node) { | |
| var functionElement = node.declaredFragment!.element; | |
| findIn(functionElement.formalParameters); | |
| }, | |
| localFunctionDeclaration: (node) { | |
| var functionElement = node.declaredFragment!.element; | |
| findIn(functionElement.formalParameters); | |
| }, | |
| ), | |
| ); | |
| if (result != null) { | |
| return result!; | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| LibraryFragmentImpl part(String targetUri) { | |
| LibraryFragment? result; | |
| for (var partElement in libraryFragment.partIncludes) { | |
| var uri = partElement.uri; | |
| if (uri is DirectiveUriWithUnit) { | |
| var libraryFragment = uri.libraryFragment; | |
| if ('${libraryFragment.source.uri}' == targetUri) { | |
| if (result != null) { | |
| throw StateError('Not unique: $targetUri'); | |
| } | |
| result = libraryFragment; | |
| } | |
| } | |
| } | |
| if (result != null) { | |
| return result as LibraryFragmentImpl; | |
| } | |
| throw StateError('Not found: $targetUri'); | |
| } | |
| PartFindElement partFind(String targetUri) { | |
| var part = this.part(targetUri); | |
| return PartFindElement(part); | |
| } | |
| PrefixElement prefix(String name) { | |
| for (var libraryFragment in libraryFragment.withEnclosing2) { | |
| for (var importPrefix in libraryFragment.prefixes) { | |
| if (importPrefix.name == name) { | |
| return importPrefix; | |
| } | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| TypeParameterElement typeParameter(String name) { | |
| TypeParameterElement? result; | |
| unit.accept2( | |
| FunctionAstVisitor( | |
| typeParameter: (node) { | |
| var element = node.declaredFragment!.element; | |
| if (element.name == name) { | |
| if (result != null) { | |
| throw StateError('Not unique: $name'); | |
| } | |
| result = element; | |
| } | |
| }, | |
| ), | |
| ); | |
| if (result != null) { | |
| return result!; | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| } | |
| /// Helper for searching imported elements. | |
| class ImportFindElement extends _FindElementBase { | |
| final LibraryImport import; | |
| new(this.import); | |
| LibraryElement get importedLibrary => import.importedLibrary!; | |
| @override | |
| LibraryFragment get libraryFragment { | |
| return importedLibrary.firstFragment; | |
| } | |
| PrefixElement? get prefix => import.prefix?.element; | |
| } | |
| class PartFindElement extends _FindElementBase { | |
| @override | |
| final LibraryFragment libraryFragment; | |
| new(this.libraryFragment); | |
| } | |
| abstract class _FindElementBase { | |
| LibraryElement get libraryElement => libraryFragment.element; | |
| LibraryFragment get libraryFragment; | |
| ClassElement class_(String name) { | |
| for (var class_ in libraryElement.classes) { | |
| if (class_.name == name) { | |
| return class_; | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| InterfaceElement classOrMixin(String name) { | |
| for (var class_ in libraryElement.classes) { | |
| if (class_.name == name) { | |
| return class_; | |
| } | |
| } | |
| for (var mixin in libraryElement.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 libraryElement.classes) { | |
| if (of == null || class_.name == of) { | |
| findIn(class_.constructors); | |
| } | |
| } | |
| for (var enum_ in libraryElement.enums) { | |
| if (of == null || enum_.name == of) { | |
| findIn(enum_.constructors); | |
| } | |
| } | |
| for (var extensionType in libraryElement.extensionTypes) { | |
| if (of == null || extensionType.name == of) { | |
| findIn(extensionType.constructors); | |
| } | |
| } | |
| if (result != null) { | |
| return result!; | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| EnumElement enum_(String name) { | |
| for (var enum_ in libraryElement.enums) { | |
| if (enum_.name == name) { | |
| return enum_; | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| ExtensionElement extension_(String name) { | |
| for (var extension_ in libraryElement.extensions) { | |
| if (extension_.name == name) { | |
| return extension_; | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| ExtensionTypeElement extensionType(String name) { | |
| for (var element in libraryElement.extensionTypes) { | |
| if (element.name == name) { | |
| return element; | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| FieldElement field(String name, {String? of}) { | |
| return _findInClassesLike( | |
| className: of, | |
| fromClass: (element) => element.fields.named(name), | |
| fromExtension: (element) => element.fields.named(name), | |
| ); | |
| } | |
| GetterElement getter(String name, {String? of}) { | |
| return _findInClassesLike( | |
| className: of, | |
| fromClass: (element) => element.getters.named(name), | |
| fromExtension: (element) => element.getters.named(name), | |
| ); | |
| } | |
| MethodElement method(String name, {String? of}) { | |
| return _findInClassesLike( | |
| className: of, | |
| fromClass: (element) => element.methods.named(name), | |
| fromExtension: (element) => element.methods.named(name), | |
| ); | |
| } | |
| MixinElement mixin(String name) { | |
| for (var mixin in libraryElement.mixins) { | |
| if (mixin.name == name) { | |
| return mixin; | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| FormalParameterElement parameter(String name) { | |
| FormalParameterElement? result; | |
| for (var class_ in libraryElement.classes) { | |
| for (var constructor in class_.constructors) { | |
| for (var formalParameter in constructor.formalParameters) { | |
| if (formalParameter.name == name) { | |
| if (result != null) { | |
| throw StateError('Not unique: $name'); | |
| } | |
| result = formalParameter; | |
| } | |
| } | |
| } | |
| } | |
| if (result != null) { | |
| return result; | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| SetterElement setter(String name, {String? of}) { | |
| return _findInClassesLike( | |
| className: of, | |
| fromClass: (element) => element.setters.named(name), | |
| fromExtension: (element) => element.setters.named(name), | |
| ); | |
| } | |
| TopLevelFunctionElement topFunction(String name) { | |
| for (var function in libraryElement.topLevelFunctions) { | |
| if (function.name == name) { | |
| return function; | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| GetterElement topGet(String name) { | |
| return topVar(name).getter!; | |
| } | |
| SetterElement topSet(String name) { | |
| return topVar(name).setter!; | |
| } | |
| TopLevelVariableElementImpl topVar(String name) { | |
| for (var variable in libraryElement.topLevelVariables) { | |
| if (variable.name == name) { | |
| return variable as TopLevelVariableElementImpl; | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| TypeAliasElement typeAlias(String name) { | |
| for (var element in libraryElement.typeAliases) { | |
| if (element.name == name) { | |
| return element; | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| ConstructorElement unnamedConstructor(String name) { | |
| return _findInClassesLike( | |
| className: name, | |
| fromClass: (e) => e.constructors.firstWhereOrNull((element) { | |
| return element.name == 'new'; | |
| }), | |
| fromExtension: (_) => null, | |
| ); | |
| } | |
| ExtensionElement unnamedExtension() { | |
| for (var extension_ in libraryElement.extensions) { | |
| if (extension_.name == null) { | |
| return extension_; | |
| } | |
| } | |
| throw StateError('Not found: an unnamed extension'); | |
| } | |
| T _findInClassesLike<T extends Element>({ | |
| required String? className, | |
| required T? Function(InterfaceElement element) fromClass, | |
| required T? Function(ExtensionElement element) fromExtension, | |
| }) { | |
| bool filter(InstanceElement element) { | |
| return className == null || element.name == className; | |
| } | |
| var classes = [ | |
| ...libraryElement.classes, | |
| ...libraryElement.enums, | |
| ...libraryElement.extensionTypes, | |
| ...libraryElement.mixins, | |
| ]; | |
| var results = [ | |
| ...classes.where(filter).map(fromClass), | |
| ...libraryElement.extensions.where(filter).map(fromExtension), | |
| ].nonNulls.toList(); | |
| var result = results.singleOrNull; | |
| if (result != null) { | |
| return result; | |
| } | |
| if (results.isEmpty) { | |
| throw StateError('Not found'); | |
| } else { | |
| throw StateError('Not unique'); | |
| } | |
| } | |
| } | |
| extension<T extends Element> on List<T> { | |
| T? named(String targetName) { | |
| for (var element in this) { | |
| if (element.name == targetName) { | |
| return element; | |
| } | |
| } | |
| return null; | |
| } | |
| } | |
| extension ExecutableElementExtensions on ExecutableElement { | |
| FormalParameterElement parameter(String name) { | |
| for (var formalParameter in formalParameters) { | |
| if (formalParameter.name == name) { | |
| return formalParameter; | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| SuperFormalParameterElement superFormalParameter(String name) { | |
| for (var formalParameter in formalParameters) { | |
| if (formalParameter is SuperFormalParameterElement && | |
| formalParameter.name == name) { | |
| return formalParameter; | |
| } | |
| } | |
| throw StateError('Not found: $name'); | |
| } | |
| } |