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:
Konstantin Shcheglov 2018-10-10 16:25:49 +00:00 committed by commit-bot@chromium.org
parent be39eacf00
commit e221a6288b
7 changed files with 213 additions and 120 deletions

View file

@ -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,

View file

@ -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.");
/**

View file

@ -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],

View file

@ -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]);
}
}
}

View file

@ -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; }

View file

@ -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 {

View file

@ -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.