From f311d9febf0b934dd66f3783a506427f0c396518 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 14 Oct 2015 19:55:08 +0300 Subject: [PATCH] Refactored change signature fixes to not hold DeclarationDescriptor's --- .../ChangeFunctionLiteralSignatureFix.kt | 25 +++ .../quickfix/ChangeFunctionSignatureFix.kt | 146 +++++++++++------- .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 8 +- .../quickfix/RemoveFunctionParametersFix.kt | 56 ------- .../RemoveUnusedFunctionParameterFix.kt | 43 ++++++ .../quickfix/changeSignature/notEditable.kt | 2 +- .../removeParameterNotAvailableForBuiltins.kt | 2 +- 7 files changed, 164 insertions(+), 118 deletions(-) delete mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveFunctionParametersFix.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveUnusedFunctionParameterFix.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralSignatureFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralSignatureFix.kt index d5de060c879..f0eba522dcc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralSignatureFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralSignatureFix.kt @@ -20,6 +20,9 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.core.CollectingNameValidator import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.refactoring.changeSignature.* @@ -57,4 +60,26 @@ class ChangeFunctionLiteralSignatureFix( context, text) } + + companion object : KotlinIntentionActionFactoryWithDelegate() { + data class Data(val functionLiteral: JetFunctionLiteral, val descriptor: FunctionDescriptor, val parameterTypes: List) + + override fun getElementOfInterest(diagnostic: Diagnostic): JetFunctionLiteral? { + val diagnosticWithParameters = Errors.EXPECTED_PARAMETERS_NUMBER_MISMATCH.cast(diagnostic) + return diagnosticWithParameters.psiElement as? JetFunctionLiteral + } + + override fun createQuickFixData(element: JetFunctionLiteral, diagnostic: Diagnostic): Data? { + val descriptor = element.resolveToDescriptor() as? FunctionDescriptor ?: return null + val parameterTypes = Errors.EXPECTED_PARAMETERS_NUMBER_MISMATCH.cast(diagnostic).b + return Data(element, descriptor, parameterTypes) + } + + override fun createQuickFix(diagnostic: Diagnostic, quickFixDataFactory: () -> Data?): QuickFixWithDelegateFactory? { + return QuickFixWithDelegateFactory { + val (functionLiteral, descriptor, parameterTypes) = quickFixDataFactory() ?: return@QuickFixWithDelegateFactory null + ChangeFunctionLiteralSignatureFix(functionLiteral, descriptor, parameterTypes) + } + } + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.kt index ede6f492a6c..a1bd44a5fad 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionSignatureFix.kt @@ -20,7 +20,6 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile -import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED import org.jetbrains.kotlin.descriptors.FunctionDescriptor @@ -28,17 +27,19 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.diagnostics.Errors.EXPECTED_PARAMETERS_NUMBER_MISMATCH -import org.jetbrains.kotlin.diagnostics.Errors.UNUSED_PARAMETER import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.core.mapArgumentsToParameters import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeSignatureConfiguration +import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetMethodDescriptor +import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify +import org.jetbrains.kotlin.idea.refactoring.changeSignature.runChangeSignature import org.jetbrains.kotlin.psi.JetCallElement -import org.jetbrains.kotlin.psi.JetFunctionLiteral +import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.checker.JetTypeChecker @@ -48,7 +49,7 @@ abstract class ChangeFunctionSignatureFix( protected val functionDescriptor: FunctionDescriptor ) : KotlinQuickFixAction(context) { - override fun getFamilyName() = "Change signature of function/constructor" + override fun getFamilyName() = FAMILY_NAME override fun startInWriteAction() = false @@ -75,61 +76,94 @@ abstract class ChangeFunctionSignatureFix( } } - object Factory : JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): ChangeFunctionSignatureFix? { - val callElement = PsiTreeUtil.getParentOfType(diagnostic.psiElement, JetCallElement::class.java) ?: return null + companion object : KotlinIntentionActionFactoryWithDelegate() { + data class Data(val callElement: JetCallElement, val descriptor: CallableDescriptor) + + override fun getElementOfInterest(diagnostic: Diagnostic): JetCallElement? { + return diagnostic.psiElement.getNonStrictParentOfType() + } + + override fun createQuickFixData(element: JetCallElement, diagnostic: Diagnostic): Data? { val descriptor = DiagnosticFactory.cast(diagnostic, Errors.TOO_MANY_ARGUMENTS, Errors.NO_VALUE_FOR_PARAMETER).a - return createFix(callElement, callElement, descriptor) + return Data(element, descriptor) } - } - object FactoryForParametersNumberMismatch: JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): ChangeFunctionSignatureFix? { - val diagnosticWithParameters = EXPECTED_PARAMETERS_NUMBER_MISMATCH.cast(diagnostic) - val functionLiteral = diagnosticWithParameters.psiElement as? JetFunctionLiteral ?: return null - val descriptor = functionLiteral.resolveToDescriptor() as? FunctionDescriptor ?: return null - return ChangeFunctionLiteralSignatureFix(functionLiteral, descriptor, diagnosticWithParameters.b) - } - } - - object FactoryForUnusedParameter : JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): ChangeFunctionSignatureFix? { - val descriptor = UNUSED_PARAMETER.cast(diagnostic).a as? ValueParameterDescriptor ?: return null - return createFix(null, diagnostic.psiElement, descriptor) - } - } -} - -private fun createFix(callElement: JetCallElement?, context: PsiElement, descriptor: CallableDescriptor): ChangeFunctionSignatureFix? { - val functionDescriptor = when (descriptor) { - is FunctionDescriptor -> descriptor as FunctionDescriptor - else -> if (descriptor is ValueParameterDescriptor) descriptor.containingDeclaration as? FunctionDescriptor - else null - } ?: return null - - if (functionDescriptor.kind == SYNTHESIZED) return null - - if (descriptor is ValueParameterDescriptor) { - return RemoveFunctionParametersFix(context, functionDescriptor, descriptor) - } - else { - val parameters = functionDescriptor.valueParameters - val arguments = callElement!!.valueArguments - - if (arguments.size > parameters.size) { - val bindingContext = callElement.analyze() - val call = callElement.getCall(bindingContext) ?: return null - val argumentToParameter = call.mapArgumentsToParameters(functionDescriptor) - val hasTypeMismatches = argumentToParameter.any { - val argument = it.key - val parameter = it.value - val argumentType = argument.getArgumentExpression()?.let { bindingContext.getType(it) } - argumentType == null || !JetTypeChecker.DEFAULT.isSubtypeOf(argumentType, parameter.type) + override fun createQuickFix(diagnostic: Diagnostic, quickFixDataFactory: () -> Data?): QuickFixWithDelegateFactory { + return QuickFixWithDelegateFactory { + val (callElement, descriptor) = quickFixDataFactory() ?: return@QuickFixWithDelegateFactory null + createFix(callElement, callElement, descriptor) } - return AddFunctionParametersFix(callElement, functionDescriptor, hasTypeMismatches) } - } - return null + private fun createFix(callElement: JetCallElement, context: PsiElement, descriptor: CallableDescriptor): ChangeFunctionSignatureFix? { + val functionDescriptor = when (descriptor) { + is FunctionDescriptor -> descriptor as FunctionDescriptor + else -> if (descriptor is ValueParameterDescriptor) descriptor.containingDeclaration as? FunctionDescriptor + else null + } ?: return null + + if (functionDescriptor.kind == SYNTHESIZED) return null + + if (descriptor is ValueParameterDescriptor) { + return RemoveParameterFix(context, functionDescriptor, descriptor) + } + else { + val parameters = functionDescriptor.valueParameters + val arguments = callElement.valueArguments + + if (arguments.size > parameters.size) { + val bindingContext = callElement.analyze() + val call = callElement.getCall(bindingContext) ?: return null + val argumentToParameter = call.mapArgumentsToParameters(functionDescriptor) + val hasTypeMismatches = argumentToParameter.any { + val argument = it.key + val parameter = it.value + val argumentType = argument.getArgumentExpression()?.let { bindingContext.getType(it) } + argumentType == null || !JetTypeChecker.DEFAULT.isSubtypeOf(argumentType, parameter.type) + } + return AddFunctionParametersFix(callElement, functionDescriptor, hasTypeMismatches) + } + } + + return null + } + + private class RemoveParameterFix( + context: PsiElement, + functionDescriptor: FunctionDescriptor, + private val parameterToRemove: ValueParameterDescriptor + ) : ChangeFunctionSignatureFix(context, functionDescriptor) { + + override fun getText() = "Remove parameter '${parameterToRemove.name.asString()}'" + + override fun invoke(project: Project, editor: Editor?, file: JetFile) { + runRemoveParameter(parameterToRemove, context) + } + } + + val FAMILY_NAME = "Change signature of function/constructor" + + fun runRemoveParameter(parameterDescriptor: ValueParameterDescriptor, context: PsiElement) { + val functionDescriptor = parameterDescriptor.containingDeclaration as FunctionDescriptor + runChangeSignature( + context.project, + functionDescriptor, + object : JetChangeSignatureConfiguration { + override fun configure(originalDescriptor: JetMethodDescriptor): JetMethodDescriptor { + return originalDescriptor.modify { descriptor -> + val index = if (descriptor.receiver != null) parameterDescriptor.index + 1 else parameterDescriptor.index + descriptor.removeParameter(index) + } + } + + override fun performSilently(affectedFunctions: Collection) = false + override fun forcePerformForSelectedFunctionOnly() = false + }, + context, + "Remove parameter '${parameterDescriptor.name.asString()}'") + } + + } } + diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index c07f23577b6..fc7c9d7cff2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -222,10 +222,10 @@ public class QuickFixRegistrar : QuickFixContributor { COMPARE_TO_TYPE_MISMATCH.registerFactory(ChangeFunctionReturnTypeFix.createFactoryForCompareToTypeMismatch()) IMPLICIT_NOTHING_RETURN_TYPE.registerFactory(ChangeFunctionReturnTypeFix.createFactoryForChangingReturnTypeToNothing()) - TOO_MANY_ARGUMENTS.registerFactory(ChangeFunctionSignatureFix.Factory) - NO_VALUE_FOR_PARAMETER.registerFactory(ChangeFunctionSignatureFix.Factory) - UNUSED_PARAMETER.registerFactory(ChangeFunctionSignatureFix.FactoryForUnusedParameter) - EXPECTED_PARAMETERS_NUMBER_MISMATCH.registerFactory(ChangeFunctionSignatureFix.FactoryForParametersNumberMismatch) + TOO_MANY_ARGUMENTS.registerFactory(ChangeFunctionSignatureFix) + NO_VALUE_FOR_PARAMETER.registerFactory(ChangeFunctionSignatureFix) + UNUSED_PARAMETER.registerFactory(RemoveUnusedFunctionParameterFix) + EXPECTED_PARAMETERS_NUMBER_MISMATCH.registerFactory(ChangeFunctionLiteralSignatureFix) EXPECTED_PARAMETER_TYPE_MISMATCH.registerFactory(ChangeTypeFix.createFactoryForExpectedParameterTypeMismatch()) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveFunctionParametersFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveFunctionParametersFix.kt deleted file mode 100644 index 4d795d10ce5..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveFunctionParametersFix.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.idea.quickfix - -import com.intellij.openapi.editor.Editor -import com.intellij.openapi.project.Project -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeSignatureConfiguration -import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetMethodDescriptor -import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify -import org.jetbrains.kotlin.idea.refactoring.changeSignature.runChangeSignature -import org.jetbrains.kotlin.psi.JetFile - -class RemoveFunctionParametersFix( - context: PsiElement, - functionDescriptor: FunctionDescriptor, - private val parameterToRemove: ValueParameterDescriptor -) : ChangeFunctionSignatureFix(context, functionDescriptor) { - - override fun getText() = "Remove parameter '${parameterToRemove.name.asString()}'" - - override fun invoke(project: Project, editor: Editor?, file: JetFile) { - runChangeSignature( - project, - functionDescriptor, - object : JetChangeSignatureConfiguration { - override fun configure(originalDescriptor: JetMethodDescriptor): JetMethodDescriptor { - return originalDescriptor.modify { descriptor -> - val index = functionDescriptor.valueParameters.indexOf(parameterToRemove) - descriptor.removeParameter(if (descriptor.receiver != null) index + 1 else index) - } - } - - override fun performSilently(affectedFunctions: Collection) = false - override fun forcePerformForSelectedFunctionOnly() = false - }, - context, - text) - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveUnusedFunctionParameterFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveUnusedFunctionParameterFix.kt new file mode 100644 index 00000000000..0e73f6e7a56 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveUnusedFunctionParameterFix.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetParameter + +class RemoveUnusedFunctionParameterFix(parameter: JetParameter) : KotlinQuickFixAction(parameter) { + override fun getFamilyName() = ChangeFunctionSignatureFix.FAMILY_NAME + + override fun getText() = "Remove parameter '${element.name}'" + + override fun invoke(project: Project, editor: Editor?, file: JetFile) { + val parameterDescriptor = element.resolveToDescriptor() as ValueParameterDescriptor + ChangeFunctionSignatureFix.runRemoveParameter(parameterDescriptor, element) + } + + companion object : JetSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction { + return RemoveUnusedFunctionParameterFix(Errors.UNUSED_PARAMETER.cast(diagnostic).psiElement) + } + } +} diff --git a/idea/testData/quickfix/changeSignature/notEditable.kt b/idea/testData/quickfix/changeSignature/notEditable.kt index 460fab40ce1..d1678187c6f 100644 --- a/idea/testData/quickfix/changeSignature/notEditable.kt +++ b/idea/testData/quickfix/changeSignature/notEditable.kt @@ -1,4 +1,4 @@ -// "class org.jetbrains.kotlin.idea.quickfix.RemoveFunctionParametersFix" "false" +// "class org.jetbrains.kotlin.idea.quickfix.ChangeFunctionSignatureFix" "false" //ERROR: No value passed for parameter other abstract class StringComparable { diff --git a/idea/testData/quickfix/changeSignature/removeParameterNotAvailableForBuiltins.kt b/idea/testData/quickfix/changeSignature/removeParameterNotAvailableForBuiltins.kt index e485fa63687..2841e4f2522 100644 --- a/idea/testData/quickfix/changeSignature/removeParameterNotAvailableForBuiltins.kt +++ b/idea/testData/quickfix/changeSignature/removeParameterNotAvailableForBuiltins.kt @@ -1,4 +1,4 @@ -// "class org.jetbrains.kotlin.idea.quickfix.RemoveFunctionParametersFix" "false" +// "class org.jetbrains.kotlin.idea.quickfix.ChangeFunctionSignatureFix" "false" // ERROR: No value passed for parameter other fun f(d: Boolean) {