From 46ae6136cb1d56f4876fc3b03d139417bb563a3a Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Mon, 10 Feb 2020 13:12:21 +0300 Subject: [PATCH] Rewrite `RedundantSamConstructorInspection` to support more cases - Now inspection does not rely on synthetic descriptors at all, instead it uses `SamConversionOracle` and `SamConversionResolver` to detect if the argument type support SAM conversion - This transparently considers all language features like `SAM conversions for kotlin functions`, `Functional interfaces` etc. - In case of multiple SAM arguments, part of them can be converted only when `SAM conversion per argument` is enabled - Fix inspection and nj2k tests that were failing because of better working inspection - Rewrite automatically fixes multiple bugs that were present - ^KT-36367 ^KT-36368 ^KT-36296 ^KT-36395 Fixed --- .../RedundantSamConstructorInspection.kt | 112 ++++++++---------- .../inspectionData/expected.xml | 22 ++++ .../otherGenericsInParams.kt.after | 4 +- .../conversionPerArgumentEnabled1.1.java | 3 + .../conversionPerArgumentEnabled1.kt | 5 + .../conversionPerArgumentEnabled1.kt.after | 5 + .../conversionPerArgumentEnabled2.kt | 7 ++ .../conversionPerArgumentEnabled2.kt.after | 7 ++ .../conversionPerArgumentEnabled3.kt | 11 ++ .../conversionPerArgumentEnabled3.kt.after | 11 ++ .../conversionPerArgumentEnabled4.kt | 7 ++ .../conversionPerArgumentEnabled4.kt.after | 7 ++ .../genericParameter.kt | 8 ++ .../javaInterfacesHierarchy.1.java | 14 +++ .../javaInterfacesHierarchy.kt | 5 + .../kotlinFunInterface.kt | 13 ++ .../kotlinFunInterface.kt.after | 13 ++ .../redundantSamConstructor/simple3.kt | 7 ++ .../redundantSamConstructor/simple3.kt.after | 7 ++ .../LocalInspectionTestGenerated.java | 40 +++++++ j2k/build.gradle.kts | 1 + .../function/java8MRSamConstructor.kt | 2 +- .../newJ2k/function/java8MRSamConstructor.kt | 2 +- 23 files changed, 248 insertions(+), 65 deletions(-) create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.1.java create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.kt create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.kt.after create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled2.kt create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled2.kt.after create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled3.kt create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled3.kt.after create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled4.kt create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled4.kt.after create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/genericParameter.kt create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/javaInterfacesHierarchy.1.java create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/javaInterfacesHierarchy.kt create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/kotlinFunInterface.kt create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/kotlinFunInterface.kt.after create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/simple3.kt create mode 100644 idea/testData/inspectionsLocal/redundantSamConstructor/simple3.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt index 88a5a4042c0..ec50d7a8778 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt @@ -11,18 +11,12 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiElementVisitor import org.jetbrains.kotlin.codegen.SamCodegenUtil import org.jetbrains.kotlin.config.LanguageFeature -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade -import org.jetbrains.kotlin.idea.codeInsight.forceEnableSamAdapters import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.idea.util.getResolutionScope -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.load.java.sam.SamAdapterDescriptor import org.jetbrains.kotlin.load.java.sam.SamConstructorDescriptor -import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType @@ -33,16 +27,17 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTraceContext import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore import org.jetbrains.kotlin.resolve.calls.CallResolver +import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess import org.jetbrains.kotlin.resolve.calls.tower.NewResolvedCallImpl import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes -import org.jetbrains.kotlin.resolve.scopes.collectSyntheticMemberFunctions -import org.jetbrains.kotlin.resolve.scopes.collectSyntheticStaticFunctions -import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor -import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.resolve.sam.SamConversionOracle +import org.jetbrains.kotlin.resolve.sam.SamConversionResolver +import org.jetbrains.kotlin.resolve.sam.getFunctionTypeForPossibleSamType +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.utils.keysToMapExceptNulls class RedundantSamConstructorInspection : AbstractKotlinInspection() { @@ -168,53 +163,60 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() { val valueArguments = functionCall.valueArguments if (valueArguments.none { canBeSamConstructorCall(it) }) return emptyList() - val bindingContext = functionCall.analyze(BodyResolveMode.PARTIAL) + val resolutionFacade = functionCall.getResolutionFacade() + val oracle = resolutionFacade.frontendService() + val resolver = resolutionFacade.frontendService() + + val bindingContext = functionCall.analyze(resolutionFacade, BodyResolveMode.PARTIAL) val functionResolvedCall = functionCall.getResolvedCall(bindingContext) ?: return emptyList() if (!functionResolvedCall.isReallySuccess()) return emptyList() - val samConstructorCallArgumentMap = valueArguments.keysToMapExceptNulls { it.toCallExpression() }.filterValues { call -> - call.getResolvedCall(bindingContext)?.resultingDescriptor?.original is SamConstructorDescriptor + /** + * Checks that SAM conversion for [arg] and [call] in the argument position is possible + * and does not loose any information. + * + * We want to do as many cheap checks as possible before actually trying to resolve substituted call in [canBeReplaced]. + * + * Several cases where we do not want the conversion: + * + * - Expected argument type is inferred from the argument; for example when the expected type is `T`, and SAM constructor + * helps to deduce it. + * - Expected argument type is a base type for the actual argument type; for example when expected type is `Any`, and removing + * SAM constructor will lead to passing object of different type. + */ + fun samConversionIsPossible(arg: KtValueArgument, call: KtCallExpression): Boolean { + val samConstructor = + call.getResolvedCall(bindingContext)?.resultingDescriptor?.original as? SamConstructorDescriptor ?: return false + + // we suppose that SAM constructors return type is always not nullable + val samConstructorReturnType = samConstructor.returnType?.unwrap()?.takeUnless { it.isNullable() } ?: return false + + // we take original parameter descriptor to get type parameter instead of inferred type (e.g. `T` instead of `Runnable`) + val originalParameterDescriptor = functionResolvedCall.getParameterForArgument(arg)?.original ?: return false + val expectedNotNullableType = originalParameterDescriptor.type.makeNotNullable().unwrap() + + if (resolver.getFunctionTypeForPossibleSamType(expectedNotNullableType, oracle) == null) return false + + return samConstructorReturnType.constructor == expectedNotNullableType.constructor } - if (samConstructorCallArgumentMap.isEmpty()) return emptyList() - - if (samConstructorCallArgumentMap.values.any(::containsLabeledReturnPreventingConversion)) return emptyList() - - val originalFunctionDescriptor = functionResolvedCall.resultingDescriptor.original as? FunctionDescriptor ?: return emptyList() - val containingClass = originalFunctionDescriptor.containingDeclaration as? ClassDescriptor ?: return emptyList() - - val syntheticScopes = - functionCall.getResolutionFacade().getFrontendService(SyntheticScopes::class.java).forceEnableSamAdapters() - - // SAM adapters for static functions - val contributedFunctions = syntheticScopes.collectSyntheticStaticFunctions(containingClass.staticScope) - for (staticFunWithSameName in contributedFunctions) { - if (staticFunWithSameName is SamAdapterDescriptor<*>) { - if (isSamAdapterSuitableForCall( - staticFunWithSameName, - originalFunctionDescriptor, - samConstructorCallArgumentMap.size - ) - ) { - return samConstructorCallArgumentMap.takeIf { canBeReplaced(functionCall, it) }?.values ?: emptyList() - } - } + val argumentsWithSamConstructors = valueArguments.keysToMapExceptNulls { arg -> + arg.toCallExpression()?.takeIf { call -> samConversionIsPossible(arg, call) } } - // SAM adapters for member functions - val syntheticExtensions = syntheticScopes.collectSyntheticMemberFunctions( - listOf(containingClass.defaultType), - functionResolvedCall.resultingDescriptor.name, - NoLookupLocation.FROM_IDE - ) - for (syntheticExtension in syntheticExtensions) { - val samAdapter = syntheticExtension as? SamAdapterExtensionFunctionDescriptor ?: continue - if (isSamAdapterSuitableForCall(samAdapter, originalFunctionDescriptor, samConstructorCallArgumentMap.size)) { - return samConstructorCallArgumentMap.takeIf { canBeReplaced(functionCall, it) }?.values ?: emptyList() - } + val haveToConvertAllArguments = !functionCall.languageVersionSettings.supportsFeature(LanguageFeature.SamConversionPerArgument) + + val argumentsThatCanBeConverted = if (haveToConvertAllArguments) { + argumentsWithSamConstructors.takeIf { it.values.none(::containsLabeledReturnPreventingConversion) }.orEmpty() + } else { + argumentsWithSamConstructors.filterValues { !containsLabeledReturnPreventingConversion(it) } } - return emptyList() + return when { + argumentsThatCanBeConverted.isEmpty() -> emptyList() + !canBeReplaced(functionCall, argumentsThatCanBeConverted) -> emptyList() + else -> argumentsThatCanBeConverted.values + } } private fun canBeSamConstructorCall(argument: KtValueArgument) = argument.toCallExpression()?.samConstructorValueArgument() != null @@ -231,18 +233,6 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() { argumentExpression) as? KtCallExpression } - private fun isSamAdapterSuitableForCall( - samAdapter: FunctionDescriptor, - originalFunction: FunctionDescriptor, - samConstructorsCount: Int - ): Boolean { - val samAdapterOriginalFunction = SamCodegenUtil.getOriginalIfSamAdapter(samAdapter)?.original - if (samAdapterOriginalFunction != originalFunction) return false - - val parametersWithSamTypeCount = originalFunction.valueParameters.count { JavaSingleAbstractMethodUtils.isSamType(it.type) } - return parametersWithSamTypeCount == samConstructorsCount - } - private fun containsLabeledReturnPreventingConversion(it: KtCallExpression): Boolean { val samValueArgument = it.samConstructorValueArgument() val samConstructorName = (it.calleeExpression as? KtSimpleNameExpression)?.getReferencedNameAsName() diff --git a/idea/testData/inspections/redundantSamConstructor/inspectionData/expected.xml b/idea/testData/inspections/redundantSamConstructor/inspectionData/expected.xml index ba983de5c4b..ab10dd15773 100644 --- a/idea/testData/inspections/redundantSamConstructor/inspectionData/expected.xml +++ b/idea/testData/inspections/redundantSamConstructor/inspectionData/expected.xml @@ -103,6 +103,17 @@ Redundant SAM-constructor Redundant SAM-constructor + + otherGenericsInParams.kt + 7 + light_idea_test_case + + Redundant SAM constructor + Redundant SAM-constructor + Runnable + 21 + 8 + otherGenericsInParams.kt 8 @@ -111,6 +122,17 @@ Redundant SAM-constructor Redundant SAM-constructor + + otherGenericsInParams.kt + 9 + light_idea_test_case + + Redundant SAM constructor + Redundant SAM-constructor + Runnable + 35 + 8 + otherGenericsInParams.kt 11 diff --git a/idea/testData/inspections/redundantSamConstructor/inspectionData/otherGenericsInParams.kt.after b/idea/testData/inspections/redundantSamConstructor/inspectionData/otherGenericsInParams.kt.after index bff3e5fbb76..27ffee5b47d 100644 --- a/idea/testData/inspections/redundantSamConstructor/inspectionData/otherGenericsInParams.kt.after +++ b/idea/testData/inspections/redundantSamConstructor/inspectionData/otherGenericsInParams.kt.after @@ -4,9 +4,9 @@ import a.* fun test() { MyJavaClass.foo1({ }, 1) - MyJavaClass.foo1(Runnable { }, Runnable { }) + MyJavaClass.foo1({ }, Runnable { }) MyJavaClass.foo2(1, { }) - MyJavaClass.foo2(Runnable { }, Runnable { }) + MyJavaClass.foo2(Runnable { }, { }) A().foo({}) } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.1.java b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.1.java new file mode 100644 index 00000000000..0979493eb49 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.1.java @@ -0,0 +1,3 @@ +public class Test { + static public void test(Runnable r1, Runnable r2) {} +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.kt b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.kt new file mode 100644 index 00000000000..f0740be67df --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.kt @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+FunctionalInterfaceConversion -XXLanguage:+SamConversionPerArgument + +fun usage(r1: Runnable) { + Test.test(r1, Runnable {}) +} diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.kt.after b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.kt.after new file mode 100644 index 00000000000..fd5ece548d8 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.kt.after @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+FunctionalInterfaceConversion -XXLanguage:+SamConversionPerArgument + +fun usage(r1: Runnable) { + Test.test(r1, {}) +} diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled2.kt b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled2.kt new file mode 100644 index 00000000000..9cbf8c0a2c3 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled2.kt @@ -0,0 +1,7 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+FunctionalInterfaceConversion -XXLanguage:+SamConversionPerArgument + +fun test(r1: Runnable, r2: Runnable) {} + +fun usage(r1: Runnable) { + test(r1, Runnable {}) +} diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled2.kt.after b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled2.kt.after new file mode 100644 index 00000000000..f6e2290741b --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled2.kt.after @@ -0,0 +1,7 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+FunctionalInterfaceConversion -XXLanguage:+SamConversionPerArgument + +fun test(r1: Runnable, r2: Runnable) {} + +fun usage(r1: Runnable) { + test(r1, {}) +} diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled3.kt b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled3.kt new file mode 100644 index 00000000000..af8207c521e --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled3.kt @@ -0,0 +1,11 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+FunctionalInterfaceConversion -XXLanguage:+SamConversionPerArgument + +fun interface KtRunnable { + fun run() +} + +fun test(r1: KtRunnable, r2: KtRunnable) {} + +fun usage(r1: KtRunnable) { + test(r1, KtRunnable {}) +} diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled3.kt.after b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled3.kt.after new file mode 100644 index 00000000000..dfd1b845c78 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled3.kt.after @@ -0,0 +1,11 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+FunctionalInterfaceConversion -XXLanguage:+SamConversionPerArgument + +fun interface KtRunnable { + fun run() +} + +fun test(r1: KtRunnable, r2: KtRunnable) {} + +fun usage(r1: KtRunnable) { + test(r1, {}) +} diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled4.kt b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled4.kt new file mode 100644 index 00000000000..f265b01c76b --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled4.kt @@ -0,0 +1,7 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+FunctionalInterfaceConversion -XXLanguage:+SamConversionPerArgument + +fun test(r1: Runnable, r2: Runnable) {} + +fun usage() { + test(Runnable { return@Runnable }, Runnable {}) +} diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled4.kt.after b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled4.kt.after new file mode 100644 index 00000000000..c51aa232ed3 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled4.kt.after @@ -0,0 +1,7 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+FunctionalInterfaceConversion -XXLanguage:+SamConversionPerArgument + +fun test(r1: Runnable, r2: Runnable) {} + +fun usage() { + test(Runnable { return@Runnable }, {}) +} diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/genericParameter.kt b/idea/testData/inspectionsLocal/redundantSamConstructor/genericParameter.kt new file mode 100644 index 00000000000..aa2add9b0bc --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/genericParameter.kt @@ -0,0 +1,8 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions +// PROBLEM: none + +fun test(t: T): T = t + +fun usage() { + test(Runnable {}) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/javaInterfacesHierarchy.1.java b/idea/testData/inspectionsLocal/redundantSamConstructor/javaInterfacesHierarchy.1.java new file mode 100644 index 00000000000..c243bafc76b --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/javaInterfacesHierarchy.1.java @@ -0,0 +1,14 @@ +interface Base { + void test1(); +} + +interface Extender extends Base { + @java.lang.Override + default void test1() { test2(); } + + void test2(); +} + +class Taker { + static void take(Base b) {} +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/javaInterfacesHierarchy.kt b/idea/testData/inspectionsLocal/redundantSamConstructor/javaInterfacesHierarchy.kt new file mode 100644 index 00000000000..659f03f78de --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/javaInterfacesHierarchy.kt @@ -0,0 +1,5 @@ +// PROBLEM: none + +fun usage() { + Taker.take(Extender {}) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/kotlinFunInterface.kt b/idea/testData/inspectionsLocal/redundantSamConstructor/kotlinFunInterface.kt new file mode 100644 index 00000000000..009d80c82da --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/kotlinFunInterface.kt @@ -0,0 +1,13 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+FunctionalInterfaceConversion + +fun interface KtRunnable { + fun run() +} + +class Test { + fun usage(r: KtRunnable) {} + + fun test() { + usage(KtRunnable { }) + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/kotlinFunInterface.kt.after b/idea/testData/inspectionsLocal/redundantSamConstructor/kotlinFunInterface.kt.after new file mode 100644 index 00000000000..3e9cf601fc3 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/kotlinFunInterface.kt.after @@ -0,0 +1,13 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions -XXLanguage:+FunctionalInterfaceConversion + +fun interface KtRunnable { + fun run() +} + +class Test { + fun usage(r: KtRunnable) {} + + fun test() { + usage({ }) + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/simple3.kt b/idea/testData/inspectionsLocal/redundantSamConstructor/simple3.kt new file mode 100644 index 00000000000..bc55abfff63 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/simple3.kt @@ -0,0 +1,7 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions + +fun usage(r: Runnable) {} + +fun test() { + usage(Runnable { }) +} diff --git a/idea/testData/inspectionsLocal/redundantSamConstructor/simple3.kt.after b/idea/testData/inspectionsLocal/redundantSamConstructor/simple3.kt.after new file mode 100644 index 00000000000..5fd62e08616 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantSamConstructor/simple3.kt.after @@ -0,0 +1,7 @@ +// COMPILER_ARGUMENTS: -XXLanguage:+NewInference -XXLanguage:+SamConversionForKotlinFunctions + +fun usage(r: Runnable) {} + +fun test() { + usage({ }) +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index a7201443136..158cb6c5d86 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -7881,6 +7881,41 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentDisabled3.kt"); } + @TestMetadata("conversionPerArgumentEnabled1.kt") + public void testConversionPerArgumentEnabled1() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled1.kt"); + } + + @TestMetadata("conversionPerArgumentEnabled2.kt") + public void testConversionPerArgumentEnabled2() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled2.kt"); + } + + @TestMetadata("conversionPerArgumentEnabled3.kt") + public void testConversionPerArgumentEnabled3() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled3.kt"); + } + + @TestMetadata("conversionPerArgumentEnabled4.kt") + public void testConversionPerArgumentEnabled4() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantSamConstructor/conversionPerArgumentEnabled4.kt"); + } + + @TestMetadata("genericParameter.kt") + public void testGenericParameter() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantSamConstructor/genericParameter.kt"); + } + + @TestMetadata("javaInterfacesHierarchy.kt") + public void testJavaInterfacesHierarchy() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantSamConstructor/javaInterfacesHierarchy.kt"); + } + + @TestMetadata("kotlinFunInterface.kt") + public void testKotlinFunInterface() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantSamConstructor/kotlinFunInterface.kt"); + } + @TestMetadata("labeledReturn1.kt") public void testLabeledReturn1() throws Exception { runTest("idea/testData/inspectionsLocal/redundantSamConstructor/labeledReturn1.kt"); @@ -7920,6 +7955,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { public void testSimple2() throws Exception { runTest("idea/testData/inspectionsLocal/redundantSamConstructor/simple2.kt"); } + + @TestMetadata("simple3.kt") + public void testSimple3() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantSamConstructor/simple3.kt"); + } } @TestMetadata("idea/testData/inspectionsLocal/redundantSemicolon") diff --git a/j2k/build.gradle.kts b/j2k/build.gradle.kts index 6105c4165c7..54e1313ee48 100644 --- a/j2k/build.gradle.kts +++ b/j2k/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { testRuntime(project(":sam-with-receiver-ide-plugin")) testRuntime(project(":allopen-ide-plugin")) testRuntime(project(":noarg-ide-plugin")) + testRuntime(project(":kotlin-reflect")) testRuntime(project(":kotlin-scripting-idea")) testRuntime(project(":kotlinx-serialization-ide-plugin")) testRuntime(intellijPluginDep("properties")) diff --git a/j2k/testData/fileOrElement/function/java8MRSamConstructor.kt b/j2k/testData/fileOrElement/function/java8MRSamConstructor.kt index 84c4534531a..717a0a93393 100644 --- a/j2k/testData/fileOrElement/function/java8MRSamConstructor.kt +++ b/j2k/testData/fileOrElement/function/java8MRSamConstructor.kt @@ -118,7 +118,7 @@ internal class Java8Class { val memberFunFromClass = JFunction2 { it.length } memberFunFromClass.foo("str") - Thread(Runnable { println() }).start() + Thread { println() }.start() Runnable { println() }.run() } diff --git a/nj2k/testData/newJ2k/function/java8MRSamConstructor.kt b/nj2k/testData/newJ2k/function/java8MRSamConstructor.kt index 51a6a774508..a1904f746c0 100644 --- a/nj2k/testData/newJ2k/function/java8MRSamConstructor.kt +++ b/nj2k/testData/newJ2k/function/java8MRSamConstructor.kt @@ -106,7 +106,7 @@ internal class Java8Class { fun testLibraryFunctions() { val memberFunFromClass = JFunction2 { obj: String -> obj.length } memberFunFromClass.foo("str") - Thread(Runnable { println() }).start() + Thread { println() }.start() Runnable { println() }.run() }