Create from Usage: Fix choosing superclass for abstract member
This applies to cases when non-existing member is called without explicit receiver #KT-21332 Fixed
This commit is contained in:
+11
@@ -84,6 +84,15 @@ abstract class TypeInfo(val variance: Variance) {
|
||||
override val staticContextRequired: Boolean = true
|
||||
}
|
||||
|
||||
class OfThis(delegate: TypeInfo) : DelegatingTypeInfo(delegate)
|
||||
|
||||
val isOfThis: Boolean
|
||||
get() = when (this) {
|
||||
is OfThis -> true
|
||||
is DelegatingTypeInfo -> delegate.isOfThis
|
||||
else -> false
|
||||
}
|
||||
|
||||
open val substitutionsAllowed: Boolean = true
|
||||
open val staticContextRequired: Boolean = false
|
||||
open fun getPossibleNamesFromExpression(bindingContext: BindingContext): Array<String> = ArrayUtil.EMPTY_STRING_ARRAY
|
||||
@@ -139,6 +148,8 @@ fun TypeInfo.forceNotNull(): TypeInfo {
|
||||
return (this as? ForcedNotNull) ?: ForcedNotNull(this)
|
||||
}
|
||||
|
||||
fun TypeInfo.ofThis() = TypeInfo.OfThis(this)
|
||||
|
||||
/**
|
||||
* Encapsulates information about a function parameter that is going to be created.
|
||||
*/
|
||||
|
||||
+8
-7
@@ -135,24 +135,25 @@ sealed class CreateCallableFromCallActionFactory<E : KtExpression>(
|
||||
}
|
||||
|
||||
protected fun getAbstractCallableInfo(mainCallable: CallableInfo, originalExpression: KtExpression): CallableInfo? {
|
||||
val containingClass: KtClass
|
||||
val receiverTypeInfo: TypeInfo
|
||||
val receiverType: KotlinType
|
||||
|
||||
val receiverTypeInfo = mainCallable.receiverTypeInfo
|
||||
if (receiverTypeInfo != TypeInfo.Empty) {
|
||||
if (receiverTypeInfo !is TypeInfo.ByType) return null
|
||||
val originalReceiverTypeInfo = mainCallable.receiverTypeInfo
|
||||
if (originalReceiverTypeInfo != TypeInfo.Empty) {
|
||||
if (originalReceiverTypeInfo !is TypeInfo.ByType) return null
|
||||
receiverTypeInfo = originalReceiverTypeInfo
|
||||
receiverType = receiverTypeInfo.theType
|
||||
containingClass = receiverType.constructor.declarationDescriptor?.source?.getPsi() as? KtClass ?: return null
|
||||
}
|
||||
else {
|
||||
containingClass = originalExpression.getStrictParentOfType<KtClassOrObject>() as? KtClass ?: return null
|
||||
val containingClass = originalExpression.getStrictParentOfType<KtClassOrObject>() as? KtClass ?: return null
|
||||
if (containingClass is KtEnumEntry) return null
|
||||
receiverType = (containingClass.unsafeResolveToDescriptor() as ClassDescriptor).defaultType
|
||||
receiverTypeInfo = TypeInfo(receiverType, Variance.IN_VARIANCE).ofThis()
|
||||
}
|
||||
|
||||
if (!receiverType.isAbstract() && TypeUtils.getAllSupertypes(receiverType).all { !it.isAbstract() }) return null
|
||||
|
||||
return mainCallable.copy(receiverTypeInfo = receiverTypeInfo, possibleContainers = listOf(containingClass), isAbstract = true)
|
||||
return mainCallable.copy(receiverTypeInfo = receiverTypeInfo, possibleContainers = emptyList(), isAbstract = true)
|
||||
}
|
||||
|
||||
protected fun getCallableWithReceiverInsideExtension(
|
||||
|
||||
+11
-8
@@ -80,6 +80,7 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
||||
|
||||
override fun getText(): String {
|
||||
val element = element ?: return ""
|
||||
val receiverTypeInfo = callableInfos.first().receiverTypeInfo
|
||||
val renderedCallables = callableInfos.map {
|
||||
buildString {
|
||||
if (it.isAbstract) {
|
||||
@@ -97,13 +98,15 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
||||
if (it.name.isNotEmpty()) {
|
||||
append(" '")
|
||||
|
||||
val callableBuilder =
|
||||
CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension)
|
||||
.createBuilder()
|
||||
val receiverType = callableBuilder
|
||||
.computeTypeCandidates(callableInfos.first().receiverTypeInfo)
|
||||
.firstOrNull()
|
||||
?.theType
|
||||
val receiverType = if (!receiverTypeInfo.isOfThis) {
|
||||
CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension)
|
||||
.createBuilder()
|
||||
.computeTypeCandidates(receiverTypeInfo)
|
||||
.firstOrNull()
|
||||
?.theType
|
||||
}
|
||||
else null
|
||||
|
||||
if (receiverType != null) {
|
||||
if (isExtension) {
|
||||
val receiverTypeText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(receiverType)
|
||||
@@ -125,7 +128,7 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
||||
return StringBuilder().apply {
|
||||
append("Create ")
|
||||
|
||||
val receiverInfo = callableInfos.first().receiverTypeInfo
|
||||
val receiverInfo = receiverTypeInfo
|
||||
if (!callableInfos.any { it.isAbstract }) {
|
||||
if (isExtension) {
|
||||
append("extension ")
|
||||
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
// "Create abstract function 'bar'" "true"
|
||||
// ERROR: Class 'Foo' is not abstract and does not implement abstract base class member public abstract fun bar(): Unit defined in A
|
||||
|
||||
abstract class A
|
||||
|
||||
class Foo : A() {
|
||||
fun foo() {
|
||||
<caret>bar()
|
||||
}
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
// "Create abstract function 'bar'" "true"
|
||||
// ERROR: Class 'Foo' is not abstract and does not implement abstract base class member public abstract fun bar(): Unit defined in A
|
||||
|
||||
abstract class A {
|
||||
abstract fun bar()
|
||||
}
|
||||
|
||||
class Foo : A() {
|
||||
fun foo() {
|
||||
bar()
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Create abstract function 'bar'" "true"
|
||||
// ERROR: Class 'Foo' is not abstract and does not implement abstract member public abstract fun bar(): Unit defined in I
|
||||
|
||||
interface I
|
||||
|
||||
class Foo : I {
|
||||
fun foo() {
|
||||
<caret>bar()
|
||||
}
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
// "Create abstract function 'bar'" "true"
|
||||
// ERROR: Class 'Foo' is not abstract and does not implement abstract member public abstract fun bar(): Unit defined in I
|
||||
|
||||
interface I {
|
||||
fun bar()
|
||||
}
|
||||
|
||||
class Foo : I {
|
||||
fun foo() {
|
||||
bar()
|
||||
}
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
// "Create abstract function 'Foo.bar'" "false"
|
||||
// ACTION: Create function 'bar'
|
||||
// ACTION: Rename reference
|
||||
// ERROR: Unresolved reference: bar
|
||||
|
||||
open class A
|
||||
|
||||
class Foo : A() {
|
||||
fun foo() {
|
||||
<caret>bar()
|
||||
}
|
||||
}
|
||||
@@ -3224,12 +3224,30 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inAbstractSuperclass.kt")
|
||||
public void testInAbstractSuperclass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/abstract/inAbstractSuperclass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inBaseInterface.kt")
|
||||
public void testInBaseInterface() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/abstract/inBaseInterface.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("interfaceNoExplicitReceiver.kt")
|
||||
public void testInterfaceNoExplicitReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/abstract/interfaceNoExplicitReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noAbstractSuperclass.kt")
|
||||
public void testNoAbstractSuperclass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/abstract/noAbstractSuperclass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notAbstractClass.kt")
|
||||
public void testNotAbstractClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/abstract/notAbstractClass.kt");
|
||||
|
||||
Reference in New Issue
Block a user