DeprecatedSymbolUsageFix - correct dealing with implicit receivers + fixed KT-7776 in completion
#KT-7776 Fixed
This commit is contained in:
@@ -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<String?, JetCallExpression?> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,22 +20,33 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
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.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||||
import java.util.HashSet
|
import java.util.LinkedHashMap
|
||||||
import java.util.LinkedHashSet
|
import java.util.LinkedHashSet
|
||||||
|
|
||||||
public fun JetScope.getImplicitReceiversWithInstance(): List<ReceiverParameterDescriptor> {
|
public fun JetScope.getImplicitReceiversWithInstance(): Collection<ReceiverParameterDescriptor>
|
||||||
|
= getImplicitReceiversWithInstanceToExpression().keySet()
|
||||||
|
|
||||||
|
public interface ReceiverExpressionFactory {
|
||||||
|
public fun createExpression(psiFactory: JetPsiFactory, shortThis: Boolean = true): JetExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun JetScope.getImplicitReceiversWithInstanceToExpression(): Map<ReceiverParameterDescriptor, ReceiverExpressionFactory?> {
|
||||||
// we use a set to workaround a bug with receiver for companion object present twice in the result of getImplicitReceiversHierarchy()
|
// 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 receivers = LinkedHashSet(getImplicitReceiversHierarchy())
|
||||||
|
|
||||||
val withInstance = HashSet<DeclarationDescriptor>()
|
val outerDeclarationsWithInstance = LinkedHashSet<DeclarationDescriptor>()
|
||||||
var current: DeclarationDescriptor? = getContainingDeclaration()
|
var current: DeclarationDescriptor? = getContainingDeclaration()
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
if (current is PropertyAccessorDescriptor) {
|
if (current is PropertyAccessorDescriptor) {
|
||||||
current = current.getCorrespondingProperty()
|
current = current.getCorrespondingProperty()
|
||||||
}
|
}
|
||||||
withInstance.add(current)
|
outerDeclarationsWithInstance.add(current)
|
||||||
|
|
||||||
val classDescriptor = current as? ClassDescriptor
|
val classDescriptor = current as? ClassDescriptor
|
||||||
if (classDescriptor != null && !classDescriptor.isInner() && !DescriptorUtils.isLocal(classDescriptor)) break
|
if (classDescriptor != null && !classDescriptor.isInner() && !DescriptorUtils.isLocal(classDescriptor)) break
|
||||||
@@ -43,8 +54,42 @@ public fun JetScope.getImplicitReceiversWithInstance(): List<ReceiverParameterDe
|
|||||||
current = current!!.getContainingDeclaration()
|
current = current!!.getContainingDeclaration()
|
||||||
}
|
}
|
||||||
|
|
||||||
return receivers.filter {
|
val result = LinkedHashMap<ReceiverParameterDescriptor, ReceiverExpressionFactory?>()
|
||||||
val owner = it.getContainingDeclaration()
|
for ((index, receiver) in receivers.withIndex()) {
|
||||||
owner is ClassDescriptor && owner.getKind().isSingleton() || owner in withInstance
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.CastReceiverInsertHandler
|
||||||
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
|
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
|
||||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
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.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
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<ThisItemInfo> {
|
fun thisExpressionItems(bindingContext: BindingContext, position: JetExpression, prefix: String): Collection<ThisItemInfo> {
|
||||||
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, position] ?: return listOf()
|
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, position] ?: return listOf()
|
||||||
|
|
||||||
|
val psiFactory = JetPsiFactory(position)
|
||||||
|
|
||||||
val result = ArrayList<ThisItemInfo>()
|
val result = ArrayList<ThisItemInfo>()
|
||||||
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 thisType = receiver.getType()
|
||||||
val fuzzyType = FuzzyType(thisType, listOf())
|
val fuzzyType = FuzzyType(thisType, listOf())
|
||||||
|
|
||||||
fun createLookupElement(label: String?): LookupElement {
|
fun createLookupElement() = createKeywordWithLabelElement("this", expression.getLabelName())
|
||||||
var element = createKeywordWithLabelElement("this", label)
|
.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(thisType))
|
||||||
element = element.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(thisType))
|
|
||||||
return element
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
result.add(ThisItemInfo(::createLookupElement, fuzzyType))
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
return result
|
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<String?, JetCallExpression?> {
|
|
||||||
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<LookupElement> {
|
fun returnExpressionItems(bindingContext: BindingContext, position: JetElement): Collection<LookupElement> {
|
||||||
val result = ArrayList<LookupElement>()
|
val result = ArrayList<LookupElement>()
|
||||||
for (parent in position.parents()) {
|
for (parent in position.parents()) {
|
||||||
if (parent is JetDeclarationWithBody) {
|
if (parent is JetDeclarationWithBody) {
|
||||||
val returnsUnit = returnsUnit(parent, bindingContext)
|
val returnsUnit = returnsUnit(parent, bindingContext)
|
||||||
if (parent is JetFunctionLiteral) {
|
if (parent is JetFunctionLiteral) {
|
||||||
val (label, call) = functionLiteralLabelAndCall(parent)
|
val (label, call) = parent.findLabelAndCall()
|
||||||
if (label != null) {
|
if (label != null) {
|
||||||
result.add(createKeywordWithLabelElement("return", label, addSpace = !returnsUnit))
|
result.add(createKeywordWithLabelElement("return", label, addSpace = !returnsUnit))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
class C {
|
||||||
|
fun foo() {
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// INVOCATION_COUNT: 1
|
||||||
|
// EXIST: "this"
|
||||||
|
// ABSENT: "this@Companion"
|
||||||
+6
@@ -281,6 +281,12 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("NoCompletionForCapitalPrefix.kt")
|
||||||
public void testNoCompletionForCapitalPrefix() throws Exception {
|
public void testNoCompletionForCapitalPrefix() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/NoCompletionForCapitalPrefix.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/NoCompletionForCapitalPrefix.kt");
|
||||||
|
|||||||
@@ -16,12 +16,17 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.core
|
package org.jetbrains.kotlin.idea.core
|
||||||
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiRecursiveElementVisitor
|
import com.intellij.psi.PsiRecursiveElementVisitor
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
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.psi.*
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
|
|
||||||
@@ -83,3 +88,11 @@ public inline fun <reified TElement> PsiElement.collectElementsOfType(@inlineOpt
|
|||||||
})
|
})
|
||||||
return result
|
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)
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
|
|||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.core.asExpression
|
||||||
import org.jetbrains.kotlin.idea.core.collectElementsOfType
|
import org.jetbrains.kotlin.idea.core.collectElementsOfType
|
||||||
import org.jetbrains.kotlin.idea.core.refactoring.JetNameSuggester
|
import org.jetbrains.kotlin.idea.core.refactoring.JetNameSuggester
|
||||||
import org.jetbrains.kotlin.idea.core.refactoring.JetNameValidator
|
import org.jetbrains.kotlin.idea.core.refactoring.JetNameValidator
|
||||||
@@ -52,6 +53,8 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
|||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
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.ErrorUtils
|
||||||
import org.jetbrains.kotlin.types.JetType
|
import org.jetbrains.kotlin.types.JetType
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
@@ -98,11 +101,20 @@ public class DeprecatedSymbolUsageFix(
|
|||||||
val FROM_PARAMETER_KEY = Key<ValueParameterDescriptor>("FROM_PARAMETER")
|
val FROM_PARAMETER_KEY = Key<ValueParameterDescriptor>("FROM_PARAMETER")
|
||||||
val FROM_THIS_KEY = Key<Unit>("FROM_THIS")
|
val FROM_THIS_KEY = Key<Unit>("FROM_THIS")
|
||||||
|
|
||||||
val explicitReceiver = qualifiedExpression?.getReceiverExpression()
|
var receiver = qualifiedExpression?.getReceiverExpression()
|
||||||
explicitReceiver?.putCopyableUserData(USER_CODE_KEY, Unit)
|
receiver?.putCopyableUserData(USER_CODE_KEY, Unit)
|
||||||
explicitReceiver?.putCopyableUserData(FROM_THIS_KEY, Unit)
|
|
||||||
//TODO: infix and operator calls
|
//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)
|
val originalDescriptor = (if (descriptor is CallableMemberDescriptor)
|
||||||
DescriptorUtils.unwrapFakeOverride(descriptor)
|
DescriptorUtils.unwrapFakeOverride(descriptor)
|
||||||
else
|
else
|
||||||
@@ -111,11 +123,10 @@ public class DeprecatedSymbolUsageFix(
|
|||||||
var (expression, imports, parameterUsages) = ReplaceWithAnnotationAnalyzer.analyze(
|
var (expression, imports, parameterUsages) = ReplaceWithAnnotationAnalyzer.analyze(
|
||||||
replaceWith, originalDescriptor, element.getResolutionFacade(), file, project)
|
replaceWith, originalDescriptor, element.getResolutionFacade(), file, project)
|
||||||
|
|
||||||
//TODO: implicit receiver is not always "this"
|
|
||||||
//TODO: this@
|
//TODO: this@
|
||||||
for (thisExpression in expression.collectElementsOfType<JetThisExpression>()) {
|
for (thisExpression in expression.collectElementsOfType<JetThisExpression>()) {
|
||||||
if (explicitReceiver != null) {
|
if (receiver != null) {
|
||||||
thisExpression.replace(explicitReceiver)
|
thisExpression.replace(receiver)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
thisExpression.putCopyableUserData(FROM_THIS_KEY, Unit)
|
thisExpression.putCopyableUserData(FROM_THIS_KEY, Unit)
|
||||||
@@ -151,7 +162,7 @@ public class DeprecatedSymbolUsageFix(
|
|||||||
if (qualified.getReceiverExpression().getCopyableUserData(FROM_THIS_KEY) != null) {
|
if (qualified.getReceiverExpression().getCopyableUserData(FROM_THIS_KEY) != null) {
|
||||||
val selector = qualified.getSelectorExpression()
|
val selector = qualified.getSelectorExpression()
|
||||||
if (selector != null) {
|
if (selector != null) {
|
||||||
expression = psiFactory.createExpressionByPattern("$0?.$1", explicitReceiver!!, selector)
|
expression = psiFactory.createExpressionByPattern("$0?.$1", receiver!!, selector)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,19 +170,19 @@ public class DeprecatedSymbolUsageFix(
|
|||||||
|
|
||||||
if (expressionToReplace.isUsedAsExpression(bindingContext)) {
|
if (expressionToReplace.isUsedAsExpression(bindingContext)) {
|
||||||
val thisReplaced = expression.collectElementsOfType<JetExpression> { it.getCopyableUserData(FROM_THIS_KEY) != null }
|
val thisReplaced = expression.collectElementsOfType<JetExpression> { 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 {
|
else {
|
||||||
expression = psiFactory.createExpressionByPattern("if ($0 != null) { $1 }", explicitReceiver!!, expression)
|
expression = psiFactory.createExpressionByPattern("if ($0 != null) { $1 }", receiver!!, expression)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processSafeCall()
|
processSafeCall()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (explicitReceiver != null && explicitReceiver.shouldIntroduceVariableIfUsedTwice()) {
|
if (receiver != null && receiver.shouldIntroduceVariableIfUsedTwice()) {
|
||||||
val thisReplaced = expression.collectElementsOfType<JetExpression> { it.getCopyableUserData(FROM_THIS_KEY) != null }
|
val thisReplaced = expression.collectElementsOfType<JetExpression> { it.getCopyableUserData(FROM_THIS_KEY) != null }
|
||||||
if (thisReplaced.size() > 1) {
|
if (thisReplaced.size() > 1) {
|
||||||
expression = expression.introduceValue(explicitReceiver, expressionToReplace, bindingContext, thisReplaced)
|
expression = expression.introduceValue(receiver!!, expressionToReplace, bindingContext, thisReplaced)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiRecursiveElementVisitor
|
|||||||
import org.jetbrains.kotlin.analyzer.analyzeInContext
|
import org.jetbrains.kotlin.analyzer.analyzeInContext
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
|
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.core.collectElementsOfType
|
||||||
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
|
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
|
||||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||||
@@ -76,10 +77,7 @@ object ReplaceWithAnnotationAnalyzer {
|
|||||||
|
|
||||||
val bindingContext = expression.analyzeInContext(scope)
|
val bindingContext = expression.analyzeInContext(scope)
|
||||||
|
|
||||||
val thisType = symbolDescriptor.getExtensionReceiverParameter()?.getType()
|
val receiversToAdd = ArrayList<Pair<JetExpression, JetExpression>>()
|
||||||
?: (symbolDescriptor.getContainingDeclaration() as? ClassifierDescriptor)?.getDefaultType()
|
|
||||||
|
|
||||||
val receiversToAdd = ArrayList<Pair<JetExpression, String>>()
|
|
||||||
|
|
||||||
val parameterUsageKey = Key<ValueParameterDescriptor>("parameterUsageKey")
|
val parameterUsageKey = Key<ValueParameterDescriptor>("parameterUsageKey")
|
||||||
|
|
||||||
@@ -105,14 +103,9 @@ object ReplaceWithAnnotationAnalyzer {
|
|||||||
else
|
else
|
||||||
resolvedCall.getDispatchReceiver()
|
resolvedCall.getDispatchReceiver()
|
||||||
if (receiver is ThisReceiver) {
|
if (receiver is ThisReceiver) {
|
||||||
if (receiver.getType() == thisType) {
|
val receiverExpression = receiver.asExpression(symbolScope, psiFactory)
|
||||||
receiversToAdd.add(expression to "this")
|
if (receiverExpression != null) {
|
||||||
}
|
receiversToAdd.add(expression to receiverExpression)
|
||||||
else {
|
|
||||||
val descriptor = receiver.getDeclarationDescriptor()
|
|
||||||
if (descriptor is ClassDescriptor && descriptor.isCompanionObject()) {
|
|
||||||
receiversToAdd.add(expression to IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(descriptor))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,9 +117,9 @@ object ReplaceWithAnnotationAnalyzer {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
for ((expr, receiverText) in receiversToAdd) {
|
for ((expr, receiverExpression) in receiversToAdd) {
|
||||||
val expressionToReplace = expr.getParent() as? JetCallExpression ?: expr
|
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) {
|
if (expressionToReplace == expression) {
|
||||||
expression = newExpr
|
expression = newExpr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ open class Base {
|
|||||||
class Derived : Base() {
|
class Derived : Base() {
|
||||||
inner class InnerDerived : Base.Inner() {
|
inner class InnerDerived : Base.Inner() {
|
||||||
fun foo() {
|
fun foo() {
|
||||||
<caret>"a".newFun(this@Derived)
|
"a".<caret>newFun(this@Derived)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
<caret>oldFun()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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() {
|
||||||
|
<caret>newFun(Companion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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") {
|
||||||
|
<caret>oldFun(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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") {
|
||||||
|
<caret>newFun(this@foo, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -11,5 +11,5 @@ class Outer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun foo(inner: Outer.Inner) {
|
fun foo(inner: Outer.Inner) {
|
||||||
<caret>newFun(inner)
|
inner.newFun(inner)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2992,6 +2992,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("incorrectReplacement.kt")
|
||||||
public void testIncorrectReplacement() throws Exception {
|
public void testIncorrectReplacement() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/incorrectReplacement.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/incorrectReplacement.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user