diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index d9754cb2fea..8e01e1056d8 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -12431,6 +12431,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/inline/recursion.kt"); } + @TestMetadata("returnedAnonymousObjects.kt") + public void testReturnedAnonymousObjects() throws Exception { + runTest("compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.kt"); + } + @TestMetadata("returns.kt") public void testReturns() throws Exception { runTest("compiler/testData/diagnostics/tests/inline/returns.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 288138a2762..96e38e08df0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -1129,6 +1129,7 @@ public interface Errors { DiagnosticFactory1 PROTECTED_CALL_FROM_PUBLIC_INLINE_ERROR = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 INVALID_DEFAULT_FUNCTIONAL_PARAMETER_FOR_INLINE = DiagnosticFactory2.create(ERROR); DiagnosticFactory2 NOT_SUPPORTED_INLINE_PARAMETER_IN_INLINE_PARAMETER_DEFAULT_VALUE = DiagnosticFactory2.create(ERROR); + DiagnosticFactory0 PRIVATE_INLINE_FUNCTIONS_RETURNING_ANONYMOUS_OBJECTS = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 NON_LOCAL_SUSPENSION_POINT = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 ILLEGAL_SUSPEND_FUNCTION_CALL = DiagnosticFactory1.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index a7d98a34f35..94a35b51afa 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -1016,6 +1016,8 @@ public class DefaultErrorMessages { MAP.put(PROTECTED_CALL_FROM_PUBLIC_INLINE_ERROR, "Protected function call from public-API inline function is prohibited", NAME); MAP.put(INVALID_DEFAULT_FUNCTIONAL_PARAMETER_FOR_INLINE, "Invalid default value for inline parameter: ''{0}''. Only lambdas, anonymous functions, and callable references are supported", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES); MAP.put(NOT_SUPPORTED_INLINE_PARAMETER_IN_INLINE_PARAMETER_DEFAULT_VALUE, "Usage of inline parameter ''{0}'' in default value for another inline parameter is not supported", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES); + MAP.put(PRIVATE_INLINE_FUNCTIONS_RETURNING_ANONYMOUS_OBJECTS, "Return type of the private inline function can't be anonymous. It will be approximated to Any in 1.5." + + "See https://youtrack.jetbrains.com/issue/KT-33917 for more details"); //Inline non locals MAP.put(NON_LOCAL_RETURN_NOT_ALLOWED, "Can''t inline ''{0}'' here: it may contain non-local returns. Add ''crossinline'' modifier to parameter declaration ''{0}''", ELEMENT_TEXT); MAP.put(INLINE_CALL_CYCLE, "The ''{0}'' invocation is a part of inline cycle", NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index 461c6c49e66..78d6cb3c058 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -39,7 +39,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf( MissingDependencySupertypeChecker.ForDeclarations, FunInterfaceDeclarationChecker(), DeprecatedSinceKotlinAnnotationChecker, - ContractDescriptionBlockChecker + ContractDescriptionBlockChecker, + PrivateInlineFunctionsReturningAnonymousObjectsChecker ) private val DEFAULT_CALL_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PrivateInlineFunctionsReturningAnonymousObjectsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PrivateInlineFunctionsReturningAnonymousObjectsChecker.kt new file mode 100644 index 00000000000..5fc1e415886 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PrivateInlineFunctionsReturningAnonymousObjectsChecker.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.checkers + +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.psiUtil.isPrivate +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny + +object PrivateInlineFunctionsReturningAnonymousObjectsChecker : DeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + if (context.languageVersionSettings.supportsFeature(LanguageFeature.ForbidAnonymousReturnTypesInPrivateInlineFunctions)) + return + + if (descriptor !is SimpleFunctionDescriptor || !descriptor.isInline || !declaration.isPrivate() || declaration !is KtNamedFunction) + return + + val returnTypeConstructor = descriptor.returnType?.constructor ?: return + + if (returnTypeConstructor.supertypes.singleOrNull { it.isAnyOrNullableAny() } == null) return + + val nameIdentifier = declaration.nameIdentifier ?: return + val returnTypeDeclarationDescriptor = returnTypeConstructor.declarationDescriptor ?: return + + if (DescriptorUtils.isAnonymousObject(returnTypeDeclarationDescriptor)) { + context.trace.report(Errors.PRIVATE_INLINE_FUNCTIONS_RETURNING_ANONYMOUS_OBJECTS.on(nameIdentifier)) + } + } +} diff --git a/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.fir.kt b/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.fir.kt new file mode 100644 index 00000000000..b7f01aac27d --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.fir.kt @@ -0,0 +1,11 @@ +private inline fun foo(crossinline f: () -> Int) = object { + fun bar(): Int = f() +} + +fun test(b: Boolean) { + var x = foo { 1 } + if (b) { + x = foo { 2 } + } + x.bar() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.kt b/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.kt new file mode 100644 index 00000000000..8a5b43e9f76 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.kt @@ -0,0 +1,11 @@ +private inline fun foo(crossinline f: () -> Int) = object { + fun bar(): Int = f() +} + +fun test(b: Boolean) { + var x = foo { 1 } + if (b) { + x = foo { 2 } + } + x.bar() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.txt b/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.txt new file mode 100644 index 00000000000..df3a53f2a39 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.txt @@ -0,0 +1,4 @@ +package + +private inline fun foo(/*0*/ crossinline f: () -> kotlin.Int): foo. +public fun test(/*0*/ b: kotlin.Boolean): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 8383d3ef729..80f7ff82137 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -12438,6 +12438,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali runTest("compiler/testData/diagnostics/tests/inline/recursion.kt"); } + @TestMetadata("returnedAnonymousObjects.kt") + public void testReturnedAnonymousObjects() throws Exception { + runTest("compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.kt"); + } + @TestMetadata("returns.kt") public void testReturns() throws Exception { runTest("compiler/testData/diagnostics/tests/inline/returns.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 911e649e25a..5f7840a1c53 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -12433,6 +12433,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/inline/recursion.kt"); } + @TestMetadata("returnedAnonymousObjects.kt") + public void testReturnedAnonymousObjects() throws Exception { + runTest("compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.kt"); + } + @TestMetadata("returns.kt") public void testReturns() throws Exception { runTest("compiler/testData/diagnostics/tests/inline/returns.kt"); diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 33730a1d417..37e5530f6af 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -137,6 +137,7 @@ enum class LanguageFeature( ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated(KOTLIN_1_5, kind = BUG_FIX), InferenceCompatibility(KOTLIN_1_5, kind = BUG_FIX), RequiredPrimaryConstructorDelegationCallInEnums(KOTLIN_1_5, kind = BUG_FIX), + ForbidAnonymousReturnTypesInPrivateInlineFunctions(KOTLIN_1_5, kind = BUG_FIX), // Temporarily disabled, see KT-27084/KT-22379 SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX),