From 594ad279527be4e385a3fe21d0d438fa96d4eaf4 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Tue, 15 Dec 2015 16:11:03 +0300 Subject: [PATCH] Change Signature: Implement proper resolution of type code fragments #KT-9210 Fixed --- .../kotlin/psi/KtTypeCodeFragment.java | 4 +- .../caches/resolve/CodeFragmentAnalyzer.kt | 37 +++++++++++-------- .../InitializePropertyQuickFixFactory.kt | 7 ++-- .../ui/KotlinChangeSignatureDialog.kt | 28 +++++++++++--- .../KotlinChangeSignatureTest.kt | 6 ++- 5 files changed, 55 insertions(+), 27 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtTypeCodeFragment.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtTypeCodeFragment.java index fe012c085dd..bf41e96d883 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtTypeCodeFragment.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtTypeCodeFragment.java @@ -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); } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt index be963d3011f..4859a2344bf 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/InitializePropertyQuickFixFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/InitializePropertyQuickFixFactory.kt index 4ea060d993d..44c8ddadd99 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/InitializePropertyQuickFixFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/InitializePropertyQuickFixFactory.kt @@ -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) } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinChangeSignatureDialog.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinChangeSignatureDialog.kt index 5b914060a7f..16bddee0393 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinChangeSignatureDialog.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinChangeSignatureDialog.kt @@ -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 { + 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) diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt index a332c37ec9f..4854bde35c1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt @@ -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() + .sure { "Context element is null" } val bindingContext = element.analyze(BodyResolveMode.FULL) val callableDescriptor = KotlinChangeSignatureHandler .findDescriptor(element, project, editor, bindingContext)