diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/MemberVisibilityCanPrivateInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/MemberVisibilityCanPrivateInspection.kt index 90406c715c0..5ff266c74c6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/MemberVisibilityCanPrivateInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/MemberVisibilityCanPrivateInspection.kt @@ -16,18 +16,24 @@ package org.jetbrains.kotlin.idea.inspections -import com.intellij.codeInspection.* +import com.intellij.codeInspection.IntentionWrapper +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.PsiReference -import com.intellij.psi.search.LocalSearchScope +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.idea.quickfix.AddModifierFix import org.jetbrains.kotlin.idea.refactoring.isConstructorDeclaredProperty import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.psi.psiUtil.isInheritable +import org.jetbrains.kotlin.psi.psiUtil.isOverridable class MemberVisibilityCanPrivateInspection : AbstractKotlinInspection() { @@ -57,20 +63,34 @@ class MemberVisibilityCanPrivateInspection : AbstractKotlinInspection() { } - private fun canBePrivate(declaration: KtDeclaration): Boolean { + private fun canBePrivate(declaration: KtNamedDeclaration): Boolean { if (declaration.hasModifier(KtTokens.PRIVATE_KEYWORD) || declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return false val classOrObject = declaration.containingClassOrObject ?: return false val inheritable = classOrObject is KtClass && classOrObject.isInheritable() if (!inheritable && declaration.hasModifier(KtTokens.PROTECTED_KEYWORD)) return false //reported by ProtectedInFinalInspection if (declaration.isOverridable()) return false + + val psiSearchHelper = PsiSearchHelper.SERVICE.getInstance(declaration.project) + val useScope = declaration.useScope + val name = declaration.name ?: return false + if (useScope is GlobalSearchScope) { + when (psiSearchHelper.isCheapEnoughToSearch(name, useScope, null, null)) { + PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES -> return false + PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES -> return false + PsiSearchHelper.SearchCostResult.FEW_OCCURRENCES -> { + } + } + } + var otherUsageFound = false var inClassUsageFound = false - ReferencesSearch.search(declaration, declaration.useScope).forEach(Processor { + ReferencesSearch.search(declaration, useScope).forEach(Processor { val usage = it.element if (classOrObject != usage.getParentOfType(false)) { otherUsageFound = true false - } else { + } + else { inClassUsageFound = true true } diff --git a/idea/testData/quickfix/memberVisibilityCanBePrivate/getter.before.Main.kt b/idea/testData/quickfix/memberVisibilityCanBePrivate/getter.before.Main.kt new file mode 100644 index 00000000000..8e0fde97a0b --- /dev/null +++ b/idea/testData/quickfix/memberVisibilityCanBePrivate/getter.before.Main.kt @@ -0,0 +1,8 @@ +// "Add 'private' modifier" "false" +// ACTION: Convert to secondary constructor +// ACTION: Create test +// ACTION: Move to class body + +class My(val parameter: Int) { + val other = parameter +} \ No newline at end of file diff --git a/idea/testData/quickfix/memberVisibilityCanBePrivate/getter.before.data.Sample.java b/idea/testData/quickfix/memberVisibilityCanBePrivate/getter.before.data.Sample.java new file mode 100644 index 00000000000..ed3a7834ea2 --- /dev/null +++ b/idea/testData/quickfix/memberVisibilityCanBePrivate/getter.before.data.Sample.java @@ -0,0 +1,5 @@ +public class User { + public static int foo(My my) { + return my.getParameter(); + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 34c227fa730..4e4ba0d6eed 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -1665,6 +1665,21 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } + @TestMetadata("idea/testData/quickfix/memberVisibilityCanBePrivate") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MemberVisibilityCanBePrivate extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInMemberVisibilityCanBePrivate() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/memberVisibilityCanBePrivate"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + + @TestMetadata("getter.before.Main.kt") + public void testGetter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/memberVisibilityCanBePrivate/getter.before.Main.kt"); + doTestWithExtraFile(fileName); + } + } + @TestMetadata("idea/testData/quickfix/migration") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)