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
+