Add actual: handle primary & secondary constructors as compatible

So #KT-23686 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-06-27 19:02:37 +03:00
parent 082c3e6767
commit dd0b267531
15 changed files with 107 additions and 12 deletions
@@ -47,7 +47,8 @@ class AddActualFix(
val expectedClass = expectedClassPointer.element ?: return val expectedClass = expectedClassPointer.element ?: return
val factory = KtPsiFactory(element) val factory = KtPsiFactory(element)
val pureActualClass = factory.generateClassOrObjectByExpectedClass( val pureActualClass = factory.generateClassOrObjectByExpectedClass(
project, expectedClass, actualNeeded = true, existingDeclarations = element.declarations project, expectedClass, actualNeeded = true,
existingDeclarations = element.declarations + listOfNotNull(element.primaryConstructor)
) )
for (declaration in pureActualClass.declarations) { for (declaration in pureActualClass.declarations) {
element.addDeclaration(declaration) element.addDeclaration(declaration)
@@ -219,19 +219,18 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass(
actualNeeded: Boolean, actualNeeded: Boolean,
existingDeclarations: List<KtDeclaration> = emptyList() existingDeclarations: List<KtDeclaration> = emptyList()
): KtClassOrObject { ): KtClassOrObject {
fun areCompatible(first: KtFunction, second: KtFunction) =
first.valueParameters.size == second.valueParameters.size &&
first.valueParameters.zip(second.valueParameters).all { (firstParam, secondParam) ->
firstParam.name == secondParam.name && firstParam.typeReference?.text == secondParam.typeReference?.text
}
fun KtDeclaration.exists() = fun KtDeclaration.exists() =
existingDeclarations.any { existingDeclarations.any {
name == it.name && this.javaClass == it.javaClass && when (this) { name == it.name && when (this) {
is KtClassOrObject, is KtProperty, is KtEnumEntry -> true is KtConstructor<*> -> it is KtConstructor<*> && areCompatible(this, it)
is KtFunction -> { is KtNamedFunction -> it is KtNamedFunction && areCompatible(this, it)
it as KtFunction else -> this.javaClass == it.javaClass
valueParameters.size == it.valueParameters.size &&
valueParameters.zip(it.valueParameters).all { (parameter, existingParameter) ->
parameter.name == existingParameter.name &&
parameter.typeReference?.text == existingParameter.typeReference?.text
}
}
else -> true
} }
} }
@@ -266,6 +265,10 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass(
} }
} }
} }
val primaryConstructor = actualClass.primaryConstructor
if (primaryConstructor != null && primaryConstructor.exists()) {
primaryConstructor.delete()
}
val context = expectedClass.analyze() val context = expectedClass.analyze()
actualClass.superTypeListEntries.zip(expectedClass.superTypeListEntries).forEach { (actualEntry, expectedEntry) -> actualClass.superTypeListEntries.zip(expectedClass.superTypeListEntries).forEach { (actualEntry, expectedEntry) ->
@@ -0,0 +1,5 @@
// DISABLE-ERRORS
expect class My() {
fun test()
}
@@ -0,0 +1,5 @@
// DISABLE-ERRORS
expect class My() {
fun test()
}
@@ -0,0 +1,6 @@
// "Add missing actual members" "true"
// DISABLE-ERRORS
actual class <caret>My {
actual constructor()
}
@@ -0,0 +1,8 @@
// "Add missing actual members" "true"
// DISABLE-ERRORS
actual class My {
actual constructor()
actual fun test() {}
}
@@ -0,0 +1,5 @@
// DISABLE-ERRORS
expect class My(a: Int, b: String) {
fun test()
}
@@ -0,0 +1,5 @@
// DISABLE-ERRORS
expect class My(a: Int, b: String) {
fun test()
}
@@ -0,0 +1,6 @@
// "Add missing actual members" "true"
// DISABLE-ERRORS
actual class <caret>My {
actual constructor(a: Int, b: String)
}
@@ -0,0 +1,8 @@
// "Add missing actual members" "true"
// DISABLE-ERRORS
actual class My {
actual constructor(a: Int, b: String)
actual fun test() {}
}
@@ -0,0 +1,7 @@
// DISABLE-ERRORS
expect class My(a: Int, b: String) {
fun test()
constructor(b: String, a: Int)
}
@@ -0,0 +1,7 @@
// DISABLE-ERRORS
expect class My(a: Int, b: String) {
fun test()
constructor(b: String, a: Int)
}
@@ -0,0 +1,6 @@
// "Add missing actual members" "true"
// DISABLE-ERRORS
actual class <caret>My {
actual constructor(b: String, a: Int)
}
@@ -0,0 +1,8 @@
// "Add missing actual members" "true"
// DISABLE-ERRORS
actual class My actual constructor(a: Int, b: String) {
actual constructor(b: String, a: Int)
actual fun test() {}
}
@@ -79,6 +79,21 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul
runTest("idea/testData/multiModuleQuickFix/classFunction/"); runTest("idea/testData/multiModuleQuickFix/classFunction/");
} }
@TestMetadata("classFunctionWithConstructor")
public void testClassFunctionWithConstructor() throws Exception {
runTest("idea/testData/multiModuleQuickFix/classFunctionWithConstructor/");
}
@TestMetadata("classFunctionWithConstructorAndParameters")
public void testClassFunctionWithConstructorAndParameters() throws Exception {
runTest("idea/testData/multiModuleQuickFix/classFunctionWithConstructorAndParameters/");
}
@TestMetadata("classFunctionWithIncompatibleConstructor")
public void testClassFunctionWithIncompatibleConstructor() throws Exception {
runTest("idea/testData/multiModuleQuickFix/classFunctionWithIncompatibleConstructor/");
}
@TestMetadata("classOverloadedFunction") @TestMetadata("classOverloadedFunction")
public void testClassOverloadedFunction() throws Exception { public void testClassOverloadedFunction() throws Exception {
runTest("idea/testData/multiModuleQuickFix/classOverloadedFunction/"); runTest("idea/testData/multiModuleQuickFix/classOverloadedFunction/");