KT-9498 extension: now type can be inferred for getter with expression body
This commit is contained in:
@@ -355,7 +355,7 @@ public interface Errors {
|
||||
DiagnosticFactory2.create(ERROR, DECLARATION_NAME);
|
||||
|
||||
|
||||
DiagnosticFactory1<KtNamedDeclaration, Collection<KotlinType>> AMBIGUOUS_ANONYMOUS_TYPE_INFERRED =
|
||||
DiagnosticFactory1<KtDeclaration, Collection<KotlinType>> AMBIGUOUS_ANONYMOUS_TYPE_INFERRED =
|
||||
DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE);
|
||||
|
||||
// Property-specific
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory;
|
||||
import org.jetbrains.kotlin.resolve.dataClassUtils.DataClassUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||
@@ -53,6 +54,8 @@ import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt;
|
||||
import org.jetbrains.kotlin.storage.StorageManager;
|
||||
import org.jetbrains.kotlin.types.*;
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices;
|
||||
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -74,6 +77,7 @@ public class DescriptorResolver {
|
||||
@NotNull private final KotlinBuiltIns builtIns;
|
||||
@NotNull private final SupertypeLoopChecker supertypeLoopsResolver;
|
||||
@NotNull private final VariableTypeResolver variableTypeResolver;
|
||||
@NotNull private final ExpressionTypingServices expressionTypingServices;
|
||||
|
||||
public DescriptorResolver(
|
||||
@NotNull AnnotationResolver annotationResolver,
|
||||
@@ -81,7 +85,8 @@ public class DescriptorResolver {
|
||||
@NotNull StorageManager storageManager,
|
||||
@NotNull TypeResolver typeResolver,
|
||||
@NotNull SupertypeLoopChecker supertypeLoopsResolver,
|
||||
@NotNull VariableTypeResolver variableTypeResolver
|
||||
@NotNull VariableTypeResolver variableTypeResolver,
|
||||
@NotNull ExpressionTypingServices expressionTypingServices
|
||||
) {
|
||||
this.annotationResolver = annotationResolver;
|
||||
this.builtIns = builtIns;
|
||||
@@ -89,6 +94,7 @@ public class DescriptorResolver {
|
||||
this.typeResolver = typeResolver;
|
||||
this.supertypeLoopsResolver = supertypeLoopsResolver;
|
||||
this.variableTypeResolver = variableTypeResolver;
|
||||
this.expressionTypingServices = expressionTypingServices;
|
||||
}
|
||||
|
||||
public List<KotlinType> resolveSupertypes(
|
||||
@@ -774,7 +780,7 @@ public class DescriptorResolver {
|
||||
@Nullable
|
||||
/*package*/ static KotlinType transformAnonymousTypeIfNeeded(
|
||||
@NotNull DeclarationDescriptorWithVisibility descriptor,
|
||||
@NotNull KtNamedDeclaration declaration,
|
||||
@NotNull KtDeclaration declaration,
|
||||
@NotNull KotlinType type,
|
||||
@NotNull BindingTrace trace
|
||||
) {
|
||||
@@ -897,7 +903,7 @@ public class DescriptorResolver {
|
||||
KtTypeReference returnTypeReference = getter.getReturnTypeReference();
|
||||
if (returnTypeReference != null) {
|
||||
returnType = typeResolver.resolveType(scopeWithTypeParameters, returnTypeReference, trace, true);
|
||||
if (outType != null && !TypeUtils.equalTypes(returnType, outType)) {
|
||||
if (!TypeUtils.equalTypes(returnType, outType)) {
|
||||
trace.report(WRONG_GETTER_RETURN_TYPE.on(returnTypeReference, propertyDescriptor.getReturnType(), outType));
|
||||
}
|
||||
}
|
||||
@@ -908,6 +914,10 @@ public class DescriptorResolver {
|
||||
getter.hasBody(), false, getter.hasModifier(EXTERNAL_KEYWORD),
|
||||
CallableMemberDescriptor.Kind.DECLARATION, null, KotlinSourceElementKt
|
||||
.toSourceElement(getter));
|
||||
if (returnType.isError() && !getter.hasBlockBody() && getter.hasBody()) {
|
||||
returnType = inferReturnTypeFromExpressionBody(storageManager, expressionTypingServices, trace, scopeWithTypeParameters,
|
||||
DataFlowInfoFactory.EMPTY, getter, getterDescriptor);
|
||||
}
|
||||
getterDescriptor.initialize(returnType);
|
||||
trace.record(BindingContext.PROPERTY_ACCESSOR, getter, getterDescriptor);
|
||||
}
|
||||
@@ -920,6 +930,27 @@ public class DescriptorResolver {
|
||||
return getterDescriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
/*package*/ static DeferredType inferReturnTypeFromExpressionBody(
|
||||
@NotNull StorageManager storageManager,
|
||||
@NotNull final ExpressionTypingServices expressionTypingServices,
|
||||
@NotNull final BindingTrace trace,
|
||||
@NotNull final LexicalScope scope,
|
||||
@NotNull final DataFlowInfo dataFlowInfo,
|
||||
@NotNull final KtDeclarationWithBody function,
|
||||
@NotNull final FunctionDescriptor functionDescriptor
|
||||
) {
|
||||
return DeferredType.createRecursionIntolerant(storageManager, trace, new Function0<KotlinType>() {
|
||||
@Override
|
||||
public KotlinType invoke() {
|
||||
PreliminaryDeclarationVisitor.Companion.createForDeclaration(function, trace);
|
||||
KotlinType type = expressionTypingServices.getBodyExpressionType(
|
||||
trace, scope, dataFlowInfo, function, functionDescriptor);
|
||||
return transformAnonymousTypeIfNeeded(functionDescriptor, function, type, trace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PropertyDescriptor resolvePrimaryConstructorParameterToAProperty(
|
||||
@NotNull ClassDescriptor classDescriptor,
|
||||
|
||||
@@ -122,11 +122,8 @@ class FunctionDescriptorResolver(
|
||||
builtIns.unitType
|
||||
}
|
||||
else if (function.hasBody()) {
|
||||
DeferredType.createRecursionIntolerant(storageManager, trace) {
|
||||
PreliminaryDeclarationVisitor.createForDeclaration(function, trace);
|
||||
val type = expressionTypingServices.getBodyExpressionType(trace, scope, dataFlowInfo, function, functionDescriptor)
|
||||
transformAnonymousTypeIfNeeded(functionDescriptor, function, type, trace)
|
||||
}
|
||||
inferReturnTypeFromExpressionBody(storageManager, expressionTypingServices, trace, scope,
|
||||
dataFlowInfo, function, functionDescriptor)
|
||||
}
|
||||
else {
|
||||
ErrorUtils.createErrorType("No type, no body")
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
class A {
|
||||
<!PROPERTY_WITH_NO_TYPE_NO_INITIALIZER!>val a<!> get() = <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>a<!>
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package
|
||||
|
||||
public final class A {
|
||||
public constructor A()
|
||||
public final val a: [ERROR : No type, no body]
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+3
@@ -24,6 +24,9 @@ class Foo {
|
||||
|
||||
<!AMBIGUOUS_ANONYMOUS_TYPE_INFERRED!>public val <!EXPOSED_PROPERTY_TYPE!>publicProperty<!><!> = object : MyClass(), MyTrait {}
|
||||
|
||||
<!PROPERTY_WITH_NO_TYPE_NO_INITIALIZER!>val propertyWithGetter<!>
|
||||
<!AMBIGUOUS_ANONYMOUS_TYPE_INFERRED!>get()<!> = object: MyClass(), MyTrait {}
|
||||
|
||||
|
||||
private fun privateFunction() = object : MyClass(), MyTrait {}
|
||||
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ public final class Foo {
|
||||
internal final val internal2Property: Foo.internal2Property.<no name provided>
|
||||
public final val internalProperty: Foo.internalProperty.<no name provided>
|
||||
private final val privateProperty: Foo.privateProperty.<no name provided>
|
||||
public final val propertyWithGetter: [ERROR : No type, no body]
|
||||
protected final val protectedProperty: Foo.protectedProperty.<no name provided>
|
||||
public final val publicProperty: Foo.publicProperty.<no name provided>
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
|
||||
@@ -547,6 +547,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("RecursiveGetter.kt")
|
||||
public void testRecursiveGetter() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/RecursiveGetter.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("RecursiveResolve.kt")
|
||||
public void testRecursiveResolve() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/RecursiveResolve.kt");
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Specify type explicitly" "false"
|
||||
// ERROR: This property must either have a type annotation, be initialized or be delegated
|
||||
// ACTION: Convert member to extension
|
||||
// ACTION: Convert property to function
|
||||
// ACTION: Move to companion object
|
||||
|
||||
class My {
|
||||
val <caret>x
|
||||
get() {
|
||||
return 3.14
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Specify type explicitly" "true"
|
||||
|
||||
class My {
|
||||
val <caret>x
|
||||
get() = "abc"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Specify type explicitly" "true"
|
||||
|
||||
class My {
|
||||
val x: String
|
||||
get() = "abc"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Specify type explicitly" "false"
|
||||
// ERROR: This property must either have a type annotation, be initialized or be delegated
|
||||
|
||||
class A {
|
||||
val a
|
||||
get() = a
|
||||
}
|
||||
@@ -6775,12 +6775,30 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("propertyWithGetterWithBlockBody.kt")
|
||||
public void testPropertyWithGetterWithBlockBody() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeAddition/propertyWithGetterWithBlockBody.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("propertyWithGetterWithInferredType.kt")
|
||||
public void testPropertyWithGetterWithInferredType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeAddition/propertyWithGetterWithInferredType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("propertyWithGetterWithoutType.kt")
|
||||
public void testPropertyWithGetterWithoutType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeAddition/propertyWithGetterWithoutType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("propertyWithRecursiveGetter.kt")
|
||||
public void testPropertyWithRecursiveGetter() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeAddition/propertyWithRecursiveGetter.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("propertyWithSetterWithoutType.kt")
|
||||
public void testPropertyWithSetterWithoutType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeAddition/propertyWithSetterWithoutType.kt");
|
||||
|
||||
Reference in New Issue
Block a user