[kotlin] Port ImplementAbstractMemberAsConstructorParameterIntention and ImplementAbstractMemberIntention for K2

^KTIJ-22962 Fixed
^KTIJ-30791 Fixed

GitOrigin-RevId: 1f64be410a90aea85db18a2b609ad4c9048796cf
This commit is contained in:
Andrey Cherkasov
2024-10-11 10:33:44 +04:00
committed by intellij-monorepo-bot
parent 7dbe81f4bf
commit 40c0bc830e
15 changed files with 748 additions and 49 deletions

View File

@@ -2708,4 +2708,6 @@ progress.title.converting.to.if.then.else.expression=Converting to if-then-else
progress.title.introducing.value.for.condition=Introducing value for condition\u2026
inspection.kotlin.options.to.compiler.options.display.name=Use of deprecated 'kotlinOptions' DSL
replace.kotlin.options.with.compiler.options=Replace 'kotlinOptions' with 'compilerOptions'
replace.kotlin.options.with.compiler.options=Replace 'kotlinOptions' with 'compilerOptions'
intention.implement.abstract.method.searching.for.descendants.progress=Searching for descendants\u2026
intention.implement.abstract.method.command.name=Implement Method

View File

@@ -415,5 +415,19 @@
<categoryKey>group.names.kotlin</categoryKey>
</intentionAction>
<intentionAction>
<language>kotlin</language>
<className>org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ImplementAbstractMemberIntention</className>
<bundleName>messages.KotlinBundle</bundleName>
<categoryKey>group.names.kotlin</categoryKey>
</intentionAction>
<intentionAction>
<language>kotlin</language>
<className>org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ImplementAbstractMemberAsConstructorParameterIntention</className>
<bundleName>messages.KotlinBundle</bundleName>
<categoryKey>group.names.kotlin</categoryKey>
</intentionAction>
</extensions>
</idea-plugin>

View File

@@ -0,0 +1,41 @@
// 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.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ImplementAbstractMemberIntentionBase.ImplementableMember.KtImplementableMember
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtProperty
internal class ImplementAbstractMemberAsConstructorParameterIntention : ImplementAbstractMemberIntentionBase() {
override fun computeText(element: KtNamedDeclaration): (() -> String)? {
if (element !is KtProperty) return null
return KotlinBundle.lazyMessage("implement.as.constructor.parameter")
}
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
if (element !is KtProperty) return null
return super.applicabilityRange(element)
}
override fun KaSession.createImplementableMember(
targetClass: PsiElement,
abstractMember: KtNamedDeclaration,
): ImplementableMember? {
return when (targetClass) {
is KtLightClass -> {
KtImplementableMember.from(
analysisSession = this,
targetClass = targetClass,
abstractMember = abstractMember,
preferConstructorParameters = true,
)
}
else -> null
}
}
}

View File

@@ -0,0 +1,51 @@
// 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.psi.PsiClass
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ImplementAbstractMemberIntentionBase.ImplementableMember.JavaImplementableMember
import org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ImplementAbstractMemberIntentionBase.ImplementableMember.KtImplementableMember
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
internal class ImplementAbstractMemberIntention : ImplementAbstractMemberIntentionBase() {
override fun computeText(element: KtNamedDeclaration): (() -> String)? = when (element) {
is KtProperty -> KotlinBundle.lazyMessage("implement.abstract.property")
is KtNamedFunction -> KotlinBundle.lazyMessage("implement.abstract.function")
else -> null
}
override fun KaSession.createImplementableMember(
targetClass: PsiElement,
abstractMember: KtNamedDeclaration,
): ImplementableMember? {
return when (targetClass) {
is KtLightClass -> KtImplementableMember.from(
analysisSession = this,
targetClass = targetClass,
abstractMember = abstractMember,
preferConstructorParameters = false,
)
is KtEnumEntry -> KtImplementableMember.from(
analysisSession = this,
targetClass = targetClass,
abstractMember = abstractMember,
preferConstructorParameters = false,
)
is PsiClass -> JavaImplementableMember.from(
targetClass = targetClass,
abstractMember = abstractMember,
)
else -> null
}
}
}

View File

