Unused symbol: don't report for enum entry when enum builtin function is used

#KT-31133 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-07-20 22:06:36 +09:00
committed by Dmitry Gridin
parent 776cccf57c
commit 892135ae23
10 changed files with 154 additions and 1 deletions
@@ -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
}
@@ -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");