Compute return type for property getters more safely
Do not call `isError()` on property's type right after creation of
PropertyGetterDescriptor because the property has no getter yet (it's created
but not yet stored to the property, that happens a bit later), and `isError()`
leads to computation of the delegate type, which for delegated properties
performs some complex resolution (see `VariableTypeResolver#process`) which
relies on the fact that the property already has a getter.
Since the purpose of the original change (883e2e4d) was to support a quick fix
which would add the type to a property in an expression like "val x get() =
...", check the type (or initializer) presence in the PSI instead, this is
safer and is still suitable for the quick fix.
Also fix arguments to "wrong getter type" diagnostic: previously something
useless like "expected Int, actual Int" was reported
#KT-11809 Fixed
This commit is contained in:
@@ -897,16 +897,6 @@ public class DescriptorResolver {
|
|||||||
annotationSplitter.getAnnotationsForTarget(PROPERTY_GETTER),
|
annotationSplitter.getAnnotationsForTarget(PROPERTY_GETTER),
|
||||||
annotationResolver.resolveAnnotationsWithoutArguments(scopeWithTypeParameters, getter.getModifierList(), trace)));
|
annotationResolver.resolveAnnotationsWithoutArguments(scopeWithTypeParameters, getter.getModifierList(), trace)));
|
||||||
|
|
||||||
KotlinType outType = propertyDescriptor.getType();
|
|
||||||
KotlinType returnType = outType;
|
|
||||||
KtTypeReference returnTypeReference = getter.getReturnTypeReference();
|
|
||||||
if (returnTypeReference != null) {
|
|
||||||
returnType = typeResolver.resolveType(scopeWithTypeParameters, returnTypeReference, trace, true);
|
|
||||||
if (!TypeUtils.equalTypes(returnType, outType)) {
|
|
||||||
trace.report(WRONG_GETTER_RETURN_TYPE.on(returnTypeReference, propertyDescriptor.getReturnType(), outType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getterDescriptor = new PropertyGetterDescriptorImpl(
|
getterDescriptor = new PropertyGetterDescriptorImpl(
|
||||||
propertyDescriptor, getterAnnotations,
|
propertyDescriptor, getterAnnotations,
|
||||||
resolveModalityFromModifiers(getter, propertyDescriptor.getModality()),
|
resolveModalityFromModifiers(getter, propertyDescriptor.getModality()),
|
||||||
@@ -914,10 +904,8 @@ public class DescriptorResolver {
|
|||||||
/* isDefault = */ false, getter.hasModifier(EXTERNAL_KEYWORD),
|
/* isDefault = */ false, getter.hasModifier(EXTERNAL_KEYWORD),
|
||||||
CallableMemberDescriptor.Kind.DECLARATION, null, KotlinSourceElementKt.toSourceElement(getter)
|
CallableMemberDescriptor.Kind.DECLARATION, null, KotlinSourceElementKt.toSourceElement(getter)
|
||||||
);
|
);
|
||||||
if (returnType.isError() && !getter.hasBlockBody() && getter.hasBody()) {
|
KotlinType returnType =
|
||||||
returnType = inferReturnTypeFromExpressionBody(storageManager, expressionTypingServices, trace, scopeWithTypeParameters,
|
determineGetterReturnType(scopeWithTypeParameters, trace, getterDescriptor, getter, propertyDescriptor.getType());
|
||||||
DataFlowInfoFactory.EMPTY, getter, getterDescriptor);
|
|
||||||
}
|
|
||||||
getterDescriptor.initialize(returnType);
|
getterDescriptor.initialize(returnType);
|
||||||
trace.record(BindingContext.PROPERTY_ACCESSOR, getter, getterDescriptor);
|
trace.record(BindingContext.PROPERTY_ACCESSOR, getter, getterDescriptor);
|
||||||
}
|
}
|
||||||
@@ -930,6 +918,37 @@ public class DescriptorResolver {
|
|||||||
return getterDescriptor;
|
return getterDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private KotlinType determineGetterReturnType(
|
||||||
|
@NotNull LexicalScope scope,
|
||||||
|
@NotNull BindingTrace trace,
|
||||||
|
@NotNull PropertyGetterDescriptor getterDescriptor,
|
||||||
|
@NotNull KtPropertyAccessor getter,
|
||||||
|
@NotNull KotlinType propertyType
|
||||||
|
) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
return explicitReturnType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a property has no type specified in the PSI but the getter does (or has an initializer e.g. "val x get() = ..."),
|
||||||
|
// infer the correct type for the getter but leave the error type for the property.
|
||||||
|
// This is useful for an IDE quick fix which would add the type to the property
|
||||||
|
KtProperty property = getter.getProperty();
|
||||||
|
if (!property.hasDelegateExpressionOrInitializer() && property.getTypeReference() == null &&
|
||||||
|
getter.hasBody() && !getter.hasBlockBody()) {
|
||||||
|
return inferReturnTypeFromExpressionBody(
|
||||||
|
storageManager, expressionTypingServices, trace, scope, DataFlowInfoFactory.EMPTY, getter, getterDescriptor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return propertyType;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
/*package*/ static DeferredType inferReturnTypeFromExpressionBody(
|
/*package*/ static DeferredType inferReturnTypeFromExpressionBody(
|
||||||
@NotNull StorageManager storageManager,
|
@NotNull StorageManager storageManager,
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// KT-11809 Assertion error when delegated property has getter
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val p1 by this
|
||||||
|
get
|
||||||
|
|
||||||
|
var p2 by this
|
||||||
|
<!ACCESSOR_FOR_DELEGATED_PROPERTY!>get() = ""<!>
|
||||||
|
|
||||||
|
operator fun getValue(a: Any?, p: Any?) = ""
|
||||||
|
operator fun setValue(a: Any?, p: Any?, v: Any?) {}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public final class A {
|
||||||
|
public constructor A()
|
||||||
|
public final val p1: kotlin.String
|
||||||
|
public final var p2: kotlin.String
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final operator fun getValue(/*0*/ a: kotlin.Any?, /*1*/ p: kotlin.Any?): kotlin.String
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public final operator fun setValue(/*0*/ a: kotlin.Any?, /*1*/ p: kotlin.Any?, /*2*/ v: kotlin.Any?): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ class A() {
|
|||||||
field = <!TYPE_MISMATCH!>value<!>
|
field = <!TYPE_MISMATCH!>value<!>
|
||||||
}
|
}
|
||||||
val y: Int
|
val y: Int
|
||||||
get(): <!WRONG_GETTER_RETURN_TYPE!>String<!> = "s"
|
get(): <!WRONG_GETTER_RETURN_TYPE(Int; String)!>String<!> = "s"
|
||||||
val z: Int
|
val z: Int
|
||||||
get() {
|
get() {
|
||||||
return <!TYPE_MISMATCH!>"s"<!>
|
return <!TYPE_MISMATCH!>"s"<!>
|
||||||
|
|||||||
@@ -4767,6 +4767,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nonDefaultAccessors.kt")
|
||||||
|
public void testNonDefaultAccessors() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/delegatedProperty/nonDefaultAccessors.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("propertyDefferedType.kt")
|
@TestMetadata("propertyDefferedType.kt")
|
||||||
public void testPropertyDefferedType() throws Exception {
|
public void testPropertyDefferedType() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/delegatedProperty/propertyDefferedType.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/delegatedProperty/propertyDefferedType.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user