KT-7144 Intention action on an argument in a call to use named arguments form
#KT-7144 Fixed
This commit is contained in:
@@ -354,12 +354,12 @@ public class JetPsiFactory(private val project: Project) {
|
||||
createExpressionByPattern("if ($0) $1", condition, thenExpr)) as JetIfExpression
|
||||
}
|
||||
|
||||
public fun createArgument(expression: JetExpression, name: String? = null, isSpread: Boolean = false): JetValueArgument {
|
||||
public fun createArgument(expression: JetExpression, name: Name? = null, isSpread: Boolean = false): JetValueArgument {
|
||||
val argumentList = buildByPattern({ pattern, args -> createByPattern(pattern, *args) { createCallArguments(it) } }) {
|
||||
appendFixedText("(")
|
||||
|
||||
if (name != null) {
|
||||
appendName(Name.identifier(name))
|
||||
appendName(name)
|
||||
appendFixedText("=")
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentsInParentheses
|
||||
@@ -43,10 +43,10 @@ public fun JetFunctionLiteralArgument.moveInsideParentheses(bindingContext: Bind
|
||||
return moveInsideParenthesesAndReplaceWith(this.getArgumentExpression(), bindingContext)
|
||||
}
|
||||
|
||||
public fun JetFunctionLiteralArgument.getFunctionLiteralArgumentName(bindingContext: BindingContext): String? {
|
||||
public fun JetFunctionLiteralArgument.getFunctionLiteralArgumentName(bindingContext: BindingContext): Name? {
|
||||
val callExpression = getParent() as JetCallExpression
|
||||
val resolvedCall = callExpression.getResolvedCall(bindingContext)
|
||||
return (resolvedCall?.getArgumentMapping(this) as? ArgumentMatch)?.valueParameter?.getName()?.toString()
|
||||
return (resolvedCall?.getArgumentMapping(this) as? ArgumentMatch)?.valueParameter?.getName()
|
||||
}
|
||||
|
||||
public fun JetFunctionLiteralArgument.moveInsideParenthesesAndReplaceWith(
|
||||
@@ -56,7 +56,7 @@ public fun JetFunctionLiteralArgument.moveInsideParenthesesAndReplaceWith(
|
||||
|
||||
public fun JetFunctionLiteralArgument.moveInsideParenthesesAndReplaceWith(
|
||||
replacement: JetExpression,
|
||||
functionLiteralArgumentName: String?
|
||||
functionLiteralArgumentName: Name?
|
||||
): JetCallExpression {
|
||||
val oldCallExpression = getParent() as JetCallExpression
|
||||
val newCallExpression = oldCallExpression.copy() as JetCallExpression
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
list.joinToString(<spot>separator = </spot>"\n")
|
||||
@@ -0,0 +1 @@
|
||||
list.joinToString("\n")
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention adds name to an argument of a function call to use named argument syntax.
|
||||
</body>
|
||||
</html>
|
||||
@@ -923,6 +923,11 @@
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.AddNameToArgumentIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.DeprecatedCallableAddReplaceWithInspection"
|
||||
displayName="Add 'replaceWith' argument to 'deprecated' annotation"
|
||||
groupName="Kotlin"
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.conversion.copy.end
|
||||
import org.jetbrains.kotlin.idea.conversion.copy.start
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatchStatus
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
public class AddNameToArgumentIntention
|
||||
: JetSelfTargetingIntention<JetValueArgument>(javaClass(), "Use named argument"), LowPriorityAction {
|
||||
|
||||
override fun isApplicableTo(element: JetValueArgument, caretOffset: Int): Boolean {
|
||||
val expression = element.getArgumentExpression() ?: return false
|
||||
if (detectNameToAdd(element) == null) return false
|
||||
|
||||
if (expression is JetFunctionLiteralExpression) {
|
||||
val range = expression.getTextRange()
|
||||
return caretOffset == range.start || caretOffset == range.end
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun applyTo(element: JetValueArgument, editor: Editor) {
|
||||
val name = detectNameToAdd(element)!!
|
||||
val newArgument = JetPsiFactory(element).createArgument(element.getArgumentExpression()!!, name, element.getSpreadElement() != null)
|
||||
element.replace(newArgument)
|
||||
}
|
||||
|
||||
private fun detectNameToAdd(argument: JetValueArgument): Name? {
|
||||
if (argument.isNamed()) return null
|
||||
if (argument is JetFunctionLiteralArgument) return null
|
||||
|
||||
val argumentList = argument.getParent() as? JetValueArgumentList ?: return null
|
||||
if (argument != argumentList.getArguments().last()) return null
|
||||
|
||||
val callExpr = argumentList.getParent() as? JetExpression ?: return null
|
||||
val resolvedCall = callExpr.getResolvedCall(callExpr.analyze(BodyResolveMode.PARTIAL)) ?: return null
|
||||
val argumentMatch = resolvedCall.getArgumentMapping(argument) as? ArgumentMatch ?: return null
|
||||
if (argumentMatch.status != ArgumentMatchStatus.SUCCESS) return null
|
||||
|
||||
if (!resolvedCall.getResultingDescriptor().hasStableParameterNames()) return null
|
||||
|
||||
if (argumentMatch.valueParameter.getVarargElementType() != null) {
|
||||
val varargArgument = resolvedCall.getValueArguments()[argumentMatch.valueParameter] as? VarargValueArgument ?: return null
|
||||
if (varargArgument.getArguments().size() != 1) return null
|
||||
}
|
||||
|
||||
return argumentMatch.valueParameter.getName()
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.idea.JetIcons;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
|
||||
import org.jetbrains.kotlin.idea.core.CorePackage;
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.JetCallElement;
|
||||
import org.jetbrains.kotlin.psi.JetExpression;
|
||||
import org.jetbrains.kotlin.psi.JetFile;
|
||||
@@ -147,7 +148,7 @@ public class AddNameToArgumentFix extends JetIntentionAction<JetValueArgument> {
|
||||
private static JetValueArgument getParsedArgumentWithName(@NotNull String name, @NotNull JetValueArgument argument) {
|
||||
JetExpression argumentExpression = argument.getArgumentExpression();
|
||||
assert argumentExpression != null : "Argument should be already parsed.";
|
||||
return JetPsiFactory(argument).createArgument(argumentExpression, name, false);
|
||||
return JetPsiFactory(argument).createArgument(argumentExpression, Name.identifier(name), argument.getSpreadElement() != null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -23,17 +23,15 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||
import org.jetbrains.kotlin.idea.JetBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.core.getFunctionLiteralArgumentName
|
||||
import org.jetbrains.kotlin.idea.core.moveInsideParenthesesAndReplaceWith
|
||||
import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.core.getFunctionLiteralArgumentName
|
||||
import org.jetbrains.kotlin.idea.core.moveInsideParenthesesAndReplaceWith
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import java.util.ArrayList
|
||||
|
||||
public class DeprecatedLambdaSyntaxFix(element: JetFunctionLiteralExpression) : JetIntentionAction<JetFunctionLiteralExpression>(element), CleanupFix {
|
||||
override fun getText() = JetBundle.message("migrate.lambda.syntax")
|
||||
@@ -102,7 +100,7 @@ private class DeparenthesizeParameterList(
|
||||
private class LambdaToFunctionExpression(
|
||||
val functionLiteralExpression: JetFunctionLiteralExpression
|
||||
): DeprecatedSyntaxFix {
|
||||
val functionLiteralArgumentName: String?
|
||||
val functionLiteralArgumentName: Name?
|
||||
val receiverType: String?
|
||||
val returnType: String?
|
||||
|
||||
|
||||
@@ -485,7 +485,7 @@ public abstract class DeprecatedSymbolUsageFixBase(
|
||||
if (argument.isNamed()) continue
|
||||
if (argument is JetFunctionLiteralArgument) continue
|
||||
val argumentMatch = resolvedCall.getArgumentMapping(argument) as ArgumentMatch
|
||||
val name = argumentMatch.valueParameter.getName().asString()
|
||||
val name = argumentMatch.valueParameter.getName()
|
||||
//TODO: not always correct for vararg's
|
||||
val newArgument = psiFactory.createArgument(argument.getArgumentExpression()!!, name, argument.getSpreadElement() != null)
|
||||
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ public class ReplaceJavaAnnotationPositionedArgumentsFix(element: JetAnnotationE
|
||||
val valueArgument = (argument.value as? ExpressionValueArgument)?.getValueArgument() ?: return@argumentProcessor
|
||||
val expression = valueArgument.getArgumentExpression() ?: return@argumentProcessor
|
||||
|
||||
valueArgument.asElement().replace(psiFactory.createArgument(expression, argument.getKey().getName().asString()))
|
||||
valueArgument.asElement().replace(psiFactory.createArgument(expression, argument.getKey().getName()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.AddNameToArgumentIntention
|
||||
@@ -0,0 +1,6 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(s: String, b: Boolean){}
|
||||
|
||||
fun bar() {
|
||||
foo("", b = true<caret>)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: <html>None of the following functions can be called with the arguments supplied. <ul><li>foo(String, Boolean, <font color=red><b>Char</b></font>) <i>defined in</i> root package</li><li>foo(String, Boolean, <font color=red><b>Int</b></font>) <i>defined in</i> root package</li></ul></html>
|
||||
fun foo(s: String, b: Boolean, p: Int){}
|
||||
fun foo(s: String, b: Boolean, c: Char){}
|
||||
|
||||
fun bar() {
|
||||
foo("", <caret>true)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(s: String, handler: () -> Unit){}
|
||||
|
||||
fun bar() {
|
||||
foo("") <caret>{ }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// ERROR: No value passed for parameter p
|
||||
|
||||
fun foo(s: String, b: Boolean, p: Int){}
|
||||
|
||||
fun bar() {
|
||||
foo("", <caret>true)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// ERROR: No value passed for parameter p
|
||||
|
||||
fun foo(s: String, b: Boolean, p: Int){}
|
||||
|
||||
fun bar() {
|
||||
foo("", <caret>b = true)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// IS_APPLICABLE: false
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun f() {
|
||||
java.io.File(<caret>"file")
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(s: String, b: Boolean){}
|
||||
|
||||
fun bar() {
|
||||
foo(<caret>"", true)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Unresolved reference: foo
|
||||
fun bar() {
|
||||
foo("", <caret>true)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(handler: () -> Unit){}
|
||||
|
||||
fun bar() {
|
||||
foo(<caret>{ })
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(handler: () -> Unit){}
|
||||
|
||||
fun bar() {
|
||||
foo(<caret>handler = { })
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(handler: () -> Unit){}
|
||||
|
||||
fun bar() {
|
||||
foo({ }<caret>)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(handler: () -> Unit){}
|
||||
|
||||
fun bar() {
|
||||
foo(handler = { }<caret>)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// IS_APPLICABLE: false
|
||||
|
||||
fun foo(handler: () -> Unit){}
|
||||
|
||||
fun bar() {
|
||||
foo({<caret>})
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(s: String, b: Boolean){}
|
||||
|
||||
fun bar() {
|
||||
foo("", true<caret>)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(s: String, b: Boolean){}
|
||||
|
||||
fun bar() {
|
||||
foo("", b = true<caret>)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(vararg s: String){}
|
||||
|
||||
fun bar() {
|
||||
foo(""<caret>)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(vararg s: String){}
|
||||
|
||||
fun bar() {
|
||||
foo(s = ""<caret>)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(vararg s: String){}
|
||||
|
||||
fun bar(array: Array<String>) {
|
||||
foo(<caret>*array)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(vararg s: String){}
|
||||
|
||||
fun bar(array: Array<String>) {
|
||||
foo(<caret>s = *array)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// IS_APPLICABLE: false
|
||||
|
||||
fun foo(vararg s: String){}
|
||||
|
||||
fun bar() {
|
||||
foo("a", "b"<caret>)
|
||||
}
|
||||
@@ -92,6 +92,99 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/addNameToArgument")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class AddNameToArgument extends AbstractIntentionTest {
|
||||
public void testAllFilesPresentInAddNameToArgument() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/addNameToArgument"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("alreadyNamed.kt")
|
||||
public void testAlreadyNamed() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/alreadyNamed.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ambiguousCall.kt")
|
||||
public void testAmbiguousCall() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/ambiguousCall.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("functionLiteralArgument.kt")
|
||||
public void testFunctionLiteralArgument() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/functionLiteralArgument.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("incompleteCall.kt")
|
||||
public void testIncompleteCall() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/incompleteCall.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("javaMethod.kt")
|
||||
public void testJavaMethod() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/javaMethod.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notLast.kt")
|
||||
public void testNotLast() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/notLast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notResolved.kt")
|
||||
public void testNotResolved() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/notResolved.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("rangeForLambda1.kt")
|
||||
public void testRangeForLambda1() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/rangeForLambda1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("rangeForLambda2.kt")
|
||||
public void testRangeForLambda2() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/rangeForLambda2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("rangeForLambda3.kt")
|
||||
public void testRangeForLambda3() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/rangeForLambda3.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("vararg1.kt")
|
||||
public void testVararg1() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/vararg1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("vararg2.kt")
|
||||
public void testVararg2() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/vararg2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("vararg3.kt")
|
||||
public void testVararg3() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/vararg3.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/branched")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user