ChangeVisibilityModifierIntention: should affect all actual/expect declarations

#KT-29737 Fixed
This commit is contained in:
Dmitry Gridin
2019-10-22 21:01:59 +07:00
parent 13e98e712e
commit 9c61f42121
15 changed files with 71 additions and 8 deletions
@@ -145,4 +145,10 @@ fun KtDeclaration.runOnExpectAndAllActuals(checkExpect: Boolean = true, f: (KtDe
} else if (!checkExpect || isExpectDeclaration()) { } else if (!checkExpect || isExpectDeclaration()) {
actualsForExpected().forEach { f(it) } actualsForExpected().forEach { f(it) }
} }
}
fun KtDeclaration.collectAllExpectAndActualDeclaration(): Set<KtDeclaration> = when {
isExpectDeclaration() -> actualsForExpected() + this
hasActualModifier() -> liftToExpected()?.let { it.actualsForExpected() + it }.orEmpty()
else -> emptySet()
} }
@@ -23,17 +23,17 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.collectAllExpectAndActualDeclaration
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.psiUtil.toVisibility
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
open class ChangeVisibilityModifierIntention protected constructor( open class ChangeVisibilityModifierIntention protected constructor(
val modifier: KtModifierKeywordToken val modifier: KtModifierKeywordToken
) : SelfTargetingRangeIntention<KtDeclaration>(KtDeclaration::class.java, "Make ${modifier.value}") { ) : SelfTargetingRangeIntention<KtDeclaration>(KtDeclaration::class.java, "Make ${modifier.value}") {
override fun startInWriteAction(): Boolean = false
override fun applicabilityRange(element: KtDeclaration): TextRange? { override fun applicabilityRange(element: KtDeclaration): TextRange? {
val modifierList = element.modifierList val modifierList = element.modifierList
@@ -46,11 +46,12 @@ open class ChangeVisibilityModifierIntention protected constructor(
if (descriptor.visibility == targetVisibility) return null if (descriptor.visibility == targetVisibility) return null
if (modifierList?.hasModifier(KtTokens.OVERRIDE_KEYWORD) == true) { if (modifierList?.hasModifier(KtTokens.OVERRIDE_KEYWORD) == true) {
val callableDescriptor = descriptor as? CallableDescriptor ?: return null val callableDescriptor = descriptor as? CallableDescriptor ?: return null
// cannot make visibility less than (or non-comparable with) any of the supers // cannot make visibility less than (or non-comparable with) any of the supers
if (callableDescriptor.overriddenDescriptors if (callableDescriptor.overriddenDescriptors
.map { Visibilities.compare(it.visibility, targetVisibility) } .map { Visibilities.compare(it.visibility, targetVisibility) }
.any { it == null || it > 0 }) return null .any { it == null || it > 0 }
) return null
} }
text = defaultText text = defaultText
@@ -80,8 +81,21 @@ open class ChangeVisibilityModifierIntention protected constructor(
} }
override fun applyTo(element: KtDeclaration, editor: Editor?) { override fun applyTo(element: KtDeclaration, editor: Editor?) {
element.setVisibility(modifier) val pointers = element.collectAllExpectAndActualDeclaration().map { it.createSmartPointer() }
if (element is KtPropertyAccessor) element.modifierList?.nextSibling?.replace(KtPsiFactory(element).createWhiteSpace())
val project = element.project
val factory = KtPsiFactory(project)
project.executeWriteCommand("Change visibility modifier") {
for (pointer in pointers) {
val declaration = pointer.element ?: continue
declaration.setVisibility(modifier)
if (declaration is KtPropertyAccessor) {
declaration.modifierList?.nextSibling?.replace(factory.createWhiteSpace())
}
}
}
} }
private fun noModifierYetApplicabilityRange(declaration: KtDeclaration): TextRange? { private fun noModifierYetApplicabilityRange(declaration: KtDeclaration): TextRange? {
@@ -0,0 +1,3 @@
// "Make internal" "true"
expect <caret>class Sample
@@ -0,0 +1,3 @@
// "Make internal" "true"
internal expect <caret>class Sample
@@ -0,0 +1 @@
actual class Sample
@@ -0,0 +1 @@
internal actual class Sample
@@ -0,0 +1 @@
actual class Sample
@@ -0,0 +1 @@
internal actual class Sample
@@ -0,0 +1 @@
internal expect class Sample
@@ -0,0 +1 @@
expect class Sample
@@ -0,0 +1,3 @@
// "Make public" "true"
internal actual c<caret>lass Sample
@@ -0,0 +1,3 @@
// "Make public" "true"
actual c<caret>lass Sample
@@ -0,0 +1 @@
internal actual class Sample
@@ -0,0 +1 @@
actual class Sample
@@ -209,6 +209,29 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul
} }
} }
@TestMetadata("idea/testData/multiModuleQuickFix/changeModifier")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ChangeModifier extends AbstractQuickFixMultiModuleTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInChangeModifier() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/multiModuleQuickFix/changeModifier"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true);
}
@TestMetadata("internal")
public void testInternal() throws Exception {
runTest("idea/testData/multiModuleQuickFix/changeModifier/internal/");
}
@TestMetadata("public")
public void testPublic() throws Exception {
runTest("idea/testData/multiModuleQuickFix/changeModifier/public/");
}
}
@TestMetadata("idea/testData/multiModuleQuickFix/changeSignature") @TestMetadata("idea/testData/multiModuleQuickFix/changeSignature")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)