@@ -0,0 +1,323 @@
// 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.CodeInsightBundle
import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInsight.generation.OverrideImplementUtil
import com.intellij.codeInsight.intention.impl.BaseIntentionAction
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.impl.Utils.computeWithProgressIcon
import com.intellij.openapi.application.readAction
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.platform.backend.presentation.TargetPresentation
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.ui.awt.RelativePoint
import com.intellij.ui.list.buildTargetPopupWithMultiSelect
import com.intellij.util.IncorrectOperationException
import com.intellij.util.application
import org.jetbrains.kotlin.analysis.api.KaExperimentalApi
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KaClassKind
import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KaDeclarationContainerSymbol
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.toLightMethods
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.findExistingEditor
import org.jetbrains.kotlin.idea.codeinsights.impl.base.intentions.ClassListCellRenderer
import org.jetbrains.kotlin.idea.core.overrideImplement.*
import org.jetbrains.kotlin.idea.k2.refactoring.findCallableMemberBySignature
import org.jetbrains.kotlin.idea.refactoring.isAbstract
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.isUnitTestMode
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.util.ImplementationStatus.ALREADY_IMPLEMENTED
import org.jetbrains.kotlin.util.ImplementationStatus.INHERITED_OR_SYNTHESIZED
private val LOG = Logger.getInstance(ImplementAbstractMemberIntentionBase::class.java.canonicalName)
abstract class ImplementAbstractMemberIntentionBase : SelfTargetingRangeIntention<KtNamedDeclaration>(
KtNamedDeclaration::class.java,
KotlinBundle.lazyMessage("implement.abstract.member"),
) {
override fun startInWriteAction(): Boolean = false
protected abstract fun computeText(element: KtNamedDeclaration): (() -> String)?
private fun isApplicable(element: KtNamedDeclaration): Boolean = analyze(element) {
findImplementableMembers(element).any()
}
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
if (!element.isAbstract()) return null
setTextGetter(computeText(element) ?: return null)
val isApplicable = if (application.isDispatchThread) {
val editor = element.findExistingEditor()!!
val aComponent = editor.contentComponent
val point = RelativePoint(aComponent, editor.logicalPositionToXY(editor.offsetToLogicalPosition(element.startOffset)))
computeWithProgressIcon(point, aComponent, ActionPlaces.UNKNOWN) {
readAction { isApplicable(element) }
}
} else {
isApplicable(element)
}
if (!isApplicable) return null
return element.nameIdentifier?.textRange
}
protected abstract fun KaSession.createImplementableMember(
targetClass: PsiElement,
abstractMember: KtNamedDeclaration,
): ImplementableMember?
override fun applyTo(element: KtNamedDeclaration, editor: Editor?) {
if (editor == null) throw IllegalArgumentException("This intention requires an editor")
val project = element.project
val implementableMembers = runWithModalProgressBlocking(
project,
KotlinBundle.message("intention.implement.abstract.method.searching.for.descendants.progress"),
) {
runReadAction {
analyze(element) {
findImplementableMembers(element).toList()
}
}
}
if (implementableMembers.isEmpty()) return
implementableMembers.singleOrNull()?.let {
return it.generateMembers(editor)
}
val classRenderer = ClassListCellRenderer()
val classComparator = classRenderer.comparator
val sortedImplementableMembers = implementableMembers.sortedWith { o1, o2 ->
classComparator.compare(o1.getTargetClass(), o2.getTargetClass())
}
if (isUnitTestMode()) {
return implementTargetMembers(project, sortedImplementableMembers)
}
buildTargetPopupWithMultiSelect(
items = sortedImplementableMembers,
presentationProvider = {
val targetClass = it.getTargetClass()
TargetPresentation.builder(classRenderer.getElementText(targetClass)!!)
.icon(targetClass.getIcon(0))
.presentation()
}) { true }
.setTitle(CodeInsightBundle.message("intention.implement.abstract.method.class.chooser.title"))
.setItemsChosenCallback {
implementTargetMembers(project, it)
}
.createPopup()
.showInBestPositionFor(editor)
}
private fun implementTargetMembers(
project: Project,
implementableMembers: Collection<ImplementableMember>,
) {
project.executeCommand(KotlinBundle.message("intention.implement.abstract.method.command.name")) {
val targetClasses = implementableMembers.map(ImplementableMember::getTargetClass)
if (!FileModificationService.getInstance().preparePsiElementsForWrite(targetClasses)) return@executeCommand
runWriteAction {
for (implementableMember in implementableMembers) {
try {
val descriptor = OpenFileDescriptor(project, implementableMember.getTargetClass().containingFile.virtualFile)
val targetEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, /* focusEditor = */ true)
implementableMember.generateMembers(targetEditor)
} catch (e: IncorrectOperationException) {
LOG.error(e)
}
}
}
}
}
private fun KaSession.findImplementableMembers(
abstractMember: KtNamedDeclaration,
): Sequence<ImplementableMember> {
val baseClass = abstractMember.containingClassOrObject as? KtClass ?: return emptySequence()
fun KaSession.createImplementableMember(targetClass: PsiElement): ImplementableMember? {
if (!BaseIntentionAction.canModify(targetClass)) return null
return createImplementableMember(targetClass, abstractMember)
}
return if (baseClass.isEnum()) {
baseClass.declarations
.asSequence()
.filterIsInstance<KtEnumEntry>()
.mapNotNull(::createImplementableMember)
} else {
HierarchySearchRequest(baseClass, baseClass.useScope, searchDeeply = false)
.searchInheritors()
.asSequence()
.mapNotNull(::createImplementableMember)
}
}
protected sealed interface ImplementableMember {
fun generateMembers(editor: Editor?)
fun getTargetClass(): PsiElement
class KtImplementableMember private constructor(
private val origin: KtClassOrObject,
private val targetClass: PsiElement,
private val member: KtClassMember,
) : ImplementableMember {
override fun generateMembers(editor: Editor?) {
KtOverrideMembersHandler().generateMembers(
editor = editor!!,
classOrObject = origin,
selectedElements = listOf(member),
copyDoc = false,
)
}
override fun getTargetClass(): PsiElement {
return targetClass
}
companion object {
fun from(
analysisSession: KaSession,
targetClass: KtLightClass,
abstractMember: KtNamedDeclaration,
preferConstructorParameters: Boolean,
): KtImplementableMember? {
val origin = targetClass.kotlinOrigin ?: return null
val ktClassMember = with(analysisSession) {
val subClass = origin.symbol as? KaClassSymbol ?: return null
if (subClass.classKind == KaClassKind.INTERFACE) return null
val existingImplementation = findExistingImplementation(subClass, abstractMember)
if (existingImplementation != null) return null
val symbolToImplement = getCallableMemberToImplement(abstractMember, subClass) ?: return null
createKtClassMember(symbolToImplement, preferConstructorParameters)
}
return KtImplementableMember(
targetClass = targetClass,
member = ktClassMember,
origin = origin,
)
}
@OptIn(KaExperimentalApi::class)
private fun KaSession.findExistingImplementation(
targetClass: KaClassSymbol,
abstractMember: KtNamedDeclaration,
): KaCallableSymbol? {
val superMember = abstractMember.symbol as? KaCallableSymbol ?: return null
val superClass = superMember.containingDeclaration as? KaClassSymbol ?: return null
val substitutor = createInheritanceTypeSubstitutor(targetClass, superClass) ?: return null
val signatureInSubClass = superMember.substitute(substitutor)
return targetClass.memberScope.findCallableMemberBySignature(signatureInSubClass)?.takeIf {
val implementationStatus = it.getImplementationStatus(targetClass)
implementationStatus == ALREADY_IMPLEMENTED || implementationStatus == INHERITED_OR_SYNTHESIZED
}
}
@OptIn(KaExperimentalApi::class)
fun from(
analysisSession: KaSession,
targetClass: KtEnumEntry,
abstractMember: KtNamedDeclaration,
preferConstructorParameters: Boolean,
): KtImplementableMember? {
val ktClassMember = with(analysisSession) {
val symbol = targetClass.symbol
val enumEntryInitializer = symbol.enumEntryInitializer
val existingImplementation = enumEntryInitializer?.memberScope?.findCallableMemberBySignature(symbol.asSignature())
if (existingImplementation != null) return null
val symbolToImplement = abstractMember.symbol as? KaCallableSymbol ?: return null
createKtClassMember(symbolToImplement, preferConstructorParameters)
}
return KtImplementableMember(
targetClass = targetClass,
member = ktClassMember,
origin = targetClass,
)
}
@OptIn(KaExperimentalApi::class)
private fun KaSession.getCallableMemberToImplement(
abstractMember: KtNamedDeclaration,
subClass: KaDeclarationContainerSymbol,
): KaCallableSymbol? {
val superMember = abstractMember.symbol as? KaCallableSymbol ?: return null
val superClass = superMember.containingDeclaration as? KaClassSymbol ?: return null
val substitution = createInheritanceTypeSubstitutor(subClass as KaClassSymbol, superClass) ?: return null
val signatureToImplement = superMember.substitute(substitution)
return subClass.memberScope.findCallableMemberBySignature(signatureToImplement)
}
@OptIn(KaExperimentalApi::class)
private fun KaSession.createKtClassMember(
symbolToImplement: KaCallableSymbol,
preferConstructorParameters: Boolean,
): KtClassMember = KtClassMember(
memberInfo = KtClassMemberInfo.create(
symbol = symbolToImplement,
memberText = symbolToImplement.render(KtGenerateMembersHandler.renderer),
),
bodyType = BodyType.FromTemplate,
preferConstructorParameter = preferConstructorParameters,
)
}
}
class JavaImplementableMember private constructor(
private val targetClass: PsiClass,
private val abstractMember: KtNamedDeclaration,
) : ImplementableMember {
companion object {
fun from(
targetClass: PsiClass,
abstractMember: KtNamedDeclaration,
): JavaImplementableMember? {
if (targetClass.isInterface) return null
return JavaImplementableMember(targetClass, abstractMember)
}
}
override fun generateMembers(editor: Editor?) {
abstractMember.toLightMethods().forEach { OverrideImplementUtil.overrideOrImplement(targetClass, it) }
}
override fun getTargetClass(): PsiElement {
return targetClass
}
}
}
}

