diff --git a/idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt b/idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt index b9a31e38c71..5c4ee7f1c67 100644 --- a/idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt +++ b/idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt @@ -43,7 +43,7 @@ public fun DeclarationDescriptor.canBeReferencedViaImport(): Boolean { if (this is PackageViewDescriptor || DescriptorUtils.isTopLevelDeclaration(this) || (this is CallableDescriptor && DescriptorUtils.isStaticDeclaration(this))) { - return true + return !getName().isSpecial() } val parent = getContainingDeclaration()!! if (parent !is ClassDescriptor || !parent.canBeReferencedViaImport()) { diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractableAnalysisUtil.kt index 1c26849ad2f..3b82b365d0d 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractableAnalysisUtil.kt @@ -395,6 +395,17 @@ fun TypeParameter.collectReferencedTypes(bindingContext: BindingContext): List + val parameterTypeDescriptor = typeToCheck.getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor + val typeParameter = parameterTypeDescriptor?.let { + DescriptorToSourceUtils.descriptorToDeclaration(it) + } as? JetTypeParameter + + extractable && (typeParameter != null || typeToCheck.canBeReferencedViaImport()) + } +} + private fun JetType.processTypeIfExtractable( typeParameters: MutableSet, nonDenotableTypes: MutableSet, @@ -429,8 +440,6 @@ private fun JetType.processTypeIfExtractable( private class MutableParameter( override val argumentText: String, override val originalDescriptor: DeclarationDescriptor, - override val name: String, - override val mirrorVarName: String?, override val receiverCandidate: Boolean ): Parameter { // All modifications happen in the same thread @@ -441,24 +450,32 @@ private class MutableParameter( var refCount: Int = 0 fun addDefaultType(jetType: JetType) { - assert(writable, "Can't add type to non-writable parameter $name") + assert(writable, "Can't add type to non-writable parameter $currentName") defaultTypes.add(jetType) } fun addTypePredicate(predicate: TypePredicate) { - assert(writable, "Can't add type predicate to non-writable parameter $name") + assert(writable, "Can't add type predicate to non-writable parameter $currentName") typePredicates.add(predicate) } + var currentName: String? = null + override val name: String get() = currentName!! + + override var mirrorVarName: String? = null + + private val defaultType: JetType by Delegates.lazy { + writable = false + CommonSupertypes.commonSupertype(defaultTypes) + } + override val parameterTypeCandidates: List by Delegates.lazy { writable = false - listOf(parameterType) + TypeUtils.getAllSupertypes(parameterType).filter(and(typePredicates)) + val superTypes = TypeUtils.getAllSupertypes(defaultType).filter(and(typePredicates)) + (Collections.singletonList(defaultType) + superTypes).filter { it.isExtractable() } } - override val parameterType: JetType by Delegates.lazy { - writable = false - CommonSupertypes.commonSupertype(defaultTypes) - } + override val parameterType: JetType by Delegates.lazy { parameterTypeCandidates.firstOrNull() ?: defaultType } override fun copy(name: String, parameterType: JetType): Parameter = DelegatingParameter(this, name, parameterType) } @@ -488,12 +505,6 @@ private fun ExtractionData.inferParametersInfo( ): ParametersInfo { val info = ParametersInfo() - val varNameValidator = JetNameValidatorImpl( - commonParent.getParentByType(javaClass()), - originalElements.first, - JetNameValidatorImpl.Target.PROPERTIES - ) - val extractedDescriptorToParameter = HashMap() for (refInfo in getBrokenReferencesInfo(createTemporaryCodeBlock())) { @@ -557,28 +568,21 @@ private fun ExtractionData.inferParametersInfo( ?: DEFAULT_PARAMETER_TYPE } - if (!parameterType.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes)) continue - val parameterTypePredicate = and(pseudocode.getElementValuesRecursively(originalRef).map { getExpectedTypePredicate(it, bindingContext) }) val parameter = extractedDescriptorToParameter.getOrPut(descriptorToExtract) { - val parameterName = - if (extractThis) { - JetNameSuggester.suggestNames(parameterType, varNameValidator, null).first() - } - else originalDeclaration.getName()!! - - val mirrorVarName = - if (descriptorToExtract in modifiedVarDescriptors) varNameValidator.validateName(parameterName)!! else null - val argumentText = if (hasThisReceiver && extractThis) "this@${parameterType.getConstructor().getDeclarationDescriptor()!!.getName().asString()}" else (thisExpr ?: ref).getText() ?: throw AssertionError("'this' reference shouldn't be empty: code fragment = ${getCodeFragmentText()}") - MutableParameter(argumentText, descriptorToExtract, parameterName, mirrorVarName, extractThis) + MutableParameter(argumentText, descriptorToExtract, extractThis) + } + + if (!extractThis) { + parameter.currentName = originalDeclaration.getName() } parameter.refCount++ @@ -593,11 +597,28 @@ private fun ExtractionData.inferParametersInfo( } } + val varNameValidator = JetNameValidatorImpl( + commonParent.getParentByType(javaClass()), + originalElements.first, + JetNameValidatorImpl.Target.PROPERTIES + ) + + for ((descriptorToExtract, parameter) in extractedDescriptorToParameter) { + if (!parameter.parameterType.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes)) continue + + with (parameter) { + if (currentName == null) { + currentName = JetNameSuggester.suggestNames(parameterType, varNameValidator, null).first() + } + mirrorVarName = if (descriptorToExtract in modifiedVarDescriptors) varNameValidator.validateName(name) else null + info.parameters.add(this) + } + } + for (typeToCheck in info.typeParameters.flatMapTo(HashSet()) { it.collectReferencedTypes(bindingContext) }) { typeToCheck.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes) } - info.parameters.addAll(extractedDescriptorToParameter.values()) return info } diff --git a/idea/testData/refactoring/extractFunction/parameters/candidateTypes/cantLiftAnonymousToSupertype.kt b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/cantLiftAnonymousToSupertype.kt new file mode 100644 index 00000000000..69863a4a1c5 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/cantLiftAnonymousToSupertype.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME + +// SIBLING: +val x = object { + val t = 1 + + fun test() { + println(this.t) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/parameters/candidateTypes/cantLiftAnonymousToSupertype.kt.conflicts b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/cantLiftAnonymousToSupertype.kt.conflicts new file mode 100644 index 00000000000..440cc5151f4 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/cantLiftAnonymousToSupertype.kt.conflicts @@ -0,0 +1 @@ +Cannot extract method since following types are not denotable in the target scope: <no name provided> \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype1.kt b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype1.kt new file mode 100644 index 00000000000..6ea284afb15 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype1.kt @@ -0,0 +1,10 @@ +// PARAM_DESCRIPTOR: internal final class defined in root package +// PARAM_TYPES: kotlin.Any +// WITH_RUNTIME + +// SIBLING: +val x = object { + fun test() { + println(this) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype1.kt.after b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype1.kt.after new file mode 100644 index 00000000000..8f05fd89c8b --- /dev/null +++ b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype1.kt.after @@ -0,0 +1,14 @@ +// PARAM_DESCRIPTOR: internal final class defined in root package +// PARAM_TYPES: kotlin.Any +// WITH_RUNTIME + +// SIBLING: +val x = object { + fun test() { + unit() + } +} + +private fun Any.unit() { + println(this) +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype2.kt b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype2.kt new file mode 100644 index 00000000000..e273e266daa --- /dev/null +++ b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype2.kt @@ -0,0 +1,17 @@ +// PARAM_DESCRIPTOR: val x: defined in test +// PARAM_TYPES: A +// WITH_RUNTIME + +open class A { + +} + +fun foo(a: A) { + +} + +// SIBLING: +fun test() { + val x = object: A() { } + foo(x) +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype2.kt.after b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype2.kt.after new file mode 100644 index 00000000000..dbb6c7133f5 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype2.kt.after @@ -0,0 +1,21 @@ +// PARAM_DESCRIPTOR: val x: defined in test +// PARAM_TYPES: A +// WITH_RUNTIME + +open class A { + +} + +fun foo(a: A) { + +} + +// SIBLING: +fun test() { + val x = object: A() { } + unit(x) +} + +private fun unit(x: A) { + foo(x) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java index a11cd35cb35..d5004772e6e 100644 --- a/idea/tests/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java @@ -1361,6 +1361,24 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/extractFunction/parameters/candidateTypes"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("cantLiftAnonymousToSupertype.kt") + public void testCantLiftAnonymousToSupertype() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/candidateTypes/cantLiftAnonymousToSupertype.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("liftAnonymousToSupertype1.kt") + public void testLiftAnonymousToSupertype1() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype1.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("liftAnonymousToSupertype2.kt") + public void testLiftAnonymousToSupertype2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/candidateTypes/liftAnonymousToSupertype2.kt"); + doExtractFunctionTest(fileName); + } + @TestMetadata("nonNullableTypes.kt") public void testNonNullableTypes() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/candidateTypes/nonNullableTypes.kt");