Change Signature: Represent return type using KotlinTypeInfo

This commit is contained in:
Alexey Sedunov
2015-12-15 18:09:21 +03:00
parent 24951e8a38
commit e558581667
8 changed files with 97 additions and 60 deletions
@@ -29,7 +29,6 @@ import com.intellij.util.VisibilityUtil
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
@@ -48,7 +47,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmOverloadsAnnotation
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
import org.jetbrains.kotlin.utils.keysToMap
import org.jetbrains.kotlin.utils.removeLast
@@ -57,8 +56,7 @@ import java.util.*
public open class KotlinChangeInfo(
val methodDescriptor: KotlinMethodDescriptor,
private var name: String = methodDescriptor.getName(),
val newReturnType: KotlinType? = methodDescriptor.baseDescriptor.getReturnType(),
var newReturnTypeText: String = methodDescriptor.renderOriginalReturnType(),
var newReturnTypeInfo: KotlinTypeInfo = KotlinTypeInfo(methodDescriptor.baseDescriptor.returnType),
var newVisibility: Visibility = methodDescriptor.getVisibility(),
parameterInfos: List<KotlinParameterInfo> = methodDescriptor.getParameters(),
receiver: KotlinParameterInfo? = methodDescriptor.receiver,
@@ -75,6 +73,8 @@ public open class KotlinChangeInfo(
}
}
private val originalReturnTypeInfo = methodDescriptor.returnTypeInfo
var receiverParameterInfo: KotlinParameterInfo? = receiver
set(value: KotlinParameterInfo?) {
if (value != null && value !in newParameters) {
@@ -184,7 +184,8 @@ public open class KotlinChangeInfo(
return methodDescriptor.getMethod()
}
override fun isReturnTypeChanged(): Boolean = newReturnTypeText != methodDescriptor.renderOriginalReturnType()
override fun isReturnTypeChanged(): Boolean = !newReturnTypeInfo.isEquivalentTo(originalReturnTypeInfo)
fun isReceiverTypeChanged(): Boolean = receiverParameterInfo?.getTypeText() != methodDescriptor.renderOriginalReceiverType()
@@ -226,6 +227,13 @@ public open class KotlinChangeInfo(
this.primaryPropagationTargets = primaryPropagationTargets
}
private fun renderReturnTypeIfNeeded(): String? {
val typeInfo = newReturnTypeInfo
if (kind != Kind.FUNCTION) return null
if (typeInfo.type?.isUnit() ?: false) return null
return typeInfo.render()
}
public fun getNewSignature(inheritedCallable: KotlinCallableDefinitionUsage<PsiElement>): String {
val buffer = StringBuilder()
@@ -262,8 +270,7 @@ public open class KotlinChangeInfo(
buffer.append(getNewParametersSignature(inheritedCallable))
if (newReturnType != null && !KotlinBuiltIns.isUnit(newReturnType) && kind == Kind.FUNCTION)
buffer.append(": ").append(newReturnTypeText)
renderReturnTypeIfNeeded()?.let { buffer.append(": ").append(it) }
return buffer.toString()
}
@@ -300,9 +307,10 @@ public open class KotlinChangeInfo(
}
public fun renderReturnType(inheritedCallable: KotlinCallableDefinitionUsage<*>): String {
val typeSubstitutor = inheritedCallable.typeSubstitutor ?: return newReturnTypeText
val currentBaseFunction = inheritedCallable.baseFunction.currentCallableDescriptor ?: return newReturnTypeText
return currentBaseFunction.getReturnType()!!.renderTypeWithSubstitution(typeSubstitutor, newReturnTypeText, false)
val defaultRendering = newReturnTypeInfo.render()
val typeSubstitutor = inheritedCallable.typeSubstitutor ?: return defaultRendering
val currentBaseFunction = inheritedCallable.baseFunction.currentCallableDescriptor ?: return defaultRendering
return currentBaseFunction.returnType!!.renderTypeWithSubstitution(typeSubstitutor, defaultRendering, false)
}
public fun primaryMethodUpdated() {
@@ -545,13 +553,9 @@ public fun ChangeInfo.toJetChangeInfo(originalChangeSignatureDescriptor: KotlinM
}
}
val returnType = functionDescriptor.getReturnType()
val returnTypeText = if (returnType != null) IdeDescriptorRenderers.SOURCE_CODE.renderType(returnType) else ""
return KotlinChangeInfo(originalChangeSignatureDescriptor,
getNewName(),
returnType,
returnTypeText,
KotlinTypeInfo(functionDescriptor.returnType),
functionDescriptor.getVisibility(),
newParameters,
null,
@@ -91,8 +91,7 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor {
methodDescriptor: KotlinMethodDescriptor
) : KotlinChangeInfo(methodDescriptor = methodDescriptor,
name = "",
newReturnType = null,
newReturnTypeText = "",
newReturnTypeInfo = KotlinTypeInfo(),
newVisibility = Visibilities.DEFAULT_VISIBILITY,
parameterInfos = emptyList<KotlinParameterInfo>(),
receiver = null,
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinCallableDefinitionUsage
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.KtCallableDeclaration
public interface KotlinMethodDescriptor : MethodDescriptor<KotlinParameterInfo, Visibility> {
enum class Kind(val isConstructor: Boolean) {
@@ -53,8 +54,14 @@ public interface KotlinMethodDescriptor : MethodDescriptor<KotlinParameterInfo,
val receiver: KotlinParameterInfo?
}
fun KotlinMethodDescriptor.renderOriginalReturnType(): String =
baseDescriptor.getReturnType()?.let { IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(it) } ?: ""
val KotlinMethodDescriptor.returnTypeInfo: KotlinTypeInfo
get() {
val type = baseDescriptor.returnType
val text = (baseDeclaration as? KtCallableDeclaration)?.typeReference?.text
?: type?.let { IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type) }
?: "Unit"
return KotlinTypeInfo(type, text)
}
fun KotlinMethodDescriptor.renderOriginalReceiverType(): String? =
baseDescriptor.getExtensionReceiverParameter()?.getType()?.let { IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(it) }
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2015 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.changeSignature
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
data class KotlinTypeInfo(val type: KotlinType? = null, val text: String? = null)
fun KotlinTypeInfo.render(): String {
return when {
text != null -> text
type != null -> IdeDescriptorRenderers.SOURCE_CODE.renderType(type)
else -> ""
}
}
fun KotlinTypeInfo.isEquivalentTo(other: KotlinTypeInfo): Boolean {
return if (type != null && other.type != null) TypeUtils.equalTypes(type, other.type) else render() == other.render()
}
@@ -16,15 +16,11 @@
package org.jetbrains.kotlin.idea.refactoring.changeSignature
import com.intellij.openapi.options.ConfigurationException
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.ui.ComboBoxVisibilityPanel
import com.intellij.refactoring.ui.RefactoringDialog
import com.intellij.ui.EditorTextField
import com.intellij.ui.NonFocusableCheckBox
import com.intellij.util.ui.FormBuilder
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
@@ -33,8 +29,6 @@ import org.jetbrains.kotlin.idea.core.refactoring.validateElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtTypeCodeFragment
import org.jetbrains.kotlin.resolve.AnalyzingUtils
import javax.swing.JCheckBox
import javax.swing.JComboBox
import javax.swing.JComponent
@@ -85,7 +79,7 @@ public class KotlinChangePropertySignatureDialog(
addLabeledComponent("&Name: ", nameField)
val returnTypeCodeFragment = psiFactory.createTypeCodeFragment(methodDescriptor.renderOriginalReturnType(),
val returnTypeCodeFragment = psiFactory.createTypeCodeFragment(methodDescriptor.returnTypeInfo.render(),
baseDeclaration)
returnTypeField = EditorTextField(documentManager.getDocument(returnTypeCodeFragment), myProject, KotlinFileType.INSTANCE)
addLabeledComponent("&Type: ", returnTypeField)
@@ -151,8 +145,7 @@ public class KotlinChangePropertySignatureDialog(
receiver?.currentTypeText = receiverTypeField.getText()
val changeInfo = KotlinChangeInfo(originalDescriptor,
nameField.getText(),
null,
returnTypeField.getText(),
KotlinTypeInfo(null, returnTypeField.text),
visibilityCombo.getSelectedItem() as Visibility,
emptyList(),
receiver,
@@ -49,13 +49,11 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor.Kind
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.KtExpressionCodeFragment
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtTypeCodeFragment
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import java.awt.BorderLayout
import java.awt.Font
import java.awt.Toolkit
@@ -317,7 +315,8 @@ public class KotlinChangeSignatureDialog(
getMethodDescriptor(),
getVisibility(),
getMethodName(),
myDefaultValueContext)
myDefaultValueContext,
true)
return changeInfo.getNewSignature(getMethodDescriptor().originalPrimaryCallable)
}
@@ -365,7 +364,8 @@ public class KotlinChangeSignatureDialog(
getMethodDescriptor(),
getVisibility(),
getMethodName(),
myDefaultValueContext)
myDefaultValueContext,
false)
changeInfo.primaryPropagationTargets = myMethodsToPropagateParameters ?: emptyList();
return KotlinChangeSignatureProcessor(myProject, changeInfo, commandName ?: getTitle())
}
@@ -387,7 +387,7 @@ public class KotlinChangeSignatureDialog(
}
private fun createReturnTypeCodeFragment(project: Project, method: KotlinMethodDescriptor) =
KtPsiFactory(project).createTypeCodeFragment(method.renderOriginalReturnType(), method.baseDeclaration)
KtPsiFactory(project).createTypeCodeFragment(method.returnTypeInfo.render(), method.baseDeclaration)
public fun createRefactoringProcessorForSilentChangeSignature(project: Project,
commandName: String,
@@ -400,21 +400,21 @@ public class KotlinChangeSignatureDialog(
method,
method.getVisibility(),
method.getName(),
defaultValueContext)
defaultValueContext,
false)
return KotlinChangeSignatureProcessor(project, changeInfo, commandName)
}
private fun PsiCodeFragment?.getTypeWithText(): Pair<KotlinType?, String> {
if (this !is KtTypeCodeFragment) return null to ""
private fun PsiCodeFragment?.getTypeInfo(forPreview: Boolean): KotlinTypeInfo {
if (this !is KtTypeCodeFragment) return KotlinTypeInfo()
val typeRef = getContentElement()
val type = typeRef?.analyze(BodyResolveMode.PARTIAL)?.get(BindingContext.TYPE, typeRef)
val typeText = when {
type != null && !type.isError -> IdeDescriptorRenderers.SOURCE_CODE.renderType(type)
typeRef != null -> typeRef.text
else -> ""
return when {
type != null && !type.isError -> KotlinTypeInfo(type, if (forPreview) typeRef?.text else null)
typeRef != null -> KotlinTypeInfo(null, typeRef.text)
else -> KotlinTypeInfo()
}
return type to typeText
}
private fun evaluateChangeInfo(parametersModel: KotlinCallableParameterTableModel,
@@ -422,11 +422,12 @@ public class KotlinChangeSignatureDialog(
methodDescriptor: KotlinMethodDescriptor,
visibility: Visibility?,
methodName: String,
defaultValueContext: PsiElement): KotlinChangeInfo {
defaultValueContext: PsiElement,
forPreview: Boolean): KotlinChangeInfo {
val parameters = parametersModel.getItems().map { parameter ->
val parameterInfo = parameter.parameter
parameterInfo.currentTypeText = parameter.typeCodeFragment.getTypeWithText().second
parameterInfo.currentTypeText = parameter.typeCodeFragment.getTypeInfo(forPreview).render()
val codeFragment = parameter.defaultValueCodeFragment as KtExpressionCodeFragment
val oldDefaultValue = parameterInfo.defaultValueForCall
@@ -437,11 +438,9 @@ public class KotlinChangeSignatureDialog(
parameterInfo
}
val (returnType, returnTypeText) = returnTypeCodeFragment.getTypeWithText()
return KotlinChangeInfo(methodDescriptor.original,
methodName,
returnType,
returnTypeText,
returnTypeCodeFragment.getTypeInfo(forPreview),
visibility ?: Visibilities.DEFAULT_VISIBILITY,
parameters,
parametersModel.getReceiver(),
@@ -47,6 +47,7 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.utils.sure
class KotlinCallableDefinitionUsage<T : PsiElement>(
@@ -162,11 +163,9 @@ class KotlinCallableDefinitionUsage<T : PsiElement>(
if (changeInfo.isReturnTypeChanged && returnTypeIsNeeded) {
element.setTypeReference(null)
val returnTypeText = changeInfo.renderReturnType(this)
//TODO use ChangeFunctionReturnTypeFix.invoke when JetTypeCodeFragment.getType() is ready
if (!(returnTypeText == "Unit" || returnTypeText == "kotlin.Unit")) {
element.setTypeReference(KtPsiFactory(element).createType(returnTypeText))!!.addToShorteningWaitSet(
Options.DEFAULT)
val returnType = changeInfo.newReturnTypeInfo.type
if (returnType == null || !returnType.isUnit()) {
element.setTypeReference(KtPsiFactory(element).createType(returnTypeText))!!.addToShorteningWaitSet(Options.DEFAULT)
}
}
}
@@ -56,6 +56,7 @@ import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import org.jetbrains.kotlin.utils.sure
import java.io.File
import java.util.*
@@ -267,11 +268,11 @@ class KotlinChangeSignatureTest : KotlinCodeInsightTestCase() {
fun testRenameFunction() = doTest { newName = "after" }
fun testChangeReturnType() = doTest { newReturnTypeText = "Float" }
fun testChangeReturnType() = doTest { newReturnTypeInfo = KotlinTypeInfo(BUILT_INS.floatType) }
fun testAddReturnType() = doTest { newReturnTypeText = "Float" }
fun testAddReturnType() = doTest { newReturnTypeInfo = KotlinTypeInfo(BUILT_INS.floatType) }
fun testRemoveReturnType() = doTest { newReturnTypeText = "Unit" }
fun testRemoveReturnType() = doTest { newReturnTypeInfo = KotlinTypeInfo(BUILT_INS.unitType) }
fun testChangeConstructorVisibility() = doTest { newVisibility = Visibilities.PROTECTED }
@@ -367,7 +368,7 @@ class KotlinChangeSignatureTest : KotlinCodeInsightTestCase() {
newParameters[1].name = "y1"
addParameter(KotlinParameterInfo(originalBaseFunctionDescriptor, -1, "x", BUILT_INS.anyType))
newReturnTypeText = "Int"
newReturnTypeInfo = KotlinTypeInfo(BUILT_INS.intType)
}
fun testVarargs() = doTestConflict()
@@ -427,7 +428,7 @@ class KotlinChangeSignatureTest : KotlinCodeInsightTestCase() {
newParameters[1].currentTypeText = "String?"
newParameters[2].currentTypeText = "Any"
newReturnTypeText = "String?"
newReturnTypeInfo = KotlinTypeInfo(BUILT_INS.stringType.makeNullable())
}
fun testFunctionJavaUsagesAndOverridesChangeTypes() = doTest {
@@ -435,7 +436,7 @@ class KotlinChangeSignatureTest : KotlinCodeInsightTestCase() {
newParameters[1].currentTypeText = "Int"
newParameters[2].currentTypeText = "Long?"
newReturnTypeText = "Any?"
newReturnTypeInfo = KotlinTypeInfo(BUILT_INS.nullableAnyType)
}
fun testGenericsWithOverrides() = doTest {
@@ -443,7 +444,7 @@ class KotlinChangeSignatureTest : KotlinCodeInsightTestCase() {
newParameters[1].currentTypeText = "A?"
newParameters[2].currentTypeText = "U<B>"
newReturnTypeText = "U<C>?"
newReturnTypeInfo = KotlinTypeInfo(null, "U<C>?")
}
fun testAddReceiverToGenericsWithOverrides() = doTest {
@@ -668,7 +669,7 @@ class KotlinChangeSignatureTest : KotlinCodeInsightTestCase() {
fun testChangeProperty() = doTest {
newName = "s"
newReturnTypeText = "String"
newReturnTypeInfo = KotlinTypeInfo(BUILT_INS.stringType)
}
fun testAddPropertyReceiverConflict() = doTestConflict {
@@ -697,7 +698,7 @@ class KotlinChangeSignatureTest : KotlinCodeInsightTestCase() {
fun testChangeClassParameter() = doTest {
newName = "s"
newReturnTypeText = "String"
newReturnTypeInfo = KotlinTypeInfo(BUILT_INS.stringType)
}
fun testParameterPropagation() = doTest {