View File

@@ -7190,7 +7190,74 @@ public abstract class K2IntentionTestGenerated extends AbstractK2IntentionTest {
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/implementAsConstructorParameter")
public static class ImplementAsConstructorParameter 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("enumClass.kt")
public void testEnumClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAsConstructorParameter/enumClass.kt");
}
@TestMetadata("function.kt")
public void testFunction() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAsConstructorParameter/function.kt");
}
@TestMetadata("implementAll.kt")
public void testImplementAll() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAsConstructorParameter/implementAll.kt");
}
@TestMetadata("inEnumClass.kt")
public void testInEnumClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAsConstructorParameter/inEnumClass.kt");
}
@TestMetadata("inFinalClass.kt")
public void testInFinalClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAsConstructorParameter/inFinalClass.kt");
}
@TestMetadata("inObject.kt")
public void testInObject() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAsConstructorParameter/inObject.kt");
}
@TestMetadata("noDirectOverridesNeeded.kt")
public void testNoDirectOverridesNeeded() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAsConstructorParameter/noDirectOverridesNeeded.kt");
}
@TestMetadata("noInheritors.kt")
public void testNoInheritors() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAsConstructorParameter/noInheritors.kt");
}
@TestMetadata("notAbstractInClass.kt")
public void testNotAbstractInClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAsConstructorParameter/notAbstractInClass.kt");
}
@TestMetadata("notAbstractNoBodyInClass.kt")
public void testNotAbstractNoBodyInClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAsConstructorParameter/notAbstractNoBodyInClass.kt");
}
@TestMetadata("notAbstractWithGetterInInterface.kt")
public void testNotAbstractWithGetterInInterface() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAsConstructorParameter/notAbstractWithGetterInInterface.kt");
}
}
@@ -10450,7 +10517,167 @@ public abstract class K2IntentionTestGenerated extends AbstractK2IntentionTest {
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/implementAbstractMember")
public abstract static class ImplementAbstractMember extends AbstractK2IntentionTest {
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/implementAbstractMember/function")
public static class Function 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("enumClass.kt")
public void testEnumClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/enumClass.kt");
}
@TestMetadata("enumClassWithSemicolon.kt")
public void testEnumClassWithSemicolon() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/enumClassWithSemicolon.kt");
}
@TestMetadata("enumClassWithSemicolonAndMembers.kt")
public void testEnumClassWithSemicolonAndMembers() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/enumClassWithSemicolonAndMembers.kt");
}
@TestMetadata("enumEntries.kt")
public void testEnumEntries() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/enumEntries.kt");
}
@TestMetadata("enumEntriesWithArgs.kt")
public void testEnumEntriesWithArgs() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/enumEntriesWithArgs.kt");
}
@TestMetadata("implementAll.kt")
public void testImplementAll() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/implementAll.kt");
}
@TestMetadata("inFinalClass.kt")
public void testInFinalClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/inFinalClass.kt");
}
@TestMetadata("inObject.kt")
public void testInObject() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/inObject.kt");
}
@TestMetadata("noDirectOverridesNeeded.kt")
public void testNoDirectOverridesNeeded() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/noDirectOverridesNeeded.kt");
}
@TestMetadata("noInheritors.kt")
public void testNoInheritors() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/noInheritors.kt");
}
@TestMetadata("notAbstractInClass.kt")
public void testNotAbstractInClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/notAbstractInClass.kt");
}
@TestMetadata("notAbstractInInterface.kt")
public void testNotAbstractInInterface() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/notAbstractInInterface.kt");
}
@TestMetadata("notAbstractNoBodyInClass.kt")
public void testNotAbstractNoBodyInClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/function/notAbstractNoBodyInClass.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/implementAbstractMember/property")
public static class Property 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("enumClass.kt")
public void testEnumClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/enumClass.kt");
}
@TestMetadata("enumClassWithSemicolon.kt")
public void testEnumClassWithSemicolon() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/enumClassWithSemicolon.kt");
}
@TestMetadata("enumClassWithSemicolonAndMembers.kt")
public void testEnumClassWithSemicolonAndMembers() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/enumClassWithSemicolonAndMembers.kt");
}
@TestMetadata("enumEntries.kt")
public void testEnumEntries() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/enumEntries.kt");
}
@TestMetadata("enumEntriesWithArgs.kt")
public void testEnumEntriesWithArgs() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/enumEntriesWithArgs.kt");
}
@TestMetadata("implementAll.kt")
public void testImplementAll() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/implementAll.kt");
}
@TestMetadata("inFinalClass.kt")
public void testInFinalClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/inFinalClass.kt");
}
@TestMetadata("inObject.kt")
public void testInObject() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/inObject.kt");
}
@TestMetadata("noDirectOverridesNeeded.kt")
public void testNoDirectOverridesNeeded() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/noDirectOverridesNeeded.kt");
}
@TestMetadata("noInheritors.kt")
public void testNoInheritors() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/noInheritors.kt");
}
@TestMetadata("notAbstractInClass.kt")
public void testNotAbstractInClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/notAbstractInClass.kt");
}
@TestMetadata("notAbstractNoBodyInClass.kt")
public void testNotAbstractNoBodyInClass() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/notAbstractNoBodyInClass.kt");
}
@TestMetadata("notAbstractWithGetterInInterface.kt")
public void testNotAbstractWithGetterInInterface() throws Exception {
runTest("../../../idea/tests/testData/intentions/implementAbstractMember/property/notAbstractWithGetterInInterface.kt");
}
}
}

