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 factory = KtPsiFactory(element)
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) {
element.addDeclaration(declaration)
@@ -219,19 +219,18 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass(
actualNeeded: Boolean,
existingDeclarations: List<KtDeclaration> = emptyList()
): 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() =
existingDeclarations.any {
name == it.name && this.javaClass == it.javaClass && when (this) {
is KtClassOrObject, is KtProperty, is KtEnumEntry -> true
is KtFunction -> {
it as KtFunction
valueParameters.size == it.valueParameters.size &&
valueParameters.zip(it.valueParameters).all { (parameter, existingParameter) ->
parameter.name == existingParameter.name &&
parameter.typeReference?.text == existingParameter.typeReference?.text
}
}
else -> true
name == it.name && when (this) {
is KtConstructor<*> -> it is KtConstructor<*> && areCompatible(this, it)
is KtNamedFunction -> it is KtNamedFunction && areCompatible(this, it)
else -> this.javaClass == it.javaClass
}
}
@@ -266,6 +265,10 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass(
}
}
}
val primaryConstructor = actualClass.primaryConstructor
if (primaryConstructor != null && primaryConstructor.exists()) {
primaryConstructor.delete()
}
val context = expectedClass.analyze()
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/");
}
@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")
public void testClassOverloadedFunction() throws Exception {
runTest("idea/testData/multiModuleQuickFix/classOverloadedFunction/");