Extraction Engine: Support type parameter references

#KT-7260 Fixed
This commit is contained in:
Alexey Sedunov
2015-09-25 19:56:54 +03:00
parent aa080bf201
commit 78f7e6b459
10 changed files with 116 additions and 10 deletions
@@ -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
@@ -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
@@ -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>
}
@@ -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>()
@@ -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>
}
@@ -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)
@@ -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");
@@ -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");