Change Signature: Implement proper resolution of type code fragments

#KT-9210 Fixed
This commit is contained in:
Alexey Sedunov
2015-12-15 16:11:03 +03:00
parent cfe1c44260
commit 594ad27952
5 changed files with 55 additions and 27 deletions
@@ -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,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)
}
@@ -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)
@@ -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)