Code cleanup to remove deprecated symbol imports that have ReplaceWith

This commit is contained in:
Valentin Kipyatkov
2015-08-28 21:37:56 +03:00
parent c020189ad0
commit e98edbcbac
9 changed files with 76 additions and 38 deletions
@@ -19,11 +19,15 @@ package org.jetbrains.kotlin.idea.core
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor 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.caches.resolve.getFileTopLevelScope
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstanceToExpression import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstanceToExpression
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.scopes.JetScope import org.jetbrains.kotlin.resolve.scopes.JetScope
@@ -102,3 +106,8 @@ public fun PsiElement.getResolutionScope(bindingContext: BindingContext, resolut
} }
error("Not in JetFile") 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.caches.resolve.analyzeFullyAndGetResult
import org.jetbrains.kotlin.idea.highlighter.JetPsiChecker import org.jetbrains.kotlin.idea.highlighter.JetPsiChecker
import org.jetbrains.kotlin.idea.quickfix.CleanupFix 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.quickfix.ReplaceObsoleteLabelSyntaxFix
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.psi.JetAnnotationEntry import org.jetbrains.kotlin.psi.JetAnnotationEntry
import org.jetbrains.kotlin.psi.JetFile 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.forEachDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType 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.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import kotlin.properties.Delegates
public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspectionTool { public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspectionTool {
// required to simplify the inspection registration in tests // required to simplify the inspection registration in tests
@@ -61,6 +62,14 @@ public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspe
} }
val problems = arrayListOf<ProblemData>() 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 -> file.forEachDescendantOfType<PsiElement> { element ->
for (diagnostic in diagnostics.forElement(element)) { for (diagnostic in diagnostics.forElement(element)) {
if (diagnostic.isCleanup()) { if (diagnostic.isCleanup()) {
@@ -111,10 +120,23 @@ public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspe
} }
private fun Diagnostic.toProblemDescriptor(fixes: Collection<CleanupFix>, file: JetFile, manager: InspectionManager): ProblemDescriptor { private fun Diagnostic.toProblemDescriptor(fixes: Collection<CleanupFix>, file: JetFile, manager: InspectionManager): ProblemDescriptor {
return manager.createProblemDescriptor(getPsiElement(), return createProblemDescriptor(psiElement, DefaultErrorMessages.render(this), fixes, file, manager)
DefaultErrorMessages.render(this), }
false,
fixes.map { Wrapper(it, file) }.toTypedArray(), private fun createProblemDescriptor(element: PsiElement, message: String, fixes: Collection<CleanupFix>, file: JetFile, manager: InspectionManager): ProblemDescriptor {
ProblemHighlightType.GENERIC_ERROR_OR_WARNING) 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.DocumentUtil
import com.intellij.util.Processor import com.intellij.util.Processor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor 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.KotlinImportOptimizer
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile 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.ImportPath
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
import java.util.* import java.util.*
class UnusedImportInspection : AbstractKotlinInspection() { class UnusedImportInspection : AbstractKotlinInspection() {
@@ -104,8 +101,7 @@ class UnusedImportInspection : AbstractKotlinInspection() {
} }
if (!isUsed) { if (!isUsed) {
val nameExpression = directive.importedReference?.getQualifiedElementSelector() as? JetSimpleNameExpression if (directive.targetDescriptors().isEmpty()) continue // do not highlight unresolved imports as unused
if (nameExpression == null || nameExpression.getReferenceTargets(nameExpression.analyze()).isEmpty()) continue // do not highlight unresolved imports as unused
val fixes = arrayListOf<LocalQuickFix>() val fixes = arrayListOf<LocalQuickFix>()
fixes.add(OptimizeImportsQuickFix(file)) fixes.add(OptimizeImportsQuickFix(file))
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.core.targetDescriptors
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
@@ -72,5 +73,9 @@ public class DeprecatedSymbolUsageFix(
return DeprecatedSymbolUsageFix(nameExpression, replacement) return DeprecatedSymbolUsageFix(nameExpression, replacement)
} }
public fun isImportToBeRemoved(import: JetImportDirective): Boolean {
return !import.isAllUnder
&& import.targetDescriptors().all { DeprecatedSymbolUsageFixBase.replaceWithPattern(it, import.project) != null }
}
} }
} }
@@ -17,31 +17,16 @@
package org.jetbrains.kotlin.idea.refactoring.safeDelete package org.jetbrains.kotlin.idea.refactoring.safeDelete
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceSimpleDeleteUsageInfo import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceSimpleDeleteUsageInfo
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.idea.core.targetDescriptors
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.psi.JetDeclaration
import org.jetbrains.kotlin.psi.JetImportDirective
public class SafeDeleteImportDirectiveUsageInfo( public class SafeDeleteImportDirectiveUsageInfo(
importDirective: JetImportDirective, declaration: JetDeclaration importDirective: JetImportDirective, declaration: JetDeclaration
) : SafeDeleteReferenceSimpleDeleteUsageInfo(importDirective, declaration, importDirective.isSafeToDelete(declaration)) ) : SafeDeleteReferenceSimpleDeleteUsageInfo(importDirective, declaration, importDirective.isSafeToDelete(declaration))
fun JetImportDirective.isSafeToDelete(declaration: JetDeclaration): Boolean { private fun JetImportDirective.isSafeToDelete(declaration: JetDeclaration): Boolean {
val importExpr = getImportedReference() val referencedDescriptor = targetDescriptors().singleOrNull() ?: return false
val importReference: JetReferenceExpression? = when (importExpr) { return referencedDescriptor == declaration.resolveToDescriptor()
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
} }
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.getFileTopLevelScope import org.jetbrains.kotlin.idea.caches.resolve.getFileTopLevelScope
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.formatter.JetCodeStyleSettings 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.getImportableTargets
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
@@ -299,9 +300,8 @@ public class ImportInsertHelperImpl(private val project: Project) : ImportInsert
val importsToCheck = ArrayList<FqName>() val importsToCheck = ArrayList<FqName>()
for (import in dropCandidates) { for (import in dropCandidates) {
val importedReference = import.getImportedReference() ?: continue if (import.importedReference == null) continue
val refExpr = (importedReference as JetDotQualifiedExpression).getSelectorExpression() as JetSimpleNameExpression val targets = import.targetDescriptors()
val targets = refExpr.resolveTargets()
if (targets.any { it is PackageViewDescriptor }) continue // do not drop import of package if (targets.any { it is PackageViewDescriptor }) continue // do not drop import of package
val classDescriptor = targets.filterIsInstance<ClassDescriptor>().firstOrNull() val classDescriptor = targets.filterIsInstance<ClassDescriptor>().firstOrNull()
importsToCheck.addIfNotNull(classDescriptor?.importableFqName) importsToCheck.addIfNotNull(classDescriptor?.importableFqName)
+6
View File
@@ -1,3 +1,7 @@
import pack.oldFun1
import pack.oldFun2 // should not be removed for non-deprecated overload used
import pack.oldFun3
trait Foo { trait Foo {
} }
@@ -16,6 +20,8 @@ fun foo() {
} }
oldFun1(oldFun2(10)) oldFun1(oldFun2(10))
oldFun2()
} }
fun unnecessarySafeCall(x: String) { fun unnecessarySafeCall(x: String) {
+5
View File
@@ -1,3 +1,6 @@
import pack.bar
import pack.oldFun2 // should not be removed for non-deprecated overload used
interface Foo { interface Foo {
} }
@@ -16,6 +19,8 @@ fun foo() {
} }
bar(bar(10 + 2) + 1) bar(bar(10 + 2) + 1)
oldFun2()
} }
fun unnecessarySafeCall(x: String) { fun unnecessarySafeCall(x: String) {
+10
View File
@@ -1,7 +1,17 @@
package pack
@deprecated("", ReplaceWith("bar(p + 1)")) @deprecated("", ReplaceWith("bar(p + 1)"))
public fun oldFun1(p: Int): Int = 0 public fun oldFun1(p: Int): Int = 0
@deprecated("", ReplaceWith("bar(-1)"))
public fun oldFun1(): Int = 0
@deprecated("", ReplaceWith("bar(p + 2)")) @deprecated("", ReplaceWith("bar(p + 2)"))
public fun oldFun2(p: Int): Int = 0 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 public fun bar(p: Int): Int = p