Extraction Engine: Do not extract type parameter if it's resolved in the target scope

#KT-7246 Fixed
This commit is contained in:
Alexey Sedunov
2015-05-12 12:44:34 +03:00
parent 7045140d11
commit b69b66feab
12 changed files with 197 additions and 48 deletions
@@ -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
@@ -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<PsiElement>, 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<PsiElement>, 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<out PsiElement>, dataContext: DataContext?) {
@@ -0,0 +1,16 @@
// WITH_RUNTIME
// PARAM_TYPES: Foo<T>
// PARAM_TYPES: kotlin.String, Comparable<String>, CharSequence, kotlin.Any
// PARAM_DESCRIPTOR: internal final class Foo<T> defined in root package
// PARAM_DESCRIPTOR: value-parameter val l: kotlin.String defined in Foo.test
import java.util.*
// SIBLING:
class Foo<T> {
val map = HashMap<String, T>()
fun test(l: String): T {
return <selection>map[l]</selection>
}
}
@@ -0,0 +1,18 @@
// WITH_RUNTIME
// PARAM_TYPES: Foo<T>
// PARAM_TYPES: kotlin.String, Comparable<String>, CharSequence, kotlin.Any
// PARAM_DESCRIPTOR: internal final class Foo<T> defined in root package
// PARAM_DESCRIPTOR: value-parameter val l: kotlin.String defined in Foo.test
import java.util.*
// SIBLING:
class Foo<T> {
val map = HashMap<String, T>()
fun test(l: String): T {
return t(l)
}
}
private fun <T> Foo<T>.t(l: String) = map[l]
@@ -0,0 +1,13 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.String, Comparable<String>, CharSequence, kotlin.Any
// PARAM_DESCRIPTOR: value-parameter val l: kotlin.String defined in Foo.test
import java.util.*
class Foo<T> {
val map = HashMap<String, T>()
fun test(l: String): T {
return <selection>map[l]</selection>
}
}
@@ -0,0 +1,15 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.String, Comparable<String>, CharSequence, kotlin.Any
// PARAM_DESCRIPTOR: value-parameter val l: kotlin.String defined in Foo.test
import java.util.*
class Foo<T> {
val map = HashMap<String, T>()
fun test(l: String): T {
return t(l)
}
private fun t(l: String) = map[l]
}
@@ -0,0 +1,13 @@
// EXTRACTION_TARGET: property with getter
// WITH_RUNTIME
import java.util.*
// SIBLING:
class Foo<T> {
val map = HashMap<String, T>()
fun test(): T {
return <selection>map[""]</selection>
}
}
@@ -0,0 +1,16 @@
// EXTRACTION_TARGET: property with getter
// WITH_RUNTIME
import java.util.*
private val <T> Foo<T>.t: T?
get() = map[""]
// SIBLING:
class Foo<T> {
val map = HashMap<String, T>()
fun test(): T {
return t
}
}
@@ -0,0 +1,12 @@
// EXTRACTION_TARGET: property with initializer
// WITH_RUNTIME
import java.util.*
class Foo<T> {
val map = HashMap<String, T>()
fun test(): T {
return <selection>map[""]</selection>
}
}
@@ -0,0 +1,14 @@
// EXTRACTION_TARGET: property with initializer
// WITH_RUNTIME
import java.util.*
class Foo<T> {
val map = HashMap<String, T>()
private val t = map[""]
fun test(): T {
return t
}
}
@@ -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)
}
}
}
@@ -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")