KT-22428 Create member function from usage shouldn't present type parameters as options (#1509)
* KT-22428 Create member function from usage shouldn't present type parameters as options * Reformat & cleanup
This commit is contained in:
committed by
Dmitry Jemerov
parent
ea6582dd7d
commit
a36e8c86f1
+32
-32
@@ -17,7 +17,6 @@
|
|||||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||||
|
|
||||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||||
import com.intellij.codeInsight.navigation.NavigationUtil
|
|
||||||
import com.intellij.ide.util.EditorHelper
|
import com.intellij.ide.util.EditorHelper
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
@@ -38,30 +37,31 @@ import org.jetbrains.kotlin.idea.util.application.executeCommand
|
|||||||
import org.jetbrains.kotlin.idea.util.isAbstract
|
import org.jetbrains.kotlin.idea.util.isAbstract
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
open class CreateCallableFromUsageFix<E : KtElement>(
|
open class CreateCallableFromUsageFix<E : KtElement>(
|
||||||
originalExpression: E,
|
originalExpression: E,
|
||||||
callableInfos: List<CallableInfo>
|
callableInfos: List<CallableInfo>
|
||||||
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, false)
|
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, false)
|
||||||
|
|
||||||
class CreateExtensionCallableFromUsageFix<E : KtElement>(
|
class CreateExtensionCallableFromUsageFix<E : KtElement>(
|
||||||
originalExpression: E,
|
originalExpression: E,
|
||||||
callableInfos: List<CallableInfo>
|
callableInfos: List<CallableInfo>
|
||||||
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, true), LowPriorityAction
|
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, true), LowPriorityAction
|
||||||
|
|
||||||
abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
||||||
originalExpression: E,
|
originalExpression: E,
|
||||||
protected val callableInfos: List<CallableInfo>,
|
protected val callableInfos: List<CallableInfo>,
|
||||||
val isExtension: Boolean
|
val isExtension: Boolean
|
||||||
) : KotlinCrossLanguageQuickFixAction<E>(originalExpression) {
|
) : KotlinCrossLanguageQuickFixAction<E>(originalExpression) {
|
||||||
init {
|
init {
|
||||||
assert (callableInfos.isNotEmpty()) { "No CallableInfos: ${originalExpression.getElementTextWithContext()}" }
|
assert(callableInfos.isNotEmpty()) { "No CallableInfos: ${originalExpression.getElementTextWithContext()}" }
|
||||||
if (callableInfos.size > 1) {
|
if (callableInfos.size > 1) {
|
||||||
val receiverSet = callableInfos.mapTo(HashSet<TypeInfo>()) { it.receiverTypeInfo }
|
val receiverSet = callableInfos.mapTo(HashSet()) { it.receiverTypeInfo }
|
||||||
if (receiverSet.size > 1) throw AssertionError("All functions must have common receiver: $receiverSet")
|
if (receiverSet.size > 1) throw AssertionError("All functions must have common receiver: $receiverSet")
|
||||||
|
|
||||||
val possibleContainerSet = callableInfos.mapTo(HashSet<List<KtElement>>()) { it.possibleContainers }
|
val possibleContainerSet = callableInfos.mapTo(HashSet()) { it.possibleContainers }
|
||||||
if (possibleContainerSet.size > 1) throw AssertionError("All functions must have common containers: $possibleContainerSet")
|
if (possibleContainerSet.size > 1) throw AssertionError("All functions must have common containers: $possibleContainerSet")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,20 +106,18 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
|
|
||||||
val receiverType = if (!receiverTypeInfo.isOfThis) {
|
val receiverType = if (!receiverTypeInfo.isOfThis) {
|
||||||
CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension)
|
CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension)
|
||||||
.createBuilder()
|
.createBuilder()
|
||||||
.computeTypeCandidates(receiverTypeInfo)
|
.computeTypeCandidates(receiverTypeInfo)
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
?.theType
|
?.theType
|
||||||
}
|
} else null
|
||||||
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)
|
||||||
val isFunctionType = receiverType.constructor.declarationDescriptor is FunctionClassDescriptor
|
val isFunctionType = receiverType.constructor.declarationDescriptor is FunctionClassDescriptor
|
||||||
append(if (isFunctionType) "($receiverTypeText)" else receiverTypeText).append('.')
|
append(if (isFunctionType) "($receiverTypeText)" else receiverTypeText).append('.')
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
receiverType.constructor.declarationDescriptor?.let {
|
receiverType.constructor.declarationDescriptor?.let {
|
||||||
append(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderClassifierName(it)).append('.')
|
append(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderClassifierName(it)).append('.')
|
||||||
}
|
}
|
||||||
@@ -134,12 +132,10 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
return StringBuilder().apply {
|
return StringBuilder().apply {
|
||||||
append("Create ")
|
append("Create ")
|
||||||
|
|
||||||
val receiverInfo = receiverTypeInfo
|
|
||||||
if (!callableInfos.any { it.isAbstract }) {
|
if (!callableInfos.any { it.isAbstract }) {
|
||||||
if (isExtension) {
|
if (isExtension) {
|
||||||
append("extension ")
|
append("extension ")
|
||||||
}
|
} else if (receiverTypeInfo != TypeInfo.Empty) {
|
||||||
else if (receiverInfo !is TypeInfo.Empty) {
|
|
||||||
append("member ")
|
append("member ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,7 +149,7 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
|
|
||||||
val receiverInfo = callableInfos.first().receiverTypeInfo
|
val receiverInfo = callableInfos.first().receiverTypeInfo
|
||||||
|
|
||||||
if (receiverInfo is TypeInfo.Empty) {
|
if (receiverInfo == TypeInfo.Empty) {
|
||||||
if (callableInfos.any { it is PropertyInfo && it.possibleContainers.isEmpty() }) return false
|
if (callableInfos.any { it is PropertyInfo && it.possibleContainers.isEmpty() }) return false
|
||||||
return !isExtension
|
return !isExtension
|
||||||
}
|
}
|
||||||
@@ -191,15 +187,14 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
if (file is KtFile) {
|
if (file is KtFile) {
|
||||||
fileForBuilder = file
|
fileForBuilder = file
|
||||||
editorForBuilder = editor
|
editorForBuilder = editor
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
fileForBuilder = element.containingKtFile
|
fileForBuilder = element.containingKtFile
|
||||||
EditorHelper.openInEditor(element)
|
EditorHelper.openInEditor(element)
|
||||||
editorForBuilder = FileEditorManager.getInstance(project).selectedTextEditor!!
|
editorForBuilder = FileEditorManager.getInstance(project).selectedTextEditor!!
|
||||||
}
|
}
|
||||||
|
|
||||||
val callableBuilder =
|
val callableBuilder =
|
||||||
CallableBuilderConfiguration(callableInfos, element as KtElement, fileForBuilder, editorForBuilder, isExtension).createBuilder()
|
CallableBuilderConfiguration(callableInfos, element as KtElement, fileForBuilder, editorForBuilder, isExtension).createBuilder()
|
||||||
|
|
||||||
fun runBuilder(placement: CallablePlacement) {
|
fun runBuilder(placement: CallablePlacement) {
|
||||||
callableBuilder.placement = placement
|
callableBuilder.placement = placement
|
||||||
@@ -212,19 +207,24 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val popupTitle = "Choose target class or interface"
|
val popupTitle = "Choose target class or interface"
|
||||||
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfo.receiverTypeInfo).let {
|
val receiverTypeInfo = callableInfo.receiverTypeInfo
|
||||||
if (callableInfo.isAbstract) it.filter { it.theType.isAbstract() } else it
|
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(receiverTypeInfo).let {
|
||||||
|
if (callableInfo.isAbstract)
|
||||||
|
it.filter { it.theType.isAbstract() }
|
||||||
|
else if (!isExtension && receiverTypeInfo != TypeInfo.Empty)
|
||||||
|
it.filter { !it.theType.isTypeParameter() }
|
||||||
|
else
|
||||||
|
it
|
||||||
}
|
}
|
||||||
if (receiverTypeCandidates.isNotEmpty()) {
|
if (receiverTypeCandidates.isNotEmpty()) {
|
||||||
val containers = receiverTypeCandidates
|
val containers = receiverTypeCandidates
|
||||||
.mapNotNull { candidate -> getDeclarationIfApplicable(project, candidate)?.let { candidate to it } }
|
.mapNotNull { candidate -> getDeclarationIfApplicable(project, candidate)?.let { candidate to it } }
|
||||||
|
|
||||||
chooseContainerElementIfNecessary(containers, editor, popupTitle, false, { it.second }) {
|
chooseContainerElementIfNecessary(containers, editor, popupTitle, false, { it.second }) {
|
||||||
runBuilder(CallablePlacement.WithReceiver(it.first))
|
runBuilder(CallablePlacement.WithReceiver(it.first))
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
assert(receiverTypeInfo == TypeInfo.Empty) {
|
||||||
assert(callableInfo.receiverTypeInfo is TypeInfo.Empty) {
|
|
||||||
"No receiver type candidates: ${element.text} in ${file.text}"
|
"No receiver type candidates: ${element.text} in ${file.text}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// "Create member function 'T.bar'" "true"
|
||||||
|
open class X
|
||||||
|
|
||||||
|
fun <T : X> foo(t: T, f: T.() -> Unit = {}) {}
|
||||||
|
|
||||||
|
class Text<T : X>(private val t: T) {
|
||||||
|
fun f() = foo(t) { <caret>bar() }
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// "Create member function 'T.bar'" "true"
|
||||||
|
open class X {
|
||||||
|
fun bar() {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : X> foo(t: T, f: T.() -> Unit = {}) {}
|
||||||
|
|
||||||
|
class Text<T : X>(private val t: T) {
|
||||||
|
fun f() = foo(t) { bar() }
|
||||||
|
}
|
||||||
@@ -3345,6 +3345,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("classMemberInFunctionLiteralWithReceiver.kt")
|
||||||
|
public void testClassMemberInFunctionLiteralWithReceiver() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/classMemberInFunctionLiteralWithReceiver.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("classMemberPartialSubstitution.kt")
|
@TestMetadata("classMemberPartialSubstitution.kt")
|
||||||
public void testClassMemberPartialSubstitution() throws Exception {
|
public void testClassMemberPartialSubstitution() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/classMemberPartialSubstitution.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/classMemberPartialSubstitution.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user