From b69b66feab3167c0cb0fd0e1efe7b3a1f274eec8 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Tue, 12 May 2015 12:44:34 +0300 Subject: [PATCH] Extraction Engine: Do not extract type parameter if it's resolved in the target scope #KT-7246 Fixed --- .../extractableAnalysisUtil.kt | 6 +- .../KotlinIntroducePropertyHandler.kt | 86 ++++++++++--------- ...typeParameterNotResolvableInTargetScope.kt | 16 ++++ ...rameterNotResolvableInTargetScope.kt.after | 18 ++++ .../typeParameterResolvableInTargetScope.kt | 13 +++ ...eParameterResolvableInTargetScope.kt.after | 15 ++++ ...typeParameterNotResolvableInTargetScope.kt | 13 +++ ...rameterNotResolvableInTargetScope.kt.after | 16 ++++ .../typeParameterResolvableInTargetScope.kt | 12 +++ ...eParameterResolvableInTargetScope.kt.after | 14 +++ .../introduce/AbstractJetExtractionTest.kt | 12 +-- .../introduce/JetExtractionTestGenerated.java | 24 ++++++ 12 files changed, 197 insertions(+), 48 deletions(-) create mode 100644 idea/testData/refactoring/extractFunction/typeParameters/typeParameterNotResolvableInTargetScope.kt create mode 100644 idea/testData/refactoring/extractFunction/typeParameters/typeParameterNotResolvableInTargetScope.kt.after create mode 100644 idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt create mode 100644 idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt.after create mode 100644 idea/testData/refactoring/introduceProperty/typeParameterNotResolvableInTargetScope.kt create mode 100644 idea/testData/refactoring/introduceProperty/typeParameterNotResolvableInTargetScope.kt.after create mode 100644 idea/testData/refactoring/introduceProperty/typeParameterResolvableInTargetScope.kt create mode 100644 idea/testData/refactoring/introduceProperty/typeParameterResolvableInTargetScope.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt index 73bf75de8cd..34d4bc907d7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt @@ -460,14 +460,14 @@ private fun JetType.processTypeIfExtractable( } as? JetTypeParameter when { + typeToCheck.isResolvableInScope(targetScope, true) -> + extractable + typeParameter != null -> { typeParameters.add(TypeParameter(typeParameter, typeParameter.collectRelevantConstraints())) extractable } - typeToCheck.isResolvableInScope(targetScope, false) -> - extractable - options.allowSpecialClassNames && typeToCheck.isSpecial() -> extractable diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceProperty/KotlinIntroducePropertyHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceProperty/KotlinIntroducePropertyHandler.kt index f2a2a16c05b..a5464df66e6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceProperty/KotlinIntroducePropertyHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceProperty/KotlinIntroducePropertyHandler.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.util.psi.patternMatching.* import kotlin.test.* import com.intellij.openapi.application.* import org.jetbrains.kotlin.idea.core.refactoring.getExtractionContainers +import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.EXTRACT_FUNCTION import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.* import java.util.* @@ -52,53 +53,60 @@ public class KotlinIntroducePropertyHandler( } } - override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) { - if (file !is JetFile) return + public fun selectElements(editor: Editor, file: PsiFile, continuation: (elements: List, targetSibling: PsiElement) -> Unit) { selectElementsWithTargetSibling( - operationName = INTRODUCE_PROPERTY, - editor = editor, - file = file, - getContainers = { elements, parent -> + INTRODUCE_PROPERTY, + editor, + file, + { elements, parent -> parent.getExtractionContainers(strict = true, includeAll = true).filter { it is JetClassBody || it is JetFile } - } - ) { elements, targetSibling -> - val adjustedElements = (elements.singleOrNull() as? JetBlockExpression)?.getStatements() ?: elements - if (adjustedElements.isNotEmpty()) { - val options = ExtractionOptions(extractAsProperty = true) - val extractionData = ExtractionData(file, adjustedElements.toRange(), targetSibling, null, options) - ExtractionEngine(helper).run(editor, extractionData) { - val property = it.declaration as JetProperty - val descriptor = it.config.descriptor + }, + continuation + ) + } - editor.getCaretModel().moveToOffset(property.getTextOffset()) - editor.getSelectionModel().removeSelection() - if (editor.getSettings().isVariableInplaceRenameEnabled() && !ApplicationManager.getApplication().isUnitTestMode()) { - with(PsiDocumentManager.getInstance(project)) { - commitDocument(editor.getDocument()) - doPostponedOperationsAndUnblockDocument(editor.getDocument()) - } + public fun doInvoke(project: Project, editor: Editor, file: JetFile, elements: List, targetSibling: PsiElement) { + val adjustedElements = (elements.singleOrNull() as? JetBlockExpression)?.getStatements() ?: elements + if (adjustedElements.isNotEmpty()) { + val options = ExtractionOptions(extractAsProperty = true) + val extractionData = ExtractionData(file, adjustedElements.toRange(), targetSibling, null, options) + ExtractionEngine(helper).run(editor, extractionData) { + val property = it.declaration as JetProperty + val descriptor = it.config.descriptor - val introducer = KotlinInplacePropertyIntroducer( - property = property, - editor = editor, - project = project, - title = INTRODUCE_PROPERTY, - doNotChangeVar = false, - exprType = descriptor.controlFlow.outputValueBoxer.returnType, - extractionResult = it, - availableTargets = propertyTargets.filter { it.isAvailable(descriptor) } - ) - introducer.performInplaceRefactoring(LinkedHashSet(descriptor.suggestedNames)) - } - else { - processDuplicatesSilently(it.duplicateReplacers, project) + editor.getCaretModel().moveToOffset(property.getTextOffset()) + editor.getSelectionModel().removeSelection() + if (editor.getSettings().isVariableInplaceRenameEnabled() && !ApplicationManager.getApplication().isUnitTestMode()) { + with(PsiDocumentManager.getInstance(project)) { + commitDocument(editor.getDocument()) + doPostponedOperationsAndUnblockDocument(editor.getDocument()) } + + val introducer = KotlinInplacePropertyIntroducer( + property = property, + editor = editor, + project = project, + title = INTRODUCE_PROPERTY, + doNotChangeVar = false, + exprType = descriptor.controlFlow.outputValueBoxer.returnType, + extractionResult = it, + availableTargets = propertyTargets.filter { it.isAvailable(descriptor) } + ) + introducer.performInplaceRefactoring(LinkedHashSet(descriptor.suggestedNames)) + } + else { + processDuplicatesSilently(it.duplicateReplacers, project) } - } - else { - showErrorHintByKey(project, editor, "cannot.refactor.no.expression", INTRODUCE_PROPERTY) } } + else { + showErrorHintByKey(project, editor, "cannot.refactor.no.expression", INTRODUCE_PROPERTY) + } + } + + override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) { + if (file !is JetFile) return + selectElements(editor, file) { elements, targetSibling -> doInvoke(project, editor, file, elements, targetSibling) } } override fun invoke(project: Project, elements: Array, dataContext: DataContext?) { diff --git a/idea/testData/refactoring/extractFunction/typeParameters/typeParameterNotResolvableInTargetScope.kt b/idea/testData/refactoring/extractFunction/typeParameters/typeParameterNotResolvableInTargetScope.kt new file mode 100644 index 00000000000..a0282a38ac1 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/typeParameters/typeParameterNotResolvableInTargetScope.kt @@ -0,0 +1,16 @@ +// WITH_RUNTIME +// PARAM_TYPES: Foo +// PARAM_TYPES: kotlin.String, Comparable, CharSequence, kotlin.Any +// PARAM_DESCRIPTOR: internal final class Foo defined in root package +// PARAM_DESCRIPTOR: value-parameter val l: kotlin.String defined in Foo.test + +import java.util.* + +// SIBLING: +class Foo { + val map = HashMap() + + fun test(l: String): T { + return map[l] + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/typeParameters/typeParameterNotResolvableInTargetScope.kt.after b/idea/testData/refactoring/extractFunction/typeParameters/typeParameterNotResolvableInTargetScope.kt.after new file mode 100644 index 00000000000..d4af32e7b7a --- /dev/null +++ b/idea/testData/refactoring/extractFunction/typeParameters/typeParameterNotResolvableInTargetScope.kt.after @@ -0,0 +1,18 @@ +// WITH_RUNTIME +// PARAM_TYPES: Foo +// PARAM_TYPES: kotlin.String, Comparable, CharSequence, kotlin.Any +// PARAM_DESCRIPTOR: internal final class Foo defined in root package +// PARAM_DESCRIPTOR: value-parameter val l: kotlin.String defined in Foo.test + +import java.util.* + +// SIBLING: +class Foo { + val map = HashMap() + + fun test(l: String): T { + return t(l) + } +} + +private fun Foo.t(l: String) = map[l] \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt b/idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt new file mode 100644 index 00000000000..218bac818e7 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME +// PARAM_TYPES: kotlin.String, Comparable, CharSequence, kotlin.Any +// PARAM_DESCRIPTOR: value-parameter val l: kotlin.String defined in Foo.test + +import java.util.* + +class Foo { + val map = HashMap() + + fun test(l: String): T { + return map[l] + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt.after b/idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt.after new file mode 100644 index 00000000000..56b4d7bb3f2 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME +// PARAM_TYPES: kotlin.String, Comparable, CharSequence, kotlin.Any +// PARAM_DESCRIPTOR: value-parameter val l: kotlin.String defined in Foo.test + +import java.util.* + +class Foo { + val map = HashMap() + + fun test(l: String): T { + return t(l) + } + + private fun t(l: String) = map[l] +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/typeParameterNotResolvableInTargetScope.kt b/idea/testData/refactoring/introduceProperty/typeParameterNotResolvableInTargetScope.kt new file mode 100644 index 00000000000..cbabe07ac5a --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/typeParameterNotResolvableInTargetScope.kt @@ -0,0 +1,13 @@ +// EXTRACTION_TARGET: property with getter +// WITH_RUNTIME + +import java.util.* + +// SIBLING: +class Foo { + val map = HashMap() + + fun test(): T { + return map[""] + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/typeParameterNotResolvableInTargetScope.kt.after b/idea/testData/refactoring/introduceProperty/typeParameterNotResolvableInTargetScope.kt.after new file mode 100644 index 00000000000..b9961b8e82a --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/typeParameterNotResolvableInTargetScope.kt.after @@ -0,0 +1,16 @@ +// EXTRACTION_TARGET: property with getter +// WITH_RUNTIME + +import java.util.* + +private val Foo.t: T? + get() = map[""] + +// SIBLING: +class Foo { + val map = HashMap() + + fun test(): T { + return t + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/typeParameterResolvableInTargetScope.kt b/idea/testData/refactoring/introduceProperty/typeParameterResolvableInTargetScope.kt new file mode 100644 index 00000000000..1b150c2e3f5 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/typeParameterResolvableInTargetScope.kt @@ -0,0 +1,12 @@ +// EXTRACTION_TARGET: property with initializer +// WITH_RUNTIME + +import java.util.* + +class Foo { + val map = HashMap() + + fun test(): T { + return map[""] + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/typeParameterResolvableInTargetScope.kt.after b/idea/testData/refactoring/introduceProperty/typeParameterResolvableInTargetScope.kt.after new file mode 100644 index 00000000000..bb93184ba45 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/typeParameterResolvableInTargetScope.kt.after @@ -0,0 +1,14 @@ +// EXTRACTION_TARGET: property with initializer +// WITH_RUNTIME + +import java.util.* + +class Foo { + val map = HashMap() + + private val t = map[""] + + fun test(): T { + return t + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractJetExtractionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractJetExtractionTest.kt index 90c5618e759..dad34d84618 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractJetExtractionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractJetExtractionTest.kt @@ -207,6 +207,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe val extractionTarget = propertyTargets.single { it.name == InTextDirectivesUtils.findStringWithPrefixes(file.getText(), "// EXTRACTION_TARGET: ") } + val explicitPreviousSibling = file.findElementByComment("// SIBLING:") val helper = object : ExtractionEngineHelper(INTRODUCE_PROPERTY) { override fun configureAndRun( project: Project, @@ -223,12 +224,11 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe ) } } - KotlinIntroducePropertyHandler(helper).invoke( - fixture.getProject(), - fixture.getEditor(), - file, - DataManager.getInstance().getDataContext(fixture.getEditor().getComponent()) - ) + val handler = KotlinIntroducePropertyHandler(helper) + val editor = fixture.getEditor() + handler.selectElements(editor, file) { elements, previousSibling -> + handler.doInvoke(getProject(), editor, file as JetFile, elements, explicitPreviousSibling ?: previousSibling) + } } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java index b4cb076f9cb..d34ab8f475a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java @@ -1989,6 +1989,18 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { doExtractFunctionTest(fileName); } + @TestMetadata("typeParameterNotResolvableInTargetScope.kt") + public void testTypeParameterNotResolvableInTargetScope() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/typeParameters/typeParameterNotResolvableInTargetScope.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("typeParameterResolvableInTargetScope.kt") + public void testTypeParameterResolvableInTargetScope() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/typeParameters/typeParameterResolvableInTargetScope.kt"); + doExtractFunctionTest(fileName); + } + @TestMetadata("typeParametersAndConstraintsCombined1.kt") public void testTypeParametersAndConstraintsCombined1() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/typeParameters/typeParametersAndConstraintsCombined1.kt"); @@ -2196,6 +2208,18 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/replaceDuplicates.kt"); doIntroducePropertyTest(fileName); } + + @TestMetadata("typeParameterNotResolvableInTargetScope.kt") + public void testTypeParameterNotResolvableInTargetScope() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/typeParameterNotResolvableInTargetScope.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("typeParameterResolvableInTargetScope.kt") + public void testTypeParameterResolvableInTargetScope() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/typeParameterResolvableInTargetScope.kt"); + doIntroducePropertyTest(fileName); + } } @TestMetadata("idea/testData/refactoring/introduceParameter")