From 2a5ca095eec4f3c86e71dc54d38129c85873e8f2 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 19 May 2015 18:11:12 +0300 Subject: [PATCH] DeprecatedSymbolUsageFix - correct dealing with implicit receivers + fixed KT-7776 in completion #KT-7776 Fixed --- .../org/jetbrains/kotlin/idea/util/Utils.kt | 45 ++++++++++++ .../idea/util/implicitReceiversUtils.kt | 59 ++++++++++++++-- .../kotlin/idea/completion/CompletionUtils.kt | 68 ++++--------------- .../testData/keywords/NoCompanionThis.kt | 12 ++++ .../test/KeywordCompletionTestGenerated.java | 6 ++ .../org/jetbrains/kotlin/idea/core/Utils.kt | 15 +++- .../idea/quickfix/DeprecatedSymbolUsageFix.kt | 33 ++++++--- .../quickfix/ReplaceWithAnnotationAnalyzer.kt | 21 ++---- .../changeThisToOuterThis.kt.after | 2 +- .../implicitCompanionObjectThis.kt | 18 +++++ .../implicitCompanionObjectThis.kt.after | 18 +++++ .../implicitQualifiedThisRuntime.kt | 15 ++++ .../implicitQualifiedThisRuntime.kt.after | 15 ++++ .../toOuterClassMethod.kt.after | 2 +- .../idea/quickfix/QuickFixTestGenerated.java | 12 ++++ 15 files changed, 251 insertions(+), 90 deletions(-) create mode 100644 idea/ide-common/src/org/jetbrains/kotlin/idea/util/Utils.kt create mode 100644 idea/idea-completion/testData/keywords/NoCompanionThis.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/implicitCompanionObjectThis.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/implicitCompanionObjectThis.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/implicitQualifiedThisRuntime.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/implicitQualifiedThisRuntime.kt.after diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/Utils.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/Utils.kt new file mode 100644 index 00000000000..ed71ebcc294 --- /dev/null +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/Utils.kt @@ -0,0 +1,45 @@ +/* + * 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.util + +import org.jetbrains.kotlin.psi.* + +public fun JetFunctionLiteral.findLabelAndCall(): Pair { + val literalParent = (this.getParent() as JetFunctionLiteralExpression).getParent() + + fun JetValueArgument.callExpression(): JetCallExpression? { + val parent = getParent() + return (if (parent is JetValueArgumentList) parent else this).getParent() as? JetCallExpression + } + + when (literalParent) { + is JetLabeledExpression -> { + val callExpression = (literalParent.getParent() as? JetValueArgument)?.callExpression() + return Pair(literalParent.getLabelName(), callExpression) + } + + is JetValueArgument -> { + val callExpression = literalParent.callExpression() + val label = (callExpression?.getCalleeExpression() as? JetSimpleNameExpression)?.getReferencedName() + return Pair(label, callExpression) + } + + else -> { + return Pair(null, null) + } + } +} diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/implicitReceiversUtils.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/implicitReceiversUtils.kt index ff93dcb223b..eccd13f8c61 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/implicitReceiversUtils.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/implicitReceiversUtils.kt @@ -20,22 +20,33 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.psi.JetFunctionLiteral +import org.jetbrains.kotlin.psi.JetPsiFactory +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.scopes.JetScope -import java.util.HashSet +import java.util.LinkedHashMap import java.util.LinkedHashSet -public fun JetScope.getImplicitReceiversWithInstance(): List { +public fun JetScope.getImplicitReceiversWithInstance(): Collection + = getImplicitReceiversWithInstanceToExpression().keySet() + +public interface ReceiverExpressionFactory { + public fun createExpression(psiFactory: JetPsiFactory, shortThis: Boolean = true): JetExpression +} + +public fun JetScope.getImplicitReceiversWithInstanceToExpression(): Map { // we use a set to workaround a bug with receiver for companion object present twice in the result of getImplicitReceiversHierarchy() val receivers = LinkedHashSet(getImplicitReceiversHierarchy()) - val withInstance = HashSet() + val outerDeclarationsWithInstance = LinkedHashSet() var current: DeclarationDescriptor? = getContainingDeclaration() while (current != null) { if (current is PropertyAccessorDescriptor) { current = current.getCorrespondingProperty() } - withInstance.add(current) + outerDeclarationsWithInstance.add(current) val classDescriptor = current as? ClassDescriptor if (classDescriptor != null && !classDescriptor.isInner() && !DescriptorUtils.isLocal(classDescriptor)) break @@ -43,8 +54,42 @@ public fun JetScope.getImplicitReceiversWithInstance(): List() + for ((index, receiver) in receivers.withIndex()) { + val owner = receiver.getContainingDeclaration() + val (expressionText, isImmediateThis) = if (owner in outerDeclarationsWithInstance) { + val thisWithLabel = thisQualifierName(receiver)?.let { "this@$it" } + if (index == 0) + (thisWithLabel ?: "this") to true + else + thisWithLabel to false + } + else if (owner is ClassDescriptor && owner.getKind().isSingleton()) { + IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(owner) to false + } + else { + continue + } + val factory = if (expressionText != null) + object : ReceiverExpressionFactory { + override fun createExpression(psiFactory: JetPsiFactory, shortThis: Boolean): JetExpression { + return psiFactory.createExpression(if (shortThis && isImmediateThis) "this" else expressionText) + } + } + else + null + result.put(receiver, factory) } + return result +} + +private fun thisQualifierName(receiver: ReceiverParameterDescriptor): String? { + val descriptor = receiver.getContainingDeclaration() + val name = descriptor.getName() + if (!name.isSpecial()) { + return name.asString() + } + + val functionLiteral = DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as? JetFunctionLiteral + return functionLiteral?.findLabelAndCall()?.first } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt index b4571acea95..59aeb06e149 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt @@ -29,7 +29,8 @@ import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.completion.handlers.CastReceiverInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler import org.jetbrains.kotlin.idea.util.FuzzyType -import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstance +import org.jetbrains.kotlin.idea.util.findLabelAndCall +import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstanceToExpression import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.parents @@ -197,75 +198,32 @@ data class ThisItemInfo(val factory: () -> LookupElement, val type: FuzzyType) fun thisExpressionItems(bindingContext: BindingContext, position: JetExpression, prefix: String): Collection { val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, position] ?: return listOf() + val psiFactory = JetPsiFactory(position) + val result = ArrayList() - for ((i, receiver) in scope.getImplicitReceiversWithInstance().withIndex()) { + for ((receiver, expressionFactory) in scope.getImplicitReceiversWithInstanceToExpression()) { + if (expressionFactory == null) continue + // if prefix does not start with "this@" do not include immediate this in the form with label + val expression = expressionFactory.createExpression(psiFactory, shortThis = !prefix.startsWith("this@")) as? JetThisExpression ?: continue + val thisType = receiver.getType() val fuzzyType = FuzzyType(thisType, listOf()) - fun createLookupElement(label: String?): LookupElement { - var element = createKeywordWithLabelElement("this", label) - element = element.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(thisType)) - return element - } + fun createLookupElement() = createKeywordWithLabelElement("this", expression.getLabelName()) + .withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(thisType)) - if (i == 0) { - result.add(ThisItemInfo({ createLookupElement(null) }, fuzzyType)) - if (!prefix.startsWith("this@")) continue // if prefix does not start with "this@" do not include immediate this in the form with label - } - - val label = thisQualifierName(receiver) ?: continue - result.add(ThisItemInfo({ createLookupElement(label) }, fuzzyType)) + result.add(ThisItemInfo(::createLookupElement, fuzzyType)) } return result } -private fun thisQualifierName(receiver: ReceiverParameterDescriptor): String? { - val descriptor = receiver.getContainingDeclaration() - val name = descriptor.getName() - if (!name.isSpecial()) { - return name.asString() - } - - val functionLiteral = DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as? JetFunctionLiteral ?: return null - return functionLiteralLabel(functionLiteral) -} - -private fun functionLiteralLabel(functionLiteral: JetFunctionLiteral): String? - = functionLiteralLabelAndCall(functionLiteral).first - -private fun functionLiteralLabelAndCall(functionLiteral: JetFunctionLiteral): Pair { - val literalParent = (functionLiteral.getParent() as JetFunctionLiteralExpression).getParent() - - fun JetValueArgument.callExpression(): JetCallExpression? { - val parent = getParent() - return (if (parent is JetValueArgumentList) parent else this).getParent() as? JetCallExpression - } - - when (literalParent) { - is JetLabeledExpression -> { - val callExpression = (literalParent.getParent() as? JetValueArgument)?.callExpression() - return Pair(literalParent.getLabelName(), callExpression) - } - - is JetValueArgument -> { - val callExpression = literalParent.callExpression() - val label = (callExpression?.getCalleeExpression() as? JetSimpleNameExpression)?.getReferencedName() - return Pair(label, callExpression) - } - - else -> { - return Pair(null, null) - } - } -} - fun returnExpressionItems(bindingContext: BindingContext, position: JetElement): Collection { val result = ArrayList() for (parent in position.parents()) { if (parent is JetDeclarationWithBody) { val returnsUnit = returnsUnit(parent, bindingContext) if (parent is JetFunctionLiteral) { - val (label, call) = functionLiteralLabelAndCall(parent) + val (label, call) = parent.findLabelAndCall() if (label != null) { result.add(createKeywordWithLabelElement("return", label, addSpace = !returnsUnit)) } diff --git a/idea/idea-completion/testData/keywords/NoCompanionThis.kt b/idea/idea-completion/testData/keywords/NoCompanionThis.kt new file mode 100644 index 00000000000..07f574fe714 --- /dev/null +++ b/idea/idea-completion/testData/keywords/NoCompanionThis.kt @@ -0,0 +1,12 @@ +class C { + fun foo() { + + } + + companion object { + } +} + +// INVOCATION_COUNT: 1 +// EXIST: "this" +// ABSENT: "this@Companion" diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java index c1f54cebee5..9c40c390fef 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java @@ -281,6 +281,12 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes doTest(fileName); } + @TestMetadata("NoCompanionThis.kt") + public void testNoCompanionThis() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/NoCompanionThis.kt"); + doTest(fileName); + } + @TestMetadata("NoCompletionForCapitalPrefix.kt") public void testNoCompletionForCapitalPrefix() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/NoCompletionForCapitalPrefix.kt"); diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt index b2d4ed01dea..7537507aa14 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt @@ -16,12 +16,17 @@ package org.jetbrains.kotlin.idea.core -import com.intellij.openapi.util.Key import com.intellij.psi.PsiElement import com.intellij.psi.PsiRecursiveElementVisitor import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstance +import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstanceToExpression import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.scopes.JetScope +import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver import java.util.ArrayList import java.util.HashMap @@ -83,3 +88,11 @@ public inline fun PsiElement.collectElementsOfType(@inlineOpt }) return result } + +public fun ThisReceiver.asExpression(resolutionScope: JetScope, psiFactory: JetPsiFactory): JetExpression? { + val expressionFactory = resolutionScope.getImplicitReceiversWithInstanceToExpression() + .entrySet() + .firstOrNull { it.key.getContainingDeclaration() == this.getDeclarationDescriptor() } + ?.value ?: return null + return expressionFactory.createExpression(psiFactory) +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFix.kt index d6e4c4b9c7c..fc28a6090a1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFix.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.core.asExpression import org.jetbrains.kotlin.idea.core.collectElementsOfType import org.jetbrains.kotlin.idea.core.refactoring.JetNameSuggester import org.jetbrains.kotlin.idea.core.refactoring.JetNameValidator @@ -52,6 +53,8 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension +import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.JetType import java.util.ArrayList @@ -98,11 +101,20 @@ public class DeprecatedSymbolUsageFix( val FROM_PARAMETER_KEY = Key("FROM_PARAMETER") val FROM_THIS_KEY = Key("FROM_THIS") - val explicitReceiver = qualifiedExpression?.getReceiverExpression() - explicitReceiver?.putCopyableUserData(USER_CODE_KEY, Unit) - explicitReceiver?.putCopyableUserData(FROM_THIS_KEY, Unit) + var receiver = qualifiedExpression?.getReceiverExpression() + receiver?.putCopyableUserData(USER_CODE_KEY, Unit) //TODO: infix and operator calls + 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) + } + } + + receiver?.putCopyableUserData(FROM_THIS_KEY, Unit) + val originalDescriptor = (if (descriptor is CallableMemberDescriptor) DescriptorUtils.unwrapFakeOverride(descriptor) else @@ -111,11 +123,10 @@ public class DeprecatedSymbolUsageFix( var (expression, imports, parameterUsages) = ReplaceWithAnnotationAnalyzer.analyze( replaceWith, originalDescriptor, element.getResolutionFacade(), file, project) - //TODO: implicit receiver is not always "this" //TODO: this@ for (thisExpression in expression.collectElementsOfType()) { - if (explicitReceiver != null) { - thisExpression.replace(explicitReceiver) + if (receiver != null) { + thisExpression.replace(receiver) } else { thisExpression.putCopyableUserData(FROM_THIS_KEY, Unit) @@ -151,7 +162,7 @@ public class DeprecatedSymbolUsageFix( if (qualified.getReceiverExpression().getCopyableUserData(FROM_THIS_KEY) != null) { val selector = qualified.getSelectorExpression() if (selector != null) { - expression = psiFactory.createExpressionByPattern("$0?.$1", explicitReceiver!!, selector) + expression = psiFactory.createExpressionByPattern("$0?.$1", receiver!!, selector) return } } @@ -159,19 +170,19 @@ public class DeprecatedSymbolUsageFix( if (expressionToReplace.isUsedAsExpression(bindingContext)) { val thisReplaced = expression.collectElementsOfType { it.getCopyableUserData(FROM_THIS_KEY) != null } - expression = expression.introduceValue(explicitReceiver!!, expressionToReplace, bindingContext, thisReplaced, safeCall = true) + expression = expression.introduceValue(receiver!!, expressionToReplace, bindingContext, thisReplaced, safeCall = true) } else { - expression = psiFactory.createExpressionByPattern("if ($0 != null) { $1 }", explicitReceiver!!, expression) + expression = psiFactory.createExpressionByPattern("if ($0 != null) { $1 }", receiver!!, expression) } } processSafeCall() } - if (explicitReceiver != null && explicitReceiver.shouldIntroduceVariableIfUsedTwice()) { + if (receiver != null && receiver.shouldIntroduceVariableIfUsedTwice()) { val thisReplaced = expression.collectElementsOfType { it.getCopyableUserData(FROM_THIS_KEY) != null } if (thisReplaced.size() > 1) { - expression = expression.introduceValue(explicitReceiver, expressionToReplace, bindingContext, thisReplaced) + expression = expression.introduceValue(receiver!!, expressionToReplace, bindingContext, thisReplaced) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceWithAnnotationAnalyzer.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceWithAnnotationAnalyzer.kt index 8c50a560578..264de8cf33c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceWithAnnotationAnalyzer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceWithAnnotationAnalyzer.kt @@ -23,6 +23,7 @@ import com.intellij.psi.PsiRecursiveElementVisitor import org.jetbrains.kotlin.analyzer.analyzeInContext import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade +import org.jetbrains.kotlin.idea.core.asExpression import org.jetbrains.kotlin.idea.core.collectElementsOfType import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport import org.jetbrains.kotlin.idea.imports.importableFqName @@ -76,10 +77,7 @@ object ReplaceWithAnnotationAnalyzer { val bindingContext = expression.analyzeInContext(scope) - val thisType = symbolDescriptor.getExtensionReceiverParameter()?.getType() - ?: (symbolDescriptor.getContainingDeclaration() as? ClassifierDescriptor)?.getDefaultType() - - val receiversToAdd = ArrayList>() + val receiversToAdd = ArrayList>() val parameterUsageKey = Key("parameterUsageKey") @@ -105,14 +103,9 @@ object ReplaceWithAnnotationAnalyzer { else resolvedCall.getDispatchReceiver() if (receiver is ThisReceiver) { - if (receiver.getType() == thisType) { - receiversToAdd.add(expression to "this") - } - else { - val descriptor = receiver.getDeclarationDescriptor() - if (descriptor is ClassDescriptor && descriptor.isCompanionObject()) { - receiversToAdd.add(expression to IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(descriptor)) - } + val receiverExpression = receiver.asExpression(symbolScope, psiFactory) + if (receiverExpression != null) { + receiversToAdd.add(expression to receiverExpression) } } } @@ -124,9 +117,9 @@ object ReplaceWithAnnotationAnalyzer { } }) - for ((expr, receiverText) in receiversToAdd) { + for ((expr, receiverExpression) in receiversToAdd) { val expressionToReplace = expr.getParent() as? JetCallExpression ?: expr - val newExpr = expressionToReplace.replaced(psiFactory.createExpressionByPattern("$receiverText.$0", expressionToReplace)) + val newExpr = expressionToReplace.replaced(psiFactory.createExpressionByPattern("$0.$1", receiverExpression, expressionToReplace)) if (expressionToReplace == expression) { expression = newExpr } diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/changeThisToOuterThis.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/changeThisToOuterThis.kt.after index c7768185d8b..983dcfdd739 100644 --- a/idea/testData/quickfix/deprecatedSymbolUsage/changeThisToOuterThis.kt.after +++ b/idea/testData/quickfix/deprecatedSymbolUsage/changeThisToOuterThis.kt.after @@ -10,7 +10,7 @@ open class Base { class Derived : Base() { inner class InnerDerived : Base.Inner() { fun foo() { - "a".newFun(this@Derived) + "a".newFun(this@Derived) } } } diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/implicitCompanionObjectThis.kt b/idea/testData/quickfix/deprecatedSymbolUsage/implicitCompanionObjectThis.kt new file mode 100644 index 00000000000..37de4ff790d --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/implicitCompanionObjectThis.kt @@ -0,0 +1,18 @@ +// "Replace with 'newFun(this)'" "true" + +open class C { + @deprecated("", ReplaceWith("newFun(this)")) + fun oldFun(){} +} + +fun newFun(c: C){} + +class X { + companion object : C() { + } + + fun foo() { + oldFun() + } +} + diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/implicitCompanionObjectThis.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/implicitCompanionObjectThis.kt.after new file mode 100644 index 00000000000..8164a9bfe94 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/implicitCompanionObjectThis.kt.after @@ -0,0 +1,18 @@ +// "Replace with 'newFun(this)'" "true" + +open class C { + @deprecated("", ReplaceWith("newFun(this)")) + fun oldFun(){} +} + +fun newFun(c: C){} + +class X { + companion object : C() { + } + + fun foo() { + newFun(Companion) + } +} + diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/implicitQualifiedThisRuntime.kt b/idea/testData/quickfix/deprecatedSymbolUsage/implicitQualifiedThisRuntime.kt new file mode 100644 index 00000000000..cb90d855e34 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/implicitQualifiedThisRuntime.kt @@ -0,0 +1,15 @@ +// "Replace with 'newFun(this, s)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(this, s)")) + fun oldFun(s: String) +} + +fun newFun(i: I, s: String){} + +fun I.foo() { + with("a") { + oldFun(this) + } +} + diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/implicitQualifiedThisRuntime.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/implicitQualifiedThisRuntime.kt.after new file mode 100644 index 00000000000..f16a89cfff2 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/implicitQualifiedThisRuntime.kt.after @@ -0,0 +1,15 @@ +// "Replace with 'newFun(this, s)'" "true" + +interface I { + @deprecated("", ReplaceWith("newFun(this, s)")) + fun oldFun(s: String) +} + +fun newFun(i: I, s: String){} + +fun I.foo() { + with("a") { + newFun(this@foo, this) + } +} + diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/toOuterClassMethod.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/toOuterClassMethod.kt.after index 60887df28b4..11a1c809de3 100644 --- a/idea/testData/quickfix/deprecatedSymbolUsage/toOuterClassMethod.kt.after +++ b/idea/testData/quickfix/deprecatedSymbolUsage/toOuterClassMethod.kt.after @@ -11,5 +11,5 @@ class Outer { } fun foo(inner: Outer.Inner) { - newFun(inner) + inner.newFun(inner) } diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index cf0493f59d3..8e391dfb8ef 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -2992,6 +2992,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("implicitCompanionObjectThis.kt") + public void testImplicitCompanionObjectThis() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/implicitCompanionObjectThis.kt"); + doTest(fileName); + } + + @TestMetadata("implicitQualifiedThisRuntime.kt") + public void testImplicitQualifiedThisRuntime() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/implicitQualifiedThisRuntime.kt"); + doTest(fileName); + } + @TestMetadata("incorrectReplacement.kt") public void testIncorrectReplacement() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/incorrectReplacement.kt");