DeprecatedSymbolUsageFix: keeping functional literal arguments out of parenthesis
This commit is contained in:
@@ -52,6 +52,7 @@ public object OptionalParametersHelper {
|
|||||||
|
|
||||||
val arguments = resolvedCall.getCall().getValueArguments()
|
val arguments = resolvedCall.getCall().getValueArguments()
|
||||||
val argumentsToDrop = ArrayList<ValueArgument>()
|
val argumentsToDrop = ArrayList<ValueArgument>()
|
||||||
|
//TODO: can drop arguments leaving last functional literal one (outside of parenthesis)
|
||||||
for (argument in arguments.reverse()) {
|
for (argument in arguments.reverse()) {
|
||||||
if (!canDrop(argument)) break // TODO: not correct because of named arguments
|
if (!canDrop(argument)) break // TODO: not correct because of named arguments
|
||||||
if (!argument.matchesDefault(resolvedCall, parameterToDefaultValue)) break // TODO: not correct because of named arguments
|
if (!argument.matchesDefault(resolvedCall, parameterToDefaultValue)) break // TODO: not correct because of named arguments
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.idea.intentions.setType
|
|||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||||
|
import org.jetbrains.kotlin.idea.util.psiModificationUtil.moveFunctionLiteralOutsideParentheses
|
||||||
import org.jetbrains.kotlin.lexer.JetTokens
|
import org.jetbrains.kotlin.lexer.JetTokens
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.name.renderName
|
import org.jetbrains.kotlin.name.renderName
|
||||||
@@ -51,10 +52,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
|
||||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||||
@@ -241,6 +239,8 @@ public abstract class DeprecatedSymbolUsageFixBase(
|
|||||||
|
|
||||||
simplifySpreadArrayOfArguments(result)
|
simplifySpreadArrayOfArguments(result)
|
||||||
|
|
||||||
|
restoreFunctionLiteralArguments(result)
|
||||||
|
|
||||||
// clean up user data
|
// clean up user data
|
||||||
result.forEachDescendantOfType<JetExpression> {
|
result.forEachDescendantOfType<JetExpression> {
|
||||||
it.clear(USER_CODE_KEY)
|
it.clear(USER_CODE_KEY)
|
||||||
@@ -248,6 +248,7 @@ public abstract class DeprecatedSymbolUsageFixBase(
|
|||||||
it.clear(PARAMETER_VALUE_KEY)
|
it.clear(PARAMETER_VALUE_KEY)
|
||||||
it.clear(RECEIVER_VALUE_KEY)
|
it.clear(RECEIVER_VALUE_KEY)
|
||||||
it.clear(DEFAULT_PARAMETER_VALUE_KEY)
|
it.clear(DEFAULT_PARAMETER_VALUE_KEY)
|
||||||
|
it.clear(WAS_FUNCTION_LITERAL_ARGUMENT_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -420,8 +421,12 @@ public abstract class DeprecatedSymbolUsageFixBase(
|
|||||||
val resolvedArgument = resolvedCall.getValueArguments()[parameter]!!
|
val resolvedArgument = resolvedCall.getValueArguments()[parameter]!!
|
||||||
when (resolvedArgument) {
|
when (resolvedArgument) {
|
||||||
is ExpressionValueArgument -> {
|
is ExpressionValueArgument -> {
|
||||||
val expression = resolvedArgument.getValueArgument()!!.getArgumentExpression()!!
|
val valueArgument = resolvedArgument.getValueArgument()!!
|
||||||
|
val expression = valueArgument.getArgumentExpression()!!
|
||||||
expression.mark(USER_CODE_KEY)
|
expression.mark(USER_CODE_KEY)
|
||||||
|
if (valueArgument is FunctionLiteralArgument) {
|
||||||
|
expression.mark(WAS_FUNCTION_LITERAL_ARGUMENT_KEY)
|
||||||
|
}
|
||||||
return Argument(expression, expression, bindingContext.getType(expression), isDefaultValue = false)
|
return Argument(expression, expression, bindingContext.getType(expression), isDefaultValue = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,6 +578,32 @@ public abstract class DeprecatedSymbolUsageFixBase(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: do this before processing of optional parameters
|
||||||
|
private fun restoreFunctionLiteralArguments(expression: JetExpression) {
|
||||||
|
val callExpressions = ArrayList<JetCallExpression>()
|
||||||
|
|
||||||
|
expression.forEachDescendantOfType<JetExpression>( fun (expr: JetExpression) {
|
||||||
|
if (!expr[WAS_FUNCTION_LITERAL_ARGUMENT_KEY]) return
|
||||||
|
assert(expr.unpackFunctionLiteral() != null)
|
||||||
|
|
||||||
|
val argument = expr.getParent() as? JetValueArgument ?: return
|
||||||
|
if (argument is JetFunctionLiteralArgument) return
|
||||||
|
if (argument.isNamed()) return
|
||||||
|
val argumentList = argument.getParent() as? JetValueArgumentList ?: return
|
||||||
|
if (argument != argumentList.getArguments().last()) return
|
||||||
|
val callExpression = argumentList.getParent() as? JetCallExpression ?: return
|
||||||
|
if (callExpression.getFunctionLiteralArguments().isNotEmpty()) return
|
||||||
|
|
||||||
|
val resolvedCall = callExpression.getResolvedCall(callExpression.analyze(BodyResolveMode.PARTIAL)) ?: return
|
||||||
|
val argumentMatch = resolvedCall.getArgumentMapping(argument) as? ArgumentMatch ?: return
|
||||||
|
if (argumentMatch.valueParameter != resolvedCall.getResultingDescriptor().getValueParameters().last()) return
|
||||||
|
|
||||||
|
callExpressions.add(callExpression)
|
||||||
|
})
|
||||||
|
|
||||||
|
callExpressions.forEach { it.moveFunctionLiteralOutsideParentheses() }
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: making functions below private causes VerifyError
|
//TODO: making functions below private causes VerifyError
|
||||||
fun <T: Any> PsiElement.get(key: Key<T>): T? = getCopyableUserData(key)
|
fun <T: Any> PsiElement.get(key: Key<T>): T? = getCopyableUserData(key)
|
||||||
fun PsiElement.get(key: Key<Unit>): Boolean = getCopyableUserData(key) != null
|
fun PsiElement.get(key: Key<Unit>): Boolean = getCopyableUserData(key) != null
|
||||||
@@ -589,6 +620,7 @@ public abstract class DeprecatedSymbolUsageFixBase(
|
|||||||
private val PARAMETER_VALUE_KEY = Key<ValueParameterDescriptor>("PARAMETER_VALUE")
|
private val PARAMETER_VALUE_KEY = Key<ValueParameterDescriptor>("PARAMETER_VALUE")
|
||||||
private val RECEIVER_VALUE_KEY = Key<Unit>("RECEIVER_VALUE")
|
private val RECEIVER_VALUE_KEY = Key<Unit>("RECEIVER_VALUE")
|
||||||
private val DEFAULT_PARAMETER_VALUE_KEY = Key<Unit>("DEFAULT_PARAMETER_VALUE")
|
private val DEFAULT_PARAMETER_VALUE_KEY = Key<Unit>("DEFAULT_PARAMETER_VALUE")
|
||||||
|
private val WAS_FUNCTION_LITERAL_ARGUMENT_KEY = Key<Unit>("WAS_FUNCTION_LITERAL_ARGUMENT")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// "Replace with 'newFun(p1, p2)'" "true"
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
@deprecated("", ReplaceWith("newFun(p1, p2)"))
|
||||||
|
fun oldFun(p1: String, p2: () -> Boolean)
|
||||||
|
|
||||||
|
fun newFun(p1: String, p2: () -> Boolean, p3: String? = null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(i: I) {
|
||||||
|
i.<caret>oldFun("a") { true }
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// "Replace with 'newFun(p1, p2)'" "true"
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
@deprecated("", ReplaceWith("newFun(p1, p2)"))
|
||||||
|
fun oldFun(p1: String, p2: () -> Boolean)
|
||||||
|
|
||||||
|
fun newFun(p1: String, p2: () -> Boolean, p3: String? = null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(i: I) {
|
||||||
|
i.<caret>newFun("a", { true })
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// "Replace with 'newFun(p1, null, p2)'" "true"
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
@deprecated("", ReplaceWith("newFun(p1, null, p2)"))
|
||||||
|
fun oldFun(p1: String, p2: () -> Boolean)
|
||||||
|
|
||||||
|
fun newFun(p1: String, p2: String?, p3: () -> Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(i: I) {
|
||||||
|
i.<caret>oldFun("a", { true })
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// "Replace with 'newFun(p1, null, p2)'" "true"
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
@deprecated("", ReplaceWith("newFun(p1, null, p2)"))
|
||||||
|
fun oldFun(p1: String, p2: () -> Boolean)
|
||||||
|
|
||||||
|
fun newFun(p1: String, p2: String?, p3: () -> Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(i: I) {
|
||||||
|
i.<caret>newFun("a", null, { true })
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// "Replace with 'newFun(p1, null, p2)'" "true"
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
@deprecated("", ReplaceWith("newFun(p1, null, p2)"))
|
||||||
|
fun oldFun(p1: String, p2: () -> Boolean)
|
||||||
|
|
||||||
|
fun newFun(p1: String, p2: String?, p3: () -> Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(i: I) {
|
||||||
|
i.<caret>oldFun("a") { true }
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// "Replace with 'newFun(p1, null, p2)'" "true"
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
@deprecated("", ReplaceWith("newFun(p1, null, p2)"))
|
||||||
|
fun oldFun(p1: String, p2: () -> Boolean)
|
||||||
|
|
||||||
|
fun newFun(p1: String, p2: String?, p3: () -> Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(i: I) {
|
||||||
|
i.<caret>newFun("a", null) { true }
|
||||||
|
}
|
||||||
@@ -3163,6 +3163,33 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/functionLiteralArguments")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class FunctionLiteralArguments extends AbstractQuickFixTest {
|
||||||
|
public void testAllFilesPresentInFunctionLiteralArguments() throws Exception {
|
||||||
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/functionLiteralArguments"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("cannotKeepOutside.kt")
|
||||||
|
public void testCannotKeepOutside() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/functionLiteralArguments/cannotKeepOutside.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("keepInside.kt")
|
||||||
|
public void testKeepInside() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/functionLiteralArguments/keepInside.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("keepOutside.kt")
|
||||||
|
public void testKeepOutside() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/functionLiteralArguments/keepOutside.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/imports")
|
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/imports")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user