Extraction Engine: Support type parameter references
#KT-7260 Fixed
This commit is contained in:
+13
-7
@@ -672,27 +672,31 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val referencedClassDescriptor: ClassDescriptor? = (thisDescriptor ?: originalDescriptor).let {
|
val referencedClassifierDescriptor: ClassifierDescriptor? = (thisDescriptor ?: originalDescriptor).let {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ClassDescriptor ->
|
is ClassDescriptor ->
|
||||||
when(it.getKind()) {
|
when(it.getKind()) {
|
||||||
ClassKind.OBJECT, ClassKind.ENUM_CLASS -> it : ClassDescriptor
|
ClassKind.OBJECT, ClassKind.ENUM_CLASS -> it as ClassifierDescriptor
|
||||||
ClassKind.ENUM_ENTRY -> it.getContainingDeclaration() as? ClassDescriptor
|
ClassKind.ENUM_ENTRY -> it.getContainingDeclaration() as? ClassDescriptor
|
||||||
else -> if (ref.getNonStrictParentOfType<JetTypeReference>() != null) it : ClassDescriptor else null
|
else -> if (ref.getNonStrictParentOfType<JetTypeReference>() != null) it as ClassifierDescriptor else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is TypeParameterDescriptor -> it as ClassifierDescriptor
|
||||||
|
|
||||||
is ConstructorDescriptor -> it.getContainingDeclaration()
|
is ConstructorDescriptor -> it.getContainingDeclaration()
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (referencedClassDescriptor != null) {
|
if (referencedClassifierDescriptor != null) {
|
||||||
if (!referencedClassDescriptor.getDefaultType().processTypeIfExtractable(
|
if (!referencedClassifierDescriptor.getDefaultType().processTypeIfExtractable(
|
||||||
info.typeParameters, info.nonDenotableTypes, options, targetScope, false
|
info.typeParameters, info.nonDenotableTypes, options, targetScope, referencedClassifierDescriptor is TypeParameterDescriptor
|
||||||
)) continue
|
)) continue
|
||||||
|
|
||||||
info.replacementMap[refInfo.offsetInBody] = FqNameReplacement(originalDescriptor.getImportableDescriptor().fqNameSafe)
|
if (referencedClassifierDescriptor is ClassDescriptor) {
|
||||||
|
info.replacementMap[refInfo.offsetInBody] = FqNameReplacement(originalDescriptor.getImportableDescriptor().fqNameSafe)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val extractThis = (hasThisReceiver && refInfo.smartCast == null) || thisExpr != null
|
val extractThis = (hasThisReceiver && refInfo.smartCast == null) || thisExpr != null
|
||||||
@@ -998,6 +1002,7 @@ fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts
|
|||||||
).generateDeclaration()
|
).generateDeclaration()
|
||||||
|
|
||||||
val valueParameterList = (result.declaration as? JetNamedFunction)?.getValueParameterList()
|
val valueParameterList = (result.declaration as? JetNamedFunction)?.getValueParameterList()
|
||||||
|
val typeParameterList = (result.declaration as? JetNamedFunction)?.getTypeParameterList()
|
||||||
val body = result.declaration.getGeneratedBody()
|
val body = result.declaration.getGeneratedBody()
|
||||||
val bindingContext = body.analyzeFully()
|
val bindingContext = body.analyzeFully()
|
||||||
|
|
||||||
@@ -1017,6 +1022,7 @@ fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts
|
|||||||
val currentTarget =
|
val currentTarget =
|
||||||
currentDescriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(extractionData.project, it) } as? PsiNamedElement
|
currentDescriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(extractionData.project, it) } as? PsiNamedElement
|
||||||
if (currentTarget is JetParameter && currentTarget.getParent() == valueParameterList) continue
|
if (currentTarget is JetParameter && currentTarget.getParent() == valueParameterList) continue
|
||||||
|
if (currentTarget is JetTypeParameter && currentTarget.getParent() == typeParameterList) continue
|
||||||
if (currentDescriptor is LocalVariableDescriptor
|
if (currentDescriptor is LocalVariableDescriptor
|
||||||
&& parameters.any { it.mirrorVarName == currentDescriptor.getName().asString() }) continue
|
&& parameters.any { it.mirrorVarName == currentDescriptor.getName().asString() }) continue
|
||||||
|
|
||||||
|
|||||||
+28
-3
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine
|
|||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.idea.core.*
|
import org.jetbrains.kotlin.idea.core.*
|
||||||
@@ -25,6 +27,7 @@ import org.jetbrains.kotlin.idea.core.refactoring.isMultiLine
|
|||||||
import org.jetbrains.kotlin.idea.intentions.ConvertToExpressionBodyIntention
|
import org.jetbrains.kotlin.idea.intentions.ConvertToExpressionBodyIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.InfixCallToOrdinaryIntention
|
import org.jetbrains.kotlin.idea.intentions.InfixCallToOrdinaryIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
|
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.ExpressionValue
|
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.ExpressionValue
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.Initializer
|
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.Initializer
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.Jump
|
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.Jump
|
||||||
@@ -205,7 +208,7 @@ fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
|||||||
|
|
||||||
val unifier = JetPsiUnifier(unifierParameters, true)
|
val unifier = JetPsiUnifier(unifierParameters, true)
|
||||||
|
|
||||||
val scopeElement = extractionData.duplicateContainer ?: extractionData.targetSibling.getParent() ?: return Collections.emptyList()
|
val scopeElement = getOccurrenceContainer() ?: return Collections.emptyList()
|
||||||
val originalTextRange = extractionData.originalRange.getTextRange()
|
val originalTextRange = extractionData.originalRange.getTextRange()
|
||||||
return extractionData
|
return extractionData
|
||||||
.originalRange
|
.originalRange
|
||||||
@@ -220,6 +223,10 @@ fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
|||||||
.toList()
|
.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ExtractableCodeDescriptor.getOccurrenceContainer(): PsiElement? {
|
||||||
|
return extractionData.duplicateContainer ?: extractionData.targetSibling.getParent()
|
||||||
|
}
|
||||||
|
|
||||||
private fun makeCall(
|
private fun makeCall(
|
||||||
extractableDescriptor: ExtractableCodeDescriptor,
|
extractableDescriptor: ExtractableCodeDescriptor,
|
||||||
declaration: JetNamedDeclaration,
|
declaration: JetNamedDeclaration,
|
||||||
@@ -264,8 +271,14 @@ private fun makeCall(
|
|||||||
|
|
||||||
val calleeName = declaration.getName()
|
val calleeName = declaration.getName()
|
||||||
val callText = when (declaration) {
|
val callText = when (declaration) {
|
||||||
is JetNamedFunction ->
|
is JetNamedFunction -> {
|
||||||
arguments.joinToString(separator = ", ", prefix = "$calleeName(", postfix = ")")
|
val argumentsText = arguments.joinToString(separator = ", ", prefix = "(", postfix = ")")
|
||||||
|
val typeArguments = extractableDescriptor.typeParameters.map { it.originalDeclaration.name }
|
||||||
|
val typeArgumentsText = with(typeArguments) {
|
||||||
|
if (isNotEmpty()) joinToString(separator = ", ", prefix = "<", postfix = ">") else ""
|
||||||
|
}
|
||||||
|
"$calleeName$typeArgumentsText$argumentsText"
|
||||||
|
}
|
||||||
else -> calleeName
|
else -> calleeName
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,6 +647,18 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
ShortenReferences.DEFAULT.process(declaration)
|
ShortenReferences.DEFAULT.process(declaration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (generatorOptions.inTempFile) return ExtractionResult(this, declaration, emptyMap(), nameByOffset)
|
||||||
|
|
||||||
val duplicateReplacers = duplicates.map { it.range to { makeCall(descriptor, declaration, it.controlFlow, it.range, it.arguments) } }.toMap()
|
val duplicateReplacers = duplicates.map { it.range to { makeCall(descriptor, declaration, it.controlFlow, it.range, it.arguments) } }.toMap()
|
||||||
|
|
||||||
|
if (descriptor.typeParameters.isNotEmpty()) {
|
||||||
|
for (ref in ReferencesSearch.search(declaration, LocalSearchScope(descriptor.getOccurrenceContainer()!!))) {
|
||||||
|
val typeArgumentList = (ref.element.parent as? JetCallExpression)?.typeArgumentList ?: continue
|
||||||
|
if (RemoveExplicitTypeArgumentsIntention.isApplicableTo(typeArgumentList, false)) {
|
||||||
|
typeArgumentList.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ExtractionResult(this, declaration, duplicateReplacers, nameByOffset)
|
return ExtractionResult(this, declaration, duplicateReplacers, nameByOffset)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
LineBreakpoint created at typeParameterRef.kt:13
|
||||||
|
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! typeParameterRef.TypeParameterRefPackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
typeParameterRef.kt:13
|
||||||
|
Compile bytecode for foo<T>()
|
||||||
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package typeParameterRef
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
test<Int>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo<U>(): Int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test<T>() {
|
||||||
|
//Breakpoint!
|
||||||
|
val a = foo<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPRESSION: foo<T>()
|
||||||
|
// RESULT: 1: I
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// SUGGESTED_NAMES: i, getA
|
||||||
|
fun foo<U>() = 1
|
||||||
|
|
||||||
|
fun test<T>() {
|
||||||
|
val a = <selection>foo<T>()</selection>
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// SUGGESTED_NAMES: i, getA
|
||||||
|
fun foo<U>() = 1
|
||||||
|
|
||||||
|
fun test<T>() {
|
||||||
|
val a = i<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> i() = foo<T>()
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// SUGGESTED_NAMES: i, getA
|
||||||
|
// PARAM_DESCRIPTOR: value-parameter val t: T defined in test
|
||||||
|
// PARAM_TYPES: T
|
||||||
|
fun foo<U>(u: U) = 1
|
||||||
|
|
||||||
|
fun test<T>(t: T) {
|
||||||
|
val a = <selection>foo<T>(t)</selection>
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// SUGGESTED_NAMES: i, getA
|
||||||
|
// PARAM_DESCRIPTOR: value-parameter val t: T defined in test
|
||||||
|
// PARAM_TYPES: T
|
||||||
|
fun foo<U>(u: U) = 1
|
||||||
|
|
||||||
|
fun test<T>(t: T) {
|
||||||
|
val a = i(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> i(t: T) = foo<T>(t)
|
||||||
+6
@@ -253,6 +253,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
|||||||
doSingleBreakpointTest(fileName);
|
doSingleBreakpointTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeParameterRef.kt")
|
||||||
|
public void testTypeParameterRef() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/typeParameterRef.kt");
|
||||||
|
doSingleBreakpointTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("unboxParam.kt")
|
@TestMetadata("unboxParam.kt")
|
||||||
public void testUnboxParam() throws Exception {
|
public void testUnboxParam() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/unboxParam.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/unboxParam.kt");
|
||||||
|
|||||||
+12
@@ -2340,6 +2340,18 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
|||||||
doExtractFunctionTest(fileName);
|
doExtractFunctionTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeParameterRef.kt")
|
||||||
|
public void testTypeParameterRef() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/typeParameters/typeParameterRef.kt");
|
||||||
|
doExtractFunctionTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeParameterRefCanOmit.kt")
|
||||||
|
public void testTypeParameterRefCanOmit() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/typeParameters/typeParameterRefCanOmit.kt");
|
||||||
|
doExtractFunctionTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("typeParameterResolvableInTargetScope.kt")
|
@TestMetadata("typeParameterResolvableInTargetScope.kt")
|
||||||
public void testTypeParameterResolvableInTargetScope() throws Exception {
|
public void testTypeParameterResolvableInTargetScope() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user