Copy lookup methods from InterfaceElement to Augmented classes

Change-Id: Ie93e6aee9dc0f7a918d694303b0f640296e2df42
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/352971
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Brian Wilkerson
2024-02-17 01:15:37 +00:00
committed by Commit Queue
parent 8fe47542f5
commit 1af1f2ea8d
3 changed files with 479 additions and 0 deletions
@@ -143,6 +143,39 @@ abstract class AugmentedInstanceElement {
/// Returns the setter from [accessors] that has the given [name].
PropertyAccessorElement? getSetter(String name);
/// Returns the element representing the getter that results from looking up
/// the given [name] in this class with respect to the given [library],
/// or `null` if the look up fails.
///
/// The behavior of this method is defined by the Dart Language Specification
/// in section 17.18 Lookup.
PropertyAccessorElement? lookUpGetter({
required String name,
required LibraryElement library,
});
/// Returns the element representing the method that results from looking up
/// the given [name] in this class with respect to the given [library],
/// or `null` if the look up fails.
///
/// The behavior of this method is defined by the Dart Language Specification
/// in section 17.18 Lookup.
MethodElement? lookUpMethod({
required String name,
required LibraryElement library,
});
/// Returns the element representing the setter that results from looking up
/// the given [name] in this class with respect to the given [library],
/// or `null` if the look up fails.
///
/// The behavior of this method is defined by the Dart Language Specification
/// in section 17.18 Lookup.
PropertyAccessorElement? lookUpSetter({
required String name,
required LibraryElement library,
});
}
/// The result of applying augmentations to a [InterfaceElement].
@@ -941,6 +941,18 @@ class CompilationUnitElementImpl extends UriReferencedElementImpl
return null;
}
/// Returns the mixin defined in this compilation unit that has the given
/// [name], or `null` if this compilation unit does not define a mixin with
/// the given name.
MixinElement? getMixin(String name) {
for (final mixin in mixins) {
if (mixin.name == name) {
return mixin;
}
}
return null;
}
void setLinkedData(Reference reference, ElementLinkedData linkedData) {
this.reference = reference;
reference.element = this;
@@ -4420,6 +4432,16 @@ class LibraryElementImpl extends LibraryOrAugmentationElementImpl
return null;
}
MixinElement? getMixin(String name) {
for (final unitElement in units) {
final element = unitElement.getMixin(name);
if (element != null) {
return element;
}
}
return null;
}
/// Return `true` if [reference] comes only from deprecated exports.
bool isFromDeprecatedExport(ExportedReference reference) {
if (reference is ExportedReferenceExported) {
@@ -4981,6 +5003,131 @@ mixin MaybeAugmentedInstanceElementMixin implements AugmentedInstanceElement {
}
return null;
}
@override
PropertyAccessorElement? lookUpGetter({
required String name,
required LibraryElement library,
}) {
return _implementationsOfGetter(name)
.firstWhereOrNull((getter) => getter.isAccessibleIn(library));
}
@override
MethodElement? lookUpMethod({
required String name,
required LibraryElement library,
}) {
return _implementationsOfMethod(name).firstWhereOrNull(
(MethodElement method) => method.isAccessibleIn(library));
}
@override
PropertyAccessorElement? lookUpSetter({
required String name,
required LibraryElement library,
}) {
return _implementationsOfSetter(name).firstWhereOrNull(
(PropertyAccessorElement setter) => setter.isAccessibleIn(library));
}
/// Return an iterable containing all of the implementations of a getter with
/// the given [name] that are defined in this class and any superclass of this
/// class (but not in interfaces).
///
/// The getters that are returned are not filtered in any way. In particular,
/// they can include getters that are not visible in some context. Clients
/// must perform any necessary filtering.
///
/// The getters are returned based on the depth of their defining class; if
/// this class contains a definition of the getter it will occur first, if
/// Object contains a definition of the getter it will occur last.
Iterable<PropertyAccessorElement> _implementationsOfGetter(
String name) sync* {
final visitedClasses = <AugmentedInstanceElement>{};
AugmentedInstanceElement? augmented = this;
while (augmented != null && visitedClasses.add(augmented)) {
var getter = augmented.getGetter(name);
if (getter != null) {
yield getter;
}
if (augmented is! AugmentedInterfaceElement) {
return;
}
for (InterfaceType mixin in augmented.mixins.reversed) {
getter = mixin.element.augmented?.getGetter(name);
if (getter != null) {
yield getter;
}
}
augmented = augmented.declaration.supertype?.element.augmented;
}
}
/// Return an iterable containing all of the implementations of a method with
/// the given [name] that are defined in this class and any superclass of this
/// class (but not in interfaces).
///
/// The methods that are returned are not filtered in any way. In particular,
/// they can include methods that are not visible in some context. Clients
/// must perform any necessary filtering.
///
/// The methods are returned based on the depth of their defining class; if
/// this class contains a definition of the method it will occur first, if
/// Object contains a definition of the method it will occur last.
Iterable<MethodElement> _implementationsOfMethod(String name) sync* {
final visitedClasses = <AugmentedInstanceElement>{};
AugmentedInstanceElement? augmented = this;
while (augmented != null && visitedClasses.add(augmented)) {
var method = augmented.getMethod(name);
if (method != null) {
yield method;
}
if (augmented is! AugmentedInterfaceElement) {
return;
}
for (InterfaceType mixin in augmented.mixins.reversed) {
method = mixin.element.augmented?.getMethod(name);
if (method != null) {
yield method;
}
}
augmented = augmented.declaration.supertype?.element.augmented;
}
}
/// Return an iterable containing all of the implementations of a setter with
/// the given [name] that are defined in this class and any superclass of this
/// class (but not in interfaces).
///
/// The setters that are returned are not filtered in any way. In particular,
/// they can include setters that are not visible in some context. Clients
/// must perform any necessary filtering.
///
/// The setters are returned based on the depth of their defining class; if
/// this class contains a definition of the setter it will occur first, if
/// Object contains a definition of the setter it will occur last.
Iterable<PropertyAccessorElement> _implementationsOfSetter(
String name) sync* {
final visitedClasses = <AugmentedInstanceElement>{};
AugmentedInstanceElement? augmented = this;
while (augmented != null && visitedClasses.add(augmented)) {
var setter = augmented.getSetter(name);
if (setter != null) {
yield setter;
}
if (augmented is! AugmentedInterfaceElement) {
return;
}
for (InterfaceType mixin in augmented.mixins.reversed) {
setter = mixin.element.augmented?.getSetter(name);
if (setter != null) {
yield setter;
}
}
augmented = augmented.declaration.supertype?.element.augmented;
}
}
}
mixin MaybeAugmentedInterfaceElementMixin on MaybeAugmentedInstanceElementMixin
@@ -15,6 +15,7 @@ import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../generated/type_system_base.dart';
import '../../../util/feature_sets.dart';
import '../../summary/elements_base.dart';
import '../resolution/context_collection_resolution.dart';
import 'string_types.dart';
@@ -24,6 +25,7 @@ main() {
defineReflectiveTests(FieldElementImplTest);
defineReflectiveTests(FunctionTypeImplTest);
defineReflectiveTests(InterfaceTypeImplTest);
defineReflectiveTests(MaybeAugmentedInstanceElementMixinTest);
defineReflectiveTests(MethodElementImplTest);
defineReflectiveTests(TypeParameterTypeImplTest);
defineReflectiveTests(VoidTypeImplTest);
@@ -1622,6 +1624,303 @@ A<int>?
}
}
@reflectiveTest
class MaybeAugmentedInstanceElementMixinTest extends ElementsBaseTest {
@override
bool get keepLinkingLibraries => true;
test_lookUpGetter_declared() async {
var library = await buildLibrary('''
class A {
int get g {}
}
''');
var classA = library.getClass('A')!;
var getter = classA.accessors[0];
expect(classA.augmented?.lookUpGetter(name: 'g', library: library),
same(getter));
}
test_lookUpGetter_fromAugmentation() async {
newFile('$testPackageLibPath/a.dart', '''
library augment 'test.dart';
augment class A {
int get g {}
}
''');
var library = await buildLibrary('''
import augment 'a.dart';
class A {}
''');
var classA = library.getClass('A')!;
var getter = classA.augmented!.accessors[0];
expect(classA.augmented?.lookUpGetter(name: 'g', library: library),
same(getter));
}
test_lookUpGetter_inherited() async {
var library = await buildLibrary('''
class A {
int get g {}
}
class B extends A {}
''');
var classA = library.getClass('A')!;
var getter = classA.accessors[0];
var classB = library.getClass('B')!;
expect(classB.augmented?.lookUpGetter(name: 'g', library: library),
same(getter));
}
test_lookUpGetter_inherited_fromAugmentation() async {
newFile('$testPackageLibPath/a.dart', '''
library augment 'test.dart';
augment class A {
int get g {}
}
''');
var library = await buildLibrary('''
import augment 'a.dart';
class A {}
class B extends A {}
''');
var classA = library.getClass('A')!;
var getter = classA.augmented!.accessors[0];
var classB = library.getClass('B')!;
expect(classB.augmented?.lookUpGetter(name: 'g', library: library),
same(getter));
}
test_lookUpGetter_inherited_fromMixin() async {
var library = await buildLibrary('''
mixin A {
int get g {}
}
class B with A {}
''');
var mixinA = library.getMixin('A')!;
var getter = mixinA.accessors[0];
var classB = library.getClass('B')!;
expect(classB.augmented?.lookUpGetter(name: 'g', library: library),
same(getter));
}
test_lookUpGetter_undeclared() async {
var library = await buildLibrary('''
class A {}
''');
var classA = library.getClass('A')!;
expect(classA.augmented?.lookUpGetter(name: 'g', library: library), isNull);
}
test_lookUpGetter_undeclared_recursive() async {
var library = await buildLibrary('''
class A extends B {}
class B extends A {}
''');
var classA = library.getClass('A')!;
expect(classA.augmented?.lookUpGetter(name: 'g', library: library), isNull);
}
test_lookUpMethod_declared() async {
var library = await buildLibrary('''
class A {
int m() {}
}
''');
var classA = library.getClass('A')!;
var method = classA.methods[0];
expect(classA.augmented?.lookUpMethod(name: 'm', library: library),
same(method));
}
test_lookUpMethod_fromAugmentation() async {
newFile('$testPackageLibPath/a.dart', '''
library augment 'test.dart';
augment class A {
int m() {}
}
''');
var library = await buildLibrary('''
import augment 'a.dart';
class A {}
''');
var classA = library.getClass('A')!;
var method = classA.augmented!.methods[0];
expect(classA.augmented?.lookUpMethod(name: 'm', library: library),
same(method));
}
test_lookUpMethod_inherited() async {
var library = await buildLibrary('''
class A {
int m() {}
}
class B extends A {}
''');
var classA = library.getClass('A')!;
var method = classA.methods[0];
var classB = library.getClass('B')!;
expect(classB.augmented?.lookUpMethod(name: 'm', library: library),
same(method));
}
test_lookUpMethod_inherited_fromAugmentation() async {
newFile('$testPackageLibPath/a.dart', '''
library augment 'test.dart';
augment class A {
int m() {}
}
''');
var library = await buildLibrary('''
import augment 'a.dart';
class A {}
class B extends A {}
''');
var classA = library.getClass('A')!;
var method = classA.augmented!.methods[0];
var classB = library.getClass('B')!;
expect(classB.augmented?.lookUpMethod(name: 'm', library: library),
same(method));
}
test_lookUpMethod_inherited_fromMixin() async {
var library = await buildLibrary('''
mixin A {
int m() {}
}
class B with A {}
''');
var mixinA = library.getMixin('A')!;
var method = mixinA.methods[0];
var classB = library.getClass('B')!;
expect(classB.augmented?.lookUpMethod(name: 'm', library: library),
same(method));
}
test_lookUpMethod_undeclared() async {
var library = await buildLibrary('''
class A {}
''');
var classA = library.getClass('A')!;
expect(classA.augmented?.lookUpMethod(name: 'm', library: library), isNull);
}
test_lookUpMethod_undeclared_recursive() async {
var library = await buildLibrary('''
class A extends B {}
class B extends A {}
''');
var classA = library.getClass('A')!;
expect(classA.augmented?.lookUpMethod(name: 'm', library: library), isNull);
}
test_lookUpSetter_declared() async {
var library = await buildLibrary('''
class A {
set s(x) {}
}
''');
var classA = library.getClass('A')!;
var setter = classA.accessors[0];
expect(classA.augmented?.lookUpSetter(name: 's', library: library),
same(setter));
}
test_lookUpSetter_fromAugmentation() async {
newFile('$testPackageLibPath/a.dart', '''
library augment 'test.dart';
augment class A {
set s(x) {}
}
''');
var library = await buildLibrary('''
import augment 'a.dart';
class A {}
''');
var classA = library.getClass('A')!;
var setter = classA.augmented!.accessors[0];
expect(classA.augmented?.lookUpSetter(name: 's', library: library),
same(setter));
}
test_lookUpSetter_inherited() async {
var library = await buildLibrary('''
class A {
set s(x) {}
}
class B extends A {}
''');
var classA = library.getClass('A')!;
var setter = classA.accessors[0];
var classB = library.getClass('B')!;
expect(classB.augmented?.lookUpSetter(name: 's', library: library),
same(setter));
}
test_lookUpSetter_inherited_fromAugmentation() async {
newFile('$testPackageLibPath/a.dart', '''
library augment 'test.dart';
augment class A {
set s(x) {}
}
''');
var library = await buildLibrary('''
import augment 'a.dart';
class A {}
class B extends A {}
''');
var classA = library.getClass('A')!;
var setter = classA.augmented!.accessors[0];
var classB = library.getClass('B')!;
expect(classB.augmented?.lookUpSetter(name: 's', library: library),
same(setter));
}
test_lookUpSetter_inherited_fromMixin() async {
var library = await buildLibrary('''
mixin A {
set s(x) {}
}
class B with A {}
''');
var mixinA = library.getMixin('A')!;
var setter = mixinA.accessors[0];
var classB = library.getClass('B')!;
expect(classB.augmented?.lookUpSetter(name: 's', library: library),
same(setter));
}
test_lookUpSetter_undeclared() async {
var library = await buildLibrary('''
class A {}
''');
var classA = library.getClass('A')!;
expect(classA.augmented?.lookUpSetter(name: 's', library: library), isNull);
}
test_lookUpSetter_undeclared_recursive() async {
var library = await buildLibrary('''
class A extends B {}
class B extends A {}
''');
var classA = library.getClass('A')!;
expect(classA.augmented?.lookUpSetter(name: 's', library: library), isNull);
}
}
@reflectiveTest
class MethodElementImplTest extends AbstractTypeSystemTest {
void test_equal() {