Code cleanup to remove deprecated symbol imports that have ReplaceWith
This commit is contained in:
@@ -19,11 +19,15 @@ package org.jetbrains.kotlin.idea.core
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getFileTopLevelScope
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstanceToExpression
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
@@ -102,3 +106,8 @@ public fun PsiElement.getResolutionScope(bindingContext: BindingContext, resolut
|
||||
}
|
||||
error("Not in JetFile")
|
||||
}
|
||||
|
||||
public fun JetImportDirective.targetDescriptors(): Collection<DeclarationDescriptor> {
|
||||
val nameExpression = importedReference?.getQualifiedElementSelector() as? JetSimpleNameExpression ?: return emptyList()
|
||||
return nameExpression.mainReference.resolveToDescriptors(nameExpression.analyze())
|
||||
}
|
||||
|
||||
@@ -29,16 +29,17 @@ import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
|
||||
import org.jetbrains.kotlin.idea.highlighter.JetPsiChecker
|
||||
import org.jetbrains.kotlin.idea.quickfix.CleanupFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.DeprecatedSymbolUsageFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.JetIntentionAction
|
||||
import org.jetbrains.kotlin.idea.quickfix.ReplaceObsoleteLabelSyntaxFix
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.psi.JetAnnotationEntry
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.JetImportDirective
|
||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspectionTool {
|
||||
// required to simplify the inspection registration in tests
|
||||
@@ -61,6 +62,14 @@ public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspe
|
||||
}
|
||||
|
||||
val problems = arrayListOf<ProblemData>()
|
||||
|
||||
val importsToRemove = file.importDirectives.filter { DeprecatedSymbolUsageFix.isImportToBeRemoved(it) }
|
||||
for (import in importsToRemove) {
|
||||
val removeImportFix = RemoveImportFix(import)
|
||||
val problemDescriptor = createProblemDescriptor(import, removeImportFix.text, listOf(removeImportFix), file, manager)
|
||||
problems.add(ProblemData(problemDescriptor, import))
|
||||
}
|
||||
|
||||
file.forEachDescendantOfType<PsiElement> { element ->
|
||||
for (diagnostic in diagnostics.forElement(element)) {
|
||||
if (diagnostic.isCleanup()) {
|
||||
@@ -111,10 +120,23 @@ public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspe
|
||||
}
|
||||
|
||||
private fun Diagnostic.toProblemDescriptor(fixes: Collection<CleanupFix>, file: JetFile, manager: InspectionManager): ProblemDescriptor {
|
||||
return manager.createProblemDescriptor(getPsiElement(),
|
||||
DefaultErrorMessages.render(this),
|
||||
false,
|
||||
fixes.map { Wrapper(it, file) }.toTypedArray(),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
|
||||
return createProblemDescriptor(psiElement, DefaultErrorMessages.render(this), fixes, file, manager)
|
||||
}
|
||||
|
||||
private fun createProblemDescriptor(element: PsiElement, message: String, fixes: Collection<CleanupFix>, file: JetFile, manager: InspectionManager): ProblemDescriptor {
|
||||
return manager.createProblemDescriptor(element,
|
||||
message,
|
||||
false,
|
||||
fixes.map { Wrapper(it, file) }.toTypedArray(),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
|
||||
}
|
||||
|
||||
private class RemoveImportFix(import: JetImportDirective) : JetIntentionAction<JetImportDirective>(import), CleanupFix {
|
||||
override fun getFamilyName() = "Remove deprecated symbol import"
|
||||
override fun getText() = familyName
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: JetFile) {
|
||||
element.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,15 +41,12 @@ import com.intellij.psi.PsiFile
|
||||
import com.intellij.util.DocumentUtil
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.core.targetDescriptors
|
||||
import org.jetbrains.kotlin.idea.imports.KotlinImportOptimizer
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
|
||||
import org.jetbrains.kotlin.resolve.ImportPath
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
|
||||
import java.util.*
|
||||
|
||||
class UnusedImportInspection : AbstractKotlinInspection() {
|
||||
@@ -104,8 +101,7 @@ class UnusedImportInspection : AbstractKotlinInspection() {
|
||||
}
|
||||
|
||||
if (!isUsed) {
|
||||
val nameExpression = directive.importedReference?.getQualifiedElementSelector() as? JetSimpleNameExpression
|
||||
if (nameExpression == null || nameExpression.getReferenceTargets(nameExpression.analyze()).isEmpty()) continue // do not highlight unresolved imports as unused
|
||||
if (directive.targetDescriptors().isEmpty()) continue // do not highlight unresolved imports as unused
|
||||
|
||||
val fixes = arrayListOf<LocalQuickFix>()
|
||||
fixes.add(OptimizeImportsQuickFix(file))
|
||||
|
||||
@@ -24,6 +24,7 @@ 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
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
|
||||
@@ -72,5 +73,9 @@ public class DeprecatedSymbolUsageFix(
|
||||
return DeprecatedSymbolUsageFix(nameExpression, replacement)
|
||||
}
|
||||
|
||||
public fun isImportToBeRemoved(import: JetImportDirective): Boolean {
|
||||
return !import.isAllUnder
|
||||
&& import.targetDescriptors().all { DeprecatedSymbolUsageFixBase.replaceWithPattern(it, import.project) != null }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-22
@@ -17,31 +17,16 @@
|
||||
package org.jetbrains.kotlin.idea.refactoring.safeDelete
|
||||
|
||||
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceSimpleDeleteUsageInfo
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.core.targetDescriptors
|
||||
import org.jetbrains.kotlin.psi.JetDeclaration
|
||||
import org.jetbrains.kotlin.psi.JetImportDirective
|
||||
|
||||
public class SafeDeleteImportDirectiveUsageInfo(
|
||||
importDirective: JetImportDirective, declaration: JetDeclaration
|
||||
) : SafeDeleteReferenceSimpleDeleteUsageInfo(importDirective, declaration, importDirective.isSafeToDelete(declaration))
|
||||
|
||||
fun JetImportDirective.isSafeToDelete(declaration: JetDeclaration): Boolean {
|
||||
val importExpr = getImportedReference()
|
||||
val importReference: JetReferenceExpression? = when (importExpr) {
|
||||
is JetSimpleNameExpression ->
|
||||
importExpr
|
||||
is JetDotQualifiedExpression ->
|
||||
importExpr.getSelectorExpression()?.let { selector ->
|
||||
if (selector is JetSimpleNameExpression) selector else null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (importReference != null) {
|
||||
val bindingContext = importReference.analyze()
|
||||
val referenceDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, importReference)
|
||||
val declarationDescriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration)
|
||||
return referenceDescriptor == declarationDescriptor
|
||||
}
|
||||
return false
|
||||
private fun JetImportDirective.isSafeToDelete(declaration: JetDeclaration): Boolean {
|
||||
val referencedDescriptor = targetDescriptors().singleOrNull() ?: return false
|
||||
return referencedDescriptor == declaration.resolveToDescriptor()
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getFileTopLevelScope
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.core.formatter.JetCodeStyleSettings
|
||||
import org.jetbrains.kotlin.idea.core.targetDescriptors
|
||||
import org.jetbrains.kotlin.idea.imports.getImportableTargets
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
|
||||
@@ -299,9 +300,8 @@ public class ImportInsertHelperImpl(private val project: Project) : ImportInsert
|
||||
|
||||
val importsToCheck = ArrayList<FqName>()
|
||||
for (import in dropCandidates) {
|
||||
val importedReference = import.getImportedReference() ?: continue
|
||||
val refExpr = (importedReference as JetDotQualifiedExpression).getSelectorExpression() as JetSimpleNameExpression
|
||||
val targets = refExpr.resolveTargets()
|
||||
if (import.importedReference == null) continue
|
||||
val targets = import.targetDescriptors()
|
||||
if (targets.any { it is PackageViewDescriptor }) continue // do not drop import of package
|
||||
val classDescriptor = targets.filterIsInstance<ClassDescriptor>().firstOrNull()
|
||||
importsToCheck.addIfNotNull(classDescriptor?.importableFqName)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import pack.oldFun1
|
||||
import pack.oldFun2 // should not be removed for non-deprecated overload used
|
||||
import pack.oldFun3
|
||||
|
||||
trait Foo {
|
||||
}
|
||||
|
||||
@@ -16,6 +20,8 @@ fun foo() {
|
||||
}
|
||||
|
||||
oldFun1(oldFun2(10))
|
||||
|
||||
oldFun2()
|
||||
}
|
||||
|
||||
fun unnecessarySafeCall(x: String) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import pack.bar
|
||||
import pack.oldFun2 // should not be removed for non-deprecated overload used
|
||||
|
||||
interface Foo {
|
||||
}
|
||||
|
||||
@@ -16,6 +19,8 @@ fun foo() {
|
||||
}
|
||||
|
||||
bar(bar(10 + 2) + 1)
|
||||
|
||||
oldFun2()
|
||||
}
|
||||
|
||||
fun unnecessarySafeCall(x: String) {
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
package pack
|
||||
|
||||
@deprecated("", ReplaceWith("bar(p + 1)"))
|
||||
public fun oldFun1(p: Int): Int = 0
|
||||
|
||||
@deprecated("", ReplaceWith("bar(-1)"))
|
||||
public fun oldFun1(): Int = 0
|
||||
|
||||
@deprecated("", ReplaceWith("bar(p + 2)"))
|
||||
public fun oldFun2(p: Int): Int = 0
|
||||
|
||||
public fun oldFun2(): Int = 0
|
||||
|
||||
@deprecated("", ReplaceWith("bar(p)"))
|
||||
public fun oldFun3(p: Int): Int = 0
|
||||
|
||||
public fun bar(p: Int): Int = p
|
||||
Reference in New Issue
Block a user