Name Suggester: Suggest parameter name for the corresponding argument expression
#KT-8111 Fixed
This commit is contained in:
@@ -406,4 +406,16 @@ public fun KtDeclaration.visibilityModifier(): PsiElement? {
|
||||
public fun KtDeclaration.visibilityModifierType(): KtModifierKeywordToken?
|
||||
= visibilityModifier()?.node?.elementType as KtModifierKeywordToken?
|
||||
|
||||
public fun KtStringTemplateExpression.isPlain() = entries.all { it is KtLiteralStringTemplateEntry }
|
||||
public fun KtStringTemplateExpression.isPlain() = entries.all { it is KtLiteralStringTemplateEntry }
|
||||
|
||||
public fun KtExpression.getOutermostParenthesizerOrThis(): KtExpression {
|
||||
return (parentsWithSelf zip parents).firstOrNull {
|
||||
val (element, parent) = it
|
||||
when (parent) {
|
||||
is KtParenthesizedExpression -> false
|
||||
is KtAnnotatedExpression -> parent.baseExpression != element
|
||||
is KtLabeledExpression -> parent.baseExpression != element
|
||||
else -> true
|
||||
}
|
||||
}?.first as KtExpression? ?: this
|
||||
}
|
||||
@@ -18,10 +18,15 @@ package org.jetbrains.kotlin.idea.core
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.lexer.KotlinLexer
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getOutermostParenthesizerOrThis
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
@@ -36,7 +41,7 @@ public object KotlinNameSuggester {
|
||||
public fun suggestNamesByExpressionAndType(expression: KtExpression, bindingContext: BindingContext, validator: (String) -> Boolean, defaultName: String?): Collection<String> {
|
||||
val result = LinkedHashSet<String>()
|
||||
|
||||
result.addNamesByExpression(expression, validator)
|
||||
result.addNamesByExpression(expression, bindingContext, validator)
|
||||
|
||||
val type = bindingContext.getType(expression)
|
||||
if (type != null) {
|
||||
@@ -62,10 +67,13 @@ public object KotlinNameSuggester {
|
||||
return result
|
||||
}
|
||||
|
||||
public fun suggestNamesByExpressionOnly(expression: KtExpression, validator: (String) -> Boolean, defaultName: String? = null): List<String> {
|
||||
public fun suggestNamesByExpressionOnly(
|
||||
expression: KtExpression,
|
||||
bindingContext: BindingContext?,
|
||||
validator: (String) -> Boolean, defaultName: String? = null): List<String> {
|
||||
val result = ArrayList<String>()
|
||||
|
||||
result.addNamesByExpression(expression, validator)
|
||||
result.addNamesByExpression(expression, bindingContext, validator)
|
||||
|
||||
if (result.isEmpty()) {
|
||||
result.addName(defaultName, validator)
|
||||
@@ -74,10 +82,14 @@ public object KotlinNameSuggester {
|
||||
return result
|
||||
}
|
||||
|
||||
public fun suggestIterationVariableNames(collection: KtExpression, elementType: KotlinType, validator: (String) -> Boolean, defaultName: String?): Collection<String> {
|
||||
public fun suggestIterationVariableNames(
|
||||
collection: KtExpression,
|
||||
elementType: KotlinType,
|
||||
bindingContext: BindingContext?,
|
||||
validator: (String) -> Boolean, defaultName: String?): Collection<String> {
|
||||
val result = LinkedHashSet<String>()
|
||||
|
||||
suggestNamesByExpressionOnly(collection, { true })
|
||||
suggestNamesByExpressionOnly(collection, bindingContext, { true })
|
||||
.map { StringUtil.unpluralize(it) }
|
||||
.filterNotNull()
|
||||
.mapTo(result) { suggestNameByName(it, validator) }
|
||||
@@ -261,18 +273,41 @@ public object KotlinNameSuggester {
|
||||
return matcher.replaceAll("")
|
||||
}
|
||||
|
||||
private fun MutableCollection<String>.addNamesByExpression(expression: KtExpression?, validator: (String) -> Boolean) {
|
||||
private fun MutableCollection<String>.addNamesByExpressionPSI(expression: KtExpression?, validator: (String) -> Boolean) {
|
||||
if (expression == null) return
|
||||
val deparenthesized = KtPsiUtil.safeDeparenthesize(expression)
|
||||
when (deparenthesized) {
|
||||
is KtSimpleNameExpression -> addCamelNames(deparenthesized.getReferencedName(), validator)
|
||||
is KtQualifiedExpression -> addNamesByExpressionPSI(deparenthesized.selectorExpression, validator)
|
||||
is KtCallExpression -> addNamesByExpressionPSI(deparenthesized.calleeExpression, validator)
|
||||
is KtPostfixExpression -> addNamesByExpressionPSI(deparenthesized.baseExpression, validator)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableCollection<String>.addNamesByExpression(
|
||||
expression: KtExpression?,
|
||||
bindingContext: BindingContext?,
|
||||
validator: (String) -> Boolean
|
||||
) {
|
||||
if (expression == null) return
|
||||
|
||||
val expression = KtPsiUtil.safeDeparenthesize(expression)
|
||||
when (expression) {
|
||||
is KtSimpleNameExpression -> addCamelNames(expression.getReferencedName(), validator)
|
||||
addNamesByValueArgument(expression, bindingContext, validator)
|
||||
addNamesByExpressionPSI(expression, validator)
|
||||
}
|
||||
|
||||
is KtQualifiedExpression -> addNamesByExpression(expression.getSelectorExpression(), validator)
|
||||
|
||||
is KtCallExpression -> addNamesByExpression(expression.getCalleeExpression(), validator)
|
||||
|
||||
is KtPostfixExpression -> addNamesByExpression(expression.getBaseExpression(), validator)
|
||||
private fun MutableCollection<String>.addNamesByValueArgument(
|
||||
expression: KtExpression,
|
||||
bindingContext: BindingContext?,
|
||||
validator: (String) -> Boolean
|
||||
) {
|
||||
if (bindingContext == null) return
|
||||
val argumentExpression = expression.getOutermostParenthesizerOrThis()
|
||||
val valueArgument = argumentExpression.parent as? KtValueArgument ?: return
|
||||
val resolvedCall = argumentExpression.getParentResolvedCall(bindingContext) ?: return
|
||||
val argumentMatch = resolvedCall.getArgumentMapping(valueArgument) as? ArgumentMatch ?: return
|
||||
val parameter = argumentMatch.valueParameter
|
||||
if (parameter.containingDeclaration.hasStableParameterNames()) {
|
||||
addName(parameter.name.asString(), validator)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.intellij.codeInsight.template.TemplateBuilderImpl
|
||||
import com.intellij.codeInsight.template.impl.ConstantNode
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.core.*
|
||||
import org.jetbrains.kotlin.idea.resolve.ideService
|
||||
@@ -58,7 +59,8 @@ public class IterateExpressionIntention : SelfTargetingIntention<KtExpression>(j
|
||||
|
||||
val elementType = data(element)!!.elementType
|
||||
val nameValidator = NewDeclarationNameValidator(element, element.siblings(), NewDeclarationNameValidator.Target.VARIABLES)
|
||||
val names = KotlinNameSuggester.suggestIterationVariableNames(element, elementType, nameValidator, "e")
|
||||
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
|
||||
val names = KotlinNameSuggester.suggestIterationVariableNames(element, elementType, bindingContext, nameValidator, "e")
|
||||
|
||||
var forExpression = KtPsiFactory(element).createExpressionByPattern("for($0 in $1) {\nx\n}", names.first(), element) as KtForExpression
|
||||
forExpression = element.replaced(forExpression)
|
||||
|
||||
+1
-1
@@ -801,7 +801,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
builder.replaceElement(parameterTypeRef, parameterTypeExpression)
|
||||
|
||||
// add parameter name to the template
|
||||
val possibleNamesFromExpression = parameter.typeInfo.possibleNamesFromExpression
|
||||
val possibleNamesFromExpression = parameter.typeInfo.getPossibleNamesFromExpression(currentFileContext)
|
||||
val preferredName = parameter.preferredName
|
||||
val possibleNames = if (preferredName != null) {
|
||||
arrayOf(preferredName, *possibleNamesFromExpression)
|
||||
|
||||
+4
-4
@@ -41,8 +41,8 @@ abstract class TypeInfo(val variance: Variance) {
|
||||
}
|
||||
|
||||
class ByExpression(val expression: KtExpression, variance: Variance): TypeInfo(variance) {
|
||||
override val possibleNamesFromExpression: Array<String> by lazy {
|
||||
KotlinNameSuggester.suggestNamesByExpressionOnly(expression, { true }).toTypedArray()
|
||||
override fun getPossibleNamesFromExpression(bindingContext: BindingContext): Array<String> {
|
||||
return KotlinNameSuggester.suggestNamesByExpressionOnly(expression, bindingContext, { true }).toTypedArray()
|
||||
}
|
||||
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> =
|
||||
@@ -70,7 +70,7 @@ abstract class TypeInfo(val variance: Variance) {
|
||||
|
||||
abstract class DelegatingTypeInfo(val delegate: TypeInfo): TypeInfo(delegate.variance) {
|
||||
override val substitutionsAllowed: Boolean = delegate.substitutionsAllowed
|
||||
override val possibleNamesFromExpression: Array<String> get() = delegate.possibleNamesFromExpression
|
||||
override fun getPossibleNamesFromExpression(bindingContext: BindingContext) = delegate.getPossibleNamesFromExpression(bindingContext)
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> = delegate.getPossibleTypes(builder)
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ abstract class TypeInfo(val variance: Variance) {
|
||||
|
||||
open val substitutionsAllowed: Boolean = true
|
||||
open val staticContextRequired: Boolean = false
|
||||
open val possibleNamesFromExpression: Array<String> get() = ArrayUtil.EMPTY_STRING_ARRAY
|
||||
open fun getPossibleNamesFromExpression(bindingContext: BindingContext): Array<String> = ArrayUtil.EMPTY_STRING_ARRAY
|
||||
abstract fun getPossibleTypes(builder: CallableBuilder): List<KotlinType>
|
||||
|
||||
protected fun KotlinType?.getPossibleSupertypes(variance: Variance, callableBuilder: CallableBuilder): List<KotlinType> {
|
||||
|
||||
+1
-1
@@ -652,7 +652,7 @@ private class ConstructedExpressionWrapperWithIntroduceFeature(
|
||||
val name = if (nameSuggestion != null)
|
||||
KotlinNameSuggester.suggestNameByName(nameSuggestion, validator)
|
||||
else
|
||||
KotlinNameSuggester.suggestNamesByExpressionOnly(value, validator, "t").first()
|
||||
KotlinNameSuggester.suggestNamesByExpressionOnly(value, bindingContext, validator, "t").first()
|
||||
return Name.identifier(name)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
class A<T>(val n: T) {
|
||||
fun foo(a: Int): A<T> = throw Exception()
|
||||
|
||||
fun foo(t: T, s: String): A<T> {
|
||||
fun foo(a: T, s: String): A<T> {
|
||||
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
class A<T>(val n: T) {
|
||||
fun foo(i: Int, s: String): A<T> = throw Exception()
|
||||
|
||||
fun foo(t: T): A<T> {
|
||||
fun foo(i: T): A<T> {
|
||||
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -9,12 +9,12 @@ class A(val a: Int) {
|
||||
|
||||
fun test() {
|
||||
val a = A(1)
|
||||
val a3 = A(2)
|
||||
a.foo(a.a + a3.a)
|
||||
val i2 = A(2)
|
||||
a.foo(a.a + i2.a)
|
||||
with(A(1)) {
|
||||
val a2 = A(2)
|
||||
foo(this.a + a2.a)
|
||||
val a1 = A(2)
|
||||
this.foo(this.a + a1.a)
|
||||
val i1 = A(2)
|
||||
foo(this.a + i1.a)
|
||||
val i = A(2)
|
||||
this.foo(this.a + i.a)
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -9,12 +9,12 @@ class A(val a: Int) {
|
||||
|
||||
fun test() {
|
||||
val a = A(1)
|
||||
val a3 = A(2)
|
||||
a.foo(a.a + a3.a)
|
||||
val i2 = A(2)
|
||||
a.foo(a.a + i2.a)
|
||||
with(A(1)) {
|
||||
val a2 = A(2)
|
||||
foo(this.a + a2.a)
|
||||
val a1 = A(2)
|
||||
this.foo(this.a + a1.a)
|
||||
val i1 = A(2)
|
||||
foo(this.a + i1.a)
|
||||
val i = A(2)
|
||||
this.foo(this.a + i.a)
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
open class A(n: Int)
|
||||
|
||||
fun foo() {
|
||||
val i = 1
|
||||
val x = object : A(i) {}
|
||||
val n = 1
|
||||
val x = object : A(n) {}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
fun foo(c : Collection<String>){
|
||||
val function: (String) -> Boolean = { it; false }
|
||||
c.filter(function)
|
||||
val predicate: (String) -> Boolean = { it; false }
|
||||
c.filter(predicate)
|
||||
}
|
||||
+2
-2
@@ -3,6 +3,6 @@ class A {
|
||||
}
|
||||
fun apply(x: (A) -> Int) = 2
|
||||
fun test() {
|
||||
val function: (A) -> Int = { it.foo() }
|
||||
apply(function)
|
||||
val x: (A) -> Int = { it.foo() }
|
||||
apply(x)
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
fun foo(c : Collection<String>){
|
||||
val function: (String) -> Boolean = { it.length() > 1 }
|
||||
c.filterTo(ArrayList<String>(), function)
|
||||
val predicate: (String) -> Boolean = { it.length() > 1 }
|
||||
c.filterTo(ArrayList<String>(), predicate)
|
||||
}
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
fun foo(p: Int, list: List<Int>) {
|
||||
if (p > 0) {
|
||||
val function: (Int) -> Boolean = { it > 0 }
|
||||
list.filter(function)
|
||||
val predicate: (Int) -> Boolean = { it > 0 }
|
||||
list.filter(predicate)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
// WITH_RUNTIME
|
||||
fun main(args: Array<String>) {
|
||||
with(A()) {
|
||||
val prop1 = prop
|
||||
println(prop1)
|
||||
println(prop1)
|
||||
val message = prop
|
||||
println(message)
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fun a(x: Int) {}
|
||||
fun b() {
|
||||
val i = 1
|
||||
a(i)
|
||||
val x = 1
|
||||
a(x)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
fun x(items : Sequence<String>) {}
|
||||
fun y() {
|
||||
x(<selection>sequenceOf("")</selection>)
|
||||
}
|
||||
/*
|
||||
items
|
||||
of
|
||||
sequence
|
||||
sequenceOf
|
||||
*/
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
fun x(items : Sequence<String>) {}
|
||||
fun y() {
|
||||
x(<selection>foo@ (sequenceOf(""))</selection>)
|
||||
}
|
||||
/*
|
||||
items
|
||||
of
|
||||
sequence
|
||||
sequenceOf
|
||||
*/
|
||||
+15
@@ -21,10 +21,12 @@ import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
|
||||
public class KotlinNameSuggesterTest : LightCodeInsightFixtureTestCase() {
|
||||
@@ -56,6 +58,10 @@ public class KotlinNameSuggesterTest : LightCodeInsightFixtureTestCase() {
|
||||
|
||||
public fun testURL() { doTest() }
|
||||
|
||||
public fun testParameterNameByArgumentExpression() { doTest() }
|
||||
|
||||
public fun testParameterNameByParenthesizedArgumentExpression() { doTest() }
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase() + "/refactoring/nameSuggester")
|
||||
@@ -65,7 +71,11 @@ public class KotlinNameSuggesterTest : LightCodeInsightFixtureTestCase() {
|
||||
myFixture.configureByFile(getTestName(false) + ".kt")
|
||||
val file = myFixture.getFile() as KtFile
|
||||
val expectedResultText = KotlinTestUtils.getLastCommentInFile(file)
|
||||
val withRuntime = InTextDirectivesUtils.isDirectiveDefined(file.text, "//WITH_RUNTIME")
|
||||
try {
|
||||
if (withRuntime) {
|
||||
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
|
||||
}
|
||||
KotlinRefactoringUtil.selectExpression(myFixture.getEditor(), file, object : KotlinRefactoringUtil.SelectExpressionCallback {
|
||||
override fun run(expression: KtExpression?) {
|
||||
val names = KotlinNameSuggester.suggestNamesByExpressionAndType(expression!!, expression.analyze(BodyResolveMode.PARTIAL), { true }, "value").sorted()
|
||||
@@ -77,5 +87,10 @@ public class KotlinNameSuggesterTest : LightCodeInsightFixtureTestCase() {
|
||||
catch (e: KotlinRefactoringUtil.IntroduceRefactoringException) {
|
||||
throw AssertionError("Failed to find expression: " + e.getMessage())
|
||||
}
|
||||
finally {
|
||||
if (withRuntime) {
|
||||
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user