Introduce warning for private inline functions which return anonymous objects without specified supertypes (KT-33917)

This commit is contained in:
Victor Petukhov
2020-09-25 13:59:24 +03:00
parent 63d825fa24
commit 09f1764f82
11 changed files with 83 additions and 1 deletions
@@ -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");
@@ -1129,6 +1129,7 @@ public interface Errors {
DiagnosticFactory1<PsiElement, CallableDescriptor> PROTECTED_CALL_FROM_PUBLIC_INLINE_ERROR = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<KtElement, KtExpression, DeclarationDescriptor> INVALID_DEFAULT_FUNCTIONAL_PARAMETER_FOR_INLINE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<KtElement, KtExpression, DeclarationDescriptor> NOT_SUPPORTED_INLINE_PARAMETER_IN_INLINE_PARAMETER_DEFAULT_VALUE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory0<PsiElement> PRIVATE_INLINE_FUNCTIONS_RETURNING_ANONYMOUS_OBJECTS = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> NON_LOCAL_SUSPENSION_POINT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, CallableDescriptor> ILLEGAL_SUSPEND_FUNCTION_CALL = DiagnosticFactory1.create(ERROR);
@@ -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);
@@ -39,7 +39,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
MissingDependencySupertypeChecker.ForDeclarations,
FunInterfaceDeclarationChecker(),
DeprecatedSinceKotlinAnnotationChecker,
ContractDescriptionBlockChecker
ContractDescriptionBlockChecker,
PrivateInlineFunctionsReturningAnonymousObjectsChecker
)
private val DEFAULT_CALL_CHECKERS = listOf(
@@ -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))
}
}
}
@@ -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()
}
@@ -0,0 +1,11 @@
private inline fun <!PRIVATE_INLINE_FUNCTIONS_RETURNING_ANONYMOUS_OBJECTS!>foo<!>(crossinline f: () -> Int) = object {
fun bar(): Int = f()
}
fun test(b: Boolean) {
var x = foo { 1 }
if (b) {
x = foo { 2 }
}
x.bar()
}
@@ -0,0 +1,4 @@
package
private inline fun foo(/*0*/ crossinline f: () -> kotlin.Int): foo.<no name provided>
public fun test(/*0*/ b: kotlin.Boolean): kotlin.Unit
@@ -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");
@@ -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");
@@ -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),