Rename: Use OverloadChecker for redeclaration conflict analysis

#KT-19561 Fixed
 #KT-22812 Fixed
 #KT-13907 Fixed
This commit is contained in:
Alexey Sedunov
2018-02-22 21:03:39 +03:00
parent 12d5a72697
commit 7149a0855a
6 changed files with 79 additions and 26 deletions
@@ -29,16 +29,16 @@ import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
import org.jetbrains.kotlin.idea.core.copied
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.refactoring.explicateAsText
import org.jetbrains.kotlin.idea.refactoring.getThisLabelName
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.search.and
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.getAllAccessibleFunctions
import org.jetbrains.kotlin.idea.util.getAllAccessibleVariables
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.js.resolve.JsPlatform
import org.jetbrains.kotlin.js.resolve.JsTypeSpecificityComparator
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
@@ -46,11 +46,15 @@ import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.OverloadChecker
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverValue
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import org.jetbrains.kotlin.resolve.jvm.JvmTypeSpecificityComparator
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
@@ -61,6 +65,7 @@ import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
internal fun ResolvedCall<*>.noReceivers() = dispatchReceiver == null && extensionReceiver == null
@@ -107,11 +112,11 @@ internal fun checkRedeclarations(
return getDescriptorsFiltered(descriptorKindFilter) { it.asString() == newName }.filter { it != descriptor }
}
fun getSiblingWithNewName(): DeclarationDescriptor? {
fun getSiblingsWithNewName(): List<DeclarationDescriptor> {
val containingDescriptor = descriptor.containingDeclaration
if (descriptor is ValueParameterDescriptor) {
return (containingDescriptor as CallableDescriptor).valueParameters.firstOrNull { it.name.asString() == newName }
return (containingDescriptor as CallableDescriptor).valueParameters.filter { it.name.asString() == newName }
}
if (descriptor is TypeParameterDescriptor) {
@@ -120,44 +125,64 @@ internal fun checkRedeclarations(
is CallableDescriptor -> containingDescriptor.typeParameters
else -> emptyList()
}
typeParameters.firstOrNull { it.name.asString() == newName }?.let { return it }
val containingDeclaration = (containingDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() as? KtDeclaration
?: return null
val dummyVar = KtPsiFactory(containingDeclaration).createProperty("val foo: $newName")
val outerScope = containingDeclaration.getResolutionScope()
val context = dummyVar.analyzeInContext(outerScope, containingDeclaration)
return context[BindingContext.VARIABLE, dummyVar]?.type?.constructor?.declarationDescriptor
return SmartList<DeclarationDescriptor>().apply {
typeParameters.filterTo(this) { it.name.asString() == newName }
val containingDeclaration = (containingDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() as? KtDeclaration
?: return emptyList()
val dummyVar = KtPsiFactory(containingDeclaration).createProperty("val foo: $newName")
val outerScope = containingDeclaration.getResolutionScope()
val context = dummyVar.analyzeInContext(outerScope, containingDeclaration)
addIfNotNull(context[BindingContext.VARIABLE, dummyVar]?.type?.constructor?.declarationDescriptor)
}
}
return when (containingDescriptor) {
is ClassDescriptor -> containingDescriptor.unsubstitutedMemberScope.findSiblingsByName().firstOrNull()
is PackageFragmentDescriptor -> containingDescriptor.getMemberScope().findSiblingsByName().firstOrNull {
is ClassDescriptor -> containingDescriptor.unsubstitutedMemberScope.findSiblingsByName()
is PackageFragmentDescriptor -> containingDescriptor.getMemberScope().findSiblingsByName().filter {
it != descriptor
&& (!(descriptor.isTopLevelPrivate() || it.isTopLevelPrivate()) || isInSameFile(descriptor, it))
}
else -> {
val block = (descriptor as? DeclarationDescriptorWithSource)?.source?.getPsi()?.parent as? KtBlockExpression
?: return null
(block.statements.firstOrNull {
if (it.name != newName) return@firstOrNull false
when (descriptor) {
?: return emptyList()
block.statements.mapNotNull {
if (it.name != newName) return@mapNotNull null
val isAccepted = when (descriptor) {
is ClassDescriptor -> it is KtClassOrObject
is PropertyDescriptor -> it is KtProperty
is FunctionDescriptor -> it is KtNamedFunction
else -> false
}
} as? KtDeclaration)?.unsafeResolveToDescriptor()
if (!isAccepted) return@mapNotNull null
(it as? KtDeclaration)?.unsafeResolveToDescriptor()
}
}
}
}
val candidateDescriptor = getSiblingWithNewName() ?: return
val candidate = (candidateDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() as? KtNamedDeclaration ?: return
val what = candidate.renderDescription().capitalize()
val where = candidate.representativeContainer()?.renderDescription() ?: return
val message = "$what is already declared in $where"
result += BasicUnresolvableCollisionUsageInfo(candidate, candidate, message)
val overloadChecker = when (descriptor) {
is PropertyDescriptor,
is FunctionDescriptor,
is ClassifierDescriptor -> {
val psi = (descriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() as? KtElement ?: return
val typeSpecificityComparator = when (TargetPlatformDetector.getPlatform(psi.containingKtFile)) {
is JvmPlatform -> JvmTypeSpecificityComparator
is JsPlatform -> JsTypeSpecificityComparator
else -> TypeSpecificityComparator.NONE
}
OverloadChecker(typeSpecificityComparator)
}
else -> null
}
for (candidateDescriptor in getSiblingsWithNewName()) {
val candidate = (candidateDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() as? KtNamedDeclaration ?: continue
if (overloadChecker != null && overloadChecker.isOverloadable(descriptor, candidateDescriptor)) continue
val what = candidate.renderDescription().capitalize()
val where = candidate.representativeContainer()?.renderDescription() ?: continue
val message = "$what is already declared in $where"
result += BasicUnresolvableCollisionUsageInfo(candidate, candidate, message)
}
}
private fun LexicalScope.getRelevantDescriptors(
@@ -0,0 +1,5 @@
class A
fun A.b() {}
fun <caret>c() {}
@@ -0,0 +1,5 @@
class A
fun A.b() {}
fun b() {}
@@ -0,0 +1,5 @@
class Foo
class Bar
fun Foo.example() = "test"
fun Bar.<caret>other() = "test"
@@ -0,0 +1,5 @@
class Foo
class Bar
fun Foo.example() = "test"
fun Bar.example() = "test"
@@ -134,6 +134,14 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() {
doTestMemberInplaceRename("foo")
}
fun testExtensionAndNoReceiver() {
doTestMemberInplaceRename("b")
}
fun testTwoExtensions() {
doTestMemberInplaceRename("example")
}
private fun doTestImplicitLambdaParameter(newName: String) {
configureByFile(getTestName(false) + ".kt")