Unused symbol: don't report for enum entry when enum builtin function is used
#KT-31133 Fixed
This commit is contained in:
committed by
Dmitry Gridin
parent
776cccf57c
commit
892135ae23
@@ -48,6 +48,8 @@ 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.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.inspections.collections.isCalling
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.isFinalizeMethod
|
||||
import org.jetbrains.kotlin.idea.isMainFunction
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
@@ -67,12 +69,15 @@ import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.idea.util.hasActualsFor
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.isInlineClassType
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import java.awt.GridBagConstraints
|
||||
import java.awt.GridBagLayout
|
||||
@@ -87,6 +92,10 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
||||
|
||||
private val KOTLIN_ADDITIONAL_ANNOTATIONS = listOf("kotlin.test.*")
|
||||
|
||||
private val KOTLIN_BUILTIN_ENUM_FUNCTIONS = listOf(FqName("kotlin.enumValues"), FqName("kotlin.enumValueOf"))
|
||||
|
||||
private val ENUM_STATIC_METHODS = listOf("values", "valueOf")
|
||||
|
||||
private fun KtDeclaration.hasKotlinAdditionalAnnotation() =
|
||||
this is KtNamedDeclaration && checkAnnotatedUsingPatterns(this, KOTLIN_ADDITIONAL_ANNOTATIONS)
|
||||
|
||||
@@ -359,10 +368,11 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
||||
): Boolean {
|
||||
fun checkReference(ref: PsiReference): Boolean = checkReference(ref, declaration, descriptor)
|
||||
|
||||
val searchOptions = KotlinReferencesSearchOptions(acceptCallableOverrides = declaration.hasActualModifier())
|
||||
val searchParameters = KotlinReferencesSearchParameters(
|
||||
declaration,
|
||||
useScope,
|
||||
kotlinOptions = KotlinReferencesSearchOptions(acceptCallableOverrides = declaration.hasActualModifier())
|
||||
kotlinOptions = searchOptions
|
||||
)
|
||||
val referenceUsed: Boolean by lazy { !ReferencesSearch.search(searchParameters).forEach(::checkReference) }
|
||||
|
||||
@@ -381,8 +391,47 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration is KtEnumEntry) {
|
||||
val enumClass = declaration.containingClass()?.takeIf { it.isEnum() }
|
||||
if (enumClass != null && ReferencesSearch.search(
|
||||
KotlinReferencesSearchParameters(
|
||||
enumClass,
|
||||
useScope,
|
||||
kotlinOptions = searchOptions
|
||||
)
|
||||
).any(::hasBuiltInEnumFunctionReference)
|
||||
) return true
|
||||
}
|
||||
|
||||
return referenceUsed || checkPrivateDeclaration(declaration, descriptor)
|
||||
}
|
||||
|
||||
private fun hasBuiltInEnumFunctionReference(reference: PsiReference): Boolean {
|
||||
return when (val parent = reference.element.getParentOfTypes(
|
||||
true,
|
||||
KtTypeReference::class.java,
|
||||
KtQualifiedExpression::class.java,
|
||||
KtCallableReferenceExpression::class.java
|
||||
)) {
|
||||
is KtTypeReference -> {
|
||||
val target = (parent.getStrictParentOfType<KtTypeArgumentList>() ?: parent)
|
||||
.getParentOfTypes(true, KtCallExpression::class.java, KtCallableDeclaration::class.java)
|
||||
when (target) {
|
||||
is KtCallExpression -> target.isCalling(KOTLIN_BUILTIN_ENUM_FUNCTIONS)
|
||||
is KtCallableDeclaration -> {
|
||||
target.anyDescendantOfType<KtCallExpression> {
|
||||
val context = it.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
|
||||
it.isCalling(KOTLIN_BUILTIN_ENUM_FUNCTIONS, context) && it.isUsedAsExpression(context)
|
||||
}
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
is KtQualifiedExpression -> parent.callExpression?.calleeExpression?.text in ENUM_STATIC_METHODS
|
||||
is KtCallableReferenceExpression -> parent.callableReference.text in ENUM_STATIC_METHODS
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPrivateDeclaration(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor?): Boolean {
|
||||
if (descriptor == null || !declaration.isPrivateNestedClassOrObject) return false
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
enum class SomeEnum {
|
||||
<caret>USED
|
||||
}
|
||||
|
||||
fun test() {
|
||||
SomeEnum.values()
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
enum class SomeEnum {
|
||||
<caret>USED
|
||||
}
|
||||
|
||||
fun test() {
|
||||
SomeEnum.valueOf("USED")
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
enum class SomeEnum {
|
||||
<caret>USED
|
||||
}
|
||||
|
||||
fun test() {
|
||||
enumValues<SomeEnum>()
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
enum class SomeEnum {
|
||||
<caret>USED
|
||||
}
|
||||
|
||||
fun test() {
|
||||
enumValueOf<SomeEnum>("USED")
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
enum class SomeEnum {
|
||||
<caret>USED
|
||||
}
|
||||
|
||||
fun test(): Array<SomeEnum> {
|
||||
return enumValues()
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
enum class SomeEnum {
|
||||
<caret>USED
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val e: SomeEnum = enumValueOf("USED")
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
enum class SomeEnum {
|
||||
<caret>USED
|
||||
}
|
||||
|
||||
fun test() {
|
||||
SomeEnum::values
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
enum class SomeEnum {
|
||||
<caret>USED
|
||||
}
|
||||
|
||||
fun test() {
|
||||
SomeEnum::valueOf
|
||||
}
|
||||
+40
@@ -12141,6 +12141,46 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
runTest("idea/testData/inspectionsLocal/unusedSymbol/unusedEnumEntry.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usedEnumFunction.kt")
|
||||
public void testUsedEnumFunction() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedSymbol/usedEnumFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usedEnumFunction2.kt")
|
||||
public void testUsedEnumFunction2() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedSymbol/usedEnumFunction2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usedEnumFunction3.kt")
|
||||
public void testUsedEnumFunction3() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedSymbol/usedEnumFunction3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usedEnumFunction4.kt")
|
||||
public void testUsedEnumFunction4() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedSymbol/usedEnumFunction4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usedEnumFunction5.kt")
|
||||
public void testUsedEnumFunction5() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedSymbol/usedEnumFunction5.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usedEnumFunction6.kt")
|
||||
public void testUsedEnumFunction6() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedSymbol/usedEnumFunction6.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usedEnumFunction7.kt")
|
||||
public void testUsedEnumFunction7() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedSymbol/usedEnumFunction7.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usedEnumFunction8.kt")
|
||||
public void testUsedEnumFunction8() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedSymbol/usedEnumFunction8.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withJvmNameUsedFromKotlin.kt")
|
||||
public void testWithJvmNameUsedFromKotlin() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/unusedSymbol/withJvmNameUsedFromKotlin.kt");
|
||||
|
||||
Reference in New Issue
Block a user