mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 04:37:12 +00:00
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:
parent
43131b575e
commit
24852421e2
|
@ -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}) {}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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?');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue