Allow inferring property type from its getter

#KT-550 Fixed
This commit is contained in:
Denis Zharkov
2016-11-14 14:17:39 +03:00
parent 51a5bf9f7e
commit 6fca46a452
32 changed files with 460 additions and 38 deletions
@@ -678,7 +678,7 @@ class DeclarationsChecker(
trace.report(MUST_BE_INITIALIZED_OR_BE_ABSTRACT.on(property))
}
}
else if (property.typeReference == null) {
else if (noExplicitTypeOrGetterType(property)) {
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property))
}
if (backingFieldRequired && !inTrait && propertyDescriptor.isLateInit && !isUninitialized) {
@@ -690,6 +690,10 @@ class DeclarationsChecker(
}
}
private fun noExplicitTypeOrGetterType(property: KtProperty) =
property.typeReference == null
&& (property.getter == null || (property.getter!!.hasBlockBody() && property.getter!!.returnTypeReference == null))
fun checkFunction(function: KtNamedFunction, functionDescriptor: SimpleFunctionDescriptor) {
val typeParameterList = function.typeParameterList
val nameIdentifier = function.nameIdentifier
@@ -814,11 +814,18 @@ public class DescriptorResolver {
DescriptorFactory.createExtensionReceiverParameterForCallable(propertyDescriptor, receiverType);
LexicalScope scopeForInitializer = ScopeUtils.makeScopeForPropertyInitializer(scopeWithTypeParameters, propertyDescriptor);
KotlinType type = variableTypeAndInitializerResolver.resolveType(
KotlinType typeIfKnown = variableTypeAndInitializerResolver.resolveTypeNullable(
propertyDescriptor, scopeForInitializer,
property, dataFlowInfo, true, trace
);
PropertyGetterDescriptorImpl getter = resolvePropertyGetterDescriptor(
scopeWithTypeParameters, property, propertyDescriptor, annotationSplitter, trace, typeIfKnown);
KotlinType type = typeIfKnown != null ? typeIfKnown : getter.getReturnType();
assert type != null : "At least getter type must be initialized via resolvePropertyGetterDescriptor";
variableTypeAndInitializerResolver.setConstantForVariableIfNeeded(
propertyDescriptor, scopeForInitializer, property, dataFlowInfo, type, trace
);
@@ -826,8 +833,6 @@ public class DescriptorResolver {
propertyDescriptor.setType(type, typeParameterDescriptors, getDispatchReceiverParameterIfNeeded(containingDeclaration),
receiverDescriptor);
PropertyGetterDescriptorImpl getter = resolvePropertyGetterDescriptor(
scopeWithTypeParameters, property, propertyDescriptor, annotationSplitter, trace);
PropertySetterDescriptor setter = resolvePropertySetterDescriptor(
scopeWithTypeParameters, property, propertyDescriptor, annotationSplitter, trace);
@@ -963,10 +968,12 @@ public class DescriptorResolver {
@NotNull KtProperty property,
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull AnnotationSplitter annotationSplitter,
BindingTrace trace
BindingTrace trace,
@Nullable KotlinType propertyTypeIfKnown
) {
PropertyGetterDescriptorImpl getterDescriptor;
KtPropertyAccessor getter = property.getGetter();
KotlinType getterType;
if (getter != null) {
Annotations getterAnnotations = new CompositeAnnotations(CollectionsKt.listOf(
annotationSplitter.getAnnotationsForTarget(PROPERTY_GETTER),
@@ -980,33 +987,34 @@ public class DescriptorResolver {
property.hasModifier(KtTokens.INLINE_KEYWORD) || getter.hasModifier(KtTokens.INLINE_KEYWORD),
CallableMemberDescriptor.Kind.DECLARATION, null, KotlinSourceElementKt.toSourceElement(getter)
);
KotlinType returnType =
determineGetterReturnType(scopeWithTypeParameters, trace, getterDescriptor, getter, propertyDescriptor.getType());
getterDescriptor.initialize(returnType);
getterType = determineGetterReturnType(scopeWithTypeParameters, trace, getterDescriptor, getter, propertyTypeIfKnown);
trace.record(BindingContext.PROPERTY_ACCESSOR, getter, getterDescriptor);
}
else {
Annotations getterAnnotations = annotationSplitter.getAnnotationsForTarget(PROPERTY_GETTER);
getterDescriptor = DescriptorFactory.createGetter(propertyDescriptor, getterAnnotations, !property.hasDelegate(),
/* isExternal = */ false, property.hasModifier(KtTokens.INLINE_KEYWORD));
getterDescriptor.initialize(propertyDescriptor.getType());
getterType = propertyTypeIfKnown;
}
getterDescriptor.initialize(getterType != null ? getterType : VariableTypeAndInitializerResolver.STUB_FOR_PROPERTY_WITHOUT_TYPE);
return getterDescriptor;
}
@NotNull
@Nullable
private KotlinType determineGetterReturnType(
@NotNull LexicalScope scope,
@NotNull BindingTrace trace,
@NotNull PropertyGetterDescriptor getterDescriptor,
@NotNull KtPropertyAccessor getter,
@NotNull KotlinType propertyType
@Nullable KotlinType propertyTypeIfKnown
) {
KtTypeReference returnTypeReference = getter.getReturnTypeReference();
if (returnTypeReference != null) {
KotlinType explicitReturnType = typeResolver.resolveType(scope, returnTypeReference, trace, true);
if (!TypeUtils.equalTypes(explicitReturnType, propertyType)) {
trace.report(WRONG_GETTER_RETURN_TYPE.on(returnTypeReference, propertyType, explicitReturnType));
if (propertyTypeIfKnown != null && !TypeUtils.equalTypes(explicitReturnType, propertyTypeIfKnown)) {
trace.report(WRONG_GETTER_RETURN_TYPE.on(returnTypeReference, propertyTypeIfKnown, explicitReturnType));
}
return explicitReturnType;
}
@@ -1020,7 +1028,7 @@ public class DescriptorResolver {
return inferReturnTypeFromExpressionBody(trace, scope, DataFlowInfoFactory.EMPTY, getter, getterDescriptor);
}
return propertyType;
return propertyTypeIfKnown;
}
@NotNull
@@ -43,6 +43,10 @@ class VariableTypeAndInitializerResolver(
private val constantExpressionEvaluator: ConstantExpressionEvaluator,
private val delegatedPropertyResolver: DelegatedPropertyResolver
) {
companion object {
@JvmField
val STUB_FOR_PROPERTY_WITHOUT_TYPE = ErrorUtils.createErrorType("No type, no body")
}
fun resolveType(
variableDescriptor: VariableDescriptorWithInitializerImpl,
@@ -52,8 +56,24 @@ class VariableTypeAndInitializerResolver(
notLocal: Boolean,
trace: BindingTrace
): KotlinType {
val propertyTypeRef = variable.typeReference
resolveTypeNullable(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, notLocal, trace)?.let { return it }
if (!notLocal) {
trace.report(VARIABLE_WITH_NO_TYPE_NO_INITIALIZER.on(variable))
}
return STUB_FOR_PROPERTY_WITHOUT_TYPE
}
fun resolveTypeNullable(
variableDescriptor: VariableDescriptorWithInitializerImpl,
scopeForInitializer: LexicalScope,
variable: KtVariableDeclaration,
dataFlowInfo: DataFlowInfo,
notLocal: Boolean,
trace: BindingTrace
): KotlinType? {
val propertyTypeRef = variable.typeReference
return when {
propertyTypeRef != null -> typeResolver.resolveType(scopeForInitializer, propertyTypeRef, trace, true)
@@ -74,12 +94,7 @@ class VariableTypeAndInitializerResolver(
else -> resolveInitializerType(scopeForInitializer, variable.initializer!!, dataFlowInfo, trace)
}
else -> {
if (!notLocal) {
trace.report(VARIABLE_WITH_NO_TYPE_NO_INITIALIZER.on(variable))
}
ErrorUtils.createErrorType("No type, no body")
}
else -> null
}
}