Extension type. Issue 53868. Report an error when inherit a method and setter.

Bug: https://github.com/dart-lang/sdk/issues/53868
Change-Id: I5e79cef42453647797a89bed21cac46a318a0f4c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/332443
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Konstantin Shcheglov 2023-10-26 22:13:22 +00:00 committed by Commit Queue
parent abf89a6803
commit 335aec593f
8 changed files with 243 additions and 13 deletions

View file

@ -339,6 +339,8 @@ CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD:
The fix is to rename one of the two, but we can't know what name to use.
CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES:
status: noFix
CompileTimeErrorCode.CONFLICTING_INHERITED_METHOD_AND_SETTER:
status: needsEvaluation
CompileTimeErrorCode.CONFLICTING_METHOD_AND_FIELD:
status: noFix
notes: |-

View file

@ -1183,6 +1183,10 @@ class Name {
Name._internal(this.libraryUri, this.name, this.isPublic, this.hashCode);
Name get forSetter {
return Name(libraryUri, '$name=');
}
@override
bool operator ==(Object other) {
return other is Name &&

View file

@ -551,6 +551,20 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
hasPublishedDocs: true,
);
/// 10.11 Class Member Conflicts: Let `C` be a class. It is a compile-time
/// error if the interface of `C` has an instance method named `n` and an
/// instance setter with basename `n`.
///
/// Parameters:
/// 0: the name of the enclosing element kind - class, extension type, etc
/// 1: the name of the enclosing element
/// 2: the name of the conflicting method / setter
static const CompileTimeErrorCode CONFLICTING_INHERITED_METHOD_AND_SETTER =
CompileTimeErrorCode(
'CONFLICTING_INHERITED_METHOD_AND_SETTER',
"The {0} '{1}' can't inherit both a method and a setter named '{2}'.",
);
/// 10.11 Class Member Conflicts: Let `C` be a class. It is a compile-time
/// error if `C` declares a method named `n`, and has a getter or a setter
/// with basename `n`.

View file

@ -95,6 +95,7 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_AND_STATIC_SETTER,
CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD,
CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES,
CompileTimeErrorCode.CONFLICTING_INHERITED_METHOD_AND_SETTER,
CompileTimeErrorCode.CONFLICTING_METHOD_AND_FIELD,
CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE,
CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_CLASS,

View file

@ -45,6 +45,7 @@ import 'package:analyzer/src/error/use_result_verifier.dart';
import 'package:analyzer/src/generated/element_resolver.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/error_detection_helpers.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
import 'package:analyzer/src/generated/this_access_tracker.dart';
import 'package:analyzer/src/summary2/macro_application_error.dart';
@ -1993,36 +1994,39 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
/// [CompileTimeErrorCode.CONFLICTING_METHOD_AND_FIELD], and
/// [CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD].
void _checkForConflictingClassMembers() {
if (_enclosingClass == null) {
final enclosingClass = _enclosingClass;
if (enclosingClass == null) {
return;
}
Uri libraryUri = _currentLibrary.source.uri;
final conflictingDeclaredNames = <String>{};
// method declared in the enclosing class vs. inherited getter/setter
for (MethodElement method in _enclosingClass!.methods) {
for (MethodElement method in enclosingClass.methods) {
String name = method.name;
// find inherited property accessor
var inherited = _inheritanceManager.getInherited2(
_enclosingClass!, Name(libraryUri, name));
enclosingClass, Name(libraryUri, name));
inherited ??= _inheritanceManager.getInherited2(
_enclosingClass!, Name(libraryUri, '$name='));
enclosingClass, Name(libraryUri, '$name='));
if (method.isStatic && inherited != null) {
errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, method, [
_enclosingClass!.displayName,
enclosingClass.displayName,
name,
inherited.enclosingElement.displayName,
]);
} else if (inherited is PropertyAccessorElement) {
// Extension type methods redeclare getters with the same name.
if (_enclosingClass is ExtensionTypeElement && inherited.isGetter) {
if (enclosingClass is ExtensionTypeElement && inherited.isGetter) {
continue;
}
errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_METHOD_AND_FIELD, method, [
_enclosingClass!.displayName,
enclosingClass.displayName,
name,
inherited.enclosingElement.displayName
]);
@ -2030,33 +2034,88 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
}
// getter declared in the enclosing class vs. inherited method
for (PropertyAccessorElement accessor in _enclosingClass!.accessors) {
for (PropertyAccessorElement accessor in enclosingClass.accessors) {
String name = accessor.displayName;
// find inherited method or property accessor
var inherited = _inheritanceManager.getInherited2(
_enclosingClass!, Name(libraryUri, name));
enclosingClass, Name(libraryUri, name));
inherited ??= _inheritanceManager.getInherited2(
_enclosingClass!, Name(libraryUri, '$name='));
enclosingClass, Name(libraryUri, '$name='));
if (accessor.isStatic && inherited != null) {
errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, accessor, [
_enclosingClass!.displayName,
enclosingClass.displayName,
name,
inherited.enclosingElement.displayName,
]);
conflictingDeclaredNames.add(name);
} else if (inherited is MethodElement) {
// Extension type getters redeclare methods with the same name.
if (_enclosingClass is ExtensionTypeElement && accessor.isGetter) {
if (enclosingClass is ExtensionTypeElement && accessor.isGetter) {
continue;
}
errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD, accessor, [
_enclosingClass!.displayName,
enclosingClass.displayName,
name,
inherited.enclosingElement.displayName
]);
conflictingDeclaredNames.add(name);
}
}
// Inherited method and setter with the same name.
final inherited = _inheritanceManager.getInheritedMap2(enclosingClass);
for (final entry in inherited.entries) {
final method = entry.value;
if (method is MethodElement) {
final methodName = entry.key;
if (conflictingDeclaredNames.contains(methodName.name)) {
continue;
}
final setterName = methodName.forSetter;
final setter = inherited[setterName];
if (setter is PropertyAccessorElement) {
errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_INHERITED_METHOD_AND_SETTER,
enclosingClass,
[
enclosingClass.kind.displayName,
enclosingClass.displayName,
methodName.name,
],
[
DiagnosticMessageImpl(
filePath: method.source.fullName,
message: formatList(
"The method is inherited from the {0} '{1}'.",
[
method.enclosingElement.kind.displayName,
method.enclosingElement.name,
],
),
offset: method.nameOffset,
length: method.nameLength,
url: null,
),
DiagnosticMessageImpl(
filePath: setter.source.fullName,
message: formatList(
"The setter is inherited from the {0} '{1}'.",
[
setter.enclosingElement.kind.displayName,
setter.enclosingElement.name,
],
),
offset: setter.nameOffset,
length: setter.nameLength,
url: null,
),
],
);
}
}
}
}

