diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/MemberVisibilityCanBePrivateInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/MemberVisibilityCanBePrivateInspection.kt index 66ca09d51ad..22e88ac2b67 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/MemberVisibilityCanBePrivateInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/MemberVisibilityCanBePrivateInspection.kt @@ -28,23 +28,26 @@ import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.PsiSearchHelper import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.util.Processor +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility import org.jetbrains.kotlin.descriptors.EffectiveVisibility import org.jetbrains.kotlin.descriptors.effectiveVisibility import org.jetbrains.kotlin.idea.core.isInheritable import org.jetbrains.kotlin.idea.core.isOverridable import org.jetbrains.kotlin.idea.core.toDescriptor +import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor import org.jetbrains.kotlin.idea.quickfix.AddModifierFix import org.jetbrains.kotlin.idea.refactoring.isConstructorDeclaredProperty +import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.search.isCheapEnoughToSearchConsideringOperators import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction +import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope -import org.jetbrains.kotlin.idea.util.isEffectivelyActual import org.jetbrains.kotlin.idea.util.isExpectDeclaration import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* -import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmFieldAnnotation +import org.jetbrains.kotlin.resolve.DescriptorUtils class MemberVisibilityCanBePrivateInspection : AbstractKotlinInspection() { @@ -118,20 +121,27 @@ class MemberVisibilityCanBePrivateInspection : AbstractKotlinInspection() { ReferencesSearch.search(declaration, restrictedScope).forEach(Processor { val usage = it.element if (classOrObject != usage.getParentOfType(false)) { + otherUsageFound = true + return@Processor false + } + val classOrObjectDescriptor = classOrObject.descriptor as? ClassDescriptor + if (classOrObjectDescriptor != null) { + val receiver = usage.getStrictParentOfType()?.receiverExpression + val receiverDescriptor = (receiver?.getCallableDescriptor()?.returnType?.constructor?.declarationDescriptor + ?: (receiver?.mainReference?.resolve() as? KtClassOrObject)?.descriptor) as? ClassDescriptor + if (receiverDescriptor != null && DescriptorUtils.isSubclass(receiverDescriptor, classOrObjectDescriptor)) { + otherUsageFound = true + return@Processor false + } + } + val function = usage.getParentOfType(false) + val insideInlineFun = function?.modifierList?.let { it.hasModifier(KtTokens.INLINE_KEYWORD) && !function.isPrivate() } ?: false + if (insideInlineFun) { otherUsageFound = true false } else { - val function = usage.getParentOfType(false) - val insideInlineFun = function?.modifierList?.let { - it.hasModifier(KtTokens.INLINE_KEYWORD) && !function.isPrivate() - } ?: false - if (insideInlineFun) { - otherUsageFound = true - false - } else { - inClassUsageFound = true - true - } + inClassUsageFound = true + true } }) return inClassUsageFound && !otherUsageFound diff --git a/idea/testData/inspectionsLocal/memberVisibilityCanBePrivate/subClassFunctionCall.kt b/idea/testData/inspectionsLocal/memberVisibilityCanBePrivate/subClassFunctionCall.kt new file mode 100644 index 00000000000..1a68131fa59 --- /dev/null +++ b/idea/testData/inspectionsLocal/memberVisibilityCanBePrivate/subClassFunctionCall.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +open class A { + internal fun foo() {} + + fun bar(c: C) { + c.foo() + } +} + +class C : A() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/memberVisibilityCanBePrivate/subObjectFunctionCall.kt b/idea/testData/inspectionsLocal/memberVisibilityCanBePrivate/subObjectFunctionCall.kt new file mode 100644 index 00000000000..e6fb6d0ab84 --- /dev/null +++ b/idea/testData/inspectionsLocal/memberVisibilityCanBePrivate/subObjectFunctionCall.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +open class A { + internal fun foo() {} + + fun bar() { + O.foo() + } +} + +object O : A() \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 5c980efeda2..7bde00b60ac 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -2548,6 +2548,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { public void testJvmFieldOnProperty() throws Exception { runTest("idea/testData/inspectionsLocal/memberVisibilityCanBePrivate/JvmFieldOnProperty.kt"); } + + @TestMetadata("subClassFunctionCall.kt") + public void testSubClassFunctionCall() throws Exception { + runTest("idea/testData/inspectionsLocal/memberVisibilityCanBePrivate/subClassFunctionCall.kt"); + } + + @TestMetadata("subObjectFunctionCall.kt") + public void testSubObjectFunctionCall() throws Exception { + runTest("idea/testData/inspectionsLocal/memberVisibilityCanBePrivate/subObjectFunctionCall.kt"); + } } @TestMetadata("idea/testData/inspectionsLocal/migrateDiagnosticSuppression")