diff --git a/ChangeLog.md b/ChangeLog.md index 4530b476630..34db7efc6e7 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -118,6 +118,10 @@ These artifacts include extensions for the types available in the latter JDKs, s - [`KT-13216`](https://youtrack.jetbrains.com/issue/KT-13216) Move: Report separate conflicts for each property accessor - [`KT-13216`](https://youtrack.jetbrains.com/issue/KT-13216) Move: Forbid moving of enum entries +##### New features + +- [`KT-13155`](https://youtrack.jetbrains.com/issue/KT-13155) Implement "Introduce Type Parameter" refactoring + ## 1.0.4 ### Compiler diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.kt index a8ba8539f71..cbc99e9ae26 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.kt @@ -32,7 +32,9 @@ import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.utils.takeSnapshot +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice operator fun BindingContext.get(slice: ReadOnlySlice, key: K): V? = get(slice, key) @@ -98,4 +100,16 @@ fun KtExpression.getReferenceTargets(context: BindingContext): Collection parent.getAbbreviatedTypeOrType(context) + is KtNullableType -> { + val outerType = parent.getAbbreviatedTypeOrType(context) + if (this is KtNullableType) outerType else outerType?.makeNotNullable() + } + else -> null + } +} \ No newline at end of file diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 5ea357c4aea..7162ef03526 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -782,6 +782,7 @@ fun main(args: Array) { model("refactoring/introduceParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceSimpleParameterTest") model("refactoring/introduceLambdaParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceLambdaParameterTest") model("refactoring/introduceJavaParameter", extension = "java", testMethod = "doIntroduceJavaParameterTest") + model("refactoring/introduceTypeParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceTypeParameterTest") model("refactoring/introduceTypeAlias", pattern = KT_OR_KTS, testMethod = "doIntroduceTypeAliasTest") } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 5ed0736741c..776e855e40b 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -162,10 +162,16 @@ + + + + + - + diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createTypeParameter/CreateTypeParameterByRefActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createTypeParameter/CreateTypeParameterByRefActionFactory.kt index 1b586662338..b2e9f2d61b1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createTypeParameter/CreateTypeParameterByRefActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createTypeParameter/CreateTypeParameterByRefActionFactory.kt @@ -53,18 +53,22 @@ object CreateTypeParameterByRefActionFactory : KotlinIntentionActionFactoryWithD return ktUserType } - override fun extractFixData(element: KtUserType, diagnostic: Diagnostic): CreateTypeParameterData? { - val name = element.referencedName ?: return null + fun extractFixData(element: KtTypeElement, newName: String): CreateTypeParameterData? { val declaration = element.parents.firstOrNull { it is KtProperty || it is KtNamedFunction || it is KtClass } as? KtTypeParameterListOwner ?: return null val containingDescriptor = declaration.resolveToDescriptor() - val fakeTypeParameter = createFakeTypeParameterDescriptor(containingDescriptor, name) + val fakeTypeParameter = createFakeTypeParameterDescriptor(containingDescriptor, newName) val upperBoundType = getUnsubstitutedTypeConstraintInfo(element)?.let { it.performSubstitution(it.typeParameter.typeConstructor to TypeProjectionImpl(fakeTypeParameter.defaultType))?.upperBound } if (upperBoundType != null && upperBoundType.containsError()) return null - return CreateTypeParameterData(name, declaration, upperBoundType, fakeTypeParameter) + return CreateTypeParameterData(newName, declaration, upperBoundType, fakeTypeParameter) + } + + override fun extractFixData(element: KtUserType, diagnostic: Diagnostic): CreateTypeParameterData? { + val name = element.referencedName ?: return null + return extractFixData(element, name) } override fun createFixes( diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createTypeParameter/CreateTypeParameterFromUsageFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createTypeParameter/CreateTypeParameterFromUsageFix.kt index cd6fc5e70b2..81f3fb148cf 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createTypeParameter/CreateTypeParameterFromUsageFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createTypeParameter/CreateTypeParameterFromUsageFix.kt @@ -42,16 +42,21 @@ import org.jetbrains.kotlin.types.typeUtil.isNullableAny import org.jetbrains.kotlin.utils.SmartList class CreateTypeParameterFromUsageFix( - originalElement: KtUserType, + originalElement: KtTypeElement, private val data: CreateTypeParameterData -) : CreateFromUsageFixBase(originalElement) { +) : CreateFromUsageFixBase(originalElement) { override fun getText() = "Create type parameter '${data.name}' in " + ElementDescriptionUtil.getElementDescription(data.declaration, UsageViewTypeLocation.INSTANCE) + " '${data.declaration.name}'" override fun startInWriteAction() = false override fun invoke(project: Project, editor: Editor?, file: KtFile) { + doInvoke() + } + + fun doInvoke(): KtTypeParameter? { val declaration = data.declaration + val project = declaration.project val usages = project.runSynchronouslyWithProgress("Searching ${declaration.name}...", true) { runReadAction { ReferencesSearch @@ -62,9 +67,9 @@ class CreateTypeParameterFromUsageFix( } .toSet() } - } ?: return + } ?: return null - runWriteAction { + return runWriteAction { val psiFactory = KtPsiFactory(project) val elementsToShorten = SmartList() @@ -76,7 +81,8 @@ class CreateTypeParameterFromUsageFix( else null val upperBound = upperBoundText?.let { psiFactory.createType(it) } val newTypeParameterText = if (upperBound != null) "${data.name} : ${upperBound.text}" else data.name - elementsToShorten += declaration.addTypeParameter(psiFactory.createTypeParameter(newTypeParameterText))!! + val newTypeParameter = declaration.addTypeParameter(psiFactory.createTypeParameter(newTypeParameterText))!! + elementsToShorten += newTypeParameter val anonymizedTypeParameter = createFakeTypeParameterDescriptor(data.fakeTypeParameter.containingDeclaration, "_") val anonymizedUpperBoundText = upperBoundType?.let { @@ -132,6 +138,8 @@ class CreateTypeParameterFromUsageFix( } ShortenReferences.DEFAULT.process(elementsToShorten) + + newTypeParameter } } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringBundle.properties b/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringBundle.properties index 9dcf34bef19..f0bfb8de2fc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringBundle.properties +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringBundle.properties @@ -25,7 +25,7 @@ cannot.refactor.synthesized.function=Cannot refactor synthesized function ''{0}' error.types.in.generated.function=Cannot generate function with erroneous return type cannot.introduce.parameter.of.0.type=Cannot introduce parameter of type ''{0}'' -0.has.detected.1.code.fragments.in.this.file.that.can.be.replaced.with.a.call.to.extracted.declaration={0} has detected {1} code {1,choice,1#fragment|2#fragments} in this file that can be replaced with a call to extracted declaration. Would you like to review and replace {1,choice,1#it|2#them}? +0.has.detected.1.code.fragments.in.this.file.that.can.be.replaced.with.a.call.to.extracted.declaration={0} has detected {1} code {1,choice,1#fragment|2#fragments} in this file that can be replaced with a usage of extracted declaration. Would you like to review and replace {1,choice,1#it|2#them}? error.wrong.caret.position.function.or.constructor.name=The caret should be positioned at the name of the function or constructor to be refactored. error.cant.refactor.vararg.functions=Can't refactor the function with variable arguments diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterHandler.kt index c915fda9915..1abec488929 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterHandler.kt @@ -176,8 +176,6 @@ fun IntroduceParameterDescriptor.performRefactoring() { } } -private fun isObjectOrNonInnerClass(e: PsiElement): Boolean = e is KtObjectDeclaration || (e is KtClass && !e.isInner()) - fun selectNewParameterContext( editor: Editor, file: KtFile, diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceTypeParameter/KotlinIntroduceTypeParameterHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceTypeParameter/KotlinIntroduceTypeParameterHandler.kt new file mode 100644 index 00000000000..c5108244de2 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceTypeParameter/KotlinIntroduceTypeParameterHandler.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeParameter + +import com.intellij.lang.refactoring.RefactoringSupportProvider +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.actionSystem.DataContext +import com.intellij.openapi.actionSystem.impl.SimpleDataContext +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.ex.EditorEx +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.rename.inplace.VariableInplaceRenameHandler +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils +import org.jetbrains.kotlin.idea.core.KotlinNameSuggester +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createTypeParameter.CreateTypeParameterByRefActionFactory +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createTypeParameter.CreateTypeParameterFromUsageFix +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createTypeParameter.getPossibleTypeParameterContainers +import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractIntroduceAction +import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.processDuplicates +import org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeAlias.KotlinIntroduceTypeAliasHandler +import org.jetbrains.kotlin.idea.refactoring.introduce.selectElementsWithTargetParent +import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint +import org.jetbrains.kotlin.idea.util.application.executeCommand +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.idea.util.getResolutionScope +import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiUnifier +import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.KtTypeElement +import org.jetbrains.kotlin.psi.KtTypeParameterListOwner +import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier +import org.jetbrains.kotlin.utils.keysToMap +import java.lang.AssertionError + +object KotlinIntroduceTypeParameterHandler : RefactoringActionHandler { + @JvmField + val REFACTORING_NAME = "Introduce Type Parameter" + + fun selectElements(editor: Editor, file: KtFile, continuation: (elements: List, targetParent: PsiElement) -> Unit) { + selectElementsWithTargetParent( + REFACTORING_NAME, + editor, + file, + "Introduce type parameter to declaration", + listOf(CodeInsightUtils.ElementKind.TYPE_ELEMENT), + { elements, parent -> getPossibleTypeParameterContainers(parent) }, + continuation + ) + } + + fun doInvoke(project: Project, editor: Editor, elements: List, targetParent: PsiElement) { + val targetOwner = targetParent as KtTypeParameterListOwner + val typeElementToExtract = elements.singleOrNull() as? KtTypeElement + ?: return showErrorHint(project, editor, "No type to refactor", REFACTORING_NAME) + + val scope = targetOwner.getResolutionScope() + val suggestedNames = KotlinNameSuggester.suggestNamesForTypeParameters(1) { + scope.findClassifier(Name.identifier(it), NoLookupLocation.FROM_IDE) == null + } + val defaultName = suggestedNames.single() + + val context = typeElementToExtract.analyze(BodyResolveMode.PARTIAL) + val originalType = typeElementToExtract.getAbbreviatedTypeOrType(context) + + val createTypeParameterData = + CreateTypeParameterByRefActionFactory.extractFixData(typeElementToExtract, defaultName) + ?.copy(upperBoundType = originalType, declaration = targetOwner) + ?: return showErrorHint(project, editor, "Refactoring is not applicable in the current context", REFACTORING_NAME) + + val parameterRefElement = KtPsiFactory(project).createType(defaultName).typeElement!! + + val duplicateRanges = typeElementToExtract + .toRange() + .match(targetParent, KotlinPsiUnifier.DEFAULT) + .filterNot { + val textRange = it.range.getTextRange() + typeElementToExtract.textRange.intersects(textRange) || targetOwner.typeParameterList?.textRange?.intersects(textRange) ?: false + } + .mapNotNull { it.range.elements.toRange() } + + project.executeCommand(REFACTORING_NAME) { + runWriteAction { + typeElementToExtract.replace(parameterRefElement) + + processDuplicates( + duplicateRanges.keysToMap { + { + it.elements.singleOrNull()?.replace(parameterRefElement) + Unit + } + }, + project, + editor + ) + } + + val newTypeParameter = CreateTypeParameterFromUsageFix(typeElementToExtract, createTypeParameterData).doInvoke() + ?: return@executeCommand + + if (!ApplicationManager.getApplication().isUnitTestMode) { + val dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.name, newTypeParameter, + (editor as? EditorEx)?.dataContext) + VariableInplaceRenameHandler().doRename(newTypeParameter, editor, dataContext) + } + } + } + + override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) { + if (file !is KtFile) return + selectElements(editor, file) { elements, targetParent -> doInvoke(project, editor, elements, targetParent) } + } + + override fun invoke(project: Project, elements: Array, dataContext: DataContext?) { + throw AssertionError("${KotlinIntroduceTypeAliasHandler.REFACTORING_NAME} can only be invoked from editor") + } +} + +class IntroduceTypeParameterAction : AbstractIntroduceAction() { + override fun getRefactoringHandler(provider: RefactoringSupportProvider) = KotlinIntroduceTypeParameterHandler +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/duplicates.kt b/idea/testData/refactoring/introduceTypeParameter/duplicates.kt new file mode 100644 index 00000000000..6edad23dcca --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/duplicates.kt @@ -0,0 +1,9 @@ +class A + +fun foo(x: List, f: (A) -> Int) { + val a: A? = x.firstOrNull() +} + +fun test() { + foo(listOf(A())) { 1 } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/duplicates.kt.after b/idea/testData/refactoring/introduceTypeParameter/duplicates.kt.after new file mode 100644 index 00000000000..0698541b4d2 --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/duplicates.kt.after @@ -0,0 +1,9 @@ +class A + +fun foo(x: List, f: (T) -> Int) { + val a: T? = x.firstOrNull() +} + +fun test() { + foo(listOf(A())) { 1 } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/functionType.kt b/idea/testData/refactoring/introduceTypeParameter/functionType.kt new file mode 100644 index 00000000000..cb64520e6f3 --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/functionType.kt @@ -0,0 +1,9 @@ +class A + +fun foo(x: List<(A?) -> List>) { + +} + +fun test() { + foo(listOf({ a -> listOf(1) })) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/functionType.kt.after b/idea/testData/refactoring/introduceTypeParameter/functionType.kt.after new file mode 100644 index 00000000000..7255668a629 --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/functionType.kt.after @@ -0,0 +1,9 @@ +class A + +fun List> foo(x: List) { + +} + +fun test() { + foo(listOf({ a -> listOf(1) })) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/inClass.kt b/idea/testData/refactoring/introduceTypeParameter/inClass.kt new file mode 100644 index 00000000000..5eae9044045 --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/inClass.kt @@ -0,0 +1,11 @@ +class A + +open class X(x: List, f: (A) -> Int) { + val a: A? = x.firstOrNull() +} + +class Y : X(listOf(A()), { 1 }) + +fun test() { + X(listOf(A())) { 1 } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/inClass.kt.after b/idea/testData/refactoring/introduceTypeParameter/inClass.kt.after new file mode 100644 index 00000000000..3493f2792fb --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/inClass.kt.after @@ -0,0 +1,11 @@ +class A + +open class X(x: List, f: (T) -> Int) { + val a: T? = x.firstOrNull() +} + +class Y : X(listOf(A()), { 1 }) + +fun test() { + X(listOf(A())) { 1 } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/inProperty.kt b/idea/testData/refactoring/introduceTypeParameter/inProperty.kt new file mode 100644 index 00000000000..da0df5826ec --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/inProperty.kt @@ -0,0 +1,11 @@ +class A + +val List<A>.foo: (A) -> Int + get() { + val a: A? = firstOrNull() + return { 0 } + } + +fun test() { + val t = listOf(A()).foo +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/inProperty.kt.after b/idea/testData/refactoring/introduceTypeParameter/inProperty.kt.after new file mode 100644 index 00000000000..1073f8f3b87 --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/inProperty.kt.after @@ -0,0 +1,11 @@ +class A + +val List.foo: (T) -> Int + get() { + val a: T? = firstOrNull() + return { 0 } + } + +fun test() { + val t = listOf(A()).foo +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/nullableType.kt b/idea/testData/refactoring/introduceTypeParameter/nullableType.kt new file mode 100644 index 00000000000..66b961def76 --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/nullableType.kt @@ -0,0 +1,9 @@ +class A + +fun foo(x: List<A?>) { + +} + +fun test() { + foo(listOf(A())) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/nullableType.kt.after b/idea/testData/refactoring/introduceTypeParameter/nullableType.kt.after new file mode 100644 index 00000000000..4d402f3b4a0 --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/nullableType.kt.after @@ -0,0 +1,9 @@ +class A + +fun foo(x: List) { + +} + +fun test() { + foo(listOf(A())) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/userType.kt b/idea/testData/refactoring/introduceTypeParameter/userType.kt new file mode 100644 index 00000000000..028e1260633 --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/userType.kt @@ -0,0 +1,9 @@ +class A + +fun foo(x: (List) -> Int) { + +} + +fun test() { + foo { 1 } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceTypeParameter/userType.kt.after b/idea/testData/refactoring/introduceTypeParameter/userType.kt.after new file mode 100644 index 00000000000..d20963ed9ea --- /dev/null +++ b/idea/testData/refactoring/introduceTypeParameter/userType.kt.after @@ -0,0 +1,9 @@ +class A + +fun > foo(x: (T) -> Int) { + +} + +fun test() { + foo> { 1 } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractExtractionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractExtractionTest.kt index f27b6105cd5..2f9c7ce7dac 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractExtractionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractExtractionTest.kt @@ -45,6 +45,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.* import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.INTRODUCE_PROPERTY import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler import org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeAlias.KotlinIntroduceTypeAliasHandler +import org.jetbrains.kotlin.idea.refactoring.introduce.introduceTypeParameter.KotlinIntroduceTypeParameterHandler import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase @@ -314,6 +315,18 @@ abstract class AbstractExtractionTest() : KotlinLightCodeInsightFixtureTestCase( } } + protected fun doIntroduceTypeParameterTest(path: String) { + doTest(path) { file -> + file as KtFile + + val explicitPreviousSibling = file.findElementByCommentPrefix("// SIBLING:") + val editor = fixture.editor + KotlinIntroduceTypeParameterHandler.selectElements(editor, file) { elements, previousSibling -> + KotlinIntroduceTypeParameterHandler.doInvoke(project, editor, elements, explicitPreviousSibling ?: previousSibling) + } + } + } + protected fun doIntroduceTypeAliasTest(path: String) { doTest(path) { file -> file as KtFile diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java index 79e883f33ef..0c6ec3007a0 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java @@ -3992,6 +3992,51 @@ public class ExtractionTestGenerated extends AbstractExtractionTest { } } + @TestMetadata("idea/testData/refactoring/introduceTypeParameter") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class IntroduceTypeParameter extends AbstractExtractionTest { + public void testAllFilesPresentInIntroduceTypeParameter() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/introduceTypeParameter"), Pattern.compile("^(.+)\\.(kt|kts)$"), true); + } + + @TestMetadata("duplicates.kt") + public void testDuplicates() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/duplicates.kt"); + doIntroduceTypeParameterTest(fileName); + } + + @TestMetadata("functionType.kt") + public void testFunctionType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/functionType.kt"); + doIntroduceTypeParameterTest(fileName); + } + + @TestMetadata("inClass.kt") + public void testInClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/inClass.kt"); + doIntroduceTypeParameterTest(fileName); + } + + @TestMetadata("inProperty.kt") + public void testInProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/inProperty.kt"); + doIntroduceTypeParameterTest(fileName); + } + + @TestMetadata("nullableType.kt") + public void testNullableType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/nullableType.kt"); + doIntroduceTypeParameterTest(fileName); + } + + @TestMetadata("userType.kt") + public void testUserType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceTypeParameter/userType.kt"); + doIntroduceTypeParameterTest(fileName); + } + } + @TestMetadata("idea/testData/refactoring/introduceTypeAlias") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)