diff --git a/ChangeLog.md b/ChangeLog.md index 1d62b57e89c..dd6fc16b63e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -338,6 +338,10 @@ These artifacts include extensions for the types available in the latter JDKs, s - [`KT-14044`](https://youtrack.jetbrains.com/issue/KT-14044) Fix exception on deleting unused declaration in IDEA 2016.3 - [`KT-14019`](https://youtrack.jetbrains.com/issue/KT-14019) Create from Usage: Support generation of abstract members for superclasses +##### New features + +- [`KT-14729`](https://youtrack.jetbrains.com/issue/KT-14729) Implement "Add names to call arguments" intention + #### Refactorings - [`KT-14583`](https://youtrack.jetbrains.com/issue/KT-14583) Change Signature: Use new signature when looking for redeclaration conflicts diff --git a/idea/resources/intentionDescriptions/AddNamesToCallArgumentsIntention/after.kt.template b/idea/resources/intentionDescriptions/AddNamesToCallArgumentsIntention/after.kt.template new file mode 100644 index 00000000000..0ddeeb21c57 --- /dev/null +++ b/idea/resources/intentionDescriptions/AddNamesToCallArgumentsIntention/after.kt.template @@ -0,0 +1,7 @@ +fun foo(x: Int, y: Int, comment: String) { + +} + +fun test() { + foo(x = 24, y = 42, comment = "Hello") +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/AddNamesToCallArgumentsIntention/before.kt.template b/idea/resources/intentionDescriptions/AddNamesToCallArgumentsIntention/before.kt.template new file mode 100644 index 00000000000..a4dd055e872 --- /dev/null +++ b/idea/resources/intentionDescriptions/AddNamesToCallArgumentsIntention/before.kt.template @@ -0,0 +1,7 @@ +fun foo(x: Int, y: Int, comment: String) { + +} + +fun test() { + foo(24, 42, "Hello") +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/AddNamesToCallArgumentsIntention/description.html b/idea/resources/intentionDescriptions/AddNamesToCallArgumentsIntention/description.html new file mode 100644 index 00000000000..a34564564be --- /dev/null +++ b/idea/resources/intentionDescriptions/AddNamesToCallArgumentsIntention/description.html @@ -0,0 +1,5 @@ + + +This intention adds names to all positional arguments of a function call according to the named argument syntax. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index c2cf48a6e7d..cdabd048326 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1,4 +1,4 @@ - + org.jetbrains.kotlin Kotlin @@ -1451,6 +1451,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.AddNamesToCallArgumentsIntention + Kotlin + + ( + KtCallElement::class.java, + "Add names to call arguments" +) { + override fun applicabilityRange(element: KtCallElement): TextRange? { + val arguments = element.valueArguments + if (arguments.all { it.isNamed() }) return null + + val resolvedCall = element.getResolvedCall(element.analyze(BodyResolveMode.PARTIAL)) ?: return null + if (!resolvedCall.resultingDescriptor.hasStableParameterNames()) return null + + for (argument in arguments) { + val argumentMatch = resolvedCall.getArgumentMapping(argument) as? ArgumentMatch ?: return null + if (argumentMatch.status != ArgumentMatchStatus.SUCCESS) return null + + if (argumentMatch.valueParameter.varargElementType != null) { + val varargArgument = resolvedCall.valueArguments[argumentMatch.valueParameter] as? VarargValueArgument ?: return null + if (varargArgument.arguments.size != 1) return null + } + } + + return element.calleeExpression?.textRange + } + + override fun applyTo(element: KtCallElement, editor: Editor?) { + val arguments = element.valueArguments + val resolvedCall = element.getResolvedCall(element.analyze(BodyResolveMode.PARTIAL)) ?: return + for (argument in arguments) { + if (argument !is KtValueArgument || argument is KtLambdaArgument) continue + val argumentMatch = resolvedCall.getArgumentMapping(argument) as? ArgumentMatch ?: continue + val name = argumentMatch.valueParameter.name + val newArgument = KtPsiFactory(element).createArgument(argument.getArgumentExpression()!!, + name, + argument.getSpreadElement() != null) + argument.replace(newArgument) + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/.intention b/idea/testData/intentions/addNamesToCallArguments/.intention new file mode 100644 index 00000000000..ae1bbf39288 --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.AddNamesToCallArgumentsIntention diff --git a/idea/testData/intentions/addNamesToCallArguments/allNamed.kt b/idea/testData/intentions/addNamesToCallArguments/allNamed.kt new file mode 100644 index 00000000000..e7399dd8fa7 --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/allNamed.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +fun foo(s: String, b: Boolean){} + +fun bar() { + foo(s = "", b = true) +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/ambiguousCall.kt b/idea/testData/intentions/addNamesToCallArguments/ambiguousCall.kt new file mode 100644 index 00000000000..1e749605207 --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/ambiguousCall.kt @@ -0,0 +1,8 @@ +// IS_APPLICABLE: false +// ERROR: None of the following functions can be called with the arguments supplied:
public fun foo(s: String, b: Boolean, c: Char): Unit defined in root package
public fun foo(s: String, b: Boolean, p: Int): Unit defined in root package +fun foo(s: String, b: Boolean, p: Int){} +fun foo(s: String, b: Boolean, c: Char){} + +fun bar() { + foo("", true) +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/incompleteCall.kt b/idea/testData/intentions/addNamesToCallArguments/incompleteCall.kt new file mode 100644 index 00000000000..de8372d626c --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/incompleteCall.kt @@ -0,0 +1,7 @@ +// ERROR: No value passed for parameter p + +fun foo(s: String, b: Boolean, p: Int){} + +fun bar() { + foo("", true) +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/incompleteCall.kt.after b/idea/testData/intentions/addNamesToCallArguments/incompleteCall.kt.after new file mode 100644 index 00000000000..c21b7fe362a --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/incompleteCall.kt.after @@ -0,0 +1,7 @@ +// ERROR: No value passed for parameter p + +fun foo(s: String, b: Boolean, p: Int){} + +fun bar() { + foo(s = "", b = true) +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/javaMethod.kt b/idea/testData/intentions/addNamesToCallArguments/javaMethod.kt new file mode 100644 index 00000000000..3f2be7469d2 --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/javaMethod.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME + +fun f() { + java.io.File("file") +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/notOnCallee.kt b/idea/testData/intentions/addNamesToCallArguments/notOnCallee.kt new file mode 100644 index 00000000000..f9656f650f9 --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/notOnCallee.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +fun foo(s: String){} + +fun bar() { + foo("") +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/notResolved.kt b/idea/testData/intentions/addNamesToCallArguments/notResolved.kt new file mode 100644 index 00000000000..cace3a6e77b --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/notResolved.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +// ERROR: Unresolved reference: foo +fun bar() { + foo("", true) +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/simple.kt b/idea/testData/intentions/addNamesToCallArguments/simple.kt new file mode 100644 index 00000000000..1916303271f --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/simple.kt @@ -0,0 +1,5 @@ +fun foo(s: String, b: Boolean){} + +fun bar() { + foo("", true) +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/simple.kt.after b/idea/testData/intentions/addNamesToCallArguments/simple.kt.after new file mode 100644 index 00000000000..a1a10dd0c27 --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/simple.kt.after @@ -0,0 +1,5 @@ +fun foo(s: String, b: Boolean){} + +fun bar() { + foo(s = "", b = true) +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/superClassConstructor.kt b/idea/testData/intentions/addNamesToCallArguments/superClassConstructor.kt new file mode 100644 index 00000000000..49fc21e07ad --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/superClassConstructor.kt @@ -0,0 +1,3 @@ +open class C(p1: Int, p2: Int) + +class D : C(1, 2) \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/superClassConstructor.kt.after b/idea/testData/intentions/addNamesToCallArguments/superClassConstructor.kt.after new file mode 100644 index 00000000000..ddf17ca8ebf --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/superClassConstructor.kt.after @@ -0,0 +1,3 @@ +open class C(p1: Int, p2: Int) + +class D : C(p1 = 1, p2 = 2) \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/varargMultiple.kt b/idea/testData/intentions/addNamesToCallArguments/varargMultiple.kt new file mode 100644 index 00000000000..37dbd27be7e --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/varargMultiple.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +fun foo(n: Int, vararg s: String){} + +fun bar() { + foo(1, "2", "3") +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/varargSingle.kt b/idea/testData/intentions/addNamesToCallArguments/varargSingle.kt new file mode 100644 index 00000000000..5fa6e178c5e --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/varargSingle.kt @@ -0,0 +1,5 @@ +fun foo(n: Int, vararg s: String){} + +fun bar() { + foo(1, "") +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/varargSingle.kt.after b/idea/testData/intentions/addNamesToCallArguments/varargSingle.kt.after new file mode 100644 index 00000000000..8cb4d242706 --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/varargSingle.kt.after @@ -0,0 +1,5 @@ +fun foo(n: Int, vararg s: String){} + +fun bar() { + foo(n = 1, s = "") +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/varargSingleWithSpread.kt b/idea/testData/intentions/addNamesToCallArguments/varargSingleWithSpread.kt new file mode 100644 index 00000000000..d9264b256df --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/varargSingleWithSpread.kt @@ -0,0 +1,5 @@ +fun foo(n: Int, vararg s: String){} + +fun bar(array: Array) { + foo(1, *array) +} \ No newline at end of file diff --git a/idea/testData/intentions/addNamesToCallArguments/varargSingleWithSpread.kt.after b/idea/testData/intentions/addNamesToCallArguments/varargSingleWithSpread.kt.after new file mode 100644 index 00000000000..eab0e0816a4 --- /dev/null +++ b/idea/testData/intentions/addNamesToCallArguments/varargSingleWithSpread.kt.after @@ -0,0 +1,5 @@ +fun foo(n: Int, vararg s: String){} + +fun bar(array: Array) { + foo(n = 1, s = *array) +} \ No newline at end of file diff --git a/idea/testData/quickfix/increaseVisibility/privateMemberToInternalWithExposed.kt b/idea/testData/quickfix/increaseVisibility/privateMemberToInternalWithExposed.kt index 2147a0a9a75..42f67345325 100644 --- a/idea/testData/quickfix/increaseVisibility/privateMemberToInternalWithExposed.kt +++ b/idea/testData/quickfix/increaseVisibility/privateMemberToInternalWithExposed.kt @@ -1,5 +1,6 @@ // "Make bar internal" "false" // ACTION: Convert property initializer to getter +// ACTION: Add names to call arguments // ERROR: Cannot access 'bar': it is private in 'First' private data class Data(val x: Int) diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index c965a775a2c..3b9ae49b651 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -417,6 +417,81 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/addNamesToCallArguments") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class AddNamesToCallArguments extends AbstractIntentionTest { + public void testAllFilesPresentInAddNamesToCallArguments() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/addNamesToCallArguments"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("allNamed.kt") + public void testAllNamed() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addNamesToCallArguments/allNamed.kt"); + doTest(fileName); + } + + @TestMetadata("ambiguousCall.kt") + public void testAmbiguousCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addNamesToCallArguments/ambiguousCall.kt"); + doTest(fileName); + } + + @TestMetadata("incompleteCall.kt") + public void testIncompleteCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addNamesToCallArguments/incompleteCall.kt"); + doTest(fileName); + } + + @TestMetadata("javaMethod.kt") + public void testJavaMethod() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addNamesToCallArguments/javaMethod.kt"); + doTest(fileName); + } + + @TestMetadata("notOnCallee.kt") + public void testNotOnCallee() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addNamesToCallArguments/notOnCallee.kt"); + doTest(fileName); + } + + @TestMetadata("notResolved.kt") + public void testNotResolved() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addNamesToCallArguments/notResolved.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addNamesToCallArguments/simple.kt"); + doTest(fileName); + } + + @TestMetadata("superClassConstructor.kt") + public void testSuperClassConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addNamesToCallArguments/superClassConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("varargMultiple.kt") + public void testVarargMultiple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addNamesToCallArguments/varargMultiple.kt"); + doTest(fileName); + } + + @TestMetadata("varargSingle.kt") + public void testVarargSingle() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addNamesToCallArguments/varargSingle.kt"); + doTest(fileName); + } + + @TestMetadata("varargSingleWithSpread.kt") + public void testVarargSingleWithSpread() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addNamesToCallArguments/varargSingleWithSpread.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/addOperatorModifier") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)