View File

@@ -18,40 +18,72 @@ import org.junit.runner.RunWith;
@TestRoot("code-insight/intentions-k2/tests")
@TestDataPath("$CONTENT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile")
public class K2MultiFileIntentionTestGenerated extends AbstractK2MultiFileIntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
public abstract class K2MultiFileIntentionTestGenerated extends AbstractK2MultiFileIntentionTest {
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile")
public static class MoveDeclarationToSeparateFile extends AbstractK2MultiFileIntentionTest {
@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("moveClassToExistingFile/moveClassToExistingFile.test")
public void testMoveClassToExistingFile_MoveClassToExistingFile() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile/moveClassToExistingFile/moveClassToExistingFile.test");
}
@TestMetadata("moveClassToFileInDefaultPackage/moveClassToFileInDefaultPackage.test")
public void testMoveClassToFileInDefaultPackage_MoveClassToFileInDefaultPackage() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile/moveClassToFileInDefaultPackage/moveClassToFileInDefaultPackage.test");
}
@TestMetadata("moveClassToFile/moveClassToFile.test")
public void testMoveClassToFile_MoveClassToFile() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile/moveClassToFile/moveClassToFile.test");
}
@TestMetadata("moveSingleToFile/moveSingleToFile.test")
public void testMoveSingleToFile_MoveSingleToFile() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile/moveSingleToFile/moveSingleToFile.test");
}
@TestMetadata("optimizeImports/optimizeImports.test")
public void testOptimizeImports_OptimizeImports() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile/optimizeImports/optimizeImports.test");
}
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/multiFileIntentions/implementAbstractMember")
public static class ImplementAbstractMember extends AbstractK2MultiFileIntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
@TestMetadata("moveClassToExistingFile/moveClassToExistingFile.test")
public void testMoveClassToExistingFile_MoveClassToExistingFile() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile/moveClassToExistingFile/moveClassToExistingFile.test");
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("moveClassToFileInDefaultPackage/moveClassToFileInDefaultPackage.test")
public void testMoveClassToFileInDefaultPackage_MoveClassToFileInDefaultPackage() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile/moveClassToFileInDefaultPackage/moveClassToFileInDefaultPackage.test");
}
@TestMetadata("implementFunctionInJava/implementAllInJava.test")
public void testImplementFunctionInJava_ImplementAllInJava() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/implementAbstractMember/implementFunctionInJava/implementAllInJava.test");
}
@TestMetadata("moveClassToFile/moveClassToFile.test")
public void testMoveClassToFile_MoveClassToFile() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile/moveClassToFile/moveClassToFile.test");
}
@TestMetadata("implementValInJava/implementAllInJava.test")
public void testImplementValInJava_ImplementAllInJava() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/implementAbstractMember/implementValInJava/implementAllInJava.test");
}
@TestMetadata("moveSingleToFile/moveSingleToFile.test")
public void testMoveSingleToFile_MoveSingleToFile() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile/moveSingleToFile/moveSingleToFile.test");
}
@TestMetadata("optimizeImports/optimizeImports.test")
public void testOptimizeImports_OptimizeImports() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/moveDeclarationToSeparateFile/optimizeImports/optimizeImports.test");
@TestMetadata("implementVarInJava/implementAllInJava.test")
public void testImplementVarInJava_ImplementAllInJava() throws Exception {
runTest("../../../idea/tests/testData/multiFileIntentions/implementAbstractMember/implementVarInJava/implementAllInJava.test");
}
}
}

