blob: d0e19d89c202d18f4390eb68b5344e6129b3a9e7 [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:collection';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
import 'package:analyzer/src/summary/format.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:analyzer/src/summary2/lazy_ast.dart';
/**
* 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;
}
/**
* A function that returns `true` if the given [element] passes the filter.
*/
typedef bool VariableFilter(VariableElement element);
/**
* An object used to infer the type of instance fields and the return types of
* instance methods within a single compilation unit.
*/
class InstanceMemberInferrer {
final TypeProvider typeProvider;
final InheritanceManager3 inheritance;
final Set<ClassElement> elementsBeingInferred = new HashSet<ClassElement>();
InterfaceType interfaceType;
/**
* Initialize a newly create inferrer.
*/
InstanceMemberInferrer(this.typeProvider, this.inheritance);
/**
* Infer type information for all of the instance members in the given
* compilation [unit].
*/
void inferCompilationUnit(CompilationUnitElement unit) {
_inferClasses(unit.mixins);
_inferClasses(unit.types);
}
/**
* Return `true` if the elements corresponding to the [elements] have the same
* kind as the [element].
*/
bool _allSameElementKind(
ExecutableElement element, List<ExecutableElement> elements) {
var elementKind = element.kind;
for (int i = 0; i < elements.length; i++) {
if (elements[i].kind != elementKind) {
return false;
}
}
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.getOverridden(
interfaceType,
new Name(accessor.library.source.uri, name),
);
List<ExecutableElement> overriddenSetters;
if (overriddenGetters == null || !accessor.variable.isFinal) {
overriddenSetters = inheritance.getOverridden(
interfaceType,
new 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>[]
..addAll(overriddenGetters)
..addAll(overriddenSetters);
}
bool isCovariant = false;
DartType impliedType;
for (ExecutableElement overriddenElement in overriddenElements) {
var overriddenElementKind = overriddenElement.kind;
if (overriddenElement == null) {
return new _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 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
* [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) {
DartType parameterType = null;
int length = overriddenTypes.length;
for (int i = 0; i < length; i++) {
ParameterElement matchingParameter = _getCorrespondingParameter(
parameter, index, overriddenTypes[i].parameters);
DartType type = matchingParameter?.type ?? typeProvider.dynamicType;
if (parameterType == null) {
if (type is FunctionType &&
type.element != null &&
type.element is! TypeDefiningElement &&
type.element.enclosingElement is! TypeDefiningElement) {
// The resulting parameter's type element has an `enclosingElement` of
// the overridden parameter. Change it to the overriding parameter.
parameterType = new FunctionTypeImpl.fresh(type, force: true);
(parameterType.element as ElementImpl).enclosingElement = parameter;
// TODO(mfairhurst) handle cases where non-functions contain functions
// See test_inferredType_parameter_genericFunctionType_asTypeArgument
} else {
parameterType = type;
}
} else if (parameterType != type) {
if (parameter is ParameterElementImpl && parameter.linkedNode != null) {
LazyAst.setTypeInferenceError(
parameter.linkedNode,
TopLevelInferenceErrorBuilder(
kind: TopLevelInferenceErrorKind.overrideConflictParameterType,
),
);
}
return typeProvider.dynamicType;
}
}
return parameterType ?? typeProvider.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) {
DartType returnType = null;
for (DartType type in overriddenReturnTypes) {
if (type == null) {
type = typeProvider.dynamicType;
}
if (returnType == null) {
returnType = type;
} else if (returnType != type) {
return typeProvider.dynamicType;
}
}
return returnType ?? typeProvider.dynamicType;
}
/**
* Given a method, return the parameter in the method that corresponds to the
* given [parameter]. If the parameter is positional, then
* it appears at the given [index] in its enclosing element's list of
* parameters.
*/
ParameterElement _getCorrespondingParameter(ParameterElement parameter,
int index, List<ParameterElement> methodParameters) {
//
// Find the corresponding parameter.
//
if (parameter.isNamed) {
//
// If we're looking for a named parameter, only a named parameter with
// the same name will be matched.
//
return methodParameters.lastWhere(
(ParameterElement methodParameter) =>
methodParameter.isNamed && methodParameter.name == parameter.name,
orElse: () => null);
}
//
// If we're looking for a positional parameter we ignore the difference
// between required and optional parameters.
//
if (index < methodParameters.length) {
var matchingParameter = methodParameters[index];
if (!matchingParameter.isNamed) {
return matchingParameter;
}
}
return null;
}
/**
* 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) {
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;
}
}
setFieldType(element.variable, typeResult.type);
}
/**
* Infer type information for all of the instance members in the given
* [classElement].
*/
void _inferClass(ClassElement classElement) {
if (classElement is ClassElementImpl) {
if (classElement.hasBeenInferred) {
return;
}
if (!elementsBeingInferred.add(classElement)) {
// We have found a circularity in the class hierarchy. For now we just
// stop trying to infer any type information for any classes that
// inherit from any class in the cycle. We could potentially limit the
// algorithm to only not inferring types in the classes in the cycle,
// but it isn't clear that the results would be significantly better.
throw new _CycleException();
}
try {
//
// Ensure that all of instance members in the supertypes have had types
// inferred for them.
//
_inferType(classElement.supertype);
classElement.mixins.forEach(_inferType);
classElement.interfaces.forEach(_inferType);
classElement.superclassConstraints.forEach(_inferType);
//
// Then infer the types for the members.
//
this.interfaceType = classElement.thisType;
for (FieldElement field in classElement.fields) {
_inferField(field);
}
for (PropertyAccessorElement accessor in classElement.accessors) {
_inferAccessor(accessor);
}
for (MethodElement method in classElement.methods) {
_inferExecutable(method);
}
//
// Infer initializing formal parameter types. This must happen after
// field types are inferred.
//
classElement.constructors.forEach(_inferConstructorFieldFormals);
classElement.hasBeenInferred = true;
} finally {
elementsBeingInferred.remove(classElement);
}
}
}
void _inferClasses(List<ClassElement> elements) {
for (ClassElement element in elements) {
try {
_inferClass(element);
} on _CycleException {
// This is a short circuit return to prevent types that inherit from
// types containing a circular reference from being inferred.
}
}
}
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;
}
}
}
}
/**
* If the given [element] represents a non-synthetic instance method,
* getter or setter, infer the return type and any parameter type(s) where
* they were not provided.
*/
void _inferExecutable(ExecutableElement element) {
if (element.isSynthetic || element.isStatic) {
return;
}
// TODO(scheglov) If no implicit types, don't ask inherited.
List<ExecutableElement> overriddenElements = inheritance.getOverridden(
interfaceType,
new Name(element.library.source.uri, element.name),
);
if (overriddenElements == null ||
!_allSameElementKind(element, overriddenElements)) {
return;
}
List<FunctionType> overriddenTypes =
_toOverriddenFunctionTypes(element, overriddenElements);
if (overriddenTypes.isEmpty) {
return;
}
//
// Infer the return type.
//
if (element.hasImplicitReturnType && element.displayName != '[]=') {
(element as ExecutableElementImpl).returnType =
_computeReturnType(overriddenTypes.map((t) => t.returnType));
if (element is PropertyAccessorElement) {
_updateSyntheticVariableType(element);
}
}
//
// Infer the parameter types.
//
List<ParameterElement> parameters = element.parameters;
int length = parameters.length;
for (int i = 0; i < length; ++i) {
ParameterElement parameter = parameters[i];
if (parameter is ParameterElementImpl) {
_inferParameterCovariance(parameter, i, overriddenTypes);
if (parameter.hasImplicitType) {
parameter.type = _computeParameterType(parameter, i, overriddenTypes);
if (element is PropertyAccessorElement) {
_updateSyntheticVariableType(element);
}
}
}
}
}
/**
* 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 field) {
if (field.isSynthetic || field.isStatic) {
return;
}
_FieldOverrideInferenceResult typeResult =
_computeFieldOverrideType(field.getter);
if (typeResult.isError) {
if (field is FieldElementImpl && 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 = typeProvider.dynamicType;
}
setFieldType(field, 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) {
var param = _getCorrespondingParameter(parameter, index, f.parameters);
return param != null && param.isCovariant;
});
}
/**
* Infer type information for all of the instance members in the given
* interface [type].
*/
void _inferType(InterfaceType type) {
if (type != null) {
ClassElement element = type.element;
if (element != null) {
_inferClass(element);
}
}
}
/**
* Return the [FunctionType] of the [overriddenElement] that [element]
* overrides. Return `null`, in case of type parameters inconsistency.
*
* The overridden element 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)`.
*/
FunctionType _toOverriddenFunctionType(
ExecutableElement element, ExecutableElement overriddenElement) {
List<DartType> typeFormals =
TypeParameterTypeImpl.getTypes(element.type.typeFormals);
FunctionType overriddenType = overriddenElement.type;
if (overriddenType.typeFormals.isNotEmpty) {
if (overriddenType.typeFormals.length != typeFormals.length) {
return null;
}
overriddenType = overriddenType.instantiate(typeFormals);
}
return overriddenType;
}
/**
* 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);
}
return overriddenTypes;
}
/**
* If the given [element] is a non-synthetic getter or setter, update its
* synthetic variable's type to match the getter's return type, or if no
* corresponding getter exists, use the setter's parameter type.
*
* In general, the type of the synthetic variable should not be used, because
* getters and setters are independent methods. But this logic matches what
* `TypeResolverVisitor.visitMethodDeclaration` would fill in there.
*/
void _updateSyntheticVariableType(PropertyAccessorElement element) {
assert(!element.isSynthetic);
PropertyAccessorElement getter = element;
if (element.isSetter) {
// See if we can find any getter.
getter = element.correspondingGetter;
}
DartType newType;
if (getter != null) {
newType = getter.returnType;
} else if (element.isSetter && element.parameters.isNotEmpty) {
newType = element.parameters[0].type;
}
if (newType != null) {
(element.variable as VariableElementImpl).type = newType;
}
}
}
/**
* A visitor that will gather all of the variables referenced within a given
* AST structure. The collection can be restricted to contain only those
* variables that pass a specified filter.
*/
class VariableGatherer extends RecursiveAstVisitor {
/**
* The filter used to limit which variables are gathered, or `null` if no
* filtering is to be performed.
*/
final VariableFilter filter;
/**
* The variables that were found.
*/
final Set<VariableElement> results = new HashSet<VariableElement>();
/**
* Initialize a newly created gatherer to gather all of the variables that
* pass the given [filter] (or all variables if no filter is provided).
*/
VariableGatherer([this.filter = null]);
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
if (!node.inDeclarationContext()) {
Element nonAccessor(Element element) {
if (element is PropertyAccessorElement && element.isSynthetic) {
return element.variable;
}
return element;
}
Element element = nonAccessor(node.staticElement);
if (element is VariableElement && (filter == null || filter(element))) {
results.add(element);
}
}
}
}
/**
* 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);
}