Extension types. Update precluded executables.
See https://github.com/dart-lang/language/commit/8cb002ff08f79abb433fb9c0311f58a20398aceb
Change-Id: I627977abbfe8f8620c76b9a9dff0643c8f35f67f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/339229
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart b/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
index 72c5bd5..d7f1678 100644
--- a/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
+++ b/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
@@ -708,17 +708,19 @@
final implemented = Map.of(declared);
// Prepare candidates for inheritance.
- final extensionCandidates = <Name, List<ExecutableElement>>{};
- final notExtensionCandidates = <Name, List<ExecutableElement>>{};
+ final extensionCandidates = <Name, _ExtensionTypeCandidates>{};
+ final notExtensionCandidates = <Name, _ExtensionTypeCandidates>{};
for (final interface in augmented.interfaces) {
final substitution = Substitution.fromInterfaceType(interface);
for (final entry in getInterface(interface.element).map.entries) {
final name = entry.key;
final executable = ExecutableMember.from2(entry.value, substitution);
if (executable.isExtensionTypeMember) {
- (extensionCandidates[name] ??= []).add(executable);
+ (extensionCandidates[name] ??= _ExtensionTypeCandidates(name))
+ .add(executable);
} else {
- (notExtensionCandidates[name] ??= []).add(executable);
+ (notExtensionCandidates[name] ??= _ExtensionTypeCandidates(name))
+ .add(executable);
}
}
}
@@ -730,29 +732,42 @@
for (final entry in extensionCandidates.entries) {
final name = entry.key;
final candidates = entry.value;
- (redeclared[name] ??= []).addAll(candidates);
+ (redeclared[name] ??= []).addAll(candidates.all);
- // Stop if precluded.
- if (precludedNames.contains(name)) {
+ final notPrecluded = candidates.notPrecluded(
+ precludedNames: precludedNames,
+ precludedMethods: precludedMethods,
+ precludedSetters: precludedSetters,
+ );
+
+ // Stop if all precluded.
+ if (notPrecluded.isEmpty) {
continue;
}
- // If not redeclared, can have either non-extension, or extension.
+ // If not precluded, can have either non-extension, or extension.
final nonExtensionSignatures = notExtensionCandidates[name];
if (nonExtensionSignatures != null) {
- conflicts.add(
- HasNonExtensionAndExtensionMemberConflict(
- name: name,
- nonExtension: nonExtensionSignatures,
- extension: candidates,
- ),
+ final notExtensionNotPrecluded = nonExtensionSignatures.notPrecluded(
+ precludedNames: precludedNames,
+ precludedMethods: precludedMethods,
+ precludedSetters: precludedSetters,
);
+ if (notExtensionNotPrecluded.isNotEmpty) {
+ conflicts.add(
+ HasNonExtensionAndExtensionMemberConflict(
+ name: name,
+ nonExtension: notExtensionNotPrecluded,
+ extension: notPrecluded,
+ ),
+ );
+ }
continue;
}
// The inherited member must be unique.
ExecutableElement? uniqueElement;
- for (final candidate in candidates) {
+ for (final candidate in notPrecluded) {
if (uniqueElement == null) {
uniqueElement = candidate;
} else if (uniqueElement.declaration != candidate.declaration) {
@@ -765,24 +780,12 @@
conflicts.add(
NotUniqueExtensionMemberConflict(
name: name,
- candidates: candidates,
+ candidates: notPrecluded,
),
);
continue;
}
- // Stop if precluded.
- switch (uniqueElement) {
- case MethodElement():
- if (precludedMethods.contains(name)) {
- continue;
- }
- case PropertyAccessorElement(isSetter: true):
- if (precludedSetters.contains(name)) {
- continue;
- }
- }
-
implemented[name] = uniqueElement;
}
@@ -790,10 +793,16 @@
for (final entry in notExtensionCandidates.entries) {
final name = entry.key;
final candidates = entry.value;
- (redeclared[name] ??= []).addAll(candidates);
+ (redeclared[name] ??= []).addAll(candidates.all);
- // Stop if precluded.
- if (precludedNames.contains(name)) {
+ final notPrecluded = candidates.notPrecluded(
+ precludedNames: precludedNames,
+ precludedMethods: precludedMethods,
+ precludedSetters: precludedSetters,
+ );
+
+ // Stop if all precluded.
+ if (notPrecluded.isEmpty) {
continue;
}
@@ -805,7 +814,7 @@
final combinedSignature = combineSignatures(
targetClass: element,
- candidates: candidates,
+ candidates: notPrecluded,
doTopMerge: true,
name: name,
);
@@ -814,24 +823,12 @@
conflicts.add(
CandidatesConflict(
name: name,
- candidates: candidates,
+ candidates: notPrecluded,
),
);
continue;
}
- // Stop if precluded.
- switch (combinedSignature) {
- case MethodElement():
- if (precludedMethods.contains(name)) {
- continue;
- }
- case PropertyAccessorElement(isSetter: true):
- if (precludedSetters.contains(name)) {
- continue;
- }
- }
-
implemented[name] = combinedSignature;
}
@@ -1262,6 +1259,45 @@
});
}
+class _ExtensionTypeCandidates {
+ final Name name;
+ final List<MethodElement> methods = [];
+ final List<PropertyAccessorElement> getters = [];
+ final List<PropertyAccessorElement> setters = [];
+
+ _ExtensionTypeCandidates(this.name);
+
+ List<ExecutableElement> get all {
+ return [...methods, ...getters, ...setters];
+ }
+
+ void add(ExecutableElement element) {
+ switch (element) {
+ case MethodElement():
+ methods.add(element);
+ case PropertyAccessorElement(isGetter: true):
+ getters.add(element);
+ case PropertyAccessorElement(isSetter: true):
+ setters.add(element);
+ }
+ }
+
+ List<ExecutableElement> notPrecluded({
+ required Set<Name> precludedNames,
+ required Set<Name> precludedMethods,
+ required Set<Name> precludedSetters,
+ }) {
+ if (precludedNames.contains(name)) {
+ return const [];
+ }
+ return [
+ if (!precludedMethods.contains(name)) ...methods,
+ ...getters,
+ if (!precludedSetters.contains(name)) ...setters,
+ ];
+ }
+}
+
class _ParameterDesc {
final int? index;
final String? name;
diff --git a/pkg/analyzer/test/src/dart/element/inheritance_manager3_test.dart b/pkg/analyzer/test/src/dart/element/inheritance_manager3_test.dart
index a407216..35953aa 100644
--- a/pkg/analyzer/test/src/dart/element/inheritance_manager3_test.dart
+++ b/pkg/analyzer/test/src/dart/element/inheritance_manager3_test.dart
@@ -1080,7 +1080,7 @@
''');
}
- test_noDeclaration_implementClass_implementExtensionType_hasConflict() async {
+ test_noDeclaration_implementClass_implementExtensionType_hasConflict_methods() async {
final library = await buildLibrary(r'''
class A {
void foo() {}
@@ -1109,7 +1109,7 @@
foo: self::@extensionType::B::@method::foo
it: self::@extensionType::B::@getter::it
conflicts
- HasExtensionAndNotExtensionMemberConflict
+ HasNonExtensionAndExtensionMemberConflict
nonExtension
self::@class::A::@method::foo
extension
@@ -1117,6 +1117,113 @@
''');
}
+ test_noDeclaration_implementClass_implementExtensionType_hasConflict_setters() async {
+ final library = await buildLibrary(r'''
+class A {
+ set foo(int _) {}
+}
+
+extension type B(A it) {
+ set foo(int _) {}
+}
+
+extension type C(A it) implements A, B {}
+''');
+
+ final element = library.extensionType('C');
+ assertInterfaceText(element, r'''
+map
+ it: self::@extensionType::C::@getter::it
+declared
+ it: self::@extensionType::C::@getter::it
+redeclared
+ foo=
+ self::@extensionType::B::@setter::foo
+ self::@class::A::@setter::foo
+ it
+ self::@extensionType::B::@getter::it
+inheritedMap
+ foo=: self::@extensionType::B::@setter::foo
+ it: self::@extensionType::B::@getter::it
+conflicts
+ HasNonExtensionAndExtensionMemberConflict
+ nonExtension
+ self::@class::A::@setter::foo
+ extension
+ self::@extensionType::B::@setter::foo
+''');
+ }
+
+ test_noDeclaration_implementClass_implementExtensionType_noConflict_methodPrecludesSetters() async {
+ final library = await buildLibrary(r'''
+class A {
+ set foo(int _) {}
+}
+
+extension type B(A it) {
+ set foo(int _) {}
+}
+
+extension type C(A it) implements A, B {
+ void foo() {}
+}
+''');
+
+ final element = library.extensionType('C');
+ assertInterfaceText(element, r'''
+map
+ foo: self::@extensionType::C::@method::foo
+ it: self::@extensionType::C::@getter::it
+declared
+ foo: self::@extensionType::C::@method::foo
+ it: self::@extensionType::C::@getter::it
+redeclared
+ foo=
+ self::@extensionType::B::@setter::foo
+ self::@class::A::@setter::foo
+ it
+ self::@extensionType::B::@getter::it
+inheritedMap
+ foo=: self::@extensionType::B::@setter::foo
+ it: self::@extensionType::B::@getter::it
+''');
+ }
+
+ test_noDeclaration_implementClass_implementExtensionType_noConflict_setterPrecludesMethods() async {
+ final library = await buildLibrary(r'''
+class A {
+ void foo() {}
+}
+
+extension type B(A it) {
+ void foo() {}
+}
+
+extension type C(A it) implements A, B {
+ set foo(int _) {}
+}
+''');
+
+ final element = library.extensionType('C');
+ assertInterfaceText(element, r'''
+map
+ foo=: self::@extensionType::C::@setter::foo
+ it: self::@extensionType::C::@getter::it
+declared
+ foo=: self::@extensionType::C::@setter::foo
+ it: self::@extensionType::C::@getter::it
+redeclared
+ foo
+ self::@extensionType::B::@method::foo
+ self::@class::A::@method::foo
+ it
+ self::@extensionType::B::@getter::it
+inheritedMap
+ foo: self::@extensionType::B::@method::foo
+ it: self::@extensionType::B::@getter::it
+''');
+ }
+
test_noDeclaration_implementClass_method() async {
final library = await buildLibrary(r'''
class A {
@@ -1358,7 +1465,43 @@
''');
}
- test_noDeclaration_implementExtensionType_method2_noConflict() async {
+ test_noDeclaration_implementExtensionType_method2_noConflict_setterPrecludes() async {
+ final library = await buildLibrary(r'''
+extension type A1(int it) {
+ void foo() {}
+}
+
+extension type A2(int it) {
+ void foo() {}
+}
+
+extension type B(int it) implements A1, A2 {
+ set foo(int _) {}
+}
+''');
+
+ final element = library.extensionType('B');
+ assertInterfaceText(element, r'''
+map
+ foo=: self::@extensionType::B::@setter::foo
+ it: self::@extensionType::B::@getter::it
+declared
+ foo=: self::@extensionType::B::@setter::foo
+ it: self::@extensionType::B::@getter::it
+redeclared
+ foo
+ self::@extensionType::A1::@method::foo
+ self::@extensionType::A2::@method::foo
+ it
+ self::@extensionType::A1::@getter::it
+ self::@extensionType::A2::@getter::it
+inheritedMap
+ foo: self::@extensionType::A1::@method::foo
+ it: self::@extensionType::A1::@getter::it
+''');
+ }
+
+ test_noDeclaration_implementExtensionType_method2_noConflict_unique() async {
final library = await buildLibrary(r'''
extension type A(int it) {
void foo() {}
@@ -1390,6 +1533,110 @@
''');
}
+ test_noDeclaration_implementExtensionType_setter2_hasConflict() async {
+ final library = await buildLibrary(r'''
+extension type A1(int it) {
+ set foo(int _) {}
+}
+
+extension type A2(int it) {
+ set foo(int _) {}
+}
+
+extension type B(int it) implements A1, A2 {}
+''');
+
+ final element = library.extensionType('B');
+ assertInterfaceText(element, r'''
+map
+ it: self::@extensionType::B::@getter::it
+declared
+ it: self::@extensionType::B::@getter::it
+redeclared
+ foo=
+ self::@extensionType::A1::@setter::foo
+ self::@extensionType::A2::@setter::foo
+ it
+ self::@extensionType::A1::@getter::it
+ self::@extensionType::A2::@getter::it
+inheritedMap
+ foo=: self::@extensionType::A1::@setter::foo
+ it: self::@extensionType::A1::@getter::it
+conflicts
+ NotUniqueExtensionMemberConflict
+ self::@extensionType::A1::@setter::foo
+ self::@extensionType::A2::@setter::foo
+''');
+ }
+
+ test_noDeclaration_implementExtensionType_setter2_noConflict_methodPrecludes() async {
+ final library = await buildLibrary(r'''
+extension type A1(int it) {
+ set foo(int _) {}
+}
+
+extension type A2(int it) {
+ set foo(int _) {}
+}
+
+extension type B(int it) implements A1, A2 {
+ void foo() {}
+}
+''');
+
+ final element = library.extensionType('B');
+ assertInterfaceText(element, r'''
+map
+ foo: self::@extensionType::B::@method::foo
+ it: self::@extensionType::B::@getter::it
+declared
+ foo: self::@extensionType::B::@method::foo
+ it: self::@extensionType::B::@getter::it
+redeclared
+ foo=
+ self::@extensionType::A1::@setter::foo
+ self::@extensionType::A2::@setter::foo
+ it
+ self::@extensionType::A1::@getter::it
+ self::@extensionType::A2::@getter::it
+inheritedMap
+ foo=: self::@extensionType::A1::@setter::foo
+ it: self::@extensionType::A1::@getter::it
+''');
+ }
+
+ test_noDeclaration_implementExtensionType_setter2_noConflict_unique() async {
+ final library = await buildLibrary(r'''
+extension type A(int it) {
+ set foo(int _) {}
+}
+
+extension type B1(int it) implements A {}
+
+extension type B2(int it) implements A {}
+
+extension type C(int it) implements B1, B2 {}
+''');
+
+ final element = library.extensionType('C');
+ assertInterfaceText(element, r'''
+map
+ foo=: self::@extensionType::A::@setter::foo
+ it: self::@extensionType::C::@getter::it
+declared
+ it: self::@extensionType::C::@getter::it
+redeclared
+ foo=
+ self::@extensionType::A::@setter::foo
+ it
+ self::@extensionType::B1::@getter::it
+ self::@extensionType::B2::@getter::it
+inheritedMap
+ foo=: self::@extensionType::A::@setter::foo
+ it: self::@extensionType::B1::@getter::it
+''');
+ }
+
test_withObjectMembers() async {
final library = await buildLibrary(r'''
extension type A(int it) {}
@@ -2733,7 +2980,7 @@
);
case HasNonExtensionAndExtensionMemberConflict _:
_sink.writelnWithIndent(
- 'HasExtensionAndNotExtensionMemberConflict',
+ 'HasNonExtensionAndExtensionMemberConflict',
);
_sink.withIndent(() {
_elementPrinter.writeElementList(