From a5e508a083289276993562b0774942bd1166ef1a Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Thu, 18 Oct 2018 16:34:06 +0300 Subject: [PATCH] Unused symbol: handle functions with inline class parameters correctly #KT-27357 Fixed --- .../inspections/UnusedSymbolInspection.kt | 21 ++++++++++++++++++- .../function/functionWithInlineClassParam.kt | 6 ++++++ .../function/inspectionData/expected.xml | 9 ++++++++ .../unusedSymbol/functionWithInlineClass.kt | 7 +++++++ .../functionWithInlineClassReceiver.kt | 7 +++++++ .../unusedSymbol/inlineClassMemberFunction.kt | 7 +++++++ .../LocalInspectionTestGenerated.java | 15 +++++++++++++ 7 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 idea/testData/inspections/unusedSymbol/function/functionWithInlineClassParam.kt create mode 100644 idea/testData/inspectionsLocal/unusedSymbol/functionWithInlineClass.kt create mode 100644 idea/testData/inspectionsLocal/unusedSymbol/functionWithInlineClassReceiver.kt create mode 100644 idea/testData/inspectionsLocal/unusedSymbol/inlineClassMemberFunction.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt index c6263179634..f92ac9fb866 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt @@ -67,6 +67,7 @@ import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.isInlineClassType import org.jetbrains.kotlin.util.findCallableMemberBySignature import java.awt.GridBagConstraints import java.awt.GridBagLayout @@ -309,7 +310,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { if (referenceUsed) return true } - if (declaration is KtCallableDeclaration && !declaration.hasModifier(KtTokens.INTERNAL_KEYWORD)) { + if (declaration is KtCallableDeclaration && declaration.canBeHandledByLightMethods(descriptor)) { val lightMethods = declaration.toLightMethods() if (lightMethods.isNotEmpty()) { val lightMethodsUsed = lightMethods.any { method -> @@ -323,6 +324,24 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { return referenceUsed } + private fun KtCallableDeclaration.canBeHandledByLightMethods(descriptor: DeclarationDescriptor?): Boolean { + return when { + hasModifier(KtTokens.INTERNAL_KEYWORD) -> false + this !is KtNamedFunction -> true + descriptor !is FunctionDescriptor -> true + else -> !descriptor.hasInlineClassParameters() + } + + } + + private fun FunctionDescriptor.hasInlineClassParameters(): Boolean { + return when { + dispatchReceiverParameter?.type?.isInlineClassType() == true -> true + extensionReceiverParameter?.type?.isInlineClassType() == true -> true + else -> valueParameters.any { it.type.isInlineClassType() } + } + } + private fun hasOverrides(declaration: KtNamedDeclaration, useScope: SearchScope): Boolean = DefinitionsScopedSearch.search(declaration, useScope).findFirst() != null diff --git a/idea/testData/inspections/unusedSymbol/function/functionWithInlineClassParam.kt b/idea/testData/inspections/unusedSymbol/function/functionWithInlineClassParam.kt new file mode 100644 index 00000000000..61e229254ca --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/functionWithInlineClassParam.kt @@ -0,0 +1,6 @@ +inline class InlineClass(val x: Int) + +// Unused +fun foo(arg: InlineClass) { + arg.x.hashCode() +} \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/inspectionData/expected.xml b/idea/testData/inspections/unusedSymbol/function/inspectionData/expected.xml index 5051e4dc54a..c8de5ceba0b 100644 --- a/idea/testData/inspections/unusedSymbol/function/inspectionData/expected.xml +++ b/idea/testData/inspections/unusedSymbol/function/inspectionData/expected.xml @@ -78,4 +78,13 @@ Unused symbol Constructor is never used + + + functionWithInlineClassParam.kt + 4 + light_idea_test_case + + Unused symbol + Function "foo" is never used + diff --git a/idea/testData/inspectionsLocal/unusedSymbol/functionWithInlineClass.kt b/idea/testData/inspectionsLocal/unusedSymbol/functionWithInlineClass.kt new file mode 100644 index 00000000000..7ff5e2f344b --- /dev/null +++ b/idea/testData/inspectionsLocal/unusedSymbol/functionWithInlineClass.kt @@ -0,0 +1,7 @@ +// PROBLEM: none + +inline class InlineClass(val x: Int) + +fun takeInline(inlineClass: InlineClass) = 1 + +val call = takeInline(InlineClass(1)) \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/unusedSymbol/functionWithInlineClassReceiver.kt b/idea/testData/inspectionsLocal/unusedSymbol/functionWithInlineClassReceiver.kt new file mode 100644 index 00000000000..dbdc1920805 --- /dev/null +++ b/idea/testData/inspectionsLocal/unusedSymbol/functionWithInlineClassReceiver.kt @@ -0,0 +1,7 @@ +// PROBLEM: none + +inline class InlineClass(val x: Int) + +fun InlineClass.takeInline() = 1 + +val call = InlineClass(1).takeInline() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/unusedSymbol/inlineClassMemberFunction.kt b/idea/testData/inspectionsLocal/unusedSymbol/inlineClassMemberFunction.kt new file mode 100644 index 00000000000..3946f1be277 --- /dev/null +++ b/idea/testData/inspectionsLocal/unusedSymbol/inlineClassMemberFunction.kt @@ -0,0 +1,7 @@ +// PROBLEM: none + +inline class InlineClass(val x: Int) { + fun takeInline() = 1 +} + +val call = InlineClass(1).takeInline() \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 6f67ab77377..772631e6965 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -6377,6 +6377,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/unusedSymbol/dataInlineClassDeclaration.kt"); } + @TestMetadata("functionWithInlineClass.kt") + public void testFunctionWithInlineClass() throws Exception { + runTest("idea/testData/inspectionsLocal/unusedSymbol/functionWithInlineClass.kt"); + } + + @TestMetadata("functionWithInlineClassReceiver.kt") + public void testFunctionWithInlineClassReceiver() throws Exception { + runTest("idea/testData/inspectionsLocal/unusedSymbol/functionWithInlineClassReceiver.kt"); + } + @TestMetadata("inAnonymous.kt") public void testInAnonymous() throws Exception { runTest("idea/testData/inspectionsLocal/unusedSymbol/inAnonymous.kt"); @@ -6392,6 +6402,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/unusedSymbol/inAnonymousRunWrapped.kt"); } + @TestMetadata("inlineClassMemberFunction.kt") + public void testInlineClassMemberFunction() throws Exception { + runTest("idea/testData/inspectionsLocal/unusedSymbol/inlineClassMemberFunction.kt"); + } + @TestMetadata("internal.kt") public void testInternal() throws Exception { runTest("idea/testData/inspectionsLocal/unusedSymbol/internal.kt");