Deprecated symbol usages fix for the whole project

This commit is contained in:
Valentin Kipyatkov
2015-05-20 16:11:13 +03:00
parent 78302be571
commit 766ca2b905
27 changed files with 780 additions and 344 deletions
@@ -16,371 +16,47 @@
package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.intention.HighPriorityAction
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiRecursiveElementVisitor
import org.jetbrains.kotlin.analyzer.analyzeInContext
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
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.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.asExpression
import org.jetbrains.kotlin.idea.core.refactoring.JetNameSuggester
import org.jetbrains.kotlin.idea.core.refactoring.JetNameValidator
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.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.collectElementsOfType
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi.psiUtil.replaced
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
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
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
//TODO: replacement of class usages
//TODO: different replacements for property accessors
//TODO: replace all in project quickfixes on usage and on deprecated annotation
public class DeprecatedSymbolUsageFix(
element: JetSimpleNameExpression/*TODO?*/,
val replaceWith: ReplaceWith
) : JetIntentionAction<JetSimpleNameExpression>(element) {
replaceWith: ReplaceWith
) : DeprecatedSymbolUsageFixBase(element, replaceWith), HighPriorityAction {
override fun getFamilyName() = "Replace deprecated symbol usage"
override fun getText() = "Replace with '${replaceWith.expression}'" //TODO: substitute?
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.getStatus().isSuccess()) return false
val descriptor = resolvedCall.getResultingDescriptor()
if (replaceWithPattern(descriptor) != replaceWith) return false
try {
JetPsiFactory(project).createExpression(replaceWith.expression)
return true
}
catch(e: Exception) {
return false
}
}
override fun invoke(project: Project, editor: Editor?, file: JetFile) {
val psiFactory = JetPsiFactory(project)
val bindingContext = element.analyze()
val resolvedCall = element.getResolvedCall(bindingContext)!!
val descriptor = resolvedCall.getResultingDescriptor()
val callExpression = resolvedCall.getCall().getCallElement() as JetExpression
val qualifiedExpression = callExpression.getParent() as? JetQualifiedExpression
val expressionToReplace = qualifiedExpression ?: callExpression
var receiver = element.getReceiverExpression()
receiver?.putCopyableUserData(USER_CODE_KEY, Unit)
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
descriptor).getOriginal()
var (expression, imports, parameterUsages) = ReplaceWithAnnotationAnalyzer.analyze(
replaceWith, originalDescriptor, element.getResolutionFacade(), file, project)
//TODO: this@
for (thisExpression in expression.collectElementsOfType<JetThisExpression>()) {
if (receiver != null) {
thisExpression.replace(receiver)
}
else {
thisExpression.putCopyableUserData(FROM_THIS_KEY, Unit)
}
}
fun argumentForParameter(parameter: ValueParameterDescriptor): JetExpression? {
//TODO: optional parameters
val arguments = resolvedCall.getValueArguments()[parameter] ?: return null //TODO: what if not? vararg?
return arguments.getArguments().firstOrNull()?.getArgumentExpression() //TODO: what if multiple?
}
val introduceValuesForParameters = ArrayList<Pair<ValueParameterDescriptor, JetExpression>>()
for (parameter in descriptor.getValueParameters()) {
val argument = argumentForParameter(parameter) ?: continue
argument.putCopyableUserData(FROM_PARAMETER_KEY, parameter)
argument.putCopyableUserData(USER_CODE_KEY, Unit)
val usages = parameterUsages[parameter.getOriginal()]!!
usages.forEach { it.replace(argument) }
if (argument.shouldKeepValue(usages.size())) {
introduceValuesForParameters.add(parameter to argument)
}
}
if (qualifiedExpression is JetSafeQualifiedExpression) {
expression = expression.wrapExpressionForSafeCall(expressionToReplace, receiver!!, bindingContext)
}
else if (callExpression is JetBinaryExpression && callExpression.getOperationToken() == JetTokens.IDENTIFIER) {
expression = expression.keepInfixFormIfPossible()
}
if (receiver != null) {
val thisReplaced = expression.collectElementsOfType<JetExpression> { it.getCopyableUserData(FROM_THIS_KEY) != null }
if (receiver.shouldKeepValue(thisReplaced.size())) {
expression = expression.introduceValue(receiver, expressionToReplace, bindingContext, thisReplaced)
}
}
for ((parameter, value) in introduceValuesForParameters) {
val usagesReplaced = expression.collectElementsOfType<JetExpression> { it.getCopyableUserData(FROM_PARAMETER_KEY) == parameter }
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)?
for (importFqName in imports) {
val descriptors = file.getResolutionFacade().resolveImportReference(file, importFqName)
val descriptorToImport = descriptors.firstOrNull() ?: continue
ImportInsertHelper.getInstance(project).importDescriptor(file, descriptorToImport)
}
val shortenFilter = { element: PsiElement ->
if (element.getCopyableUserData(USER_CODE_KEY) != null) {
ShortenReferences.FilterResult.SKIP
}
else {
val thisReceiver = (element as? JetQualifiedExpression)?.getReceiverExpression() as? JetThisExpression
if (thisReceiver != null && thisReceiver.getCopyableUserData(USER_CODE_KEY) != null) // don't remove explicit 'this' coming from user's code
ShortenReferences.FilterResult.GO_INSIDE
else
ShortenReferences.FilterResult.PROCESS
}
}
result = ShortenReferences({ ShortenReferences.Options(removeThis = true) }).process(result, shortenFilter) as JetExpression
// clean up user data
result.accept(object : PsiRecursiveElementVisitor() {
override fun visitElement(element: PsiElement) {
element.putCopyableUserData(USER_CODE_KEY, null)
element.putCopyableUserData(FROM_PARAMETER_KEY, null)
element.putCopyableUserData(FROM_THIS_KEY, null)
}
})
override fun invoke(
resolvedCall: ResolvedCall<out CallableDescriptor>,
bindingContext: BindingContext,
replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression,
project: Project,
editor: Editor?
) {
val result = DeprecatedSymbolUsageFixBase.performReplacement(element, bindingContext, resolvedCall, replacement)
val offset = (result.getCalleeExpressionIfAny() ?: result).getTextOffset()
editor?.moveCaret(offset)
}
private fun JetExpression.wrapExpressionForSafeCall(
expressionToReplace: JetExpression,
receiver: JetExpression,
bindingContext: BindingContext
): JetExpression {
val psiFactory = JetPsiFactory(this)
val qualified = this as? JetQualifiedExpression
if (qualified != null) {
if (qualified.getReceiverExpression().getCopyableUserData(FROM_THIS_KEY) != null) {
if (qualified is JetSafeQualifiedExpression) return this // already safe
val selector = qualified.getSelectorExpression()
if (selector != null) {
return psiFactory.createExpressionByPattern("$0?.$1", receiver, selector)
}
}
}
if (expressionToReplace.isUsedAsExpression(bindingContext)) {
val thisReplaced = this.collectElementsOfType<JetExpression> { it.getCopyableUserData(FROM_THIS_KEY) != null }
return this.introduceValue(receiver, expressionToReplace, bindingContext, thisReplaced, safeCall = true)
}
else {
return psiFactory.createExpressionByPattern("if ($0 != null) { $1 }", receiver, this)
}
}
private fun JetExpression.keepInfixFormIfPossible(): JetExpression {
if (this !is JetDotQualifiedExpression) return this
val receiver = getReceiverExpression()
if (receiver.getCopyableUserData(FROM_THIS_KEY) == null) return this
val call = getSelectorExpression() as? JetCallExpression ?: return this
val nameExpression = call.getCalleeExpression() as? JetSimpleNameExpression ?: return this
val argument = call.getValueArguments().singleOrNull() ?: return this
if (argument.getArgumentName() != null) return this
val argumentExpression = argument.getArgumentExpression() ?: return this
return JetPsiFactory(this).createExpressionByPattern("$0 ${nameExpression.getText()} $1", receiver, argumentExpression)
}
private fun JetExpression.introduceValue(
value: JetExpression,
insertDeclarationsBefore: JetExpression,
bindingContext: BindingContext,
usages: Collection<JetExpression>,
nameSuggestion: String? = null,
safeCall: Boolean = false
): JetExpression {
assert(usages.all { isAncestor(it, strict = true) })
val psiFactory = JetPsiFactory(this)
fun nameInCode(name: String) = IdeDescriptorRenderers.SOURCE_CODE.renderName(Name.identifier(name))
fun replaceUsages(name: String) {
val nameInCode = psiFactory.createExpression(nameInCode(name))
for (usage in usages) {
usage.replace(nameInCode)
}
}
fun suggestName(validator: JetNameValidator): String {
return if (nameSuggestion != null)
validator.validateName(nameSuggestion)
else
JetNameSuggester.suggestNamesForExpression(value, validator, "t").first()
}
// 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]
if (usages.isNotEmpty()) {
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<JetVariableDeclaration>("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)
}
else {
block.addBefore(value, insertDeclarationsBefore)
block.addBefore(psiFactory.createNewLine(), insertDeclarationsBefore)
}
return this
}
}
val dot = if (safeCall) "?." else "."
if (!isNameUsed("it")) {
replaceUsages("it")
return psiFactory.createExpressionByPattern("$0${dot}let { $1 }", value, this)
}
else {
val name = suggestName(object : JetNameValidator() {
override fun validateInner(name: String) = !isNameUsed(name)
})
replaceUsages(name)
return psiFactory.createExpressionByPattern("$0${dot}let { ${nameInCode(name)} -> $1 }", value, this)
}
}
private fun collectNameUsages(scope: JetExpression, name: String)
= scope.collectElementsOfType<JetSimpleNameExpression> { it.getReceiverExpression() == null && it.getReferencedName() == name }
private fun JetExpression?.shouldKeepValue(usageCount: Int): Boolean {
if (usageCount == 1) return false
val sideEffectOnly = usageCount == 0
return when (this) {
is JetSimpleNameExpression -> false
is JetQualifiedExpression -> getReceiverExpression().shouldKeepValue(usageCount) || getSelectorExpression().shouldKeepValue(usageCount)
is JetUnaryExpression -> getOperationToken() in setOf(JetTokens.PLUSPLUS, JetTokens.MINUSMINUS) || getBaseExpression().shouldKeepValue(usageCount)
is JetStringTemplateExpression -> getEntries().any { if (sideEffectOnly) it.getExpression().shouldKeepValue(usageCount) else it is JetStringTemplateEntryWithExpression }
is JetThisExpression, is JetSuperExpression, is JetConstantExpression -> false
is JetParenthesizedExpression -> getExpression().shouldKeepValue(usageCount)
// TODO: discuss it
is JetBinaryExpression -> if (sideEffectOnly) getLeft().shouldKeepValue(usageCount) || getRight().shouldKeepValue(usageCount) else true
is JetIfExpression -> if (sideEffectOnly) getCondition().shouldKeepValue(usageCount) || getThen().shouldKeepValue(usageCount) || getElse().shouldKeepValue(usageCount) else true
is JetBinaryExpressionWithTypeRHS -> true
else -> true // what else it can be?
}
}
companion object : JetSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val nameExpression = diagnostic.getPsiElement() as? JetSimpleNameExpression ?: return null
val descriptor = Errors.DEPRECATED_SYMBOL_WITH_MESSAGE.cast(diagnostic).getA()
val replacement = replaceWithPattern(descriptor) ?: return null
val replacement = DeprecatedSymbolUsageFixBase.replaceWithPattern(descriptor) ?: return null
return DeprecatedSymbolUsageFix(nameExpression, replacement)
}
private fun replaceWithPattern(descriptor: DeclarationDescriptor): ReplaceWith? {
val annotationClass = descriptor.builtIns.getDeprecatedAnnotation()
val annotation = descriptor.getAnnotations().findAnnotation(DescriptorUtils.getFqNameSafe(annotationClass))!!
val replaceWithValue = annotation.getAllValueArguments().entrySet()
.singleOrNull { it.key.getName().asString() == "replaceWith"/*TODO*/ }
?.value?.getValue() as? AnnotationDescriptor ?: return null
val pattern = replaceWithValue.getAllValueArguments().entrySet()
.singleOrNull { it.key.getName().asString() == "expression"/*TODO*/ }
?.value?.getValue() as? String ?: return null
if (pattern.isEmpty()) return null
val argument = replaceWithValue.getAllValueArguments().entrySet().singleOrNull { it.key.getName().asString() == "imports"/*TODO*/ }?.value
val imports = (argument?.getValue() as? List<CompileTimeConstant<String>>)?.map { it.getValue() } ?: emptyList()
return ReplaceWith(pattern, *imports.toTypedArray())
}
private val USER_CODE_KEY = Key<Unit>("USER_CODE")
private val FROM_PARAMETER_KEY = Key<ValueParameterDescriptor>("FROM_PARAMETER")
private val FROM_THIS_KEY = Key<Unit>("FROM_THIS")
}
}
@@ -0,0 +1,388 @@
/*
* 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
import com.intellij.codeInsight.intention.HighPriorityAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiRecursiveElementVisitor
import org.jetbrains.kotlin.analyzer.analyzeInContext
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.asExpression
import org.jetbrains.kotlin.idea.core.refactoring.JetNameSuggester
import org.jetbrains.kotlin.idea.core.refactoring.JetNameValidator
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.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.collectElementsOfType
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi.psiUtil.replaced
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
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
//TODO: replacement of class usages
//TODO: different replacements for property accessors
public abstract class DeprecatedSymbolUsageFixBase(
element: JetSimpleNameExpression/*TODO?*/,
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.getStatus().isSuccess()) return false
val descriptor = resolvedCall.getResultingDescriptor()
if (replaceWithPattern(descriptor) != replaceWith) return false
try {
JetPsiFactory(project).createExpression(replaceWith.expression)
return true
}
catch(e: Exception) {
return false
}
}
final override fun invoke(project: Project, editor: Editor?, file: JetFile) {
val bindingContext = element.analyze()
val resolvedCall = element.getResolvedCall(bindingContext)!!
val descriptor = resolvedCall.getResultingDescriptor()
val replacement = ReplaceWithAnnotationAnalyzer.analyze(replaceWith, descriptor, element.getResolutionFacade(), file, project)
invoke(resolvedCall, bindingContext, replacement, project, editor)
}
protected abstract fun invoke(
resolvedCall: ResolvedCall<out CallableDescriptor>,
bindingContext: BindingContext,
replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression,
project: Project,
editor: Editor?)
companion object {
public fun replaceWithPattern(descriptor: DeclarationDescriptor): ReplaceWith? {
val annotationClass = descriptor.builtIns.getDeprecatedAnnotation()
val annotation = descriptor.getAnnotations().findAnnotation(DescriptorUtils.getFqNameSafe(annotationClass))!!
val replaceWithValue = annotation.getAllValueArguments().entrySet()
.singleOrNull { it.key.getName().asString() == "replaceWith"/*TODO*/ }
?.value?.getValue() as? AnnotationDescriptor ?: return null
val pattern = replaceWithValue.getAllValueArguments().entrySet()
.singleOrNull { it.key.getName().asString() == "expression"/*TODO*/ }
?.value?.getValue() as? String ?: return null
if (pattern.isEmpty()) return null
val argument = replaceWithValue.getAllValueArguments().entrySet().singleOrNull { it.key.getName().asString() == "imports"/*TODO*/ }?.value
val imports = (argument?.getValue() as? List<CompileTimeConstant<String>>)?.map { it.getValue() } ?: emptyList()
return ReplaceWith(pattern, *imports.toTypedArray())
}
public fun performReplacement(
element: JetSimpleNameExpression,
bindingContext: BindingContext,
resolvedCall: ResolvedCall<out CallableDescriptor>,
replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression
): JetExpression {
var (expression, imports, parameterUsages) = replacement
val project = element.getProject()
val psiFactory = JetPsiFactory(project)
val descriptor = resolvedCall.getResultingDescriptor()
val callExpression = resolvedCall.getCall().getCallElement() as JetExpression
val qualifiedExpression = callExpression.getParent() as? JetQualifiedExpression
val expressionToReplace = qualifiedExpression ?: callExpression
var receiver = element.getReceiverExpression()
receiver?.putCopyableUserData(USER_CODE_KEY, Unit)
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)
//TODO: this@
for (thisExpression in expression.collectElementsOfType<JetThisExpression>()) {
if (receiver != null) {
thisExpression.replace(receiver)
}
else {
thisExpression.putCopyableUserData(FROM_THIS_KEY, Unit)
}
}
fun argumentForParameter(parameter: ValueParameterDescriptor): JetExpression? {
//TODO: optional parameters
val arguments = resolvedCall.getValueArguments()[parameter] ?: return null //TODO: what if not? vararg?
return arguments.getArguments().firstOrNull()?.getArgumentExpression() //TODO: what if multiple?
}
val introduceValuesForParameters = ArrayList<Pair<ValueParameterDescriptor, JetExpression>>()
for (parameter in descriptor.getValueParameters()) {
val argument = argumentForParameter(parameter) ?: continue
argument.putCopyableUserData(FROM_PARAMETER_KEY, parameter)
argument.putCopyableUserData(USER_CODE_KEY, Unit)
val usages = parameterUsages[parameter.getOriginal()]!!
usages.forEach { it.replace(argument) }
if (argument.shouldKeepValue(usages.size())) {
introduceValuesForParameters.add(parameter to argument)
}
}
if (qualifiedExpression is JetSafeQualifiedExpression) {
expression = expression.wrapExpressionForSafeCall(expressionToReplace, receiver!!, bindingContext)
}
else if (callExpression is JetBinaryExpression && callExpression.getOperationToken() == JetTokens.IDENTIFIER) {
expression = expression.keepInfixFormIfPossible()
}
if (receiver != null) {
val thisReplaced = expression.collectElementsOfType<JetExpression> { it.getCopyableUserData(FROM_THIS_KEY) != null }
if (receiver.shouldKeepValue(thisReplaced.size())) {
expression = expression.introduceValue(receiver, expressionToReplace, bindingContext, thisReplaced)
}
}
for ((parameter, value) in introduceValuesForParameters) {
val usagesReplaced = expression.collectElementsOfType<JetExpression> { it.getCopyableUserData(FROM_PARAMETER_KEY) == parameter }
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)?
val file = result.getContainingJetFile()
for (importFqName in imports) {
val descriptors = file.getResolutionFacade().resolveImportReference(file, importFqName)
val descriptorToImport = descriptors.firstOrNull() ?: continue
ImportInsertHelper.getInstance(project).importDescriptor(file, descriptorToImport)
}
val shortenFilter = { element: PsiElement ->
if (element.getCopyableUserData(USER_CODE_KEY) != null) {
ShortenReferences.FilterResult.SKIP
}
else {
val thisReceiver = (element as? JetQualifiedExpression)?.getReceiverExpression() as? JetThisExpression
if (thisReceiver != null && thisReceiver.getCopyableUserData(USER_CODE_KEY) != null) // don't remove explicit 'this' coming from user's code
ShortenReferences.FilterResult.GO_INSIDE
else
ShortenReferences.FilterResult.PROCESS
}
}
result = ShortenReferences({ ShortenReferences.Options(removeThis = true) }).process(result, shortenFilter) as JetExpression
// clean up user data
result.accept(object : PsiRecursiveElementVisitor() {
override fun visitElement(element: PsiElement) {
element.putCopyableUserData(USER_CODE_KEY, null)
element.putCopyableUserData(FROM_PARAMETER_KEY, null)
element.putCopyableUserData(FROM_THIS_KEY, null)
}
})
return result
}
private fun JetExpression.wrapExpressionForSafeCall(
expressionToReplace: JetExpression,
receiver: JetExpression,
bindingContext: BindingContext
): JetExpression {
val psiFactory = JetPsiFactory(this)
val qualified = this as? JetQualifiedExpression
if (qualified != null) {
if (qualified.getReceiverExpression().getCopyableUserData(FROM_THIS_KEY) != null) {
if (qualified is JetSafeQualifiedExpression) return this // already safe
val selector = qualified.getSelectorExpression()
if (selector != null) {
return psiFactory.createExpressionByPattern("$0?.$1", receiver, selector)
}
}
}
if (expressionToReplace.isUsedAsExpression(bindingContext)) {
val thisReplaced = this.collectElementsOfType<JetExpression> { it.getCopyableUserData(FROM_THIS_KEY) != null }
return this.introduceValue(receiver, expressionToReplace, bindingContext, thisReplaced, safeCall = true)
}
else {
return psiFactory.createExpressionByPattern("if ($0 != null) { $1 }", receiver, this)
}
}
private fun JetExpression.keepInfixFormIfPossible(): JetExpression {
if (this !is JetDotQualifiedExpression) return this
val receiver = getReceiverExpression()
if (receiver.getCopyableUserData(FROM_THIS_KEY) == null) return this
val call = getSelectorExpression() as? JetCallExpression ?: return this
val nameExpression = call.getCalleeExpression() as? JetSimpleNameExpression ?: return this
val argument = call.getValueArguments().singleOrNull() ?: return this
if (argument.getArgumentName() != null) return this
val argumentExpression = argument.getArgumentExpression() ?: return this
return JetPsiFactory(this).createExpressionByPattern("$0 ${nameExpression.getText()} $1", receiver, argumentExpression)
}
private fun JetExpression.introduceValue(
value: JetExpression,
insertDeclarationsBefore: JetExpression,
bindingContext: BindingContext,
usages: Collection<JetExpression>,
nameSuggestion: String? = null,
safeCall: Boolean = false
): JetExpression {
assert(usages.all { isAncestor(it, strict = true) })
val psiFactory = JetPsiFactory(this)
fun nameInCode(name: String) = IdeDescriptorRenderers.SOURCE_CODE.renderName(Name.identifier(name))
fun replaceUsages(name: String) {
val nameInCode = psiFactory.createExpression(nameInCode(name))
for (usage in usages) {
usage.replace(nameInCode)
}
}
fun suggestName(validator: JetNameValidator): String {
return if (nameSuggestion != null)
validator.validateName(nameSuggestion)
else
JetNameSuggester.suggestNamesForExpression(value, validator, "t").first()
}
// 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]
if (usages.isNotEmpty()) {
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<JetVariableDeclaration>("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)
}
else {
block.addBefore(value, insertDeclarationsBefore)
block.addBefore(psiFactory.createNewLine(), insertDeclarationsBefore)
}
return this
}
}
val dot = if (safeCall) "?." else "."
if (!isNameUsed("it")) {
replaceUsages("it")
return psiFactory.createExpressionByPattern("$0${dot}let { $1 }", value, this)
}
else {
val name = suggestName(object : JetNameValidator() {
override fun validateInner(name: String) = !isNameUsed(name)
})
replaceUsages(name)
return psiFactory.createExpressionByPattern("$0${dot}let { ${nameInCode(name)} -> $1 }", value, this)
}
}
private fun collectNameUsages(scope: JetExpression, name: String)
= scope.collectElementsOfType<JetSimpleNameExpression> { it.getReceiverExpression() == null && it.getReferencedName() == name }
private fun JetExpression?.shouldKeepValue(usageCount: Int): Boolean {
if (usageCount == 1) return false
val sideEffectOnly = usageCount == 0
return when (this) {
is JetSimpleNameExpression -> false
is JetQualifiedExpression -> getReceiverExpression().shouldKeepValue(usageCount) || getSelectorExpression().shouldKeepValue(usageCount)
is JetUnaryExpression -> getOperationToken() in setOf(JetTokens.PLUSPLUS, JetTokens.MINUSMINUS) || getBaseExpression().shouldKeepValue(usageCount)
is JetStringTemplateExpression -> getEntries().any { if (sideEffectOnly) it.getExpression().shouldKeepValue(usageCount) else it is JetStringTemplateEntryWithExpression }
is JetThisExpression, is JetSuperExpression, is JetConstantExpression -> false
is JetParenthesizedExpression -> getExpression().shouldKeepValue(usageCount)
// TODO: discuss it
is JetBinaryExpression -> if (sideEffectOnly) getLeft().shouldKeepValue(usageCount) || getRight().shouldKeepValue(usageCount) else true
is JetIfExpression -> if (sideEffectOnly) getCondition().shouldKeepValue(usageCount) || getThen().shouldKeepValue(usageCount) || getElse().shouldKeepValue(usageCount) else true
is JetBinaryExpressionWithTypeRHS -> true
else -> true // what else it can be?
}
}
private val USER_CODE_KEY = Key<Unit>("USER_CODE")
private val FROM_PARAMETER_KEY = Key<ValueParameterDescriptor>("FROM_PARAMETER")
private val FROM_THIS_KEY = Key<Unit>("FROM_THIS")
}
}
@@ -0,0 +1,180 @@
/*
* 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
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.find.findUsages.FindUsagesOptions
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.ex.MessagesEx
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.usageView.UsageInfo
import com.intellij.util.CommonProcessors
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.findUsages.KotlinFindUsagesHandlerFactory
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.JetNamedFunction
import org.jetbrains.kotlin.psi.JetProperty
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRendererBuilder
import org.jetbrains.kotlin.renderer.NameShortness
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,
replaceWith: ReplaceWith,
private val text: String
) : DeprecatedSymbolUsageFixBase(element, replaceWith) {
private val LOG = Logger.getInstance(javaClass<DeprecatedSymbolUsageInWholeProjectFix>());
override fun getFamilyName() = "Replace deprecated symbol usage in whole project"
override fun getText() = text
override fun startInWriteAction() = false
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
if (!super.isAvailable(project, editor, file)) return false
val targetPsiElement = element.getReference()?.resolve()
return targetPsiElement is JetNamedFunction || targetPsiElement is JetProperty
}
override fun invoke(
resolvedCall: ResolvedCall<out CallableDescriptor>,
bindingContext: BindingContext,
replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression,
project: Project,
editor: Editor?
) {
val psiElement = element.getReference()!!.resolve()!!
ProgressManager.getInstance().run(
object : Task.Modal(project, "Applying '$text'", true) {
override fun run(indicator: ProgressIndicator) {
val usages = runReadAction {
val searchScope = JetSourceFilterScope.kotlinSources(GlobalSearchScope.projectScope(project), project)
val findUsagesHandler = KotlinFindUsagesHandlerFactory(project).createFindUsagesHandler(psiElement, false)!!
val processor = CommonProcessors.CollectProcessor<UsageInfo>()
val options = createFindUsagesOptions(psiElement, searchScope, project)
findUsagesHandler.processElementUsages(psiElement, processor, options)
processor.getResults().map { it.getElement() }.filterIsInstance<JetSimpleNameExpression>()
}
replaceUsages(project, usages, replacement)
}
})
}
private fun createFindUsagesOptions(element: PsiElement, searchScope: GlobalSearchScope, project: Project): FindUsagesOptions {
val options: FindUsagesOptions = when (element) {
is JetNamedFunction -> {
with(KotlinFunctionFindUsagesOptions(project)) {
isSkipImportStatements = true
isOverridingMethods = false
isImplementingMethods = false
isIncludeInherited = false
isIncludeOverloadUsages = false
this
}
}
is JetProperty -> {
with(KotlinPropertyFindUsagesOptions(project)) {
isSkipImportStatements = true
this
}
}
else -> throw IllegalArgumentException(element.toString()) //TODO?
}
options.searchScope = searchScope
options.isSearchForTextOccurrences = false
return options
}
private fun replaceUsages(project: Project, usages: Collection<JetSimpleNameExpression>, replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression) {
UIUtil.invokeLaterIfNeeded {
var replacedCount = 0
project.executeCommand(getText()) {
runWriteAction {
for (usage in usages) {
try {
if (!usage.isValid()) continue // TODO: nested calls
val bindingContext = usage.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = element.getResolvedCall(bindingContext) ?: continue
if (!resolvedCall.getStatus().isSuccess()) continue
DeprecatedSymbolUsageFixBase.performReplacement(usage, bindingContext, resolvedCall, replacement)
replacedCount++
}
catch (e: Throwable) {
LOG.error(e)
}
}
}
}
if (!ApplicationManager.getApplication().isUnitTestMode()) {
MessagesEx.showInfoMessage(null, "$replacedCount usages found and replaced", getText())
}
}
}
companion object : JetSingleIntentionActionFactory() {
//TODO: better rendering needed
private val RENDERER = DescriptorRendererBuilder()
.setModifiers()
.setNameShortness(NameShortness.SHORT)
// .setWithoutTypeParameters(true)
.setParameterNameRenderingPolicy(DescriptorRenderer.ParameterNameRenderingPolicy.NONE)
.setReceiverAfterName(true)
.setRenderCompanionObjectName(true)
.setWithoutSuperTypes(true)
.setStartFromName(true)
.setWithDefinedIn(false)
.build()
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val nameExpression = diagnostic.getPsiElement() as? JetSimpleNameExpression ?: return null
val descriptor = Errors.DEPRECATED_SYMBOL_WITH_MESSAGE.cast(diagnostic).getA()
val replacement = DeprecatedSymbolUsageFixBase.replaceWithPattern(descriptor) ?: return null
val descriptorName = RENDERER.render(descriptor)
return DeprecatedSymbolUsageInWholeProjectFix(nameExpression, replacement, "Replace usages of '$descriptorName' in whole project")
}
}
}
@@ -345,5 +345,6 @@ public class QuickFixRegistrar {
QuickFixes.factories.put(UNRESOLVED_REFERENCE, ReplaceObsoleteLabelSyntaxFix.Companion.createWholeProjectFixFactory());
QuickFixes.factories.put(DEPRECATED_SYMBOL_WITH_MESSAGE, DeprecatedSymbolUsageFix.Companion);
QuickFixes.factories.put(DEPRECATED_SYMBOL_WITH_MESSAGE, DeprecatedSymbolUsageInWholeProjectFix.Companion);
}
}
@@ -22,11 +22,12 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiRecursiveElementVisitor
import org.jetbrains.kotlin.analyzer.analyzeInContext
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.asExpression
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
@@ -35,6 +36,8 @@ import org.jetbrains.kotlin.psi.psiUtil.collectElementsOfType
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.psi.psiUtil.replaced
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
@@ -43,7 +46,8 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver
import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.util.*
import java.util.ArrayList
import java.util.LinkedHashSet
//TODO: use ReplaceWith from package kotlin
data class ReplaceWith(val expression: String, vararg val imports: String)
@@ -61,6 +65,20 @@ object ReplaceWithAnnotationAnalyzer {
resolutionFacade: ResolutionFacade,
file: JetFile/*TODO: drop it*/,
project: Project
): ReplacementExpression {
val originalDescriptor = (if (symbolDescriptor is CallableMemberDescriptor)
DescriptorUtils.unwrapFakeOverride(symbolDescriptor)
else
symbolDescriptor).getOriginal()
return analyzeOriginal(annotation, originalDescriptor, resolutionFacade, file, project)
}
private fun analyzeOriginal(
annotation: ReplaceWith,
symbolDescriptor: CallableDescriptor,
resolutionFacade: ResolutionFacade,
file: JetFile/*TODO: drop it*/,
project: Project
): ReplacementExpression {
val psiFactory = JetPsiFactory(project)
var expression = psiFactory.createExpression(annotation.expression)
@@ -159,7 +177,12 @@ object ReplaceWithAnnotationAnalyzer {
JetScopeUtils.getPropertyDeclarationInnerScope(descriptor,
getResolutionScope(descriptor.getContainingDeclaration()!!),
RedeclarationHandler.DO_NOTHING)
is LocalVariableDescriptor -> {
val declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as JetDeclaration
declaration.analyze()[BindingContext.RESOLUTION_SCOPE, declaration]
}
//TODO?
else -> throw IllegalArgumentException("Cannot find resolution scope for $descriptor")
}
}
@@ -0,0 +1,8 @@
// "Replace with '1'" "true"
fun foo(): Int {
@deprecated("", ReplaceWith("1"))
fun localFun() = 1
return <caret>localFun()
}
@@ -0,0 +1,8 @@
// "Replace with '1'" "true"
fun foo(): Int {
@deprecated("", ReplaceWith("1"))
fun localFun() = 1
return <caret>1
}
@@ -0,0 +1,8 @@
// "Replace with '1'" "true"
fun foo(): Int {
@deprecated("", ReplaceWith("1"))
val localVar = 1
return <caret>localVar
}
@@ -0,0 +1,8 @@
// "Replace with '1'" "true"
fun foo(): Int {
@deprecated("", ReplaceWith("1"))
val localVar = 1
return <caret>1
}
@@ -0,0 +1,11 @@
import newPack.newFun
import pack.oldFun
fun x() {
newFun(1 + 1)
newFun(2 + 1)
}
fun y() {
newFun(3 + 1)
}
@@ -0,0 +1,7 @@
import pack.PackPackage;
class C {
void foo() {
PackPackage.oldFun(-1);
}
}
@@ -0,0 +1,3 @@
package newPack
fun newFun(p: Int){}
@@ -0,0 +1,6 @@
package pack
@deprecated("", ReplaceWith("newFun(p + 1)", "newPack.newFun"))
fun oldFun(p: Int) {
newFun(p + 1)
}
@@ -0,0 +1,8 @@
// "Replace usages of 'oldFun(Int): Unit' in whole project" "true"
import newPack.newFun
import pack.oldFun
fun foo() {
<caret>newFun(0 + 1)
}
@@ -0,0 +1,10 @@
import pack.oldFun
fun x() {
oldFun(1)
oldFun(2)
}
fun y() {
oldFun(3)
}
@@ -0,0 +1,7 @@
import pack.PackPackage;
class C {
void foo() {
PackPackage.oldFun(-1);
}
}
@@ -0,0 +1,7 @@
// "Replace usages of 'oldFun(Int): Unit' in whole project" "true"
import pack.oldFun
fun foo() {
<caret>oldFun(0)
}
@@ -0,0 +1,3 @@
package newPack
fun newFun(p: Int){}
@@ -0,0 +1,6 @@
package pack
@deprecated("", ReplaceWith("newFun(p + 1)", "newPack.newFun"))
fun oldFun(p: Int) {
newFun(p + 1)
}
@@ -0,0 +1,3 @@
fun x() {
pack.bar(pack.newProp)
}
@@ -0,0 +1,7 @@
package pack
@deprecated("", ReplaceWith("newProp"))
val oldProp: String = ""
fun foo(s: String){}
fun bar(s: String){}
@@ -0,0 +1,7 @@
// "Replace usages of 'oldProp: String' in whole project" "true"
import pack.*
fun foo() {
foo(<caret>newProp)
}
@@ -0,0 +1,3 @@
fun x() {
pack.bar(pack.oldProp)
}
@@ -0,0 +1,7 @@
package pack
@deprecated("", ReplaceWith("newProp"))
val oldProp: String = ""
fun foo(s: String){}
fun bar(s: String){}
@@ -0,0 +1,7 @@
// "Replace usages of 'oldProp: String' in whole project" "true"
import pack.*
fun foo() {
foo(<caret>oldProp)
}
@@ -881,6 +881,27 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/toMethodFromCompanionObject.before.Main.kt");
doTestWithExtraFile(fileName);
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/wholeProject")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class WholeProject extends AbstractQuickFixMultiFileTest {
public void testAllFilesPresentInWholeProject() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/wholeProject"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true);
}
@TestMetadata("function.before.Main.kt")
public void testFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/wholeProject/function.before.Main.kt");
doTestWithExtraFile(fileName);
}
@TestMetadata("property.before.Main.kt")
public void testProperty() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/wholeProject/property.before.Main.kt");
doTestWithExtraFile(fileName);
}
}
}
@TestMetadata("idea/testData/quickfix/migration")
@@ -3070,6 +3070,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName);
}
@TestMetadata("localFun.kt")
public void testLocalFun() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/localFun.kt");
doTest(fileName);
}
@TestMetadata("localVar.kt")
public void testLocalVar() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/localVar.kt");
doTest(fileName);
}
@TestMetadata("memberFunction.kt")
public void testMemberFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/memberFunction.kt");
@@ -3195,6 +3207,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/usageInDerivedClassGeneric.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/expressions")
@@ -3624,7 +3637,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
@RunWith(JUnit3RunnerWithInners.class)
public static class MissingConstructorKeyword extends AbstractQuickFixTest {
public void testAllFilesPresentInMissingConstructorKeyword() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/missingConstructorKeyword"), Pattern.compile("^(\\w+)\\.kt$"), true);
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/missingConstructorKeyword"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("basic.kt")
@@ -3639,7 +3652,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
@RunWith(JUnit3RunnerWithInners.class)
public static class ObsoleteLabelSyntax extends AbstractQuickFixTest {
public void testAllFilesPresentInObsoleteLabelSyntax() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/obsoleteLabelSyntax"), Pattern.compile("^(\\w+)\\.kt$"), true);
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/obsoleteLabelSyntax"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("lambda.kt")
@@ -3660,7 +3673,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
@RunWith(JUnit3RunnerWithInners.class)
public static class RemoveNameFromFunctionExpression extends AbstractQuickFixTest {
public void testAllFilesPresentInRemoveNameFromFunctionExpression() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/removeNameFromFunctionExpression"), Pattern.compile("^(\\w+)\\.kt$"), true);
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/removeNameFromFunctionExpression"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("basic.kt")