KT-11920: Increase visibility fix is now active for exposed visibility errors

This commit is contained in:
Mikhail Glukhikh
2016-04-15 18:54:26 +03:00
committed by Mikhail Glukhikh
parent 04db491dd4
commit fe44671b6a
26 changed files with 335 additions and 34 deletions
@@ -41,17 +41,23 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals
object Private : EffectiveVisibility("private", privateApi = true) {
override fun relation(other: EffectiveVisibility) =
if (this == other || Local == other) Permissiveness.SAME else Permissiveness.LESS
override fun toVisibility() = Visibilities.PRIVATE
}
// Effectively same as Private
object Local : EffectiveVisibility("local") {
override fun relation(other: EffectiveVisibility) =
if (this == other || Private == other) Permissiveness.SAME else Permissiveness.LESS
override fun toVisibility() = Visibilities.LOCAL
}
object Public : EffectiveVisibility("public", publicApi = true) {
override fun relation(other: EffectiveVisibility) =
if (this == other) Permissiveness.SAME else Permissiveness.MORE
override fun toVisibility() = Visibilities.PUBLIC
}
abstract class InternalOrPackage protected constructor(internal: Boolean) : EffectiveVisibility(
@@ -72,9 +78,13 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals
}
}
object Internal : InternalOrPackage(true)
object Internal : InternalOrPackage(true) {
override fun toVisibility() = Visibilities.INTERNAL
}
object PackagePrivate : InternalOrPackage(false)
object PackagePrivate : InternalOrPackage(false) {
override fun toVisibility() = Visibilities.PRIVATE
}
class Protected(val container: ClassDescriptor?) : EffectiveVisibility("protected", publicApi = true) {
@@ -110,6 +120,8 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals
}
is InternalOrPackage -> InternalProtected(container)
}
override fun toVisibility() = Visibilities.PROTECTED
}
// Lower bound for all protected visibilities
@@ -126,6 +138,8 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals
Private, Local, ProtectedBound, InternalProtectedBound -> other
is InternalOrPackage, is InternalProtected -> InternalProtectedBound
}
override fun toVisibility() = Visibilities.PROTECTED
}
// Lower bound for internal and protected(C)
@@ -159,6 +173,8 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals
}
ProtectedBound -> InternalProtectedBound
}
override fun toVisibility() = Visibilities.PRIVATE
}
// Lower bound for internal and protected lower bound
@@ -168,16 +184,20 @@ sealed class EffectiveVisibility(val name: String, val publicApi: Boolean = fals
Private, Local -> Permissiveness.MORE
InternalProtectedBound -> Permissiveness.SAME
}
override fun toVisibility() = Visibilities.PRIVATE
}
internal enum class Permissiveness {
enum class Permissiveness {
LESS,
SAME,
MORE,
UNKNOWN
}
abstract internal fun relation(other: EffectiveVisibility): Permissiveness
abstract fun relation(other: EffectiveVisibility): Permissiveness
abstract fun toVisibility(): Visibility
open internal fun lowerBound(other: EffectiveVisibility) = when (relation(other)) {
Permissiveness.SAME, Permissiveness.LESS -> this
@@ -193,6 +193,20 @@ fun KtDeclaration.implicitVisibility(): KtModifierKeywordToken? {
return defaultVisibilityKeyword
}
fun KtModifierListOwner.canBePrivate(): Boolean {
if (modifierList?.hasModifier(KtTokens.ABSTRACT_KEYWORD) ?: false) return false
return true
}
fun KtModifierListOwner.canBeProtected(): Boolean {
val parent = this.parent
return when (parent) {
is KtClassBody -> parent.parent is KtClass
is KtParameterList -> parent.parent is KtPrimaryConstructor
else -> false
}
}
fun KtDeclaration.implicitModality(): KtModifierKeywordToken {
if (this is KtClassOrObject) return KtTokens.FINAL_KEYWORD
val klass = containingClassOrObject ?: return KtTokens.FINAL_KEYWORD
@@ -22,6 +22,8 @@ import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
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.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
@@ -38,7 +40,7 @@ open class ChangeVisibilityModifierIntention protected constructor(
val modifierList = element.modifierList
if (modifierList?.hasModifier(modifier) ?: false) return null
var descriptor = element.toDescriptor() as? DeclarationDescriptorWithVisibility ?: return null
val descriptor = element.toDescriptor() as? DeclarationDescriptorWithVisibility ?: return null
val targetVisibility = modifier.toVisibility()
if (descriptor.visibility == targetVisibility) return null
@@ -115,27 +117,13 @@ open class ChangeVisibilityModifierIntention protected constructor(
class Private : ChangeVisibilityModifierIntention(KtTokens.PRIVATE_KEYWORD), HighPriorityAction {
override fun applicabilityRange(element: KtDeclaration): TextRange? {
return if (canBePrivate(element)) super.applicabilityRange(element) else null
}
private fun canBePrivate(declaration: KtDeclaration): Boolean {
if (declaration.modifierList?.hasModifier(KtTokens.ABSTRACT_KEYWORD) ?: false) return false
return true
return if (element.canBePrivate()) super.applicabilityRange(element) else null
}
}
class Protected : ChangeVisibilityModifierIntention(KtTokens.PROTECTED_KEYWORD) {
override fun applicabilityRange(element: KtDeclaration): TextRange? {
return if (canBeProtected(element)) super.applicabilityRange(element) else null
}
private fun canBeProtected(declaration: KtDeclaration): Boolean {
val parent = declaration.parent
return when (parent) {
is KtClassBody -> parent.parent is KtClass
is KtParameterList -> parent.parent is KtPrimaryConstructor
else -> false
}
return if (element.canBeProtected()) super.applicabilityRange(element) else null
}
}
@@ -16,17 +16,19 @@
package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.intention.HighPriorityAction
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.descriptors.Visibilities
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.EffectiveVisibility.Permissiveness.LESS
import org.jetbrains.kotlin.descriptors.Visibilities.*
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory3
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.canBeProtected
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtModifierListOwner
@@ -37,8 +39,11 @@ import org.jetbrains.kotlin.resolve.ExposedVisibilityChecker
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.idea.core.setVisibility
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
class IncreaseVisibilityFix(
open class IncreaseVisibilityFix(
element: KtModifierListOwner,
private val elementName: String,
private val visibilityModifier: KtModifierKeywordToken
@@ -51,7 +56,38 @@ class IncreaseVisibilityFix(
element.setVisibility(visibilityModifier)
}
class IncreaseToPublicFix(element: KtModifierListOwner, elementName: String) :
IncreaseVisibilityFix(element, elementName, PUBLIC_KEYWORD), HighPriorityAction
class IncreaseToProtectedFix(element: KtModifierListOwner, elementName: String) :
IncreaseVisibilityFix(element, elementName, PROTECTED_KEYWORD) {
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile) =
super.isAvailable(project, editor, file) && element.canBeProtected()
}
class IncreaseToInternalFix(element: KtModifierListOwner, elementName: String) :
IncreaseVisibilityFix(element, elementName, INTERNAL_KEYWORD)
companion object : KotlinSingleIntentionActionFactory() {
private fun create(
declaration: KtModifierListOwner,
descriptor: DeclarationDescriptorWithVisibility,
targetVisibility: Visibility
) : IntentionAction? {
if (!ExposedVisibilityChecker().checkDeclarationWithVisibility(declaration, descriptor, targetVisibility)) return null
val name = descriptor.name.asString()
return when (targetVisibility) {
INTERNAL -> IncreaseToInternalFix(declaration, name)
PROTECTED -> IncreaseToProtectedFix(declaration, name)
PUBLIC -> IncreaseToPublicFix(declaration, name)
else -> null
}
}
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val element = diagnostic.psiElement as? KtElement ?: return null
val context = element.analyze(BodyResolveMode.PARTIAL)
@@ -63,16 +99,31 @@ class IncreaseVisibilityFix(
val declaration = DescriptorToSourceUtils.getSourceFromDescriptor(descriptor) as? KtModifierListOwner ?: return null
val module = DescriptorUtils.getContainingModule(descriptor)
val (modifier, visibility) = if (module != usageModule || descriptor.visibility != Visibilities.PRIVATE) {
Pair(KtTokens.PUBLIC_KEYWORD, Visibilities.PUBLIC)
}
else {
Pair(KtTokens.INTERNAL_KEYWORD, Visibilities.INTERNAL)
}
val targetVisibility = if (module != usageModule || descriptor.visibility != PRIVATE) PUBLIC else INTERNAL
if (!ExposedVisibilityChecker().checkDeclarationWithVisibility(declaration, descriptor, visibility)) return null
return create(declaration, descriptor, targetVisibility)
}
}
return IncreaseVisibilityFix(declaration, descriptor.name.asString(), modifier)
object Exposed : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
@Suppress("UNCHECKED_CAST")
val factory = diagnostic.factory as DiagnosticFactory3<*, EffectiveVisibility, DescriptorWithRelation, EffectiveVisibility>
val exposedDiagnostic = factory.cast(diagnostic)
val exposedDescriptor = exposedDiagnostic.b.descriptor as? DeclarationDescriptorWithVisibility ?: return null
val exposedDeclaration = DescriptorToSourceUtils.getSourceFromDescriptor(exposedDescriptor) as? KtModifierListOwner ?: return null
val exposedVisibility = exposedDiagnostic.c
val exposingVisibility = exposedDiagnostic.a
val boundVisibility = when (exposedVisibility.relation(exposingVisibility)) {
LESS -> exposingVisibility.toVisibility()
else -> PUBLIC
}
val exposingDeclaration = diagnostic.psiElement.getParentOfType<KtDeclaration>(true)
val targetVisibility = when (boundVisibility) {
PROTECTED -> if (exposedDeclaration.parent == exposingDeclaration?.parent) PROTECTED else PUBLIC
else -> boundVisibility
}
return create(exposedDeclaration, exposedDescriptor, targetVisibility)
}
}
}
@@ -153,6 +153,14 @@ class QuickFixRegistrar : QuickFixContributor {
INVISIBLE_MEMBER.registerFactory(IncreaseVisibilityFix)
INVISIBLE_SETTER.registerFactory(IncreaseVisibilityFix)
EXPOSED_FUNCTION_RETURN_TYPE.registerFactory(IncreaseVisibilityFix.Exposed)
EXPOSED_PARAMETER_TYPE.registerFactory(IncreaseVisibilityFix.Exposed)
EXPOSED_PROPERTY_TYPE.registerFactory(IncreaseVisibilityFix.Exposed)
EXPOSED_RECEIVER_TYPE.registerFactory(IncreaseVisibilityFix.Exposed)
EXPOSED_SUPER_CLASS.registerFactory(IncreaseVisibilityFix.Exposed)
EXPOSED_SUPER_INTERFACE.registerFactory(IncreaseVisibilityFix.Exposed)
EXPOSED_TYPE_PARAMETER_BOUND.registerFactory(IncreaseVisibilityFix.Exposed)
REDUNDANT_NULLABLE.registerFactory(RemoveNullableFix.Factory(RemoveNullableFix.NullableKind.REDUNDANT))
NULLABLE_SUPERTYPE.registerFactory(RemoveNullableFix.Factory(RemoveNullableFix.NullableKind.SUPERTYPE))
USELESS_NULLABLE_CHECK.registerFactory(RemoveNullableFix.Factory(RemoveNullableFix.NullableKind.USELESS))
@@ -0,0 +1,9 @@
// "Make Nested internal" "true"
class Outer {
private class Nested
}
class Generic<T>
internal fun foo(<caret>arg: Generic<Outer.Nested>) {}
@@ -0,0 +1,9 @@
// "Make Nested internal" "true"
class Outer {
internal class Nested
}
class Generic<T>
internal fun foo(arg: Generic<Outer.Nested>) {}
@@ -0,0 +1,7 @@
// "Make Data public" "true"
private data class Data(val x: Int)
class First {
val <caret>foo = Data(13)
}
@@ -0,0 +1,7 @@
// "Make Data public" "true"
data class Data(val x: Int)
class First {
val foo = Data(13)
}
@@ -0,0 +1,9 @@
// "Make Private protected" "true"
class Receiver<T>
abstract class My {
private class Private
abstract protected fun <caret>Receiver<Private>.foo()
}
@@ -0,0 +1,9 @@
// "Make Private protected" "true"
class Receiver<T>
abstract class My {
protected class Private
abstract protected fun Receiver<Private>.foo()
}
@@ -0,0 +1,7 @@
// "Make Data internal" "true"
private data class Data(val x: Int)
class First {
internal fun <caret>bar(x: Int) = Data(x)
}
@@ -0,0 +1,7 @@
// "Make Data internal" "true"
internal data class Data(val x: Int)
class First {
internal fun bar(x: Int) = Data(x)
}
@@ -0,0 +1,7 @@
// "Make Data protected" "true"
class Outer {
private open class Data(val x: Int)
protected class First : <caret>Data(42)
}
@@ -0,0 +1,7 @@
// "Make Data protected" "true"
class Outer {
protected open class Data(val x: Int)
protected class First : Data(42)
}
@@ -0,0 +1,7 @@
// "Make Data public" "true"
private open class Data(val x: Int)
class Outer {
protected class First : <caret>Data(42)
}
@@ -0,0 +1,7 @@
// "Make Data public" "true"
open class Data(val x: Int)
class Outer {
protected class First : Data(42)
}
@@ -0,0 +1,9 @@
// "Make Data public" "true"
class Other {
internal open class Data(val x: Int)
}
class Another {
protected class First : Other.<caret>Data(42)
}
@@ -0,0 +1,9 @@
// "Make Data public" "true"
class Other {
open class Data(val x: Int)
}
class Another {
protected class First : Other.<caret>Data(42)
}
@@ -0,0 +1,9 @@
// "Make Data public" "true"
class Other {
private open class Data(val x: Int)
}
class Another {
protected class First : Other.<caret>Data(42)
}
@@ -0,0 +1,9 @@
// "Make Data public" "true"
class Other {
open class Data(val x: Int)
}
class Another {
protected class First : Other.Data(42)
}
@@ -0,0 +1,11 @@
// "Make Outer public" "true"
import Outer.Base
internal class Outer {
interface Base
}
class Container {
interface Derived : <caret>Base
}
@@ -0,0 +1,11 @@
// "Make Outer public" "true"
import Outer.Base
class Outer {
interface Base
}
class Container {
interface Derived : Base
}
@@ -0,0 +1,5 @@
// "Make InternalString public" "true"
internal open class InternalString
class User<T : <caret>User<T, InternalString>, R>
@@ -0,0 +1,5 @@
// "Make InternalString public" "true"
open class InternalString
class User<T : User<T, InternalString>, R>
@@ -4731,6 +4731,66 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/increaseVisibility"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("exposedParameterType.kt")
public void testExposedParameterType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/exposedParameterType.kt");
doTest(fileName);
}
@TestMetadata("exposedPropertyType.kt")
public void testExposedPropertyType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/exposedPropertyType.kt");
doTest(fileName);
}
@TestMetadata("exposedReceiverType.kt")
public void testExposedReceiverType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/exposedReceiverType.kt");
doTest(fileName);
}
@TestMetadata("exposedReturnType.kt")
public void testExposedReturnType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/exposedReturnType.kt");
doTest(fileName);
}
@TestMetadata("exposedSuperClass.kt")
public void testExposedSuperClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/exposedSuperClass.kt");
doTest(fileName);
}
@TestMetadata("exposedSuperClassProtectedBase.kt")
public void testExposedSuperClassProtectedBase() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/exposedSuperClassProtectedBase.kt");
doTest(fileName);
}
@TestMetadata("exposedSuperClassProtectedInAnother.kt")
public void testExposedSuperClassProtectedInAnother() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/exposedSuperClassProtectedInAnother.kt");
doTest(fileName);
}
@TestMetadata("exposedSuperClassProtectedInAnotherFromPrivate.kt")
public void testExposedSuperClassProtectedInAnotherFromPrivate() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/exposedSuperClassProtectedInAnotherFromPrivate.kt");
doTest(fileName);
}
@TestMetadata("exposedSuperInterface.kt")
public void testExposedSuperInterface() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/exposedSuperInterface.kt");
doTest(fileName);
}
@TestMetadata("exposedTypeParameterBound.kt")
public void testExposedTypeParameterBound() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/exposedTypeParameterBound.kt");
doTest(fileName);
}
@TestMetadata("overriddenProtectedMemberToPublicSingleFile.kt")
public void testOverriddenProtectedMemberToPublicSingleFile() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/overriddenProtectedMemberToPublicSingleFile.kt");