From 31a8689999cda5ead04e4e1909f320e8bd3fac8d Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 10 Jan 2017 16:41:18 +0300 Subject: [PATCH] Do not count 'impl' or 'header' with implementations as unused #KT-15305 Fixed --- .../inspections/UnusedSymbolInspection.kt | 30 ++++++++++++++----- .../unusedSymbol/class/headerImpl.kt | 9 ++++++ .../class/inspectionData/expected.xml | 8 +++++ 3 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 idea/testData/inspections/unusedSymbol/class/headerImpl.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt index 49a7128504b..93547ff1db0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt @@ -47,16 +47,15 @@ import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.asJava.toLightMethods -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.core.toDescriptor import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler +import org.jetbrains.kotlin.idea.highlighter.markers.hasImplementationsOf import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction @@ -162,7 +161,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { if (declaration is KtEnumEntry) return if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return if (declaration is KtProperty && declaration.isLocal) return - if (declaration is KtParameter && (declaration.getParent()?.parent !is KtPrimaryConstructor || !declaration.hasValOrVar())) return + if (declaration is KtParameter && (declaration.getParent().parent !is KtPrimaryConstructor || !declaration.hasValOrVar())) return // More expensive, resolve-based checks val descriptor = declaration.resolveToDescriptorIfAny() ?: return @@ -174,7 +173,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { if (declaration is KtParameter && declaration.dataClassComponentFunction() != null) return // Main checks: finding reference usages && text usages - if (hasNonTrivialUsages(declaration)) return + if (hasNonTrivialUsages(declaration, descriptor)) return if (declaration is KtClassOrObject && classOrObjectHasTextUsages(declaration)) return val psiElement = declaration.nameIdentifier ?: (declaration as? KtConstructor<*>)?.getConstructorKeyword() ?: return @@ -210,7 +209,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { return hasTextUsages } - private fun hasNonTrivialUsages(declaration: KtNamedDeclaration): Boolean { + private fun hasNonTrivialUsages(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor? = null): Boolean { val psiSearchHelper = PsiSearchHelper.SERVICE.getInstance(declaration.project) val useScope = declaration.useScope @@ -240,7 +239,9 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { declaration.getBody()?.declarations?.isNotEmpty() == true) || hasReferences(declaration, useScope) || hasOverrides(declaration, useScope) || - hasFakeOverrides(declaration, useScope) + hasFakeOverrides(declaration, useScope) || + isPlatformImplementation(declaration) || + hasPlatformImplementations(declaration, descriptor) } private fun hasReferences(declaration: KtNamedDeclaration, useScope: SearchScope): Boolean { @@ -322,6 +323,19 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { } } + private fun isPlatformImplementation(declaration: KtNamedDeclaration) = + declaration.hasModifier(KtTokens.IMPL_KEYWORD) + + private fun hasPlatformImplementations(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor?): Boolean { + if (!declaration.hasModifier(KtTokens.HEADER_KEYWORD)) return false + + descriptor as? MemberDescriptor ?: return false + val commonModuleDescriptor = declaration.containingKtFile.findModuleDescriptor() + + return commonModuleDescriptor.allImplementingModules.any { it.hasImplementationsOf(descriptor) } || + commonModuleDescriptor.hasImplementationsOf(descriptor) + } + override fun createOptionsPanel(): JComponent? { val panel = JPanel(GridBagLayout()) panel.add( diff --git a/idea/testData/inspections/unusedSymbol/class/headerImpl.kt b/idea/testData/inspections/unusedSymbol/class/headerImpl.kt new file mode 100644 index 00000000000..c1a9f69fcd0 --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/class/headerImpl.kt @@ -0,0 +1,9 @@ +// No "unused symbol" should be reported here + +header class My + +impl class My + +// But this should be reported +header val bar: String + diff --git a/idea/testData/inspections/unusedSymbol/class/inspectionData/expected.xml b/idea/testData/inspections/unusedSymbol/class/inspectionData/expected.xml index 68268bf7268..3b9869c7d6d 100644 --- a/idea/testData/inspections/unusedSymbol/class/inspectionData/expected.xml +++ b/idea/testData/inspections/unusedSymbol/class/inspectionData/expected.xml @@ -63,4 +63,12 @@ Unused symbol Class 'Some' is never used + + headerImpl.kt + 8 + light_idea_test_case + + Unused symbol + Property ''bar'' is never used +