From f58ce760ca6f8960fd382883d03c6edb80ef7678 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 21 May 2015 08:44:31 +0300 Subject: [PATCH] DeprecatedSymbolUsageFix: support for optional parameters (not complete) --- .../kotlin/psi/JetValueArgumentList.java | 1 + .../kotlin/psi/psiUtil/jetPsiUtil.kt | 81 +++---- .../idea/core/OptionalParametersHelper.kt | 135 +++++++++++ .../quickfix/DeprecatedSymbolUsageFixBase.kt | 219 +++++++++++++----- .../RemoveNameFromFunctionExpressionFix.kt | 4 +- .../quickfix/ReplaceWithAnnotationAnalyzer.kt | 4 +- .../idea/quickfix/replaceJavaClass/common.kt | 6 +- .../optionalParameters1.kt | 12 + .../optionalParameters1.kt.after | 12 + .../optionalParameters2.kt | 12 + .../optionalParameters2.kt.after | 12 + .../optionalParameters3.kt | 12 + .../optionalParameters3.kt.after | 12 + .../optionalParameters4.kt | 12 + .../optionalParameters4.kt.after | 12 + .../optionalParameters5.kt | 14 ++ .../optionalParameters5.kt.after | 15 ++ .../optionalParameters6.kt.after | 14 ++ .../optionalParameters6.kt.todo | 14 ++ .../optionalParameters7.kt | 14 ++ .../optionalParameters7.kt.after | 15 ++ .../optionalParameters8.kt.after | 14 ++ .../optionalParameters8.kt.todo | 14 ++ .../optionalParameters9.kt | 12 + .../optionalParameters9.kt.after | 12 + .../idea/quickfix/QuickFixTestGenerated.java | 42 ++++ 26 files changed, 627 insertions(+), 99 deletions(-) create mode 100644 idea/idea-core/src/org/jetbrains/kotlin/idea/core/OptionalParametersHelper.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters1.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters1.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters2.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters2.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters3.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters3.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters4.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters4.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters5.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters5.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters6.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters6.kt.todo create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters7.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters7.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters8.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters8.kt.todo create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters9.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters9.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetValueArgumentList.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetValueArgumentList.java index f5b9c0bacd3..e656db1dfe6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetValueArgumentList.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetValueArgumentList.java @@ -66,6 +66,7 @@ public class JetValueArgumentList extends JetElementImpl { } public void removeArgument(@NotNull JetValueArgument argument) { + assert argument.getParent() == this; EditCommaSeparatedListHelper.INSTANCE$.removeItem(argument); } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt index d6a1da9b941..6f672d594d5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -546,33 +546,54 @@ public fun PsiElement.getElementTextWithContext(): String { // Calls `block` on each descendant of T type // Note, that calls happen in order of DFS-exit, so deeper nodes are applied earlier -inline fun forEachDescendantOfTypeVisitor( - noinline block: (T) -> Unit -): JetVisitorVoid = - object : JetTreeVisitorVoid() { - override fun visitJetElement(element: JetElement) { - super.visitJetElement(element) - if (element is T) { - block(element) - } +public inline fun forEachDescendantOfTypeVisitor(noinline block: (T) -> Unit): JetVisitorVoid { + return object : JetTreeVisitorVoid() { + override fun visitJetElement(element: JetElement) { + super.visitJetElement(element) + if (element is T) { + block(element) } } + } +} -inline fun flatMapDescendantsOfTypeVisitor( - accumulator: MutableCollection, - noinline map: (T) -> Collection -): JetVisitorVoid = forEachDescendantOfTypeVisitor { accumulator.addAll(map(it)) } +public inline fun flatMapDescendantsOfTypeVisitor(accumulator: MutableCollection, noinline map: (T) -> Collection): JetVisitorVoid { + return forEachDescendantOfTypeVisitor { accumulator.addAll(map(it)) } +} -inline fun PsiElement.forEachDescendantsOfType(noinline block: (T) -> Unit) = - accept(forEachDescendantOfTypeVisitor(block)) - -inline fun PsiElement.anyDescendantOfType(noinline predicate: (T) -> Boolean): Boolean { - var result = false - accept(forEachDescendantOfTypeVisitor { - if (!result && predicate(it)) { - result = true +public inline fun PsiElement.forEachDescendantOfType(noinline action: (T) -> Unit) { + this.accept(object : PsiRecursiveElementVisitor(){ + override fun visitElement(element: PsiElement) { + super.visitElement(element) + if (element is T) { + action(element) + } } }) +} + +public inline fun PsiElement.anyDescendantOfType(noinline predicate: (T) -> Boolean): Boolean { + var result = false + this.accept(object : PsiRecursiveElementVisitor(){ + override fun visitElement(element: PsiElement) { + if (result) return + if (element is T && predicate(element)) { + result = true + return + } + super.visitElement(element) + } + }) + return result +} + +public inline fun PsiElement.collectDescendantsOfType(noinline predicate: (T) -> Boolean = { true }): Collection { + val result = ArrayList() + forEachDescendantOfType { + if (predicate(it)) { + result.add(it) + } + } return result } @@ -636,21 +657,3 @@ public fun JetExpression.getAnnotationEntries(): List { else -> emptyList() } } - -// TODO: it can be default value for parameter but it's not supported yet by the compiler -public inline fun PsiElement.collectElementsOfType(): Collection { - return collectElementsOfType { true } -} - -public inline fun PsiElement.collectElementsOfType(@inlineOptions(InlineOption.ONLY_LOCAL_RETURN) predicate: (TElement) -> Boolean): Collection { - val result = ArrayList() - this.accept(object : PsiRecursiveElementVisitor(){ - override fun visitElement(element: PsiElement) { - if (element is TElement && predicate(element)) { - result.add(element) - } - super.visitElement(element) - } - }) - return result -} diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/OptionalParametersHelper.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/OptionalParametersHelper.kt new file mode 100644 index 00000000000..b0a5b57d308 --- /dev/null +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/OptionalParametersHelper.kt @@ -0,0 +1,135 @@ +/* + * 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.core + +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.psi.JetParameter +import org.jetbrains.kotlin.psi.JetSimpleNameExpression +import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.psi.psiUtil.copied +import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import java.util.ArrayList +import java.util.HashMap + +public object OptionalParametersHelper { + public fun detectArgumentsToDropForDefaults( + resolvedCall: ResolvedCall, + project: Project, + canDrop: (ValueArgument) -> Boolean = { true } + ): Collection { + if (!resolvedCall.getStatus().isSuccess()) return emptyList() + val descriptor = resolvedCall.getResultingDescriptor() + + val parameterToDefaultValue = descriptor.getValueParameters() + .map { parameter -> defaultParameterValue(parameter, project)?.let { parameter to it } } + .filterNotNull() + .toMap() + if (parameterToDefaultValue.isEmpty()) return emptyList() + + val arguments = resolvedCall.getCall().getValueArguments() + val argumentsToDrop = ArrayList() + for (argument in arguments.reverse()) { + if (!canDrop(argument)) break // TODO: not correct because of named arguments + if (!argument.matchesDefault(resolvedCall, parameterToDefaultValue)) break // TODO: not correct because of named arguments + argumentsToDrop.add(argument) + } + return argumentsToDrop + } + + private fun ValueArgument.matchesDefault(resolvedCall: ResolvedCall, parameterToDefaultValue: Map): Boolean { + val parameter = resolvedCall.getParameterForArgument(this) ?: return false + val defaultValue = parameterToDefaultValue[parameter] ?: return false + val expression = defaultValue.substituteArguments(resolvedCall) + val argumentExpression = getArgumentExpression()!! + return argumentExpression.getText() == expression.getText() //TODO + } + + private fun DefaultValue.substituteArguments(resolvedCall: ResolvedCall): JetExpression { + if (parameterUsages.isEmpty()) return expression + + val key = Key("SUBSTITUTION") + + for ((parameter, usages) in parameterUsages) { + val resolvedArgument = resolvedCall.getValueArguments()[parameter]!! + if (resolvedArgument is ExpressionValueArgument) { + val argument = resolvedArgument.getValueArgument()!!.getArgumentExpression()!! + usages.forEach { it.putCopyableUserData(key, argument) } + } + //TODO: vararg + } + + var expressionCopy = expression.copied() + + expression.forEachDescendantOfType { it.putCopyableUserData(key, null) } + + val replacements = ArrayList>() + expressionCopy.forEachDescendantOfType { + val replacement = it.getCopyableUserData(key) + if (replacement != null) { + replacements.add(it to replacement) + } + } + + for ((expression, replacement) in replacements) { + val replaced = expression.replace(replacement) as JetExpression + if (expression == expressionCopy) { + expressionCopy = replaced + } + } + + return expressionCopy + } + + public data class DefaultValue( + public val expression: JetExpression, + public val parameterUsages: Map> + ) + + //TODO: handle imports + //TODO: handle implicit receivers + public fun defaultParameterValue(parameter: ValueParameterDescriptor, project: Project): DefaultValue? { + if (!parameter.hasDefaultValue()) return null + //TODO: parameter in overriding method! + val sourceParameter = DescriptorToSourceUtilsIde.getAnyDeclaration(project, parameter) as? JetParameter ?: return null + //TODO: use JetSourceNavigationHelper + val expression = sourceParameter.getDefaultValue() ?: return null + + val allParameters = (parameter.getContainingDeclaration() as CallableDescriptor).getValueParameters().toSet() + + val parameterUsages = HashMap>() + + val bindingContext = expression.analyze() + expression.forEachDescendantOfType { + val target = bindingContext[BindingContext.REFERENCE_TARGET, it] + if (target is ValueParameterDescriptor && target in allParameters) { + parameterUsages.getOrPut(target) { ArrayList() }.add(it) + } + } + + return DefaultValue(expression, parameterUsages) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt index 508eeb53e70..62700e709a4 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt @@ -16,13 +16,11 @@ package org.jetbrains.kotlin.idea.quickfix -import com.intellij.codeInsight.intention.HighPriorityAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile -import com.intellij.psi.PsiRecursiveElementVisitor import org.jetbrains.kotlin.analyzer.analyzeInContext import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor @@ -30,6 +28,7 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.core.OptionalParametersHelper import org.jetbrains.kotlin.idea.core.asExpression import org.jetbrains.kotlin.idea.core.refactoring.JetNameSuggester import org.jetbrains.kotlin.idea.core.refactoring.JetNameValidator @@ -39,17 +38,18 @@ import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.idea.util.ShortenReferences import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.renderName import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.collectElementsOfType -import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression -import org.jetbrains.kotlin.psi.psiUtil.isAncestor -import org.jetbrains.kotlin.psi.psiUtil.replaced +import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension @@ -122,7 +122,6 @@ public abstract class DeprecatedSymbolUsageFixBase( resolvedCall: ResolvedCall, replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression ): JetExpression { - var (expression, imports, parameterUsages) = replacement val project = element.getProject() val psiFactory = JetPsiFactory(project) val descriptor = resolvedCall.getResultingDescriptor() @@ -131,87 +130,100 @@ public abstract class DeprecatedSymbolUsageFixBase( val qualifiedExpression = callExpression.getParent() as? JetQualifiedExpression val expressionToReplace = qualifiedExpression ?: callExpression - var receiver = element.getReceiverExpression() - receiver?.putCopyableUserData(USER_CODE_KEY, Unit) + var receiver = element.getReceiverExpression()?.marked(USER_CODE_KEY) + var receiverType = if (receiver != null) bindingContext.getType(receiver) else null if (receiver == null) { val receiverValue = if (descriptor.isExtension) resolvedCall.getExtensionReceiver() else resolvedCall.getDispatchReceiver() val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, expressionToReplace] if (receiverValue is ThisReceiver && resolutionScope != null) { receiver = receiverValue.asExpression(resolutionScope, psiFactory) + receiverType = receiverValue.getType() } } - receiver?.putCopyableUserData(FROM_THIS_KEY, Unit) + receiver?.mark(RECEIVER_VALUE_KEY) + + for ((parameter, usages) in replacement.parameterUsages.entrySet()) { + usages.forEach { it.put(PARAMETER_USAGE_KEY, parameter) } + } + + var expression = replacement.expression //TODO: this@ - for (thisExpression in expression.collectElementsOfType()) { + for (thisExpression in expression.collectDescendantsOfType()) { if (receiver != null) { thisExpression.replace(receiver) } else { - thisExpression.putCopyableUserData(FROM_THIS_KEY, Unit) + thisExpression.mark(RECEIVER_VALUE_KEY) } } - fun argumentForParameter(parameter: ValueParameterDescriptor): JetExpression? { - //TODO: optional parameters - val arguments = resolvedCall.getValueArguments()[parameter] ?: return null //TODO: what if not? vararg? - return arguments.getArguments().firstOrNull()?.getArgumentExpression() //TODO: what if multiple? - } + @data class IntroduceValueForParameter( + val parameter: ValueParameterDescriptor, + val value: JetExpression, + val valueType: JetType?) - val introduceValuesForParameters = ArrayList>() + val introduceValuesForParameters = ArrayList() - for (parameter in descriptor.getValueParameters()) { - val argument = argumentForParameter(parameter) ?: continue - argument.putCopyableUserData(FROM_PARAMETER_KEY, parameter) - argument.putCopyableUserData(USER_CODE_KEY, Unit) + // process parameters in reverse order because default values can use previous parameters + for (parameter in descriptor.getValueParameters().reverse()) { + val argument = argumentForParameter(parameter, resolvedCall, bindingContext, project) ?: continue - val usages = parameterUsages[parameter.getOriginal()]!! - usages.forEach { it.replace(argument) } + argument.expression.put(PARAMETER_VALUE_KEY, parameter) - if (argument.shouldKeepValue(usages.size())) { - introduceValuesForParameters.add(parameter to argument) + val originalParameter = parameter.getOriginal() + val usages = expression.collectDescendantsOfType { it[PARAMETER_USAGE_KEY] == originalParameter } + usages.forEach { it.replace(argument.wrapped) } + + if (argument.expression.shouldKeepValue(usages.size())) { + introduceValuesForParameters.add(IntroduceValueForParameter(parameter, argument.expression, argument.expressionType)) } } + unwrapDefaultValues(expression) + if (qualifiedExpression is JetSafeQualifiedExpression) { - expression = expression.wrapExpressionForSafeCall(expressionToReplace, receiver!!, bindingContext) + expression = expression.wrapExpressionForSafeCall(expressionToReplace, receiver!!, receiverType, bindingContext) } else if (callExpression is JetBinaryExpression && callExpression.getOperationToken() == JetTokens.IDENTIFIER) { expression = expression.keepInfixFormIfPossible() } if (receiver != null) { - val thisReplaced = expression.collectElementsOfType { it.getCopyableUserData(FROM_THIS_KEY) != null } + val thisReplaced = expression.collectDescendantsOfType { it[RECEIVER_VALUE_KEY] } if (receiver.shouldKeepValue(thisReplaced.size())) { - expression = expression.introduceValue(receiver, expressionToReplace, bindingContext, thisReplaced) + expression = expression.introduceValue(receiver, receiverType, expressionToReplace, bindingContext, thisReplaced) } } - for ((parameter, value) in introduceValuesForParameters) { - val usagesReplaced = expression.collectElementsOfType { it.getCopyableUserData(FROM_PARAMETER_KEY) == parameter } - expression = expression.introduceValue(value, expressionToReplace, bindingContext, usagesReplaced, nameSuggestion = parameter.getName().asString()) + for ((parameter, value, valueType) in introduceValuesForParameters) { + val usagesReplaced = expression.collectDescendantsOfType { it[PARAMETER_VALUE_KEY] == parameter } + expression = expression.introduceValue(value, valueType, expressionToReplace, bindingContext, usagesReplaced, nameSuggestion = parameter.getName().asString()) } - var result = expressionToReplace.replaced(expression) + var result = expressionToReplace.replace(expression) as JetExpression //TODO: drop import of old function (if not needed anymore)? val file = result.getContainingJetFile() - for (importFqName in imports) { + for (importFqName in replacement.imports) { val descriptors = file.getResolutionFacade().resolveImportReference(file, importFqName) val descriptorToImport = descriptors.firstOrNull() ?: continue ImportInsertHelper.getInstance(project).importDescriptor(file, descriptorToImport) } + //TODO: do this earlier + dropArgumentsForDefaultValues(result) + val shortenFilter = { element: PsiElement -> - if (element.getCopyableUserData(USER_CODE_KEY) != null) { + if (element[USER_CODE_KEY]) { ShortenReferences.FilterResult.SKIP } else { val thisReceiver = (element as? JetQualifiedExpression)?.getReceiverExpression() as? JetThisExpression - if (thisReceiver != null && thisReceiver.getCopyableUserData(USER_CODE_KEY) != null) // don't remove explicit 'this' coming from user's code + if (thisReceiver != null && thisReceiver[USER_CODE_KEY]) // don't remove explicit 'this' coming from user's code ShortenReferences.FilterResult.GO_INSIDE else ShortenReferences.FilterResult.PROCESS @@ -220,13 +232,13 @@ public abstract class DeprecatedSymbolUsageFixBase( result = ShortenReferences({ ShortenReferences.Options(removeThis = true) }).process(result, shortenFilter) as JetExpression // clean up user data - result.accept(object : PsiRecursiveElementVisitor() { - override fun visitElement(element: PsiElement) { - element.putCopyableUserData(USER_CODE_KEY, null) - element.putCopyableUserData(FROM_PARAMETER_KEY, null) - element.putCopyableUserData(FROM_THIS_KEY, null) - } - }) + result.forEachDescendantOfType { + it.clear(USER_CODE_KEY) + it.clear(PARAMETER_USAGE_KEY) + it.clear(PARAMETER_VALUE_KEY) + it.clear(RECEIVER_VALUE_KEY) + it.clear(DEFAULT_PARAMETER_VALUE_KEY) + } return result } @@ -234,12 +246,13 @@ public abstract class DeprecatedSymbolUsageFixBase( private fun JetExpression.wrapExpressionForSafeCall( expressionToReplace: JetExpression, receiver: JetExpression, + receiverType: JetType?, bindingContext: BindingContext ): JetExpression { val psiFactory = JetPsiFactory(this) val qualified = this as? JetQualifiedExpression if (qualified != null) { - if (qualified.getReceiverExpression().getCopyableUserData(FROM_THIS_KEY) != null) { + if (qualified.getReceiverExpression()[RECEIVER_VALUE_KEY]) { if (qualified is JetSafeQualifiedExpression) return this // already safe val selector = qualified.getSelectorExpression() if (selector != null) { @@ -249,8 +262,8 @@ public abstract class DeprecatedSymbolUsageFixBase( } if (expressionToReplace.isUsedAsExpression(bindingContext)) { - val thisReplaced = this.collectElementsOfType { it.getCopyableUserData(FROM_THIS_KEY) != null } - return this.introduceValue(receiver, expressionToReplace, bindingContext, thisReplaced, safeCall = true) + val thisReplaced = this.collectDescendantsOfType { it[RECEIVER_VALUE_KEY] } + return this.introduceValue(receiver, receiverType, expressionToReplace, bindingContext, thisReplaced, safeCall = true) } else { return psiFactory.createExpressionByPattern("if ($0 != null) { $1 }", receiver, this) @@ -260,7 +273,7 @@ public abstract class DeprecatedSymbolUsageFixBase( private fun JetExpression.keepInfixFormIfPossible(): JetExpression { if (this !is JetDotQualifiedExpression) return this val receiver = getReceiverExpression() - if (receiver.getCopyableUserData(FROM_THIS_KEY) == null) return this + if (!receiver[RECEIVER_VALUE_KEY]) return this val call = getSelectorExpression() as? JetCallExpression ?: return this val nameExpression = call.getCalleeExpression() as? JetSimpleNameExpression ?: return this val argument = call.getValueArguments().singleOrNull() ?: return this @@ -271,6 +284,7 @@ public abstract class DeprecatedSymbolUsageFixBase( private fun JetExpression.introduceValue( value: JetExpression, + valueType: JetType?, insertDeclarationsBefore: JetExpression, bindingContext: BindingContext, usages: Collection, @@ -281,7 +295,7 @@ public abstract class DeprecatedSymbolUsageFixBase( val psiFactory = JetPsiFactory(this) - fun nameInCode(name: String) = IdeDescriptorRenderers.SOURCE_CODE.renderName(Name.identifier(name)) + fun nameInCode(name: String) = Name.identifier(name).renderName() fun replaceUsages(name: String) { val nameInCode = psiFactory.createExpression(nameInCode(name)) @@ -306,7 +320,6 @@ public abstract class DeprecatedSymbolUsageFixBase( val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, insertDeclarationsBefore] if (usages.isNotEmpty()) { - val valueType = bindingContext.getType(value) var explicitType: JetType? = null if (valueType != null && !ErrorUtils.containsErrorType(valueType)) { val valueTypeWithoutExpectedType = value.analyzeInContext( @@ -358,7 +371,7 @@ public abstract class DeprecatedSymbolUsageFixBase( } private fun collectNameUsages(scope: JetExpression, name: String) - = scope.collectElementsOfType { it.getReceiverExpression() == null && it.getReferencedName() == name } + = scope.collectDescendantsOfType { it.getReceiverExpression() == null && it.getReferencedName() == name } private fun JetExpression?.shouldKeepValue(usageCount: Int): Boolean { if (usageCount == 1) return false @@ -381,8 +394,108 @@ public abstract class DeprecatedSymbolUsageFixBase( } } + private class Argument( + val expression: JetExpression, + val wrapped: JetExpression, + val expressionType: JetType?, + val isDefaultValue: Boolean) + + private fun argumentForParameter( + parameter: ValueParameterDescriptor, + resolvedCall: ResolvedCall, + bindingContext: BindingContext, + project: Project): Argument? { + //TODO: named parameters - keep named form if makes sense + //TODO: keep functional literal argument form + val resolvedArgument = resolvedCall.getValueArguments()[parameter]!! + when (resolvedArgument) { + is ExpressionValueArgument -> { + val expression = resolvedArgument.getValueArgument()!!.getArgumentExpression()!! + expression.mark(USER_CODE_KEY) + return Argument(expression, expression, bindingContext.getType(expression), isDefaultValue = false) + } + + is DefaultValueArgument -> { + val defaultValue = OptionalParametersHelper.defaultParameterValue(parameter, project) ?: return null + val (expression, parameterUsages) = defaultValue + + for ((param, usages) in parameterUsages) { + usages.forEach { it.put(PARAMETER_USAGE_KEY, param) } + } + + // we temporary wrap default values into parenthesis so that we can safely mark them with DEFAULT_PARAMETER_VALUE_KEY + val wrapped = JetPsiFactory(project).createExpressionByPattern("($0)", expression) as JetParenthesizedExpression + wrapped.mark(DEFAULT_PARAMETER_VALUE_KEY) + + // clean up user data in original + expression.forEachDescendantOfType { it.clear(PARAMETER_USAGE_KEY) } + + return Argument(wrapped.getExpression()!!, wrapped, null/*TODO*/, isDefaultValue = true) + } + + is VarargValueArgument -> /*TODO*/ return null + + else -> error("Unknown argument type: $resolvedArgument") + } + } + + private fun dropArgumentsForDefaultValues(result: JetExpression) { + val project = result.getProject() + val newBindingContext = result.analyze() + val argumentsToDrop = ArrayList() + + // 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] + + //TODO: other types of calls + result.forEachDescendantOfType { callExpression -> + val resolvedCall = callExpression.getResolvedCall(newBindingContext) ?: return@forEachDescendantOfType + + argumentsToDrop.addAll(OptionalParametersHelper.detectArgumentsToDropForDefaults(resolvedCall, project, ::canDropArgument)) + } + + for (argument in argumentsToDrop) { + argument as JetValueArgument + val argumentList = argument.getParent() as JetValueArgumentList + argumentList.removeArgument(argument) + } + } + + private fun unwrapDefaultValues(expression: JetExpression) { + val values = expression.collectDescendantsOfType { + 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() } + } + + //TODO: making functions below private causes VerifyError + fun PsiElement.get(key: Key): T? = getCopyableUserData(key) + fun PsiElement.get(key: Key): Boolean = getCopyableUserData(key) != null + fun JetExpression.clear(key: Key) = putCopyableUserData(key, null) + fun JetExpression.put(key: Key, value: T) = putCopyableUserData(key, value) + fun JetExpression.mark(key: Key) = putCopyableUserData(key, Unit) + fun T.marked(key: Key): T { + putCopyableUserData(key, Unit) + return this + } + private val USER_CODE_KEY = Key("USER_CODE") - private val FROM_PARAMETER_KEY = Key("FROM_PARAMETER") - private val FROM_THIS_KEY = Key("FROM_THIS") + private val PARAMETER_USAGE_KEY = Key("PARAMETER_USAGE") + private val PARAMETER_VALUE_KEY = Key("PARAMETER_VALUE") + private val RECEIVER_VALUE_KEY = Key("RECEIVER_VALUE") + private val DEFAULT_PARAMETER_VALUE_KEY = Key("DEFAULT_PARAMETER_VALUE") } -} \ No newline at end of file +} + diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveNameFromFunctionExpressionFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveNameFromFunctionExpressionFix.kt index df52e741b55..58f195705b2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveNameFromFunctionExpressionFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveNameFromFunctionExpressionFix.kt @@ -26,7 +26,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantsOfType +import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments import org.jetbrains.kotlin.resolve.BindingContext @@ -63,7 +63,7 @@ public class RemoveNameFromFunctionExpressionFix(element: JetNamedFunction) : Je var wereAutoLabelUsages = false val name = function.getName() ?: return - function.forEachDescendantsOfType { + function.forEachDescendantOfType { if (!wereAutoLabelUsages && it.getLabelName() == name) { wereAutoLabelUsages = it.analyze().get(BindingContext.LABEL_TARGET, it.getTargetLabel()) == function } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceWithAnnotationAnalyzer.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceWithAnnotationAnalyzer.kt index a6110359fcb..af6a0cc0df8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceWithAnnotationAnalyzer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceWithAnnotationAnalyzer.kt @@ -32,7 +32,7 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.collectElementsOfType +import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression import org.jetbrains.kotlin.psi.psiUtil.replaced import org.jetbrains.kotlin.resolve.BindingContext @@ -144,7 +144,7 @@ object ReplaceWithAnnotationAnalyzer { } val parameterUsages = symbolDescriptor.getValueParameters() - .map { parameter -> parameter to expression.collectElementsOfType { it.getCopyableUserData(parameterUsageKey) == parameter } } + .map { parameter -> parameter to expression.collectDescendantsOfType { it.getCopyableUserData(parameterUsageKey) == parameter } } .toMap() expression.accept(object : PsiRecursiveElementVisitor() { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/common.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/common.kt index 9e7424563ba..3f89aa9472d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/common.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/common.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.ShortenReferences import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantsOfType +import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -66,7 +66,7 @@ private fun renderClassNameForKClassLiteral(type: JetType): String? { fun createReplacementTasks(element: JetElement, anyJavaClass: Boolean = false): List { val replacementTasks = arrayListOf() - element.forEachDescendantsOfType(fun(expression: JetCallExpression) { + element.forEachDescendantOfType(fun(expression: JetCallExpression) { val context = expression.analyze() val resolvedCall = expression.getResolvedCall(context) ?: return @@ -94,7 +94,7 @@ fun createReplacementTasksForAnnotationClass(element: JetClass): ListoldFun("a") +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters1.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters1.kt.after new file mode 100644 index 00000000000..5d888105ef8 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters1.kt.after @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p1, p2.toString(), p3, p4, p5)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2.toString(), p3, p4, p5)")) + fun oldFun(p1: String, p2: Int = 0, p3: String? = null, p4: String = p1, p5: Int = p2) + + fun newFun(p1: String, p2: String, p3: String?, p4: String, p5: Int) +} + +fun foo(i: I) { + i.newFun("a", 0.toString(), null, "a", 0) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters2.kt b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters2.kt new file mode 100644 index 00000000000..91b5c8c5641 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters2.kt @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p1, p2.toString(), p3)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2.toString(), p3)")) + fun oldFun(p1: String, p2: Int = 0, p3: String? = null) + + fun newFun(p1: String, p2: String, p3: String? = null) +} + +fun foo(i: I) { + i.oldFun("a") +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters2.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters2.kt.after new file mode 100644 index 00000000000..188df757d4d --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters2.kt.after @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p1, p2.toString(), p3)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2.toString(), p3)")) + fun oldFun(p1: String, p2: Int = 0, p3: String? = null) + + fun newFun(p1: String, p2: String, p3: String? = null) +} + +fun foo(i: I) { + i.newFun("a", 0.toString()) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters3.kt b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters3.kt new file mode 100644 index 00000000000..d662dea247f --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters3.kt @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p1, p2.toString(), p3)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2.toString(), p3)")) + fun oldFun(p1: String, p2: Int, p3: String?) + + fun newFun(p1: String, p2: String = "", p3: String? = null) +} + +fun foo(i: I) { + i.oldFun("a", 0, null) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters3.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters3.kt.after new file mode 100644 index 00000000000..c448ee97852 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters3.kt.after @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p1, p2.toString(), p3)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2.toString(), p3)")) + fun oldFun(p1: String, p2: Int, p3: String?) + + fun newFun(p1: String, p2: String = "", p3: String? = null) +} + +fun foo(i: I) { + i.newFun("a", 0.toString(), null) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters4.kt b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters4.kt new file mode 100644 index 00000000000..444fc911537 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters4.kt @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p1, p2, p3)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2, p3)")) + fun oldFun(p1: String, p2: Int = p1.length(), p3: String? = p1) + + fun newFun(x: String, y: Int = x.length(), z: String? = "a") +} + +fun foo(i: I) { + i.oldFun("a") +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters4.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters4.kt.after new file mode 100644 index 00000000000..e30715ee44e --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters4.kt.after @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p1, p2, p3)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2, p3)")) + fun oldFun(p1: String, p2: Int = p1.length(), p3: String? = p1) + + fun newFun(x: String, y: Int = x.length(), z: String? = "a") +} + +fun foo(i: I) { + i.newFun("a") +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters5.kt b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters5.kt new file mode 100644 index 00000000000..af7b60cca72 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters5.kt @@ -0,0 +1,14 @@ +// "Replace with 'newFun(p1, p2)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2)")) + fun oldFun(p1: String, p2: String = p1) + + fun newFun(p1: String, p2: String) +} + +fun foo(i: I) { + i.oldFun(bar()) +} + +fun bar(): String = "" diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters5.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters5.kt.after new file mode 100644 index 00000000000..625c62e0e22 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters5.kt.after @@ -0,0 +1,15 @@ +// "Replace with 'newFun(p1, p2)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2)")) + fun oldFun(p1: String, p2: String = p1) + + fun newFun(p1: String, p2: String) +} + +fun foo(i: I) { + val p1 = bar() + i.newFun(p1, p1) +} + +fun bar(): String = "" diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters6.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters6.kt.after new file mode 100644 index 00000000000..c569d84d3ae --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters6.kt.after @@ -0,0 +1,14 @@ +// "Replace with 'newFun(p1, p2)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2)")) + fun oldFun(p1: String, p2: String = p1) + + fun newFun(p1: String, p2: String = p2) +} + +fun foo(i: I) { + i.newFun(bar()) +} + +fun bar(): String = "" diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters6.kt.todo b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters6.kt.todo new file mode 100644 index 00000000000..44ec947572d --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters6.kt.todo @@ -0,0 +1,14 @@ +// "Replace with 'newFun(p1, p2)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2)")) + fun oldFun(p1: String, p2: String = p1) + + fun newFun(p1: String, p2: String = p2) +} + +fun foo(i: I) { + i.oldFun(bar()) +} + +fun bar(): String = "" diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters7.kt b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters7.kt new file mode 100644 index 00000000000..a570474a568 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters7.kt @@ -0,0 +1,14 @@ +// "Replace with 'newFun(p1, p2, p3)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2, p3)")) + fun oldFun(p1: String, p2: String = bar(), p3: String = p2) + + fun newFun(p1: String, p2: String, p3: String) +} + +fun foo(i: I) { + i.oldFun("") +} + +fun bar(): String = "" diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters7.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters7.kt.after new file mode 100644 index 00000000000..a4403ae64cc --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters7.kt.after @@ -0,0 +1,15 @@ +// "Replace with 'newFun(p1, p2, p3)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2, p3)")) + fun oldFun(p1: String, p2: String = bar(), p3: String = p2) + + fun newFun(p1: String, p2: String, p3: String) +} + +fun foo(i: I) { + val p2 = bar() + i.newFun("", p2, p2) +} + +fun bar(): String = "" diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters8.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters8.kt.after new file mode 100644 index 00000000000..c73e6910413 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters8.kt.after @@ -0,0 +1,14 @@ +// "Replace with 'newFun(p1, p2, p3)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2, p3)")) + fun oldFun(p1: String, p2: String = bar(), p3: String = p2) + + fun newFun(p1: String, p2: String, p3: String = p2) +} + +fun foo(i: I) { + i.newFun("", bar()) +} + +fun bar(): String = "" diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters8.kt.todo b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters8.kt.todo new file mode 100644 index 00000000000..a69ff18c03b --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters8.kt.todo @@ -0,0 +1,14 @@ +// "Replace with 'newFun(p1, p2, p3)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p2, p3)")) + fun oldFun(p1: String, p2: String = bar(), p3: String = p2) + + fun newFun(p1: String, p2: String, p3: String = p2) +} + +fun foo(i: I) { + i.oldFun("") +} + +fun bar(): String = "" diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters9.kt b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters9.kt new file mode 100644 index 00000000000..b55cc6b124f --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters9.kt @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p1, p3, p2)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p3, p2)")) + fun oldFun(p1: String, p2: String = "", p3: Int = -1) + + fun newFun(p1: String, p2: Int = -1, p3: String = "") +} + +fun foo(i: I) { + i.oldFun("") +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters9.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters9.kt.after new file mode 100644 index 00000000000..f42437706ce --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters9.kt.after @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p1, p3, p2)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(p1, p3, p2)")) + fun oldFun(p1: String, p2: String = "", p3: Int = -1) + + fun newFun(p1: String, p2: Int = -1, p3: String = "") +} + +fun foo(i: I) { + i.newFun("") +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index bc9b4992c7c..5fed7d65e5b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -3124,6 +3124,48 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("optionalParameters1.kt") + public void testOptionalParameters1() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters1.kt"); + doTest(fileName); + } + + @TestMetadata("optionalParameters2.kt") + public void testOptionalParameters2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters2.kt"); + doTest(fileName); + } + + @TestMetadata("optionalParameters3.kt") + public void testOptionalParameters3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters3.kt"); + doTest(fileName); + } + + @TestMetadata("optionalParameters4.kt") + public void testOptionalParameters4() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters4.kt"); + doTest(fileName); + } + + @TestMetadata("optionalParameters5.kt") + public void testOptionalParameters5() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters5.kt"); + doTest(fileName); + } + + @TestMetadata("optionalParameters7.kt") + public void testOptionalParameters7() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters7.kt"); + doTest(fileName); + } + + @TestMetadata("optionalParameters9.kt") + public void testOptionalParameters9() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters9.kt"); + doTest(fileName); + } + @TestMetadata("parameters.kt") public void testParameters() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/parameters.kt");