diff --git a/plugins/kotlin/code-insight/intentions-k2/kotlin.code-insight.intentions.k2.iml b/plugins/kotlin/code-insight/intentions-k2/kotlin.code-insight.intentions.k2.iml
index 11d754f06297..834db932735a 100644
--- a/plugins/kotlin/code-insight/intentions-k2/kotlin.code-insight.intentions.k2.iml
+++ b/plugins/kotlin/code-insight/intentions-k2/kotlin.code-insight.intentions.k2.iml
@@ -57,5 +57,6 @@
+
\ No newline at end of file
diff --git a/plugins/kotlin/code-insight/intentions-k2/resources/kotlin.code-insight.intentions.k2.xml b/plugins/kotlin/code-insight/intentions-k2/resources/kotlin.code-insight.intentions.k2.xml
index 8206a2b65d86..5bd303724d77 100644
--- a/plugins/kotlin/code-insight/intentions-k2/resources/kotlin.code-insight.intentions.k2.xml
+++ b/plugins/kotlin/code-insight/intentions-k2/resources/kotlin.code-insight.intentions.k2.xml
@@ -86,6 +86,13 @@
group.names.kotlin
+
+ kotlin
+ org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ConvertMemberToExtensionIntention
+ messages.KotlinBundle
+ group.names.kotlin
+
+
kotlin
org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ConvertStringTemplateToBuildStringIntention
diff --git a/plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertMemberToExtensionIntention.kt b/plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertMemberToExtensionIntention.kt
new file mode 100644
index 000000000000..ca4929283c85
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertMemberToExtensionIntention.kt
@@ -0,0 +1,350 @@
+// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package org.jetbrains.kotlin.idea.k2.codeinsight.intentions
+
+import com.intellij.codeInsight.FileModificationService
+import com.intellij.codeInsight.intention.LowPriorityAction
+import com.intellij.openapi.application.readAction
+import com.intellij.openapi.application.runWriteAction
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.ScrollType
+import com.intellij.openapi.ui.Messages
+import com.intellij.openapi.util.TextRange
+import com.intellij.platform.ide.progress.runWithModalProgressBlocking
+import com.intellij.psi.PsiMethodCallExpression
+import com.intellij.psi.PsiReferenceExpression
+import com.intellij.psi.PsiWhiteSpace
+import com.intellij.psi.codeStyle.JavaCodeStyleManager
+import com.intellij.psi.search.searches.ReferencesSearch
+import com.intellij.util.SmartList
+import org.jetbrains.kotlin.asJava.toLightMethods
+import org.jetbrains.kotlin.idea.base.analysis.api.utils.shortenReferences
+import org.jetbrains.kotlin.idea.base.psi.getReturnTypeReference
+import org.jetbrains.kotlin.idea.base.psi.imports.addImport
+import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
+import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingRangeIntention
+import org.jetbrains.kotlin.idea.codeinsight.utils.isFunInterface
+import org.jetbrains.kotlin.idea.core.TemplateKind
+import org.jetbrains.kotlin.idea.core.getFunctionBodyTextFromTemplate
+import org.jetbrains.kotlin.idea.core.moveCaret
+import org.jetbrains.kotlin.idea.core.unblockDocument
+import org.jetbrains.kotlin.idea.references.KtReference
+import org.jetbrains.kotlin.idea.search.ExpectActualUtils.actualsForExpected
+import org.jetbrains.kotlin.idea.search.ExpectActualUtils.liftToExpected
+import org.jetbrains.kotlin.idea.search.ExpectActualUtils.withExpectedActuals
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.*
+import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
+import org.jetbrains.kotlin.types.Variance
+import org.jetbrains.kotlin.util.match
+import org.jetbrains.kotlin.utils.addIfNotNull
+
+private val LOG = Logger.getInstance(ConvertMemberToExtensionIntention::class.java)
+
+class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention(
+ KtCallableDeclaration::class.java, KotlinBundle.lazyMessage("convert.member.to.extension")
+), LowPriorityAction {
+ private fun isApplicable(element: KtCallableDeclaration): Boolean {
+ val classBody = element.parent as? KtClassBody ?: return false
+ val parentClass = classBody.parent as? KtClassOrObject ?: return false
+ if (parentClass.isLocal) return false
+ if (parentClass is KtObjectDeclaration && parentClass.isCompanion()) return false
+ if ((parentClass as? KtClass)?.isFunInterface() == true && !element.hasBody()) return false
+ if (element.receiverTypeReference != null) return false
+ if (element.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return false
+ when (element) {
+ is KtProperty -> if (element.hasInitializer() || element.hasDelegate()) return false
+ is KtSecondaryConstructor -> return false
+ }
+
+ return true
+ }
+
+ override fun applicabilityRange(element: KtCallableDeclaration): TextRange? {
+ val nameIdentifier = element.nameIdentifier ?: return null
+ if (!isApplicable(element)) return null
+ return nameIdentifier.textRange
+ }
+
+ override fun startInWriteAction() = false
+
+ override fun applyTo(element: KtCallableDeclaration, editor: Editor?) {
+ if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return
+
+ var allowExpected = true
+
+ liftToExpected(element)?.actualsForExpected()?.let {
+ if (it.isEmpty()) {
+ allowExpected = askIfExpectedIsAllowed()
+ }
+ }
+
+ val (extension, bodyTypeToSelect) = createExtensionCallableAndPrepareBodyToSelect(element, allowExpected)
+
+ runWriteAction {
+ editor?.apply {
+ unblockDocument()
+
+ if (extension.isValid) {
+
+ if (bodyTypeToSelect != GeneratedBodyType.NOTHING) {
+ val bodyToSelect = getBodyForSelection(extension, bodyTypeToSelect)
+
+ if (bodyToSelect != null) {
+ val range = bodyToSelect.textRange
+ moveCaret(range.startOffset, ScrollType.CENTER)
+
+ val parent = bodyToSelect.parent
+ val lastSibling = if (parent is KtBlockExpression) parent.rBrace?.siblings(forward = false, withItself = false)
+ ?.first { it !is PsiWhiteSpace }
+ else bodyToSelect.siblings(forward = true, withItself = false).lastOrNull()
+ val endOffset = lastSibling?.endOffset ?: range.endOffset
+ selectionModel.setSelection(range.startOffset, endOffset)
+ } else {
+ LOG.error(
+ "Extension created with new method body but this body was not found after document commit. Extension text: \"${extension.text}\""
+ )
+ moveCaret(extension.textOffset, ScrollType.CENTER)
+ }
+ } else {
+ moveCaret(extension.textOffset, ScrollType.CENTER)
+ }
+ } else {
+ LOG.error("Extension invalidated during document commit. Extension text \"${extension.text}\"")
+ }
+ }
+ }
+ }
+
+ private fun getBodyForSelection(extension: KtCallableDeclaration, bodyTypeToSelect: GeneratedBodyType): KtExpression? {
+ fun selectBody(declaration: KtDeclarationWithBody): KtExpression? {
+
+ if (!declaration.hasBody()) return extension
+
+ return declaration.bodyExpression?.let {
+ (it as? KtBlockExpression)?.statements?.singleOrNull() ?: it
+ }
+ }
+
+ return when (bodyTypeToSelect) {
+ GeneratedBodyType.FUNCTION -> (extension as? KtFunction)?.let { selectBody(it) }
+ GeneratedBodyType.GETTER -> (extension as? KtProperty)?.getter?.let { selectBody(it) }
+ GeneratedBodyType.SETTER -> (extension as? KtProperty)?.setter?.let { selectBody(it) }
+ else -> null
+ }
+ }
+
+ private enum class GeneratedBodyType {
+ NOTHING, FUNCTION, SETTER, GETTER
+ }
+
+ private fun processSingleDeclaration(
+ element: KtCallableDeclaration, allowExpected: Boolean
+ ): Pair {
+ val containingClass = element.containingClassOrObject
+
+ val isEffectivelyExpected = allowExpected && element.isExpectDeclaration()
+
+ val file = element.containingKtFile
+ val project = file.project
+ val outermostParent = KtPsiUtil.getOutermostParent(element, file, false)
+
+ val ktFilesToAddImports = LinkedHashSet()
+ val javaCallsToFix = SmartList()
+ runWithModalProgressBlocking(project, KotlinBundle.message("searching.for.0", element.name!!)) {
+ readAction {
+ for (ref in ReferencesSearch.search(element)) {
+ when (ref) {
+ is KtReference -> {
+ val refFile = ref.element.containingKtFile
+ if (refFile != file) {
+ ktFilesToAddImports.add(refFile)
+ }
+ }
+
+ is PsiReferenceExpression -> javaCallsToFix.addIfNotNull(ref.parent as? PsiMethodCallExpression)
+ }
+ }
+ }
+ }
+
+ val typeParameterList = newTypeParameterList(element)
+
+ val psiFactory = KtPsiFactory(project)
+
+ val (extension, bodyTypeToSelect) = runWriteAction {
+ val extension = file.addAfter(element, outermostParent) as KtCallableDeclaration
+ file.addAfter(psiFactory.createNewLine(), outermostParent)
+ file.addAfter(psiFactory.createNewLine(), outermostParent)
+ element.delete()
+
+ extension.setReceiverType(containingClass!!)
+
+ if (typeParameterList != null) {
+ if (extension.typeParameterList != null) {
+ extension.typeParameterList!!.replace(typeParameterList)
+ }
+ else {
+ extension.addBefore(typeParameterList, extension.receiverTypeReference)
+ extension.addBefore(psiFactory.createWhiteSpace(), extension.receiverTypeReference)
+ }
+ }
+
+ extension.modifierList?.getModifier(KtTokens.PROTECTED_KEYWORD)?.delete()
+ extension.modifierList?.getModifier(KtTokens.ABSTRACT_KEYWORD)?.delete()
+ extension.modifierList?.getModifier(KtTokens.OPEN_KEYWORD)?.delete()
+ extension.modifierList?.getModifier(KtTokens.FINAL_KEYWORD)?.delete()
+
+ if (isEffectivelyExpected && !extension.hasExpectModifier()) {
+ extension.addModifier(KtTokens.EXPECT_KEYWORD)
+ }
+
+ var bodyTypeToSelect = GeneratedBodyType.NOTHING
+
+ val bodyText = getFunctionBodyTextFromTemplate(
+ project,
+ if (extension is KtFunction) TemplateKind.FUNCTION else TemplateKind.PROPERTY_INITIALIZER,
+ extension.name,
+ extension.getReturnTypeReference()?.text ?: "Unit",
+ extension.containingClassOrObject?.fqName
+ )
+
+ when (extension) {
+ is KtFunction -> {
+ if (!extension.hasBody() && !isEffectivelyExpected) { //TODO: methods in PSI for setBody
+ extension.add(psiFactory.createBlock(bodyText))
+ bodyTypeToSelect = GeneratedBodyType.FUNCTION
+ }
+ }
+
+ is KtProperty -> {
+ val templateProperty =
+ psiFactory.createDeclaration("var v: Any\nget()=$bodyText\nset(value){\n$bodyText\n}")
+
+ if (!isEffectivelyExpected) {
+ val templateGetter = templateProperty.getter!!
+ val templateSetter = templateProperty.setter!!
+
+ var getter = extension.getter
+ if (getter == null) {
+ getter = extension.addAfter(templateGetter, extension.typeReference) as KtPropertyAccessor
+ extension.addBefore(psiFactory.createNewLine(), getter)
+ bodyTypeToSelect = GeneratedBodyType.GETTER
+ }
+ else if (!getter.hasBody()) {
+ getter = getter.replace(templateGetter) as KtPropertyAccessor
+ bodyTypeToSelect = GeneratedBodyType.GETTER
+ }
+
+ if (extension.isVar) {
+ var setter = extension.setter
+ if (setter == null) {
+ setter = extension.addAfter(templateSetter, getter) as KtPropertyAccessor
+ extension.addBefore(psiFactory.createNewLine(), setter)
+ if (bodyTypeToSelect == GeneratedBodyType.NOTHING) {
+ bodyTypeToSelect = GeneratedBodyType.SETTER
+ }
+ }
+ else if (!setter.hasBody()) {
+ setter.replace(templateSetter) as KtPropertyAccessor
+ if (bodyTypeToSelect == GeneratedBodyType.NOTHING) {
+ bodyTypeToSelect = GeneratedBodyType.SETTER
+ }
+ }
+ }
+ }
+ }
+ }
+ extension to bodyTypeToSelect
+ }
+
+ if (ktFilesToAddImports.isNotEmpty()) {
+ extension.fqName?.let { fqName ->
+ runWriteAction {
+ for (ktFile in ktFilesToAddImports) {
+ ktFile.addImport(fqName)
+ }
+ }
+ }
+ }
+
+ if (javaCallsToFix.isNotEmpty()) {
+ val lightMethod = extension.toLightMethods().first()
+ for (javaCallToFix in javaCallsToFix) {
+ runWriteAction {
+ javaCallToFix.methodExpression.qualifierExpression?.let {
+ val argumentList = javaCallToFix.argumentList
+ argumentList.addBefore(it, argumentList.expressions.firstOrNull())
+ }
+
+ val newRef = javaCallToFix.methodExpression.bindToElement(lightMethod)
+ JavaCodeStyleManager.getInstance(project).shortenClassReferences(newRef)
+ }
+ }
+ }
+
+ return extension to bodyTypeToSelect
+ }
+
+ private fun askIfExpectedIsAllowed(): Boolean {
+ return Messages.showYesNoDialog(
+ KotlinBundle.message("do.you.want.to.make.new.extension.an.expected.declaration"), text, Messages.getQuestionIcon()
+ ) == Messages.YES
+ }
+
+ private fun createExtensionCallableAndPrepareBodyToSelect(
+ element: KtCallableDeclaration, allowExpected: Boolean = true
+ ): Pair {
+ val expectedDeclaration = liftToExpected(element) as? KtCallableDeclaration
+ if (expectedDeclaration != null) {
+ withExpectedActuals(element).filterIsInstance().forEach {
+ if (it != element) {
+ processSingleDeclaration(it, allowExpected)
+ }
+ }
+ }
+
+ val classVisibility = element.containingClass()?.visibilityModifierType()
+ val (extension, bodyTypeToSelect) = processSingleDeclaration(element, allowExpected)
+ if (classVisibility != null && extension.visibilityModifier() == null) {
+ runWriteAction {
+ extension.addModifier(classVisibility)
+ }
+ }
+
+ return extension to bodyTypeToSelect
+ }
+
+ private fun newTypeParameterList(member: KtCallableDeclaration): KtTypeParameterList? {
+ val classElement =
+ member.parents.match(KtClassBody::class, last = KtClassOrObject::class) ?: error("Can't typeMatch ${member.parent.parent}")
+ val classParams = classElement.typeParameters
+ if (classParams.isEmpty()) return null
+ val allTypeParameters = classParams + member.typeParameters
+ val text = allTypeParameters.joinToString(",", "<", ">") { it.textWithoutVariance() }
+ return org.jetbrains.kotlin.psi.KtPsiFactory(member.project).createDeclaration("fun $text foo()").typeParameterList
+ }
+
+ private fun KtTypeParameter.textWithoutVariance(): String {
+ if (variance == Variance.INVARIANT) return text
+ val copied = this.copy() as KtTypeParameter
+ copied.modifierList?.getModifier(KtTokens.OUT_KEYWORD)?.delete()
+ copied.modifierList?.getModifier(KtTokens.IN_KEYWORD)?.delete()
+ return copied.text
+ }
+
+ private fun KtCallableDeclaration.setReceiverType(klass: KtClassOrObject) {
+ val className = buildString {
+ append(klass.name)
+ if (klass.typeParameters.isNotEmpty()) {
+ append(klass.typeParameters.joinToString(", ", "<", ">") {
+ it.name ?: ""
+ })
+ }
+ }
+ val typeReference = KtPsiFactory.contextual(this@setReceiverType).createType(className)
+ val receiverReference = setReceiverTypeReference(typeReference) ?: return
+ shortenReferences(receiverReference)
+ }
+}
\ No newline at end of file
diff --git a/plugins/kotlin/code-insight/intentions-k2/tests/test/org/jetbrains/kotlin/idea/k2/intentions/tests/K2IntentionTestGenerated.java b/plugins/kotlin/code-insight/intentions-k2/tests/test/org/jetbrains/kotlin/idea/k2/intentions/tests/K2IntentionTestGenerated.java
index b18a6da24144..ea8e7b2b11e6 100644
--- a/plugins/kotlin/code-insight/intentions-k2/tests/test/org/jetbrains/kotlin/idea/k2/intentions/tests/K2IntentionTestGenerated.java
+++ b/plugins/kotlin/code-insight/intentions-k2/tests/test/org/jetbrains/kotlin/idea/k2/intentions/tests/K2IntentionTestGenerated.java
@@ -7469,7 +7469,284 @@ public abstract class K2IntentionTestGenerated extends AbstractK2IntentionTest {
+ @RunWith(JUnit3RunnerWithInners.class)
+ @TestMetadata("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension")
+ public static class ConvertMemberToExtension extends AbstractK2IntentionTest {
+ @java.lang.Override
+ @org.jetbrains.annotations.NotNull
+ public final KotlinPluginMode getPluginMode() {
+ return KotlinPluginMode.K2;
+ }
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
+ }
+
+ @TestMetadata("abstract.kt")
+ public void testAbstract() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/abstract.kt");
+ }
+
+ @TestMetadata("abstractProperty.kt")
+ public void testAbstractProperty() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/abstractProperty.kt");
+ }
+
+ @TestMetadata("delegatedProperty.kt")
+ public void testDelegatedProperty() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/delegatedProperty.kt");
+ }
+
+ @TestMetadata("explicitUnit.kt")
+ public void testExplicitUnit() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/explicitUnit.kt");
+ }
+
+ @TestMetadata("extension.kt")
+ public void testExtension() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/extension.kt");
+ }
+
+ @TestMetadata("final.kt")
+ public void testFinal() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/final.kt");
+ }
+
+ @TestMetadata("funInterface.kt")
+ public void testFunInterface() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/funInterface.kt");
+ }
+
+ @TestMetadata("funInterface2.kt")
+ public void testFunInterface2() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/funInterface2.kt");
+ }
+
+ @TestMetadata("funcitonNoName.kt")
+ public void testFuncitonNoName() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/funcitonNoName.kt");
+ }
+
+ @TestMetadata("functionType.kt")
+ public void testFunctionType() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/functionType.kt");
+ }
+
+ @TestMetadata("genericClass.kt")
+ public void testGenericClass() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/genericClass.kt");
+ }
+
+ @TestMetadata("genericClassWithInVariance.kt")
+ public void testGenericClassWithInVariance() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/genericClassWithInVariance.kt");
+ }
+
+ @TestMetadata("genericClassWithOutVariance.kt")
+ public void testGenericClassWithOutVariance() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/genericClassWithOutVariance.kt");
+ }
+
+ @TestMetadata("genericClassWithVarianceAndReified.kt")
+ public void testGenericClassWithVarianceAndReified() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/genericClassWithVarianceAndReified.kt");
+ }
+
+ @TestMetadata("genericFun.kt")
+ public void testGenericFun() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/genericFun.kt");
+ }
+
+ @TestMetadata("genericFunInGenericClass.kt")
+ public void testGenericFunInGenericClass() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/genericFunInGenericClass.kt");
+ }
+
+ @TestMetadata("genericFunInGenericClassWithUpperBounds.kt")
+ public void testGenericFunInGenericClassWithUpperBounds() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/genericFunInGenericClassWithUpperBounds.kt");
+ }
+
+ @TestMetadata("genericFunParamAfterName.kt")
+ public void testGenericFunParamAfterName() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/genericFunParamAfterName.kt");
+ }
+
+ @TestMetadata("inCompanionObject.kt")
+ public void testInCompanionObject() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/inCompanionObject.kt");
+ }
+
+ @TestMetadata("inFunctionBody.kt")
+ public void testInFunctionBody() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/inFunctionBody.kt");
+ }
+
+ @TestMetadata("inLocalClass.kt")
+ public void testInLocalClass() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/inLocalClass.kt");
+ }
+
+ @TestMetadata("inObject.kt")
+ public void testInObject() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/inObject.kt");
+ }
+
+ @TestMetadata("inObjectLiteral.kt")
+ public void testInObjectLiteral() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/inObjectLiteral.kt");
+ }
+
+ @TestMetadata("internal.kt")
+ public void testInternal() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/internal.kt");
+ }
+
+ @TestMetadata("KDoc.kt")
+ public void testKDoc() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/KDoc.kt");
+ }
+
+ @TestMetadata("localFunction.kt")
+ public void testLocalFunction() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/localFunction.kt");
+ }
+
+ @TestMetadata("nonBlockBodyNoType.kt")
+ public void testNonBlockBodyNoType() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/nonBlockBodyNoType.kt");
+ }
+
+ @TestMetadata("nonBlockBodyWithType.kt")
+ public void testNonBlockBodyWithType() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/nonBlockBodyWithType.kt");
+ }
+
+ @TestMetadata("open.kt")
+ public void testOpen() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/open.kt");
+ }
+
+ @TestMetadata("outsideFunction.kt")
+ public void testOutsideFunction() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/outsideFunction.kt");
+ }
+
+ @TestMetadata("override.kt")
+ public void testOverride() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/override.kt");
+ }
+
+ @TestMetadata("private.kt")
+ public void testPrivate() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/private.kt");
+ }
+
+ @TestMetadata("privateInInternalClass.kt")
+ public void testPrivateInInternalClass() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/privateInInternalClass.kt");
+ }
+
+ @TestMetadata("protected.kt")
+ public void testProtected() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/protected.kt");
+ }
+
+ @TestMetadata("public.kt")
+ public void testPublic() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/public.kt");
+ }
+
+ @TestMetadata("publicInInternalClass.kt")
+ public void testPublicInInternalClass() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/publicInInternalClass.kt");
+ }
+
+ @TestMetadata("publicInPrivateClass.kt")
+ public void testPublicInPrivateClass() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/publicInPrivateClass.kt");
+ }
+
+ @TestMetadata("secondaryConstructor.kt")
+ public void testSecondaryConstructor() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/secondaryConstructor.kt");
+ }
+
+ @TestMetadata("simple.kt")
+ public void testSimple() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/simple.kt");
+ }
+
+ @TestMetadata("topLevelFunction.kt")
+ public void testTopLevelFunction() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/topLevelFunction.kt");
+ }
+
+ @TestMetadata("typeFqName.kt")
+ public void testTypeFqName() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/typeFqName.kt");
+ }
+
+ @TestMetadata("unknownType.kt")
+ public void testUnknownType() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/unknownType.kt");
+ }
+
+ @TestMetadata("valWithDefaultGetter.kt")
+ public void testValWithDefaultGetter() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/valWithDefaultGetter.kt");
+ }
+
+ @TestMetadata("valWithGetter.kt")
+ public void testValWithGetter() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/valWithGetter.kt");
+ }
+
+ @TestMetadata("valWithGetterBlockBody.kt")
+ public void testValWithGetterBlockBody() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/valWithGetterBlockBody.kt");
+ }
+
+ @TestMetadata("varWithDefaultGetterAndSetter.kt")
+ public void testVarWithDefaultGetterAndSetter() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/varWithDefaultGetterAndSetter.kt");
+ }
+
+ @TestMetadata("varWithDefaultSetter.kt")
+ public void testVarWithDefaultSetter() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/varWithDefaultSetter.kt");
+ }
+
+ @TestMetadata("varWithGetter.kt")
+ public void testVarWithGetter() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/varWithGetter.kt");
+ }
+
+ @TestMetadata("varWithGetterAndSetter.kt")
+ public void testVarWithGetterAndSetter() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/varWithGetterAndSetter.kt");
+ }
+
+ @TestMetadata("varWithSetter.kt")
+ public void testVarWithSetter() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/varWithSetter.kt");
+ }
+
+ @TestMetadata("withImport.kt")
+ public void testWithImport() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.kt");
+ }
+
+ @TestMetadata("withJavaUsage.kt")
+ public void testWithJavaUsage() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.kt");
+ }
+
+ @TestMetadata("withType.kt")
+ public void testWithType() throws Exception {
+ runTest("../../../idea/tests/testData/intentions/declarations/convertMemberToExtension/withType.kt");
+ }
+ }
diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/K1IntentionTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/K1IntentionTestGenerated.java
index 2c5f7b4438f9..45887b53c6a4 100644
--- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/K1IntentionTestGenerated.java
+++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/K1IntentionTestGenerated.java
@@ -10965,16 +10965,31 @@ public abstract class K1IntentionTestGenerated extends AbstractK1IntentionTest {
runTest("testData/intentions/declarations/convertMemberToExtension/genericFunParamAfterName.kt");
}
+ @TestMetadata("inCompanionObject.kt")
+ public void testInCompanionObject() throws Exception {
+ runTest("testData/intentions/declarations/convertMemberToExtension/inCompanionObject.kt");
+ }
+
@TestMetadata("inFunctionBody.kt")
public void testInFunctionBody() throws Exception {
runTest("testData/intentions/declarations/convertMemberToExtension/inFunctionBody.kt");
}
+ @TestMetadata("inLocalClass.kt")
+ public void testInLocalClass() throws Exception {
+ runTest("testData/intentions/declarations/convertMemberToExtension/inLocalClass.kt");
+ }
+
@TestMetadata("inObject.kt")
public void testInObject() throws Exception {
runTest("testData/intentions/declarations/convertMemberToExtension/inObject.kt");
}
+ @TestMetadata("inObjectLiteral.kt")
+ public void testInObjectLiteral() throws Exception {
+ runTest("testData/intentions/declarations/convertMemberToExtension/inObjectLiteral.kt");
+ }
+
@TestMetadata("internal.kt")
public void testInternal() throws Exception {
runTest("testData/intentions/declarations/convertMemberToExtension/internal.kt");
@@ -11110,6 +11125,16 @@ public abstract class K1IntentionTestGenerated extends AbstractK1IntentionTest {
runTest("testData/intentions/declarations/convertMemberToExtension/varWithSetter.kt");
}
+ @TestMetadata("withImport.kt")
+ public void testWithImport() throws Exception {
+ runTest("testData/intentions/declarations/convertMemberToExtension/withImport.kt");
+ }
+
+ @TestMetadata("withJavaUsage.kt")
+ public void testWithJavaUsage() throws Exception {
+ runTest("testData/intentions/declarations/convertMemberToExtension/withJavaUsage.kt");
+ }
+
@TestMetadata("withType.kt")
public void testWithType() throws Exception {
runTest("testData/intentions/declarations/convertMemberToExtension/withType.kt");
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/.k2Intention b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/.k2Intention
new file mode 100644
index 000000000000..95cc6ee58a5c
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/.k2Intention
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ConvertMemberToExtensionIntention
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inCompanionObject.kt b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inCompanionObject.kt
new file mode 100644
index 000000000000..03d295130b28
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inCompanionObject.kt
@@ -0,0 +1,6 @@
+// IS_APPLICABLE: false
+class Owner {
+ companion object {
+ fun f() {}
+ }
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inLocalClass.kt b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inLocalClass.kt
new file mode 100644
index 000000000000..af6ccf713faa
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inLocalClass.kt
@@ -0,0 +1,9 @@
+// IS_APPLICABLE: false
+class Owner {
+ fun m() {
+ class Local {
+ fun f() {}
+ }
+ }
+}
+// IGNORE_K1
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObject.kt b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObject.kt
index 4611fb18dec2..74d44088075d 100644
--- a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObject.kt
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObject.kt
@@ -1,4 +1,4 @@
-// IS_APPLICABLE: false
object Owner {
fun f() {}
-}
\ No newline at end of file
+}
+// IGNORE_K1
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObject.kt.after b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObject.kt.after
new file mode 100644
index 000000000000..11b414002719
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObject.kt.after
@@ -0,0 +1,5 @@
+object Owner {
+}
+
+fun Owner.f() {}
+// IGNORE_K1
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObjectLiteral.kt b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObjectLiteral.kt
new file mode 100644
index 000000000000..6e41bb2817cc
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObjectLiteral.kt
@@ -0,0 +1,7 @@
+// IS_APPLICABLE: false
+interface A
+fun m() {
+ object: A {
+ fun f() {}
+ }
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.1.kt b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.1.kt
new file mode 100644
index 000000000000..ae151fa0528c
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.1.kt
@@ -0,0 +1,7 @@
+package b
+
+import a.A
+
+fun m(a: A) {
+ a.f()
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.1.kt.after b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.1.kt.after
new file mode 100644
index 000000000000..510d84c05af5
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.1.kt.after
@@ -0,0 +1,8 @@
+package b
+
+import a.A
+import a.f
+
+fun m(a: A) {
+ a.f()
+}
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.kt b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.kt
new file mode 100644
index 000000000000..5d57436d40da
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.kt
@@ -0,0 +1,5 @@
+package a
+
+class A {
+ fun f() {}
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.kt.after b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.kt.after
new file mode 100644
index 000000000000..ef9cb3e1a6e2
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.kt.after
@@ -0,0 +1,6 @@
+package a
+
+class A {
+}
+
+fun A.f() {}
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.1.java b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.1.java
new file mode 100644
index 000000000000..7b49167b252e
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.1.java
@@ -0,0 +1,9 @@
+package b;
+
+import a.A;
+
+class B {
+ void m(A a) {
+ a.f();
+ }
+}
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.1.java.after b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.1.java.after
new file mode 100644
index 000000000000..82da66588010
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.1.java.after
@@ -0,0 +1,10 @@
+package b;
+
+import a.A;
+import a.WithJavaUsageKt;
+
+class B {
+ void m(A a) {
+ WithJavaUsageKt.f(a);
+ }
+}
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.kt b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.kt
new file mode 100644
index 000000000000..5d57436d40da
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.kt
@@ -0,0 +1,5 @@
+package a
+
+class A {
+ fun f() {}
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.kt.after b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.kt.after
new file mode 100644
index 000000000000..ef9cb3e1a6e2
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.kt.after
@@ -0,0 +1,6 @@
+package a
+
+class A {
+}
+
+fun A.f() {}
diff --git a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2IntentionTests.kt b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2IntentionTests.kt
index 2d4044f43563..57418de762ad 100644
--- a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2IntentionTests.kt
+++ b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2IntentionTests.kt
@@ -107,7 +107,7 @@ internal fun MutableTWorkspace.generateK2IntentionTests() {
model("${idea}intentions/convertObjectLiteralToClass", pattern = pattern, isIgnored = true)
model("${idea}intentions/toOrdinaryStringLiteral", pattern = pattern, isIgnored = true)
model("${idea}intentions/convertLineCommentToBlockComment", pattern = pattern, isIgnored = true)
- model("${idea}intentions/declarations", pattern = pattern, isIgnored = true)
+ model("${idea}intentions/declarations/convertMemberToExtension", pattern = pattern)
model("${idea}intentions/removeEmptyPrimaryConstructor", pattern = pattern, isIgnored = true)
model("${idea}intentions/useWithIndex", pattern = pattern, isIgnored = true)
model("${idea}intentions/joinDeclarationAndAssignment", pattern = pattern, isIgnored = true)