Extract Function: Replace non-denotable parameter types with their super types when possible

#KT-6187 Fixed
This commit is contained in:
Alexey Sedunov
2014-11-12 19:10:11 +03:00
parent 94efdeb3d5
commit 558e038ba5
9 changed files with 141 additions and 29 deletions
@@ -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()) {
@@ -395,6 +395,17 @@ fun TypeParameter.collectReferencedTypes(bindingContext: BindingContext): List<J
.filterNotNull()
}
private fun JetType.isExtractable(): Boolean {
return collectReferencedTypes(true).fold(true) { (extractable, typeToCheck) ->
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<TypeParameter>,
nonDenotableTypes: MutableSet<JetType>,
@@ -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<JetType> 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<JetExpression>()),
originalElements.first,
JetNameValidatorImpl.Target.PROPERTIES
)
val extractedDescriptorToParameter = HashMap<DeclarationDescriptor, MutableParameter>()
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<JetExpression>()),
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<JetType>()) { it.collectReferencedTypes(bindingContext) }) {
typeToCheck.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes)
}
info.parameters.addAll(extractedDescriptorToParameter.values())
return info
}