Increase visibility: make not available in case of potential exposed visibility problems (see KT-11920)
This commit is contained in:
@@ -17,13 +17,15 @@
|
|||||||
package org.jetbrains.kotlin.resolve
|
package org.jetbrains.kotlin.resolve
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink.DO_NOTHING
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
|
||||||
// Checker for all seven EXPOSED_* errors
|
// Checker for all seven EXPOSED_* errors
|
||||||
// All functions return true if everything is OK, or false in case of any errors
|
// All functions return true if everything is OK, or false in case of any errors
|
||||||
class ExposedVisibilityChecker(private val trace: BindingTrace) {
|
class ExposedVisibilityChecker(private val trace: DiagnosticSink = DO_NOTHING) {
|
||||||
|
|
||||||
// NB: does not check any members
|
// NB: does not check any members
|
||||||
fun checkClassHeader(klass: KtClassOrObject, classDescriptor: ClassDescriptor): Boolean {
|
fun checkClassHeader(klass: KtClassOrObject, classDescriptor: ClassDescriptor): Boolean {
|
||||||
@@ -35,8 +37,27 @@ class ExposedVisibilityChecker(private val trace: BindingTrace) {
|
|||||||
return result and checkFunction(constructor, constructorDescriptor)
|
return result and checkFunction(constructor, constructorDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkFunction(function: KtFunction, functionDescriptor: FunctionDescriptor): Boolean {
|
fun checkDeclarationWithVisibility(modifierListOwner: KtModifierListOwner,
|
||||||
val functionVisibility = functionDescriptor.effectiveVisibility()
|
descriptor: DeclarationDescriptorWithVisibility,
|
||||||
|
visibility: Visibility
|
||||||
|
) : Boolean {
|
||||||
|
return when {
|
||||||
|
modifierListOwner is KtFunction &&
|
||||||
|
descriptor is FunctionDescriptor -> checkFunction(modifierListOwner, descriptor, visibility)
|
||||||
|
|
||||||
|
modifierListOwner is KtProperty &&
|
||||||
|
descriptor is PropertyDescriptor -> checkProperty(modifierListOwner, descriptor, visibility)
|
||||||
|
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkFunction(function: KtFunction,
|
||||||
|
functionDescriptor: FunctionDescriptor,
|
||||||
|
// for checking situation with modified basic visibility
|
||||||
|
visibility: Visibility = functionDescriptor.visibility
|
||||||
|
): Boolean {
|
||||||
|
val functionVisibility = functionDescriptor.effectiveVisibility(visibility)
|
||||||
var result = true
|
var result = true
|
||||||
if (function !is KtConstructor<*>) {
|
if (function !is KtConstructor<*>) {
|
||||||
val restricting = functionDescriptor.returnType?.leastPermissiveDescriptor(functionVisibility)
|
val restricting = functionDescriptor.returnType?.leastPermissiveDescriptor(functionVisibility)
|
||||||
@@ -57,8 +78,12 @@ class ExposedVisibilityChecker(private val trace: BindingTrace) {
|
|||||||
return result and checkMemberReceiver(function.receiverTypeReference, functionDescriptor)
|
return result and checkMemberReceiver(function.receiverTypeReference, functionDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkProperty(property: KtProperty, propertyDescriptor: PropertyDescriptor): Boolean {
|
fun checkProperty(property: KtProperty,
|
||||||
val propertyVisibility = propertyDescriptor.effectiveVisibility()
|
propertyDescriptor: PropertyDescriptor,
|
||||||
|
// for checking situation with modified basic visibility
|
||||||
|
visibility: Visibility = propertyDescriptor.visibility
|
||||||
|
): Boolean {
|
||||||
|
val propertyVisibility = propertyDescriptor.effectiveVisibility(visibility)
|
||||||
val restricting = propertyDescriptor.type.leastPermissiveDescriptor(propertyVisibility)
|
val restricting = propertyDescriptor.type.leastPermissiveDescriptor(propertyVisibility)
|
||||||
var result = true
|
var result = true
|
||||||
if (restricting != null) {
|
if (restricting != null) {
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ private fun Set<DescriptorWithRelation>.leastPermissive(base: EffectiveVisibilit
|
|||||||
|
|
||||||
fun KotlinType.leastPermissiveDescriptor(base: EffectiveVisibility) = dependentDescriptors().leastPermissive(base)
|
fun KotlinType.leastPermissiveDescriptor(base: EffectiveVisibility) = dependentDescriptors().leastPermissive(base)
|
||||||
|
|
||||||
fun DeclarationDescriptorWithVisibility.effectiveVisibility(): EffectiveVisibility =
|
fun DeclarationDescriptorWithVisibility.effectiveVisibility(visibility: Visibility = this.visibility): EffectiveVisibility =
|
||||||
lowerBound(visibility.effectiveVisibility(this.containingDeclaration as? ClassDescriptor),
|
lowerBound(visibility.effectiveVisibility(this.containingDeclaration as? ClassDescriptor),
|
||||||
(this.containingDeclaration as? ClassDescriptor)?.effectiveVisibility() ?: Public)
|
(this.containingDeclaration as? ClassDescriptor)?.effectiveVisibility() ?: Public)
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.psi.KtModifierListOwner
|
|||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.ExposedVisibilityChecker
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.idea.core.setVisibility
|
import org.jetbrains.kotlin.idea.core.setVisibility
|
||||||
@@ -62,12 +63,15 @@ class IncreaseVisibilityFix(
|
|||||||
val declaration = DescriptorToSourceUtils.getSourceFromDescriptor(descriptor) as? KtModifierListOwner ?: return null
|
val declaration = DescriptorToSourceUtils.getSourceFromDescriptor(descriptor) as? KtModifierListOwner ?: return null
|
||||||
|
|
||||||
val module = DescriptorUtils.getContainingModule(descriptor)
|
val module = DescriptorUtils.getContainingModule(descriptor)
|
||||||
val modifier = if (module != usageModule || descriptor.visibility != Visibilities.PRIVATE) {
|
val (modifier, visibility) = if (module != usageModule || descriptor.visibility != Visibilities.PRIVATE) {
|
||||||
KtTokens.PUBLIC_KEYWORD
|
Pair(KtTokens.PUBLIC_KEYWORD, Visibilities.PUBLIC)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
KtTokens.INTERNAL_KEYWORD
|
Pair(KtTokens.INTERNAL_KEYWORD, Visibilities.INTERNAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ExposedVisibilityChecker().checkDeclarationWithVisibility(declaration, descriptor, visibility)) return null
|
||||||
|
|
||||||
return IncreaseVisibilityFix(declaration, descriptor.name.asString(), modifier)
|
return IncreaseVisibilityFix(declaration, descriptor.name.asString(), modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// "Make bar internal" "false"
|
||||||
|
// ACTION: Convert property initializer to getter
|
||||||
|
// ERROR: Cannot access 'bar': it is 'private' in 'First'
|
||||||
|
|
||||||
|
private data class Data(val x: Int)
|
||||||
|
|
||||||
|
class First {
|
||||||
|
// Making it internal exposes 'Data'
|
||||||
|
private fun bar(x: Int) = Data(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Second(f: First) {
|
||||||
|
private val y = f.<caret>bar(42)
|
||||||
|
}
|
||||||
@@ -4731,6 +4731,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("privateMemberToInternalWithExposed.kt")
|
||||||
|
public void testPrivateMemberToInternalWithExposed() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/privateMemberToInternalWithExposed.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("protectedMemberToPublicSingleFile.kt")
|
@TestMetadata("protectedMemberToPublicSingleFile.kt")
|
||||||
public void testProtectedMemberToPublicSingleFile() throws Exception {
|
public void testProtectedMemberToPublicSingleFile() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/protectedMemberToPublicSingleFile.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/increaseVisibility/protectedMemberToPublicSingleFile.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user