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
|
||||
}
|
||||
|
||||
val referencedClassDescriptor: ClassDescriptor? = (thisDescriptor ?: originalDescriptor).let {
|
||||
val referencedClassifierDescriptor: ClassifierDescriptor? = (thisDescriptor ?: originalDescriptor).let {
|
||||
when (it) {
|
||||
is ClassDescriptor ->
|
||||
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
|
||||
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()
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
if (referencedClassDescriptor != null) {
|
||||
if (!referencedClassDescriptor.getDefaultType().processTypeIfExtractable(
|
||||
info.typeParameters, info.nonDenotableTypes, options, targetScope, false
|
||||
if (referencedClassifierDescriptor != null) {
|
||||
if (!referencedClassifierDescriptor.getDefaultType().processTypeIfExtractable(
|
||||
info.typeParameters, info.nonDenotableTypes, options, targetScope, referencedClassifierDescriptor is TypeParameterDescriptor
|
||||
)) continue
|
||||
|
||||
info.replacementMap[refInfo.offsetInBody] = FqNameReplacement(originalDescriptor.getImportableDescriptor().fqNameSafe)
|
||||
if (referencedClassifierDescriptor is ClassDescriptor) {
|
||||
info.replacementMap[refInfo.offsetInBody] = FqNameReplacement(originalDescriptor.getImportableDescriptor().fqNameSafe)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val extractThis = (hasThisReceiver && refInfo.smartCast == null) || thisExpr != null
|
||||
@@ -998,6 +1002,7 @@ fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts
|
||||
).generateDeclaration()
|
||||
|
||||
val valueParameterList = (result.declaration as? JetNamedFunction)?.getValueParameterList()
|
||||
val typeParameterList = (result.declaration as? JetNamedFunction)?.getTypeParameterList()
|
||||
val body = result.declaration.getGeneratedBody()
|
||||
val bindingContext = body.analyzeFully()
|
||||
|
||||
@@ -1017,6 +1022,7 @@ fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts
|
||||
val currentTarget =
|
||||
currentDescriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(extractionData.project, it) } as? PsiNamedElement
|
||||
if (currentTarget is JetParameter && currentTarget.getParent() == valueParameterList) continue
|
||||
if (currentTarget is JetTypeParameter && currentTarget.getParent() == typeParameterList) continue
|
||||
if (currentDescriptor is LocalVariableDescriptor
|
||||
&& 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.PsiFile
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
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.InfixCallToOrdinaryIntention
|
||||
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.Initializer
|
||||
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 scopeElement = extractionData.duplicateContainer ?: extractionData.targetSibling.getParent() ?: return Collections.emptyList()
|
||||
val scopeElement = getOccurrenceContainer() ?: return Collections.emptyList()
|
||||
val originalTextRange = extractionData.originalRange.getTextRange()
|
||||
return extractionData
|
||||
.originalRange
|
||||
@@ -220,6 +223,10 @@ fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
||||
.toList()
|
||||
}
|
||||
|
||||
private fun ExtractableCodeDescriptor.getOccurrenceContainer(): PsiElement? {
|
||||
return extractionData.duplicateContainer ?: extractionData.targetSibling.getParent()
|
||||
}
|
||||
|
||||
private fun makeCall(
|
||||
extractableDescriptor: ExtractableCodeDescriptor,
|
||||
declaration: JetNamedDeclaration,
|
||||
@@ -264,8 +271,14 @@ private fun makeCall(
|
||||
|
||||
val calleeName = declaration.getName()
|
||||
val callText = when (declaration) {
|
||||
is JetNamedFunction ->
|
||||
arguments.joinToString(separator = ", ", prefix = "$calleeName(", postfix = ")")
|
||||
is JetNamedFunction -> {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -634,6 +647,18 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
||||
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()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testUnboxParam() throws Exception {
|
||||
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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testTypeParameterResolvableInTargetScope() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt");
|
||||
|
||||
Reference in New Issue
Block a user