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() {
|
||||
return getContentElement() instanceof KtTypeReference;
|
||||
return getContentElement() != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public KtElement getContentElement() {
|
||||
public KtTypeReference getContentElement() {
|
||||
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.psiUtil.parents
|
||||
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
@@ -40,7 +38,8 @@ import javax.inject.Inject
|
||||
public class CodeFragmentAnalyzer(
|
||||
private val resolveSession: ResolveSession,
|
||||
private val qualifierResolver: QualifiedExpressionResolver,
|
||||
private val expressionTypingServices: ExpressionTypingServices
|
||||
private val expressionTypingServices: ExpressionTypingServices,
|
||||
private val typeResolver: TypeResolver
|
||||
) {
|
||||
|
||||
// component dependency cycle
|
||||
@@ -48,22 +47,30 @@ public class CodeFragmentAnalyzer(
|
||||
@Inject set
|
||||
|
||||
public fun analyzeCodeFragment(codeFragment: KtCodeFragment, trace: BindingTrace, bodyResolveMode: BodyResolveMode) {
|
||||
val codeFragmentExpression = codeFragment.getContentElement()
|
||||
if (codeFragmentExpression !is KtExpression) return
|
||||
val codeFragmentElement = codeFragment.getContentElement()
|
||||
|
||||
val (scopeForContextElement, dataFlowInfo) = getScopeAndDataFlowForAnalyzeFragment(codeFragment) {
|
||||
resolveElementCache!!.resolveToElement(it, bodyResolveMode)
|
||||
} ?: return
|
||||
|
||||
PreliminaryDeclarationVisitor.createForExpression(codeFragmentExpression, trace)
|
||||
expressionTypingServices.getTypeInfo(
|
||||
scopeForContextElement,
|
||||
codeFragmentExpression,
|
||||
TypeUtils.NO_EXPECTED_TYPE,
|
||||
dataFlowInfo,
|
||||
trace,
|
||||
false
|
||||
)
|
||||
when (codeFragmentElement) {
|
||||
is KtExpression -> {
|
||||
PreliminaryDeclarationVisitor.createForExpression(codeFragmentElement, trace)
|
||||
expressionTypingServices.getTypeInfo(
|
||||
scopeForContextElement,
|
||||
codeFragmentElement,
|
||||
TypeUtils.NO_EXPECTED_TYPE,
|
||||
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
|
||||
|
||||
@@ -102,11 +102,12 @@ object InitializePropertyQuickFixFactory : KotlinIntentionActionsFactory() {
|
||||
|
||||
val classDescriptor = klass.resolveToDescriptorIfAny() as? ClassDescriptorWithResolutionScopes ?: 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 changeSignature = { runChangeSignature(project, constructorDescriptor, config, element, text) }
|
||||
val changeSignature = { runChangeSignature(project, constructorDescriptor, config, contextElement, text) }
|
||||
changeSignature.runRefactoringWithPostprocessing(project, "refactoring.changeSignature") {
|
||||
val constructorOrClass = constructorPointer?.element
|
||||
val constructorOrClass = constructorPointer.element
|
||||
val constructor = constructorOrClass as? KtConstructor<*> ?: (constructorOrClass as? KtClass)?.getPrimaryConstructor()
|
||||
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.Visibility
|
||||
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.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.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.Font
|
||||
import java.awt.Toolkit
|
||||
@@ -400,6 +404,19 @@ public class KotlinChangeSignatureDialog(
|
||||
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,
|
||||
returnTypeCodeFragment: PsiCodeFragment?,
|
||||
methodDescriptor: KotlinMethodDescriptor,
|
||||
@@ -409,7 +426,8 @@ public class KotlinChangeSignatureDialog(
|
||||
val parameters = parametersModel.getItems().map { parameter ->
|
||||
val parameterInfo = parameter.parameter
|
||||
|
||||
parameterInfo.currentTypeText = parameter.typeCodeFragment.getText().trim()
|
||||
parameterInfo.currentTypeText = parameter.typeCodeFragment.getTypeWithText().second
|
||||
|
||||
val codeFragment = parameter.defaultValueCodeFragment as KtExpressionCodeFragment
|
||||
val oldDefaultValue = parameterInfo.defaultValueForCall
|
||||
if (codeFragment.getText() != (if (oldDefaultValue != null) oldDefaultValue.getText() else "")) {
|
||||
@@ -419,14 +437,12 @@ public class KotlinChangeSignatureDialog(
|
||||
parameterInfo
|
||||
}
|
||||
|
||||
val returnTypeText = if (returnTypeCodeFragment != null) returnTypeCodeFragment.getText().trim() else ""
|
||||
//TODO return the actual type
|
||||
val returnType = if (hasTypeReference(returnTypeCodeFragment)) methodDescriptor.baseDescriptor.builtIns.anyType else null
|
||||
val (returnType, returnTypeText) = returnTypeCodeFragment.getTypeWithText()
|
||||
return KotlinChangeInfo(methodDescriptor.original,
|
||||
methodName,
|
||||
returnType,
|
||||
returnTypeText,
|
||||
visibility ?: Visibilities.DEFAULT_VISIBILITY,
|
||||
visibility ?: Visibilities.DEFAULT_VISIBILITY,
|
||||
parameters,
|
||||
parametersModel.getReceiver(),
|
||||
defaultValueContext)
|
||||
|
||||
+5
-1
@@ -51,6 +51,7 @@ import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
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.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
@@ -116,7 +117,10 @@ class KotlinChangeSignatureTest : KotlinCodeInsightTestCase() {
|
||||
configureFiles()
|
||||
|
||||
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 callableDescriptor = KotlinChangeSignatureHandler
|
||||
.findDescriptor(element, project, editor, bindingContext)
|
||||
|
||||
Reference in New Issue
Block a user