Change Signature: Implement proper resolution of type code fragments
#KT-9210 Fixed
This commit is contained in:
@@ -27,12 +27,12 @@ public class KtTypeCodeFragment extends KtCodeFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasTypeReference() {
|
public boolean hasTypeReference() {
|
||||||
return getContentElement() instanceof KtTypeReference;
|
return getContentElement() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public KtElement getContentElement() {
|
public KtTypeReference getContentElement() {
|
||||||
return findChildByClass(KtTypeReference.class);
|
return findChildByClass(KtTypeReference.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-15
@@ -22,9 +22,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
|||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
|
||||||
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
|
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
@@ -40,7 +38,8 @@ import javax.inject.Inject
|
|||||||
public class CodeFragmentAnalyzer(
|
public class CodeFragmentAnalyzer(
|
||||||
private val resolveSession: ResolveSession,
|
private val resolveSession: ResolveSession,
|
||||||
private val qualifierResolver: QualifiedExpressionResolver,
|
private val qualifierResolver: QualifiedExpressionResolver,
|
||||||
private val expressionTypingServices: ExpressionTypingServices
|
private val expressionTypingServices: ExpressionTypingServices,
|
||||||
|
private val typeResolver: TypeResolver
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// component dependency cycle
|
// component dependency cycle
|
||||||
@@ -48,22 +47,30 @@ public class CodeFragmentAnalyzer(
|
|||||||
@Inject set
|
@Inject set
|
||||||
|
|
||||||
public fun analyzeCodeFragment(codeFragment: KtCodeFragment, trace: BindingTrace, bodyResolveMode: BodyResolveMode) {
|
public fun analyzeCodeFragment(codeFragment: KtCodeFragment, trace: BindingTrace, bodyResolveMode: BodyResolveMode) {
|
||||||
val codeFragmentExpression = codeFragment.getContentElement()
|
val codeFragmentElement = codeFragment.getContentElement()
|
||||||
if (codeFragmentExpression !is KtExpression) return
|
|
||||||
|
|
||||||
val (scopeForContextElement, dataFlowInfo) = getScopeAndDataFlowForAnalyzeFragment(codeFragment) {
|
val (scopeForContextElement, dataFlowInfo) = getScopeAndDataFlowForAnalyzeFragment(codeFragment) {
|
||||||
resolveElementCache!!.resolveToElement(it, bodyResolveMode)
|
resolveElementCache!!.resolveToElement(it, bodyResolveMode)
|
||||||
} ?: return
|
} ?: return
|
||||||
|
|
||||||
PreliminaryDeclarationVisitor.createForExpression(codeFragmentExpression, trace)
|
when (codeFragmentElement) {
|
||||||
expressionTypingServices.getTypeInfo(
|
is KtExpression -> {
|
||||||
scopeForContextElement,
|
PreliminaryDeclarationVisitor.createForExpression(codeFragmentElement, trace)
|
||||||
codeFragmentExpression,
|
expressionTypingServices.getTypeInfo(
|
||||||
TypeUtils.NO_EXPECTED_TYPE,
|
scopeForContextElement,
|
||||||
dataFlowInfo,
|
codeFragmentElement,
|
||||||
trace,
|
TypeUtils.NO_EXPECTED_TYPE,
|
||||||
false
|
dataFlowInfo,
|
||||||
)
|
trace,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is KtTypeReference -> {
|
||||||
|
val context = TypeResolutionContext(scopeForContextElement, trace, true, true).noBareTypes()
|
||||||
|
typeResolver.resolvePossiblyBareType(context, codeFragmentElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: this code should be moved into debugger which should set correct context for its code fragment
|
//TODO: this code should be moved into debugger which should set correct context for its code fragment
|
||||||
|
|||||||
@@ -102,11 +102,12 @@ object InitializePropertyQuickFixFactory : KotlinIntentionActionsFactory() {
|
|||||||
|
|
||||||
val classDescriptor = klass.resolveToDescriptorIfAny() as? ClassDescriptorWithResolutionScopes ?: return
|
val classDescriptor = klass.resolveToDescriptorIfAny() as? ClassDescriptorWithResolutionScopes ?: return
|
||||||
val constructorDescriptor = classDescriptor.unsubstitutedPrimaryConstructor ?: return
|
val constructorDescriptor = classDescriptor.unsubstitutedPrimaryConstructor ?: return
|
||||||
val constructorPointer = constructorDescriptor.source.getPsi()?.createSmartPointer()
|
val contextElement = constructorDescriptor.source.getPsi() ?: return
|
||||||
|
val constructorPointer = contextElement.createSmartPointer()
|
||||||
val config = configureChangeSignature(propertyDescriptor)
|
val config = configureChangeSignature(propertyDescriptor)
|
||||||
val changeSignature = { runChangeSignature(project, constructorDescriptor, config, element, text) }
|
val changeSignature = { runChangeSignature(project, constructorDescriptor, config, contextElement, text) }
|
||||||
changeSignature.runRefactoringWithPostprocessing(project, "refactoring.changeSignature") {
|
changeSignature.runRefactoringWithPostprocessing(project, "refactoring.changeSignature") {
|
||||||
val constructorOrClass = constructorPointer?.element
|
val constructorOrClass = constructorPointer.element
|
||||||
val constructor = constructorOrClass as? KtConstructor<*> ?: (constructorOrClass as? KtClass)?.getPrimaryConstructor()
|
val constructor = constructorOrClass as? KtConstructor<*> ?: (constructorOrClass as? KtClass)?.getPrimaryConstructor()
|
||||||
constructor?.getValueParameters()?.lastOrNull()?.replace(parameterToInsert)
|
constructor?.getValueParameters()?.lastOrNull()?.replace(parameterToInsert)
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-6
@@ -45,13 +45,17 @@ import com.intellij.util.ui.table.JBTableRowEditor
|
|||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.descriptors.Visibility
|
import org.jetbrains.kotlin.descriptors.Visibility
|
||||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle
|
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor.Kind
|
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.KtExpressionCodeFragment
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
import org.jetbrains.kotlin.psi.KtTypeCodeFragment
|
import org.jetbrains.kotlin.psi.KtTypeCodeFragment
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
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.BorderLayout
|
||||||
import java.awt.Font
|
import java.awt.Font
|
||||||
import java.awt.Toolkit
|
import java.awt.Toolkit
|
||||||
@@ -400,6 +404,19 @@ public class KotlinChangeSignatureDialog(
|
|||||||
return KotlinChangeSignatureProcessor(project, changeInfo, commandName)
|
return KotlinChangeSignatureProcessor(project, changeInfo, commandName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun PsiCodeFragment?.getTypeWithText(): Pair<KotlinType?, String> {
|
||||||
|
if (this !is KtTypeCodeFragment) return null to ""
|
||||||
|
|
||||||
|
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 type to typeText
|
||||||
|
}
|
||||||
|
|
||||||
private fun evaluateChangeInfo(parametersModel: KotlinCallableParameterTableModel,
|
private fun evaluateChangeInfo(parametersModel: KotlinCallableParameterTableModel,
|
||||||
returnTypeCodeFragment: PsiCodeFragment?,
|
returnTypeCodeFragment: PsiCodeFragment?,
|
||||||
methodDescriptor: KotlinMethodDescriptor,
|
methodDescriptor: KotlinMethodDescriptor,
|
||||||
@@ -409,7 +426,8 @@ public class KotlinChangeSignatureDialog(
|
|||||||
val parameters = parametersModel.getItems().map { parameter ->
|
val parameters = parametersModel.getItems().map { parameter ->
|
||||||
val parameterInfo = parameter.parameter
|
val parameterInfo = parameter.parameter
|
||||||
|
|
||||||
parameterInfo.currentTypeText = parameter.typeCodeFragment.getText().trim()
|
parameterInfo.currentTypeText = parameter.typeCodeFragment.getTypeWithText().second
|
||||||
|
|
||||||
val codeFragment = parameter.defaultValueCodeFragment as KtExpressionCodeFragment
|
val codeFragment = parameter.defaultValueCodeFragment as KtExpressionCodeFragment
|
||||||
val oldDefaultValue = parameterInfo.defaultValueForCall
|
val oldDefaultValue = parameterInfo.defaultValueForCall
|
||||||
if (codeFragment.getText() != (if (oldDefaultValue != null) oldDefaultValue.getText() else "")) {
|
if (codeFragment.getText() != (if (oldDefaultValue != null) oldDefaultValue.getText() else "")) {
|
||||||
@@ -419,14 +437,12 @@ public class KotlinChangeSignatureDialog(
|
|||||||
parameterInfo
|
parameterInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
val returnTypeText = if (returnTypeCodeFragment != null) returnTypeCodeFragment.getText().trim() else ""
|
val (returnType, returnTypeText) = returnTypeCodeFragment.getTypeWithText()
|
||||||
//TODO return the actual type
|
|
||||||
val returnType = if (hasTypeReference(returnTypeCodeFragment)) methodDescriptor.baseDescriptor.builtIns.anyType else null
|
|
||||||
return KotlinChangeInfo(methodDescriptor.original,
|
return KotlinChangeInfo(methodDescriptor.original,
|
||||||
methodName,
|
methodName,
|
||||||
returnType,
|
returnType,
|
||||||
returnTypeText,
|
returnTypeText,
|
||||||
visibility ?: Visibilities.DEFAULT_VISIBILITY,
|
visibility ?: Visibilities.DEFAULT_VISIBILITY,
|
||||||
parameters,
|
parameters,
|
||||||
parametersModel.getReceiver(),
|
parametersModel.getReceiver(),
|
||||||
defaultValueContext)
|
defaultValueContext)
|
||||||
|
|||||||
+5
-1
@@ -51,6 +51,7 @@ import org.jetbrains.kotlin.psi.KtElement
|
|||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
@@ -116,7 +117,10 @@ class KotlinChangeSignatureTest : KotlinCodeInsightTestCase() {
|
|||||||
configureFiles()
|
configureFiles()
|
||||||
|
|
||||||
val element = (KotlinChangeSignatureHandler().findTargetMember(file, editor) as KtElement?).sure { "Target element is null" }
|
val element = (KotlinChangeSignatureHandler().findTargetMember(file, editor) as KtElement?).sure { "Target element is null" }
|
||||||
val context = file.findElementAt(editor.caretModel.offset).sure { "Context element is null" }
|
val context = file
|
||||||
|
.findElementAt(editor.caretModel.offset)
|
||||||
|
?.getNonStrictParentOfType<KtElement>()
|
||||||
|
.sure { "Context element is null" }
|
||||||
val bindingContext = element.analyze(BodyResolveMode.FULL)
|
val bindingContext = element.analyze(BodyResolveMode.FULL)
|
||||||
val callableDescriptor = KotlinChangeSignatureHandler
|
val callableDescriptor = KotlinChangeSignatureHandler
|
||||||
.findDescriptor(element, project, editor, bindingContext)
|
.findDescriptor(element, project, editor, bindingContext)
|
||||||
|
|||||||
Reference in New Issue
Block a user