KT-9638 Optimize imports: don't create import for static methods from superclasses

#KT-9638 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-10-18 20:44:27 +03:00
parent edd2d853b1
commit 27228c2fcc
9 changed files with 139 additions and 19 deletions
@@ -281,16 +281,28 @@ public fun LexicalScope.addImportScope(importScope: JetScope): LexicalScope {
val fileScope = getFileScope()
val scopeWithAdditionImport =
LexicalChainedScope(fileScope, fileScope.ownerDescriptor, false, null, "Scope with addition import", importScope)
return LexicalScopeWrapper(this, scopeWithAdditionImport)
return replaceFileScope(scopeWithAdditionImport)
}
public fun LexicalScope.replaceFileScope(fileScopeReplace: LexicalScope): LexicalScope {
if (this is FileScope) return fileScopeReplace
return LexicalScopeWrapper(this, fileScopeReplace)
}
public fun LexicalScope.withNoFileScope(): LexicalScope = replaceFileScope(MemberScopeToFileScopeAdapter(JetScope.Empty))
private class LexicalScopeWrapper(val delegate: LexicalScope, val fileScopeReplace: LexicalScope): LexicalScope by delegate {
override val parent: LexicalScope? by lazy(LazyThreadSafetyMode.NONE) {
if (delegate is FileScope) {
assert(delegate !is FileScope) { "We should replace FileScope($delegate) to $fileScopeReplace" }
val parent = delegate.parent!!
if (parent is FileScope) {
fileScopeReplace
}
else {
LexicalScopeWrapper(delegate.parent!!, fileScopeReplace)
LexicalScopeWrapper(parent, fileScopeReplace)
}
}
}
@@ -21,13 +21,12 @@ import com.intellij.lang.ImportOptimizer
import com.intellij.openapi.progress.ProgressIndicatorProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze
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.getResolutionScope
import org.jetbrains.kotlin.idea.references.JetReference
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
@@ -36,14 +35,15 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.scopes.utils.asJetScope
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
import org.jetbrains.kotlin.resolve.scopes.utils.withNoFileScope
import java.util.*
public class KotlinImportOptimizer() : ImportOptimizer {
@@ -112,7 +112,7 @@ public class KotlinImportOptimizer() : ImportOptimizer {
if (referencedName != null && importableDescriptor.name != referencedName) continue // resolved via alias
if (isAccessibleAsMember(importableDescriptor, element)) continue
if (isAccessibleAsMember(importableDescriptor, element, bindingContext)) continue
_descriptors.add(importableDescriptor)
}
@@ -121,15 +121,29 @@ public class KotlinImportOptimizer() : ImportOptimizer {
super.visitJetElement(element)
}
private fun isAccessibleAsMember(target: DeclarationDescriptor, place: JetElement): Boolean {
val container = target.containingDeclaration
if (container !is ClassDescriptor) return false
val scope = if (DescriptorUtils.isCompanionObject(container))
container.getContainingDeclaration() as? ClassDescriptor ?: return false
else
container
val classBody = (DescriptorToSourceUtils.getSourceFromDescriptor(scope) as? JetClassOrObject)?.getBody()
return classBody != null && classBody.containingFile == file && classBody.isAncestor(place)
private fun isAccessibleAsMember(target: DeclarationDescriptor, place: JetElement, bindingContext: BindingContext): Boolean {
if (target.containingDeclaration !is ClassDescriptor) return false
fun isInScope(scope: JetScope): Boolean {
return when (target) {
is FunctionDescriptor ->
scope.getFunctions(target.name, NoLookupLocation.FROM_IDE).contains(target)
is PropertyDescriptor ->
scope.getProperties(target.name, NoLookupLocation.FROM_IDE).contains(target)
is ClassDescriptor ->
scope.getClassifier(target.name, NoLookupLocation.FROM_IDE) == target
else -> false
}
}
val resolutionScope = place.getResolutionScope(bindingContext, place.getResolutionFacade())
val noImportsScope = resolutionScope.withNoFileScope()
return isInScope(noImportsScope.asJetScope())
|| resolutionScope.getImplicitReceiversHierarchy().any { isInScope(it.type.memberScope) }
}
}
@@ -0,0 +1,16 @@
import java.util.ArrayList
class B {
companion object {
fun foo(){}
val property = 0
class Nested
}
fun f(): Nested {
foo()
property
}
}
@@ -0,0 +1,14 @@
class B {
companion object {
fun foo(){}
val property = 0
class Nested
}
fun f(): Nested {
foo()
property
}
}
@@ -0,0 +1,18 @@
import A.Companion.run
interface Action<T> {
fun run(t: T)
}
open class A {
companion object : Action<String> {
override fun run(t: String) {
}
}
}
class B : A() {
fun foo() {
run("")
}
}
@@ -0,0 +1,16 @@
interface Action<T> {
fun run(t: T)
}
open class A {
companion object : Action<String> {
override fun run(t: String) {
}
}
}
class B : A() {
fun foo() {
run("")
}
}
@@ -0,0 +1,7 @@
import java.util.ArrayList
class B : Thread() {
init {
currentThread()
}
}
@@ -0,0 +1,5 @@
class B : Thread() {
init {
currentThread()
}
}
@@ -107,6 +107,18 @@ public class OptimizeImportsTestGenerated extends AbstractOptimizeImportsTest {
doTest(fileName);
}
@TestMetadata("FromCompanionObject.kt")
public void testFromCompanionObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/FromCompanionObject.kt");
doTest(fileName);
}
@TestMetadata("FromCompanionObjectGeneric.kt")
public void testFromCompanionObjectGeneric() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/FromCompanionObjectGeneric.kt");
doTest(fileName);
}
@TestMetadata("InvokeFunction.kt")
public void testInvokeFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/InvokeFunction.kt");
@@ -191,6 +203,12 @@ public class OptimizeImportsTestGenerated extends AbstractOptimizeImportsTest {
doTest(fileName);
}
@TestMetadata("StaticMethodFromSuper.kt")
public void testStaticMethodFromSuper() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/StaticMethodFromSuper.kt");
doTest(fileName);
}
@TestMetadata("UnusedImports.kt")
public void testUnusedImports() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/UnusedImports.kt");