ChangeVisibilityModifierIntention: should affect all actual/expect declarations
#KT-29737 Fixed
This commit is contained in:
@@ -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
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// "Make internal" "true"
|
||||
|
||||
internal expect <caret>class Sample
|
||||
@@ -0,0 +1 @@
|
||||
actual class Sample
|
||||
+1
@@ -0,0 +1 @@
|
||||
internal actual class Sample
|
||||
@@ -0,0 +1 @@
|
||||
actual class Sample
|
||||
+1
@@ -0,0 +1 @@
|
||||
internal actual class Sample
|
||||
@@ -0,0 +1 @@
|
||||
internal expect class Sample
|
||||
+1
@@ -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
|
||||
+23
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user