Implement liftToExpected correctly for primary constructor properties

Before this commit, we always tried to find expect property in this case.
However, there is at least one case when we should find parameter of
expect primary constructor instead (safe delete).
So #KT-25321 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-07-26 14:02:44 +03:00
parent 437b6f2c2d
commit ddd3a0a46e
19 changed files with 212 additions and 5 deletions
@@ -12,11 +12,12 @@ import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors
import org.jetbrains.kotlin.idea.caches.project.implementingDescriptors import org.jetbrains.kotlin.idea.caches.project.implementingDescriptors
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.setVisibility import org.jetbrains.kotlin.idea.caches.resolve.resolveToParameterDescriptorIfAny
import org.jetbrains.kotlin.idea.core.toDescriptor import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
@@ -60,13 +61,19 @@ fun KtDeclaration.liftToExpected(): KtDeclaration? {
return DescriptorToSourceUtils.descriptorToDeclaration(expectedDescriptor) as? KtDeclaration return DescriptorToSourceUtils.descriptorToDeclaration(expectedDescriptor) as? KtDeclaration
} }
fun KtParameter.liftToExpected(): KtParameter? {
val parameterDescriptor = resolveToParameterDescriptorIfAny()
val expectedDescriptor = parameterDescriptor?.liftToExpected() ?: return null
return DescriptorToSourceUtils.descriptorToDeclaration(expectedDescriptor) as? KtParameter
}
fun ModuleDescriptor.hasDeclarationOf(descriptor: MemberDescriptor) = declarationOf(descriptor) != null fun ModuleDescriptor.hasDeclarationOf(descriptor: MemberDescriptor) = declarationOf(descriptor) != null
private fun ModuleDescriptor.declarationOf(descriptor: MemberDescriptor): DeclarationDescriptor? = private fun ModuleDescriptor.declarationOf(descriptor: MemberDescriptor): DeclarationDescriptor? =
with(ExpectedActualResolver) { with(ExpectedActualResolver) {
val expectedCompatibilityMap = findExpectedForActual(descriptor, this@declarationOf) val expectedCompatibilityMap = findExpectedForActual(descriptor, this@declarationOf)
expectedCompatibilityMap?.get(ExpectedActualResolver.Compatibility.Compatible)?.firstOrNull() expectedCompatibilityMap?.get(ExpectedActualResolver.Compatibility.Compatible)?.firstOrNull()
?: expectedCompatibilityMap?.values?.flatten()?.firstOrNull() ?: expectedCompatibilityMap?.values?.flatten()?.firstOrNull()
} }
fun ModuleDescriptor.hasActualsFor(descriptor: MemberDescriptor) = fun ModuleDescriptor.hasActualsFor(descriptor: MemberDescriptor) =
@@ -457,7 +457,7 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
): Collection<PsiElement>? { ): Collection<PsiElement>? {
when (element) { when (element) {
is KtParameter -> { is KtParameter -> {
val expectParameter = element.liftToExpected() as? KtParameter val expectParameter = element.liftToExpected()
if (expectParameter != null && expectParameter != element) { if (expectParameter != null && expectParameter != element) {
return if (shouldAllowPropagationToExpected(element)) { return if (shouldAllowPropagationToExpected(element)) {
listOf(expectParameter) listOf(expectParameter)
@@ -449,7 +449,7 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
): Collection<PsiElement>? { ): Collection<PsiElement>? {
when (element) { when (element) {
is KtParameter -> { is KtParameter -> {
val expectParameter = element.liftToExpected() as? KtParameter val expectParameter = element.liftToExpected()
if (expectParameter != null && expectParameter != element) { if (expectParameter != null && expectParameter != element) {
if (shouldAllowPropagationToExpected(element)) { if (shouldAllowPropagationToExpected(element)) {
return listOf(expectParameter) return listOf(expectParameter)
@@ -459,7 +459,7 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
): Collection<PsiElement>? { ): Collection<PsiElement>? {
when (element) { when (element) {
is KtParameter -> { is KtParameter -> {
val expectParameter = element.liftToExpected() as? KtParameter val expectParameter = element.liftToExpected()
if (expectParameter != null && expectParameter != element) { if (expectParameter != null && expectParameter != element) {
if (shouldAllowPropagationToExpected(element)) { if (shouldAllowPropagationToExpected(element)) {
return listOf(expectParameter) return listOf(expectParameter)
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="2" platform="Common (experimental) " useProjectSettings="false">
<compilerSettings/>
<compilerArguments/>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
@@ -0,0 +1,8 @@
package test
expect class Foo()
fun test() {
Foo()
Foo()
}
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="2" platform="JavaScript " useProjectSettings="false">
<implements>Common</implements>
<compilerSettings/>
<compilerArguments/>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Common" />
</component>
</module>
@@ -0,0 +1,12 @@
package test
actual class Foo actual constructor() {
constructor(s: String): this(0)
}
fun test() {
Foo("1")
Foo(s = "1")
Foo()
Foo()
}
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="2" platform="JVM 1.6" useProjectSettings="false">
<implements>Common</implements>
<compilerSettings/>
<compilerArguments/>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Common" />
</component>
</module>
@@ -0,0 +1,14 @@
package test
actual class Foo(s: String) {
actual constructor(): this("") {
val x = n + 1
}
}
fun test() {
Foo("1")
Foo(s = "1")
Foo()
Foo()
}
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="2" platform="Common (experimental) " useProjectSettings="false">
<compilerSettings/>
<compilerArguments/>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
@@ -0,0 +1,8 @@
package test
expect class Foo(n: Int)
fun test() {
Foo(1)
Foo(n = 1)
}
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="2" platform="JavaScript " useProjectSettings="false">
<implements>Common</implements>
<compilerSettings/>
<compilerArguments/>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Common" />
</component>
</module>
@@ -0,0 +1,12 @@
package test
actual class Foo actual constructor(val <caret>n: Int) {
constructor(s: String): this(0)
}
fun test() {
Foo("1")
Foo(s = "1")
Foo(1)
Foo(n = 1)
}
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="2" platform="JVM 1.6" useProjectSettings="false">
<implements>Common</implements>
<compilerSettings/>
<compilerArguments/>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Common" />
</component>
</module>
@@ -0,0 +1,14 @@
package test
actual class Foo(s: String) {
actual constructor(n: Int): this("") {
val x = n + 1
}
}
fun test() {
Foo("1")
Foo(s = "1")
Foo(1)
Foo(n = 1)
}
@@ -0,0 +1 @@
parameter n has 2 usages that are not safe to delete.
@@ -0,0 +1,4 @@
{
"mainFile": "JS/src/test/test.kt",
"elementClass": "org.jetbrains.kotlin.psi.KtParameter"
}
@@ -54,6 +54,11 @@ public class MultiModuleSafeDeleteTestGenerated extends AbstractMultiModuleSafeD
runTest("idea/testData/refactoring/safeDeleteMultiModule/byActualClassPrimaryConstructorParameterLiftingToExpect/expectsAndActualsByActualClassPrimaryConstructorParameter.test"); runTest("idea/testData/refactoring/safeDeleteMultiModule/byActualClassPrimaryConstructorParameterLiftingToExpect/expectsAndActualsByActualClassPrimaryConstructorParameter.test");
} }
@TestMetadata("byActualClassPrimaryConstructorPropertyLiftingToExpect/expectsAndActualsByActualClassPrimaryConstructorProperty.test")
public void testByActualClassPrimaryConstructorPropertyLiftingToExpect_ExpectsAndActualsByActualClassPrimaryConstructorProperty() throws Exception {
runTest("idea/testData/refactoring/safeDeleteMultiModule/byActualClassPrimaryConstructorPropertyLiftingToExpect/expectsAndActualsByActualClassPrimaryConstructorProperty.test");
}
@TestMetadata("byActualClassSecondaryConstructor/byActualClassSecondaryConstructor.test") @TestMetadata("byActualClassSecondaryConstructor/byActualClassSecondaryConstructor.test")
public void testByActualClassSecondaryConstructor_ByActualClassSecondaryConstructor() throws Exception { public void testByActualClassSecondaryConstructor_ByActualClassSecondaryConstructor() throws Exception {
runTest("idea/testData/refactoring/safeDeleteMultiModule/byActualClassSecondaryConstructor/byActualClassSecondaryConstructor.test"); runTest("idea/testData/refactoring/safeDeleteMultiModule/byActualClassSecondaryConstructor/byActualClassSecondaryConstructor.test");