Create from Usage: Allow type parameters in the receiver position
#KT-7497 Fixed
This commit is contained in:
+5
-3
@@ -72,10 +72,12 @@ import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
|||||||
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
|
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
|
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
|
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
@@ -223,7 +225,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
val containingFileEditor: Editor
|
val containingFileEditor: Editor
|
||||||
val containingElement: PsiElement
|
val containingElement: PsiElement
|
||||||
val dialogWithEditor: DialogWithEditor?
|
val dialogWithEditor: DialogWithEditor?
|
||||||
val receiverClassDescriptor: ClassDescriptor?
|
val receiverClassDescriptor: ClassifierDescriptor?
|
||||||
val typeParameterNameMap: Map<TypeParameterDescriptor, String>
|
val typeParameterNameMap: Map<TypeParameterDescriptor, String>
|
||||||
val receiverTypeCandidate: TypeCandidate?
|
val receiverTypeCandidate: TypeCandidate?
|
||||||
val mandatoryTypeParametersAsCandidates: List<TypeCandidate>
|
val mandatoryTypeParametersAsCandidates: List<TypeCandidate>
|
||||||
@@ -247,7 +249,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
}
|
}
|
||||||
is CallablePlacement.WithReceiver -> {
|
is CallablePlacement.WithReceiver -> {
|
||||||
receiverClassDescriptor =
|
receiverClassDescriptor =
|
||||||
placement.receiverTypeCandidate.theType.getConstructor().getDeclarationDescriptor() as? ClassDescriptor
|
placement.receiverTypeCandidate.theType.getConstructor().getDeclarationDescriptor()
|
||||||
val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.getSourceFromDescriptor(it) }
|
val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.getSourceFromDescriptor(it) }
|
||||||
containingElement = if (!config.isExtension && classDeclaration != null) classDeclaration else config.currentFile
|
containingElement = if (!config.isExtension && classDeclaration != null) classDeclaration else config.currentFile
|
||||||
}
|
}
|
||||||
@@ -350,7 +352,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
|
|
||||||
assert (receiverClassDescriptor is JavaClassDescriptor) { "Unexpected receiver class: $receiverClassDescriptor" }
|
assert (receiverClassDescriptor is JavaClassDescriptor) { "Unexpected receiver class: $receiverClassDescriptor" }
|
||||||
|
|
||||||
val projections = receiverClassDescriptor.declaredTypeParameters
|
val projections = ((receiverClassDescriptor as JavaClassDescriptor).declaredTypeParameters)
|
||||||
.map { TypeProjectionImpl(it.getDefaultType()) }
|
.map { TypeProjectionImpl(it.getDefaultType()) }
|
||||||
val memberScope = receiverClassDescriptor.getMemberScope(projections)
|
val memberScope = receiverClassDescriptor.getMemberScope(projections)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -73,7 +73,7 @@ public abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
private fun getDeclarationIfApplicable(project: Project, candidate: TypeCandidate): PsiElement? {
|
private fun getDeclarationIfApplicable(project: Project, candidate: TypeCandidate): PsiElement? {
|
||||||
val descriptor = candidate.theType.constructor.declarationDescriptor ?: return null
|
val descriptor = candidate.theType.constructor.declarationDescriptor ?: return null
|
||||||
val declaration = getDeclaration(descriptor, project) ?: return null
|
val declaration = getDeclaration(descriptor, project) ?: return null
|
||||||
if (declaration !is KtClassOrObject && declaration !is PsiClass) return null
|
if (declaration !is KtClassOrObject && declaration !is KtTypeParameter && declaration !is PsiClass) return null
|
||||||
return if (isExtension || declaration.canRefactor()) declaration else null
|
return if (isExtension || declaration.canRefactor()) declaration else null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +125,7 @@ public abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
false
|
false
|
||||||
isFunction && insertToJavaInterface && receiverInfo.staticContextRequired ->
|
isFunction && insertToJavaInterface && receiverInfo.staticContextRequired ->
|
||||||
false
|
false
|
||||||
|
!isExtension && declaration is KtTypeParameter -> false
|
||||||
else ->
|
else ->
|
||||||
declaration != null
|
declaration != null
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
// "Create extension function 'bar'" "true"
|
||||||
|
fun <T> foo(t: T) {
|
||||||
|
t.<caret>bar()
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// "Create extension function 'bar'" "true"
|
||||||
|
fun <T> foo(t: T) {
|
||||||
|
t.bar()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> T.bar() {
|
||||||
|
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Create member function 'bar'" "false"
|
||||||
|
// ACTION: Create extension function 'bar'
|
||||||
|
// ACTION: Rename reference
|
||||||
|
// ERROR: Unresolved reference: bar
|
||||||
|
fun <T> foo(t: T) {
|
||||||
|
t.<caret>bar()
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Create extension property 'bar'" "true"
|
||||||
|
// ERROR: Property must be initialized
|
||||||
|
fun consume(n: Int) {}
|
||||||
|
|
||||||
|
fun <T> foo(t: T) {
|
||||||
|
consume(t.<caret>bar)
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// "Create extension property 'bar'" "true"
|
||||||
|
// ERROR: Property must be initialized
|
||||||
|
fun consume(n: Int) {}
|
||||||
|
|
||||||
|
val <T> T.bar: Int
|
||||||
|
|
||||||
|
fun <T> foo(t: T) {
|
||||||
|
consume(t.bar)
|
||||||
|
}
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// "Create member property 'bar'" "false"
|
||||||
|
// ACTION: Create extension property 'bar'
|
||||||
|
// ACTION: Rename reference
|
||||||
|
// ACTION: Convert to expression body
|
||||||
|
// ERROR: Unresolved reference: bar
|
||||||
|
fun consume(n: Int) {}
|
||||||
|
|
||||||
|
fun <T> foo(t: T) {
|
||||||
|
consume(t.<caret>bar)
|
||||||
|
}
|
||||||
@@ -2119,6 +2119,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("extensionWithTypeParameterAsReceiver.kt")
|
||||||
|
public void testExtensionWithTypeParameterAsReceiver() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/extensionWithTypeParameterAsReceiver.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("memberWithTypeParameterAsReceiver.kt")
|
||||||
|
public void testMemberWithTypeParameterAsReceiver() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/memberWithTypeParameterAsReceiver.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("noReceiver.kt")
|
@TestMetadata("noReceiver.kt")
|
||||||
public void testNoReceiver() throws Exception {
|
public void testNoReceiver() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/noReceiver.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/noReceiver.kt");
|
||||||
@@ -3065,6 +3077,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("extensionWithTypeParameterAsReceiver.kt")
|
||||||
|
public void testExtensionWithTypeParameterAsReceiver() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/extensionWithTypeParameterAsReceiver.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inBinaryOperation.kt")
|
@TestMetadata("inBinaryOperation.kt")
|
||||||
public void testInBinaryOperation() throws Exception {
|
public void testInBinaryOperation() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/inBinaryOperation.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/inBinaryOperation.kt");
|
||||||
@@ -3113,6 +3131,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("memberWithTypeParameterAsReceiver.kt")
|
||||||
|
public void testMemberWithTypeParameterAsReceiver() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/memberWithTypeParameterAsReceiver.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("objectMemberValNoReceiver.kt")
|
@TestMetadata("objectMemberValNoReceiver.kt")
|
||||||
public void testObjectMemberValNoReceiver() throws Exception {
|
public void testObjectMemberValNoReceiver() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/objectMemberValNoReceiver.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/objectMemberValNoReceiver.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user