View file

@ -2160,6 +2160,17 @@ CompileTimeErrorCode:
class B implements I<String> {}
class C extends A<String> implements B {}
```
CONFLICTING_INHERITED_METHOD_AND_SETTER:
problemMessage: "The {0} '{1}' can't inherit both a method and a setter named '{2}'."
comment: |-
10.11 Class Member Conflicts: Let `C` be a class. It is a compile-time
error if the interface of `C` has an instance method named `n` and an
instance setter with basename `n`.
Parameters:
0: the name of the enclosing element kind - class, extension type, etc
1: the name of the enclosing element
2: the name of the conflicting method / setter
CONFLICTING_METHOD_AND_FIELD:
problemMessage: "Class '{0}' can't define method '{1}' and have field '{2}.{1}' with the same name."
correctionMessage: "Try converting the method to a getter, or renaming the method to a name that doesn't conflict."

View file

@ -0,0 +1,136 @@
// Copyright (c) 2023, 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/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ConflictingInheritedMethodAndSetterTest);
});
}
@reflectiveTest
class ConflictingInheritedMethodAndSetterTest extends PubPackageResolutionTest {
test_class_declaresSetter() async {
await assertErrorsInCode(r'''
class A {
void foo() {}
}
class B {
set foo(int _) {}
}
abstract class C implements A, B {
set foo(int _) {}
}
''', [
error(CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD, 103, 3),
]);
}
test_class_interface2() async {
await assertErrorsInCode(r'''
class A {
void foo() {}
}
class B {
set foo(int _) {}
}
abstract class C implements A, B {}
''', [
error(CompileTimeErrorCode.CONFLICTING_INHERITED_METHOD_AND_SETTER, 77, 1,
contextMessages: [
message('/home/test/lib/test.dart', 17, 3),
message('/home/test/lib/test.dart', 45, 3)
]),
]);
}
test_class_mixin_interface() async {
await assertErrorsInCode(r'''
mixin A {
void foo() {}
}
class B {
set foo(int _) {}
}
abstract class C with A implements B {}
''', [
error(CompileTimeErrorCode.CONFLICTING_INHERITED_METHOD_AND_SETTER, 77, 1,
contextMessages: [
message('/home/test/lib/test.dart', 17, 3),
message('/home/test/lib/test.dart', 45, 3)
]),
]);
}
test_class_superclass_interface() async {
await assertErrorsInCode(r'''
class A {
void foo() {}
}
class B {
set foo(int _) {}
}
abstract class C extends A implements B {}
''', [
error(CompileTimeErrorCode.CONFLICTING_INHERITED_METHOD_AND_SETTER, 77, 1,
contextMessages: [
message('/home/test/lib/test.dart', 17, 3),
message('/home/test/lib/test.dart', 45, 3)
]),
]);
}
test_class_superclass_mixin() async {
await assertErrorsInCode(r'''
class A {
void foo() {}
}
mixin B {
set foo(int _) {}
}
abstract class C extends A with B {}
''', [
error(CompileTimeErrorCode.CONFLICTING_INHERITED_METHOD_AND_SETTER, 77, 1,
contextMessages: [
message('/home/test/lib/test.dart', 17, 3),
message('/home/test/lib/test.dart', 45, 3)
]),
]);
}
test_extensionType() async {
await assertErrorsInCode(r'''
extension type A(Object? it) {
void foo() {}
}
extension type B(Object? it) {
set foo(int _) {}
}
extension type C(Object? it) implements A, B {}
''', [
error(
CompileTimeErrorCode.CONFLICTING_INHERITED_METHOD_AND_SETTER, 119, 1,
contextMessages: [
message('/home/test/lib/test.dart', 38, 3),
message('/home/test/lib/test.dart', 87, 3)
]),
]);
}
}

View file

@ -85,6 +85,8 @@ import 'conflicting_constructor_and_static_method_test.dart'
import 'conflicting_field_and_method_test.dart' as conflicting_field_and_method;
import 'conflicting_generic_interfaces_test.dart'
as conflicting_generic_interfaces;
import 'conflicting_inherited_method_and_setter_test.dart'
as conflicting_inherited_method_and_setter;
import 'conflicting_method_and_field_test.dart' as conflicting_method_and_field;
import 'conflicting_static_and_instance_test.dart'
as conflicting_static_and_instance;
@ -975,6 +977,7 @@ main() {
conflicting_constructor_and_static_method.main();
conflicting_field_and_method.main();
conflicting_generic_interfaces.main();
conflicting_inherited_method_and_setter.main();
conflicting_method_and_field.main();
conflicting_static_and_instance.main();
conflicting_type_variable_and_container.main();