Add modality/visibility/inline to all expect/actual declarations in QF

So #KT-25539 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-07-25 17:27:18 +03:00
parent 54b63ea43a
commit 14d6560e67
23 changed files with 119 additions and 44 deletions
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors
import org.jetbrains.kotlin.idea.caches.project.implementingDescriptors
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.setVisibility
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.psi.KtClassOrObject
@@ -126,3 +127,17 @@ fun KtDeclaration.isEffectivelyActual(): Boolean {
private fun MemberDescriptor.isEnumEntryInActual() =
(DescriptorUtils.isEnumEntry(this) && (containingDeclaration as? MemberDescriptor)?.isActual == true)
fun KtDeclaration.runOnExpectAndAllActuals(checkExpect: Boolean = true, f: (KtDeclaration) -> Unit) {
if (hasActualModifier()) {
val expectElement = liftToExpected()
expectElement?.actualsForExpected()?.forEach {
if (it !== this) {
f(it)
}
}
expectElement?.let { f(it) }
} else if (!checkExpect || isExpectDeclaration()) {
actualsForExpected().forEach { f(it) }
}
}
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix
import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.idea.util.runOnExpectAndAllActuals
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.lexer.KtTokens.*
@@ -55,11 +56,11 @@ open class AddModifierFix(
override fun getFamilyName() = "Add modifier"
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
private fun invokeOnElement(element: KtModifierListOwner?) {
element?.addModifier(modifier)
if (modifier == KtTokens.ABSTRACT_KEYWORD && (element is KtProperty || element is KtNamedFunction)) {
element?.containingClass()?.run {
element.containingClass()?.run {
if (!hasModifier(KtTokens.ABSTRACT_KEYWORD) && !hasModifier(KtTokens.SEALED_KEYWORD)) {
addModifier(KtTokens.ABSTRACT_KEYWORD)
}
@@ -67,12 +68,24 @@ open class AddModifierFix(
}
}
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val originalElement = element
if (originalElement is KtDeclaration && modifier.isMultiplatformPersistent()) {
originalElement.runOnExpectAndAllActuals { invokeOnElement(it) }
}
invokeOnElement(originalElement)
}
override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
val element = element ?: return false
return element.canRefactor()
}
companion object {
private fun KtModifierKeywordToken.isMultiplatformPersistent(): Boolean =
this in KtTokens.MODALITY_MODIFIERS || this == KtTokens.INLINE_KEYWORD
private val modalityModifiers = setOf(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD)
fun getElementName(modifierListOwner: KtModifierListOwner): String {
@@ -26,8 +26,10 @@ import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.idea.core.canBePrivate
import org.jetbrains.kotlin.idea.core.canBeProtected
import org.jetbrains.kotlin.idea.core.setVisibility
import org.jetbrains.kotlin.idea.util.runOnExpectAndAllActuals
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.resolve.ExposedVisibilityChecker
@@ -42,6 +44,11 @@ open class ChangeVisibilityFix(
override fun getFamilyName() = "Make $visibilityModifier"
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val originalElement = element
if (originalElement is KtDeclaration) {
originalElement.runOnExpectAndAllActuals { it.setVisibility(visibilityModifier) }
}
element?.setVisibility(visibilityModifier)
}
@@ -53,9 +53,9 @@ import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOpt
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages
import org.jetbrains.kotlin.idea.util.actualsForExpected
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
import org.jetbrains.kotlin.idea.util.liftToExpected
import org.jetbrains.kotlin.idea.util.runOnExpectAndAllActuals
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
@@ -400,19 +400,7 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
override fun prepareForDeletion(element: PsiElement) {
if (element is KtDeclaration) {
if (element.hasActualModifier()) {
val expectElement = element.liftToExpected()
expectElement?.actualsForExpected()?.forEach {
if (it !== element) {
it.removeOrClean()
}
}
expectElement?.removeOrClean()
} else {
element.actualsForExpected().forEach {
it.removeOrClean()
}
}
element.runOnExpectAndAllActuals(checkExpect = false) { it.removeOrClean() }
}
when (element) {
@@ -51,9 +51,9 @@ import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOpt
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages
import org.jetbrains.kotlin.idea.util.actualsForExpected
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
import org.jetbrains.kotlin.idea.util.liftToExpected
import org.jetbrains.kotlin.idea.util.runOnExpectAndAllActuals
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
@@ -392,19 +392,7 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
override fun prepareForDeletion(element: PsiElement) {
if (element is KtDeclaration) {
if (element.hasActualModifier()) {
val expectElement = element.liftToExpected()
expectElement?.actualsForExpected()?.forEach {
if (it !== element) {
it.removeOrClean()
}
}
expectElement?.removeOrClean()
} else {
element.actualsForExpected().forEach {
it.removeOrClean()
}
}
element.runOnExpectAndAllActuals(checkExpect = false) { it.removeOrClean() }
}
when (element) {
@@ -53,9 +53,9 @@ import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOpt
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages
import org.jetbrains.kotlin.idea.util.actualsForExpected
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
import org.jetbrains.kotlin.idea.util.liftToExpected
import org.jetbrains.kotlin.idea.util.runOnExpectAndAllActuals
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
@@ -402,19 +402,7 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
override fun prepareForDeletion(element: PsiElement) {
if (element is KtDeclaration) {
if (element.hasActualModifier()) {
val expectElement = element.liftToExpected()
expectElement?.actualsForExpected()?.forEach {
if (it !== element) {
it.removeOrClean()
}
}
expectElement?.removeOrClean()
} else {
element.actualsForExpected().forEach {
it.removeOrClean()
}
}
element.runOnExpectAndAllActuals(checkExpect = false) { it.removeOrClean() }
}
when (element) {
@@ -0,0 +1,4 @@
// "Add 'inline' modifier" "true"
// ERROR: Only type parameters of inline functions can be reified
expect fun <reif<caret>ied T> inlineFun(t: T)
@@ -0,0 +1,4 @@
// "Add 'inline' modifier" "true"
// ERROR: Only type parameters of inline functions can be reified
expect inline fun <reified T> inlineFun(t: T)
@@ -0,0 +1 @@
actual fun <reified T> inlineFun(t: T) {}
@@ -0,0 +1 @@
actual inline fun <reified T> inlineFun(t: T) {}
@@ -0,0 +1,6 @@
// "Make 'getInternal' internal" "true"
// ERROR: 'public' function exposes its 'internal' return type Internal
internal expect class Internal
expect fun <caret>getInternal(): Internal
@@ -0,0 +1,6 @@
// "Make 'getInternal' internal" "true"
// ERROR: 'public' function exposes its 'internal' return type Internal
internal expect class Internal
internal expect fun getInternal(): Internal
@@ -0,0 +1,5 @@
internal actual class Internal
actual fun getInternal(): Internal {
return Internal()
}
@@ -0,0 +1,5 @@
internal actual class Internal
internal actual fun getInternal(): Internal {
return Internal()
}
@@ -0,0 +1 @@
expect class One
@@ -0,0 +1 @@
expect open class One
@@ -0,0 +1,5 @@
// "Make 'One' open" "true"
// ERROR: This type is final, so it cannot be inherited from
actual class One
class Two : <caret>One()
@@ -0,0 +1,5 @@
// "Make 'One' open" "true"
// ERROR: This type is final, so it cannot be inherited from
actual open class One
class Two : <caret>One()
@@ -0,0 +1,5 @@
// "Make 'One' open" "true"
// ERROR: This type is final, so it cannot be inherited from
expect class One
expect class Two : <caret>One
@@ -0,0 +1,5 @@
// "Make 'One' open" "true"
// ERROR: This type is final, so it cannot be inherited from
expect open class One
expect class Two : <caret>One
@@ -0,0 +1 @@
actual class One
@@ -0,0 +1 @@
actual open class One
@@ -274,6 +274,26 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul
runTest("idea/testData/multiModuleQuickFix/interface/");
}
@TestMetadata("makeInlineFromExpect")
public void testMakeInlineFromExpect() throws Exception {
runTest("idea/testData/multiModuleQuickFix/makeInlineFromExpect/");
}
@TestMetadata("makeInternalFromExpect")
public void testMakeInternalFromExpect() throws Exception {
runTest("idea/testData/multiModuleQuickFix/makeInternalFromExpect/");
}
@TestMetadata("makeOpenFromActual")
public void testMakeOpenFromActual() throws Exception {
runTest("idea/testData/multiModuleQuickFix/makeOpenFromActual/");
}
@TestMetadata("makeOpenFromExpect")
public void testMakeOpenFromExpect() throws Exception {
runTest("idea/testData/multiModuleQuickFix/makeOpenFromExpect/");
}
@TestMetadata("memberFunParameterToReceiverByHeader")
public void testMemberFunParameterToReceiverByHeader() throws Exception {
runTest("idea/testData/multiModuleQuickFix/memberFunParameterToReceiverByHeader/");