Initial support for class usages replacement with ReplaceWith

This commit is contained in:
Valentin Kipyatkov
2015-09-01 20:32:20 +03:00
parent e7e2a9b1e2
commit de13e23a0a
23 changed files with 394 additions and 98 deletions
+4 -4
View File
@@ -16,9 +16,9 @@
package kotlin
import kotlin.annotation.*
import kotlin.annotation.AnnotationRetention.BINARY
import kotlin.annotation.AnnotationRetention.SOURCE
import kotlin.annotation.AnnotationTarget.*
import kotlin.annotation.AnnotationRetention.*
/**
* Marks the annotated class as a data class. The compiler automatically generates
@@ -40,11 +40,11 @@ target(CLASSIFIER, FUNCTION, PROPERTY, ANNOTATION_CLASS, CONSTRUCTOR, PROPERTY_S
public annotation(mustBeDocumented = true) class deprecated(val value: String, val replaceWith: ReplaceWith = ReplaceWith(""))
/**
* Specifies a code fragment that can be used to replace a deprecated function or property. Tools such
* Specifies a code fragment that can be used to replace a deprecated function, property or class. Tools such
* as IDEs can automatically apply the replacements specified through this annotation.
*
* @property expression the replacement expression. The replacement expression is interpreted in the context
* of the function or property being called, and can reference members of enclosing classes etc.
* of the symbol being used, and can reference members of enclosing classes etc.
* For function calls, the replacement expression may contain argument names of the deprecated function,
* which will be substituted with actual parameters used in the call being updated. The imports used in the file
* containing the deprecated function or property are NOT accessible; if the replacement expression refers
@@ -50,7 +50,22 @@ import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
fun performCallReplacement(
class CallableUsageReplacementStrategy(
private val replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression
) : UsageReplacementStrategy {
override fun createReplacer(usage: JetSimpleNameExpression): (() -> JetElement)? {
val bindingContext = usage.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = usage.getResolvedCall(bindingContext) ?: return null
if (!resolvedCall.status.isSuccess) return null
return {
// copy replacement expression because it is modified by performCallReplacement
performCallReplacement(usage, bindingContext, resolvedCall, replacement.copy())
}
}
}
private fun performCallReplacement(
element: JetSimpleNameExpression,
bindingContext: BindingContext,
resolvedCall: ResolvedCall<out CallableDescriptor>,
@@ -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.quickfix.replaceWith
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.psi.JetElement
import org.jetbrains.kotlin.psi.JetNameReferenceExpression
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.psi.JetUserType
class ClassUsageReplacementStrategy(
private val replacement: JetUserType
) : UsageReplacementStrategy {
override fun createReplacer(usage: JetSimpleNameExpression): (() -> JetElement)? {
if (usage !is JetNameReferenceExpression) return null
val parent = usage.parent
when (parent) {
is JetUserType -> {
return {
val replaced = parent.replaced(replacement)
ShortenReferences.DEFAULT.process(replaced)
} //TODO: type arguments and type arguments of outer class are lost
}
else -> return null //TODO
}
}
}
@@ -21,7 +21,6 @@ import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.core.targetDescriptors
@@ -29,9 +28,7 @@ import org.jetbrains.kotlin.idea.quickfix.CleanupFix
import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.quickfix.moveCaret
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
public class DeprecatedSymbolUsageFix(
element: JetSimpleNameExpression/*TODO?*/,
@@ -40,17 +37,10 @@ public class DeprecatedSymbolUsageFix(
override fun getFamilyName() = "Replace deprecated symbol usage"
override fun getText() = "Replace with '${replaceWith.expression}'" //TODO: substitute?
override fun invoke(
resolvedCall: ResolvedCall<out CallableDescriptor>,
bindingContext: BindingContext,
replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression,
project: Project,
editor: Editor?
) {
val result = performCallReplacement(element, bindingContext, resolvedCall, replacement)
override fun getText() = "Replace with '${replaceWith.pattern}'" //TODO: substitute?
override fun invoke(replacementStrategy: UsageReplacementStrategy, project: Project, editor: Editor?) {
val result = replacementStrategy.createReplacer(element)!!.invoke()
val offset = (result.getCalleeExpressionIfAny() ?: result).textOffset
editor?.moveCaret(offset)
}
@@ -22,61 +22,36 @@ import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
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.quickfix.JetIntentionAction
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetPsiFactory
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
//TODO: replacement of class usages
//TODO: different replacements for property accessors
public abstract class DeprecatedSymbolUsageFixBase(
element: JetSimpleNameExpression/*TODO?*/,
element: JetSimpleNameExpression,
val replaceWith: ReplaceWith
) : JetIntentionAction<JetSimpleNameExpression>(element) {
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
if (!super.isAvailable(project, editor, file)) return false
val resolvedCall = element.getResolvedCall(element.analyze()) ?: return false
if (!resolvedCall.status.isSuccess) return false
val descriptor = resolvedCall.resultingDescriptor
if (replaceWithPattern(descriptor, project) != replaceWith) return false
try {
JetPsiFactory(project).createExpression(replaceWith.expression)
return true
}
catch(e: Exception) {
return false
}
val strategy = UsageReplacementStrategy.build(element, replaceWith)
return strategy != null && strategy.createReplacer(element) != null
}
final override fun invoke(project: Project, editor: Editor?, file: JetFile) {
val bindingContext = element.analyze()
val resolvedCall = element.getResolvedCall(bindingContext)!!
val descriptor = resolvedCall.resultingDescriptor
val replacement = ReplaceWithAnnotationAnalyzer.analyze(replaceWith, descriptor, element.getResolutionFacade())
invoke(resolvedCall, bindingContext, replacement, project, editor)
val strategy = UsageReplacementStrategy.build(element, replaceWith)!!
invoke(strategy, project, editor)
}
protected abstract fun invoke(
resolvedCall: ResolvedCall<out CallableDescriptor>,
bindingContext: BindingContext,
replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression,
replacementStrategy: UsageReplacementStrategy,
project: Project,
editor: Editor?)
@@ -27,10 +27,8 @@ import com.intellij.psi.PsiFile
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.descriptors.CallableDescriptor
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.core.targetDescriptors
import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.references.JetSimpleNameReference
@@ -46,10 +44,6 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.NameShortness
import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
public class DeprecatedSymbolUsageInWholeProjectFix(
element: JetSimpleNameExpression,
@@ -68,16 +62,10 @@ public class DeprecatedSymbolUsageInWholeProjectFix(
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
if (!super.isAvailable(project, editor, file)) return false
val targetPsiElement = element.mainReference.resolve()
return targetPsiElement is JetNamedFunction || targetPsiElement is JetProperty
return targetPsiElement is JetNamedFunction || targetPsiElement is JetProperty //TODO
}
override fun invoke(
resolvedCall: ResolvedCall<out CallableDescriptor>,
bindingContext: BindingContext,
replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression,
project: Project,
editor: Editor?
) {
override fun invoke(replacementStrategy: UsageReplacementStrategy, project: Project, editor: Editor?) {
val psiElement = element.mainReference.resolve()!!
ProgressManager.getInstance().run(
@@ -89,12 +77,12 @@ public class DeprecatedSymbolUsageInWholeProjectFix(
.filterIsInstance<JetSimpleNameReference>()
.map { ref -> ref.expression }
}
replaceUsages(project, usages, replacement)
replaceUsages(project, usages, replacementStrategy)
}
})
}
private fun replaceUsages(project: Project, usages: Collection<JetSimpleNameExpression>, replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression) {
private fun replaceUsages(project: Project, usages: Collection<JetSimpleNameExpression>, replacementStrategy: UsageReplacementStrategy) {
UIUtil.invokeLaterIfNeeded {
project.executeWriteCommand(text) {
// we should delete imports later to not affect other usages
@@ -104,6 +92,7 @@ public class DeprecatedSymbolUsageInWholeProjectFix(
try {
if (!usage.isValid) continue // TODO: nested calls
//TODO: keep the import if we don't know how to replace some of the usages
val importDirective = usage.getStrictParentOfType<JetImportDirective>()
if (importDirective != null) {
if (!importDirective.isAllUnder && importDirective.targetDescriptors().size() == 1) {
@@ -112,11 +101,7 @@ public class DeprecatedSymbolUsageInWholeProjectFix(
continue
}
val bindingContext = usage.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = usage.getResolvedCall(bindingContext) ?: continue
if (!resolvedCall.status.isSuccess) continue
// copy replacement expression because it is modified by performReplacement
performCallReplacement(usage, bindingContext, resolvedCall, replacement.copy())
replacementStrategy.createReplacer(usage)?.invoke()
}
catch (e: Throwable) {
LOG.error(e)
@@ -26,6 +26,8 @@ import org.jetbrains.kotlin.idea.core.copied
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.intentions.InsertExplicitTypeArgumentsIntention
import org.jetbrains.kotlin.idea.references.JetSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
@@ -36,6 +38,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.FileScopeProvider
@@ -50,7 +53,7 @@ import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
data class ReplaceWith(val expression: String, vararg val imports: String)
data class ReplaceWith(val pattern: String, vararg val imports: String)
object ReplaceWithAnnotationAnalyzer {
public val PARAMETER_USAGE_KEY: Key<Name> = Key("PARAMETER_USAGE")
@@ -63,11 +66,11 @@ object ReplaceWithAnnotationAnalyzer {
fun copy() = ReplacementExpression(expression.copied(), fqNamesToImport)
}
public fun analyze(
public fun analyzeCallableReplacement(
annotation: ReplaceWith,
symbolDescriptor: CallableDescriptor,
resolutionFacade: ResolutionFacade
): ReplacementExpression {
): ReplacementExpression? {
val originalDescriptor = (if (symbolDescriptor is CallableMemberDescriptor)
DescriptorUtils.unwrapFakeOverride(symbolDescriptor)
else
@@ -79,23 +82,22 @@ object ReplaceWithAnnotationAnalyzer {
annotation: ReplaceWith,
symbolDescriptor: CallableDescriptor,
resolutionFacade: ResolutionFacade
): ReplacementExpression {
): ReplacementExpression? {
val psiFactory = JetPsiFactory(resolutionFacade.project)
var expression = psiFactory.createExpression(annotation.expression)
val importFqNames = annotation.imports
.filter { FqNameUnsafe.isValid(it) }
.map { FqNameUnsafe(it) }
.filter { it.isSafe }
.mapTo(LinkedHashSet<FqName>()) { it.toSafe() }
val explicitlyImportedSymbols = importFqNames.flatMap { resolutionFacade.resolveImportReference(symbolDescriptor.module, it) }
var expression = try {
psiFactory.createExpression(annotation.pattern)
}
catch(e: Exception) {
return null
}
val module = symbolDescriptor.module
val explicitImportsScope = buildExplicitImportsScope(annotation, resolutionFacade, module)
val additionalScopes = resolutionFacade.getFrontendService(FileScopeProvider.AdditionalScopes::class.java)
val scope = getResolutionScope(symbolDescriptor, symbolDescriptor,
listOf(ExplicitImportsScope(explicitlyImportedSymbols)) + additionalScopes.scopes)
listOf(explicitImportsScope) + additionalScopes.scopes)
var bindingContext = analyzeInContext(expression, symbolDescriptor, scope, resolutionFacade)
var bindingContext = analyzeInContext(expression, module, scope, resolutionFacade)
val typeArgsToAdd = ArrayList<Pair<JetCallExpression, JetTypeArgumentList>>()
expression.forEachDescendantOfType<JetCallExpression> {
@@ -110,10 +112,11 @@ object ReplaceWithAnnotationAnalyzer {
}
// reanalyze expression - new usages of type parameters may be added
bindingContext = analyzeInContext(expression, symbolDescriptor, scope, resolutionFacade)
bindingContext = analyzeInContext(expression, module, scope, resolutionFacade)
}
val receiversToAdd = ArrayList<Pair<JetExpression, JetExpression>>()
val importFqNames = importFqNames(annotation).toMutableSet()
expression.forEachDescendantOfType<JetSimpleNameExpression> { expression ->
val target = bindingContext[BindingContext.REFERENCE_TARGET, expression] ?: return@forEachDescendantOfType
@@ -158,14 +161,70 @@ object ReplaceWithAnnotationAnalyzer {
return ReplacementExpression(expression, importFqNames)
}
public fun analyzeClassReplacement(
annotation: ReplaceWith,
symbolDescriptor: ClassDescriptor,
resolutionFacade: ResolutionFacade
): JetUserType? {
val psiFactory = JetPsiFactory(resolutionFacade.project)
val typeReference = try {
psiFactory.createType(annotation.pattern)
}
catch(e: Exception) {
return null
}
if (typeReference.typeElement !is JetUserType) return null
val module = symbolDescriptor.module
val explicitImportsScope = buildExplicitImportsScope(annotation, resolutionFacade, module)
val scope = getResolutionScope(symbolDescriptor, symbolDescriptor, listOf(explicitImportsScope))
val dummyExpression = psiFactory.createExpressionByPattern("x as $0", typeReference) as JetBinaryExpressionWithTypeRHS
val bindingContext = analyzeInContext(dummyExpression, module, scope, resolutionFacade)
val typesToQualify = ArrayList<Pair<JetNameReferenceExpression, FqName>>()
dummyExpression.right!!.forEachDescendantOfType<JetNameReferenceExpression> { expression ->
val parentType = expression.parent as? JetUserType ?: return@forEachDescendantOfType
if (parentType.qualifier != null) return@forEachDescendantOfType
val targetClass = bindingContext[BindingContext.REFERENCE_TARGET, expression] as? ClassDescriptor ?: return@forEachDescendantOfType
val fqName = targetClass.fqNameUnsafe
if (fqName.isSafe) {
typesToQualify.add(expression to fqName.toSafe())
}
}
for ((nameExpression, fqName) in typesToQualify) {
nameExpression.mainReference.bindToFqName(fqName, JetSimpleNameReference.ShorteningMode.NO_SHORTENING)
}
return dummyExpression.right!!.typeElement as JetUserType
}
private fun buildExplicitImportsScope(annotation: ReplaceWith, resolutionFacade: ResolutionFacade, module: ModuleDescriptor): ExplicitImportsScope {
val importedSymbols = importFqNames(annotation)
.flatMap { resolutionFacade.resolveImportReference(module, it) }
return ExplicitImportsScope(importedSymbols)
}
private fun importFqNames(annotation: ReplaceWith): List<FqName> {
return annotation.imports
.filter { FqNameUnsafe.isValid(it) }
.map { FqNameUnsafe(it) }
.filter { it.isSafe }
.map { it.toSafe() }
}
private fun analyzeInContext(
expression: JetExpression,
symbolDescriptor: CallableDescriptor,
module: ModuleDescriptor,
scope: LexicalScope,
resolutionFacade: ResolutionFacade
): BindingContext {
val traceContext = BindingTraceContext()
resolutionFacade.getFrontendService(symbolDescriptor.module, ExpressionTypingServices::class.java)
resolutionFacade.getFrontendService(module, ExpressionTypingServices::class.java)
.getTypeInfo(scope, expression, TypeUtils.NO_EXPECTED_TYPE, DataFlowInfo.EMPTY, traceContext, false)
return traceContext.bindingContext
}
@@ -180,22 +239,22 @@ object ReplaceWithAnnotationAnalyzer {
is PackageViewDescriptor ->
ChainedScope(ownerDescriptor, "ReplaceWith resolution scope", descriptor.memberScope, *additionalScopes.toTypedArray()).asLexicalScope()
is ClassDescriptorWithResolutionScopes ->
descriptor.scopeForMemberDeclarationResolution
is ClassDescriptor -> {
val outerScope = getResolutionScope(descriptor.containingDeclaration, ownerDescriptor, additionalScopes)
ClassResolutionScopesSupport(descriptor, LockBasedStorageManager.NO_LOCKS, { outerScope }).scopeForMemberDeclarationResolution()
}
is FunctionDescriptor ->
FunctionDescriptorUtil.getFunctionInnerScope(getResolutionScope(descriptor.containingDeclaration, ownerDescriptor, additionalScopes),
descriptor, RedeclarationHandler.DO_NOTHING)
is FunctionDescriptor -> {
val outerScope = getResolutionScope(descriptor.containingDeclaration, ownerDescriptor, additionalScopes)
FunctionDescriptorUtil.getFunctionInnerScope(outerScope, descriptor, RedeclarationHandler.DO_NOTHING)
}
is PropertyDescriptor ->
JetScopeUtils.getPropertyDeclarationInnerScope(descriptor,
getResolutionScope(descriptor.containingDeclaration, ownerDescriptor, additionalScopes),
RedeclarationHandler.DO_NOTHING)
is PropertyDescriptor -> {
val outerScope = getResolutionScope(descriptor.containingDeclaration, ownerDescriptor, additionalScopes)
JetScopeUtils.getPropertyDeclarationInnerScope(descriptor, outerScope, RedeclarationHandler.DO_NOTHING)
}
//TODO: it's not correct (it does not use additionalScopes!), drop this branch
is LocalVariableDescriptor -> {
val declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as JetDeclaration
declaration.analyze()[BindingContext.RESOLUTION_SCOPE, declaration]!!.asLexicalScope()
@@ -0,0 +1,57 @@
/*
* 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.quickfix.replaceWith
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.JetElement
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
interface UsageReplacementStrategy {
fun createReplacer(usage: JetSimpleNameExpression): (() -> JetElement)?
companion object {
fun build(element: JetSimpleNameExpression, replaceWith: ReplaceWith): UsageReplacementStrategy? {
val resolutionFacade = element.getResolutionFacade()
val bindingContext = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL)
val target = element.mainReference.resolveToDescriptors(bindingContext).singleOrNull() ?: return null
// check that ReplaceWith hasn't changed
if (DeprecatedSymbolUsageFixBase.replaceWithPattern(target, resolutionFacade.project) != replaceWith) return null
when (target) {
is CallableDescriptor -> {
val resolvedCall = element.getResolvedCall(bindingContext) ?: return null
if (!resolvedCall.status.isSuccess) return null
val replacement = ReplaceWithAnnotationAnalyzer.analyzeCallableReplacement(replaceWith, target, resolutionFacade) ?: return null
return CallableUsageReplacementStrategy(replacement)
}
is ClassDescriptor -> {
val replacement = ReplaceWithAnnotationAnalyzer.analyzeClassReplacement(replaceWith, target, resolutionFacade) ?: return null
return ClassUsageReplacementStrategy(replacement)
}
else -> return null
}
}
}
}
@@ -0,0 +1,8 @@
// "Replace with 'NewClass'" "true"
import dependency.NewClass
import dependency.OldClass
fun foo(): NewClass? {
return null
}
@@ -0,0 +1,6 @@
package dependency
@deprecated("", ReplaceWith("NewClass"))
class OldClass
class NewClass
@@ -0,0 +1,7 @@
// "Replace with 'NewClass'" "true"
import dependency.OldClass
fun foo(): <caret>OldClass? {
return null
}
@@ -0,0 +1,8 @@
// "Replace with 'File'" "true"
@deprecated("", ReplaceWith("File", "java.io.File"))
class OldClass
fun foo(): OldClass<caret>? {
return null
}
@@ -0,0 +1,10 @@
import java.io.File
// "Replace with 'File'" "true"
@deprecated("", ReplaceWith("File", "java.io.File"))
class OldClass
fun foo(): File? {
return null
}
@@ -0,0 +1,12 @@
// "Replace with 'NewClass'" "true"
class Outer {
@deprecated("", ReplaceWith("NewClass"))
class OldClass
class NewClass
}
fun foo(): Outer.OldClass<caret>? {
return null
}
@@ -0,0 +1,12 @@
// "Replace with 'NewClass'" "true"
class Outer {
@deprecated("", ReplaceWith("NewClass"))
class OldClass
class NewClass
}
fun foo(): Outer.NewClass? {
return null
}
@@ -0,0 +1,12 @@
// "Replace with 'NewClass'" "true"
package ppp
@deprecated("", ReplaceWith("NewClass"))
class OldClass
class NewClass
fun foo(): ppp.OldClass<caret>? {
return null
}
@@ -0,0 +1,12 @@
// "Replace with 'NewClass'" "true"
package ppp
@deprecated("", ReplaceWith("NewClass"))
class OldClass
class NewClass
fun foo(): NewClass? {
return null
}
@@ -0,0 +1,8 @@
// "Replace with 'java.io.File'" "true"
@deprecated("", ReplaceWith("java.io.File"))
class OldClass
fun foo(): OldClass<caret>? {
return null
}
@@ -0,0 +1,10 @@
import java.io.File
// "Replace with 'java.io.File'" "true"
@deprecated("", ReplaceWith("java.io.File"))
class OldClass
fun foo(): File? {
return null
}
@@ -0,0 +1,10 @@
// "Replace with 'NewClass'" "true"
@deprecated("", ReplaceWith("NewClass"))
class OldClass
class NewClass
fun foo(): OldClass<caret>? {
return null
}
@@ -0,0 +1,10 @@
// "Replace with 'NewClass'" "true"
@deprecated("", ReplaceWith("NewClass"))
class OldClass
class NewClass
fun foo(): NewClass? {
return null
}
@@ -906,6 +906,21 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
doTestWithExtraFile(fileName);
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ClassUsages extends AbstractQuickFixMultiFileTest {
@TestMetadata("addImportFromSamePackage.before.Main.kt")
public void testAddImportFromSamePackage() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/addImportFromSamePackage.before.Main.kt");
doTestWithExtraFile(fileName);
}
public void testAllFilesPresentInClassUsages() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/classUsages"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true);
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/imports")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -3312,11 +3312,41 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/classUsages"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("imports.kt")
public void testImports() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/imports.kt");
doTest(fileName);
}
@TestMetadata("nestedClassToNestedClass.kt")
public void testNestedClassToNestedClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/nestedClassToNestedClass.kt");
doTest(fileName);
}
@TestMetadata("noAnnotationConstructorUsage.kt")
public void testNoAnnotationConstructorUsage() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/noAnnotationConstructorUsage.kt");
doTest(fileName);
}
@TestMetadata("qualifiedClassName.kt")
public void testQualifiedClassName() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/qualifiedClassName.kt");
doTest(fileName);
}
@TestMetadata("qualifiedClassNameInPattern.kt")
public void testQualifiedClassNameInPattern() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/qualifiedClassNameInPattern.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/simple.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/functionLiteralArguments")