View File

@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ImplementAbstractMemberIntention

View File

@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ImplementAbstractMemberIntention

View File

@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ImplementAbstractMemberAsConstructorParameterIntention

View File

@@ -1,5 +1,6 @@
{
"mainFile": "source/test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.intentions.ImplementAbstractMemberIntention",
"intentionK2Class": "org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ImplementAbstractMemberIntention",
"withRuntime": "true"
}

View File

@@ -1,5 +1,6 @@
{
"mainFile": "source/test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.intentions.ImplementAbstractMemberIntention",
"intentionK2Class": "org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ImplementAbstractMemberIntention",
"withRuntime": "true"
}

View File

@@ -1,5 +1,6 @@
{
"mainFile": "source/test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.intentions.ImplementAbstractMemberIntention",
"intentionK2Class": "org.jetbrains.kotlin.idea.k2.codeinsight.intentions.ImplementAbstractMemberIntention",
"withRuntime": "true"
}

View File

@@ -13,24 +13,13 @@ import org.jetbrains.annotations.Nls
import org.jetbrains.kotlin.analysis.api.KaExperimentalApi
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.resolution.KaErrorCallInfo
import org.jetbrains.kotlin.analysis.api.resolution.KaImplicitReceiverValue
import org.jetbrains.kotlin.analysis.api.resolution.KaSimpleFunctionCall
import org.jetbrains.kotlin.analysis.api.resolution.successfulFunctionCallOrNull
import org.jetbrains.kotlin.analysis.api.resolution.successfulVariableAccessCall
import org.jetbrains.kotlin.analysis.api.resolution.symbol
import org.jetbrains.kotlin.analysis.api.resolution.*
import org.jetbrains.kotlin.analysis.api.scopes.KaScope
import org.jetbrains.kotlin.analysis.api.signatures.KaCallableSignature
import org.jetbrains.kotlin.analysis.api.signatures.KaFunctionSignature
import org.jetbrains.kotlin.analysis.api.symbols.KaAnonymousObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KaClassKind
import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KaClassifierSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KaReceiverParameterSymbol
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.symbols.markers.KaDeclarationContainerSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KaNamedSymbol
import org.jetbrains.kotlin.analysis.api.symbols.name
import org.jetbrains.kotlin.analysis.api.symbols.receiverType
import org.jetbrains.kotlin.analysis.api.types.KaType
import org.jetbrains.kotlin.analysis.api.types.KaTypeParameterType
import org.jetbrains.kotlin.idea.base.analysis.api.utils.analyzeInModalWindow
@@ -221,7 +210,20 @@ fun getThisQualifier(receiverValue: KaImplicitReceiverValue): String {
* @return The matching callable symbol if found, null otherwise.
*/
context(KaSession)
fun KaClassSymbol.findCallableMemberBySignature(
fun KaDeclarationContainerSymbol.findCallableMemberBySignature(
callableSignature: KaCallableSignature<KaCallableSymbol>
): KaCallableSymbol? = declaredMemberScope.findCallableMemberBySignature(callableSignature)
/**
* Finds a callable member of the class by its signature in the scope.
*
* @param callableSignature The signature of the callable to be found, which includes
* the symbol name, return type, receiver type, and value parameters.
*
* @return The matching callable symbol if found, null otherwise.
*/
context(KaSession)
fun KaScope.findCallableMemberBySignature(
callableSignature: KaCallableSignature<KaCallableSymbol>
): KaCallableSymbol? {
fun KaType?.eq(anotherType: KaType?): Boolean {
@@ -229,7 +231,7 @@ fun KaClassSymbol.findCallableMemberBySignature(
return this.semanticallyEquals(anotherType)
}
return declaredMemberScope.callables.firstOrNull { callable ->
return callables.firstOrNull { callable ->
fun parametersMatch(): Boolean {
if (callableSignature is KaFunctionSignature && callable is KaFunctionSymbol) {
if (callable.valueParameters.size != callableSignature.valueParameters.size) return false

View File

@@ -77,7 +77,7 @@ internal fun MutableTWorkspace.generateK2IntentionTests() {
model("${idea}intentions/convertRangeCheckToTwoComparisons", pattern = pattern, isIgnored = true)
model("${idea}intentions/removeRedundantCallsOfConversionMethods", pattern = pattern, isIgnored = true)
model("${idea}intentions/addJvmStatic", pattern = pattern, isIgnored = true)
model("${idea}intentions/implementAsConstructorParameter", pattern = pattern, isIgnored = true)
model("${idea}intentions/implementAsConstructorParameter", pattern = pattern)
model("${idea}intentions/insertCurlyBracesToTemplate", pattern = pattern, isIgnored = true)
model("${idea}intentions/replaceUntilWithRangeTo", pattern = pattern, isIgnored = true)
model("${idea}intentions/convertLateinitPropertyToNullable", pattern = pattern, isIgnored = true)
@@ -161,7 +161,7 @@ internal fun MutableTWorkspace.generateK2IntentionTests() {
model("${idea}intentions/copyConcatenatedStringToClipboard", pattern = pattern, isIgnored = true)
model("${idea}intentions/inlayHints", pattern = pattern, isIgnored = true)
model("${idea}intentions/convertToScope", pattern = pattern)
model("${idea}intentions/implementAbstractMember", pattern = pattern, isIgnored = true)
model("${idea}intentions/implementAbstractMember", pattern = pattern)
model("${idea}intentions/replaceSizeZeroCheckWithIsEmpty", pattern = pattern, isIgnored = true)
model("${idea}intentions/movePropertyToClassBody", pattern = pattern, isIgnored = true)
model("${idea}intentions/indentRawString", pattern = pattern, isIgnored = true)
@@ -190,6 +190,7 @@ internal fun MutableTWorkspace.generateK2IntentionTests() {
testClass<AbstractK2MultiFileIntentionTest> {
model("${idea}/multiFileIntentions/moveDeclarationToSeparateFile", pattern = TEST, flatten = true)
model("${idea}/multiFileIntentions/implementAbstractMember", pattern = TEST, flatten = true)
}
testClass<AbstractK2GotoTestOrCodeActionTest> {