| Index: pkg/analyzer/lib/src/task/strong_mode.dart
|
| diff --git a/pkg/analyzer/lib/src/task/strong_mode.dart b/pkg/analyzer/lib/src/task/strong_mode.dart
|
| index eb0ac52605332a5cdd9583a45b5dbed5c19457f1..ec778b47227d313caa134b6601c5fb8bba2f0b73 100644
|
| --- a/pkg/analyzer/lib/src/task/strong_mode.dart
|
| +++ b/pkg/analyzer/lib/src/task/strong_mode.dart
|
| @@ -29,29 +29,11 @@ bool isValidForTypeInference(Expression expression) {
|
| }
|
|
|
| /**
|
| - * Sets the type of the field. This is stored in the field itself, and the
|
| - * synthetic getter/setter types.
|
| + * Sets the type of the field. The types in implicit accessors are updated
|
| + * implicitly, and the types of explicit accessors should be updated separately.
|
| */
|
| void setFieldType(VariableElement field, DartType newType) {
|
| (field as VariableElementImpl).type = newType;
|
| - if (field.initializer != null) {
|
| - (field.initializer as ExecutableElementImpl).returnType = newType;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Return the element for the single parameter of the given [setter], or `null`
|
| - * if the executable element is not a setter or does not have a single
|
| - * parameter.
|
| - */
|
| -ParameterElement _getParameter(ExecutableElement setter) {
|
| - if (setter is PropertyAccessorElement && setter.isSetter) {
|
| - List<ParameterElement> parameters = setter.parameters;
|
| - if (parameters.length == 1) {
|
| - return parameters[0];
|
| - }
|
| - }
|
| - return null;
|
| }
|
|
|
| /**
|
| @@ -128,9 +110,56 @@ class InstanceMemberInferrer {
|
| }
|
|
|
| /**
|
| + * Compute the inferred type for the given property accessor [element]. The
|
| + * returned value is never `null`, but might be an error, and/or have the
|
| + * `null` type.
|
| + */
|
| + _FieldOverrideInferenceResult _computeFieldOverrideType(
|
| + ExecutableElement element) {
|
| + String name = element.displayName;
|
| +
|
| + var overriddenElements = <ExecutableElement>[];
|
| + overriddenElements.addAll(
|
| + inheritanceManager.lookupOverrides(element.enclosingElement, name));
|
| + overriddenElements.addAll(
|
| + inheritanceManager.lookupOverrides(element.enclosingElement, '$name='));
|
| +
|
| + bool isCovariant = false;
|
| + DartType impliedType;
|
| + for (ExecutableElement overriddenElement in overriddenElements) {
|
| + FunctionType overriddenType =
|
| + _toOverriddenFunctionType(element, overriddenElement);
|
| + if (overriddenType == null) {
|
| + return new _FieldOverrideInferenceResult(false, null, true);
|
| + }
|
| +
|
| + DartType type;
|
| + if (overriddenElement.kind == ElementKind.GETTER) {
|
| + type = overriddenType.returnType;
|
| + } else if (overriddenElement.kind == ElementKind.SETTER) {
|
| + if (overriddenType.parameters.length == 1) {
|
| + ParameterElement parameter = overriddenType.parameters[0];
|
| + type = parameter.type;
|
| + isCovariant = isCovariant || parameter.isCovariant;
|
| + }
|
| + } else {
|
| + return new _FieldOverrideInferenceResult(false, null, true);
|
| + }
|
| +
|
| + if (impliedType == null) {
|
| + impliedType = type;
|
| + } else if (type != impliedType) {
|
| + return new _FieldOverrideInferenceResult(false, null, true);
|
| + }
|
| + }
|
| +
|
| + return new _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
|
| - * [overriddenMethods].
|
| + * [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
|
| @@ -142,9 +171,9 @@ class InstanceMemberInferrer {
|
| DartType parameterType = null;
|
| int length = overriddenTypes.length;
|
| for (int i = 0; i < length; i++) {
|
| - ParameterElement matchingParam = _getCorrespondingParameter(
|
| + ParameterElement matchingParameter = _getCorrespondingParameter(
|
| parameter, index, overriddenTypes[i].parameters);
|
| - var type = matchingParam?.type ?? typeProvider.dynamicType;
|
| + DartType type = matchingParameter?.type ?? typeProvider.dynamicType;
|
| if (parameterType == null) {
|
| parameterType = type;
|
| } else if (parameterType != type) {
|
| @@ -213,6 +242,37 @@ class InstanceMemberInferrer {
|
| }
|
|
|
| /**
|
| + * If the given [element] represents a non-synthetic instance property
|
| + * accessor for which no type was provided, infer its types.
|
| + */
|
| + void _inferAccessor(PropertyAccessorElement element) {
|
| + if (element.isSynthetic || element.isStatic) {
|
| + return;
|
| + }
|
| +
|
| + 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) {
|
| + var parameter = element.parameters[0] as ParameterElementImpl;
|
| + if (parameter.hasImplicitType) {
|
| + parameter.type = typeResult.type;
|
| + }
|
| + parameter.inheritsCovariant = typeResult.isCovariant;
|
| + }
|
| + setFieldType(element.variable, typeResult.type);
|
| + }
|
| +
|
| + /**
|
| * Infer type information for all of the instance members in the given
|
| * [classElement].
|
| */
|
| @@ -241,7 +301,7 @@ class InstanceMemberInferrer {
|
| // Then infer the types for the members.
|
| //
|
| classElement.fields.forEach(_inferField);
|
| - classElement.accessors.forEach(_inferExecutable);
|
| + classElement.accessors.forEach(_inferAccessor);
|
| classElement.methods.forEach(_inferExecutable);
|
| //
|
| // Infer initializing formal parameter types. This must happen after
|
| @@ -255,10 +315,14 @@ class InstanceMemberInferrer {
|
| }
|
| }
|
|
|
| - void _inferConstructorFieldFormals(ConstructorElement element) {
|
| - for (ParameterElement p in element.parameters) {
|
| - if (p is FieldFormalParameterElementImpl) {
|
| - _inferFieldFormalParameter(p);
|
| + void _inferConstructorFieldFormals(ConstructorElement constructor) {
|
| + for (ParameterElement parameter in constructor.parameters) {
|
| + if (parameter.hasImplicitType &&
|
| + parameter is FieldFormalParameterElementImpl) {
|
| + FieldElement field = parameter.field;
|
| + if (field != null) {
|
| + parameter.type = field.type;
|
| + }
|
| }
|
| }
|
| }
|
| @@ -272,41 +336,17 @@ class InstanceMemberInferrer {
|
| if (element.isSynthetic || element.isStatic) {
|
| return;
|
| }
|
| - List<ExecutableElement> overriddenMethods = inheritanceManager
|
| - .lookupOverrides(element.enclosingElement, element.name);
|
| - if (overriddenMethods.isEmpty ||
|
| - !_allSameElementKind(element, overriddenMethods)) {
|
| + List<ExecutableElement> overriddenElements = inheritanceManager
|
| + .lookupOverrides(element.enclosingElement, element.displayName);
|
| + if (overriddenElements.isEmpty ||
|
| + !_allSameElementKind(element, overriddenElements)) {
|
| return;
|
| }
|
|
|
| - //
|
| - // Overridden methods must have the same number of generic type parameters
|
| - // as this method, or none.
|
| - //
|
| - // If we do have generic type parameters on the element we're inferring,
|
| - // we must express its parameter and return types in terms of its own
|
| - // parameters. For example, given `m<T>(t)` overriding `m<S>(S s)` we
|
| - // should infer this as `m<T>(T t)`.
|
| - //
|
| - List<DartType> typeFormals =
|
| - TypeParameterTypeImpl.getTypes(element.type.typeFormals);
|
| -
|
| - List<FunctionType> overriddenTypes = new List<FunctionType>();
|
| - for (ExecutableElement overriddenMethod in overriddenMethods) {
|
| - FunctionType overriddenType = overriddenMethod.type;
|
| - if (overriddenType == null) {
|
| - // TODO(brianwilkerson) I think the overridden method should always have
|
| - // a type, but there appears to be a bug that causes it to sometimes be
|
| - // null, we guard against that case by not performing inference.
|
| - return;
|
| - }
|
| - if (overriddenType.typeFormals.isNotEmpty) {
|
| - if (overriddenType.typeFormals.length != typeFormals.length) {
|
| - return;
|
| - }
|
| - overriddenType = overriddenType.instantiate(typeFormals);
|
| - }
|
| - overriddenTypes.add(overriddenType);
|
| + List<FunctionType> overriddenTypes =
|
| + _toOverriddenFunctionTypes(element, overriddenElements);
|
| + if (overriddenTypes.isEmpty) {
|
| + return;
|
| }
|
|
|
| //
|
| @@ -340,66 +380,38 @@ class InstanceMemberInferrer {
|
| }
|
|
|
| /**
|
| - * If the given [fieldElement] represents a non-synthetic instance field for
|
| + * If the given [field] represents a non-synthetic instance field for
|
| * which no type was provided, infer the type of the field.
|
| */
|
| - void _inferField(FieldElement fieldElement) {
|
| - if (fieldElement.isSynthetic || fieldElement.isStatic) {
|
| + void _inferField(FieldElement field) {
|
| + if (field.isSynthetic || field.isStatic) {
|
| return;
|
| }
|
| - List<ExecutableElement> overriddenSetters =
|
| - inheritanceManager.lookupOverrides(
|
| - fieldElement.enclosingElement, fieldElement.name + '=');
|
| - var setter = fieldElement.setter;
|
| - if (setter != null && overriddenSetters.isNotEmpty) {
|
| - _inferParameterCovariance(
|
| - setter.parameters[0], 0, overriddenSetters.map((s) => s.type));
|
| +
|
| + _FieldOverrideInferenceResult typeResult =
|
| + _computeFieldOverrideType(field.getter);
|
| + if (typeResult.isError) {
|
| + return;
|
| }
|
|
|
| - if (fieldElement.hasImplicitType) {
|
| - //
|
| - // First look for overridden getters with the same name as the field.
|
| - //
|
| - List<ExecutableElement> overriddenGetters = inheritanceManager
|
| - .lookupOverrides(fieldElement.enclosingElement, fieldElement.name);
|
| - DartType newType = null;
|
| - if (overriddenGetters.isNotEmpty && _onlyGetters(overriddenGetters)) {
|
| - newType =
|
| - _computeReturnType(overriddenGetters.map((e) => e.returnType));
|
| -
|
| - if (!_isCompatible(newType, overriddenSetters)) {
|
| - newType = null;
|
| - }
|
| - }
|
| - //
|
| - // If there is no overridden getter or if the overridden getter's type is
|
| - // dynamic, then we can infer the type from the initialization expression
|
| - // without breaking subtype rules. We could potentially infer a consistent
|
| - // return type even if the overridden getter's type was not dynamic, but
|
| - // choose not to for simplicity. The field is required to be final to
|
| - // prevent choosing a type that is inconsistent with assignments we cannot
|
| - // analyze.
|
| - //
|
| - if (newType == null || newType.isDynamic) {
|
| - FunctionElement initializer = fieldElement.initializer;
|
| - if (initializer != null &&
|
| - (fieldElement.isFinal || overriddenGetters.isEmpty)) {
|
| - if (!fieldsWithDisabledInitializerInference.contains(fieldElement)) {
|
| - newType = initializer.returnType;
|
| - }
|
| - }
|
| + if (field.hasImplicitType) {
|
| + DartType newType = typeResult.type;
|
| + if (newType == null &&
|
| + field.initializer != null &&
|
| + !fieldsWithDisabledInitializerInference.contains(field)) {
|
| + newType = field.initializer.returnType;
|
| }
|
| +
|
| if (newType == null || newType.isBottom || newType.isDartCoreNull) {
|
| newType = typeProvider.dynamicType;
|
| }
|
| - setFieldType(fieldElement, newType);
|
| +
|
| + setFieldType(field, newType);
|
| }
|
| - }
|
|
|
| - void _inferFieldFormalParameter(FieldFormalParameterElementImpl element) {
|
| - FieldElement field = element.field;
|
| - if (field != null && element.hasImplicitType) {
|
| - element.type = field.type;
|
| + if (field.setter != null) {
|
| + var parameter = field.setter.parameters[0] as ParameterElementImpl;
|
| + parameter.inheritsCovariant = typeResult.isCovariant;
|
| }
|
| }
|
|
|
| @@ -428,29 +440,56 @@ class InstanceMemberInferrer {
|
| }
|
|
|
| /**
|
| - * Return `true` if the given [type] is compatible with the argument types of
|
| - * all of the given [setters].
|
| + * TODO
|
| + *
|
| + * Return [FunctionType]s of the [overriddenElement] that [element] overrides.
|
| + * Return `null`, in case of type parameters inconsistency.
|
| + *
|
| + * Overridden elements must have the same number of generic type parameters
|
| + * as the target element, or none.
|
| + *
|
| + * If we do have generic type parameters on the element we're inferring,
|
| + * we must express its parameter and return types in terms of its own
|
| + * parameters. For example, given `m<T>(t)` overriding `m<S>(S s)` we
|
| + * should infer this as `m<T>(T t)`.
|
| */
|
| - bool _isCompatible(DartType type, List<ExecutableElement> setters) {
|
| - for (ExecutableElement setter in setters) {
|
| - ParameterElement parameter = _getParameter(setter);
|
| - if (parameter != null && !typeSystem.isSubtypeOf(parameter.type, type)) {
|
| - return false;
|
| + FunctionType _toOverriddenFunctionType(
|
| + ExecutableElement element, ExecutableElement overriddenElement) {
|
| + List<DartType> typeFormals =
|
| + TypeParameterTypeImpl.getTypes(element.type.typeFormals);
|
| +
|
| + FunctionType overriddenType = overriddenElement.type;
|
| + if (overriddenType == null) {
|
| + // TODO(brianwilkerson) I think the overridden method should always have
|
| + // a type, but there appears to be a bug that causes it to sometimes be
|
| + // null, we guard against that case by not performing inference.
|
| + return null;
|
| + }
|
| + if (overriddenType.typeFormals.isNotEmpty) {
|
| + if (overriddenType.typeFormals.length != typeFormals.length) {
|
| + return null;
|
| }
|
| + overriddenType = overriddenType.instantiate(typeFormals);
|
| }
|
| - return true;
|
| + return overriddenType;
|
| }
|
|
|
| /**
|
| - * Return `true` if the list of [elements] contains only getters.
|
| + * Return [FunctionType]s of [overriddenElements] that override [element].
|
| + * Return the empty list, in case of type parameters inconsistency.
|
| */
|
| - bool _onlyGetters(List<ExecutableElement> elements) {
|
| - for (ExecutableElement element in elements) {
|
| - if (!(element is PropertyAccessorElement && element.isGetter)) {
|
| - return false;
|
| + 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);
|
| }
|
| - return true;
|
| + return overriddenTypes;
|
| }
|
|
|
| /**
|
| @@ -515,10 +554,8 @@ class VariableGatherer extends RecursiveAstVisitor {
|
| }
|
|
|
| Element element = nonAccessor(node.staticElement);
|
| - if (element is VariableElement) {
|
| - if (filter == null || filter(element)) {
|
| - results.add(element);
|
| - }
|
| + if (element is VariableElement && (filter == null || filter(element))) {
|
| + results.add(element);
|
| }
|
| }
|
| }
|
| @@ -530,6 +567,17 @@ class VariableGatherer extends RecursiveAstVisitor {
|
| 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);
|
| +}
|
| +
|
| +/**
|
| * The visitor for [isValidForTypeInference].
|
| */
|
| class _IsValidForTypeInferenceVisitor extends RecursiveAstVisitor {
|
|
|