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()) {
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.Visibilities
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.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi.psiUtil.toVisibility
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
import org.jetbrains.kotlin.psi.psiUtil.*
open class ChangeVisibilityModifierIntention protected constructor(
val modifier: KtModifierKeywordToken
) : SelfTargetingRangeIntention<KtDeclaration>(KtDeclaration::class.java, "Make ${modifier.value}") {
override fun startInWriteAction(): Boolean = false
override fun applicabilityRange(element: KtDeclaration): TextRange? {
val modifierList = element.modifierList
@@ -46,11 +46,12 @@ open class ChangeVisibilityModifierIntention protected constructor(
if (descriptor.visibility == targetVisibility) return null
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
if (callableDescriptor.overriddenDescriptors
.map { Visibilities.compare(it.visibility, targetVisibility) }
.any { it == null || it > 0 }) return null
.any { it == null || it > 0 }
) return null
}
text = defaultText
@@ -80,8 +81,21 @@ open class ChangeVisibilityModifierIntention protected constructor(
}
override fun applyTo(element: KtDeclaration, editor: Editor?) {
element.setVisibility(modifier)
if (element is KtPropertyAccessor) element.modifierList?.nextSibling?.replace(KtPsiFactory(element).createWhiteSpace())
val pointers = element.collectAllExpectAndActualDeclaration().map { it.createSmartPointer() }
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? {
@@ -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")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)