From d3bf2e0e206a82016af35b928d51f73b9209f61c Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Sat, 21 Sep 2024 13:58:14 +0200 Subject: [PATCH] [kotlin] k2: port ConvertMemberToExtensionIntention ^KTIJ-29625 fixed GitOrigin-RevId: 0cfacaeb45fe6ceb95b5b6b9216fbe740a81ea4c --- .../kotlin.code-insight.intentions.k2.iml | 1 + .../kotlin.code-insight.intentions.k2.xml | 7 + .../ConvertMemberToExtensionIntention.kt | 350 ++++++++++++++++++ .../tests/K2IntentionTestGenerated.java | 277 ++++++++++++++ .../intentions/K1IntentionTestGenerated.java | 25 ++ .../convertMemberToExtension/.k2Intention | 1 + .../inCompanionObject.kt | 6 + .../convertMemberToExtension/inLocalClass.kt | 9 + .../convertMemberToExtension/inObject.kt | 4 +- .../inObject.kt.after | 5 + .../inObjectLiteral.kt | 7 + .../convertMemberToExtension/withImport.1.kt | 7 + .../withImport.1.kt.after | 8 + .../convertMemberToExtension/withImport.kt | 5 + .../withImport.kt.after | 6 + .../withJavaUsage.1.java | 9 + .../withJavaUsage.1.java.after | 10 + .../convertMemberToExtension/withJavaUsage.kt | 5 + .../withJavaUsage.kt.after | 6 + .../codeinsight/GenerateK2IntentionTests.kt | 2 +- 20 files changed, 747 insertions(+), 3 deletions(-) create mode 100644 plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertMemberToExtensionIntention.kt create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/.k2Intention create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inCompanionObject.kt create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inLocalClass.kt create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObject.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/inObjectLiteral.kt create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.1.kt create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.1.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.kt create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withImport.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.1.java create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.1.java.after create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.kt create mode 100644 plugins/kotlin/idea/tests/testData/intentions/declarations/convertMemberToExtension/withJavaUsage.kt.after 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)