diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt index f42539a01f9..fe30a0fe8fe 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt @@ -19,17 +19,20 @@ package org.jetbrains.kotlin.idea.quickfix.crossLanguage import com.intellij.codeInsight.daemon.QuickFixBundle import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.QuickFixFactory +import com.intellij.lang.java.beans.PropertyKind import com.intellij.lang.jvm.JvmClass import com.intellij.lang.jvm.JvmElement import com.intellij.lang.jvm.JvmModifier import com.intellij.lang.jvm.JvmModifiersOwner import com.intellij.lang.jvm.actions.* +import com.intellij.lang.jvm.types.JvmType import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.openapi.util.text.StringUtilRt import com.intellij.psi.* import com.intellij.psi.codeStyle.SuggestedNameInfo import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl +import com.intellij.psi.util.PropertyUtilBase import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration import org.jetbrains.kotlin.asJava.elements.KtLightElement @@ -144,9 +147,9 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { } class CreatePropertyFix( - private val targetClass: JvmClass, - contextElement: KtElement, - propertyInfo: PropertyInfo + contextElement: KtElement, + propertyInfo: PropertyInfo, + private val classOrFileName: String? ) : CreateCallableFromUsageFix(contextElement, listOf(propertyInfo)) { override fun getFamilyName() = "Add property" override fun getText(): String { @@ -157,7 +160,7 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { append("lateinit ") } append(if (info.writable) "var" else "val") - append("' property '${info.name}' to '${targetClass.name}'") + append("' property '${info.name}' to '$classOrFileName'") } } } @@ -324,29 +327,42 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { override fun createAddPropertyActions(targetClass: JvmClass, request: MemberRequest.Property): List { val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList() + return createAddPropertyActions( + targetContainer, listOf(request.visibilityModifier), + request.propertyType, request.propertyName, request.setterRequired, targetClass.name + ) + } - val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifier(request.visibilityModifier) } + private fun createAddPropertyActions( + targetContainer: KtElement, + modifiers: Iterable, + propertyType: JvmType, + propertyName: String, + setterRequired: Boolean, + classOrFileName: String? + ): List { + val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifiers(modifiers) } if (!modifierBuilder.isValid) return emptyList() val resolutionFacade = targetContainer.getResolutionFacade() val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType - val ktType = (request.propertyType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType + + val ktType = (propertyType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType val propertyInfo = PropertyInfo( - request.propertyName, - TypeInfo.Empty, - TypeInfo(ktType, Variance.INVARIANT), - request.setterRequired, - listOf(targetContainer), - modifierList = modifierBuilder.modifierList, - withInitializer = true + propertyName, + TypeInfo.Empty, + TypeInfo(ktType, Variance.INVARIANT), + setterRequired, + listOf(targetContainer), + modifierList = modifierBuilder.modifierList, + withInitializer = true ) - val propertyInfos = if (request.setterRequired) { + val propertyInfos = if (setterRequired) { listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true)) - } - else { + } else { listOf(propertyInfo) } - return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) } + return propertyInfos.map { CreatePropertyFix(targetContainer, it, classOrFileName) } } override fun createAddFieldActions(targetClass: JvmClass, request: CreateFieldRequest): List { @@ -382,7 +398,7 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { else { listOf(propertyInfo(false)) } - return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) } + return propertyInfos.map { CreatePropertyFix(targetContainer, it, targetClass.name) } } override fun createAddMethodActions(targetClass: JvmClass, request: CreateMethodRequest): List { @@ -410,11 +426,27 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { preferEmptyBody = true ) val targetClassName = targetClass.name + val action = object : CreateCallableFromUsageFix(targetContainer, listOf(functionInfo)) { override fun getFamilyName() = "Add method" override fun getText() = "Add method '$methodName' to '$targetClassName'" } - return listOf(action) + + val nameAndKind = PropertyUtilBase.getPropertyNameAndKind(methodName) ?: return listOf(action) + + val propertyType = (request.expectedParameters.singleOrNull()?.expectedTypes ?: request.returnType) + .firstOrNull { JvmPsiConversionHelper.getInstance(targetContainer.project).convertType(it.theType) != PsiType.VOID } + ?: return listOf(action) + + return createAddPropertyActions( + targetContainer, + request.modifiers, + propertyType.theType, + nameAndKind.first, + nameAndKind.second == PropertyKind.SETTER, + targetClass.name + ) + } override fun createAddAnnotationActions(target: JvmModifiersOwner, request: AnnotationRequest): List { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt.183 b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt.183 index 61ae834bc87..16fc92543df 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt.183 +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt.183 @@ -17,17 +17,19 @@ package org.jetbrains.kotlin.idea.quickfix.crossLanguage import com.intellij.codeInsight.daemon.QuickFixBundle -import com.intellij.codeInsight.daemon.impl.quickfix.CreateParameterFromUsageFix import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.QuickFixFactory +import com.intellij.lang.java.beans.PropertyKind import com.intellij.lang.jvm.* import com.intellij.lang.jvm.actions.* +import com.intellij.lang.jvm.types.JvmType import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.openapi.util.text.StringUtilRt import com.intellij.psi.* import com.intellij.psi.codeStyle.SuggestedNameInfo import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl +import com.intellij.psi.util.PropertyUtilBase import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration import org.jetbrains.kotlin.asJava.elements.KtLightElement @@ -142,9 +144,9 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { } class CreatePropertyFix( - private val targetClass: JvmClass, contextElement: KtElement, - propertyInfo: PropertyInfo + propertyInfo: PropertyInfo, + private val classOrFileName: String? ) : CreateCallableFromUsageFix(contextElement, listOf(propertyInfo)) { override fun getFamilyName() = "Add property" override fun getText(): String { @@ -155,7 +157,7 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { append("lateinit ") } append(if (info.writable) "var" else "val") - append("' property '${info.name}' to '${targetClass.name}'") + append("' property '${info.name}' to '$classOrFileName'") } } } @@ -322,29 +324,43 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { override fun createAddPropertyActions(targetClass: JvmClass, request: MemberRequest.Property): List { val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList() + return createAddPropertyActions( + targetContainer, listOf(request.visibilityModifier), + request.propertyType, request.propertyName, request.setterRequired, targetClass.name + ) + } - val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifier(request.visibilityModifier) } + private fun createAddPropertyActions( + targetContainer: KtElement, + modifiers: Iterable, + propertyType: JvmType, + propertyName: String, + setterRequired: Boolean, + classOrFileName: String? + ): List { + val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifiers(modifiers) } if (!modifierBuilder.isValid) return emptyList() val resolutionFacade = targetContainer.getResolutionFacade() val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType - val ktType = (request.propertyType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType + + val ktType = (propertyType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType val propertyInfo = PropertyInfo( - request.propertyName, + propertyName, TypeInfo.Empty, TypeInfo(ktType, Variance.INVARIANT), - request.setterRequired, + setterRequired, listOf(targetContainer), modifierList = modifierBuilder.modifierList, withInitializer = true ) - val propertyInfos = if (request.setterRequired) { + val propertyInfos = if (setterRequired) { listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true)) } else { listOf(propertyInfo) } - return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) } + return propertyInfos.map { CreatePropertyFix(targetContainer, it, classOrFileName) } } override fun createAddFieldActions(targetClass: JvmClass, request: CreateFieldRequest): List { @@ -380,7 +396,7 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { else { listOf(propertyInfo(false)) } - return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) } + return propertyInfos.map { CreatePropertyFix(targetContainer, it, targetClass.name) } } override fun createAddMethodActions(targetClass: JvmClass, request: CreateMethodRequest): List { @@ -412,7 +428,22 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { override fun getFamilyName() = "Add method" override fun getText() = "Add method '$methodName' to '$targetClassName'" } - return listOf(action) + + val nameAndKind = PropertyUtilBase.getPropertyNameAndKind(methodName) ?: return listOf(action) + + val propertyType = (request.expectedParameters.singleOrNull()?.expectedTypes ?: request.returnType) + .firstOrNull { JvmPsiConversionHelper.getInstance(targetContainer.project).convertType(it.theType) != PsiType.VOID } + ?: return listOf(action) + + return createAddPropertyActions( + targetContainer, + request.modifiers, + propertyType.theType, + nameAndKind.first, + nameAndKind.second == PropertyKind.SETTER, + targetClass.name + ) + } override fun createAddAnnotationActions(target: JvmModifiersOwner, request: AnnotationRequest): List { diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt b/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt index 42988bce5cb..258ea2e38d8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt @@ -24,7 +24,10 @@ import com.intellij.lang.jvm.actions.* import com.intellij.lang.jvm.types.JvmSubstitutor import com.intellij.openapi.project.Project import com.intellij.openapi.util.Pair.pair -import com.intellij.psi.* +import com.intellij.psi.PsiJvmSubstitutor +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiSubstitutor +import com.intellij.psi.PsiType import com.intellij.psi.codeStyle.SuggestedNameInfo import com.intellij.testFramework.fixtures.CodeInsightTestFixture import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase @@ -32,8 +35,6 @@ import org.jetbrains.kotlin.asJava.toLightElements import org.jetbrains.kotlin.idea.search.allScope import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor import org.jetbrains.kotlin.psi.KtModifierListOwner -import org.jetbrains.uast.UParameter -import org.jetbrains.uast.UastContext import org.jetbrains.uast.toUElement import org.junit.Assert @@ -399,16 +400,16 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { """.trim().trimMargin()) myFixture.launchAction( - createPropertyActions( - myFixture.atCaret(), - MemberRequest.Property( - propertyName = "baz", - visibilityModifier = JvmModifier.PUBLIC, - propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()), - getterRequired = true, - setterRequired = true - ) - ).findWithText("Add 'var' property 'baz' to 'Foo'") + createMethodActions( + myFixture.atCaret(), + SimpleMethodRequest( + project, + methodName = "setBaz", + modifiers = listOf(JvmModifier.PUBLIC), + returnType = expectedTypes(), + parameters = expectedParams(PsiType.getTypeByName("java.lang.String", project, project.allScope())) + ) + ).findWithText("Add 'var' property 'baz' to 'Foo'") ) myFixture.checkResult(""" |class Foo { @@ -427,16 +428,16 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { """.trim().trimMargin()) myFixture.launchAction( - createPropertyActions( - myFixture.atCaret(), - MemberRequest.Property( - propertyName = "baz", - visibilityModifier = JvmModifier.PUBLIC, - propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()), - getterRequired = true, - setterRequired = true - ) - ).findWithText("Add 'lateinit var' property 'baz' to 'Foo'") + createMethodActions( + myFixture.atCaret(), + SimpleMethodRequest( + project, + methodName = "setBaz", + modifiers = listOf(JvmModifier.PUBLIC), + returnType = expectedTypes(), + parameters = expectedParams(PsiType.getTypeByName("java.lang.String", project, project.allScope())) + ) + ).findWithText("Add 'lateinit var' property 'baz' to 'Foo'") ) myFixture.checkResult(""" |class Foo { @@ -502,10 +503,6 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { } - - private fun createPropertyActions(atCaret: JvmClass, property: MemberRequest.Property): List = - com.intellij.lang.jvm.actions.EP_NAME.extensions.flatMap { it.createAddPropertyActions(atCaret, property) } - private fun createFieldActions(atCaret: JvmClass, fieldRequest: CreateFieldRequest): List = com.intellij.lang.jvm.actions.EP_NAME.extensions.flatMap { it.createAddFieldActions(atCaret, fieldRequest) } @@ -517,16 +514,16 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { """.trim().trimMargin()) myFixture.launchAction( - createPropertyActions( - myFixture.atCaret(), - MemberRequest.Property( - propertyName = "baz", - visibilityModifier = JvmModifier.PUBLIC, - propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()), - getterRequired = true, - setterRequired = false - ) - ).findWithText("Add 'val' property 'baz' to 'Foo'") + createMethodActions( + myFixture.atCaret(), + SimpleMethodRequest( + project, + methodName = "getBaz", + modifiers = listOf(JvmModifier.PUBLIC), + returnType = expectedTypes(PsiType.getTypeByName("java.lang.String", project, project.allScope())), + parameters = expectedParams() + ) + ).findWithText("Add 'val' property 'baz' to 'Foo'") ) myFixture.checkResult(""" |class Foo { @@ -537,13 +534,6 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { """.trim().trimMargin(), true) } - private fun makeParams(vararg psyTypes: PsiType): List { - val uastContext = UastContext(myFixture.project) - val factory = JavaPsiFacade.getElementFactory(myFixture.project) - val parameters = psyTypes.mapIndexed { index, psiType -> factory.createParameter("param$index", psiType) } - return parameters.map { uastContext.convertElement(it, null, UParameter::class.java) as UParameter } - } - private fun expectedTypes(vararg psiTypes: PsiType) = psiTypes.map { expectedType(it) } private fun expectedParams(vararg psyTypes: PsiType) = diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt.183 b/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt.183 index 29156c97398..353a6082149 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt.183 +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt.183 @@ -402,14 +402,14 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { """.trim().trimMargin()) myFixture.launchAction( - createPropertyActions( + createMethodActions( myFixture.atCaret(), - MemberRequest.Property( - propertyName = "baz", - visibilityModifier = JvmModifier.PUBLIC, - propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()), - getterRequired = true, - setterRequired = true + SimpleMethodRequest( + project, + methodName = "setBaz", + modifiers = listOf(JvmModifier.PUBLIC), + returnType = expectedTypes(), + parameters = expectedParams(PsiType.getTypeByName("java.lang.String", project, project.allScope())) ) ).findWithText("Add 'var' property 'baz' to 'Foo'") ) @@ -430,14 +430,14 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { """.trim().trimMargin()) myFixture.launchAction( - createPropertyActions( + createMethodActions( myFixture.atCaret(), - MemberRequest.Property( - propertyName = "baz", - visibilityModifier = JvmModifier.PUBLIC, - propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()), - getterRequired = true, - setterRequired = true + SimpleMethodRequest( + project, + methodName = "setBaz", + modifiers = listOf(JvmModifier.PUBLIC), + returnType = expectedTypes(), + parameters = expectedParams(PsiType.getTypeByName("java.lang.String", project, project.allScope())) ) ).findWithText("Add 'lateinit var' property 'baz' to 'Foo'") ) @@ -505,10 +505,6 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { } - - private fun createPropertyActions(atCaret: JvmClass, property: MemberRequest.Property): List = - com.intellij.lang.jvm.actions.EP_NAME.extensions.flatMap { it.createAddPropertyActions(atCaret, property) } - private fun createFieldActions(atCaret: JvmClass, fieldRequest: CreateFieldRequest): List = com.intellij.lang.jvm.actions.EP_NAME.extensions.flatMap { it.createAddFieldActions(atCaret, fieldRequest) } @@ -520,14 +516,14 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { """.trim().trimMargin()) myFixture.launchAction( - createPropertyActions( + createMethodActions( myFixture.atCaret(), - MemberRequest.Property( - propertyName = "baz", - visibilityModifier = JvmModifier.PUBLIC, - propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()), - getterRequired = true, - setterRequired = false + SimpleMethodRequest( + project, + methodName = "getBaz", + modifiers = listOf(JvmModifier.PUBLIC), + returnType = expectedTypes(PsiType.getTypeByName("java.lang.String", project, project.allScope())), + parameters = expectedParams() ) ).findWithText("Add 'val' property 'baz' to 'Foo'") )