[kotlin] k2: introduce type alias

^KTIJ-21140 fixed

GitOrigin-RevId: 88a5721101bde2f6b0c808e950e74cfae85dfd3a
This commit is contained in:
Anna Kozlova
2025-07-29 20:33:41 +00:00
committed by intellij-monorepo-bot
parent bda500c565
commit 891c58b534
12 changed files with 1176 additions and 14 deletions
@@ -563,7 +563,26 @@ class KotlinNameSuggester(
}
}
val TYPE_PARAMETER_NAMES = listOf(
fun suggestNameByMultipleNames(names: Collection<String>, validator: (String) -> Boolean): String {
var i = 0
while (true) {
for (name in names) {
val candidate = if (i > 0) name + i else name
if (validator(candidate)) return candidate
}
i++
}
}
fun suggestNamesForTypeParameters(count: Int, validator: (String) -> Boolean): List<String> {
val result = ArrayList<String>()
for (i in 0 until count) {
result.add(suggestNameByMultipleNames(TYPE_PARAMETER_NAMES, validator))
}
return result
}
val TYPE_PARAMETER_NAMES: List<String> = listOf(
"T", "U", "V", "W", "X", "Y", "Z", "A", "B", "C", "D", "E",
"G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "R", "S"
)
@@ -379,19 +379,25 @@ abstract class AbstractExtractionTest : KotlinLightCodeInsightFixtureTestCase()
KtPsiFactory(project).createModifierList(it).firstChild.node.elementType as KtModifierKeywordToken
}
val editor = fixture.editor
object : KotlinIntroduceTypeAliasHandler() {
override fun doInvoke(
project: Project,
editor: Editor,
elements: List<PsiElement>,
targetSibling: PsiElement,
descriptorSubstitutor: ((IntroduceTypeAliasDescriptor) -> IntroduceTypeAliasDescriptor)?
) {
super.doInvoke(project, editor, elements, explicitPreviousSibling ?: targetSibling) {
it.copy(name = aliasName ?: it.name, visibility = aliasVisibility ?: it.visibility)
}
}
}.invoke(project, editor, file, null)
getIntroduceTypeAliasHandler(explicitPreviousSibling, aliasName, aliasVisibility).invoke(project, editor, file, null)
}
}
protected open fun getIntroduceTypeAliasHandler(
explicitPreviousSibling: PsiElement?,
aliasName: String?,
aliasVisibility: KtModifierKeywordToken?
): RefactoringActionHandler = object : KotlinIntroduceTypeAliasHandler() {
override fun doInvoke(
project: Project,
editor: Editor,
elements: List<PsiElement>,
targetSibling: PsiElement,
descriptorSubstitutor: ((IntroduceTypeAliasDescriptor) -> IntroduceTypeAliasDescriptor)?
) {
super.doInvoke(project, editor, elements, explicitPreviousSibling ?: targetSibling) {
it.copy(name = aliasName ?: it.name, visibility = aliasVisibility ?: it.visibility)
}
}
}
@@ -98,6 +98,11 @@
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="IntroduceField"/>
</action>
<action id="IntroduceTypeAlias" class="org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias.IntroduceTypeAliasAction">
<keyboard-shortcut keymap="$default" first-keystroke="control alt shift A"/>
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="IntroduceProperty"/>
</action>
</actions>
</idea-plugin>
@@ -0,0 +1,151 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias
import com.intellij.lang.refactoring.RefactoringSupportProvider
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.refactoring.RefactoringActionHandler
import com.intellij.refactoring.actions.BasePlatformRefactoringAction
import org.jetbrains.annotations.Nls
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.idea.base.analysis.api.utils.analyzeInModalWindow
import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.k2.refactoring.KotlinFirRefactoringSupportProvider
import org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias.ui.KotlinIntroduceTypeAliasDialog
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.processDuplicates
import org.jetbrains.kotlin.idea.refactoring.introduce.selectElementsWithTargetSibling
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint
import org.jetbrains.kotlin.idea.util.ElementKind.TYPE_CONSTRUCTOR
import org.jetbrains.kotlin.idea.util.ElementKind.TYPE_ELEMENT
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.isUnitTestMode
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
open class KotlinIntroduceTypeAliasHandler : RefactoringActionHandler {
companion object {
@JvmStatic
val REFACTORING_NAME
@Nls
get() = KotlinBundle.message("name.introduce.type.alias")
val INSTANCE = KotlinIntroduceTypeAliasHandler()
}
private fun selectElements(
editor: Editor,
file: KtFile,
continuation: (elements: List<PsiElement>, targetSibling: PsiElement) -> Unit
) {
selectElementsWithTargetSibling(
REFACTORING_NAME,
editor,
file,
KotlinBundle.message("title.select.target.code.block"),
listOf(TYPE_ELEMENT, TYPE_CONSTRUCTOR),
{ null },
{ _, parent -> listOf(parent.containingFile) },
continuation
)
}
private fun runRefactoring(descriptor: IntroduceTypeAliasDescriptor, project: Project, editor: Editor) {
val typeAlias = project.executeWriteCommand<KtTypeAlias>(REFACTORING_NAME) { descriptor.generateTypeAlias() }
val duplicateReplacers = analyzeInModalWindow(typeAlias, KotlinBundle.message("text.process.duplicates")) {
findDuplicates(typeAlias)
}
if (duplicateReplacers.isNotEmpty()) {
processDuplicates(duplicateReplacers, project, editor)
}
}
open fun doInvoke(
project: Project,
editor: Editor,
elements: List<PsiElement>,
targetSibling: KtElement,
descriptorSubstitutor: ((IntroduceTypeAliasDescriptor) -> IntroduceTypeAliasDescriptor)? = null
) {
val elementToExtract = elements.singleOrNull()
val errorMessage = when (elementToExtract) {
is KtSimpleNameExpression -> {
if (!(isTypeConstructorReference(elementToExtract) || isDoubleColonReceiver(elementToExtract))) KotlinBundle.message("error.text.type.reference.is.expected"
) else null
}
!is KtTypeElement -> KotlinBundle.message("error.text.no.type.to.refactor")
else -> null
}
if (errorMessage != null) return showErrorHint(project, editor, errorMessage, REFACTORING_NAME)
val introduceData = when (elementToExtract) {
is KtTypeElement -> IntroduceTypeAliasData(elementToExtract, targetSibling)
else -> IntroduceTypeAliasData(
elementToExtract!!.getStrictParentOfType<KtTypeElement>() ?: elementToExtract as KtElement,
targetSibling,
true
)
}
when (val analysisResult = analyzeResult(introduceData)) {
is IntroduceTypeAliasAnalysisResult.Error -> {
return showErrorHint(project, editor, analysisResult.message, REFACTORING_NAME)
}
is IntroduceTypeAliasAnalysisResult.Success -> {
val originalDescriptor = analysisResult.descriptor
if (isUnitTestMode()) {
val (descriptor, conflicts) = descriptorSubstitutor!!(originalDescriptor).validate()
project.checkConflictsInteractively(conflicts) { runRefactoring(descriptor, project, editor) }
} else {
KotlinIntroduceTypeAliasDialog(project, originalDescriptor) {
runRefactoring(
it.currentDescriptor,
project,
editor
)
}.show()
}
}
}
}
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
if (file !is KtFile) return
val offset = if (editor.selectionModel.hasSelection()) editor.selectionModel.selectionStart else editor.caretModel.offset
val refExpression = file.findElementAt(offset)?.getNonStrictParentOfType<KtSimpleNameExpression>()
if (refExpression != null && isDoubleColonReceiver(refExpression)) {
return doInvoke(project, editor, listOf(refExpression), refExpression.getOutermostParentContainedIn(file) as KtElement)
}
selectElements(editor, file) { elements, targetSibling -> doInvoke(project, editor, elements, targetSibling as KtElement) }
}
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
throw AssertionError("$REFACTORING_NAME can only be invoked from editor")
}
}
class IntroduceTypeAliasAction : BasePlatformRefactoringAction() {
override fun getRefactoringHandler(provider: RefactoringSupportProvider): RefactoringActionHandler? {
return if (provider is KotlinFirRefactoringSupportProvider) KotlinIntroduceTypeAliasHandler.INSTANCE else null
}
override fun isAvailableInEditorOnly(): Boolean {
return true
}
override fun isAvailableOnElementInEditorAndFile(element: PsiElement, editor: Editor, file: PsiFile, context: DataContext): Boolean {
return super.isAvailableOnElementInEditorAndFile(element, editor, file, context) &&
(ModuleUtil.findModuleForPsiElement(file)?.languageVersionSettings?.supportsFeature(LanguageFeature.TypeAliases) ?: false)
}
}
@@ -0,0 +1,60 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.psi.CopyablePsiUserDataProperty
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
internal var KtTypeReference.originalReference: KtTypeReference? by CopyablePsiUserDataProperty(Key.create("RESOLVE_INFO"))
class IntroduceTypeAliasData(
val originalTypeElement: KtElement,
val targetSibling: KtElement,
val extractTypeConstructor: Boolean = false
) : Disposable {
init {
markReferences()
}
private fun markReferences() {
val visitor = object : KtTreeVisitorVoid() {
override fun visitTypeReference(typeReference: KtTypeReference) {
val typeElement = typeReference.typeElement ?: return
typeReference.originalReference = typeReference
typeElement.typeArgumentsAsTypes.forEach { it?.accept(this) }
}
}
(originalTypeElement.parent as? KtTypeReference ?: originalTypeElement).accept(visitor)
}
override fun dispose() {
if (!originalTypeElement.isValid) return
originalTypeElement.forEachDescendantOfType<KtTypeReference> { it.originalReference = null }
}
}
data class TypeParameter(val name: String, val typeReferenceInfos: Collection<KtTypeReference>)
data class IntroduceTypeAliasDescriptor(
val originalData: IntroduceTypeAliasData,
val name: String,
val visibility: KtModifierKeywordToken?,
val typeParameters: List<TypeParameter>
)
data class IntroduceTypeAliasDescriptorWithConflicts(
val descriptor: IntroduceTypeAliasDescriptor,
val conflicts: MultiMap<PsiElement, String>
)
@@ -0,0 +1,299 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.NlsContexts
import com.intellij.psi.PsiElement
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.analysis.api.symbols.KaClassifierSymbol
import org.jetbrains.kotlin.analysis.api.types.KaClassType
import org.jetbrains.kotlin.idea.base.analysis.api.utils.analyzeInModalWindow
import org.jetbrains.kotlin.idea.base.codeInsight.KotlinNameSuggester
import org.jetbrains.kotlin.idea.base.psi.replaced
import org.jetbrains.kotlin.idea.base.psi.unifier.KotlinPsiRange
import org.jetbrains.kotlin.idea.base.psi.unifier.toRange
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
import org.jetbrains.kotlin.idea.k2.refactoring.introduce.K2SemanticMatcher
import org.jetbrains.kotlin.idea.k2.refactoring.introduce.K2SemanticMatcher.matchRanges
import org.jetbrains.kotlin.idea.refactoring.introduce.insertDeclaration
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
sealed class IntroduceTypeAliasAnalysisResult {
class Error(@NlsContexts.DialogMessage val message: String) : IntroduceTypeAliasAnalysisResult()
class Success(val descriptor: IntroduceTypeAliasDescriptor) : IntroduceTypeAliasAnalysisResult()
}
fun analyzeResult(aliasData: IntroduceTypeAliasData): IntroduceTypeAliasAnalysisResult {
val psiFactory = KtPsiFactory(aliasData.originalTypeElement.project)
val dummyVar =
(psiFactory.createBlockCodeFragment("val a: Int", aliasData.targetSibling.parent).getContentElement().children[0] as KtProperty).apply {
WriteCommandAction.writeCommandAction(project).run<Throwable> {
typeReference!!.replace(
aliasData.originalTypeElement.parent as? KtTypeReference
?: if (aliasData.originalTypeElement is KtTypeElement) psiFactory.createType(
aliasData.originalTypeElement
) else psiFactory.createType(aliasData.originalTypeElement.text)
)
}
}
val newTypeReference = dummyVar.typeReference!!
return analyzeInModalWindow(newTypeReference, KotlinBundle.message("fix.change.signature.prepare")) {
val referencesToReplaceWithTypeParameters = findReferencesToReplaceWithTypeParameters(aliasData, newTypeReference)
val typeParameterNameValidator = CollectingNameValidator()
val brokenReferences =
referencesToReplaceWithTypeParameters.keySet().filter { referencesToReplaceWithTypeParameters[it].isNotEmpty() }
val typeParameterNames = KotlinNameSuggester.suggestNamesForTypeParameters(brokenReferences.size, typeParameterNameValidator)
val typeParameters =
(typeParameterNames zip brokenReferences).map { TypeParameter(it.first, referencesToReplaceWithTypeParameters[it.second]) }
if (typeParameters.any { it.typeReferenceInfos.any { info -> info.typeElement == aliasData.originalTypeElement } }) {
return@analyzeInModalWindow IntroduceTypeAliasAnalysisResult.Error(KotlinBundle.message("text.type.alias.cannot.refer.to.types.which.aren.t.accessible.in.the.scope.where.it.s.defined"))
}
val descriptor = IntroduceTypeAliasDescriptor(aliasData, "Dummy", null, typeParameters)
val initialName =
KotlinNameSuggester.suggestTypeAliasNameByPsi(descriptor.generateTypeAlias(true).getTypeReference()!!.typeElement!!) {
findClassifierByName(aliasData.targetSibling, descriptor) == null
}
return@analyzeInModalWindow IntroduceTypeAliasAnalysisResult.Success(descriptor.copy(name = initialName))
}
}
context(KaSession)
private fun findReferencesToReplaceWithTypeParameters(
aliasData: IntroduceTypeAliasData,
newTypeReference: KtTypeReference,
): MultiMap<KtTypeReference, KtTypeReference> {
val newReferences = newTypeReference.collectDescendantsOfType<KtTypeReference> { it.originalReference != null }
val referencesToReplaceWithTypeParameters = MultiMap.createLinked<KtTypeReference, KtTypeReference>()
val forcedCandidates = if (aliasData.extractTypeConstructor) newTypeReference.typeElement!!.typeArgumentsAsTypes else emptyList()
for (newReference in newReferences) {
val originalReference = newReference.originalReference!!
if (newReference !in forcedCandidates) { //skip for resolved references
val originalSymbol = (originalReference.type as? KaClassType)?.symbol
val newSymbol = (newReference.type as? KaClassType)?.symbol
if (originalSymbol == newSymbol) continue
}
with(K2SemanticMatcher) {
val firstEquivalentReference =
referencesToReplaceWithTypeParameters.keySet().firstOrNull { it.isSemanticMatch(originalReference) }
if (firstEquivalentReference != null) {
referencesToReplaceWithTypeParameters.putValue(firstEquivalentReference, originalReference)
} else {
referencesToReplaceWithTypeParameters.putValue(originalReference, originalReference)
}
}
// skip references under other references
val referencesToExtractIterator = referencesToReplaceWithTypeParameters.values().iterator()
while (referencesToExtractIterator.hasNext()) {
val referenceToExtract = referencesToExtractIterator.next()
if (originalReference.isAncestor(referenceToExtract, true)) {
referencesToExtractIterator.remove()
}
}
}
return referencesToReplaceWithTypeParameters
}
fun IntroduceTypeAliasData.getApplicableVisibilities(): List<KtModifierKeywordToken> = when (targetSibling.parent) {
is KtClassBody -> listOf(PRIVATE_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD, PROTECTED_KEYWORD)
is KtFile -> listOf(PRIVATE_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD)
else -> emptyList()
}
fun IntroduceTypeAliasDescriptor.validate(): IntroduceTypeAliasDescriptorWithConflicts {
val conflicts = MultiMap<PsiElement, String>()
val originalType = originalData.originalTypeElement
when {
name.isEmpty() -> conflicts.putValue(originalType, KotlinBundle.message("text.no.name.provided.for.type.alias"))
!name.isIdentifier() -> conflicts.putValue(
originalType,
KotlinBundle.message("text.type.alias.name.must.be.a.valid.identifier.0", name)
)
analyzeInModalWindow(originalData.targetSibling, KotlinBundle.message("progress.title.check.for.conflicts")) {
findClassifierByName(originalData.targetSibling, this@validate) != null
} -> conflicts.putValue(originalType, KotlinBundle.message("text.type.already.exists.in.the.target.scope", name))
}
if (typeParameters.distinctBy { it.name }.size != typeParameters.size) {
conflicts.putValue(originalType, KotlinBundle.message("text.type.parameter.names.must.be.distinct"))
}
if (visibility != null && visibility !in originalData.getApplicableVisibilities()) {
conflicts.putValue(originalType, KotlinBundle.message("text.0.is.not.allowed.in.the.target.context", visibility))
}
return IntroduceTypeAliasDescriptorWithConflicts(this, conflicts)
}
context(KaSession)
private fun findClassifierByName(
targetSibling: KtElement, descriptor: IntroduceTypeAliasDescriptor
): KaClassifierSymbol? {
val ktFile = targetSibling.containingKtFile
return ktFile.scopeContext(targetSibling).compositeScope().classifiers { it.asString() == descriptor.name }.firstOrNull()
}
context(KaSession)
fun findDuplicates(typeAlias: KtTypeAlias): Map<KotlinPsiRange, () -> Unit> {
val aliasName = typeAlias.name?.quoteIfNeeded() ?: return emptyMap()
val aliasRange = typeAlias.textRange
val typeAliasSymbol = typeAlias.symbol
val psiFactory = KtPsiFactory(typeAlias.project)
fun replaceTypeReference(occurrence: KtTypeReference, typeArgumentsText: String) {
occurrence.replace(psiFactory.createType("$aliasName$typeArgumentsText"))
}
fun replaceOccurrence(occurrence: PsiElement, arguments: List<KtTypeElement>) {
val typeArgumentsText = if (arguments.isNotEmpty()) "<${arguments.joinToString { it.text }}>" else ""
when (occurrence) {
is KtTypeReference -> {
replaceTypeReference(occurrence, typeArgumentsText)
}
is KtSuperTypeCallEntry -> {
occurrence.calleeExpression.typeReference?.let { replaceTypeReference(it, typeArgumentsText) }
}
is KtCallElement -> {
val qualifiedExpression = occurrence.parent as? KtQualifiedExpression
val callExpression = if (qualifiedExpression != null && qualifiedExpression.selectorExpression == occurrence) {
qualifiedExpression.replaced(occurrence)
} else occurrence
val typeArgumentList = callExpression.typeArgumentList
if (arguments.isNotEmpty()) {
val newTypeArgumentList = psiFactory.createTypeArguments(typeArgumentsText)
typeArgumentList?.replace(newTypeArgumentList) ?: callExpression.addAfter(
newTypeArgumentList, callExpression.calleeExpression
)
} else {
typeArgumentList?.delete()
}
callExpression.calleeExpression?.replace(psiFactory.createExpression(aliasName))
}
is KtExpression -> occurrence.replace(psiFactory.createExpression(aliasName))
}
}
val rangesWithReplacers = ArrayList<Pair<KotlinPsiRange, () -> Unit>>()
val originalTypePsi = (typeAliasSymbol.expandedType as? KaClassType)?.symbol?.psi
if (originalTypePsi != null) {
for (reference in ReferencesSearch.search(originalTypePsi, LocalSearchScope(typeAlias.parent)).asIterable()) {
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 != typeAliasSymbol.typeParameters.size) continue
if ((typeAliasSymbol.expandedType as? KaClassType)?.isMarkedNullable == true && occurrence is KtUserType && occurrence.parent !is KtNullableType) continue
rangesWithReplacers += occurrence.toRange() to { replaceOccurrence(occurrence, arguments) }
}
}
typeAlias.getTypeReference().toRange()
.match(typeAlias.parent) { targetRange, patternRange -> matchRanges(targetRange, patternRange, typeAlias.typeParameters) }
.asSequence()
.filter { !(it.range.textRange.intersects(aliasRange)) }
.mapNotNullTo(rangesWithReplacers) { match ->
val occurrence = match.range.elements.singleOrNull() as? KtTypeReference ?: return@mapNotNullTo null
val arguments = typeAlias.typeParameters.mapNotNull { (match.substitution[it] as? KtTypeReference)?.typeElement }
if (arguments.size != typeAlias.typeParameters.size) return@mapNotNullTo null
match.range to { replaceOccurrence(occurrence, arguments) }
}
return rangesWithReplacers.toMap()
}
private var KtTypeReference.typeParameterInfo: TypeParameter? by CopyablePsiUserDataProperty(Key.create("TYPE_PARAMETER_INFO"))
fun IntroduceTypeAliasDescriptor.generateTypeAlias(previewOnly: Boolean = false): KtTypeAlias {
val originalElement = originalData.originalTypeElement
val psiFactory = KtPsiFactory(originalElement.project)
for (typeParameter in typeParameters) for (it in typeParameter.typeReferenceInfos) {
it.typeParameterInfo = typeParameter
}
val typeParameterNames = typeParameters.map { it.name }
val typeAlias = if (originalElement is KtTypeElement) {
psiFactory.createTypeAlias(name, typeParameterNames, originalElement)
} else {
psiFactory.createTypeAlias(name, typeParameterNames, originalElement.text)
}
if (visibility != null && visibility != DEFAULT_VISIBILITY_KEYWORD) {
typeAlias.addModifier(visibility)
}
for (typeParameter in typeParameters) for (it in typeParameter.typeReferenceInfos) {
it.typeParameterInfo = null
}
fun replaceUsage() {
val aliasInstanceText = if (typeParameters.isNotEmpty()) {
"$name<${typeParameters.joinToString { it.typeReferenceInfos.first().text }}>"
} else {
name
}
when (originalElement) {
is KtTypeElement -> originalElement.replace(psiFactory.createType(aliasInstanceText).typeElement!!)
is KtExpression -> originalElement.replace(psiFactory.createExpression(aliasInstanceText))
}
}
fun introduceTypeParameters() {
typeAlias.getTypeReference()!!.forEachDescendantOfType<KtTypeReference> {
val typeParameter = it.typeParameterInfo ?: return@forEachDescendantOfType
val typeParameterReference = psiFactory.createType(typeParameter.name)
it.replace(typeParameterReference)
}
}
return if (previewOnly) {
introduceTypeParameters()
typeAlias
} else {
replaceUsage()
introduceTypeParameters()
insertDeclaration(typeAlias, originalData.targetSibling)
}
}
@@ -0,0 +1,32 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias.ui
import org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias.TypeParameter
import org.jetbrains.kotlin.idea.refactoring.introduce.ui.AbstractParameterTablePanel
open class IntroduceTypeAliasParameterTablePanel :
AbstractParameterTablePanel<TypeParameter, IntroduceTypeAliasParameterTablePanel.TypeParameterInfo>() {
class TypeParameterInfo(
originalParameter: TypeParameter
) : AbstractParameterTablePanel.AbstractParameterInfo<TypeParameter>(originalParameter) {
init {
name = originalParameter.name
}
override fun toParameter() = originalParameter.copy(name)
}
fun init(parameters: List<TypeParameter>) {
parameterInfos = parameters.mapTo(ArrayList(), ::TypeParameterInfo)
super.init()
}
override fun isCheckMarkColumnEditable() = false
val selectedTypeParameterInfos: List<TypeParameterInfo>
get() = parameterInfos.filter { it.isEnabled }
val selectedTypeParameters: List<TypeParameter>
get() = selectedTypeParameterInfos.map { it.toParameter() }
}
@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias.ui.KotlinIntroduceTypeAliasDialog">
<grid id="27dc6" binding="contentPane" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="524" height="396"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="34b30" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="fa2d1" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="2aa47" class="com.intellij.ui.TitledSeparator" binding="inputParametersPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints border-constraint="Center"/>
<properties>
<text value=""/>
</properties>
<border type="none" title-resource-bundle="messages/KotlinBundle" title-key="title.parameters"/>
<children/>
</grid>
</children>
</grid>
</children>
</grid>
<grid id="b45d1" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="-1" height="200"/>
</grid>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="4266d" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none" title-resource-bundle="messages/KotlinBundle" title-key="signature.preview"/>
<children>
<component id="7722b" class="org.jetbrains.kotlin.idea.refactoring.introduce.ui.KotlinSignatureComponent" binding="signaturePreviewField" custom-create="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<minimum-size width="500" height="100"/>
<preferred-size width="500" height="100"/>
</grid>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
<grid id="5fe96" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="true"/>
<visible value="true"/>
</properties>
<border type="empty"/>
<children>
<component id="d1d09" class="javax.swing.JLabel" binding="aliasNameLabel">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/KotlinBundle" key="name"/>
</properties>
</component>
<component id="52aa7" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="114" height="16"/>
</grid>
</constraints>
<properties>
<labelFor value="71ee7"/>
<text resource-bundle="messages/KotlinBundle" key="visibility"/>
<visible value="true"/>
</properties>
</component>
<grid id="d0f58" binding="aliasNamePanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
<component id="71ee7" class="javax.swing.JComboBox" binding="visibilityBox">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="114" height="26"/>
</grid>
</constraints>
<properties>
<visible value="true"/>
</properties>
</component>
</children>
</grid>
<vspacer id="cd10f">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
</children>
</grid>
</form>
@@ -0,0 +1,230 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias.ui;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.ui.NameSuggestionsField;
import com.intellij.ui.TitledSeparator;
import com.intellij.util.containers.MultiMap;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.KotlinFileType;
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle;
import org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias.IntroduceTypeAliasDescriptor;
import org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias.IntroduceTypeAliasImplKt;
import org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias.KotlinIntroduceTypeAliasHandler;
import org.jetbrains.kotlin.idea.refactoring.introduce.ui.KotlinSignatureComponent;
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.kotlin.idea.refactoring.KotlinCommonRefactoringUtilKt.checkConflictsInteractively;
public class KotlinIntroduceTypeAliasDialog extends DialogWrapper {
private JPanel contentPane;
private TitledSeparator inputParametersPanel;
private JComboBox<KtModifierKeywordToken> visibilityBox;
private KotlinSignatureComponent signaturePreviewField;
private JPanel aliasNamePanel;
private NameSuggestionsField aliasNameField;
private JLabel aliasNameLabel;
private IntroduceTypeAliasParameterTablePanel parameterTablePanel;
private final Project project;
private final IntroduceTypeAliasDescriptor originalDescriptor;
private IntroduceTypeAliasDescriptor currentDescriptor;
private final Function1<KotlinIntroduceTypeAliasDialog, Unit> onAccept;
public KotlinIntroduceTypeAliasDialog(
@NotNull Project project,
@NotNull IntroduceTypeAliasDescriptor originalDescriptor,
@NotNull Function1<KotlinIntroduceTypeAliasDialog, Unit> onAccept) {
super(project, true);
this.project = project;
this.originalDescriptor = originalDescriptor;
this.currentDescriptor = originalDescriptor;
this.onAccept = onAccept;
setModal(true);
setTitle(KotlinIntroduceTypeAliasHandler.getREFACTORING_NAME());
init();
update();
}
private void createUIComponents() {
this.signaturePreviewField = new KotlinSignatureComponent("", project);
}
private boolean isVisibilitySectionAvailable() {
return !getApplicableVisibilities().isEmpty();
}
private @NotNull List<KtModifierKeywordToken> getApplicableVisibilities() {
return IntroduceTypeAliasImplKt.getApplicableVisibilities(originalDescriptor.getOriginalData());
}
private String getAliasName() {
return KtPsiUtilKt.quoteIfNeeded(aliasNameField.getEnteredName());
}
private @Nullable KtModifierKeywordToken getVisibility() {
if (!isVisibilitySectionAvailable()) return null;
return (KtModifierKeywordToken) visibilityBox.getSelectedItem();
}
private boolean checkNames() {
if (!KtPsiUtilKt.isIdentifier(getAliasName())) return false;
if (parameterTablePanel != null) {
for (IntroduceTypeAliasParameterTablePanel.TypeParameterInfo parameterInfo : parameterTablePanel.getSelectedTypeParameterInfos()) {
if (!KtPsiUtilKt.isIdentifier(parameterInfo.getName())) return false;
}
}
return true;
}
private void update() {
this.currentDescriptor = createDescriptor();
setOKActionEnabled(checkNames());
signaturePreviewField.setText(IntroduceTypeAliasImplKt.generateTypeAlias(currentDescriptor, true).getText());
}
@Override
protected void init() {
super.init();
visibilityBox.setModel(new DefaultComboBoxModel<>(getApplicableVisibilities().toArray(new KtModifierKeywordToken[0])));
visibilityBox.setRenderer(
new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus
) {
@NlsSafe String tokenValue = value != null ? ((KtModifierKeywordToken) value).getValue() : null;
return super.getListCellRendererComponent(list, tokenValue, index, isSelected, cellHasFocus);
}
}
);
aliasNameField = new NameSuggestionsField(new String[]{originalDescriptor.getName()}, project, KotlinFileType.INSTANCE);
aliasNameField.addDataChangedListener(() -> update());
aliasNamePanel.add(aliasNameField, BorderLayout.CENTER);
aliasNameLabel.setLabelFor(aliasNameField);
boolean enableVisibility = isVisibilitySectionAvailable();
visibilityBox.setEnabled(enableVisibility);
if (enableVisibility) {
KtModifierKeywordToken defaultVisibility = originalDescriptor.getVisibility();
if (defaultVisibility == null) {
defaultVisibility = KtTokens.PUBLIC_KEYWORD;
}
visibilityBox.setSelectedItem(defaultVisibility);
}
visibilityBox.addItemListener(
new ItemListener() {
@Override
public void itemStateChanged(@NotNull ItemEvent e) {
update();
}
}
);
if (!originalDescriptor.getTypeParameters().isEmpty()) {
parameterTablePanel = new IntroduceTypeAliasParameterTablePanel() {
@Override
protected void updateSignature() {
KotlinIntroduceTypeAliasDialog.this.update();
}
@Override
protected void onEnterAction() {
doOKAction();
}
@Override
protected void onCancelAction() {
doCancelAction();
}
};
parameterTablePanel.init(originalDescriptor.getTypeParameters());
inputParametersPanel.setText(KotlinBundle.message("text.type.parameters"));
inputParametersPanel.setLabelFor(parameterTablePanel.getTable());
inputParametersPanel.add(parameterTablePanel);
}
else {
inputParametersPanel.setVisible(false);
}
}
@Override
protected void doOKAction() {
MultiMap<PsiElement, String> conflicts = IntroduceTypeAliasImplKt.validate(currentDescriptor).getConflicts();
checkConflictsInteractively(
project,
conflicts,
new Function0<>() {
@Override
public Unit invoke() {
close(OK_EXIT_CODE);
return Unit.INSTANCE;
}
},
new Function0<>() {
@Override
public Unit invoke() {
KotlinIntroduceTypeAliasDialog.super.doOKAction();
return onAccept.invoke(KotlinIntroduceTypeAliasDialog.this);
}
}
);
}
@Override
public JComponent getPreferredFocusedComponent() {
return aliasNameField;
}
@Override
protected JComponent createCenterPanel() {
return contentPane;
}
@Override
protected @NotNull JComponent createContentPane() {
return contentPane;
}
private @NotNull IntroduceTypeAliasDescriptor createDescriptor() {
return originalDescriptor.copy(
originalDescriptor.getOriginalData(),
getAliasName(),
getVisibility(),
parameterTablePanel != null ? parameterTablePanel.getSelectedTypeParameters() : Collections.emptyList()
);
}
public IntroduceTypeAliasDescriptor getCurrentDescriptor() {
return currentDescriptor;
}
}
@@ -0,0 +1,48 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.k2.refactoring.introduce
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.refactoring.RefactoringActionHandler
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.common.runAll
import org.jetbrains.kotlin.idea.fir.invalidateCaches
import org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias.IntroduceTypeAliasDescriptor
import org.jetbrains.kotlin.idea.k2.refactoring.introduceTypeAlias.KotlinIntroduceTypeAliasHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractExtractionTest
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.psi.KtElement
abstract class AbstractK2IntroduceTypeAliasTest : AbstractExtractionTest() {
override fun getIntroduceTypeAliasHandler(
explicitPreviousSibling: PsiElement?,
aliasName: String?,
aliasVisibility: KtModifierKeywordToken?
): RefactoringActionHandler {
return object : KotlinIntroduceTypeAliasHandler() {
override fun doInvoke(
project: Project,
editor: Editor,
elements: List<PsiElement>,
targetSibling: KtElement,
descriptorSubstitutor: ((IntroduceTypeAliasDescriptor) -> IntroduceTypeAliasDescriptor)?
) {
super.doInvoke(project, editor, elements, (explicitPreviousSibling ?: targetSibling) as KtElement) {
it.copy(name = aliasName ?: it.name, visibility = aliasVisibility ?: it.visibility)
}
}
}
}
override fun tearDown() {
runAll(
{ project.invalidateCaches() },
{ super.tearDown() },
)
}
override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.getInstance()
}
@@ -0,0 +1,177 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.k2.refactoring.introduce;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode;
import org.jetbrains.kotlin.idea.base.test.TestRoot;
import org.jetbrains.kotlin.idea.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.idea.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
/**
* This class is generated by {@link org.jetbrains.kotlin.testGenerator.generator.TestGenerator}.
* DO NOT MODIFY MANUALLY.
*/
@SuppressWarnings("all")
@TestRoot("refactorings/kotlin.refactorings.tests.k2")
@TestDataPath("$CONTENT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../idea/tests/testData/refactoring/introduceTypeAlias")
public class K2IntroduceTypeAliasTestGenerated extends AbstractK2IntroduceTypeAliasTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doIntroduceTypeAliasTest, this, testDataFilePath);
}
@TestMetadata("callableReference.kt")
public void testCallableReference() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/callableReference.kt");
}
@TestMetadata("callableReferenceSelector.kt")
public void testCallableReferenceSelector() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/callableReferenceSelector.kt");
}
@TestMetadata("classLiteral.kt")
public void testClassLiteral() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/classLiteral.kt");
}
@TestMetadata("constructorCalls.kt")
public void testConstructorCalls() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/constructorCalls.kt");
}
@TestMetadata("deeplyNestedClass.kt")
public void testDeeplyNestedClass() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/deeplyNestedClass.kt");
}
@TestMetadata("emptyName.kt")
public void testEmptyName() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/emptyName.kt");
}
@TestMetadata("entireTypeExtracted.kt")
public void testEntireTypeExtracted() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/entireTypeExtracted.kt");
}
@TestMetadata("equivalentNestedTypeElements.kt")
public void testEquivalentNestedTypeElements() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/equivalentNestedTypeElements.kt");
}
@TestMetadata("existingTypeClash.kt")
public void testExistingTypeClash() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/existingTypeClash.kt");
}
@TestMetadata("functionTypeDuplicatesNoTypeParameters.kt")
public void testFunctionTypeDuplicatesNoTypeParameters() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/functionTypeDuplicatesNoTypeParameters.kt");
}
@TestMetadata("functionTypeDuplicatesWithTypeParameters.kt")
public void testFunctionTypeDuplicatesWithTypeParameters() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/functionTypeDuplicatesWithTypeParameters.kt");
}
@TestMetadata("functionalType.kt")
public void testFunctionalType() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/functionalType.kt");
}
@TestMetadata("localTypeExtracted.kt")
public void testLocalTypeExtracted() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/localTypeExtracted.kt");
}
@TestMetadata("localWithVisibility.kt")
public void testLocalWithVisibility() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/localWithVisibility.kt");
}
@TestMetadata("nestedClass.kt")
public void testNestedClass() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/nestedClass.kt");
}
@TestMetadata("nestedTypesExtracted.kt")
public void testNestedTypesExtracted() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/nestedTypesExtracted.kt");
}
@TestMetadata("noTypeElement.kt")
public void testNoTypeElement() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/noTypeElement.kt");
}
@TestMetadata("nonIdentifierName.kt")
public void testNonIdentifierName() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/nonIdentifierName.kt");
}
@TestMetadata("nullableType.kt")
public void testNullableType() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/nullableType.kt");
}
@TestMetadata("privateTypeAlias.kt")
public void testPrivateTypeAlias() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/privateTypeAlias.kt");
}
@TestMetadata("protectedInFile.kt")
public void testProtectedInFile() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/protectedInFile.kt");
}
@TestMetadata("skipQualifiers.kt")
public void testSkipQualifiers() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/skipQualifiers.kt");
}
@TestMetadata("suggestedName.kt")
public void testSuggestedName() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/suggestedName.kt");
}
@TestMetadata("topLevelTypeAlias.kt")
public void testTopLevelTypeAlias() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/topLevelTypeAlias.kt");
}
@TestMetadata("typeConstructor.kt")
public void testTypeConstructor() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/typeConstructor.kt");
}
@TestMetadata("typesExtractedWithFunctionalType.kt")
public void testTypesExtractedWithFunctionalType() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/typesExtractedWithFunctionalType.kt");
}
@TestMetadata("unmatchedNullability.kt")
public void testUnmatchedNullability() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/unmatchedNullability.kt");
}
@TestMetadata("userTypeDuplicatesNoTypeParameters.kt")
public void testUserTypeDuplicatesNoTypeParameters() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/userTypeDuplicatesNoTypeParameters.kt");
}
@TestMetadata("userTypeDuplicatesWithTypeParameters.kt")
public void testUserTypeDuplicatesWithTypeParameters() throws Exception {
runTest("../../idea/tests/testData/refactoring/introduceTypeAlias/userTypeDuplicatesWithTypeParameters.kt");
}
}
@@ -73,6 +73,10 @@ internal fun MutableTWorkspace.generateK2RefactoringsTests() {
model("refactoring/extractFunction", pattern = Patterns.KT_OR_KTS, testMethodName = "doExtractFunctionTest")
}
testClass<AbstractK2IntroduceTypeAliasTest> {
model("refactoring/introduceTypeAlias", pattern = Patterns.KT_OR_KTS, testMethodName = "doIntroduceTypeAliasTest")
}
testClass<AbstractK2IntroduceFunctionWithExtractFunctionModifierTest> {
model("refactoring/extractFunctionModifier", pattern = Patterns.KT_OR_KTS, testMethodName = "doExtractFunctionTest")
}