Allow inferring property type from its getter
#KT-550 Fixed
This commit is contained in:
@@ -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
|
||||
|
||||
+22
-7
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user