diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index 55d06d074fb..f5ed15ce852 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -14357,6 +14357,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt"); } + @Test + @TestMetadata("LambdaReturnTypeIsUnitIfImplicitReturnExists.kt") + public void testLambdaReturnTypeIsUnitIfImplicitReturnExists() throws Exception { + runTest("compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt"); + } + @Test @TestMetadata("LambdaWithParameter.kt") public void testLambdaWithParameter() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 8e8d0e92bf9..5b49e315a0e 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -14357,6 +14357,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt"); } + @Test + @TestMetadata("LambdaReturnTypeIsUnitIfImplicitReturnExists.kt") + public void testLambdaReturnTypeIsUnitIfImplicitReturnExists() throws Exception { + runTest("compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt"); + } + @Test @TestMetadata("LambdaWithParameter.kt") public void testLambdaWithParameter() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 302360c1f1f..ef58bc7a351 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -14351,6 +14351,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt"); } + @Test + @TestMetadata("LambdaReturnTypeIsUnitIfImplicitReturnExists.kt") + public void testLambdaReturnTypeIsUnitIfImplicitReturnExists() throws Exception { + runTest("compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt"); + } + @Test @TestMetadata("LambdaWithParameter.kt") public void testLambdaWithParameter() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index e119ee7667e..7a504e49a7f 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -14357,6 +14357,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt"); } + @Test + @TestMetadata("LambdaReturnTypeIsUnitIfImplicitReturnExists.kt") + public void testLambdaReturnTypeIsUnitIfImplicitReturnExists() throws Exception { + runTest("compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt"); + } + @Test @TestMetadata("LambdaWithParameter.kt") public void testLambdaWithParameter() throws Exception { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt index 093440beb67..a09248e80a7 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt @@ -9,7 +9,6 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtRealSourceElementKind import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.reportOn -import org.jetbrains.kotlin.fir.analysis.cfa.util.previousCfgNodes import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.hasExplicitReturnType import org.jetbrains.kotlin.fir.analysis.checkers.isSubtypeForTypeMismatch @@ -21,7 +20,6 @@ import org.jetbrains.kotlin.fir.expressions.FirReturnExpression import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression import org.jetbrains.kotlin.fir.expressions.FirWhenExpression import org.jetbrains.kotlin.fir.expressions.isExhaustive -import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph import org.jetbrains.kotlin.fir.types.* object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() { @@ -91,19 +89,13 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() { } } } else if (resultExpression.source?.kind is KtFakeSourceElementKind.ImplicitUnit && - !functionReturnType.lowerBoundIfFlexible().isUnit && - shouldCheckMismatchForAnonymousFunction(targetElement, expression) + !functionReturnType.lowerBoundIfFlexible().isUnit ) { // Disallow cases like // fun foo(): Any { return } // Allow cases like // fun foo(): Unit { return } // fun foo() { return Unit } - // But ignore anonymous functions without explicit returns, the following code is valid (where type of parameter R is being inferenced): - // run { - // if (flag) return@run - // "str" - // } // If type parameter is specified explicitly, checking is performed in the branch above and RETURN_TYPE_MISMATCH is reported reporter.reportOn( resultExpression.source, @@ -116,15 +108,5 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() { ) } } - - private fun shouldCheckMismatchForAnonymousFunction(targetElement: FirFunction, expression: FirReturnExpression): Boolean { - if (targetElement !is FirAnonymousFunction || !targetElement.isLambda) return true - val cfgNodes = targetElement.controlFlowGraphReference?.controlFlowGraph?.exitNode?.previousCfgNodes ?: return true - // Check if any return expression other than the current is explicit and not Unit - return cfgNodes.any { - val fir = it.fir - if (fir === expression) false else fir is FirReturnExpression && !fir.result.resolvedType.isUnit - } - } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt index fccf2a0aa31..ca34f1b0813 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.buildSamConversionExpression -import org.jetbrains.kotlin.fir.expressions.impl.FirPropertyAccessExpressionImpl import org.jetbrains.kotlin.fir.references.FirNamedReference import org.jetbrains.kotlin.fir.references.builder.buildResolvedCallableReference import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference @@ -43,7 +42,6 @@ import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer import org.jetbrains.kotlin.fir.visitors.FirTransformer import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.resolve.calls.inference.model.InferredEmptyIntersection -import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability import org.jetbrains.kotlin.resolve.calls.tower.isSuccess import org.jetbrains.kotlin.types.TypeApproximatorConfiguration import org.jetbrains.kotlin.types.Variance @@ -707,6 +705,8 @@ class FirCallCompletionResultsWriterTransformer( val initialReturnType = anonymousFunction.returnTypeRef.coneTypeSafe() val expectedReturnType = initialReturnType?.let { finallySubstituteOrSelf(it) } + ?: runIf(returnExpressions.any { it.source?.kind is KtFakeSourceElementKind.ImplicitUnit.Return }) + { session.builtinTypes.unitType.coneType } ?: expectedType?.returnType(session) as? ConeClassLikeType ?: (data as? ExpectedArgumentType.ArgumentsMap)?.lambdasReturnTypes?.get(anonymousFunction) diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt new file mode 100644 index 00000000000..fc6ff9cf82a --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt @@ -0,0 +1,22 @@ +// FIR_IDENTICAL +// ISSUE: KT-63563 + +fun foo(x: () -> Any) = x() +fun foo2(x: () -> Unit) = x() +fun foo3(x: () -> T): T = x() + +fun main2() { + foo { + if ("0".hashCode() == 42) return@foo + "" + } + foo2 { + if ("1".hashCode() == 42) return@foo2 + "" + } + foo3 { // Infer T to Unit -> no error + if ("2".hashCode() == 42) return@foo3 + "" + } +} + diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.fir.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.fir.kt deleted file mode 100644 index ed8325c002e..00000000000 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.fir.kt +++ /dev/null @@ -1,6 +0,0 @@ -fun test(a: Int) { - run f@{ - if (a > 0) return@f - else return@f 1 - } -} diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.kt index 758b5b2a188..2b2faba8d7f 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnNoCoercionToUnit.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL fun test(a: Int) { run f@{ if (a > 0) return@f diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.fir.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.fir.kt deleted file mode 100644 index 47b2f719cb8..00000000000 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.fir.kt +++ /dev/null @@ -1,20 +0,0 @@ -// NI_EXPECTED_FILE -val flag = true - -// type of a was checked by txt -val a = run { // () -> Unit - return@run -} - -// Unit -val b = run { - if (flag) return@run - 5 -} - -// Unit -val c = run { - if (flag) return@run - - return@run 4 -} diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.kt index 61206d5f67a..a725d97def9 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // NI_EXPECTED_FILE val flag = true diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 219ca296ba3..fd66c4ab443 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -14357,6 +14357,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt"); } + @Test + @TestMetadata("LambdaReturnTypeIsUnitIfImplicitReturnExists.kt") + public void testLambdaReturnTypeIsUnitIfImplicitReturnExists() throws Exception { + runTest("compiler/testData/diagnostics/tests/functionLiterals/return/LambdaReturnTypeIsUnitIfImplicitReturnExists.kt"); + } + @Test @TestMetadata("LambdaWithParameter.kt") public void testLambdaWithParameter() throws Exception {