Make "Add name(s) to call arguments" available for generic arguments
#KT-15550 Fixed
This commit is contained in:
@@ -20,6 +20,8 @@ import com.intellij.codeInsight.intention.LowPriorityAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.conversion.copy.end
|
||||
import org.jetbrains.kotlin.idea.conversion.copy.start
|
||||
@@ -27,11 +29,14 @@ import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatchStatus
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatchStatus.ARGUMENT_HAS_NO_TYPE
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatchStatus.SUCCESS
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
||||
|
||||
class AddNameToArgumentIntention
|
||||
: SelfTargetingIntention<KtValueArgument>(KtValueArgument::class.java, "Add name to argument"), LowPriorityAction {
|
||||
class AddNameToArgumentIntention : SelfTargetingIntention<KtValueArgument>(
|
||||
KtValueArgument::class.java, "Add name to argument"
|
||||
), LowPriorityAction {
|
||||
|
||||
override fun isApplicableTo(element: KtValueArgument, caretOffset: Int): Boolean {
|
||||
val expression = element.getArgumentExpression() ?: return false
|
||||
@@ -47,8 +52,8 @@ class AddNameToArgumentIntention
|
||||
return true
|
||||
}
|
||||
|
||||
override fun allowCaretInsideElement(element: PsiElement)
|
||||
= element !is KtValueArgumentList && element !is KtContainerNode && super.allowCaretInsideElement(element)
|
||||
override fun allowCaretInsideElement(element: PsiElement) =
|
||||
element !is KtValueArgumentList && element !is KtContainerNode && super.allowCaretInsideElement(element)
|
||||
|
||||
override fun applyTo(element: KtValueArgument, editor: Editor?) {
|
||||
val name = detectNameToAdd(element)!!
|
||||
@@ -65,20 +70,30 @@ class AddNameToArgumentIntention
|
||||
|
||||
val callExpr = argumentList.parent as? KtCallElement ?: return null
|
||||
val resolvedCall = callExpr.resolveToCall() ?: return null
|
||||
val argumentMatch = resolvedCall.getArgumentMapping(argument) as? ArgumentMatch ?: return null
|
||||
if (argumentMatch.status != ArgumentMatchStatus.SUCCESS) return null
|
||||
|
||||
if (!resolvedCall.resultingDescriptor.hasStableParameterNames()) return null
|
||||
|
||||
if (argumentMatch.valueParameter.varargElementType != null) {
|
||||
val varargArgument = resolvedCall.valueArguments[argumentMatch.valueParameter] as? VarargValueArgument ?: return null
|
||||
if (varargArgument.arguments.size != 1) return null
|
||||
val versionSettings = callExpr.languageVersionSettings
|
||||
if (versionSettings.supportsFeature(LanguageFeature.ProhibitAssigningSingleElementsToVarargsInNamedForm)) {
|
||||
if (argument.getSpreadElement() == null) return null
|
||||
}
|
||||
}
|
||||
if (!argumentMatchedAndCouldBeNamedInCall(argument, resolvedCall, callExpr.languageVersionSettings)) return null
|
||||
|
||||
return argumentMatch.valueParameter.name
|
||||
return (resolvedCall.getArgumentMapping(argument) as? ArgumentMatch)?.valueParameter?.name
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun argumentMatchedAndCouldBeNamedInCall(
|
||||
argument: ValueArgument,
|
||||
resolvedCall: ResolvedCall<out CallableDescriptor>,
|
||||
versionSettings: LanguageVersionSettings
|
||||
): Boolean {
|
||||
val argumentMatch = resolvedCall.getArgumentMapping(argument) as? ArgumentMatch ?: return false
|
||||
if (argumentMatch.status != SUCCESS && argumentMatch.status != ARGUMENT_HAS_NO_TYPE) return false
|
||||
|
||||
if (argumentMatch.valueParameter.varargElementType != null) {
|
||||
val varargArgument = resolvedCall.valueArguments[argumentMatch.valueParameter] as? VarargValueArgument ?: return false
|
||||
if (varargArgument.arguments.size != 1) return false
|
||||
if (versionSettings.supportsFeature(LanguageFeature.ProhibitAssigningSingleElementsToVarargsInNamedForm)) {
|
||||
if (argument.getSpreadElement() == null) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,17 +18,14 @@ package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatchStatus
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
||||
|
||||
class AddNamesToCallArgumentsIntention : SelfTargetingRangeIntention<KtCallElement>(
|
||||
KtCallElement::class.java,
|
||||
"Add names to call arguments"
|
||||
KtCallElement::class.java,
|
||||
"Add names to call arguments"
|
||||
) {
|
||||
override fun applicabilityRange(element: KtCallElement): TextRange? {
|
||||
val arguments = element.valueArguments
|
||||
@@ -37,21 +34,14 @@ class AddNamesToCallArgumentsIntention : SelfTargetingRangeIntention<KtCallEleme
|
||||
val resolvedCall = element.resolveToCall() ?: 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
|
||||
val versionSettings = element.languageVersionSettings
|
||||
if (versionSettings.supportsFeature(LanguageFeature.ProhibitAssigningSingleElementsToVarargsInNamedForm)) {
|
||||
if (argument.getSpreadElement() == null) return null
|
||||
}
|
||||
if (arguments.all {
|
||||
AddNameToArgumentIntention.argumentMatchedAndCouldBeNamedInCall(it, resolvedCall, element.languageVersionSettings)
|
||||
}
|
||||
) {
|
||||
return element.calleeExpression?.textRange
|
||||
}
|
||||
|
||||
return element.calleeExpression?.textRange
|
||||
return null
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtCallElement, editor: Editor?) {
|
||||
@@ -61,9 +51,11 @@ class AddNamesToCallArgumentsIntention : SelfTargetingRangeIntention<KtCallEleme
|
||||
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)
|
||||
val newArgument = KtPsiFactory(element).createArgument(
|
||||
argument.getArgumentExpression()!!,
|
||||
name,
|
||||
argument.getSpreadElement() != null
|
||||
)
|
||||
argument.replace(newArgument)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(a: String) {}
|
||||
inline fun <reified T> generic() = null as T
|
||||
|
||||
fun main() {
|
||||
foo(<caret>generic())
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(a: String) {}
|
||||
inline fun <reified T> generic() = null as T
|
||||
|
||||
fun main() {
|
||||
foo(a = generic())
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(a: String) {}
|
||||
inline fun <reified T> generic() = null as T
|
||||
|
||||
fun main() {
|
||||
<caret>foo(generic())
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(a: String) {}
|
||||
inline fun <reified T> generic() = null as T
|
||||
|
||||
fun main() {
|
||||
foo(a = generic())
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// ERROR: Unresolved reference: Some
|
||||
// ACTION: Create function 'Some'
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Add 'ctor =' to argument
|
||||
|
||||
package p1
|
||||
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
// ERROR: Unresolved reference: Some
|
||||
// ACTION: Create function 'Some'
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Add 'ctor =' to argument
|
||||
|
||||
package p1
|
||||
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
// "Create abstract function 'foo'" "false"
|
||||
// ACTION: Create function 'foo'
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Add 'b =' to argument
|
||||
// ERROR: Unresolved reference: foo
|
||||
class A {
|
||||
fun bar(b: Boolean) {}
|
||||
|
||||
Vendored
+1
@@ -2,6 +2,7 @@
|
||||
// ACTION: Create extension function 'B.foo'
|
||||
// ACTION: Create member function 'B.foo'
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Add 'b =' to argument
|
||||
// ERROR: Unresolved reference: foo
|
||||
abstract class A {
|
||||
fun bar(b: Boolean) {}
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
// "Create extension function 'foo'" "false"
|
||||
// ACTION: Create function 'foo'
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Add 'b =' to argument
|
||||
// ERROR: Unresolved reference: foo
|
||||
fun bar(b: Boolean) {
|
||||
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// "Create member function 'foo'" "false"
|
||||
// ACTION: Add names to call arguments
|
||||
// ERROR: Unresolved reference: x
|
||||
|
||||
class A<T>(val n: T) {
|
||||
|
||||
Vendored
+1
@@ -1,5 +1,6 @@
|
||||
// "Create function 'foo'" "false"
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Add 'n =' to argument
|
||||
// ERROR: Unresolved reference: foo
|
||||
fun bar(n: Int) = "$n"
|
||||
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
// "Create local variable 'foo'" "false"
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Create function 'foo'
|
||||
// ACTION: Add 'f =' to argument
|
||||
// ERROR: Unresolved reference: foo
|
||||
fun test(f: (Int) -> Int) {}
|
||||
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
// "Create parameter 'foo'" "false"
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Create function 'foo'
|
||||
// ACTION: Add 'f =' to argument
|
||||
// ERROR: Unresolved reference: foo
|
||||
fun test(f: (Int) -> Int) {}
|
||||
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
// ERROR: Unresolved reference: x
|
||||
// ACTION: Create property 'x'
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Add 'n =' to argument
|
||||
|
||||
enum class E(n: Int) {
|
||||
X(<caret>x)
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
// ERROR: Unresolved reference: b
|
||||
// ACTION: Create property 'b'
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Add 'a =' to argument
|
||||
|
||||
open class A(val a: Int) {
|
||||
|
||||
|
||||
Vendored
+1
@@ -4,6 +4,7 @@
|
||||
// ACTION: Create property 'foo'
|
||||
// ACTION: Create property 'foo' as constructor parameter
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Add 'b =' to argument
|
||||
// ERROR: Unresolved reference: foo
|
||||
class A {
|
||||
fun bar(b: Boolean) {}
|
||||
|
||||
Vendored
+1
@@ -3,6 +3,7 @@
|
||||
// ACTION: Create member property 'B.foo'
|
||||
// ACTION: Create property 'foo' as constructor parameter
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Add 'b =' to argument
|
||||
// ERROR: Unresolved reference: foo
|
||||
abstract class A {
|
||||
fun bar(b: Boolean) {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// "Create property 'foo'" "false"
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Create function 'foo'
|
||||
// ACTION: Add 'f =' to argument
|
||||
// ERROR: Unresolved reference: foo
|
||||
fun test(f: (Int) -> Int) {}
|
||||
|
||||
|
||||
Vendored
+1
@@ -1,6 +1,7 @@
|
||||
// "Create member property 'bar'" "false"
|
||||
// ACTION: Create extension property 'T.bar'
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Add 'n =' to argument
|
||||
// ERROR: Unresolved reference: bar
|
||||
fun consume(n: Int) {}
|
||||
|
||||
|
||||
@@ -1151,6 +1151,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
runTest("idea/testData/intentions/addNameToArgument/functionLiteralArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("genericCall.kt")
|
||||
public void testGenericCall() throws Exception {
|
||||
runTest("idea/testData/intentions/addNameToArgument/genericCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("incompleteCall.kt")
|
||||
public void testIncompleteCall() throws Exception {
|
||||
runTest("idea/testData/intentions/addNameToArgument/incompleteCall.kt");
|
||||
@@ -1254,6 +1259,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
runTest("idea/testData/intentions/addNamesToCallArguments/ambiguousCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("genericCall.kt")
|
||||
public void testGenericCall() throws Exception {
|
||||
runTest("idea/testData/intentions/addNamesToCallArguments/genericCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("incompleteCall.kt")
|
||||
public void testIncompleteCall() throws Exception {
|
||||
runTest("idea/testData/intentions/addNamesToCallArguments/incompleteCall.kt");
|
||||
|
||||
Reference in New Issue
Block a user