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
|
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 substitutionsAllowed: Boolean = true
|
||||||
open val staticContextRequired: Boolean = false
|
open val staticContextRequired: Boolean = false
|
||||||
open fun getPossibleNamesFromExpression(bindingContext: BindingContext): Array<String> = ArrayUtil.EMPTY_STRING_ARRAY
|
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)
|
return (this as? ForcedNotNull) ?: ForcedNotNull(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun TypeInfo.ofThis() = TypeInfo.OfThis(this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates information about a function parameter that is going to be created.
|
* 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? {
|
protected fun getAbstractCallableInfo(mainCallable: CallableInfo, originalExpression: KtExpression): CallableInfo? {
|
||||||
val containingClass: KtClass
|
val receiverTypeInfo: TypeInfo
|
||||||
val receiverType: KotlinType
|
val receiverType: KotlinType
|
||||||
|
|
||||||
val receiverTypeInfo = mainCallable.receiverTypeInfo
|
val originalReceiverTypeInfo = mainCallable.receiverTypeInfo
|
||||||
if (receiverTypeInfo != TypeInfo.Empty) {
|
if (originalReceiverTypeInfo != TypeInfo.Empty) {
|
||||||
if (receiverTypeInfo !is TypeInfo.ByType) return null
|
if (originalReceiverTypeInfo !is TypeInfo.ByType) return null
|
||||||
|
receiverTypeInfo = originalReceiverTypeInfo
|
||||||
receiverType = receiverTypeInfo.theType
|
receiverType = receiverTypeInfo.theType
|
||||||
containingClass = receiverType.constructor.declarationDescriptor?.source?.getPsi() as? KtClass ?: return null
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
containingClass = originalExpression.getStrictParentOfType<KtClassOrObject>() as? KtClass ?: return null
|
val containingClass = originalExpression.getStrictParentOfType<KtClassOrObject>() as? KtClass ?: return null
|
||||||
if (containingClass is KtEnumEntry) return null
|
if (containingClass is KtEnumEntry) return null
|
||||||
receiverType = (containingClass.unsafeResolveToDescriptor() as ClassDescriptor).defaultType
|
receiverType = (containingClass.unsafeResolveToDescriptor() as ClassDescriptor).defaultType
|
||||||
|
receiverTypeInfo = TypeInfo(receiverType, Variance.IN_VARIANCE).ofThis()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!receiverType.isAbstract() && TypeUtils.getAllSupertypes(receiverType).all { !it.isAbstract() }) return null
|
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(
|
protected fun getCallableWithReceiverInsideExtension(
|
||||||
|
|||||||
+11
-8
@@ -80,6 +80,7 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
|
|
||||||
override fun getText(): String {
|
override fun getText(): String {
|
||||||
val element = element ?: return ""
|
val element = element ?: return ""
|
||||||
|
val receiverTypeInfo = callableInfos.first().receiverTypeInfo
|
||||||
val renderedCallables = callableInfos.map {
|
val renderedCallables = callableInfos.map {
|
||||||
buildString {
|
buildString {
|
||||||
if (it.isAbstract) {
|
if (it.isAbstract) {
|
||||||
@@ -97,13 +98,15 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
if (it.name.isNotEmpty()) {
|
if (it.name.isNotEmpty()) {
|
||||||
append(" '")
|
append(" '")
|
||||||
|
|
||||||
val callableBuilder =
|
val receiverType = if (!receiverTypeInfo.isOfThis) {
|
||||||
CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension)
|
CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension)
|
||||||
.createBuilder()
|
.createBuilder()
|
||||||
val receiverType = callableBuilder
|
.computeTypeCandidates(receiverTypeInfo)
|
||||||
.computeTypeCandidates(callableInfos.first().receiverTypeInfo)
|
.firstOrNull()
|
||||||
.firstOrNull()
|
?.theType
|
||||||
?.theType
|
}
|
||||||
|
else null
|
||||||
|
|
||||||
if (receiverType != null) {
|
if (receiverType != null) {
|
||||||
if (isExtension) {
|
if (isExtension) {
|
||||||
val receiverTypeText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(receiverType)
|
val receiverTypeText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(receiverType)
|
||||||
@@ -125,7 +128,7 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
return StringBuilder().apply {
|
return StringBuilder().apply {
|
||||||
append("Create ")
|
append("Create ")
|
||||||
|
|
||||||
val receiverInfo = callableInfos.first().receiverTypeInfo
|
val receiverInfo = receiverTypeInfo
|
||||||
if (!callableInfos.any { it.isAbstract }) {
|
if (!callableInfos.any { it.isAbstract }) {
|
||||||
if (isExtension) {
|
if (isExtension) {
|
||||||
append("extension ")
|
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);
|
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")
|
@TestMetadata("interfaceNoExplicitReceiver.kt")
|
||||||
public void testInterfaceNoExplicitReceiver() throws Exception {
|
public void testInterfaceNoExplicitReceiver() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/abstract/interfaceNoExplicitReceiver.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/abstract/interfaceNoExplicitReceiver.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("notAbstractClass.kt")
|
||||||
public void testNotAbstractClass() throws Exception {
|
public void testNotAbstractClass() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/abstract/notAbstractClass.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/abstract/notAbstractClass.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user