blob: 82e8e97da0c6556296ef6c8e382a571a6ea344d5 [file] [log] [blame]
// Copyright (c) 2024, 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/dart/element/element2.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/utilities/extensions/element.dart';
import 'package:analyzer/src/utilities/extensions/string.dart';
class ClassElementBuilder
extends InstanceElementBuilder<ClassElementImpl2, ClassElementImpl> {
ClassElementBuilder({
required super.element,
required super.firstFragment,
});
void addFragment(ClassElementImpl fragment) {
addFields(fragment.fields);
addConstructors(fragment.constructors);
addAccessors(fragment.accessors);
addMethods(fragment.methods);
if (identical(fragment, firstFragment)) {
_addFirstFragment();
} else {
lastFragment.augmentation = fragment;
lastFragment = fragment;
fragment.augmentedInternal = element;
_updatedAugmented(fragment);
}
}
}
class EnumElementBuilder
extends InstanceElementBuilder<EnumElementImpl2, EnumElementImpl> {
EnumElementBuilder({
required super.element,
required super.firstFragment,
});
void addFragment(EnumElementImpl fragment) {
addFields(fragment.fields);
addConstructors(fragment.constructors);
addAccessors(fragment.accessors);
addMethods(fragment.methods);
if (identical(fragment, firstFragment)) {
_addFirstFragment();
} else {
lastFragment.augmentation = fragment;
lastFragment = fragment;
fragment.augmentedInternal = element;
_updatedAugmented(fragment);
}
}
}
class ExtensionElementBuilder extends InstanceElementBuilder<
ExtensionElementImpl2, ExtensionElementImpl> {
ExtensionElementBuilder({
required super.element,
required super.firstFragment,
});
void addFragment(ExtensionElementImpl fragment) {
addFields(fragment.fields);
addAccessors(fragment.accessors);
addMethods(fragment.methods);
if (identical(fragment, firstFragment)) {
_addFirstFragment();
} else {
lastFragment.augmentation = fragment;
lastFragment = fragment;
fragment.augmentedInternal = element;
_updatedAugmented(fragment);
}
}
}
class ExtensionTypeElementBuilder extends InstanceElementBuilder<
ExtensionTypeElementImpl2, ExtensionTypeElementImpl> {
ExtensionTypeElementBuilder({
required super.element,
required super.firstFragment,
});
void addFragment(ExtensionTypeElementImpl fragment) {
addFields(fragment.fields);
addConstructors(fragment.constructors);
addAccessors(fragment.accessors);
addMethods(fragment.methods);
if (identical(fragment, firstFragment)) {
_addFirstFragment();
} else {
lastFragment.augmentation = fragment;
lastFragment = fragment;
fragment.augmentedInternal = element;
_updatedAugmented(fragment);
}
}
}
/// A builder for top-level fragmented elements, e.g. classes.
class FragmentedElementBuilder<E extends Element2, F extends Fragment> {
final E element;
final F firstFragment;
F lastFragment;
FragmentedElementBuilder({
required this.element,
required this.firstFragment,
}) : lastFragment = firstFragment;
/// If [fragment] is an augmentation, set its previous fragment to
/// [lastFragment].
///
/// We invoke this method on any [FragmentedElementBuilder] associated with
/// the name of [fragment], even if it is not a correct builder for this
/// [fragment]. So, the [lastFragment] might have a wrong type, but we still
/// want to remember it for generating the corresponding diagnostic.
void setPreviousFor(AugmentableElement fragment) {
if (fragment.isAugmentation) {
// TODO(scheglov): hopefully the type check can be removed in the future.
if (lastFragment case ElementImpl lastFragment) {
fragment.augmentationTargetAny = lastFragment;
}
}
}
}
class GetterElementBuilder extends FragmentedElementBuilder<GetterElementImpl,
PropertyAccessorElementImpl> {
GetterElementBuilder({
required super.element,
required super.firstFragment,
});
void addFragment(PropertyAccessorElementImpl fragment) {
if (!identical(fragment, firstFragment)) {
lastFragment.augmentation = fragment;
lastFragment = fragment;
fragment.element = element;
}
}
}
abstract class InstanceElementBuilder<E extends InstanceElementImpl2,
F extends InstanceElementImpl> extends FragmentedElementBuilder<E, F> {
final Map<String, FieldElementImpl> fields = {};
final Map<String, ConstructorElementImpl> constructors = {};
final Map<String, PropertyAccessorElementImpl> getters = {};
final Map<String, PropertyAccessorElementImpl> setters = {};
final Map<String, MethodElementImpl> methods = {};
InstanceElementBuilder({
required super.element,
required super.firstFragment,
});
void addAccessors(List<PropertyAccessorElementImpl> fragments) {
for (var fragment in fragments) {
var name = fragment.name;
if (fragment.isGetter) {
if (fragment.isAugmentation) {
if (getters[name] case var target?) {
target.augmentation = fragment;
fragment.augmentationTargetAny = target;
} else {
var target = _recoveryAugmentationTarget(name);
fragment.augmentationTargetAny = target;
}
}
getters[name] = fragment;
} else {
if (fragment.isAugmentation) {
if (setters[name] case var target?) {
target.augmentation = fragment;
fragment.augmentationTargetAny = target;
} else {
var target = _recoveryAugmentationTarget(name);
fragment.augmentationTargetAny = target;
}
}
setters[name] = fragment;
}
}
}
void addConstructors(List<ConstructorElementImpl> fragments) {
for (var fragment in fragments) {
var name = fragment.name;
if (fragment.isAugmentation) {
if (constructors[name] case var target?) {
target.augmentation = fragment;
fragment.augmentationTargetAny = target;
} else {
var target = _recoveryAugmentationTarget(name);
fragment.augmentationTargetAny = target;
}
}
constructors[name] = fragment;
}
}
void addFields(List<FieldElementImpl> fragments) {
for (var fragment in fragments) {
var name = fragment.name;
if (fragment.isAugmentation) {
if (fields[name] case var target?) {
target.augmentation = fragment;
fragment.augmentationTargetAny = target;
} else {
var target = _recoveryAugmentationTarget(name);
fragment.augmentationTargetAny = target;
}
}
fields[name] = fragment;
}
}
void addMethods(List<MethodElementImpl> fragments) {
for (var fragment in fragments) {
var name = fragment.name;
if (fragment.isAugmentation) {
if (methods[name] case var target?) {
target.augmentation = fragment;
fragment.augmentationTargetAny = target;
} else {
var target = _recoveryAugmentationTarget(name);
fragment.augmentationTargetAny = target;
}
}
methods[name] = fragment;
}
}
void _addFirstFragment() {
var firstFragment = this.firstFragment;
var augmented = firstFragment.augmented;
augmented.fields.addAll(firstFragment.fields);
augmented.accessors.addAll(firstFragment.accessors);
augmented.methods.addAll(firstFragment.methods);
if (augmented is InterfaceElementImpl2) {
if (firstFragment is InterfaceElementImpl) {
augmented.mixins.addAll(firstFragment.mixins);
augmented.interfaces.addAll(firstFragment.interfaces);
augmented.constructors.addAll(firstFragment.constructors);
}
}
if (augmented is MixinElementImpl2) {
if (firstFragment is MixinElementImpl) {
augmented.superclassConstraints.addAll(
firstFragment.superclassConstraints,
);
}
}
}
ElementImpl? _recoveryAugmentationTarget(String name) {
name = name.removeSuffix('=') ?? name;
ElementImpl? target;
target ??= getters[name];
target ??= setters['$name='];
target ??= constructors[name];
target ??= methods[name];
return target;
}
void _updatedAugmented(InstanceElementImpl augmentation) {
var element = this.element;
var firstFragment = this.firstFragment;
var firstTypeParameters = firstFragment.typeParameters;
MapSubstitution toFirstFragment;
var augmentationTypeParameters = augmentation.typeParameters;
if (augmentationTypeParameters.length == firstTypeParameters.length) {
toFirstFragment = Substitution.fromPairs(
augmentationTypeParameters,
firstTypeParameters.instantiateNone(),
);
} else {
toFirstFragment = Substitution.fromPairs(
augmentationTypeParameters,
List.filled(
augmentationTypeParameters.length,
InvalidTypeImpl.instance,
),
);
}
if (augmentation is InterfaceElementImpl &&
firstFragment is InterfaceElementImpl &&
element is InterfaceElementImpl2) {
element.constructors = [
...element.constructors.notAugmented,
...augmentation.constructors.notAugmented.map((element) {
if (toFirstFragment.map.isEmpty) {
return element;
}
return ConstructorMember(
declaration: element,
augmentationSubstitution: toFirstFragment,
substitution: Substitution.empty,
);
}),
];
}
element.fields = [
...element.fields.notAugmented,
...augmentation.fields.notAugmented.map((element) {
if (toFirstFragment.map.isEmpty) {
return element;
}
return FieldMember(element, toFirstFragment, Substitution.empty);
}),
];
element.accessors = [
...element.accessors.notAugmented,
...augmentation.accessors.notAugmented.map((element) {
if (toFirstFragment.map.isEmpty) {
return element;
}
return PropertyAccessorMember(
element, toFirstFragment, Substitution.empty);
}),
];
element.methods = [
...element.methods.notAugmented,
...augmentation.methods.notAugmented.map((element) {
if (toFirstFragment.map.isEmpty) {
return element;
}
return MethodMember(element, toFirstFragment, Substitution.empty);
}),
];
}
}
class MixinElementBuilder
extends InstanceElementBuilder<MixinElementImpl2, MixinElementImpl> {
MixinElementBuilder({
required super.element,
required super.firstFragment,
});
void addFragment(MixinElementImpl fragment) {
addFields(fragment.fields);
addAccessors(fragment.accessors);
addMethods(fragment.methods);
if (identical(fragment, firstFragment)) {
_addFirstFragment();
} else {
lastFragment.augmentation = fragment;
lastFragment = fragment;
fragment.augmentedInternal = element;
_updatedAugmented(fragment);
}
}
}
class SetterElementBuilder extends FragmentedElementBuilder<SetterElementImpl,
PropertyAccessorElementImpl> {
SetterElementBuilder({
required super.element,
required super.firstFragment,
});
void addFragment(PropertyAccessorElementImpl fragment) {
if (!identical(fragment, firstFragment)) {
lastFragment.augmentation = fragment;
lastFragment = fragment;
fragment.element = element;
}
}
}
class TopLevelFunctionElementBuilder extends FragmentedElementBuilder<
TopLevelFunctionElementImpl, FunctionElementImpl> {
TopLevelFunctionElementBuilder({
required super.element,
required super.firstFragment,
});
void addFragment(FunctionElementImpl fragment) {
if (!identical(fragment, firstFragment)) {
lastFragment.augmentation = fragment;
lastFragment = fragment;
fragment.element = element;
}
}
}
class TopLevelVariableElementBuilder extends FragmentedElementBuilder<
TopLevelVariableElementImpl2, TopLevelVariableElementImpl> {
TopLevelVariableElementBuilder({
required super.element,
required super.firstFragment,
});
void addFragment(TopLevelVariableElementImpl fragment) {
if (!identical(fragment, firstFragment)) {
lastFragment.augmentation = fragment;
lastFragment = fragment;
fragment.element = element;
}
}
}
class TypeAliasElementBuilder extends FragmentedElementBuilder<
TypeAliasElementImpl2, TypeAliasElementImpl> {
TypeAliasElementBuilder({
required super.element,
required super.firstFragment,
});
void addFragment(TypeAliasElementImpl fragment) {
if (!identical(fragment, firstFragment)) {
lastFragment.augmentation = fragment;
lastFragment = fragment;
// fragment.element = element;
}
}
}
extension<T extends ExecutableElement> on List<T> {
Iterable<T> get notAugmented {
return where((e) => e.augmentation == null);
}
}
extension<T extends PropertyInducingElement> on List<T> {
Iterable<T> get notAugmented {
return where((e) => e.augmentation == null);
}
}