[kotlin] "Redundant SAM constructor" quickfix should move lambda argument out of parentheses

^KTIJ-29216 Fixed

GitOrigin-RevId: e367f210abb7d25037e65cc77f89945237471948
This commit is contained in:
Alexey Belkov
2024-03-26 14:01:45 +00:00
committed by intellij-monorepo-bot
parent 84f279f662
commit 77e6dcbf27
14 changed files with 48 additions and 32 deletions
@@ -6,6 +6,7 @@ import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInspection.*
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.util.parentOfType
import org.jetbrains.kotlin.codegen.SamCodegenUtil
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.idea.FrontendInternals
@@ -17,6 +18,8 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.safeAnalyzeNonSourceRootCode
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.core.canMoveLambdaOutsideParentheses
import org.jetbrains.kotlin.idea.refactoring.moveFunctionLiteralOutsideParentheses
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.name.Name
@@ -44,6 +47,11 @@ import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.utils.keysToMapExceptNulls
/**
* Tests:
* * [org.jetbrains.kotlin.idea.inspections.LocalInspectionTestGenerated.RedundantSamConstructor]
* * [org.jetbrains.kotlin.idea.inspections.InspectionTestGenerated.Inspections.testRedundantSamConstructor_inspectionData_Inspections_test]
*/
class RedundantSamConstructorInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return callExpressionVisitor(fun(expression) {
@@ -86,7 +94,7 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val callExpression = pointer.element ?: return
if (!FileModificationService.getInstance().preparePsiElementForWrite(callExpression)) return
replaceSamConstructorCall(callExpression)
removeSamConstructor(callExpression)
}
}
}
@@ -100,12 +108,20 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
val callExpressions = pointers.mapNotNull { it.element }
if (!FileModificationService.getInstance().preparePsiElementsForWrite(callExpressions)) return
for (callExpression in callExpressions) {
replaceSamConstructorCall(callExpression)
removeSamConstructor(callExpression)
}
}
}
}
private fun removeSamConstructor(callExpression: KtCallExpression) {
val lambdaExpression = replaceSamConstructorCall(callExpression)
val outerCallExpression = lambdaExpression.parentOfType<KtCallExpression>() ?: return
if (outerCallExpression.canMoveLambdaOutsideParentheses(skipComplexCalls = true)) {
outerCallExpression.moveFunctionLiteralOutsideParentheses()
}
}
object Util {
private fun canBeReplaced(
parentCall: KtCallExpression,
@@ -4,6 +4,6 @@ import a.*
import a.A.*
fun testExpectedType() {
val s1: JFunction1<String> = A.expectedType({ })
val s2: JFunction1<String> = expectedType({ })
val s1: JFunction1<String> = A.expectedType { }
val s2: JFunction1<String> = expectedType { }
}
@@ -5,8 +5,8 @@ import a.*
fun test() {
MyJavaClass.foo1({ }, 1)
MyJavaClass.foo1({ }, Runnable { })
MyJavaClass.foo2(1, { })
MyJavaClass.foo2(Runnable { }, { })
MyJavaClass.foo2(1) { }
MyJavaClass.foo2(Runnable { }) { }
A<String>().foo({})
A<String>().foo {}
}
@@ -6,10 +6,10 @@ fun test() {
val runnable = Runnable { }
val klass = MyJavaClass()
MyJavaClass.staticFun1({ })
MyJavaClass.staticFun1 { }
MyJavaClass.staticFun1(runnable)
MyJavaClass.staticFun2({ }, { })
MyJavaClass.staticFun2(runnable, { })
MyJavaClass.staticFun2(runnable) { }
MyJavaClass.staticFun2({ }, { })
MyJavaClass.staticFun2(
object: Runnable {
@@ -19,13 +19,13 @@ fun test() {
override fun run() { }
})
MyJavaClass.staticFunWithOtherParam(1, { })
MyJavaClass.staticFunWithOtherParam(1) { }
MyJavaClass.staticFunWithOtherParam(1, runnable)
klass.memberFun1({ })
klass.memberFun1 { }
klass.memberFun1(runnable)
klass.memberFun2({ }, { })
klass.memberFun2(runnable, { })
klass.memberFun2(runnable) { }
klass.memberFun2({ }, { })
klass.memberFun2(
object: Runnable {
@@ -35,11 +35,11 @@ fun test() {
override fun run() { }
})
klass.memberFunWithOtherParam(1, { })
klass.memberFunWithOtherParam(1) { }
klass.memberFunWithOtherParam(1, runnable)
MyJavaClass.staticFunWithOtherParam(1, { })
klass.memberFun1({ })
MyJavaClass.staticFunWithOtherParam(1) { }
klass.memberFun1 { }
MyJavaClass.staticFun2({ }, Runnable {
if (p > 0) return@Runnable
@@ -9,7 +9,7 @@ fun testGenerics() {
GenericClass1.staticFun1(JFunction1<String> { })
GenericClass1.staticFun1(runnable)
GenericClass1.staticFun2(JFunction1<String> { }, JFunction1<String> { })
GenericClass1.staticFun2(runnable, { })
GenericClass1.staticFun2(runnable) { }
GenericClass1.staticFun2({ s: String -> }, { s: String -> })
GenericClass1.staticFunWithOtherParam(1, JFunction1<String> { })
@@ -18,7 +18,7 @@ fun testGenerics() {
klass.memberFun1(JFunction1<String> { })
klass.memberFun1(runnable)
klass.memberFun2(JFunction1<String> { }, JFunction1<String> { })
klass.memberFun2(runnable, { })
klass.memberFun2(runnable) { }
klass.memberFun2({ s: String -> }, { s: String -> })
klass.memberFunWithOtherParam(1, JFunction1<String> { })
@@ -6,21 +6,21 @@ fun testGenericsReturnType() {
val runnable = JFunction0 { "" }
val klass = GenericClass2()
GenericClass2.staticFun1({ "" })
GenericClass2.staticFun1 { "" }
GenericClass2.staticFun1(runnable)
GenericClass2.staticFun2({ "" }, { "" })
GenericClass2.staticFun2(runnable, { "" })
GenericClass2.staticFun2(runnable) { "" }
GenericClass2.staticFun2({ -> "" }, { -> "" })
GenericClass2.staticFunWithOtherParam(1, { "" })
GenericClass2.staticFunWithOtherParam(1) { "" }
GenericClass2.staticFunWithOtherParam(1, runnable)
klass.memberFun1({ "" })
klass.memberFun1 { "" }
klass.memberFun1(runnable)
klass.memberFun2({ "" }, { "" })
klass.memberFun2(runnable, { "" })
klass.memberFun2(runnable) { "" }
klass.memberFun2({ -> "" }, { -> "" })
klass.memberFunWithOtherParam(1, { "" })
klass.memberFunWithOtherParam(1) { "" }
klass.memberFunWithOtherParam(1, runnable)
}
@@ -1,5 +1,5 @@
// COMPILER_ARGUMENTS: -XXLanguage:+FunctionalInterfaceConversion
fun usage(r1: Runnable) {
Test.test(r1, {})
Test.test(r1) {}
}
@@ -3,5 +3,5 @@
fun test(r1: Runnable, r2: Runnable) {}
fun usage(r1: Runnable) {
test(r1, {})
test(r1) {}
}
@@ -7,5 +7,5 @@ fun interface KtRunnable {
fun test(r1: KtRunnable, r2: KtRunnable) {}
fun usage(r1: KtRunnable) {
test(r1, {})
test(r1) {}
}
@@ -3,5 +3,5 @@
fun test(r1: Runnable, r2: Runnable) {}
fun usage() {
test(Runnable { return@Runnable }, {})
test(Runnable { return@Runnable }) {}
}
@@ -8,6 +8,6 @@ class Test {
fun usage(r: KtRunnable) {}
fun test() {
usage({ })
usage { }
}
}
@@ -1,3 +1,3 @@
fun test() {
Foo.foo({ })
Foo.foo { }
}
@@ -1,5 +1,5 @@
fun test() {
Foo.Bar().bar({
Foo.Bar().bar {
true
})
}
}
@@ -1,5 +1,5 @@
fun usage(r: Runnable) {}
fun test() {
usage({ })
usage { }
}