DeprecatedSymbolUsageFix: refactored code for easier handling of optional arguments

This commit is contained in:
Valentin Kipyatkov
2015-05-29 18:49:52 +03:00
parent 1fdb4429c3
commit 07cb0a0b08
@@ -29,10 +29,7 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.CommentSaver import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.core.OptionalParametersHelper
import org.jetbrains.kotlin.idea.core.asExpression
import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses
import org.jetbrains.kotlin.idea.core.refactoring.JetNameSuggester import org.jetbrains.kotlin.idea.core.refactoring.JetNameSuggester
import org.jetbrains.kotlin.idea.core.refactoring.JetNameValidator import org.jetbrains.kotlin.idea.core.refactoring.JetNameValidator
import org.jetbrains.kotlin.idea.intentions.setType import org.jetbrains.kotlin.idea.intentions.setType
@@ -182,10 +179,14 @@ public abstract class DeprecatedSymbolUsageFixBase(
it[ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY] == parameterName it[ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY] == parameterName
} }
usages.forEach { usages.forEach {
val usageArgument = it.getParent() as? JetValueArgument
if (argument.isNamed) { if (argument.isNamed) {
(it.getParent() as? JetValueArgument)?.mark(MAKE_ARGUMENT_NAMED_KEY) usageArgument?.mark(MAKE_ARGUMENT_NAMED_KEY)
} }
it.replace(argument.wrapped) if (argument.isDefaultValue) {
usageArgument?.mark(DEFAULT_PARAMETER_VALUE_KEY)
}
it.replace(argument.expression)
} }
//TODO: sometimes we need to add explicit type arguments here because we don't have expected type in the new context //TODO: sometimes we need to add explicit type arguments here because we don't have expected type in the new context
@@ -195,8 +196,6 @@ public abstract class DeprecatedSymbolUsageFixBase(
} }
} }
unwrapDefaultValues(replacement.expression)
val wrapper = ConstructedExpressionWrapper(replacement.expression, expressionToBeReplaced, bindingContext) val wrapper = ConstructedExpressionWrapper(replacement.expression, expressionToBeReplaced, bindingContext)
if (qualifiedExpression is JetSafeQualifiedExpression) { if (qualifiedExpression is JetSafeQualifiedExpression) {
@@ -293,9 +292,9 @@ public abstract class DeprecatedSymbolUsageFixBase(
private class Argument( private class Argument(
val expression: JetExpression, val expression: JetExpression,
val wrapped: JetExpression,
val expressionType: JetType?, val expressionType: JetType?,
val isNamed: Boolean = false) val isNamed: Boolean = false,
val isDefaultValue: Boolean = false)
private fun argumentForParameter( private fun argumentForParameter(
parameter: ValueParameterDescriptor, parameter: ValueParameterDescriptor,
@@ -311,7 +310,7 @@ public abstract class DeprecatedSymbolUsageFixBase(
if (valueArgument is FunctionLiteralArgument) { if (valueArgument is FunctionLiteralArgument) {
expression.mark(WAS_FUNCTION_LITERAL_ARGUMENT_KEY) expression.mark(WAS_FUNCTION_LITERAL_ARGUMENT_KEY)
} }
return Argument(expression, expression, bindingContext.getType(expression), isNamed = valueArgument.isNamed()) return Argument(expression, bindingContext.getType(expression), isNamed = valueArgument.isNamed())
} }
is DefaultValueArgument -> { is DefaultValueArgument -> {
@@ -322,14 +321,12 @@ public abstract class DeprecatedSymbolUsageFixBase(
usages.forEach { it.put(ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY, param.getName()) } usages.forEach { it.put(ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY, param.getName()) }
} }
// we temporary wrap default values into parenthesis so that we can safely mark them with DEFAULT_PARAMETER_VALUE_KEY val expressionCopy = expression.copied()
val wrapped = JetPsiFactory(project).createExpressionByPattern("($0)", expression) as JetParenthesizedExpression
wrapped.mark(DEFAULT_PARAMETER_VALUE_KEY)
// clean up user data in original // clean up user data in original
expression.forEachDescendantOfType<JetExpression> { it.clear(ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY) } expression.forEachDescendantOfType<JetExpression> { it.clear(ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY) }
return Argument(wrapped.getExpression()!!, wrapped, null/*TODO*/) return Argument(expressionCopy, null/*TODO*/, isDefaultValue = true)
} }
is VarargValueArgument -> { is VarargValueArgument -> {
@@ -337,7 +334,7 @@ public abstract class DeprecatedSymbolUsageFixBase(
val single = arguments.singleOrNull() val single = arguments.singleOrNull()
if (single != null && single.getSpreadElement() != null) { if (single != null && single.getSpreadElement() != null) {
val expression = single.getArgumentExpression()!!.marked(USER_CODE_KEY) val expression = single.getArgumentExpression()!!.marked(USER_CODE_KEY)
return Argument(expression, expression, bindingContext.getType(expression), isNamed = single.isNamed()) return Argument(expression, bindingContext.getType(expression), isNamed = single.isNamed())
} }
val elementType = parameter.getVarargElementType()!! val elementType = parameter.getVarargElementType()!!
@@ -353,7 +350,7 @@ public abstract class DeprecatedSymbolUsageFixBase(
} }
appendFixedText(")") appendFixedText(")")
} }
return Argument(expression, expression, parameter.getType(), isNamed = single?.isNamed() ?: false) return Argument(expression, parameter.getType(), isNamed = single?.isNamed() ?: false)
} }
else -> error("Unknown argument type: $resolvedArgument") else -> error("Unknown argument type: $resolvedArgument")
@@ -399,11 +396,11 @@ public abstract class DeprecatedSymbolUsageFixBase(
it.clear(ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY) it.clear(ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY)
it.clear(PARAMETER_VALUE_KEY) it.clear(PARAMETER_VALUE_KEY)
it.clear(RECEIVER_VALUE_KEY) it.clear(RECEIVER_VALUE_KEY)
it.clear(DEFAULT_PARAMETER_VALUE_KEY)
it.clear(WAS_FUNCTION_LITERAL_ARGUMENT_KEY) it.clear(WAS_FUNCTION_LITERAL_ARGUMENT_KEY)
} }
it.forEachDescendantOfType<JetValueArgument> { it.forEachDescendantOfType<JetValueArgument> {
it.clear(MAKE_ARGUMENT_NAMED_KEY) it.clear(MAKE_ARGUMENT_NAMED_KEY)
it.clear(DEFAULT_PARAMETER_VALUE_KEY)
} }
} }
@@ -434,6 +431,11 @@ public abstract class DeprecatedSymbolUsageFixBase(
val name = argumentMatch.valueParameter.getName().asString() val name = argumentMatch.valueParameter.getName().asString()
//TODO: not always correct for vararg's //TODO: not always correct for vararg's
val newArgument = psiFactory.createArgument(argument.getArgumentExpression()!!, name, argument.getSpreadElement() != null) val newArgument = psiFactory.createArgument(argument.getArgumentExpression()!!, name, argument.getSpreadElement() != null)
if (argument[DEFAULT_PARAMETER_VALUE_KEY]) {
newArgument.mark(DEFAULT_PARAMETER_VALUE_KEY)
}
argument.replace(newArgument) argument.replace(newArgument)
} }
} }
@@ -445,9 +447,8 @@ public abstract class DeprecatedSymbolUsageFixBase(
val argumentsToDrop = ArrayList<ValueArgument>() val argumentsToDrop = ArrayList<ValueArgument>()
// we drop only those arguments that added to the code from some parameter's default // we drop only those arguments that added to the code from some parameter's default
fun canDropArgument(argument: ValueArgument) = argument.getArgumentExpression()!![DEFAULT_PARAMETER_VALUE_KEY] fun canDropArgument(argument: ValueArgument) = (argument as JetValueArgument)[DEFAULT_PARAMETER_VALUE_KEY]
//TODO: other types of calls
result.forEachDescendantOfType<JetCallExpression> { callExpression -> result.forEachDescendantOfType<JetCallExpression> { callExpression ->
val resolvedCall = callExpression.getResolvedCall(newBindingContext) ?: return@forEachDescendantOfType val resolvedCall = callExpression.getResolvedCall(newBindingContext) ?: return@forEachDescendantOfType
@@ -467,25 +468,6 @@ public abstract class DeprecatedSymbolUsageFixBase(
} }
} }
private fun unwrapDefaultValues(expression: JetExpression) {
val values = expression.collectDescendantsOfType<JetParenthesizedExpression> {
it[DEFAULT_PARAMETER_VALUE_KEY] && !it.getParent()[DEFAULT_PARAMETER_VALUE_KEY]
}
fun JetParenthesizedExpression.unwrap(): JetExpression {
if (!this[DEFAULT_PARAMETER_VALUE_KEY]) return this
var inner = getExpression()!!
if (inner is JetParenthesizedExpression) {
inner = inner.unwrap()
}
val result = replace(inner) as JetExpression
result.mark(DEFAULT_PARAMETER_VALUE_KEY)
return result
}
values.forEach { it.unwrap() }
}
private fun arrayOfFunctionName(elementType: JetType): String { private fun arrayOfFunctionName(elementType: JetType): String {
return when { return when {
KotlinBuiltIns.isInt(elementType) -> "kotlin.intArrayOf" KotlinBuiltIns.isInt(elementType) -> "kotlin.intArrayOf"
@@ -589,11 +571,11 @@ public abstract class DeprecatedSymbolUsageFixBase(
private val USER_CODE_KEY = Key<Unit>("USER_CODE") private val USER_CODE_KEY = Key<Unit>("USER_CODE")
private val PARAMETER_VALUE_KEY = Key<ValueParameterDescriptor>("PARAMETER_VALUE") private val PARAMETER_VALUE_KEY = Key<ValueParameterDescriptor>("PARAMETER_VALUE")
private val RECEIVER_VALUE_KEY = Key<Unit>("RECEIVER_VALUE") private val RECEIVER_VALUE_KEY = Key<Unit>("RECEIVER_VALUE")
private val DEFAULT_PARAMETER_VALUE_KEY = Key<Unit>("DEFAULT_PARAMETER_VALUE")
private val WAS_FUNCTION_LITERAL_ARGUMENT_KEY = Key<Unit>("WAS_FUNCTION_LITERAL_ARGUMENT") private val WAS_FUNCTION_LITERAL_ARGUMENT_KEY = Key<Unit>("WAS_FUNCTION_LITERAL_ARGUMENT")
// this key is used on JetValueArgument // these keys are used on JetValueArgument
private val MAKE_ARGUMENT_NAMED_KEY = Key<Unit>("MAKE_ARGUMENT_NAMED") private val MAKE_ARGUMENT_NAMED_KEY = Key<Unit>("MAKE_ARGUMENT_NAMED")
private val DEFAULT_PARAMETER_VALUE_KEY = Key<Unit>("DEFAULT_PARAMETER_VALUE")
} }
class ConstructedExpressionWrapper( class ConstructedExpressionWrapper(