diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt index 23003f1d8f4..6752338fc2b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt @@ -28,6 +28,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.PsiReference import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.PsiSearchHelper import com.intellij.psi.search.PsiSearchHelper.SearchCostResult.* @@ -35,7 +36,6 @@ import com.intellij.psi.search.SearchScope import com.intellij.psi.search.searches.DefinitionsScopedSearch import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.refactoring.safeDelete.SafeDeleteHandler -import com.intellij.util.Processor import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.descriptors.annotations.Annotated @@ -43,14 +43,12 @@ import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler +import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.search.usagesSearch.* import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext -import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType -import org.jetbrains.kotlin.psi.psiUtil.getParentOfType -import org.jetbrains.kotlin.psi.psiUtil.isAncestor +import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.singletonOrEmptyList @@ -246,11 +244,31 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { } private fun hasReferences(declaration: KtNamedDeclaration, useScope: SearchScope): Boolean { - return !ReferencesSearch.search(declaration, useScope).forEach(Processor { - assert(it != null, { "Found reference is null, was looking for: " + declaration.getElementTextWithContext() }) - declaration.isAncestor(it.element) || - it.element.parent is KtValueArgumentName || - it.element.getParentOfType(false) != null + return !ReferencesSearch.search(declaration, useScope).forEach(fun (ref: PsiReference): Boolean { + if (declaration.isAncestor(ref.element)) return true // usages inside element's declaration are not counted + + if (ref.element.parent is KtValueArgumentName) return true // usage of parameter in form of named argument is not counted + + val import = ref.element.getParentOfType(false) + if (import != null) { + // check if we import member(s) from object or enum and search for their usages + if (declaration is KtObjectDeclaration || (declaration is KtClass && declaration.isEnum())) { + if (import.isAllUnder) { + val importedFrom = import.importedReference?.getQualifiedElementSelector()?.mainReference?.resolve() + as? KtClassOrObject ?: return true + return importedFrom.declarations.none { it is KtNamedDeclaration && hasNonTrivialUsages(it) } + } + else { + if (import.importedFqName != declaration.fqName) { + val importedDeclaration = import.importedReference?.getQualifiedElementSelector()?.mainReference?.resolve() as? KtNamedDeclaration + return importedDeclaration == null || !hasNonTrivialUsages(importedDeclaration) + } + } + } + return true + } + + return false }) } diff --git a/idea/testData/inspections/unusedSymbol/enum/inspectionData/expected.xml b/idea/testData/inspections/unusedSymbol/enum/inspectionData/expected.xml new file mode 100644 index 00000000000..53d8bd62c2d --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/enum/inspectionData/expected.xml @@ -0,0 +1,28 @@ + + + usedViaMemberImport.kt + 24 + light_idea_test_case + + Unused symbol + Function 'f' is never used + + + + usedViaMemberImport.kt + 16 + light_idea_test_case + + Unused symbol + Class 'E3' is never used + + + + usedViaMemberImport.kt + 20 + light_idea_test_case + + Unused symbol + Class 'E4' is never used + + diff --git a/idea/testData/inspections/unusedSymbol/enum/inspectionData/inspections.test b/idea/testData/inspections/unusedSymbol/enum/inspectionData/inspections.test new file mode 100644 index 00000000000..02aea1c357e --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/enum/inspectionData/inspections.test @@ -0,0 +1 @@ +// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.UnusedSymbolInspection diff --git a/idea/testData/inspections/unusedSymbol/enum/usedViaMemberImport.kt b/idea/testData/inspections/unusedSymbol/enum/usedViaMemberImport.kt new file mode 100644 index 00000000000..36896dd224b --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/enum/usedViaMemberImport.kt @@ -0,0 +1,27 @@ +package xxx + +import xxx.E1.AAA1 +import xxx.E2.* +import xxx.E3.AAA3 +import xxx.E4.* + +enum class E1 { + AAA1, BBB1 +} + +enum class E2 { + AAA2, BBB2 +} + +enum class E3 { + AAA3, BBB3 +} + +enum class E4 { + AAA4, BBB4 +} + +fun f() { + print(AAA1) + print(AAA2) +} diff --git a/idea/testData/inspections/unusedSymbol/object/inspectionData/expected.xml b/idea/testData/inspections/unusedSymbol/object/inspectionData/expected.xml index 01523879c9d..a94bb72e695 100644 --- a/idea/testData/inspections/unusedSymbol/object/inspectionData/expected.xml +++ b/idea/testData/inspections/unusedSymbol/object/inspectionData/expected.xml @@ -3,31 +3,34 @@ unused.kt 1 light_idea_test_case - + Unused Symbol Object 'A' is never used + usedOnlyInside.kt 1 light_idea_test_case - + Unused Symbol Object 'A' is never used + usedInTextByShortName.kt 3 light_idea_test_case - + Unused Symbol Object 'UsedInTextByShortName' is never used + usedOnlyInImport.kt 3 light_idea_test_case - + Unused Symbol Object 'A' is never used @@ -36,7 +39,7 @@ companionObjectUnused.kt 2 light_idea_test_case - + Unused Symbol Object 'Companion' is never used @@ -45,8 +48,35 @@ companionObjectUnused.kt 7 light_idea_test_case - + Unused Symbol Object 'Named' is never used + + + usedViaMemberImport.kt + 11 + light_idea_test_case + + Unused symbol + Function 'bar' is never used + + + + usedViaMemberImport.kt + 10 + light_idea_test_case + + Unused symbol + Object 'O2' is never used + + + + usedViaMemberImport.kt + 14 + light_idea_test_case + + Unused symbol + Function 'f' is never used + diff --git a/idea/testData/inspections/unusedSymbol/object/usedViaMemberImport.kt b/idea/testData/inspections/unusedSymbol/object/usedViaMemberImport.kt new file mode 100644 index 00000000000..ea8d38b31ca --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/object/usedViaMemberImport.kt @@ -0,0 +1,16 @@ +package xxx + +import xxx.O1.foo +import xxx.O2.bar + +object O1 { + fun foo(){} +} + +object O2 { + fun bar(){} +} + +fun f() { + foo() +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java index 1dd319e3793..7e2585e5cda 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java @@ -160,6 +160,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest { doTest(fileName); } + @TestMetadata("unusedSymbol/enum/inspectionData/inspections.test") + public void testUnusedSymbol_enum_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/unusedSymbol/enum/inspectionData/inspections.test"); + doTest(fileName); + } + @TestMetadata("unusedSymbol/function/inspectionData/inspections.test") public void testUnusedSymbol_function_inspectionData_Inspections_test() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/unusedSymbol/function/inspectionData/inspections.test");