[kotlin] k2 inline function: skip arguments which are the same as default values

^KTIJ-30263 fixed

GitOrigin-RevId: 2bb6e8c726d83d31e95a9b479c83f9a523d6e3bd
This commit is contained in:
Anna Kozlova
2024-06-11 19:38:45 +02:00
committed by intellij-monorepo-bot
parent bd6a721d84
commit 59a791ea60
18 changed files with 257 additions and 2 deletions

View File

@@ -2075,6 +2075,41 @@ public abstract class InlineTestGenerated extends AbstractInlineTest {
runTest("testData/refactoring/inline/namedFunction/defaultParameter.kt");
}
@TestMetadata("defaultParameterDiffers.kt")
public void testDefaultParameterDiffers() throws Exception {
runTest("testData/refactoring/inline/namedFunction/defaultParameterDiffers.kt");
}
@TestMetadata("defaultParameterDiffers1.kt")
public void testDefaultParameterDiffers1() throws Exception {
runTest("testData/refactoring/inline/namedFunction/defaultParameterDiffers1.kt");
}
@TestMetadata("defaultParameterImplicitlyUsed.kt")
public void testDefaultParameterImplicitlyUsed() throws Exception {
runTest("testData/refactoring/inline/namedFunction/defaultParameterImplicitlyUsed.kt");
}
@TestMetadata("defaultParameterImplicitlyUsed1.kt")
public void testDefaultParameterImplicitlyUsed1() throws Exception {
runTest("testData/refactoring/inline/namedFunction/defaultParameterImplicitlyUsed1.kt");
}
@TestMetadata("defaultParameterImplicitlyUsed2.kt")
public void testDefaultParameterImplicitlyUsed2() throws Exception {
runTest("testData/refactoring/inline/namedFunction/defaultParameterImplicitlyUsed2.kt");
}
@TestMetadata("defaultParameterImplicitlyUsed3.kt")
public void testDefaultParameterImplicitlyUsed3() throws Exception {
runTest("testData/refactoring/inline/namedFunction/defaultParameterImplicitlyUsed3.kt");
}
@TestMetadata("defaultParameterImplicitlyUsed4.kt")
public void testDefaultParameterImplicitlyUsed4() throws Exception {
runTest("testData/refactoring/inline/namedFunction/defaultParameterImplicitlyUsed4.kt");
}
@TestMetadata("defaultParameterSubstitution.kt")
public void testDefaultParameterSubstitution() throws Exception {
runTest("testData/refactoring/inline/namedFunction/defaultParameterSubstitution.kt");

View File

@@ -0,0 +1,9 @@
fun oldFun(p1: String = "", p2: Int = 0) {
newFun(p1, p2)
}
fun newFun(p1: String = "", p2: Int = 42, p3: Int = -1) {}
fun foo() {
old<caret>Fun()
}

View File

@@ -0,0 +1,5 @@
fun newFun(p1: String = "", p2: Int = 42, p3: Int = -1) {}
fun foo() {
newFun("", 0)
}

View File

@@ -0,0 +1,9 @@
fun oldFun(p1: String = "", p2: Int = 0) {
newFun(p1, p2)
}
fun newFun(p1: String = "", p2: Int = p1.length, p3: Int = -1) {}
fun foo() {
old<caret>Fun()
}

View File

@@ -0,0 +1,5 @@
fun newFun(p1: String = "", p2: Int = p1.length, p3: Int = -1) {}
fun foo() {
newFun("", 0)
}

View File

@@ -0,0 +1,9 @@
fun oldFun(p1: String = "", p2: Int = 0) {
newFun(p1, p2)
}
fun newFun(p1: String = "", p2: Int = 0, p3: Int = -1) {}
fun foo() {
old<caret>Fun()
}

View File

@@ -0,0 +1,5 @@
fun newFun(p1: String = "", p2: Int = 0, p3: Int = -1) {}
fun foo() {
newFun()
}

View File

@@ -0,0 +1,9 @@
fun oldFun(p1: String = "", p2: Int = 0, handler: () -> Unit) {
newFun(p2, p1, handler)
}
fun newFun(a: Int = 0, b: String = "", handler: () -> Unit) {}
fun foo() {
oldF<caret>un { }
}

View File

@@ -0,0 +1,5 @@
fun newFun(a: Int = 0, b: String = "", handler: () -> Unit) {}
fun foo() {
newFun { }
}

View File

@@ -0,0 +1,9 @@
fun oldFun(p1: String, p2: Int = p1.length, p3: String? = p1) {
newFun(p1, p2, p3)
}
fun newFun(x: String, y: Int = x.length, z: String? = "a") {}
fun foo() {
<caret>oldFun("a")
}

View File

@@ -0,0 +1,5 @@
fun newFun(x: String, y: Int = x.length, z: String? = "a") {}
fun foo() {
newFun("a")
}

View File

@@ -0,0 +1,9 @@
fun oldFun(p1: String, p2: Int = p1.length, p3: String? = p1) {
newFun(p1, p2, p3)
}
fun newFun(x: String, y: Int = x.length, z: String? = x) {}
fun foo() {
<caret>oldFun("a")
}

View File

@@ -0,0 +1,5 @@
fun newFun(x: String, y: Int = x.length, z: String? = x) {}
fun foo() {
newFun("a")
}

View File

@@ -0,0 +1,9 @@
fun oldFun(p1: String, p2: Int = p1.length, p3: String? = p1) {
newFun(p1, p2, p3)
}
fun newFun(x: String, y: Int = x.length, z: String? = "z") {}
fun foo() {
<caret>oldFun("a")
}

View File

@@ -0,0 +1,5 @@
fun newFun(x: String, y: Int = x.length, z: String? = "z") {}
fun foo() {
newFun("a", "a".length, "a")
}

View File

@@ -31,9 +31,8 @@ abstract class AbstractInlinePostProcessor {
protected abstract fun removeExplicitTypeArguments(pointer: SmartPsiElementPointer<KtElement>)
protected abstract fun shortenReferences(pointers: List<SmartPsiElementPointer<KtElement>>): List<KtElement>
//no tests fail
protected open fun introduceNamedArguments(pointer: SmartPsiElementPointer<KtElement>) {}
protected open fun dropArgumentsForDefaultValues(pointer: SmartPsiElementPointer<KtElement>) {}
protected abstract fun dropArgumentsForDefaultValues(pointer: SmartPsiElementPointer<KtElement>)
fun postProcessInsertedCode(
pointers: List<SmartPsiElementPointer<KtElement>>,

View File

@@ -1,24 +1,36 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.k2.refactoring.inline.codeInliner
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.NlsSafe
import com.intellij.psi.SmartPsiElementPointer
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.calls.singleFunctionCallOrNull
import org.jetbrains.kotlin.analysis.api.calls.symbol
import org.jetbrains.kotlin.analysis.api.components.ShortenOptions
import org.jetbrains.kotlin.analysis.api.symbols.KaValueParameterSymbol
import org.jetbrains.kotlin.idea.base.analysis.api.utils.defaultValue
import org.jetbrains.kotlin.idea.base.codeInsight.ShortenReferencesFacility
import org.jetbrains.kotlin.idea.codeinsight.utils.RemoveExplicitTypeArgumentsUtils
import org.jetbrains.kotlin.idea.k2.refactoring.canMoveLambdaOutsideParentheses
import org.jetbrains.kotlin.idea.k2.refactoring.inline.KotlinInlineAnonymousFunctionProcessor
import org.jetbrains.kotlin.idea.k2.refactoring.introduce.K2SemanticMatcher.isSemanticMatch
import org.jetbrains.kotlin.idea.k2.refactoring.util.areTypeArgumentsRedundant
import org.jetbrains.kotlin.idea.k2.refactoring.util.isRedundantUnit
import org.jetbrains.kotlin.idea.refactoring.inline.codeInliner.AbstractInlinePostProcessor
import org.jetbrains.kotlin.idea.refactoring.inline.codeInliner.InlineDataKeys.DEFAULT_PARAMETER_VALUE_KEY
import org.jetbrains.kotlin.idea.refactoring.inline.codeInliner.InlineDataKeys.USER_CODE_KEY
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.KtCallElement
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtLambdaArgument
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.KtTypeArgumentList
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.psi.KtValueArgumentList
@@ -118,4 +130,80 @@ object InlinePostProcessor: AbstractInlinePostProcessor() {
}
}
override fun dropArgumentsForDefaultValues(pointer: SmartPsiElementPointer<KtElement>) {
val result = pointer.element ?: return
val argumentsToDrop = ArrayList<KtValueArgument>()
result.forEachDescendantOfType<KtCallElement> { callExpression ->
analyze(callExpression) {
val functionCall = callExpression.resolveCall()?.singleFunctionCallOrNull() ?: return@forEachDescendantOfType
val arguments = functionCall.argumentMapping.entries.toList()
val valueParameters = functionCall.partiallyAppliedSymbol.symbol.valueParameters
for ((argument, param) in arguments.asReversed()) {
val defaultValue = param.symbol.defaultValue
fun substituteDefaultValueWithPassedArguments(): @NlsSafe String? {
val key = Key<KtExpression>("SUBSTITUTION")
var needToSubstitute = false
defaultValue?.forEachDescendantOfType<KtSimpleNameExpression> {
val symbol = it.mainReference.resolveToSymbol()
if (symbol is KaValueParameterSymbol && symbol in valueParameters) {
it.putCopyableUserData(
key,
functionCall.argumentMapping.entries.firstOrNull { it.value.symbol == symbol }?.key
)
needToSubstitute = true
}
}
if (!needToSubstitute) return null
val copy = defaultValue!!.copy()
defaultValue.forEachDescendantOfType<KtSimpleNameExpression> {
it.putCopyableUserData(key, null)
}
copy.getCopyableUserData(key)?.let {
return it.text
}
copy.forEachDescendantOfType<KtSimpleNameExpression> { expr ->
val replacement = expr.getCopyableUserData(key)
if (replacement != null) {
expr.replace(replacement)
}
}
return copy.text
}
val substitutedValueText = substituteDefaultValueWithPassedArguments()
val valueArgument = argument.parent as? KtValueArgument ?: break
if (valueArgument.getCopyableUserData(DEFAULT_PARAMETER_VALUE_KEY) == null ||
defaultValue == null ||
!argument.isSemanticMatch(defaultValue) && (substitutedValueText == null || argument.text != substitutedValueText)) {
// for a named argument, we can try to drop arguments before it as well
if (!valueArgument.isNamed() && valueArgument !is KtLambdaArgument) break else continue
}
argumentsToDrop.add(valueArgument)
}
}
}
for (argument in argumentsToDrop) {
val argumentList = argument.parent as KtValueArgumentList
argumentList.removeArgument(argument)
if (argumentList.arguments.isEmpty()) {
val callExpression = argumentList.parent as KtCallElement
if (callExpression.lambdaArguments.isNotEmpty()) {
argumentList.delete()
}
}
}
}
}

View File

@@ -784,6 +784,41 @@ public abstract class KotlinFirInlineTestGenerated extends AbstractKotlinFirInli
runTest("../../idea/tests/testData/refactoring/inline/namedFunction/defaultParameter.kt");
}
@TestMetadata("defaultParameterDiffers.kt")
public void testDefaultParameterDiffers() throws Exception {
runTest("../../idea/tests/testData/refactoring/inline/namedFunction/defaultParameterDiffers.kt");
}
@TestMetadata("defaultParameterDiffers1.kt")
public void testDefaultParameterDiffers1() throws Exception {
runTest("../../idea/tests/testData/refactoring/inline/namedFunction/defaultParameterDiffers1.kt");
}
@TestMetadata("defaultParameterImplicitlyUsed.kt")
public void testDefaultParameterImplicitlyUsed() throws Exception {
runTest("../../idea/tests/testData/refactoring/inline/namedFunction/defaultParameterImplicitlyUsed.kt");
}
@TestMetadata("defaultParameterImplicitlyUsed1.kt")
public void testDefaultParameterImplicitlyUsed1() throws Exception {
runTest("../../idea/tests/testData/refactoring/inline/namedFunction/defaultParameterImplicitlyUsed1.kt");
}
@TestMetadata("defaultParameterImplicitlyUsed2.kt")
public void testDefaultParameterImplicitlyUsed2() throws Exception {
runTest("../../idea/tests/testData/refactoring/inline/namedFunction/defaultParameterImplicitlyUsed2.kt");
}
@TestMetadata("defaultParameterImplicitlyUsed3.kt")
public void testDefaultParameterImplicitlyUsed3() throws Exception {
runTest("../../idea/tests/testData/refactoring/inline/namedFunction/defaultParameterImplicitlyUsed3.kt");
}
@TestMetadata("defaultParameterImplicitlyUsed4.kt")
public void testDefaultParameterImplicitlyUsed4() throws Exception {
runTest("../../idea/tests/testData/refactoring/inline/namedFunction/defaultParameterImplicitlyUsed4.kt");
}
@TestMetadata("defaultParameterSubstitution.kt")
public void testDefaultParameterSubstitution() throws Exception {
runTest("../../idea/tests/testData/refactoring/inline/namedFunction/defaultParameterSubstitution.kt");