Introduce Type Alias: Replace type usages in constructor calls. Do not replace usages of existing type aliases
#KT-14685 Fixed
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
###### Issues fixed
|
||||
- [`KT-14693`](https://youtrack.jetbrains.com/issue/KT-14693) Introduce Type Alias: Do not suggest type qualifiers
|
||||
- [`KT-14696`](https://youtrack.jetbrains.com/issue/KT-14696) Introduce Type Alias: Fix NPE during dialog repaint
|
||||
- [`KT-14685`](https://youtrack.jetbrains.com/issue/KT-14685) Introduce Type Alias: Replace type usages in constructor calls
|
||||
|
||||
## 1.1-M03 (EAP-3)
|
||||
|
||||
|
||||
+4
@@ -333,6 +333,10 @@ class KotlinPsiUnifier(
|
||||
typeRef2: KtTypeReference? = null
|
||||
): Status? {
|
||||
if (type1 != null && type2 != null) {
|
||||
val unwrappedType1 = type1.unwrap()
|
||||
val unwrappedType2 = type2.unwrap()
|
||||
if (unwrappedType1 !== type1 || unwrappedType2 !== type2) return matchTypes(unwrappedType1, unwrappedType2, typeRef1, typeRef2)
|
||||
|
||||
if (type1.isError || type2.isError) return null
|
||||
if (type1 is AbbreviatedType != type2 is AbbreviatedType) return UNMATCHED
|
||||
if (type1.isExtensionFunctionType != type2.isExtensionFunctionType) return UNMATCHED
|
||||
|
||||
+59
-11
@@ -18,11 +18,14 @@ package org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeAlias
|
||||
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.util.containers.LinkedMultiMap
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
|
||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.compareDescriptors
|
||||
@@ -42,7 +45,7 @@ import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import java.util.*
|
||||
|
||||
sealed class IntroduceTypeAliasAnalysisResult {
|
||||
class Error(val message: String) : IntroduceTypeAliasAnalysisResult()
|
||||
@@ -152,6 +155,7 @@ fun IntroduceTypeAliasDescriptor.validate(): IntroduceTypeAliasDescriptorWithCon
|
||||
|
||||
fun findDuplicates(typeAlias: KtTypeAlias): Map<KotlinPsiRange, () -> Unit> {
|
||||
val aliasName = typeAlias.name?.quoteIfNeeded() ?: return emptyMap()
|
||||
val aliasRange = typeAlias.textRange
|
||||
val typeAliasDescriptor = typeAlias.resolveToDescriptor() as TypeAliasDescriptor
|
||||
|
||||
val unifierParameters = typeAliasDescriptor.declaredTypeParameters.map { UnifierParameter(it, null) }
|
||||
@@ -159,33 +163,77 @@ fun findDuplicates(typeAlias: KtTypeAlias): Map<KotlinPsiRange, () -> Unit> {
|
||||
|
||||
val psiFactory = KtPsiFactory(typeAlias)
|
||||
|
||||
fun replaceOccurrence(occurrence: KtTypeElement, arguments: List<KtTypeElement>) {
|
||||
val typeText = if (arguments.isNotEmpty()) "$aliasName<${arguments.joinToString { it.text }}>" else aliasName
|
||||
occurrence.replace(psiFactory.createType(typeText).typeElement!!)
|
||||
fun replaceOccurrence(occurrence: PsiElement, arguments: List<KtTypeElement>) {
|
||||
val typeArgumentsText = if (arguments.isNotEmpty()) "<${arguments.joinToString { it.text }}>" else ""
|
||||
when (occurrence) {
|
||||
is KtTypeElement -> {
|
||||
occurrence.replace(psiFactory.createType("$aliasName$typeArgumentsText").typeElement!!)
|
||||
}
|
||||
|
||||
is KtCallElement -> {
|
||||
val typeArgumentList = occurrence.typeArgumentList
|
||||
if (arguments.isNotEmpty()) {
|
||||
val newTypeArgumentList = psiFactory.createTypeArguments(typeArgumentsText)
|
||||
typeArgumentList?.replace(newTypeArgumentList) ?: occurrence.addAfter(newTypeArgumentList, occurrence)
|
||||
}
|
||||
else {
|
||||
typeArgumentList?.delete()
|
||||
}
|
||||
occurrence.calleeExpression?.replace(psiFactory.createExpression(aliasName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val aliasRange = typeAlias.textRange
|
||||
return typeAlias
|
||||
val rangesWithReplacers = ArrayList<Pair<KotlinPsiRange, () -> Unit>>()
|
||||
|
||||
val originalTypePsi = typeAliasDescriptor.underlyingType.constructor.declarationDescriptor?.let {
|
||||
DescriptorToSourceUtilsIde.getAnyDeclaration(typeAlias.project, it)
|
||||
}
|
||||
if (originalTypePsi != null) {
|
||||
for (reference in ReferencesSearch.search(originalTypePsi, LocalSearchScope(typeAlias.parent))) {
|
||||
val element = reference.element as? KtSimpleNameExpression ?: continue
|
||||
if ((element.textRange.intersects(aliasRange))) continue
|
||||
|
||||
val arguments: List<KtTypeElement>
|
||||
val occurrence: KtElement
|
||||
|
||||
val callElement = element.getParentOfTypeAndBranch<KtCallElement> { calleeExpression }
|
||||
if (callElement != null) {
|
||||
occurrence = callElement
|
||||
arguments = callElement.typeArguments.mapNotNull { it.typeReference?.typeElement }
|
||||
}
|
||||
else {
|
||||
val userType = element.getParentOfTypeAndBranch<KtUserType> { referenceExpression }
|
||||
if (userType != null) {
|
||||
occurrence = userType
|
||||
arguments = userType.typeArgumentsAsTypes.mapNotNull { it.typeElement }
|
||||
}
|
||||
else continue
|
||||
}
|
||||
if (arguments.size != typeAliasDescriptor.declaredTypeParameters.size) continue
|
||||
rangesWithReplacers += occurrence.toRange() to { replaceOccurrence(occurrence, arguments) }
|
||||
}
|
||||
}
|
||||
typeAlias
|
||||
.getTypeReference()
|
||||
?.typeElement
|
||||
.toRange()
|
||||
.match(typeAlias.parent, unifier)
|
||||
.asSequence()
|
||||
.filter { !(it.range.getTextRange().intersects(aliasRange)) }
|
||||
.mapNotNull { match ->
|
||||
val occurrence = match.range.elements.singleOrNull() as? KtTypeElement ?: return@mapNotNull null
|
||||
.mapNotNullTo(rangesWithReplacers) { match ->
|
||||
val occurrence = match.range.elements.singleOrNull() as? KtTypeElement ?: return@mapNotNullTo null
|
||||
val arguments = unifierParameters.mapNotNull { (match.substitution[it] as? KtTypeReference)?.typeElement }
|
||||
if (arguments.size != unifierParameters.size) return@mapNotNull null
|
||||
if (arguments.size != unifierParameters.size) return@mapNotNullTo null
|
||||
match.range to { replaceOccurrence(occurrence, arguments) }
|
||||
}
|
||||
.toMap()
|
||||
return rangesWithReplacers.toMap()
|
||||
}
|
||||
|
||||
private var KtTypeReference.typeParameterInfo : TypeParameter? by CopyableUserDataProperty(Key.create("TYPE_PARAMETER_INFO"))
|
||||
|
||||
fun IntroduceTypeAliasDescriptor.generateTypeAlias(previewOnly: Boolean = false): KtTypeAlias {
|
||||
val originalType = originalData.originalType
|
||||
val targetSibling = originalData.targetSibling
|
||||
val psiFactory = KtPsiFactory(originalType)
|
||||
|
||||
for (typeParameter in typeParameters)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// NAME: B
|
||||
open class A
|
||||
typealias MyA = A
|
||||
// SIBLING:
|
||||
fun fA(p:<selection>A</selection>): MyA = MyA()
|
||||
fun fB(p:A): A = A()
|
||||
@@ -0,0 +1,8 @@
|
||||
// NAME: B
|
||||
open class A
|
||||
typealias MyA = B
|
||||
typealias B = A
|
||||
|
||||
// SIBLING:
|
||||
fun fA(p: B): MyA = MyA()
|
||||
fun fB(p: B): B = B()
|
||||
+6
@@ -4076,6 +4076,12 @@ public class ExtractionTestGenerated extends AbstractExtractionTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/introduceTypeAlias"), Pattern.compile("^(.+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("constructorCalls.kt")
|
||||
public void testConstructorCalls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeAlias/constructorCalls.kt");
|
||||
doIntroduceTypeAliasTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("emptyName.kt")
|
||||
public void testEmptyName() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeAlias/emptyName.kt");
|
||||
|
||||
Reference in New Issue
Block a user