From 9dd217eee343d82005a675edb21d96e0f0087a77 Mon Sep 17 00:00:00 2001 From: Dmitry Neverov Date: Mon, 24 Apr 2017 23:01:35 +0300 Subject: [PATCH] Introduce intention to name anonymous parameter #KT-17191 Fixed --- .../after.kt.template | 1 + .../before.kt.template | 1 + .../description.html | 5 + idea/src/META-INF/plugin.xml | 5 + ...aceUnderscoreWithParameterNameIntention.kt | 96 +++++++++++++++++++ .../.intention | 1 + .../anonymous.kt | 1 + .../anonymous.kt.after | 1 + .../conflict.kt | 6 ++ .../conflict.kt.after | 6 ++ .../destructuringDeclaration.kt | 5 + .../destructuringDeclaration.kt.after | 5 + .../replaceUnderscoreWithParameterName/for.kt | 7 ++ .../for.kt.after | 7 ++ .../lambda.kt | 7 ++ .../lambda.kt.after | 7 ++ .../lambdaConflict.kt | 7 ++ .../lambdaConflict.kt.after | 7 ++ .../lambdaNoNames.kt | 7 ++ .../lambdaNoNames.kt.after | 7 ++ .../replaceUnderscoreWithParameterName/map.kt | 7 ++ .../map.kt.after | 7 ++ .../triple.kt | 5 + .../triple.kt.after | 5 + .../intentions/IntentionTestGenerated.java | 63 ++++++++++++ 25 files changed, 276 insertions(+) create mode 100644 idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/description.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceUnderscoreWithParameterNameIntention.kt create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/.intention create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/anonymous.kt create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/anonymous.kt.after create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/conflict.kt create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/conflict.kt.after create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/destructuringDeclaration.kt create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/destructuringDeclaration.kt.after create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/for.kt create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/for.kt.after create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/lambda.kt create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/lambda.kt.after create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaConflict.kt create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaConflict.kt.after create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaNoNames.kt create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaNoNames.kt.after create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/map.kt create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/map.kt.after create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/triple.kt create mode 100644 idea/testData/intentions/replaceUnderscoreWithParameterName/triple.kt.after diff --git a/idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/after.kt.template b/idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/after.kt.template new file mode 100644 index 00000000000..122ee7186c8 --- /dev/null +++ b/idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/after.kt.template @@ -0,0 +1 @@ +val (first, second) = Pair(1, 2) \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/before.kt.template b/idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/before.kt.template new file mode 100644 index 00000000000..38dda8ddbc0 --- /dev/null +++ b/idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/before.kt.template @@ -0,0 +1 @@ +val (first, _) = Pair(1, 2) \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/description.html b/idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/description.html new file mode 100644 index 00000000000..ab211677b10 --- /dev/null +++ b/idea/resources/intentionDescriptions/ReplaceUnderscoreWithParameterNameIntention/description.html @@ -0,0 +1,5 @@ + + +Gives name to an anonymous parameter + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 0e5e9e50902..95c91aa5d53 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1265,6 +1265,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.ReplaceUnderscoreWithParameterNameIntention + Kotlin + + org.jetbrains.kotlin.idea.intentions.AddJvmOverloadsIntention Kotlin diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceUnderscoreWithParameterNameIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceUnderscoreWithParameterNameIntention.kt new file mode 100644 index 00000000000..a02877f6a26 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceUnderscoreWithParameterNameIntention.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2010-2017 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.openapi.editor.Editor +import org.jetbrains.kotlin.builtins.extractParameterNameFromFunctionTypeArgument +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.core.CollectingNameValidator +import org.jetbrains.kotlin.idea.core.KotlinNameSuggester +import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument +import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode + +class ReplaceUnderscoreWithParameterNameIntention : SelfTargetingOffsetIndependentIntention( + KtCallableDeclaration::class.java, + "Replace '_' with parameter name" +) { + override fun isApplicableTo(element: KtCallableDeclaration) = + element.name == "_" && (element is KtDestructuringDeclarationEntry || element is KtParameter) + + override fun applyTo(element: KtCallableDeclaration, editor: Editor?) { + val suggestedParameterName = suggestedParameterName(element) + val validator = CollectingNameValidator( + filter = NewDeclarationNameValidator(element.parent.parent, null, NewDeclarationNameValidator.Target.VARIABLES) + ) + val name = suggestedParameterName?.let { + KotlinNameSuggester.suggestNameByName(it, validator) + } ?: run { + val elementDescriptor = element.resolveToDescriptorIfAny() as? CallableDescriptor + elementDescriptor?.returnType?.let { KotlinNameSuggester.suggestNamesByType(it, validator).firstOrNull() } + } ?: return + element.setName(name) + } + + private fun suggestedParameterName(element: KtCallableDeclaration) = + when (element) { + is KtDestructuringDeclarationEntry -> dataClassParameterName(element) + is KtParameter -> lambdaParameterName(element) + else -> null + } + + private fun lambdaParameterName(element: KtParameter): String? { + val functionLiteral = element.getParentOfType(strict = true) ?: return null + val idx = functionLiteral.valueParameters.indexOf(element) + if (idx == -1) return null + val context = functionLiteral.analyze(BodyResolveMode.PARTIAL) + val resolvedCall = element.getParentResolvedCall(context) + val lambdaArgument = functionLiteral.getParentOfType(strict = true) ?: return null + val lambdaParam = resolvedCall?.getParameterForArgument(lambdaArgument) ?: return null + return lambdaParam.type.arguments.getOrNull(idx)?.type?.extractParameterNameFromFunctionTypeArgument()?.asString() + } + + private fun dataClassParameterName(declarationEntry: KtDestructuringDeclarationEntry): String? { + val context = declarationEntry.analyze() + val componentResolvedCall = context[BindingContext.COMPONENT_RESOLVED_CALL, declarationEntry] ?: return null + val receiver = componentResolvedCall.dispatchReceiver ?: componentResolvedCall.extensionReceiver ?: return null + val classDescriptor = receiver.type.constructor.declarationDescriptor as? ClassDescriptor ?: return null + return when { + classDescriptor.isData -> { + val primaryParameters = classDescriptor.unsubstitutedPrimaryConstructor?.valueParameters + primaryParameters?.getOrNull(declarationEntry.entryIndex())?.name?.asString() + } + DescriptorUtils.isSubclass(classDescriptor, classDescriptor.builtIns.mapEntry) -> { + listOf("key", "value").getOrNull(declarationEntry.entryIndex()) + } + else -> null + } + } + + private fun KtDestructuringDeclarationEntry.entryIndex() = + parent.getChildrenOfType().indexOf(this) +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/.intention b/idea/testData/intentions/replaceUnderscoreWithParameterName/.intention new file mode 100644 index 00000000000..0ee411edc81 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ReplaceUnderscoreWithParameterNameIntention \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/anonymous.kt b/idea/testData/intentions/replaceUnderscoreWithParameterName/anonymous.kt new file mode 100644 index 00000000000..d9f3d2f89e3 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/anonymous.kt @@ -0,0 +1 @@ +val f = fun(i: Int, _: String) = i \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/anonymous.kt.after b/idea/testData/intentions/replaceUnderscoreWithParameterName/anonymous.kt.after new file mode 100644 index 00000000000..53666d124b6 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/anonymous.kt.after @@ -0,0 +1 @@ +val f = fun(i: Int, s: String) = i \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/conflict.kt b/idea/testData/intentions/replaceUnderscoreWithParameterName/conflict.kt new file mode 100644 index 00000000000..dabe9d9c5de --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/conflict.kt @@ -0,0 +1,6 @@ +// WITH_RUNTIME + +fun foo(pair: Pair) { + val (_, _) = pair + val first = 42 +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/conflict.kt.after b/idea/testData/intentions/replaceUnderscoreWithParameterName/conflict.kt.after new file mode 100644 index 00000000000..9c5a4d6dfa3 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/conflict.kt.after @@ -0,0 +1,6 @@ +// WITH_RUNTIME + +fun foo(pair: Pair) { + val (first1, _) = pair + val first = 42 +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/destructuringDeclaration.kt b/idea/testData/intentions/replaceUnderscoreWithParameterName/destructuringDeclaration.kt new file mode 100644 index 00000000000..6ea8369d23b --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/destructuringDeclaration.kt @@ -0,0 +1,5 @@ +data class Data(val first: Int, val second: Int) + +fun foo() { + val (first, _) = Data(1, 2) +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/destructuringDeclaration.kt.after b/idea/testData/intentions/replaceUnderscoreWithParameterName/destructuringDeclaration.kt.after new file mode 100644 index 00000000000..7f8bd53e92d --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/destructuringDeclaration.kt.after @@ -0,0 +1,5 @@ +data class Data(val first: Int, val second: Int) + +fun foo() { + val (first, second) = Data(1, 2) +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/for.kt b/idea/testData/intentions/replaceUnderscoreWithParameterName/for.kt new file mode 100644 index 00000000000..8b9b920eb6b --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/for.kt @@ -0,0 +1,7 @@ +data class Data(val first: Int, val second: Int) + +fun foo(list: List) { + for ((_, _) in list) { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/for.kt.after b/idea/testData/intentions/replaceUnderscoreWithParameterName/for.kt.after new file mode 100644 index 00000000000..01555447068 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/for.kt.after @@ -0,0 +1,7 @@ +data class Data(val first: Int, val second: Int) + +fun foo(list: List) { + for ((first, _) in list) { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/lambda.kt b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambda.kt new file mode 100644 index 00000000000..873d1a67fc0 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambda.kt @@ -0,0 +1,7 @@ +fun foo(f: (a: Int, b: Int, c: Int) -> Unit) { + f(1, 2, 3) +} + +fun bar() { + foo { _, _, _ -> } +} diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/lambda.kt.after b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambda.kt.after new file mode 100644 index 00000000000..cc001f96d37 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambda.kt.after @@ -0,0 +1,7 @@ +fun foo(f: (a: Int, b: Int, c: Int) -> Unit) { + f(1, 2, 3) +} + +fun bar() { + foo { _, b, _ -> } +} diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaConflict.kt b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaConflict.kt new file mode 100644 index 00000000000..2fca0249661 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaConflict.kt @@ -0,0 +1,7 @@ +fun foo(f: (a: Int, b: Int, c: Int) -> Int) { + f(1, 2, 3) +} + +fun bar(c: Int) { + foo { _, _, _ -> c } +} diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaConflict.kt.after b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaConflict.kt.after new file mode 100644 index 00000000000..e64b67a8f96 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaConflict.kt.after @@ -0,0 +1,7 @@ +fun foo(f: (a: Int, b: Int, c: Int) -> Int) { + f(1, 2, 3) +} + +fun bar(c: Int) { + foo { _, _, c1 -> c } +} diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaNoNames.kt b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaNoNames.kt new file mode 100644 index 00000000000..f1a05a33b67 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaNoNames.kt @@ -0,0 +1,7 @@ +fun foo(f: (Int, Int, Int) -> Unit) { + f(1, 2, 3) +} + +fun bar() { + foo { _, _, _ -> } +} diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaNoNames.kt.after b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaNoNames.kt.after new file mode 100644 index 00000000000..0b915ddf0e3 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaNoNames.kt.after @@ -0,0 +1,7 @@ +fun foo(f: (Int, Int, Int) -> Unit) { + f(1, 2, 3) +} + +fun bar() { + foo { _, i, _ -> } +} diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/map.kt b/idea/testData/intentions/replaceUnderscoreWithParameterName/map.kt new file mode 100644 index 00000000000..9ea6dc9a056 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/map.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +fun foo(map: Map) { + for ((_, _) in map) { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/map.kt.after b/idea/testData/intentions/replaceUnderscoreWithParameterName/map.kt.after new file mode 100644 index 00000000000..ca430947824 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/map.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +fun foo(map: Map) { + for ((key, _) in map) { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/triple.kt b/idea/testData/intentions/replaceUnderscoreWithParameterName/triple.kt new file mode 100644 index 00000000000..307c54feca7 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/triple.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun foo(t: Triple) { + val (_, _, _) = t +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceUnderscoreWithParameterName/triple.kt.after b/idea/testData/intentions/replaceUnderscoreWithParameterName/triple.kt.after new file mode 100644 index 00000000000..73e4220b787 --- /dev/null +++ b/idea/testData/intentions/replaceUnderscoreWithParameterName/triple.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun foo(t: Triple) { + val (_, _, third) = t +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index d0e53f6b17e..52ea8167278 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -13951,6 +13951,69 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/replaceUnderscoreWithParameterName") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ReplaceUnderscoreWithParameterName extends AbstractIntentionTest { + public void testAllFilesPresentInReplaceUnderscoreWithParameterName() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/replaceUnderscoreWithParameterName"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("anonymous.kt") + public void testAnonymous() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceUnderscoreWithParameterName/anonymous.kt"); + doTest(fileName); + } + + @TestMetadata("conflict.kt") + public void testConflict() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceUnderscoreWithParameterName/conflict.kt"); + doTest(fileName); + } + + @TestMetadata("destructuringDeclaration.kt") + public void testDestructuringDeclaration() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceUnderscoreWithParameterName/destructuringDeclaration.kt"); + doTest(fileName); + } + + @TestMetadata("for.kt") + public void testFor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceUnderscoreWithParameterName/for.kt"); + doTest(fileName); + } + + @TestMetadata("lambda.kt") + public void testLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceUnderscoreWithParameterName/lambda.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaConflict.kt") + public void testLambdaConflict() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaConflict.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaNoNames.kt") + public void testLambdaNoNames() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceUnderscoreWithParameterName/lambdaNoNames.kt"); + doTest(fileName); + } + + @TestMetadata("map.kt") + public void testMap() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceUnderscoreWithParameterName/map.kt"); + doTest(fileName); + } + + @TestMetadata("triple.kt") + public void testTriple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/replaceUnderscoreWithParameterName/triple.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/replaceUntilWithRangeTo") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)