mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:31:58 +00:00
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:
parent
abf89a6803
commit
335aec593f
|
@ -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: |-
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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)
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue