Push Down: Support moving members from Java to Kotlin class

#KT-9485 Fixed
This commit is contained in:
Alexey Sedunov
2016-09-22 20:19:36 +03:00
parent 924bb44862
commit 956c6eeec7
67 changed files with 604 additions and 161 deletions
+4
View File
@@ -711,6 +711,10 @@
language="kotlin"
implementationClass="org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinClassMembersRefactoringSupport"/>
<refactoring.pushDown
language="kotlin"
implementationClass="org.jetbrains.kotlin.idea.refactoring.pushDown.JavaToKotlinPushDownDelegate"/>
<problemFileHighlightFilter implementation="org.jetbrains.kotlin.idea.projectView.KotlinProblemFileHighlightFilter"/>
<codeInsight.typeInfo language="kotlin" implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinExpressionTypeProvider"/>
@@ -0,0 +1,116 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.pushDown
import com.intellij.psi.*
import com.intellij.refactoring.memberPushDown.JavaPushDownDelegate
import com.intellij.refactoring.memberPushDown.NewSubClassData
import com.intellij.refactoring.memberPushDown.PushDownData
import com.intellij.refactoring.util.RefactoringUtil
import com.intellij.refactoring.util.classMembers.MemberInfo
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMemberDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.getOrCreateCompanionObject
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
import org.jetbrains.kotlin.idea.refactoring.j2k
import org.jetbrains.kotlin.idea.refactoring.j2kText
import org.jetbrains.kotlin.idea.refactoring.pullUp.addMemberToTarget
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
class JavaToKotlinPushDownDelegate : JavaPushDownDelegate() {
override fun checkTargetClassConflicts(
targetClass: PsiElement?,
pushDownData: PushDownData<MemberInfo, PsiMember>,
conflicts: MultiMap<PsiElement, String>,
subClassData: NewSubClassData?
) {
super.checkTargetClassConflicts(targetClass, pushDownData, conflicts, subClassData)
val ktClass = targetClass?.unwrapped as? KtClassOrObject ?: return
val resolutionFacade = ktClass.getResolutionFacade()
val targetClassDescriptor = resolutionFacade.resolveToDescriptor(ktClass) as ClassDescriptor
for (memberInfo in pushDownData.membersToMove) {
val member = memberInfo.member ?: continue
checkExternalUsages(conflicts, member, targetClassDescriptor, resolutionFacade)
}
}
override fun pushDownToClass(targetClass: PsiElement, pushDownData: PushDownData<MemberInfo, PsiMember>) {
val superClass = pushDownData.sourceClass as? PsiClass ?: return
val subClass = targetClass.unwrapped as? KtClassOrObject ?: return
val resolutionFacade = subClass.getResolutionFacade()
val superClassDescriptor = superClass.getJavaClassDescriptor(resolutionFacade) ?: return
val subClassDescriptor = resolutionFacade.resolveToDescriptor(subClass) as ClassDescriptor
val substitutor = getTypeSubstitutor(superClassDescriptor.defaultType, subClassDescriptor.defaultType) ?: TypeSubstitutor.EMPTY
val psiFactory = KtPsiFactory(subClass)
var hasAbstractMembers = false
members@ for (memberInfo in pushDownData.membersToMove) {
val member = memberInfo.member
val memberDescriptor = member.getJavaMemberDescriptor(resolutionFacade) ?: continue
when (member) {
is PsiMethod, is PsiField -> {
val ktMember = member.j2k() as? KtCallableDeclaration ?: continue@members
ktMember.removeModifier(KtTokens.DEFAULT_VISIBILITY_KEYWORD)
val isStatic = member.hasModifierProperty(PsiModifier.STATIC)
val targetMemberClass = if (isStatic && subClass is KtClass) subClass.getOrCreateCompanionObject() else subClass
val targetMemberClassDescriptor = resolutionFacade.resolveToDescriptor(targetMemberClass) as ClassDescriptor
if (member.hasModifierProperty(PsiModifier.ABSTRACT)) {
hasAbstractMembers = true
}
moveCallableMemberToClass(
ktMember,
memberDescriptor as CallableMemberDescriptor,
targetMemberClass,
targetMemberClassDescriptor,
substitutor,
memberInfo.isToAbstract
).apply {
if (subClass.isInterfaceClass()) {
removeModifier(KtTokens.ABSTRACT_KEYWORD)
}
}
}
is PsiClass -> {
if (memberInfo.overrides != null) {
val typeText = RefactoringUtil.findReferenceToClass(superClass.implementsList, member)?.j2kText() ?: continue@members
subClass.addSuperTypeListEntry(psiFactory.createSuperTypeEntry(typeText))
}
else {
val ktClass = member.j2k() as? KtClassOrObject ?: continue@members
addMemberToTarget(ktClass, subClass)
}
}
}
}
if (hasAbstractMembers && !subClass.isInterfaceClass()) {
subClass.addModifier(KtTokens.ABSTRACT_KEYWORD)
}
}
}
@@ -29,28 +29,24 @@ import com.intellij.usageView.UsageViewDescriptor
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.refactoring.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KtPsiClassWrapper
import org.jetbrains.kotlin.idea.refactoring.pullUp.*
import org.jetbrains.kotlin.idea.refactoring.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
import org.jetbrains.kotlin.util.findCallableMemberBySignature
import org.jetbrains.kotlin.utils.keysToMap
import java.util.ArrayList
import java.util.*
class KotlinPushDownContext(
val sourceClass: KtClass,
@@ -141,31 +137,14 @@ class KotlinPushDownProcessor(
is KtProperty, is KtNamedFunction -> {
memberDescriptor as CallableMemberDescriptor
val targetMemberDescriptor = memberDescriptor.substitute(substitutor)?.let {
targetClassDescriptor.findCallableMemberBySignature(it as CallableMemberDescriptor)
}
val targetMember = targetMemberDescriptor?.source?.getPsi() as? KtCallableDeclaration
targetMember?.apply {
if (memberDescriptor.modality != Modality.ABSTRACT && memberInfo.isToAbstract) {
addModifier(KtTokens.OVERRIDE_KEYWORD)
}
else if (memberDescriptor.overriddenDescriptors.isEmpty()) {
removeModifier(KtTokens.OVERRIDE_KEYWORD)
}
else {
addModifier(KtTokens.OVERRIDE_KEYWORD)
}
} ?: addMemberToTarget(member, targetClass).apply {
if (this@KotlinPushDownProcessor.context.sourceClassDescriptor.kind == ClassKind.INTERFACE) {
if (targetClassDescriptor.kind != ClassKind.INTERFACE && memberDescriptor.modality == Modality.ABSTRACT) {
addModifier(KtTokens.ABSTRACT_KEYWORD)
}
}
if (memberDescriptor.modality != Modality.ABSTRACT && memberInfo.isToAbstract) {
KtTokens.VISIBILITY_MODIFIERS.types.forEach { removeModifier(it as KtModifierKeywordToken) }
addModifier(KtTokens.OVERRIDE_KEYWORD)
}
}
moveCallableMemberToClass(
member as KtCallableDeclaration,
memberDescriptor,
targetClass,
targetClassDescriptor,
substitutor,
memberInfo.isToAbstract
)
}
is KtClassOrObject, is KtPsiClassWrapper -> {
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.refactoring.memberInfo.KtPsiClassWrapper
import org.jetbrains.kotlin.idea.refactoring.pullUp.renderForConflicts
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver
@@ -97,7 +98,7 @@ private fun checkConflicts(
for (member in membersToPush) {
checkMemberClashing(conflicts, context, member, membersToKeepAbstract, substitutor, targetClass, targetClassDescriptor)
checkSuperCalls(conflicts, context, member, membersToPush)
checkExternalUsages(conflicts, context, member, targetClassDescriptor)
checkExternalUsages(conflicts, member, targetClassDescriptor, context.resolutionFacade)
checkVisibility(conflicts, context, member, targetClassDescriptor)
}
}
@@ -169,15 +170,15 @@ private fun checkSuperCalls(
)
}
private fun checkExternalUsages(
internal fun checkExternalUsages(
conflicts: MultiMap<PsiElement, String>,
context: KotlinPushDownContext,
member: KtNamedDeclaration,
targetClassDescriptor: ClassDescriptor
member: PsiElement,
targetClassDescriptor: ClassDescriptor,
resolutionFacade: ResolutionFacade
): Unit {
for (ref in ReferencesSearch.search(member, member.resolveScope, false)) {
val calleeExpr = ref.element as? KtSimpleNameExpression ?: continue
val resolvedCall = calleeExpr.getResolvedCall(context.resolutionFacade.analyze(calleeExpr)) ?: continue
val resolvedCall = calleeExpr.getResolvedCall(resolutionFacade.analyze(calleeExpr)) ?: continue
val callElement = resolvedCall.call.callElement
val dispatchReceiver = resolvedCall.dispatchReceiver
if (dispatchReceiver == null || dispatchReceiver is Qualifier) continue
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.pushDown
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.refactoring.pullUp.addMemberToTarget
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.util.findCallableMemberBySignature
internal fun moveCallableMemberToClass(
member: KtCallableDeclaration,
memberDescriptor: CallableMemberDescriptor,
targetClass: KtClassOrObject,
targetClassDescriptor: ClassDescriptor,
substitutor: TypeSubstitutor,
makeAbstract: Boolean
): KtCallableDeclaration {
val targetMemberDescriptor = memberDescriptor.substitute(substitutor)?.let {
targetClassDescriptor.findCallableMemberBySignature(it as CallableMemberDescriptor)
}
val targetMember = targetMemberDescriptor?.source?.getPsi() as? KtCallableDeclaration
return targetMember?.apply {
if (memberDescriptor.modality != Modality.ABSTRACT && makeAbstract) {
addModifier(KtTokens.OVERRIDE_KEYWORD)
}
else if (memberDescriptor.overriddenDescriptors.isEmpty()) {
removeModifier(KtTokens.OVERRIDE_KEYWORD)
}
else {
addModifier(KtTokens.OVERRIDE_KEYWORD)
}
} ?: addMemberToTarget(member, targetClass).apply {
val sourceClassDescriptor = memberDescriptor.containingDeclaration as? ClassDescriptor
if (sourceClassDescriptor?.kind == ClassKind.INTERFACE) {
if (targetClassDescriptor.kind != ClassKind.INTERFACE && memberDescriptor.modality == Modality.ABSTRACT) {
addModifier(KtTokens.ABSTRACT_KEYWORD)
}
}
if (memberDescriptor.modality != Modality.ABSTRACT && makeAbstract) {
KtTokens.VISIBILITY_MODIFIERS.types.forEach { removeModifier(it as KtModifierKeywordToken) }
addModifier(KtTokens.OVERRIDE_KEYWORD)
}
} as KtCallableDeclaration
}