| // Copyright (c) 2020, 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/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/error/error.dart'; |
| import 'package:analyzer/error/listener.dart'; |
| import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; |
| import 'package:analyzer/src/dart/element/type_system.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| |
| /// Verifies that the return type of the getter matches the parameter type |
| /// of the corresponding setter. Where "match" means "subtype" in non-nullable, |
| /// and "assignable" in legacy. |
| class GetterSetterTypesVerifier { |
| final TypeSystemImpl _typeSystem; |
| final ErrorReporter _errorReporter; |
| |
| GetterSetterTypesVerifier({ |
| required TypeSystemImpl typeSystem, |
| required ErrorReporter errorReporter, |
| }) : _typeSystem = typeSystem, |
| _errorReporter = errorReporter; |
| |
| ErrorCode get _errorCode { |
| return _isNonNullableByDefault |
| ? CompileTimeErrorCode.GETTER_NOT_SUBTYPE_SETTER_TYPES |
| : CompileTimeErrorCode.GETTER_NOT_ASSIGNABLE_SETTER_TYPES; |
| } |
| |
| bool get _isNonNullableByDefault => _typeSystem.isNonNullableByDefault; |
| |
| void checkExtension(ExtensionElement element) { |
| for (var getter in element.accessors) { |
| if (getter.isGetter) { |
| _checkLocalGetter(getter); |
| } |
| } |
| } |
| |
| void checkInterface(ClassElement classElement, Interface interface) { |
| var libraryUri = classElement.library.source.uri; |
| |
| for (var name in interface.map.keys) { |
| if (!name.isAccessibleFor(libraryUri)) continue; |
| |
| var getter = interface.map[name]!; |
| if (getter.kind == ElementKind.GETTER) { |
| var setter = interface.map[Name(libraryUri, '${name.name}=')]; |
| if (setter != null && setter.parameters.length == 1) { |
| var getterType = getter.returnType; |
| var setterType = setter.parameters[0].type; |
| if (!_match(getterType, setterType)) { |
| Element errorElement; |
| if (getter.enclosingElement == classElement) { |
| errorElement = getter; |
| } else if (setter.enclosingElement == classElement) { |
| errorElement = setter; |
| } else { |
| errorElement = classElement; |
| } |
| |
| var getterName = getter.displayName; |
| if (getter.enclosingElement != classElement) { |
| var getterClassName = getter.enclosingElement.displayName; |
| getterName = '$getterClassName.$getterName'; |
| } |
| |
| var setterName = setter.displayName; |
| if (setter.enclosingElement != classElement) { |
| var setterClassName = setter.enclosingElement.displayName; |
| setterName = '$setterClassName.$setterName'; |
| } |
| |
| _errorReporter.reportErrorForElement( |
| _errorCode, |
| errorElement, |
| [getterName, getterType, setterType, setterName], |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| void checkStaticAccessors(List<PropertyAccessorElement> accessors) { |
| for (var getter in accessors) { |
| if (getter.isStatic && getter.isGetter) { |
| _checkLocalGetter(getter); |
| } |
| } |
| } |
| |
| void _checkLocalGetter(PropertyAccessorElement getter) { |
| assert(getter.isGetter); |
| var setter = getter.correspondingSetter; |
| if (setter != null) { |
| var getterType = _getGetterType(getter); |
| var setterType = _getSetterType(setter); |
| if (setterType != null) { |
| if (!_match(getterType, setterType)) { |
| var name = getter.name; |
| _errorReporter.reportErrorForElement( |
| _errorCode, |
| getter, |
| [name, getterType, setterType, name], |
| ); |
| } |
| } |
| } |
| } |
| |
| bool _match(DartType getterType, DartType setterType) { |
| return _isNonNullableByDefault |
| ? _typeSystem.isSubtypeOf(getterType, setterType) |
| : _typeSystem.isAssignableTo(getterType, setterType); |
| } |
| |
| /// Return the return type of the [getter]. |
| static DartType _getGetterType(PropertyAccessorElement getter) { |
| return getter.returnType; |
| } |
| |
| /// Return the type of the first parameter of the [setter]. |
| static DartType? _getSetterType(PropertyAccessorElement setter) { |
| var parameters = setter.parameters; |
| if (parameters.isNotEmpty) { |
| return parameters[0].type; |
| } else { |
| return null; |
| } |
| } |
| } |