mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 13:28:03 +00:00
Move reporting MISMATCHED_GETTER_AND_SETTER_TYPES for classes to using new interfaces.
R=brianwilkerson@google.com Change-Id: I07e8af54ffebd446a1492fab667e4148468cc2f8 Reviewed-on: https://dart-review.googlesource.com/c/78925 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
be39eacf00
commit
e221a6288b
|
@ -599,7 +599,6 @@ const List<ErrorCode> errorCodeValues = const [
|
|||
StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE,
|
||||
StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE,
|
||||
StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES,
|
||||
StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE,
|
||||
StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH,
|
||||
StaticWarningCode.MIXED_RETURN_TYPES,
|
||||
StaticWarningCode.NEW_WITH_ABSTRACT_CLASS,
|
||||
|
|
|
@ -3978,28 +3978,21 @@ class StaticWarningCode extends ErrorCode {
|
|||
"The element type '{0}' can't be assigned to the map value type '{1}'.");
|
||||
|
||||
/**
|
||||
* 7.3 Setters: It is a static warning if a class has a setter named <i>v=</i>
|
||||
* with argument type <i>T</i> and a getter named <i>v</i> with return type
|
||||
* <i>S</i>, and <i>T</i> may not be assigned to <i>S</i>.
|
||||
* 10.3 Setters: It is a compile-time error if a class has a setter named
|
||||
* `v=` with argument type `T` and a getter named `v` with return type `S`,
|
||||
* and `S` may not be assigned to `T`.
|
||||
*
|
||||
* Parameters:
|
||||
* 0: the name of the getter
|
||||
* 1: the type of the getter
|
||||
* 2: the type of the setter
|
||||
* 3: the name of the setter
|
||||
*/
|
||||
static const StaticWarningCode MISMATCHED_GETTER_AND_SETTER_TYPES =
|
||||
const StaticWarningCode(
|
||||
'MISMATCHED_GETTER_AND_SETTER_TYPES',
|
||||
"The parameter type for setter '{0}' is '{1}' which isn't assignable "
|
||||
"to its getter (of type '{2}').",
|
||||
correction: "Try changing the types so that they are compatible.");
|
||||
|
||||
/**
|
||||
* 7.3 Setters: It is a static warning if a class has a setter named <i>v=</i>
|
||||
* with argument type <i>T</i> and a getter named <i>v</i> with return type
|
||||
* <i>S</i>, and <i>T</i> may not be assigned to <i>S</i>.
|
||||
*/
|
||||
static const StaticWarningCode
|
||||
MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE =
|
||||
const StaticWarningCode(
|
||||
'MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE',
|
||||
"The parameter type for setter '{0}' is '{1}' which isn't assignable "
|
||||
"to its getter (of type '{2}'), from superclass '{3}'.",
|
||||
"The return type of getter '{0}' is '{1}' which isn't assignable "
|
||||
"to the type '{2}' of its setter '{3}'.",
|
||||
correction: "Try changing the types so that they are compatible.");
|
||||
|
||||
/**
|
||||
|
|
|
@ -84,6 +84,7 @@ class _ClassVerifier {
|
|||
final ErrorReporter reporter;
|
||||
|
||||
final LibraryElement library;
|
||||
final Uri libraryUri;
|
||||
final ClassElementImpl classElement;
|
||||
|
||||
final SimpleIdentifier classNameNode;
|
||||
|
@ -108,7 +109,8 @@ class _ClassVerifier {
|
|||
this.onClause,
|
||||
this.superclass,
|
||||
this.withClause,
|
||||
}) : classElement =
|
||||
}) : libraryUri = library.source.uri,
|
||||
classElement =
|
||||
AbstractClassElementImpl.getImpl(classNameNode.staticElement);
|
||||
|
||||
void verify() {
|
||||
|
@ -147,7 +149,6 @@ class _ClassVerifier {
|
|||
|
||||
// Check the members if the class itself, against all the previously
|
||||
// collected superinterfaces of the supertype, mixins, and interfaces.
|
||||
var libraryUri = library.source.uri;
|
||||
for (var member in members) {
|
||||
if (member is FieldDeclaration) {
|
||||
var fieldList = member.fields;
|
||||
|
@ -169,6 +170,8 @@ class _ClassVerifier {
|
|||
_reportInconsistentInheritance(classNameNode, conflict);
|
||||
}
|
||||
|
||||
_checkForMismatchedAccessorTypes(interfaceMembers);
|
||||
|
||||
if (!classElement.isAbstract) {
|
||||
List<ExecutableElement> inheritedAbstractMembers = null;
|
||||
|
||||
|
@ -341,6 +344,52 @@ class _ClassVerifier {
|
|||
return hasError;
|
||||
}
|
||||
|
||||
void _checkForMismatchedAccessorTypes(Interface interface) {
|
||||
for (var name in interface.map.keys) {
|
||||
if (!name.isAccessibleFor(libraryUri)) continue;
|
||||
|
||||
var getter = interface.map[name];
|
||||
if (getter.element.kind == ElementKind.GETTER) {
|
||||
// TODO(scheglov) We should separate getters and setters.
|
||||
var setter = interface.map[new Name(libraryUri, '${name.name}=')];
|
||||
if (setter != null) {
|
||||
var getterType = getter.returnType;
|
||||
var setterType = setter.parameters[0].type;
|
||||
if (!typeSystem.isAssignableTo(getterType, setterType)) {
|
||||
var getterElement = getter.element;
|
||||
var setterElement = setter.element;
|
||||
|
||||
Element errorElement;
|
||||
if (getterElement.enclosingElement == classElement) {
|
||||
errorElement = getterElement;
|
||||
} else if (setterElement.enclosingElement == classElement) {
|
||||
errorElement = setterElement;
|
||||
} else {
|
||||
errorElement = classElement;
|
||||
}
|
||||
|
||||
String getterName = getterElement.displayName;
|
||||
if (getterElement.enclosingElement != classElement) {
|
||||
var getterClassName = getterElement.enclosingElement.displayName;
|
||||
getterName = '$getterClassName.$getterName';
|
||||
}
|
||||
|
||||
String setterName = setterElement.displayName;
|
||||
if (setterElement.enclosingElement != classElement) {
|
||||
var setterClassName = setterElement.enclosingElement.displayName;
|
||||
setterName = '$setterClassName.$setterName';
|
||||
}
|
||||
|
||||
reporter.reportErrorForElement(
|
||||
StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES,
|
||||
errorElement,
|
||||
[getterName, getterType, setterType, setterName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// We identified that the current non-abstract class does not have the
|
||||
/// concrete implementation of a method with the given [name]. If this is
|
||||
/// because the class itself defines an abstract method with this [name],
|
||||
|
|
|
@ -976,15 +976,7 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
|
|||
try {
|
||||
_isInStaticMethod = node.isStatic;
|
||||
_enclosingFunction = node.declaredElement;
|
||||
SimpleIdentifier identifier = node.name;
|
||||
String methodName = "";
|
||||
if (identifier != null) {
|
||||
methodName = identifier.name;
|
||||
}
|
||||
TypeAnnotation returnType = node.returnType;
|
||||
if (node.isSetter || node.isGetter) {
|
||||
_checkForMismatchedAccessorTypes(node, methodName);
|
||||
}
|
||||
if (node.isSetter) {
|
||||
_checkForInvalidModifierOnBody(
|
||||
node.body, CompileTimeErrorCode.INVALID_MODIFIER_ON_SETTER);
|
||||
|
@ -3920,8 +3912,7 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
|
|||
* Check to make sure that all similarly typed accessors are of the same type
|
||||
* (including inherited accessors).
|
||||
*
|
||||
* See [StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES], and
|
||||
* [StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE].
|
||||
* See [StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES].
|
||||
*/
|
||||
void _checkForMismatchedAccessorTypes(
|
||||
Declaration accessorDeclaration, String accessorTextName) {
|
||||
|
@ -3929,7 +3920,6 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
|
|||
accessorDeclaration.declaredElement as ExecutableElement;
|
||||
if (accessorElement is PropertyAccessorElement) {
|
||||
PropertyAccessorElement counterpartAccessor = null;
|
||||
ClassElement enclosingClassForCounterpart = null;
|
||||
if (accessorElement.isGetter) {
|
||||
counterpartAccessor = accessorElement.correspondingSetter;
|
||||
} else {
|
||||
|
@ -3943,31 +3933,7 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
|
|||
}
|
||||
}
|
||||
if (counterpartAccessor == null) {
|
||||
// If the accessor is declared in a class, check the superclasses.
|
||||
if (_enclosingClass != null) {
|
||||
// Figure out the correct identifier to lookup in the inheritance graph,
|
||||
// if 'x', then 'x=', or if 'x=', then 'x'.
|
||||
String lookupIdentifier = accessorElement.name;
|
||||
if (StringUtilities.endsWithChar(lookupIdentifier, 0x3D)) {
|
||||
lookupIdentifier =
|
||||
lookupIdentifier.substring(0, lookupIdentifier.length - 1);
|
||||
} else {
|
||||
lookupIdentifier += "=";
|
||||
}
|
||||
// lookup with the identifier.
|
||||
ExecutableElement elementFromInheritance = _inheritanceManager
|
||||
.lookupInheritance(_enclosingClass, lookupIdentifier);
|
||||
// Verify that we found something, and that it is an accessor
|
||||
if (elementFromInheritance != null &&
|
||||
elementFromInheritance is PropertyAccessorElement) {
|
||||
enclosingClassForCounterpart =
|
||||
elementFromInheritance.enclosingElement as ClassElement;
|
||||
counterpartAccessor = elementFromInheritance;
|
||||
}
|
||||
}
|
||||
if (counterpartAccessor == null) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Default of null == no accessor or no type (dynamic)
|
||||
DartType getterType = null;
|
||||
|
@ -3985,23 +3951,10 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
|
|||
if (setterType != null &&
|
||||
getterType != null &&
|
||||
!_typeSystem.isAssignableTo(getterType, setterType)) {
|
||||
if (enclosingClassForCounterpart == null) {
|
||||
_errorReporter.reportTypeErrorForNode(
|
||||
StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES,
|
||||
accessorDeclaration,
|
||||
[accessorTextName, setterType, getterType]);
|
||||
} else {
|
||||
_errorReporter.reportTypeErrorForNode(
|
||||
StaticWarningCode
|
||||
.MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE,
|
||||
accessorDeclaration,
|
||||
[
|
||||
accessorTextName,
|
||||
setterType,
|
||||
getterType,
|
||||
enclosingClassForCounterpart.displayName
|
||||
]);
|
||||
}
|
||||
_errorReporter.reportTypeErrorForNode(
|
||||
StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES,
|
||||
accessorDeclaration,
|
||||
[accessorTextName, getterType, setterType, accessorTextName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2615,46 +2615,6 @@ class B implements I<int>, J<String> {
|
|||
verify([source]);
|
||||
}
|
||||
|
||||
test_mismatchedAccessorTypes_class() async {
|
||||
Source source = addSource(r'''
|
||||
class A {
|
||||
int get g { return 0; }
|
||||
set g(String v) {}
|
||||
}''');
|
||||
await computeAnalysisResult(source);
|
||||
assertErrors(
|
||||
source, [StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES]);
|
||||
verify([source]);
|
||||
}
|
||||
|
||||
test_mismatchedAccessorTypes_getterAndSuperSetter() async {
|
||||
Source source = addSource(r'''
|
||||
class A {
|
||||
int get g { return 0; }
|
||||
}
|
||||
class B extends A {
|
||||
set g(String v) {}
|
||||
}''');
|
||||
await computeAnalysisResult(source);
|
||||
assertErrors(source,
|
||||
[StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE]);
|
||||
verify([source]);
|
||||
}
|
||||
|
||||
test_mismatchedAccessorTypes_setterAndSuperGetter() async {
|
||||
Source source = addSource(r'''
|
||||
class A {
|
||||
set g(int v) {}
|
||||
}
|
||||
class B extends A {
|
||||
String get g { return ''; }
|
||||
}''');
|
||||
await computeAnalysisResult(source);
|
||||
assertErrors(source,
|
||||
[StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE]);
|
||||
verify([source]);
|
||||
}
|
||||
|
||||
test_mismatchedAccessorTypes_topLevel() async {
|
||||
Source source = addSource(r'''
|
||||
int get g { return 0; }
|
||||
|
|
|
@ -1291,6 +1291,145 @@ class C {
|
|||
expect(method.isStatic, isTrue);
|
||||
}
|
||||
|
||||
test_error_mismatchedGetterAndSetterTypes_class() async {
|
||||
addTestFile(r'''
|
||||
class C {
|
||||
int get foo => 0;
|
||||
set foo(String _) {}
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertTestErrors([
|
||||
StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES,
|
||||
]);
|
||||
}
|
||||
|
||||
test_error_mismatchedGetterAndSetterTypes_interfaces() async {
|
||||
addTestFile(r'''
|
||||
class A {
|
||||
int get foo {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
set foo(String _) {}
|
||||
}
|
||||
|
||||
abstract class X implements A, B {}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertTestErrors([
|
||||
StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES,
|
||||
]);
|
||||
}
|
||||
|
||||
test_error_mismatchedGetterAndSetterTypes_OK_private_getter() async {
|
||||
newFile('/test/lib/a.dart', content: r'''
|
||||
class A {
|
||||
int get _foo => 0;
|
||||
}
|
||||
''');
|
||||
addTestFile(r'''
|
||||
import 'a.dart';
|
||||
|
||||
class B extends A {
|
||||
set _foo(String _) {}
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertNoTestErrors();
|
||||
}
|
||||
|
||||
test_error_mismatchedGetterAndSetterTypes_OK_private_interfaces() async {
|
||||
newFile('/test/lib/a.dart', content: r'''
|
||||
class A {
|
||||
int get _foo => 0;
|
||||
}
|
||||
''');
|
||||
newFile('/test/lib/b.dart', content: r'''
|
||||
class B {
|
||||
set _foo(String _) {}
|
||||
}
|
||||
''');
|
||||
addTestFile(r'''
|
||||
import 'a.dart';
|
||||
import 'b.dart';
|
||||
|
||||
class X implements A, B {}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertNoTestErrors();
|
||||
}
|
||||
|
||||
test_error_mismatchedGetterAndSetterTypes_OK_private_interfaces2() async {
|
||||
newFile('/test/lib/a.dart', content: r'''
|
||||
class A {
|
||||
int get _foo => 0;
|
||||
}
|
||||
|
||||
class B {
|
||||
set _foo(String _) {}
|
||||
}
|
||||
''');
|
||||
addTestFile(r'''
|
||||
import 'a.dart';
|
||||
|
||||
class X implements A, B {}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertNoTestErrors();
|
||||
}
|
||||
|
||||
test_error_mismatchedGetterAndSetterTypes_OK_private_setter() async {
|
||||
newFile('/test/lib/a.dart', content: r'''
|
||||
class A {
|
||||
set _foo(String _) {}
|
||||
}
|
||||
''');
|
||||
addTestFile(r'''
|
||||
import 'a.dart';
|
||||
|
||||
class B extends A {
|
||||
int get _foo => 0;
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertNoTestErrors();
|
||||
}
|
||||
|
||||
test_error_mismatchedGetterAndSetterTypes_superGetter() async {
|
||||
addTestFile(r'''
|
||||
class A {
|
||||
int get foo => 0;
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
set foo(String _) {}
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertTestErrors([
|
||||
StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES,
|
||||
]);
|
||||
}
|
||||
|
||||
test_error_mismatchedGetterAndSetterTypes_superSetter() async {
|
||||
addTestFile(r'''
|
||||
class A {
|
||||
set foo(String _) {}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
int get foo => 0;
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertTestErrors([
|
||||
StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES,
|
||||
]);
|
||||
}
|
||||
|
||||
test_inconsistentInheritance_parameterType() async {
|
||||
addTestFile(r'''
|
||||
abstract class A {
|
||||
|
|
|
@ -2644,17 +2644,17 @@ class Base {
|
|||
}
|
||||
|
||||
class T1 extends Base {
|
||||
/*error:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE,error:INVALID_OVERRIDE*/B get f => null;
|
||||
/*error:INVALID_OVERRIDE*/B get /*error:MISMATCHED_GETTER_AND_SETTER_TYPES*/f => null;
|
||||
}
|
||||
|
||||
class T2 extends Base {
|
||||
/*error:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE,error:INVALID_OVERRIDE*/set f(
|
||||
/*error:INVALID_OVERRIDE*/set /*error:MISMATCHED_GETTER_AND_SETTER_TYPES*/f(
|
||||
B b) => null;
|
||||
}
|
||||
|
||||
class T3 extends Base {
|
||||
/*error:INVALID_OVERRIDE*/final B
|
||||
/*error:FINAL_NOT_INITIALIZED*/f;
|
||||
/*error:FINAL_NOT_INITIALIZED, error:MISMATCHED_GETTER_AND_SETTER_TYPES*/f;
|
||||
}
|
||||
class T4 extends Base {
|
||||
// two: one for the getter one for the setter.
|
||||
|
@ -2662,15 +2662,15 @@ class T4 extends Base {
|
|||
}
|
||||
|
||||
class /*error:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/T5 implements Base {
|
||||
/*error:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE, error:INVALID_OVERRIDE*/B get f => null;
|
||||
/*error:INVALID_OVERRIDE*/B get /*error:MISMATCHED_GETTER_AND_SETTER_TYPES*/f => null;
|
||||
}
|
||||
|
||||
class /*error:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/T6 implements Base {
|
||||
/*error:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE, error:INVALID_OVERRIDE*/set f(B b) => null;
|
||||
/*error:INVALID_OVERRIDE*/set /*error:MISMATCHED_GETTER_AND_SETTER_TYPES*/f(B b) => null;
|
||||
}
|
||||
|
||||
class /*error:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/T7 implements Base {
|
||||
/*error:INVALID_OVERRIDE*/final B f = null;
|
||||
/*error:INVALID_OVERRIDE*/final B /*error:MISMATCHED_GETTER_AND_SETTER_TYPES*/f = null;
|
||||
}
|
||||
class T8 implements Base {
|
||||
// two: one for the getter one for the setter.
|
||||
|
|
Loading…
Reference in a new issue