Change method override inference to use combined member signature.

https://github.com/dart-lang/language/pull/975

Change-Id: I5cec14a384b124a29dcd5adac0f0be6e5214cc9e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151044
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Konstantin Shcheglov 2020-06-12 18:59:28 +00:00 committed by commit-bot@chromium.org
parent 43131b575e
commit 24852421e2
7 changed files with 1332 additions and 537 deletions

View file

@ -66,7 +66,8 @@ class C1 extends A implements B {
/*cfe|cfe:builder.member: C2._simpleInstanceOfTrue:bool* Function(dynamic)**/
/*cfe|cfe:builder.member: C2.==:bool* Function(dynamic)**/
class C2 extends B implements A {
/*member: C2.method:dynamic Function(dynamic, {dynamic named})**/
/*cfe|cfe:builder.member: C2.method:dynamic Function(dynamic, {dynamic named})**/
/*analyzer.member: C2.method:Object* Function(Object*, {Object* named})**/
method(o, {named}) {}
}
@ -98,7 +99,8 @@ class C3 implements A, B {
/*cfe|cfe:builder.member: C4._simpleInstanceOfTrue:bool* Function(dynamic)**/
/*cfe|cfe:builder.member: C4.==:bool* Function(dynamic)**/
class C4 implements B, A {
/*member: C4.method:dynamic Function(dynamic, {dynamic named})**/
/*cfe|cfe:builder.member: C4.method:dynamic Function(dynamic, {dynamic named})**/
/*analyzer.member: C4.method:Object* Function(Object*, {Object* named})**/
method(o, {named}) {}
}

View file

@ -54,6 +54,66 @@ class InheritanceManager3 {
/// self-referencing cycles.
final Set<ClassElement> _processingClasses = <ClassElement>{};
/// Combine [candidates] into a single signature in the [targetClass].
///
/// If such signature does not exist, return `null`, and if [conflicts] is
/// not `null`, add a new [Conflict] to it.
ExecutableElement combineSignatures({
@required ClassElement targetClass,
@required List<ExecutableElement> candidates,
@required bool doTopMerge,
@required Name name,
List<Conflict> conflicts,
}) {
// If just one candidate, it is always valid.
if (candidates.length == 1) {
return candidates[0];
}
// Check for a getter/method conflict.
var conflict = _checkForGetterMethodConflict(name, candidates);
if (conflict != null) {
conflicts?.add(conflict);
return null;
}
var validOverrides = <ExecutableElement>[];
for (var i = 0; i < candidates.length; i++) {
var validOverride = candidates[i];
var overrideHelper = CorrectOverrideHelper(
library: targetClass.library,
thisMember: validOverride,
);
for (var j = 0; j < candidates.length; j++) {
var candidate = candidates[j];
if (!overrideHelper.isCorrectOverrideOf(superMember: candidate)) {
validOverride = null;
break;
}
}
if (validOverride != null) {
validOverrides.add(validOverride);
}
}
if (validOverrides.isEmpty) {
conflicts?.add(
CandidatesConflict(
name: name,
candidates: candidates,
),
);
return null;
}
if (doTopMerge) {
var typeSystem = targetClass.library.typeSystem;
return _topMerge(typeSystem, targetClass, validOverrides);
} else {
return validOverrides.first;
}
}
/// Return the result of [getInherited2] with [type] substitution.
ExecutableElement getInherited(InterfaceType type, Name name) {
var rawElement = getInherited2(type.element, name);
@ -369,9 +429,7 @@ class InheritanceManager3 {
Map<Name, List<ExecutableElement>> namedCandidates, {
@required bool doTopMerge,
}) {
TypeSystemImpl typeSystem = targetClass.library.typeSystem;
List<Conflict> conflicts;
var conflicts = <Conflict>[];
for (var name in namedCandidates.keys) {
if (map.containsKey(name)) {
@ -380,54 +438,18 @@ class InheritanceManager3 {
var candidates = namedCandidates[name];
// If just one candidate, it is always valid.
if (candidates.length == 1) {
map[name] = candidates[0];
var combinedSignature = combineSignatures(
targetClass: targetClass,
candidates: candidates,
doTopMerge: doTopMerge,
name: name,
conflicts: conflicts,
);
if (combinedSignature != null) {
map[name] = combinedSignature;
continue;
}
// Check for a getter/method conflict.
var conflict = _checkForGetterMethodConflict(name, candidates);
if (conflict != null) {
conflicts ??= <Conflict>[];
conflicts.add(conflict);
}
var validOverrides = <ExecutableElement>[];
for (var i = 0; i < candidates.length; i++) {
var validOverride = candidates[i];
var overrideHelper = CorrectOverrideHelper(
library: targetClass.library,
thisMember: validOverride,
);
for (var j = 0; j < candidates.length; j++) {
var candidate = candidates[j];
if (!overrideHelper.isCorrectOverrideOf(superMember: candidate)) {
validOverride = null;
break;
}
}
if (validOverride != null) {
validOverrides.add(validOverride);
}
}
if (validOverrides.isEmpty) {
conflicts ??= <Conflict>[];
conflicts.add(
CandidatesConflict(
name: name,
candidates: candidates,
),
);
continue;
}
if (doTopMerge) {
map[name] = _topMerge(typeSystem, targetClass, validOverrides);
} else {
map[name] = validOverrides.first;
}
}
return conflicts;

View file

@ -60,134 +60,6 @@ class InstanceMemberInferrer {
return true;
}
/**
* Compute the inferred type for the given property [accessor]. The returned
* value is never `null`, but might be an error, and/or have the `null` type.
*/
_FieldOverrideInferenceResult _computeFieldOverrideType(
PropertyAccessorElement accessor) {
String name = accessor.displayName;
var overriddenGetters = inheritance.getOverridden2(
currentClassElement,
Name(accessor.library.source.uri, name),
);
List<ExecutableElement> overriddenSetters;
if (overriddenGetters == null || !accessor.variable.isFinal) {
overriddenSetters = inheritance.getOverridden2(
currentClassElement,
Name(accessor.library.source.uri, '$name='),
);
}
// Choose overridden members from getters or/and setters.
List<ExecutableElement> overriddenElements = <ExecutableElement>[];
if (overriddenGetters == null && overriddenSetters == null) {
overriddenElements = const <ExecutableElement>[];
} else if (overriddenGetters == null && overriddenSetters != null) {
overriddenElements = overriddenSetters;
} else if (overriddenGetters != null && overriddenSetters == null) {
overriddenElements = overriddenGetters;
} else {
overriddenElements = <ExecutableElement>[
...overriddenGetters,
...overriddenSetters,
];
}
bool isCovariant = false;
DartType impliedType;
for (ExecutableElement overriddenElement in overriddenElements) {
var overriddenElementKind = overriddenElement.kind;
if (overriddenElement == null) {
return _FieldOverrideInferenceResult(false, null, true);
}
DartType type;
if (overriddenElementKind == ElementKind.GETTER) {
type = overriddenElement.returnType;
} else if (overriddenElementKind == ElementKind.SETTER) {
if (overriddenElement.parameters.length == 1) {
ParameterElement parameter = overriddenElement.parameters[0];
type = parameter.type;
isCovariant = isCovariant || parameter.isCovariant;
}
} else {
return _FieldOverrideInferenceResult(false, null, true);
}
if (impliedType == null) {
impliedType = type;
} else if (type != impliedType) {
return _FieldOverrideInferenceResult(false, null, true);
}
}
return _FieldOverrideInferenceResult(isCovariant, impliedType, false);
}
/**
* Compute the best type for the [parameter] at the given [index] that must be
* compatible with the types of the corresponding parameters of the given
* [overriddenTypes].
*
* At the moment, this method will only return a type other than 'dynamic' if
* the types of all of the parameters are the same. In the future we might
* want to be smarter about it, such as by returning the least upper bound of
* the parameter types.
*/
DartType _computeParameterType(ParameterElement parameter, int index,
List<FunctionType> overriddenTypes) {
var typesMerger = _OverriddenTypesMerger(typeSystem);
for (var overriddenType in overriddenTypes) {
ParameterElement matchingParameter = _getCorrespondingParameter(
parameter,
index,
overriddenType.parameters,
);
DartType type = matchingParameter?.type ?? _dynamicType;
typesMerger.update(type);
if (typesMerger.hasError) {
if (parameter is ParameterElementImpl && parameter.linkedNode != null) {
LazyAst.setTypeInferenceError(
parameter.linkedNode,
TopLevelInferenceErrorBuilder(
kind: TopLevelInferenceErrorKind.overrideConflictParameterType,
),
);
}
return _dynamicType;
}
}
return typesMerger.result ?? _dynamicType;
}
/**
* Compute the best return type for a method that must be compatible with the
* return types of each of the given [overriddenReturnTypes].
*
* At the moment, this method will only return a type other than 'dynamic' if
* the return types of all of the methods are the same. In the future we might
* want to be smarter about it.
*/
DartType _computeReturnType(Iterable<DartType> overriddenReturnTypes) {
var typesMerger = _OverriddenTypesMerger(typeSystem);
for (DartType type in overriddenReturnTypes) {
type ??= _dynamicType;
typesMerger.update(type);
if (typesMerger.hasError) {
return _dynamicType;
}
}
return typesMerger.result ?? _dynamicType;
}
/**
* Given a method, return the parameter in the method that corresponds to the
* given [parameter]. If the parameter is positional, then
@ -223,37 +95,267 @@ class InstanceMemberInferrer {
}
/**
* If the given [element] represents a non-synthetic instance property
* If the given [accessor] represents a non-synthetic instance property
* accessor for which no type was provided, infer its types.
*
* If the given [field] represents a non-synthetic instance field for
* which no type was provided, infer the type of the field.
*/
void _inferAccessor(PropertyAccessorElement element) {
if (element.isSynthetic || element.isStatic) {
return;
}
void _inferAccessorOrField({
PropertyAccessorElementImpl accessor,
FieldElementImpl field,
}) {
Uri elementLibraryUri;
String elementName;
if (element.kind == ElementKind.GETTER && !element.hasImplicitReturnType) {
return;
}
_FieldOverrideInferenceResult typeResult =
_computeFieldOverrideType(element);
if (typeResult.isError == null || typeResult.type == null) {
return;
}
if (element.kind == ElementKind.GETTER) {
(element as ExecutableElementImpl).returnType = typeResult.type;
} else if (element.kind == ElementKind.SETTER) {
List<ParameterElement> parameters = element.parameters;
if (parameters.isNotEmpty) {
var parameter = parameters[0] as ParameterElementImpl;
if (parameter.hasImplicitType) {
parameter.type = typeResult.type;
}
parameter.inheritsCovariant = typeResult.isCovariant;
if (accessor != null) {
if (accessor.isSynthetic || accessor.isStatic) {
return;
}
elementLibraryUri = accessor.library.source.uri;
elementName = accessor.displayName;
}
if (field != null) {
if (field.isSynthetic || field.isStatic) {
return;
}
elementLibraryUri = field.library.source.uri;
elementName = field.name;
}
var getterName = Name(elementLibraryUri, elementName);
var overriddenGetters = inheritance.getOverridden2(
currentClassElement,
getterName,
);
overriddenGetters ??= const [];
var setterName = Name(elementLibraryUri, '$elementName=');
var overriddenSetters = inheritance.getOverridden2(
currentClassElement,
setterName,
);
overriddenSetters ??= const [];
if (accessor != null && accessor.isGetter) {
if (!accessor.hasImplicitReturnType) {
return;
}
// The return type of a getter, parameter type of a setter or type of a
// field which overrides/implements only one or more getters is inferred
// to be the return type of the combined member signature of said getter
// in the direct superinterfaces.
//
// The return type of a getter which overrides/implements both a setter
// and a getter is inferred to be the return type of the combined member
// signature of said getter in the direct superinterfaces.
if (overriddenGetters.isNotEmpty) {
var combinedGetter = inheritance.combineSignatures(
targetClass: currentClassElement,
candidates: overriddenGetters,
doTopMerge: true,
name: getterName,
);
if (combinedGetter != null) {
accessor.returnType = combinedGetter.returnType;
}
}
// The return type of a getter, parameter type of a setter or type of
// field which overrides/implements only one or more setters is inferred
// to be the parameter type of the combined member signature of said
// setter in the direct superinterfaces.
if (overriddenGetters.isEmpty && overriddenSetters.isNotEmpty) {
var combinedSetter = inheritance.combineSignatures(
targetClass: currentClassElement,
candidates: overriddenSetters,
doTopMerge: true,
name: setterName,
);
if (combinedSetter != null) {
accessor.returnType = combinedSetter.parameters[0].type;
}
}
return;
}
if (accessor != null && accessor.isSetter) {
var parameters = accessor.parameters;
if (parameters.isEmpty) {
return;
}
var parameter = parameters[0] as ParameterElementImpl;
if (overriddenSetters.any(_isCovariantSetter)) {
parameter.inheritsCovariant = true;
}
if (!parameter.hasImplicitType) {
return;
}
// The return type of a getter, parameter type of a setter or type of a
// field which overrides/implements only one or more getters is inferred
// to be the return type of the combined member signature of said getter
// in the direct superinterfaces.
if (overriddenGetters.isNotEmpty && overriddenSetters.isEmpty) {
var combinedGetter = inheritance.combineSignatures(
targetClass: currentClassElement,
candidates: overriddenGetters,
doTopMerge: true,
name: getterName,
);
if (combinedGetter != null) {
parameter.type = combinedGetter.returnType;
}
return;
}
// The return type of a getter, parameter type of a setter or type of
// field which overrides/implements only one or more setters is inferred
// to be the parameter type of the combined member signature of said
// setter in the direct superinterfaces.
//
// The parameter type of a setter which overrides/implements both a
// setter and a getter is inferred to be the parameter type of the
// combined member signature of said setter in the direct superinterfaces.
if (overriddenSetters.isNotEmpty) {
var combinedSetter = inheritance.combineSignatures(
targetClass: currentClassElement,
candidates: overriddenSetters,
doTopMerge: true,
name: setterName,
);
if (combinedSetter != null) {
parameter.type = combinedSetter.parameters[0].type;
}
return;
}
return;
}
if (field != null) {
if (field.setter != null) {
if (overriddenSetters.any(_isCovariantSetter)) {
var parameter = field.setter.parameters[0] as ParameterElementImpl;
parameter.inheritsCovariant = true;
}
}
if (!field.hasImplicitType) {
return;
}
// The return type of a getter, parameter type of a setter or type of a
// field which overrides/implements only one or more getters is inferred
// to be the return type of the combined member signature of said getter
// in the direct superinterfaces.
if (overriddenGetters.isNotEmpty && overriddenSetters.isEmpty) {
var combinedGetter = inheritance.combineSignatures(
targetClass: currentClassElement,
candidates: overriddenGetters,
doTopMerge: true,
name: getterName,
);
if (combinedGetter != null) {
field.type = combinedGetter.returnType;
}
return;
}
// The return type of a getter, parameter type of a setter or type of
// field which overrides/implements only one or more setters is inferred
// to be the parameter type of the combined member signature of said
// setter in the direct superinterfaces.
if (overriddenGetters.isEmpty && overriddenSetters.isNotEmpty) {
var combinedSetter = inheritance.combineSignatures(
targetClass: currentClassElement,
candidates: overriddenSetters,
doTopMerge: true,
name: setterName,
);
if (combinedSetter != null) {
field.type = combinedSetter.parameters[0].type;
}
return;
}
if (overriddenGetters.isNotEmpty && overriddenSetters.isNotEmpty) {
// The type of a final field which overrides/implements both a setter
// and a getter is inferred to be the return type of the combined
// member signature of said getter in the direct superinterfaces.
if (field.isFinal) {
var combinedGetter = inheritance.combineSignatures(
targetClass: currentClassElement,
candidates: overriddenGetters,
doTopMerge: true,
name: getterName,
);
if (combinedGetter != null) {
field.type = combinedGetter.returnType;
}
return;
}
// The type of a non-final field which overrides/implements both a
// setter and a getter is inferred to be the parameter type of the
// combined member signature of said setter in the direct
// superinterfaces, if this type is the same as the return type of the
// combined member signature of said getter in the direct
// superinterfaces. If the types are not the same then inference
// fails with an error.
if (!field.isFinal) {
var combinedGetter = inheritance.combineSignatures(
targetClass: currentClassElement,
candidates: overriddenGetters,
doTopMerge: true,
name: getterName,
);
var getterType = combinedGetter?.returnType;
var combinedSetter = inheritance.combineSignatures(
targetClass: currentClassElement,
candidates: overriddenSetters,
doTopMerge: true,
name: setterName,
);
DartType setterType;
if (combinedSetter != null) {
setterType = combinedSetter.parameters[0].type;
}
if (getterType == setterType) {
field.type = getterType ?? _dynamicType;
} else {
LazyAst.setTypeInferenceError(
field.linkedNode,
TopLevelInferenceErrorBuilder(
kind: TopLevelInferenceErrorKind.overrideConflictFieldType,
),
);
}
return;
}
}
// Otherwise, declarations of static variables and fields that omit a
// type will be inferred from their initializer if present.
var initializer = field.initializer;
if (initializer != null) {
var initializerType = initializer.returnType;
if (initializerType == null || initializerType.isDartCoreNull) {
initializerType = _dynamicType;
}
field.type = initializerType;
return;
}
return;
}
(element.variable as FieldElementImpl).type = typeResult.type;
}
/**
@ -287,10 +389,10 @@ class InstanceMemberInferrer {
//
currentClassElement = classElement;
for (FieldElement field in classElement.fields) {
_inferField(field);
_inferAccessorOrField(field: field);
}
for (PropertyAccessorElement accessor in classElement.accessors) {
_inferAccessor(accessor);
_inferAccessorOrField(accessor: accessor);
}
for (MethodElement method in classElement.methods) {
_inferExecutable(method);
@ -340,42 +442,57 @@ class InstanceMemberInferrer {
return;
}
// TODO(scheglov) If no implicit types, don't ask inherited.
List<ExecutableElement> overriddenElements = inheritance.getOverridden2(
currentClassElement,
Name(element.library.source.uri, element.name),
);
var name = Name(element.library.source.uri, element.name);
List<ExecutableElement> overriddenElements =
inheritance.getOverridden2(currentClassElement, name);
if (overriddenElements == null ||
!_allSameElementKind(element, overriddenElements)) {
return;
}
List<FunctionType> overriddenTypes =
_toOverriddenFunctionTypes(element, overriddenElements);
if (overriddenTypes.isEmpty) {
return;
FunctionType combinedSignatureType;
FunctionType getCombinedSignatureType() {
if (combinedSignatureType == null) {
var combinedSignature = inheritance.combineSignatures(
targetClass: currentClassElement,
candidates: overriddenElements,
doTopMerge: true,
name: name,
);
if (combinedSignature != null) {
combinedSignatureType = _toOverriddenFunctionType(
element,
combinedSignature,
);
}
}
return combinedSignatureType;
}
//
// Infer the return type.
//
if (element.hasImplicitReturnType && element.displayName != '[]=') {
element.returnType =
_computeReturnType(overriddenTypes.map((t) => t.returnType));
var combinedSignatureType = getCombinedSignatureType();
if (combinedSignatureType != null) {
element.returnType = combinedSignatureType.returnType;
} else {
element.returnType = DynamicTypeImpl.instance;
}
}
//
// Infer the parameter types.
//
List<ParameterElement> parameters = element.parameters;
int length = parameters.length;
for (int i = 0; i < length; ++i) {
ParameterElement parameter = parameters[i];
for (var index = 0; index < parameters.length; index++) {
ParameterElement parameter = parameters[index];
if (parameter is ParameterElementImpl) {
_inferParameterCovariance(parameter, i, overriddenTypes);
_inferParameterCovariance(parameter, index, overriddenElements);
if (parameter.hasImplicitType) {
parameter.type = _computeParameterType(parameter, i, overriddenTypes);
var combinedSignatureType = getCombinedSignatureType();
_inferParameterType(parameter, index, combinedSignatureType);
}
}
}
@ -383,63 +500,46 @@ class InstanceMemberInferrer {
_resetOperatorEqualParameterTypeToDynamic(element, overriddenElements);
}
/**
* If the given [field] represents a non-synthetic instance field for
* which no type was provided, infer the type of the field.
*/
void _inferField(FieldElementImpl field) {
if (field.isSynthetic || field.isStatic) {
return;
}
_FieldOverrideInferenceResult typeResult =
_computeFieldOverrideType(field.getter);
if (typeResult.isError) {
if (field.linkedNode != null) {
LazyAst.setTypeInferenceError(
field.linkedNode,
TopLevelInferenceErrorBuilder(
kind: TopLevelInferenceErrorKind.overrideConflictFieldType,
),
);
}
return;
}
if (field.hasImplicitType) {
DartType newType = typeResult.type;
if (newType == null) {
var initializer = field.initializer;
if (initializer != null) {
newType = initializer.returnType;
}
}
if (newType == null || newType.isBottom || newType.isDartCoreNull) {
newType = _dynamicType;
}
field.type = newType;
}
if (field.setter != null) {
var parameter = field.setter.parameters[0] as ParameterElementImpl;
parameter.inheritsCovariant = typeResult.isCovariant;
}
}
/**
* If a parameter is covariant, any parameters that override it are too.
*/
void _inferParameterCovariance(ParameterElementImpl parameter, int index,
Iterable<FunctionType> overriddenTypes) {
parameter.inheritsCovariant = overriddenTypes.any((f) {
Iterable<ExecutableElement> overridden) {
parameter.inheritsCovariant = overridden.any((f) {
var param = _getCorrespondingParameter(parameter, index, f.parameters);
return param != null && param.isCovariant;
});
}
/**
* Set the type for the [parameter] at the given [index] from the given
* [combinedSignatureType], which might be `null` if there is no valid
* combined signature for signatures from direct superinterfaces.
*/
void _inferParameterType(ParameterElementImpl parameter, int index,
FunctionType combinedSignatureType) {
if (combinedSignatureType != null) {
var matchingParameter = _getCorrespondingParameter(
parameter,
index,
combinedSignatureType.parameters,
);
if (matchingParameter != null) {
parameter.type = matchingParameter.type;
} else {
parameter.type = DynamicTypeImpl.instance;
}
} else {
parameter.type = DynamicTypeImpl.instance;
LazyAst.setTypeInferenceError(
parameter.linkedNode,
TopLevelInferenceErrorBuilder(
kind: TopLevelInferenceErrorKind.overrideConflictParameterType,
),
);
}
}
/**
* Infer type information for all of the instance members in the given
* interface [type].
@ -532,22 +632,12 @@ class InstanceMemberInferrer {
return replaceTypeParameters(overriddenType, elementTypeParameters);
}
/**
* Return [FunctionType]s of [overriddenElements] that override [element].
* Return the empty list, in case of type parameters inconsistency.
*/
List<FunctionType> _toOverriddenFunctionTypes(
ExecutableElement element, List<ExecutableElement> overriddenElements) {
var overriddenTypes = <FunctionType>[];
for (ExecutableElement overriddenElement in overriddenElements) {
FunctionType overriddenType =
_toOverriddenFunctionType(element, overriddenElement);
if (overriddenType == null) {
return const <FunctionType>[];
}
overriddenTypes.add(overriddenType);
static bool _isCovariantSetter(ExecutableElement element) {
if (element is PropertyAccessorElement) {
var parameters = element.parameters;
return parameters.isNotEmpty && parameters[0].isCovariant;
}
return overriddenTypes;
return false;
}
}
@ -555,64 +645,3 @@ class InstanceMemberInferrer {
* A class of exception that is not used anywhere else.
*/
class _CycleException implements Exception {}
/**
* The result of field type inference.
*/
class _FieldOverrideInferenceResult {
final bool isCovariant;
final DartType type;
final bool isError;
_FieldOverrideInferenceResult(this.isCovariant, this.type, this.isError);
}
/// Helper for merging types from several overridden executables, according
/// to legacy or NNBD rules.
class _OverriddenTypesMerger {
final TypeSystemImpl _typeSystem;
bool hasError = false;
DartType _legacyResult;
DartType _notNormalized;
DartType _currentMerge;
_OverriddenTypesMerger(this._typeSystem);
DartType get result {
if (_typeSystem.isNonNullableByDefault) {
return _currentMerge ?? _notNormalized;
} else {
return _legacyResult;
}
}
void update(DartType type) {
if (hasError) {
// Stop updating it.
} else if (_typeSystem.isNonNullableByDefault) {
if (_currentMerge == null) {
if (_notNormalized == null) {
_notNormalized = type;
return;
} else {
_currentMerge = _typeSystem.normalize(_notNormalized);
}
}
var normType = _typeSystem.normalize(type);
try {
_currentMerge = _typeSystem.topMerge(_currentMerge, normType);
} catch (_) {
hasError = true;
}
} else {
if (_legacyResult == null) {
_legacyResult = type;
} else if (_legacyResult != type) {
hasError = true;
}
}
}
}

View file

@ -3,7 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'driver_resolution.dart';
@ -17,6 +19,375 @@ main() {
@reflectiveTest
class InstanceMemberInferenceClassTest extends DriverResolutionTest {
test_field_covariant_fromField() async {
await resolveTestCode('''
class A {
covariant num foo = 0;
}
class B implements A {
int foo = 0;
}
''');
var foo = findElement.field('foo', of: 'B');
_assertFieldType(foo, 'int', isCovariant: true);
}
test_field_covariant_fromSetter() async {
await resolveTestCode('''
class A {
set foo(covariant num _) {}
}
class B implements A {
int foo = 0;
}
''');
var foo = findElement.field('foo', of: 'B');
_assertFieldType(foo, 'int', isCovariant: true);
}
test_field_fromInitializer_inherited() async {
await resolveTestCode('''
class A {
var foo = 0;
}
class B implements A {
var foo;
}
''');
var foo = findElement.field('foo', of: 'B');
_assertFieldType(foo, 'int');
}
test_field_fromInitializer_preferSuper() async {
await resolveTestCode('''
class A {
num foo;
}
class B implements A {
var foo = 0;
}
''');
var foo = findElement.field('foo', of: 'B');
_assertFieldType(foo, 'num');
}
test_field_multiple_fields_incompatible() async {
await resolveTestCode('''
class A {
int foo = throw 0;
}
class B {
String foo = throw 0;
}
class C implements A, B {
var foo;
}
''');
var foo = findElement.field('foo', of: 'C');
_assertFieldTypeDynamic(foo);
}
test_field_multiple_getters_combined() async {
await resolveTestCode('''
class A {
num get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C implements A, B {
var foo;
}
''');
var foo = findElement.field('foo', of: 'C');
_assertFieldType(foo, 'int');
}
test_field_multiple_getters_incompatible() async {
await resolveTestCode('''
class A {
String get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C implements A, B {
var foo;
}
''');
var foo = findElement.field('foo', of: 'C');
_assertFieldTypeDynamic(foo);
}
test_field_multiple_gettersSetters_final_combined() async {
await resolveTestCode('''
class A {
num get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C {
set foo(String _) {}
}
class X implements A, B, C {
final foo;
}
''');
var foo = findElement.field('foo', of: 'X');
_assertFieldType(foo, 'int');
}
test_field_multiple_gettersSetters_final_incompatible() async {
await resolveTestCode('''
class A {
String get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C {
set foo(String _) {}
}
class X implements A, B, C {
final foo;
}
''');
var foo = findElement.field('foo', of: 'X');
_assertFieldTypeDynamic(foo);
}
test_field_multiple_gettersSetters_notFinal_combined_notSame() async {
await resolveTestCode('''
class A {
num get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C {
set foo(String _) {}
}
class X implements A, B, C {
var foo;
}
''');
var foo = findElement.field('foo', of: 'X');
_assertFieldTypeDynamic(foo);
// TODO(scheglov) error?
}
test_field_multiple_gettersSetters_notFinal_combined_same() async {
await resolveTestCode('''
class A {
num get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C {
set foo(int _) {}
}
class X implements A, B, C {
var foo;
}
''');
var foo = findElement.field('foo', of: 'X');
_assertFieldType(foo, 'int');
}
test_field_multiple_gettersSetters_notFinal_incompatible_getters() async {
await resolveTestCode('''
class A {
String get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C {
set foo(int _) {}
}
class X implements A, B, C {
var foo;
}
''');
var foo = findElement.field('foo', of: 'X');
_assertFieldTypeDynamic(foo);
}
test_field_multiple_gettersSetters_notFinal_incompatible_setters() async {
await resolveTestCode('''
class A {
int get foo => throw 0;
}
class B {
set foo(String _) {}
}
class C {
set foo(int _) {}
}
class X implements A, B, C {
var foo;
}
''');
var foo = findElement.field('foo', of: 'X');
_assertFieldTypeDynamic(foo);
}
test_field_multiple_setters_combined() async {
await resolveTestCode('''
class A {
set foo(num _) {}
}
class B {
set foo(int _) {}
}
class C implements A, B {
var foo;
}
''');
var foo = findElement.field('foo', of: 'C');
_assertFieldType(foo, 'num');
}
test_field_multiple_setters_incompatible() async {
await resolveTestCode('''
class A {
set foo(String _) {}
}
class B {
set foo(int _) {}
}
class C implements A, B {
var foo;
}
''');
var foo = findElement.field('foo', of: 'C');
_assertFieldTypeDynamic(foo);
}
test_getter_multiple_getters_combined() async {
await resolveTestCode('''
class A {
num get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C implements A, B {
get foo => throw 0;
}
''');
var foo = findElement.getter('foo', of: 'C');
_assertGetterType(foo, 'int');
}
test_getter_multiple_getters_incompatible() async {
await resolveTestCode('''
class A {
String get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C implements A, B {
get foo => throw 0;
}
''');
var foo = findElement.getter('foo', of: 'C');
_assertGetterTypeDynamic(foo);
}
test_getter_multiple_getters_same() async {
await resolveTestCode('''
class A {
int get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C implements A, B {
get foo => throw 0;
}
''');
var foo = findElement.getter('foo', of: 'C');
_assertGetterType(foo, 'int');
}
test_getter_multiple_gettersSetters_combined() async {
await resolveTestCode('''
class A {
num get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C {
set foo(String _) {}
}
class X implements A, B, C {
get foo => throw 0;
}
''');
var foo = findElement.getter('foo', of: 'X');
_assertGetterType(foo, 'int');
}
test_getter_multiple_gettersSetters_incompatible() async {
await resolveTestCode('''
class A {
String get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C {
set foo(String _) {}
}
class X implements A, B, C {
get foo => throw 0;
}
''');
var foo = findElement.getter('foo', of: 'X');
_assertGetterTypeDynamic(foo);
}
test_getter_multiple_setters_combined() async {
await resolveTestCode('''
class A {
set foo(num _) {}
}
class B {
set foo(int _) {}
}
class C implements A, B {
get foo => throw 0;
}
''');
var foo = findElement.getter('foo', of: 'C');
_assertGetterType(foo, 'num');
}
test_getter_multiple_setters_incompatible() async {
await resolveTestCode('''
class A {
set foo(String _) {}
}
class B {
set foo(int _) {}
}
class C implements A, B {
get foo => throw 0;
}
''');
var foo = findElement.getter('foo', of: 'C');
_assertGetterTypeDynamic(foo);
}
test_invalid_inheritanceCycle() async {
await resolveTestCode('''
class A extends C {}
@ -25,100 +396,244 @@ class C extends B {}
''');
}
test_method_parameter_multiple_different() async {
test_method_parameter_covariant_named() async {
await resolveTestCode('''
class A {
foo(int p) => 0;
void foo({num p}) {}
}
class B {
foo(double p) => 0;
void foo({covariant num p}) {}
}
class C implements A, B {
foo(p) => 0;
void foo({int p}) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
_assertParameter(p, type: 'int', isCovariant: true);
}
test_method_parameter_covariant_positional() async {
await resolveTestCode('''
class A {
void foo([num p]) {}
}
class B {
void foo([covariant num p]) {}
}
class C implements A, B {
void foo([int p]) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
_assertParameter(p, type: 'int', isCovariant: true);
}
test_method_parameter_covariant_required() async {
await resolveTestCode('''
class A {
void foo(num p) {}
}
class B {
void foo(covariant num p) {}
}
class C implements A, B {
void foo(int p) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
_assertParameter(p, type: 'int', isCovariant: true);
}
test_method_parameter_named_multiple_combined() async {
await resolveTestCode('''
class A {
void foo({int p}) {}
}
class B {
void foo({num p}) {}
}
class C implements A, B {
void foo({p}) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
assertType(p.type, 'num');
}
test_method_parameter_named_multiple_incompatible() async {
await resolveTestCode('''
class A {
void foo({int p}) {}
}
class B {
void foo({int q}) {}
}
class C implements A, B {
void foo({p}) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
assertTypeDynamic(p.type);
}
test_method_parameter_multiple_named_different() async {
test_method_parameter_named_multiple_same() async {
await resolveTestCode('''
class A {
foo({int p}) => 0;
void foo({int p}) {}
}
class B {
foo({int q}) => 0;
void foo({int p}) {}
}
class C implements A, B {
foo({p}) => 0;
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
assertTypeDynamic(p.type);
}
test_method_parameter_multiple_named_same() async {
await resolveTestCode('''
class A {
foo({int p}) => 0;
}
class B {
foo({int p}) => 0;
}
class C implements A, B {
foo({p}) => 0;
void foo({p}) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
assertType(p.type, 'int');
}
test_method_parameter_multiple_namedAndRequired() async {
test_method_parameter_namedAndRequired() async {
await resolveTestCode('''
class A {
foo({int p}) => 0;
void foo({int p}) {}
}
class B {
foo(int p) => 0;
void foo(int p) {}
}
class C implements A, B {
foo(p) => 0;
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
assertTypeDynamic(p.type);
}
test_method_parameter_multiple_optionalAndRequired() async {
test_method_parameter_required_multiple_combined() async {
await resolveTestCode('''
class A {
foo(int p) => 0;
void foo(int p) {}
}
class B {
foo([int p]) => 0;
void foo(num p) {}
}
class C implements A, B {
foo(p) => 0;
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
assertType(p.type, 'num');
}
test_method_parameter_required_multiple_incompatible() async {
await resolveTestCode('''
class A {
void foo(int p) {}
}
class B {
void foo(double p) {}
}
class C implements A, B {
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
assertTypeDynamic(p.type);
}
test_method_parameter_required_multiple_same() async {
await resolveTestCode('''
class A {
void foo(int p) {}
}
class B {
void foo(int p) {}
}
class C implements A, B {
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
assertType(p.type, 'int');
}
test_method_parameter_single_generic() async {
test_method_parameter_required_single_generic() async {
await resolveTestCode('''
class A<E> {
foo(E p) => 0;
void foo(E p) {}
}
class C<T> implements A<T> {
foo(p) => 0;
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
assertType(p.type, 'T');
}
test_method_return_multiple_different() async {
test_method_parameter_requiredAndPositional() async {
await resolveTestCode('''
class A {
void foo(int p) {}
}
class B {
void foo([int p]) {}
}
class C implements A, B {
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'C').parameters[0];
assertType(p.type, 'int');
}
test_method_return_multiple_different_combined() async {
await resolveTestCode('''
class A {
int foo() => 0;
}
class B {
num foo() => 0.0;
}
class C implements A, B {
foo() => 0;
}
''');
var foo = findElement.method('foo', of: 'C');
assertType(foo.returnType, 'int');
}
test_method_return_multiple_different_dynamic() async {
await resolveTestCode('''
class A {
int foo() => 0;
}
class B {
foo() => 0;
}
class C implements A, B {
foo() => 0;
}
''');
var foo = findElement.method('foo', of: 'C');
assertType(foo.returnType, 'int');
}
test_method_return_multiple_different_generic() async {
await resolveTestCode('''
class A<E> {
E foo() => throw 0;
}
class B<E> {
E foo() => throw 0;
}
class C implements A<int>, B<double> {
foo() => throw 0;
}
''');
var foo = findElement.method('foo', of: 'C');
assertTypeDynamic(foo.returnType);
}
test_method_return_multiple_different_incompatible() async {
await resolveTestCode('''
class A {
int foo() => 0;
@ -134,22 +649,6 @@ class C implements A, B {
assertTypeDynamic(foo.returnType);
}
test_method_return_multiple_different_generic() async {
await resolveTestCode('''
class A<E> {
E foo() => null;
}
class B<E> {
E foo() => null;
}
class C implements A<int>, B<double> {
foo() => null;
}
''');
var foo = findElement.method('foo', of: 'C');
assertTypeDynamic(foo.returnType);
}
test_method_return_multiple_different_void() async {
await resolveTestCode('''
class A {
@ -163,23 +662,7 @@ class C implements A, B {
}
''');
var foo = findElement.method('foo', of: 'C');
assertTypeDynamic(foo.returnType);
}
test_method_return_multiple_dynamic() async {
await resolveTestCode('''
class A {
int foo() => 0;
}
class B {
foo() => 0;
}
class C implements A, B {
foo() => 0;
}
''');
var foo = findElement.method('foo', of: 'C');
assertTypeDynamic(foo.returnType);
assertType(foo.returnType, 'int');
}
test_method_return_multiple_same_generic() async {
@ -246,15 +729,189 @@ class B extends A {
test_method_return_single_generic() async {
await resolveTestCode('''
class A<E> {
E foo() => 0;
E foo() => throw 0;
}
class B<T> extends A<T> {
foo() => 0;
foo() => throw 0;
}
''');
var foo = findElement.method('foo', of: 'B');
assertType(foo.returnType, 'T');
}
test_setter_covariant_fromSetter() async {
await resolveTestCode('''
class A {
set foo(num _) {}
}
class B {
set foo(covariant num _) {}
}
class C implements A, B {
set foo(int x) {}
}
''');
var foo = findElement.setter('foo', of: 'C');
_assertSetterType(foo, 'int', isCovariant: true);
}
test_setter_multiple_getters_combined() async {
await resolveTestCode('''
class A {
num get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C implements A, B {
set foo(x) {}
}
''');
var foo = findElement.setter('foo', of: 'C');
_assertSetterType(foo, 'int');
}
test_setter_multiple_getters_incompatible() async {
await resolveTestCode('''
class A {
String get foo => throw 0;
}
class B {
int get foo => throw 0;
}
class C implements A, B {
set foo(x) {}
}
''');
var foo = findElement.setter('foo', of: 'C');
_assertSetterTypeDynamic(foo);
}
test_setter_multiple_gettersSetters_combined() async {
await resolveTestCode('''
class A {
set foo(num _) {}
}
class B {
set foo(int _) {}
}
class C {
String get foo => throw 0;
}
class X implements A, B, C {
set foo(x) {}
}
''');
var foo = findElement.setter('foo', of: 'X');
_assertSetterType(foo, 'num');
}
test_setter_multiple_gettersSetters_incompatible() async {
await resolveTestCode('''
class A {
set foo(String _) {}
}
class B {
set foo(int _) {}
}
class C {
int get foo => throw 0;
}
class X implements A, B, C {
set foo(x) {}
}
''');
var foo = findElement.setter('foo', of: 'X');
_assertSetterTypeDynamic(foo);
}
test_setter_multiple_setters_combined() async {
await resolveTestCode('''
class A {
set foo(num _) {}
}
class B {
set foo(int _) {}
}
class C implements A, B {
set foo(x) {}
}
''');
var foo = findElement.setter('foo', of: 'C');
_assertSetterType(foo, 'num');
}
test_setter_multiple_setters_incompatible() async {
await resolveTestCode('''
class A {
set foo(String _) {}
}
class B {
set foo(int _) {}
}
class C implements A, B {
set foo(x) {}
}
''');
var foo = findElement.setter('foo', of: 'C');
_assertSetterTypeDynamic(foo);
}
void _assertFieldType(
FieldElement field,
String type, {
bool isCovariant = false,
}) {
expect(field.isSynthetic, isFalse);
_assertGetterType(field.getter, type);
var setter = field.setter;
if (setter != null) {
_assertSetterType(setter, type, isCovariant: isCovariant);
}
}
void _assertFieldTypeDynamic(FieldElement field) {
expect(field.isSynthetic, isFalse);
_assertGetterTypeDynamic(field.getter);
if (!field.isFinal) {
_assertSetterTypeDynamic(field.setter);
}
}
void _assertGetterType(PropertyAccessorElement accessor, String expected) {
assertType(accessor.returnType, expected);
}
void _assertGetterTypeDynamic(PropertyAccessorElement accessor) {
assertTypeDynamic(accessor.returnType);
}
void _assertParameter(
ParameterElement element, {
String type,
bool isCovariant = false,
}) {
assertType(element.type, type);
expect(element.isCovariant, isCovariant);
}
void _assertSetterType(
PropertyAccessorElement accessor,
String expected, {
bool isCovariant = false,
}) {
var parameter = accessor.parameters.single;
assertType(parameter.type, expected);
expect(parameter.isCovariant, isCovariant);
}
void _assertSetterTypeDynamic(PropertyAccessorElement accessor) {
assertTypeDynamic(accessor.parameters.single.type);
}
}
@reflectiveTest
@ -268,7 +925,7 @@ class InstanceMemberInferenceClassWithNullSafetyTest
@override
bool get typeToStringWithNullability => true;
test_method_parameter_multiple_different_merge() async {
test_method_parameter_required_multiple_different_merge() async {
await resolveTestCode('''
class A {
void foo(Object? p) {}

View file

@ -2,121 +2,219 @@
// 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/analysis/features.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'driver_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(InstanceMemberInferenceMixinDriverResolutionTest);
defineReflectiveTests(InstanceMemberInferenceClassTest);
defineReflectiveTests(InstanceMemberInferenceClassWithNullSafetyTest);
});
}
@reflectiveTest
class InstanceMemberInferenceMixinDriverResolutionTest
extends DriverResolutionTest {
class InstanceMemberInferenceClassTest extends DriverResolutionTest {
test_invalid_inheritanceCycle() async {
await resolveTestCode('''
mixin A on C {}
mixin B on A {}
mixin C on B {}
class A extends C {}
class B extends A {}
class C extends B {}
''');
}
test_method_parameter_multiple_different() async {
test_method_parameter_named_multiple_combined() async {
await resolveTestCode('''
class A {
foo(int p) => 0;
void foo({int p}) {}
}
class B {
foo(double p) => 0;
void foo({num p}) {}
}
mixin M on A, B {
foo(p) => 0;
void foo({p}) {}
}
''');
var p = findElement.method('foo', of: 'M').parameters[0];
assertType(p.type, 'num');
}
test_method_parameter_named_multiple_incompatible() async {
await resolveTestCode('''
class A {
void foo({int p}) {}
}
class B {
void foo({int q}) {}
}
mixin M on A, B {
void foo({p}) {}
}
''');
var p = findElement.method('foo', of: 'M').parameters[0];
assertTypeDynamic(p.type);
}
test_method_parameter_multiple_named_different() async {
test_method_parameter_named_multiple_same() async {
await resolveTestCode('''
class A {
foo({int p}) => 0;
void foo({int p}) {}
}
class B {
foo({int q}) => 0;
void foo({int p}) {}
}
mixin M on A, B {
foo({p}) => 0;
}
''');
var p = findElement.method('foo', of: 'M').parameters[0];
assertTypeDynamic(p.type);
}
test_method_parameter_multiple_named_same() async {
await resolveTestCode('''
class A {
foo({int p}) => 0;
}
class B {
foo({int p}) => 0;
}
mixin M on A, B {
foo({p}) => 0;
void foo({p}) {}
}
''');
var p = findElement.method('foo', of: 'M').parameters[0];
assertType(p.type, 'int');
}
test_method_parameter_multiple_namedAndRequired() async {
test_method_parameter_namedAndRequired() async {
await resolveTestCode('''
class A {
foo({int p}) => 0;
void foo({int p}) {}
}
class B {
foo(int p) => 0;
void foo(int p) {}
}
mixin M on A, B {
foo(p) => 0;
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'M').parameters[0];
assertTypeDynamic(p.type);
}
test_method_parameter_multiple_optionalAndRequired() async {
test_method_parameter_required_multiple_combined() async {
await resolveTestCode('''
class A {
foo(int p) => 0;
void foo(int p) {}
}
class B {
foo([int p]) => 0;
void foo(num p) {}
}
mixin M on A, B {
foo(p) => 0;
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'M').parameters[0];
assertType(p.type, 'num');
}
test_method_parameter_required_multiple_incompatible() async {
await resolveTestCode('''
class A {
void foo(int p) {}
}
class B {
void foo(double p) {}
}
mixin M on A, B {
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'M').parameters[0];
assertTypeDynamic(p.type);
}
test_method_parameter_required_multiple_same() async {
await resolveTestCode('''
class A {
void foo(int p) {}
}
class B {
void foo(int p) {}
}
mixin M on A, B {
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'M').parameters[0];
assertType(p.type, 'int');
}
test_method_parameter_single_generic() async {
test_method_parameter_required_single_generic() async {
await resolveTestCode('''
class A<E> {
foo(E p) => 0;
void foo(E p) {}
}
mixin M<T> on A<T> {
foo(p) => 0;
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'M').parameters[0];
assertType(p.type, 'T');
}
test_method_return_multiple_different() async {
test_method_parameter_requiredAndPositional() async {
await resolveTestCode('''
class A {
void foo(int p) {}
}
class B {
void foo([int p]) {}
}
mixin M on A, B {
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'M').parameters[0];
assertType(p.type, 'int');
}
test_method_return_multiple_different_combined() async {
await resolveTestCode('''
class A {
int foo() => 0;
}
class B {
num foo() => 0.0;
}
mixin M on A, B {
foo() => 0;
}
''');
var foo = findElement.method('foo', of: 'M');
assertType(foo.returnType, 'int');
}
test_method_return_multiple_different_dynamic() async {
await resolveTestCode('''
class A {
int foo() => 0;
}
class B {
foo() => 0;
}
mixin M on A, B {
foo() => 0;
}
''');
var foo = findElement.method('foo', of: 'M');
assertType(foo.returnType, 'int');
}
test_method_return_multiple_different_generic() async {
await resolveTestCode('''
class A<E> {
E foo() => throw 0;
}
class B<E> {
E foo() => throw 0;
}
mixin M on A<int>, B<double> {
foo() => throw 0;
}
''');
var foo = findElement.method('foo', of: 'M');
assertTypeDynamic(foo.returnType);
}
test_method_return_multiple_different_incompatible() async {
await resolveTestCode('''
class A {
int foo() => 0;
@ -132,22 +230,6 @@ mixin M on A, B {
assertTypeDynamic(foo.returnType);
}
test_method_return_multiple_different_generic() async {
await resolveTestCode('''
class A<E> {
E foo() => null;
}
class B<E> {
E foo() => null;
}
mixin M on A<int>, B<double> {
foo() => null;
}
''');
var foo = findElement.method('foo', of: 'M');
assertTypeDynamic(foo.returnType);
}
test_method_return_multiple_different_void() async {
await resolveTestCode('''
class A {
@ -161,23 +243,7 @@ mixin M on A, B {
}
''');
var foo = findElement.method('foo', of: 'M');
assertTypeDynamic(foo.returnType);
}
test_method_return_multiple_dynamic() async {
await resolveTestCode('''
class A {
int foo() => 0;
}
class B {
foo() => 0;
}
mixin M on A, B {
foo() => 0;
}
''');
var foo = findElement.method('foo', of: 'M');
assertTypeDynamic(foo.returnType);
assertType(foo.returnType, 'int');
}
test_method_return_multiple_same_generic() async {
@ -233,24 +299,72 @@ mixin M on A, B {
class A {
int foo() => 0;
}
mixin M on A {
class B extends A {
foo() => 0;
}
''');
var foo = findElement.method('foo', of: 'M');
var foo = findElement.method('foo', of: 'B');
assertType(foo.returnType, 'int');
}
test_method_return_single_generic() async {
await resolveTestCode('''
class A<E> {
E foo() => 0;
E foo() => throw 0;
}
mixin M<T> on A<T> {
foo() => 0;
class B<T> extends A<T> {
foo() => throw 0;
}
''');
var foo = findElement.method('foo', of: 'M');
var foo = findElement.method('foo', of: 'B');
assertType(foo.returnType, 'T');
}
}
@reflectiveTest
class InstanceMemberInferenceClassWithNullSafetyTest
extends InstanceMemberInferenceClassTest {
@override
AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
..contextFeatures = FeatureSet.forTesting(
sdkVersion: '2.3.0', additionalFeatures: [Feature.non_nullable]);
@override
bool get typeToStringWithNullability => true;
test_method_parameter_required_multiple_different_merge() async {
await resolveTestCode('''
class A {
void foo(Object? p) {}
}
class B {
void foo(dynamic p) {}
}
mixin M on A, B {
void foo(p) {}
}
''');
var p = findElement.method('foo', of: 'M').parameters[0];
assertType(p.type, 'Object?');
}
test_method_return_multiple_different_merge() async {
await resolveTestCode('''
class A {
Object? foo() => throw 0;
}
class B {
dynamic foo() => throw 0;
}
mixin M on A, B {
foo() => throw 0;
}
''');
var foo = findElement.method('foo', of: 'M');
assertType(foo.returnType, 'Object?');
}
}

View file

@ -1707,7 +1707,7 @@ abstract class B {
dynamic get x;
}
class C implements A, B {
dynamic get x {}
int get x {}
}
''');
}
@ -1850,8 +1850,8 @@ abstract class B {
void set x(String _);
}
class C implements A, B {
synthetic dynamic x;
void set x(dynamic _);
synthetic String x;
void set x(String _);
}
''',
withSyntheticFields: true);
@ -2121,7 +2121,7 @@ class B<E> {
void m(E a) {}
}
class C extends A<int> implements B<double> {
m(a) {}
void m(a) {}
}
''');
checkElementText(library, r'''
@ -2146,7 +2146,7 @@ class B {
void m(String a) {}
}
class C extends A implements B {
m(a) {}
void m(a) {}
}
''');
checkElementText(library, r'''
@ -2174,7 +2174,6 @@ class C extends A<int, String> implements B<double> {
m(a) {}
}
''');
// TODO(scheglov) test for inference failure error
checkElementText(library, r'''
class A<K, V> {
V m(K a) {}
@ -2183,7 +2182,7 @@ class B<T> {
T m(int a) {}
}
class C extends A<int, String> implements B<double> {
dynamic m(int a) {}
dynamic m(dynamic a/*error: overrideConflictParameterType*/) {}
}
''');
}
@ -2220,7 +2219,7 @@ class A {
void m(int a) {}
}
class B extends A {
m(a, b) {}
void m(a, b) {}
}
''');
// It's an error to add a new required parameter, but it is not a

View file

@ -352,34 +352,6 @@ var y = () => /*error:TOP_LEVEL_CYCLE*/x;
_assertTypeStr(y.initializer.returnType, 'dynamic Function()');
}
test_conflictsCanHappen() async {
await checkFileElement('''
class I1 {
int x;
}
class I2 extends I1 {
int y;
}
class A {
final I1 a = null;
}
class B {
final I2 a = null;
}
class C1 implements A, B {
get /*error:INVALID_OVERRIDE,error:INVALID_OVERRIDE*/a => null;
}
// Still ambiguous
class C2 implements B, A {
get /*error:INVALID_OVERRIDE,error:INVALID_OVERRIDE*/a => null;
}
''');
}
test_conflictsCanHappen2() async {
await checkFileElement('''
class I1 {