KT-9498 extension: now type can be inferred for getter with expression body

This commit is contained in:
Mikhail Glukhikh
2016-02-17 13:09:20 +03:00
parent 405c21a17e
commit 883e2e4d2b
13 changed files with 108 additions and 9 deletions
@@ -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
}
@@ -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 {}
@@ -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"
}
@@ -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");