Create from Usage: Allow type parameters in the receiver position

#KT-7497 Fixed
This commit is contained in:
Alexey Sedunov
2015-12-08 14:50:06 +03:00
parent 76cf284b77
commit 44743aade5
9 changed files with 76 additions and 4 deletions
@@ -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)
@@ -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
} }
@@ -0,0 +1,4 @@
// "Create extension function 'bar'" "true"
fun <T> foo(t: T) {
t.<caret>bar()
}
@@ -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.
}
@@ -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()
}
@@ -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)
}
@@ -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)
}
@@ -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");