diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 1f833a5b7a2..4fbf4e210d4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -355,7 +355,7 @@ public interface Errors { DiagnosticFactory2.create(ERROR, DECLARATION_NAME); - DiagnosticFactory1> AMBIGUOUS_ANONYMOUS_TYPE_INFERRED = + DiagnosticFactory1> AMBIGUOUS_ANONYMOUS_TYPE_INFERRED = DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE); // Property-specific diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java index 3e6f61b206e..5b0c63beb3f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -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 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() { + @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, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt index 1346290e303..cea8591eacc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt @@ -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") diff --git a/compiler/testData/diagnostics/tests/RecursiveGetter.kt b/compiler/testData/diagnostics/tests/RecursiveGetter.kt new file mode 100644 index 00000000000..4acf31b0295 --- /dev/null +++ b/compiler/testData/diagnostics/tests/RecursiveGetter.kt @@ -0,0 +1,3 @@ +class A { + val a get() = a +} diff --git a/compiler/testData/diagnostics/tests/RecursiveGetter.txt b/compiler/testData/diagnostics/tests/RecursiveGetter.txt new file mode 100644 index 00000000000..0650ad60855 --- /dev/null +++ b/compiler/testData/diagnostics/tests/RecursiveGetter.txt @@ -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 +} diff --git a/compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.kt b/compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.kt index 5cd229e471a..34032f98091 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.kt @@ -24,6 +24,9 @@ class Foo { public val publicProperty = object : MyClass(), MyTrait {} + val propertyWithGetter + get() = object: MyClass(), MyTrait {} + private fun privateFunction() = object : MyClass(), MyTrait {} diff --git a/compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.txt b/compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.txt index 72461f5e466..d2973089870 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.txt +++ b/compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.txt @@ -16,6 +16,7 @@ public final class Foo { internal final val internal2Property: Foo.internal2Property. public final val internalProperty: Foo.internalProperty. private final val privateProperty: Foo.privateProperty. + public final val propertyWithGetter: [ERROR : No type, no body] protected final val protectedProperty: Foo.protectedProperty. public final val publicProperty: Foo.publicProperty. public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 6dbd387e838..a11f4e00823 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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"); diff --git a/idea/testData/quickfix/typeAddition/propertyWithGetterWithBlockBody.kt b/idea/testData/quickfix/typeAddition/propertyWithGetterWithBlockBody.kt new file mode 100644 index 00000000000..a68610f40ba --- /dev/null +++ b/idea/testData/quickfix/typeAddition/propertyWithGetterWithBlockBody.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 x + get() { + return 3.14 + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeAddition/propertyWithGetterWithInferredType.kt b/idea/testData/quickfix/typeAddition/propertyWithGetterWithInferredType.kt new file mode 100644 index 00000000000..360deb7bdf3 --- /dev/null +++ b/idea/testData/quickfix/typeAddition/propertyWithGetterWithInferredType.kt @@ -0,0 +1,6 @@ +// "Specify type explicitly" "true" + +class My { + val x + get() = "abc" +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeAddition/propertyWithGetterWithInferredType.kt.after b/idea/testData/quickfix/typeAddition/propertyWithGetterWithInferredType.kt.after new file mode 100644 index 00000000000..fc45de2a9f7 --- /dev/null +++ b/idea/testData/quickfix/typeAddition/propertyWithGetterWithInferredType.kt.after @@ -0,0 +1,6 @@ +// "Specify type explicitly" "true" + +class My { + val x: String + get() = "abc" +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeAddition/propertyWithRecursiveGetter.kt b/idea/testData/quickfix/typeAddition/propertyWithRecursiveGetter.kt new file mode 100644 index 00000000000..168a399aab7 --- /dev/null +++ b/idea/testData/quickfix/typeAddition/propertyWithRecursiveGetter.kt @@ -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 +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 9c7f4e63fc3..bac306d2728 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -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");