"Remove redundant receiver" quick fix: Remove unused type parameter #KT-23512

This commit is contained in:
Toshiaki Kameyama
2018-09-05 13:18:59 +09:00
committed by Mikhail Glukhikh
parent 0cd25353bc
commit 90529dfda4
19 changed files with 183 additions and 27 deletions
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.isOverridable
import org.jetbrains.kotlin.idea.isMainFunction
import org.jetbrains.kotlin.idea.quickfix.RemoveUnusedFunctionParameterFix
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatureConfiguration
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor
import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify
@@ -133,12 +134,13 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement
val element = descriptor.psiElement as? KtTypeReference ?: return
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return
val function = element.parent as? KtCallableDeclaration ?: return
val callableDescriptor = function.resolveToDescriptorIfAny(BodyResolveMode.FULL) as? CallableDescriptor ?: return
val typeParameters = RemoveUnusedFunctionParameterFix.typeParameters(element)
if (inSameClass) {
runWriteAction {
val explicateAsTextForReceiver = callableDescriptor.explicateAsTextForReceiver()
@@ -150,6 +152,7 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() {
} else {
runChangeSignature(project, callableDescriptor, configureChangeSignature(), element, name)
}
RemoveUnusedFunctionParameterFix.runRemoveUnusedTypeParameters(typeParameters)
}
override fun getFamilyName(): String = name
@@ -40,7 +40,7 @@ class RemoveUnusedFunctionParameterFix(parameter: KtParameter) : KotlinQuickFixA
val parameterList = parameter.parent as? KtParameterList ?: return
val parameterDescriptor = parameter.resolveToParameterDescriptorIfAny(BodyResolveMode.FULL) ?: return
val parameterSize = parameterList.parameters.size
val typeParameters = typeParameters(parameter)
val typeParameters = typeParameters(parameter.typeReference)
val primaryConstructor = parameterList.parent as? KtPrimaryConstructor
ChangeFunctionSignatureFix.runRemoveParameter(parameterDescriptor, parameter)
@@ -64,31 +64,6 @@ class RemoveUnusedFunctionParameterFix(parameter: KtParameter) : KotlinQuickFixA
}
}
private fun typeParameters(parameter: KtParameter): List<KtTypeParameter> {
val parameterParent = parameter.getParentOfTypesAndPredicate(false, KtNamedFunction::class.java, KtClass::class.java) { true }
return parameter.typeReference?.typeElement
?.collectDescendantsOfType<KtNameReferenceExpression> { true }
?.mapNotNull {
val typeParameter = it.reference?.resolve() as? KtTypeParameter ?: return@mapNotNull null
val parent = typeParameter.getParentOfTypesAndPredicate(false, KtNamedFunction::class.java, KtClass::class.java) { true }
if (parent == parameterParent) typeParameter else null
} ?: emptyList()
}
private fun runRemoveUnusedTypeParameters(typeParameters: List<KtTypeParameter>) {
val unusedTypeParams = typeParameters.filter { ReferencesSearch.search(it).findFirst() == null }
if (unusedTypeParams.isEmpty()) return
runWriteAction {
unusedTypeParams.forEach { typeParameter ->
val typeParameterList = typeParameter.parent as? KtTypeParameterList ?: return@forEach
if (typeParameterList.parameters.size == 1)
typeParameterList.delete()
else
EditCommaSeparatedListHelper.removeItem(typeParameter)
}
}
}
companion object : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtParameter>? {
val parameter = Errors.UNUSED_PARAMETER.cast(diagnostic).psiElement
@@ -96,5 +71,37 @@ class RemoveUnusedFunctionParameterFix(parameter: KtParameter) : KotlinQuickFixA
if (parameterOwner is KtFunctionLiteral || (parameterOwner is KtNamedFunction && parameterOwner.name == null)) return null
return RemoveUnusedFunctionParameterFix(parameter)
}
fun typeParameters(typeReference: KtTypeReference?): List<KtTypeParameter> {
if (typeReference == null) return emptyList()
val parameterParent = typeReference.getParentOfTypesAndPredicate(
true,
KtNamedFunction::class.java, KtProperty::class.java, KtClass::class.java
) { true }
return typeReference.typeElement
?.collectDescendantsOfType<KtNameReferenceExpression> { true }
?.mapNotNull {
val typeParameter = it.reference?.resolve() as? KtTypeParameter ?: return@mapNotNull null
val parent = typeParameter.getParentOfTypesAndPredicate(
true,
KtNamedFunction::class.java, KtProperty::class.java, KtClass::class.java
) { true }
if (parent == parameterParent) typeParameter else null
} ?: emptyList()
}
fun runRemoveUnusedTypeParameters(typeParameters: List<KtTypeParameter>) {
val unusedTypeParams = typeParameters.filter { ReferencesSearch.search(it).findFirst() == null }
if (unusedTypeParams.isEmpty()) return
runWriteAction {
unusedTypeParams.forEach { typeParameter ->
val typeParameterList = typeParameter.parent as? KtTypeParameterList ?: return@forEach
if (typeParameterList.parameters.size == 1)
typeParameterList.delete()
else
EditCommaSeparatedListHelper.removeItem(typeParameter)
}
}
}
}
}
@@ -0,0 +1,5 @@
fun <T> <caret>T.foo() {}
fun test() {
"".foo()
}
@@ -0,0 +1,5 @@
fun foo() {}
fun test() {
foo()
}
@@ -0,0 +1,5 @@
fun <T> <caret>T.foo(t: T) {}
fun test() {
"".foo("")
}
@@ -0,0 +1,5 @@
fun <T> foo(t: T) {}
fun test() {
foo("")
}
@@ -0,0 +1,7 @@
interface TypeHolder<T, U>
fun <T, U> <caret>TypeHolder<T, U>.foo() {}
fun test(holder: TypeHolder<String, Int>) {
holder.foo()
}
@@ -0,0 +1,7 @@
interface TypeHolder<T, U>
fun foo() {}
fun test(holder: TypeHolder<String, Int>) {
foo()
}
@@ -0,0 +1,7 @@
class Test<T> {
fun <caret>T.foo() {}
fun test(t: T) {
t.foo()
}
}
@@ -0,0 +1,7 @@
class Test<T> {
fun foo() {}
fun test(t: T) {
foo()
}
}
@@ -0,0 +1,7 @@
class Test<T> {
fun <U> <caret>U.foo() {}
fun test() {
"".foo()
}
}
@@ -0,0 +1,7 @@
class Test<T> {
fun foo() {}
fun test() {
foo()
}
}
@@ -0,0 +1,6 @@
val <T> <caret>T.bar: Int
get() = 1
fun test() {
"".bar
}
@@ -0,0 +1,6 @@
val bar: Int
get() = 1
fun test() {
bar
}
@@ -0,0 +1,8 @@
class Test<T> {
val <caret>T.bar: Int
get() = 1
fun test(t: T) {
t.bar
}
}
@@ -0,0 +1,8 @@
class Test<T> {
val bar: Int
get() = 1
fun test(t: T) {
bar
}
}
@@ -0,0 +1,8 @@
class Test<T> {
val <S> <caret>S.bar: Int
get() = 1
fun test() {
"".bar
}
}
@@ -0,0 +1,8 @@
class Test<T> {
val bar: Int
get() = 1
fun test() {
bar
}
}
@@ -6475,6 +6475,31 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/functionInSameClass2.kt");
}
@TestMetadata("functionWithTypeParameter.kt")
public void testFunctionWithTypeParameter() throws Exception {
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/functionWithTypeParameter.kt");
}
@TestMetadata("functionWithTypeParameter2.kt")
public void testFunctionWithTypeParameter2() throws Exception {
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/functionWithTypeParameter2.kt");
}
@TestMetadata("functionWithTypeParameter3.kt")
public void testFunctionWithTypeParameter3() throws Exception {
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/functionWithTypeParameter3.kt");
}
@TestMetadata("functionWithTypeParameterInClass.kt")
public void testFunctionWithTypeParameterInClass() throws Exception {
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/functionWithTypeParameterInClass.kt");
}
@TestMetadata("functionWithTypeParameterInClass2.kt")
public void testFunctionWithTypeParameterInClass2() throws Exception {
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/functionWithTypeParameterInClass2.kt");
}
@TestMetadata("infix.kt")
public void testInfix() throws Exception {
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/infix.kt");
@@ -6494,6 +6519,21 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
public void testPropertyInSameClass() throws Exception {
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/propertyInSameClass.kt");
}
@TestMetadata("propertyWithTypeParameter.kt")
public void testPropertyWithTypeParameter() throws Exception {
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/propertyWithTypeParameter.kt");
}
@TestMetadata("propertyWithTypeParameterInClass.kt")
public void testPropertyWithTypeParameterInClass() throws Exception {
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/propertyWithTypeParameterInClass.kt");
}
@TestMetadata("propertyWithTypeParameterInClass2.kt")
public void testPropertyWithTypeParameterInClass2() throws Exception {
runTest("idea/testData/inspectionsLocal/unusedReceiverParameter/propertyWithTypeParameterInClass2.kt");
}
}
@TestMetadata("idea/testData/inspectionsLocal/unusedSymbol")