blob: 570ef8f63179e2070e373e9da511af64954432b3 [file] [log] [blame]
// Copyright (c) 2016, 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/element/element.dart';
import 'package:analyzer/src/dart/element/element.dart';
/// Keeps track of the set of non-synthetic child elements of an element,
/// yielding them one at a time in response to "get" method calls.
class ElementWalker {
/// The element whose child elements are being walked.
final Element element;
String? libraryFilePath;
String? unitFilePath;
List<PropertyAccessorElement>? _accessors;
int _accessorIndex = 0;
List<ClassElement>? _classes;
int _classIndex = 0;
List<ConstructorElement>? _constructors;
int _constructorIndex = 0;
List<ClassElement>? _enums;
int _enumIndex = 0;
List<ExtensionElement>? _extensions;
int _extensionIndex = 0;
List<ExecutableElement>? _functions;
int _functionIndex = 0;
List<ClassElement>? _mixins;
int _mixinIndex = 0;
List<ParameterElement>? _parameters;
int _parameterIndex = 0;
List<TypeAliasElement>? _typedefs;
int _typedefIndex = 0;
List<TypeParameterElement>? _typeParameters;
int _typeParameterIndex = 0;
List<VariableElement>? _variables;
int _variableIndex = 0;
/// Creates an [ElementWalker] which walks the child elements of a class
/// element.
ElementWalker.forClass(ClassElement element)
: element = element,
_accessors = element.accessors.where(_isNotSynthetic).toList(),
_constructors = element.isMixinApplication
? null
: element.constructors.where(_isNotSynthetic).toList(),
_functions = element.methods,
_typeParameters = element.typeParameters,
_variables = element.fields.where(_isNotSynthetic).toList();
/// Creates an [ElementWalker] which walks the child elements of a compilation
/// unit element.
ElementWalker.forCompilationUnit(CompilationUnitElementImpl element,
{this.libraryFilePath, this.unitFilePath})
: element = element,
_accessors = element.accessors.where(_isNotSynthetic).toList(),
_classes = element.classes,
_enums = element.enums,
_extensions = element.extensions,
_functions = element.functions,
_mixins = element.mixins,
_typedefs = element.typeAliases,
_variables = element.topLevelVariables.where(_isNotSynthetic).toList();
/// Creates an [ElementWalker] which walks the child elements of a compilation
/// unit element.
ElementWalker.forExecutable(ExecutableElement element)
: element = element,
_functions = const <ExecutableElement>[],
_parameters = element.parameters,
_typeParameters = element.typeParameters;
/// Creates an [ElementWalker] which walks the child elements of an extension
/// element.
ElementWalker.forExtension(ExtensionElement element)
: element = element,
_accessors = element.accessors.where(_isNotSynthetic).toList(),
_functions = element.methods,
_typeParameters = element.typeParameters,
_variables = element.fields.where(_isNotSynthetic).toList();
/// Creates an [ElementWalker] which walks the child elements of a typedef
/// element.
ElementWalker.forGenericFunctionType(GenericFunctionTypeElement element)
: element = element,
_parameters = element.parameters,
_typeParameters = element.typeParameters;
/// Creates an [ElementWalker] which walks the child elements of a typedef
/// element defined using a generic function type.
ElementWalker.forGenericTypeAlias(TypeAliasElement element)
: element = element,
_typeParameters = element.typeParameters;
/// Creates an [ElementWalker] which walks the child elements of a parameter
/// element.
ElementWalker.forParameter(ParameterElement element)
: element = element,
_parameters = element.parameters,
_typeParameters = element.typeParameters;
/// Creates an [ElementWalker] which walks the child elements of a typedef
/// element.
ElementWalker.forTypedef(TypeAliasElement element)
: element = element,
_parameters =
(element.aliasedElement as GenericFunctionTypeElement).parameters,
_typeParameters = element.typeParameters;
String? get _unitSourceContent {
Element? element = this.element;
while (element != null) {
if (element is CompilationUnitElementImpl) {
return element.sourceContent;
}
element = element.enclosingElement;
}
}
void consumeLocalElements() {
_functionIndex = _functions!.length;
}
void consumeParameters() {
_parameterIndex = _parameters!.length;
}
/// Returns the next non-synthetic child of [element] which is an accessor;
/// throws an [IndexError] if there are no more.
PropertyAccessorElementImpl getAccessor() {
// TODO(scheglov) Remove after fixing.
// https://github.com/dart-lang/sdk/issues/46392
var accessors = _accessors;
if (accessors != null && _accessorIndex >= accessors.length) {
throw StateError(
'[_accessorIndex: $_accessorIndex]'
'[_accessors.length: ${accessors.length}]'
'[accessors: $accessors]'
'[element.source: ${element.source?.fullName}]'
'[libraryFilePath: $libraryFilePath]'
'[unitFilePath: $unitFilePath]'
'[unitSourceContent: $_unitSourceContent]',
);
}
return _accessors![_accessorIndex++] as PropertyAccessorElementImpl;
}
/// Returns the next non-synthetic child of [element] which is a class; throws
/// an [IndexError] if there are no more.
ClassElementImpl getClass() {
// TODO(scheglov) Remove after fixing.
// https://github.com/dart-lang/sdk/issues/46392
var classes = _classes;
if (classes != null && _classIndex >= classes.length) {
throw StateError(
'[_classIndex: $_classIndex]'
'[classes.length: ${classes.length}]'
'[classes: $classes]'
'[element.source: ${element.source?.fullName}]'
'[libraryFilePath: $libraryFilePath]'
'[unitFilePath: $unitFilePath]'
'[unitSourceContent: $_unitSourceContent]',
);
}
return _classes![_classIndex++] as ClassElementImpl;
}
/// Returns the next non-synthetic child of [element] which is a constructor;
/// throws an [IndexError] if there are no more.
ConstructorElementImpl getConstructor() =>
_constructors![_constructorIndex++] as ConstructorElementImpl;
/// Returns the next non-synthetic child of [element] which is an enum; throws
/// an [IndexError] if there are no more.
EnumElementImpl getEnum() => _enums![_enumIndex++] as EnumElementImpl;
ExtensionElement getExtension() => _extensions![_extensionIndex++];
/// Returns the next non-synthetic child of [element] which is a top level
/// function, method, or local function; throws an [IndexError] if there are
/// no more.
ExecutableElementImpl getFunction() =>
_functions![_functionIndex++] as ExecutableElementImpl;
/// Returns the next non-synthetic child of [element] which is a mixin; throws
/// an [IndexError] if there are no more.
ClassElement getMixin() => _mixins![_mixinIndex++];
/// Returns the next non-synthetic child of [element] which is a parameter;
/// throws an [IndexError] if there are no more.
ParameterElementImpl getParameter() =>
_parameters![_parameterIndex++] as ParameterElementImpl;
/// Returns the next non-synthetic child of [element] which is a typedef;
/// throws an [IndexError] if there are no more.
TypeAliasElementImpl getTypedef() =>
_typedefs![_typedefIndex++] as TypeAliasElementImpl;
/// Returns the next non-synthetic child of [element] which is a type
/// parameter; throws an [IndexError] if there are no more.
TypeParameterElementImpl getTypeParameter() =>
_typeParameters![_typeParameterIndex++] as TypeParameterElementImpl;
/// Returns the next non-synthetic child of [element] which is a top level
/// variable, field, or local variable; throws an [IndexError] if there are no
/// more.
VariableElementImpl getVariable() {
// TODO(scheglov) Remove after fixing.
// https://github.com/dart-lang/sdk/issues/46392
var variables = _variables;
if (variables != null && _variableIndex >= variables.length) {
throw StateError(
'[_variableIndex: $_variableIndex]'
'[_variables.length: ${variables.length}]'
'[variables: $variables]'
'[element.source: ${element.source?.fullName}]'
'[libraryFilePath: $libraryFilePath]'
'[unitFilePath: $unitFilePath]'
'[unitSourceContent: $_unitSourceContent]',
);
}
return _variables![_variableIndex++] as VariableElementImpl;
}
/// Verifies that all non-synthetic children of [element] have been obtained
/// from their corresponding "get" method calls; if not, throws a
/// [StateError].
void validate() {
void check(List<Element>? elements, int index) {
if (elements != null && elements.length != index) {
throw StateError(
'Unmatched ${elements[index].runtimeType} ${elements[index]}');
}
}
check(_accessors, _accessorIndex);
check(_classes, _classIndex);
check(_constructors, _constructorIndex);
check(_enums, _enumIndex);
check(_functions, _functionIndex);
check(_parameters, _parameterIndex);
check(_typedefs, _typedefIndex);
check(_typeParameters, _typeParameterIndex);
check(_variables, _variableIndex);
}
static bool _isNotSynthetic(Element e) => !e.isSynthetic;
}