diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java index f32231910bd..77c44157820 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -56,6 +56,7 @@ 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.FunctionsTypingVisitor; import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor; import java.util.*; @@ -80,6 +81,7 @@ public class DescriptorResolver { @NotNull private final ExpressionTypingServices expressionTypingServices; @NotNull private final OverloadChecker overloadChecker; @NotNull private final LanguageFeatureSettings languageFeatureSettings; + @NotNull private final FunctionsTypingVisitor functionsTypingVisitor; public DescriptorResolver( @NotNull AnnotationResolver annotationResolver, @@ -90,7 +92,8 @@ public class DescriptorResolver { @NotNull VariableTypeResolver variableTypeResolver, @NotNull ExpressionTypingServices expressionTypingServices, @NotNull OverloadChecker overloadChecker, - @NotNull LanguageFeatureSettings languageFeatureSettings + @NotNull LanguageFeatureSettings languageFeatureSettings, + @NotNull FunctionsTypingVisitor functionsTypingVisitor ) { this.annotationResolver = annotationResolver; this.builtIns = builtIns; @@ -101,6 +104,7 @@ public class DescriptorResolver { this.expressionTypingServices = expressionTypingServices; this.overloadChecker = overloadChecker; this.languageFeatureSettings = languageFeatureSettings; + this.functionsTypingVisitor = functionsTypingVisitor; } public List resolveSupertypes( @@ -1049,7 +1053,7 @@ public class DescriptorResolver { } @NotNull - /*package*/ static DeferredType inferReturnTypeFromExpressionBody( + /*package*/ DeferredType inferReturnTypeFromExpressionBody( @NotNull StorageManager storageManager, @NotNull final ExpressionTypingServices expressionTypingServices, @NotNull final BindingTrace trace, @@ -1064,7 +1068,9 @@ public class DescriptorResolver { PreliminaryDeclarationVisitor.Companion.createForDeclaration(function, trace); KotlinType type = expressionTypingServices.getBodyExpressionType( trace, scope, dataFlowInfo, function, functionDescriptor); - return transformAnonymousTypeIfNeeded(functionDescriptor, function, type, trace); + KotlinType result = transformAnonymousTypeIfNeeded(functionDescriptor, function, type, trace); + functionsTypingVisitor.checkTypesForReturnStatements(function, trace, result); + return result; } }); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt index 02076601808..744a51d35e3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt @@ -125,7 +125,7 @@ class FunctionDescriptorResolver( builtIns.unitType } else if (function.hasBody()) { - inferReturnTypeFromExpressionBody(storageManager, expressionTypingServices, trace, scope, + descriptorResolver.inferReturnTypeFromExpressionBody(storageManager, expressionTypingServices, trace, scope, dataFlowInfo, function, functionDescriptor) } else { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FunctionsTypingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FunctionsTypingVisitor.kt index 8b99bdb1191..710093e7b61 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FunctionsTypingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FunctionsTypingVisitor.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.diagnostics.DiagnosticUtils +import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries @@ -39,9 +40,12 @@ import org.jetbrains.kotlin.types.CommonSupertypes import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils.* +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.expressions.CoercionStrategy.COERCION_TO_UNIT import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo +import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.utils.addIfNotNull +import java.util.* internal class FunctionsTypingVisitor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) { @@ -264,4 +268,45 @@ internal class FunctionsTypingVisitor(facade: ExpressionTypingInternals) : Expre it.getTargetLabel()?.let { trace.get(BindingContext.LABEL_TARGET, it) } == functionLiteral } } + + fun checkTypesForReturnStatements(function: KtDeclarationWithBody, trace: BindingTrace, actualReturnType: KotlinType) { + if (function.hasBlockBody()) return + if ((function !is KtNamedFunction || function.typeReference != null) + && (function !is KtPropertyAccessor || function.returnTypeReference == null)) return + + val bodyExpression = function.bodyExpression ?: return + val returns = ArrayList() + + // data == false means, that we inside other function, so ours return should be with label + bodyExpression.accept(object : KtTreeVisitor() { + override fun visitReturnExpression(expression: KtReturnExpression, data: Boolean): Void? { + val label = expression.getTargetLabel() + if ((label != null && trace[BindingContext.LABEL_TARGET, label] == function) + || (label == null && data) + ) { + returns.add(expression) + } + return super.visitReturnExpression(expression, data) + } + + override fun visitNamedFunction(function: KtNamedFunction, data: Boolean): Void? { + return super.visitNamedFunction(function, false) + } + }, true) + + for (returnForCheck in returns) { + val expression = returnForCheck.returnedExpression + if (expression == null) { + if (!actualReturnType.isUnit()) { + trace.report(Errors.RETURN_TYPE_MISMATCH.on(returnForCheck, actualReturnType)) + } + continue + } + + val expressionType = trace.getType(expression) ?: continue + if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(expressionType, actualReturnType)) { + trace.report(Errors.TYPE_MISMATCH.on(expression, expressionType, actualReturnType)) + } + } + } } diff --git a/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.kt b/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.kt index ec1deb4e982..aa9c231f221 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.kt @@ -32,15 +32,15 @@ fun testReturnFromAnonFun() = fun testReturn1() = run { - return if (true) 42 - else println() + return if (true) 42 + else println() } fun testReturn2() = run { - return if (true) 42 + return if (true) 42 else if (true) 42 - else println() + else println() } fun testUsage1() = diff --git a/compiler/testData/diagnostics/tests/controlStructures/kt10717.kt b/compiler/testData/diagnostics/tests/controlStructures/kt10717.kt new file mode 100644 index 00000000000..130a53c351c --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlStructures/kt10717.kt @@ -0,0 +1,47 @@ +// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNREACHABLE_CODE -UNUSED_PARAMETER -RETURN_NOT_ALLOWED + +fun test1() = run { + return "OK" +} + +fun test2() = run { + fun local(): String { + return "" + } + return "" +} + +inline fun Iterable.map(transform: (T) -> R): List = null!! +fun test3(a: List, b: List) = a.map { + if (it.length == 3) return )!>null + if (it.length == 4) return )!>"" + if (it.length == 4) return )!>5 + if (it.length == 4) return b + 1 +} + +fun test4() = run { + fun test5() { + return + + return@test4 + + return return@test4 + + return fun() { return; return@test4 "" } + } + + return + 3 +} + +val foo: Int + get() = run { + if (true) return "" + + return + } + +fun test(): Int = run { + return "" +} diff --git a/compiler/testData/diagnostics/tests/controlStructures/kt10717.txt b/compiler/testData/diagnostics/tests/controlStructures/kt10717.txt new file mode 100644 index 00000000000..abec55388c4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlStructures/kt10717.txt @@ -0,0 +1,9 @@ +package + +public val foo: kotlin.Int +public fun test(): kotlin.Int +public fun test1(): kotlin.Nothing +public fun test2(): kotlin.Nothing +public fun test3(/*0*/ a: kotlin.collections.List, /*1*/ b: kotlin.collections.List): kotlin.collections.List +public fun test4(): kotlin.Int +public inline fun kotlin.collections.Iterable.map(/*0*/ transform: (T) -> R): kotlin.collections.List diff --git a/compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt b/compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt index fc2e88283be..f97dbc79898 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/whenToAnyDiscriminatingUsages.kt @@ -26,22 +26,22 @@ fun testResultOfLambda2() = fun testReturn1() = run { - return when { + return when { true -> 42 else -> println() - } + } } fun testReturn2() = run { - return when { + return when { true -> 42 else -> when { true -> 42 else -> println() } - } + } } fun testUsage1() = diff --git a/compiler/testData/diagnostics/tests/implicitNothing.kt b/compiler/testData/diagnostics/tests/implicitNothing.kt index 95ea8d1b5a4..40a2f07d3b9 100644 --- a/compiler/testData/diagnostics/tests/implicitNothing.kt +++ b/compiler/testData/diagnostics/tests/implicitNothing.kt @@ -17,7 +17,7 @@ fun check() { val x = null!! } -fun nonLocalReturn() = run { return } +fun nonLocalReturn() = run { return } class Klass { fun bar() = null!! diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index f4bf35dffe5..a0dc869f8cd 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3762,6 +3762,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("kt10717.kt") + public void testKt10717() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/kt10717.kt"); + doTest(fileName); + } + @TestMetadata("kt1075.kt") public void testKt1075() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/kt1075.kt");