Refactored change signature fixes to not hold DeclarationDescriptor's

This commit is contained in:
Valentin Kipyatkov
2015-10-14 19:55:08 +03:00
parent b2a04a7b04
commit f311d9febf
7 changed files with 164 additions and 118 deletions
@@ -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<JetFunctionLiteral, Data>() {
data class Data(val functionLiteral: JetFunctionLiteral, val descriptor: FunctionDescriptor, val parameterTypes: List<JetType>)
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)
}
}
}
}
@@ -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<PsiElement>(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<JetCallElement, Data>() {
data class Data(val callElement: JetCallElement, val descriptor: CallableDescriptor)
override fun getElementOfInterest(diagnostic: Diagnostic): JetCallElement? {
return diagnostic.psiElement.getNonStrictParentOfType<JetCallElement>()
}
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<PsiElement>) = false
override fun forcePerformForSelectedFunctionOnly() = false
},
context,
"Remove parameter '${parameterDescriptor.name.asString()}'")
}
}
}
@@ -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())
@@ -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<PsiElement>) = false
override fun forcePerformForSelectedFunctionOnly() = false
},
context,
text)
}
}
@@ -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<JetParameter>(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<JetParameter> {
return RemoveUnusedFunctionParameterFix(Errors.UNUSED_PARAMETER.cast(diagnostic).psiElement)
}
}
}
+1 -1
View File
@@ -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 {
@@ -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) {