Do not count 'impl' or 'header' with implementations as unused #KT-15305 Fixed
This commit is contained in:
@@ -47,16 +47,15 @@ import org.jetbrains.kotlin.asJava.LightClassUtil
|
|||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||||
import org.jetbrains.kotlin.asJava.toLightClass
|
import org.jetbrains.kotlin.asJava.toLightClass
|
||||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
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.caches.resolve.resolveToDescriptorIfAny
|
||||||
import org.jetbrains.kotlin.idea.core.toDescriptor
|
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||||
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
|
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
|
||||||
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
|
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.imports.importableFqName
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction
|
import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction
|
||||||
@@ -162,7 +161,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
|||||||
if (declaration is KtEnumEntry) return
|
if (declaration is KtEnumEntry) return
|
||||||
if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
|
if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
|
||||||
if (declaration is KtProperty && declaration.isLocal) 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
|
// More expensive, resolve-based checks
|
||||||
val descriptor = declaration.resolveToDescriptorIfAny() ?: return
|
val descriptor = declaration.resolveToDescriptorIfAny() ?: return
|
||||||
@@ -174,7 +173,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
|||||||
if (declaration is KtParameter && declaration.dataClassComponentFunction() != null) return
|
if (declaration is KtParameter && declaration.dataClassComponentFunction() != null) return
|
||||||
|
|
||||||
// Main checks: finding reference usages && text usages
|
// Main checks: finding reference usages && text usages
|
||||||
if (hasNonTrivialUsages(declaration)) return
|
if (hasNonTrivialUsages(declaration, descriptor)) return
|
||||||
if (declaration is KtClassOrObject && classOrObjectHasTextUsages(declaration)) return
|
if (declaration is KtClassOrObject && classOrObjectHasTextUsages(declaration)) return
|
||||||
|
|
||||||
val psiElement = declaration.nameIdentifier ?: (declaration as? KtConstructor<*>)?.getConstructorKeyword() ?: return
|
val psiElement = declaration.nameIdentifier ?: (declaration as? KtConstructor<*>)?.getConstructorKeyword() ?: return
|
||||||
@@ -210,7 +209,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
|||||||
return hasTextUsages
|
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 psiSearchHelper = PsiSearchHelper.SERVICE.getInstance(declaration.project)
|
||||||
|
|
||||||
val useScope = declaration.useScope
|
val useScope = declaration.useScope
|
||||||
@@ -240,7 +239,9 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
|||||||
declaration.getBody()?.declarations?.isNotEmpty() == true) ||
|
declaration.getBody()?.declarations?.isNotEmpty() == true) ||
|
||||||
hasReferences(declaration, useScope) ||
|
hasReferences(declaration, useScope) ||
|
||||||
hasOverrides(declaration, useScope) ||
|
hasOverrides(declaration, useScope) ||
|
||||||
hasFakeOverrides(declaration, useScope)
|
hasFakeOverrides(declaration, useScope) ||
|
||||||
|
isPlatformImplementation(declaration) ||
|
||||||
|
hasPlatformImplementations(declaration, descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasReferences(declaration: KtNamedDeclaration, useScope: SearchScope): Boolean {
|
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? {
|
override fun createOptionsPanel(): JComponent? {
|
||||||
val panel = JPanel(GridBagLayout())
|
val panel = JPanel(GridBagLayout())
|
||||||
panel.add(
|
panel.add(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -63,4 +63,12 @@
|
|||||||
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Unused symbol</problem_class>
|
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Unused symbol</problem_class>
|
||||||
<description>Class 'Some' is never used</description>
|
<description>Class 'Some' is never used</description>
|
||||||
</problem>
|
</problem>
|
||||||
|
<problem>
|
||||||
|
<file>headerImpl.kt</file>
|
||||||
|
<line>8</line>
|
||||||
|
<module>light_idea_test_case</module>
|
||||||
|
<entry_point TYPE="file" FQNAME="temp:///src/headerImpl.kt" />
|
||||||
|
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Unused symbol</problem_class>
|
||||||
|
<description>Property ''bar'' is never used</description>
|
||||||
|
</problem>
|
||||||
</problems>
|
</problems>
|
||||||
|
|||||||
Reference in New Issue
Block a user