From a773542d47e65539020807bf0cfde5892248223e Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 19 May 2015 12:57:58 +0300 Subject: [PATCH] DeprecatedSymbolUsageFix - handling of complex expressions used twice --- .../idea/quickfix/DeprecatedSymbolUsageFix.kt | 120 +++++++++++++++--- .../complexExpressionUsedTwice.kt | 14 ++ .../complexExpressionUsedTwice.kt.after | 15 +++ .../complexExpressionUsedTwice2.kt | 15 +++ .../complexExpressionUsedTwice2.kt.after | 16 +++ .../complexExpressionUsedTwice3Runtime.kt | 13 ++ ...omplexExpressionUsedTwice3Runtime.kt.after | 13 ++ .../complexExpressionUsedTwice4.kt | 16 +++ .../complexExpressionUsedTwice4.kt.after | 17 +++ .../complexExpressionUsedTwice5Runtime.kt | 16 +++ ...omplexExpressionUsedTwice5Runtime.kt.after | 17 +++ .../simpleExpressionUsedTwice.kt | 16 +++ .../simpleExpressionUsedTwice.kt.after | 16 +++ .../stringLiteralUsedTwice.kt | 12 ++ .../stringLiteralUsedTwice.kt.after | 12 ++ .../stringTemplateUsedTwice.kt | 12 ++ .../stringTemplateUsedTwice.kt.after | 13 ++ .../stringTemplateUsedTwice2.kt | 14 ++ .../stringTemplateUsedTwice2.kt.after | 15 +++ .../idea/quickfix/QuickFixTestGenerated.java | 54 ++++++++ 20 files changed, 419 insertions(+), 17 deletions(-) create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice2.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice2.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice3Runtime.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice3Runtime.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice4.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice4.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice5Runtime.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice5Runtime.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/simpleExpressionUsedTwice.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/simpleExpressionUsedTwice.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/stringLiteralUsedTwice.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/stringLiteralUsedTwice.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice2.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice2.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFix.kt index 48130db2f7e..20590d818a5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFix.kt @@ -23,7 +23,6 @@ import com.intellij.openapi.util.Key import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiRecursiveElementVisitor -import com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.analyzer.analyzeInContext import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor @@ -36,9 +35,11 @@ import org.jetbrains.kotlin.idea.core.refactoring.JetNameSuggester import org.jetbrains.kotlin.idea.core.refactoring.JetNameValidator import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport import org.jetbrains.kotlin.idea.imports.importableFqName +import org.jetbrains.kotlin.idea.intentions.setType import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers 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.FqName import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name @@ -46,10 +47,10 @@ import org.jetbrains.kotlin.psi.* 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.siblings import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil +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.constants.CompileTimeConstant @@ -57,6 +58,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.scopes.* import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver +import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.utils.Printer import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull @@ -132,13 +135,20 @@ public class DeprecatedSymbolUsageFix( return arguments.getArguments().firstOrNull()?.getArgumentExpression() //TODO: what if multiple? } - //TODO: check if complex expressions are used twice - //TODO: check for dropping complex expressions + val introduceValuesForParameters = ArrayList>() + + //TODO: check for dropping expressions with potential side effects for (parameter in descriptor.getValueParameters()) { val argument = argumentForParameter(parameter) ?: continue argument.putCopyableUserData(FROM_PARAMETER_KEY, parameter) argument.putCopyableUserData(USER_CODE_KEY, Unit) - parameterUsages[parameter.getOriginal()]!!.forEach { it.replace(argument) } + + val usages = parameterUsages[parameter.getOriginal()]!! + usages.forEach { it.replace(argument) } + + if (usages.size() > 1 && argument.shouldIntroduceVariableIfUsedTwice()) { + introduceValuesForParameters.add(parameter to argument) + } } if (qualifiedExpression is JetSafeQualifiedExpression) { @@ -156,7 +166,7 @@ public class DeprecatedSymbolUsageFix( if (expressionToReplace.isUsedAsExpression(bindingContext)) { val thisReplaced = expression.collectExpressionsWithData(FROM_THIS_KEY, Unit) - expression = expression.introduceValue(explicitReceiver!!, thisReplaced, safeCall = true) + expression = expression.introduceValue(explicitReceiver!!, expressionToReplace, bindingContext, thisReplaced, safeCall = true) } else { expression = psiFactory.createExpressionByPattern("if ($0 != null) { $1 }", explicitReceiver!!, expression) @@ -165,6 +175,19 @@ public class DeprecatedSymbolUsageFix( processSafeCall() } + if (explicitReceiver != null && explicitReceiver.shouldIntroduceVariableIfUsedTwice()) { + val thisReplaced = expression.collectExpressionsWithData(FROM_THIS_KEY, Unit) + if (thisReplaced.size() > 1) { + expression = expression.introduceValue(explicitReceiver, expressionToReplace, bindingContext, thisReplaced) + } + } + + for ((parameter, value) in introduceValuesForParameters) { + val usagesReplaced = expression.collectExpressionsWithData(FROM_PARAMETER_KEY, parameter) + assert(usagesReplaced.size() > 1) + expression = expression.introduceValue(value, expressionToReplace, bindingContext, usagesReplaced, nameSuggestion = parameter.getName().asString()) + } + var result = expressionToReplace.replaced(expression) //TODO: drop import of old function (if not needed anymore)? @@ -175,12 +198,12 @@ public class DeprecatedSymbolUsageFix( ImportInsertHelper.getInstance(project).importDescriptor(file, descriptorToImport) } - val shortenFilter = { it: PsiElement -> - if (it.getCopyableUserData(USER_CODE_KEY) != null) { + val shortenFilter = { element: PsiElement -> + if (element.getCopyableUserData(USER_CODE_KEY) != null) { ShortenReferences.FilterResult.SKIP } else { - val thisReceiver = (it as? JetQualifiedExpression)?.getReceiverExpression() as? JetThisExpression + 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 ShortenReferences.FilterResult.GO_INSIDE else @@ -324,7 +347,14 @@ public class DeprecatedSymbolUsageFix( } } - private fun JetExpression.introduceValue(value: JetExpression, usages: Collection, safeCall: Boolean): JetExpression { + private fun JetExpression.introduceValue( + value: JetExpression, + insertDeclarationsBefore: JetExpression, + bindingContext: BindingContext, + usages: Collection, + nameSuggestion: String? = null, + safeCall: Boolean = false + ): JetExpression { assert(usages.all { isAncestor(it, strict = true) }) val psiFactory = JetPsiFactory(this) @@ -338,19 +368,62 @@ public class DeprecatedSymbolUsageFix( } } - val dot = if (safeCall) "?." else "." + fun suggestName(validator: JetNameValidator): String { + return if (nameSuggestion != null) + validator.validateName(nameSuggestion) + else + JetNameSuggester.suggestNamesForExpression(value, validator, "t").first() + } - fun isNameUsed(name: String) = collectNameUsages(name).any { nameUsage -> usages.none { it.isAncestor(nameUsage) } } + // checks that name is used (without receiver) inside this expression but not inside usages that will be replaced + fun isNameUsed(name: String) = collectNameUsages(this, name).any { nameUsage -> usages.none { it.isAncestor(nameUsage) } } + + if (!safeCall) { + val block = insertDeclarationsBefore.getParent() as? JetBlockExpression + if (block != null) { + val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, insertDeclarationsBefore] + + val valueType = bindingContext.getType(value) + var explicitType: JetType? = null + if (valueType != null && !ErrorUtils.containsErrorType(valueType)) { + val valueTypeWithoutExpectedType = value.analyzeInContext( + resolutionScope, + dataFlowInfo = bindingContext.getDataFlowInfo(insertDeclarationsBefore) + ).getType(value) + if (valueTypeWithoutExpectedType == null || ErrorUtils.containsErrorType(valueTypeWithoutExpectedType)) { + explicitType = valueType + } + } + + val name = suggestName(object : JetNameValidator() { + override fun validateInner(name: String): Boolean { + return resolutionScope.getLocalVariable(Name.identifier(name)) == null && !isNameUsed(name) + } + }) + + var declaration = psiFactory.createDeclaration("val ${nameInCode(name)} = " + value.getText()) + declaration = block.addBefore(declaration, insertDeclarationsBefore) as JetVariableDeclaration + block.addBefore(psiFactory.createNewLine(), insertDeclarationsBefore) + + if (explicitType != null) { + declaration.setType(explicitType) + } + + replaceUsages(name) + return this + } + } + + val dot = if (safeCall) "?." else "." if (!isNameUsed("it")) { replaceUsages("it") return psiFactory.createExpressionByPattern("$0${dot}let { $1 }", value, this) } else { - val nameValidator = object : JetNameValidator() { + val name = suggestName(object : JetNameValidator() { override fun validateInner(name: String) = !isNameUsed(name) - } - val name = JetNameSuggester.suggestNamesForExpression(value, nameValidator, "t").first() + }) replaceUsages(name) return psiFactory.createExpressionByPattern("$0${dot}let { ${nameInCode(name)} -> $1 }", value, this) } @@ -391,9 +464,9 @@ public class DeprecatedSymbolUsageFix( return result } - private fun JetExpression.collectNameUsages(name: String): ArrayList { + private fun collectNameUsages(scope: JetExpression, name: String): ArrayList { val result = ArrayList() - this.accept(object : JetVisitorVoid(){ + scope.accept(object : JetVisitorVoid(){ override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) { if (expression.getReceiverExpression() == null && expression.getReferencedName() == name) { result.add(expression) @@ -407,6 +480,19 @@ public class DeprecatedSymbolUsageFix( return result } + private fun JetExpression?.shouldIntroduceVariableIfUsedTwice(): Boolean { + return when (this) { + is JetSimpleNameExpression -> false + is JetQualifiedExpression -> getReceiverExpression().shouldIntroduceVariableIfUsedTwice() || getSelectorExpression().shouldIntroduceVariableIfUsedTwice() + is JetUnaryExpression -> getOperationToken() in setOf(JetTokens.PLUSPLUS, JetTokens.MINUSMINUS) || getBaseExpression().shouldIntroduceVariableIfUsedTwice() + is JetStringTemplateExpression -> getEntries().any { it is JetStringTemplateEntryWithExpression } + is JetThisExpression, is JetSuperExpression -> false + is JetParenthesizedExpression -> getExpression().shouldIntroduceVariableIfUsedTwice() + is JetBinaryExpression, is JetIfExpression -> true // TODO: discuss it + else -> true // what else it can be? + } + } + companion object : JetSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic): IntentionAction? { val nameExpression = diagnostic.getPsiElement() as? JetSimpleNameExpression ?: return null diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice.kt b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice.kt new file mode 100644 index 00000000000..6feefff9c90 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice.kt @@ -0,0 +1,14 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: Int) { + newFun(p, p) +} + +fun newFun(p1: Int, p2: Int){} + +fun foo() { + oldFun(bar()) +} + +fun bar(): Int = 0 diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice.kt.after new file mode 100644 index 00000000000..d77684b6423 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice.kt.after @@ -0,0 +1,15 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: Int) { + newFun(p, p) +} + +fun newFun(p1: Int, p2: Int){} + +fun foo() { + val p = bar() + newFun(p, p) +} + +fun bar(): Int = 0 diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice2.kt b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice2.kt new file mode 100644 index 00000000000..a8a09ac2544 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice2.kt @@ -0,0 +1,15 @@ +// "Replace with 'newFun(this)'" "true" + +class C { + @deprecated("", ReplaceWith("newFun(this)")) + fun oldFun(){} +} + +fun C.newFun(c: C){} + +fun foo() { + val bar = 0 + getBar().oldFun() +} + +fun getBar(): C = C() diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice2.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice2.kt.after new file mode 100644 index 00000000000..eb1ec5fadc0 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice2.kt.after @@ -0,0 +1,16 @@ +// "Replace with 'newFun(this)'" "true" + +class C { + @deprecated("", ReplaceWith("newFun(this)")) + fun oldFun(){} +} + +fun C.newFun(c: C){} + +fun foo() { + val bar = 0 + val bar1 = getBar() + bar1.newFun(bar1) +} + +fun getBar(): C = C() diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice3Runtime.kt b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice3Runtime.kt new file mode 100644 index 00000000000..d6387ae56e7 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice3Runtime.kt @@ -0,0 +1,13 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: Int): Int { + return newFun(p, p) +} + +fun newFun(p1: Int, p2: Int): Int = 0 + +fun foo(): Int { + var v = 0 + return oldFun(v++) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice3Runtime.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice3Runtime.kt.after new file mode 100644 index 00000000000..4cc9846316b --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice3Runtime.kt.after @@ -0,0 +1,13 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: Int): Int { + return newFun(p, p) +} + +fun newFun(p1: Int, p2: Int): Int = 0 + +fun foo(): Int { + var v = 0 + return v++.let { newFun(it, it) } +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice4.kt b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice4.kt new file mode 100644 index 00000000000..33edd6f3ef1 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice4.kt @@ -0,0 +1,16 @@ +// "Replace with 'newFun(this, p)'" "true" + +class C { + @deprecated("", ReplaceWith("newFun(this, p)")) + fun oldFun(p: Int){} +} + +fun C.newFun(c: C, p: Int){} + +val bar = 0 + +fun foo() { + getBar().oldFun(bar) +} + +fun getBar(): C = C() diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice4.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice4.kt.after new file mode 100644 index 00000000000..9f0ee879497 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice4.kt.after @@ -0,0 +1,17 @@ +// "Replace with 'newFun(this, p)'" "true" + +class C { + @deprecated("", ReplaceWith("newFun(this, p)")) + fun oldFun(p: Int){} +} + +fun C.newFun(c: C, p: Int){} + +val bar = 0 + +fun foo() { + val bar1 = getBar() + bar1.newFun(bar1, bar) +} + +fun getBar(): C = C() diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice5Runtime.kt b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice5Runtime.kt new file mode 100644 index 00000000000..6b1b9507e3d --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice5Runtime.kt @@ -0,0 +1,16 @@ +// "Replace with 'newFun(p, p)'" "true" + +import java.util.* + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: List) { + newFun(p, p) +} + +fun newFun(p1: List, p2: List){} + +fun foo() { + oldFun(bar()) +} + +fun bar(): List = ArrayList() diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice5Runtime.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice5Runtime.kt.after new file mode 100644 index 00000000000..34f13a6d9a3 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice5Runtime.kt.after @@ -0,0 +1,17 @@ +// "Replace with 'newFun(p, p)'" "true" + +import java.util.* + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: List) { + newFun(p, p) +} + +fun newFun(p1: List, p2: List){} + +fun foo() { + val p: List = bar() + newFun(p, p) +} + +fun bar(): List = ArrayList() diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/simpleExpressionUsedTwice.kt b/idea/testData/quickfix/deprecatedSymbolUsage/simpleExpressionUsedTwice.kt new file mode 100644 index 00000000000..bd5edb36ce1 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/simpleExpressionUsedTwice.kt @@ -0,0 +1,16 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: Int) { + newFun(p, p) +} + +fun newFun(p1: Int, p2: Int){} + +fun foo(c: C) { + oldFun(c.bar) +} + +class C { + var bar = 0 +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/simpleExpressionUsedTwice.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/simpleExpressionUsedTwice.kt.after new file mode 100644 index 00000000000..643e45262e3 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/simpleExpressionUsedTwice.kt.after @@ -0,0 +1,16 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: Int) { + newFun(p, p) +} + +fun newFun(p1: Int, p2: Int){} + +fun foo(c: C) { + newFun(c.bar, c.bar) +} + +class C { + var bar = 0 +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/stringLiteralUsedTwice.kt b/idea/testData/quickfix/deprecatedSymbolUsage/stringLiteralUsedTwice.kt new file mode 100644 index 00000000000..cdeef5cba1d --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/stringLiteralUsedTwice.kt @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: String) { + newFun(p, p) +} + +fun newFun(p1: String, p2: String){} + +fun foo() { + oldFun("x") +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/stringLiteralUsedTwice.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/stringLiteralUsedTwice.kt.after new file mode 100644 index 00000000000..4feb4ccaaa9 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/stringLiteralUsedTwice.kt.after @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: String) { + newFun(p, p) +} + +fun newFun(p1: String, p2: String){} + +fun foo() { + newFun("x", "x") +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice.kt b/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice.kt new file mode 100644 index 00000000000..b230dc2ec05 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice.kt @@ -0,0 +1,12 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: String) { + newFun(p, p) +} + +fun newFun(p1: String, p2: String){} + +fun foo(p: Int) { + oldFun("p=$p") +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice.kt.after new file mode 100644 index 00000000000..6e4475aa0dc --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice.kt.after @@ -0,0 +1,13 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: String) { + newFun(p, p) +} + +fun newFun(p1: String, p2: String){} + +fun foo(p: Int) { + val p1 = "p=$p" + newFun(p1, p1) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice2.kt b/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice2.kt new file mode 100644 index 00000000000..f1bc7c77d38 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice2.kt @@ -0,0 +1,14 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: String) { + newFun(p, p) +} + +fun newFun(p1: String, p2: String){} + +fun foo(p: Int) { + oldFun("${p.bar()}") +} + +fun Int.bar(): String = "" diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice2.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice2.kt.after new file mode 100644 index 00000000000..9f93445f5eb --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice2.kt.after @@ -0,0 +1,15 @@ +// "Replace with 'newFun(p, p)'" "true" + +@deprecated("", ReplaceWith("newFun(p, p)")) +fun oldFun(p: String) { + newFun(p, p) +} + +fun newFun(p1: String, p2: String){} + +fun foo(p: Int) { + val p1 = "${p.bar()}" + newFun(p1, p1) +} + +fun Int.bar(): String = "" diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 3ddc4e6261d..81bc0ecf159 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -2932,6 +2932,36 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("complexExpressionUsedTwice.kt") + public void testComplexExpressionUsedTwice() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice.kt"); + doTest(fileName); + } + + @TestMetadata("complexExpressionUsedTwice2.kt") + public void testComplexExpressionUsedTwice2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice2.kt"); + doTest(fileName); + } + + @TestMetadata("complexExpressionUsedTwice3Runtime.kt") + public void testComplexExpressionUsedTwice3Runtime() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice3Runtime.kt"); + doTest(fileName); + } + + @TestMetadata("complexExpressionUsedTwice4.kt") + public void testComplexExpressionUsedTwice4() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice4.kt"); + doTest(fileName); + } + + @TestMetadata("complexExpressionUsedTwice5Runtime.kt") + public void testComplexExpressionUsedTwice5Runtime() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/complexExpressionUsedTwice5Runtime.kt"); + doTest(fileName); + } + @TestMetadata("doNotShortenUserReferences.kt") public void testDoNotShortenUserReferences() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/doNotShortenUserReferences.kt"); @@ -3028,6 +3058,30 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("simpleExpressionUsedTwice.kt") + public void testSimpleExpressionUsedTwice() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/simpleExpressionUsedTwice.kt"); + doTest(fileName); + } + + @TestMetadata("stringLiteralUsedTwice.kt") + public void testStringLiteralUsedTwice() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/stringLiteralUsedTwice.kt"); + doTest(fileName); + } + + @TestMetadata("stringTemplateUsedTwice.kt") + public void testStringTemplateUsedTwice() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice.kt"); + doTest(fileName); + } + + @TestMetadata("stringTemplateUsedTwice2.kt") + public void testStringTemplateUsedTwice2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/stringTemplateUsedTwice2.kt"); + doTest(fileName); + } + @TestMetadata("toOuterClassMethod.kt") public void testToOuterClassMethod() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/toOuterClassMethod.kt");