Add intention for changing setter accessibility #KT-22409 Fixed

This commit is contained in:
Toshiaki Kameyama
2018-01-24 12:12:27 +03:00
committed by Mikhail Glukhikh
parent 7f97d58970
commit 28123eaf7b
24 changed files with 212 additions and 8 deletions
@@ -232,7 +232,10 @@ fun KtDeclaration.implicitVisibility(): KtModifierKeywordToken? =
fun KtModifierListOwner.canBePrivate() = modifierList?.hasModifier(KtTokens.ABSTRACT_KEYWORD) != true
fun KtModifierListOwner.canBeProtected(): Boolean {
val parent = this.parent
val parent = when (this) {
is KtPropertyAccessor -> this.property.parent
else -> this.parent
}
return when (parent) {
is KtClassBody -> parent.parent is KtClass
is KtParameterList -> parent.parent is KtPrimaryConstructor
@@ -32,30 +32,45 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
import java.lang.IllegalArgumentException
open class ChangeVisibilityModifierIntention protected constructor(
val modifier: KtModifierKeywordToken
val modifier: KtModifierKeywordToken
) : SelfTargetingRangeIntention<KtDeclaration>(KtDeclaration::class.java, "Make ${modifier.value}") {
override fun applicabilityRange(element: KtDeclaration): TextRange? {
val modifierList = element.modifierList
if (modifierList?.hasModifier(modifier) ?: false) return null
if (modifierList?.hasModifier(modifier) == true) return null
val descriptor = element.toDescriptor() as? DeclarationDescriptorWithVisibility ?: return null
val targetVisibility = modifier.toVisibility()
if (descriptor.visibility == targetVisibility) return null
if (modifierList?.hasModifier(KtTokens.OVERRIDE_KEYWORD) ?: false) {
if (KtPsiUtil.isLocal((element as? KtPropertyAccessor)?.property ?: element)) return null
if (modifierList?.hasModifier(KtTokens.OVERRIDE_KEYWORD) == true) {
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
if (element is KtPropertyAccessor) {
if (element.isGetter) return null
if (targetVisibility == Visibilities.PUBLIC) {
val explicitVisibility = element.modifierList?.visibilityModifierType()?.value ?: return null
text = "Remove '$explicitVisibility' modifier"
} else {
val propVisibility = (element.property.toDescriptor() as? DeclarationDescriptorWithVisibility)?.visibility ?: return null
if (propVisibility == targetVisibility) return null
val compare = Visibilities.compare(targetVisibility, propVisibility)
if (compare == null || compare > 0) return null
}
}
val defaultRange = noModifierYetApplicabilityRange(element) ?: return null
if (element is KtPrimaryConstructor && defaultRange.isEmpty && element.visibilityModifier() == null) {
@@ -70,6 +85,7 @@ 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())
}
private fun KtModifierKeywordToken.toVisibility(): Visibility {
@@ -83,13 +99,16 @@ open class ChangeVisibilityModifierIntention protected constructor(
}
private fun noModifierYetApplicabilityRange(declaration: KtDeclaration): TextRange? {
if (KtPsiUtil.isLocal(declaration)) return null
return when (declaration) {
is KtNamedFunction -> declaration.funKeyword?.textRange
is KtProperty -> declaration.valOrVarKeyword.textRange
is KtPropertyAccessor -> declaration.namePlaceholder.textRange
is KtClass -> declaration.getClassOrInterfaceKeyword()?.textRange
is KtObjectDeclaration -> declaration.getObjectKeyword()?.textRange
is KtPrimaryConstructor -> declaration.valueParameterList?.let { TextRange.from(it.startOffset, 0) } //TODO: use constructor keyword if exist
is KtPrimaryConstructor -> declaration.valueParameterList?.let {
//TODO: use constructor keyword if exist
TextRange.from(it.startOffset, 0)
}
is KtSecondaryConstructor -> declaration.getConstructorKeyword().textRange
is KtParameter -> declaration.valOrVarKeyword?.textRange
is KtTypeAlias -> declaration.getTypeAliasKeyword()?.textRange
@@ -97,7 +116,8 @@ open class ChangeVisibilityModifierIntention protected constructor(
}
}
protected fun isAnnotationClassPrimaryConstructor(element: KtDeclaration) = element is KtPrimaryConstructor && (element.parent as? KtClass)?.hasModifier(KtTokens.ANNOTATION_KEYWORD) ?: false
protected fun isAnnotationClassPrimaryConstructor(element: KtDeclaration) =
element is KtPrimaryConstructor && (element.parent as? KtClass)?.hasModifier(KtTokens.ANNOTATION_KEYWORD) ?: false
class Public : ChangeVisibilityModifierIntention(KtTokens.PUBLIC_KEYWORD), HighPriorityAction
@@ -0,0 +1,4 @@
class Test {
var foo: String = ""
<caret>set
}
@@ -0,0 +1,4 @@
class Test {
var foo: String = ""
<caret>internal set
}
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
class Test {
private var foo: String = ""
<caret>set
}
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
class Test {
var foo: String = ""
<caret>get() = ""
}
@@ -0,0 +1,4 @@
class Test {
var foo: String = ""
<caret>set
}
@@ -0,0 +1,4 @@
class Test {
var foo: String = ""
<caret>private set
}
@@ -0,0 +1,4 @@
class Test {
internal var foo: String = ""
<caret>set
}
@@ -0,0 +1,4 @@
class Test {
internal var foo: String = ""
<caret>private set
}
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
class Test {
private var foo: String = ""
<caret>set
}
@@ -0,0 +1,4 @@
class Test {
protected var foo: String = ""
<caret>set
}
@@ -0,0 +1,4 @@
class Test {
protected var foo: String = ""
<caret>private set
}
@@ -0,0 +1,7 @@
// IS_APPLICABLE: false
fun test() {
class Test {
var foo: String = ""
<caret>set
}
}
@@ -0,0 +1,4 @@
class Test {
var foo: String = ""
<caret>set
}
@@ -0,0 +1,4 @@
class Test {
var foo: String = ""
<caret>protected set
}
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
class Test {
private var foo: String = ""
<caret>set
}
@@ -0,0 +1,5 @@
// INTENTION_TEXT: Remove 'private' modifier
class Test {
var foo: String = ""
<caret>private set
}
@@ -0,0 +1,5 @@
// INTENTION_TEXT: Remove 'private' modifier
class Test {
var foo: String = ""
<caret>set
}
@@ -0,0 +1,5 @@
// INTENTION_TEXT: Remove 'private' modifier
class Test {
private var foo: String = ""
<caret>private set
}
@@ -0,0 +1,5 @@
// INTENTION_TEXT: Remove 'private' modifier
class Test {
private var foo: String = ""
<caret>set
}
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
class Test {
var foo: String = ""
<caret>set
}
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
class Test {
private var foo: String = ""
<caret>set
}
@@ -2813,6 +2813,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("propertySetter.kt")
public void testPropertySetter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/internal/propertySetter.kt");
doTest(fileName);
}
@TestMetadata("propertySetterForPrivateProperty.kt")
public void testPropertySetterForPrivateProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/internal/propertySetterForPrivateProperty.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/internal/simple.kt");
@@ -2966,6 +2978,42 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("propertyGetter.kt")
public void testPropertyGetter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/private/propertyGetter.kt");
doTest(fileName);
}
@TestMetadata("propertySetter.kt")
public void testPropertySetter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/private/propertySetter.kt");
doTest(fileName);
}
@TestMetadata("propertySetterForInternalProperty.kt")
public void testPropertySetterForInternalProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/private/propertySetterForInternalProperty.kt");
doTest(fileName);
}
@TestMetadata("propertySetterForPrivateProperty.kt")
public void testPropertySetterForPrivateProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/private/propertySetterForPrivateProperty.kt");
doTest(fileName);
}
@TestMetadata("propertySetterForProtectedProperty.kt")
public void testPropertySetterForProtectedProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/private/propertySetterForProtectedProperty.kt");
doTest(fileName);
}
@TestMetadata("propertySetterInLocalClass.kt")
public void testPropertySetterInLocalClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/private/propertySetterInLocalClass.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/private/simple.kt");
@@ -3029,6 +3077,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("propertySetter.kt")
public void testPropertySetter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/protected/propertySetter.kt");
doTest(fileName);
}
@TestMetadata("propertySetterForPrivateProperty.kt")
public void testPropertySetterForPrivateProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/protected/propertySetterForPrivateProperty.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/protected/simple.kt");
@@ -3068,6 +3128,30 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("propertyPrivateSetter.kt")
public void testPropertyPrivateSetter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/public/propertyPrivateSetter.kt");
doTest(fileName);
}
@TestMetadata("propertyPrivateSetterForPrivateProperty.kt")
public void testPropertyPrivateSetterForPrivateProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/public/propertyPrivateSetterForPrivateProperty.kt");
doTest(fileName);
}
@TestMetadata("propertySetter.kt")
public void testPropertySetter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/public/propertySetter.kt");
doTest(fileName);
}
@TestMetadata("propertySetterForPrivateProperty.kt")
public void testPropertySetterForPrivateProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/public/propertySetterForPrivateProperty.kt");
doTest(fileName);
}
@TestMetadata("publicByDefault.kt")
public void testPublicByDefault() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/changeVisibility/public/publicByDefault.kt");