mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[kotlin] k2: change signature: support imports required for default values
GitOrigin-RevId: d852c4a0c9f9aa807f69efa97f65fab5525032b7
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8d820dab14
commit
ba6fb24127
+136
@@ -0,0 +1,136 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.codeinsight.utils
|
||||
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.descendantsOfType
|
||||
import com.intellij.psi.util.elementType
|
||||
import com.intellij.refactoring.suggested.createSmartPointer
|
||||
import org.jetbrains.kotlin.analysis.api.KtAllowAnalysisFromWriteAction
|
||||
import org.jetbrains.kotlin.analysis.api.KtAllowAnalysisOnEdt
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.allowAnalysisFromWriteAction
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.allowAnalysisOnEdt
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassLikeSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.base.util.quoteIfNeeded
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtInstanceExpressionWithLabel
|
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtNullableType
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.KtUserType
|
||||
import org.jetbrains.kotlin.psi.createExpressionByPattern
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments
|
||||
|
||||
object AddQualifiersUtil {
|
||||
fun addQualifiersRecursively(root: KtElement): KtElement {
|
||||
if (root is KtNameReferenceExpression) return applyIfApplicable(root) ?: root
|
||||
|
||||
root.descendantsOfType<KtNameReferenceExpression>()
|
||||
.map { it.createSmartPointer() }
|
||||
.toList()
|
||||
.asReversed()
|
||||
.forEach {
|
||||
it.element?.let(::applyIfApplicable)
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
fun isApplicableTo(referenceExpression: KtNameReferenceExpression, contextSymbol: KtNamedSymbol): Boolean {
|
||||
if (referenceExpression.parent is KtInstanceExpressionWithLabel) return false
|
||||
|
||||
val prevElement = PsiTreeUtil.skipWhitespacesAndCommentsBackward(referenceExpression)
|
||||
if (prevElement.elementType == KtTokens.DOT) return false
|
||||
val fqName = getFqName(contextSymbol)
|
||||
if (contextSymbol is KtCallableSymbol && contextSymbol.isExtension || fqName?.parent()?.isRoot == true) return false
|
||||
if (prevElement.elementType == KtTokens.COLONCOLON) {
|
||||
if (contextSymbol is KtCallableSymbol && contextSymbol.getContainingSymbol() == null) return false
|
||||
val prevSibling = prevElement?.getPrevSiblingIgnoringWhitespaceAndComments()
|
||||
if (prevSibling is KtNameReferenceExpression || prevSibling is KtDotQualifiedExpression) return false
|
||||
}
|
||||
|
||||
val file = referenceExpression.containingKtFile
|
||||
val identifier = referenceExpression.getIdentifier()?.text
|
||||
return !file.hasImportAlias() || file.importDirectives.none { it.aliasName == identifier && it.importedFqName == fqName }
|
||||
}
|
||||
|
||||
fun applyTo(referenceExpression: KtNameReferenceExpression, fqName: FqName): KtElement {
|
||||
val qualifier = fqName.parent().quoteIfNeeded().asString()
|
||||
val project = referenceExpression.project
|
||||
val psiFactory = KtPsiFactory(project)
|
||||
return project.executeWriteCommand(KotlinBundle.message("add.full.qualifier"), groupId = null) {
|
||||
when (val parent = referenceExpression.parent) {
|
||||
is KtCallableReferenceExpression -> addOrReplaceQualifier(psiFactory, parent, qualifier)
|
||||
is KtCallExpression -> replaceExpressionWithDotQualifier(psiFactory, parent, qualifier)
|
||||
is KtUserType -> addQualifierToType(psiFactory, parent, qualifier)
|
||||
else -> replaceExpressionWithQualifier(psiFactory, referenceExpression, fqName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFqName(symbol: KtNamedSymbol): FqName? {
|
||||
return when (symbol) {
|
||||
is KtClassLikeSymbol -> symbol.classIdIfNonLocal?.asSingleFqName()
|
||||
is KtCallableSymbol -> symbol.callableIdIfNonLocal?.asSingleFqName()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(KtAllowAnalysisFromWriteAction::class, KtAllowAnalysisOnEdt::class)
|
||||
private fun applyIfApplicable(referenceExpression: KtNameReferenceExpression): KtElement? {
|
||||
val fqName = allowAnalysisFromWriteAction {
|
||||
allowAnalysisOnEdt {
|
||||
analyze(referenceExpression) {
|
||||
val symbol = referenceExpression.mainReference.resolveToSymbols().singleOrNull() as? KtNamedSymbol ?: return null
|
||||
val fqName = getFqName(symbol) ?: return null
|
||||
if (!isApplicableTo(referenceExpression, symbol)) return null
|
||||
fqName
|
||||
}
|
||||
}
|
||||
}
|
||||
return applyTo(referenceExpression, fqName)
|
||||
}
|
||||
|
||||
private fun addOrReplaceQualifier(factory: KtPsiFactory, expression: KtCallableReferenceExpression, qualifier: String): KtElement {
|
||||
val receiver = expression.receiverExpression
|
||||
return if (receiver != null) {
|
||||
replaceExpressionWithDotQualifier(factory, receiver, qualifier)
|
||||
} else {
|
||||
val qualifierExpression = factory.createExpression(qualifier)
|
||||
expression.addBefore(qualifierExpression, expression.firstChild) as KtElement
|
||||
}
|
||||
}
|
||||
|
||||
private fun replaceExpressionWithDotQualifier(psiFactory: KtPsiFactory, expression: KtExpression, qualifier: String): KtElement {
|
||||
val expressionWithQualifier = psiFactory.createExpressionByPattern("$0.$1", qualifier, expression)
|
||||
return expression.replace(expressionWithQualifier) as KtElement
|
||||
}
|
||||
|
||||
private fun addQualifierToType(psiFactory: KtPsiFactory, userType: KtUserType, qualifier: String): KtElement {
|
||||
val type = userType.parent as? KtNullableType ?: userType
|
||||
val typeWithQualifier = psiFactory.createType("$qualifier.${type.text}")
|
||||
return type.parent.replace(typeWithQualifier) as KtElement
|
||||
}
|
||||
|
||||
private fun replaceExpressionWithQualifier(
|
||||
psiFactory: KtPsiFactory,
|
||||
referenceExpression: KtNameReferenceExpression,
|
||||
fqName: FqName
|
||||
): KtElement {
|
||||
val expressionWithQualifier = psiFactory.createExpression(fqName.asString())
|
||||
return referenceExpression.replace(expressionWithQualifier) as KtElement
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package usages
|
||||
|
||||
fun foo(i: Int) {}
|
||||
|
||||
fun callFoo() = foo(a.b.c.Dep.Companion.MY_CONSTANT_FROM_DEP)
|
||||
-2
@@ -5,5 +5,3 @@ import a.b.c.Dep
|
||||
fun foo<caret>(i: Int) {}
|
||||
|
||||
fun callFoo() = foo(Dep.MY_CONSTANT_FROM_DEP)
|
||||
|
||||
// IGNORE_K2
|
||||
-2
@@ -3,5 +3,3 @@ package usages
|
||||
fun foo<caret>() {}
|
||||
|
||||
fun callFoo() = foo()
|
||||
|
||||
// IGNORE_K2
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package usages
|
||||
|
||||
fun foo(i: Int) {}
|
||||
|
||||
fun callFoo() = foo(a.b.c.Dep.Companion.MY_CONSTANT_FROM_DEP)
|
||||
-2
@@ -5,5 +5,3 @@ import a.b.c.Dep
|
||||
fun foo<caret>(i: Int) {}
|
||||
|
||||
fun callFoo() = foo(Dep.MY_CONSTANT_FROM_DEP)
|
||||
|
||||
// IGNORE_K2
|
||||
-2
@@ -3,5 +3,3 @@ package usages
|
||||
fun foo<caret>() {}
|
||||
|
||||
fun callFoo() = foo()
|
||||
|
||||
// IGNORE_K2
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package usages
|
||||
|
||||
fun Int.foo() {}
|
||||
|
||||
fun callFoo() = a.b.c.Dep.Companion.MY_CONSTANT_FROM_DEP.foo()
|
||||
-2
@@ -5,5 +5,3 @@ import a.b.c.Dep
|
||||
fun Int.foo<caret>() {}
|
||||
|
||||
fun callFoo() = Dep.MY_CONSTANT_FROM_DEP.foo()
|
||||
|
||||
// IGNORE_K2
|
||||
-2
@@ -3,5 +3,3 @@ package usages
|
||||
fun foo<caret>() {}
|
||||
|
||||
fun callFoo() = foo()
|
||||
|
||||
// IGNORE_K2
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package usages
|
||||
|
||||
import a.b.Dep2.Companion.NUMBER
|
||||
import a.b.c.Dep.Companion.MY_CONSTANT_FROM_DEP
|
||||
|
||||
val Int.foo: String get() = "hello"
|
||||
|
||||
fun callFoo() = Dep2().eval(MY_CONSTANT_FROM_DEP + NUMBER).foo
|
||||
fun t() {
|
||||
val s = Dep2().eval(MY_CONSTANT_FROM_DEP + NUMBER).foo + " world"
|
||||
}
|
||||
-2
@@ -9,5 +9,3 @@ fun callFoo() = Dep2().eval(Dep.MY_CONSTANT_FROM_DEP + Dep2.NUMBER).foo
|
||||
fun t() {
|
||||
val s = Dep2().eval(Dep.MY_CONSTANT_FROM_DEP + Dep2.NUMBER).foo + " world"
|
||||
}
|
||||
|
||||
// IGNORE_K2
|
||||
+1
-3
@@ -5,6 +5,4 @@ val <caret>foo: String get() = "hello"
|
||||
fun callFoo() = foo
|
||||
fun t() {
|
||||
val s = foo + " world"
|
||||
}
|
||||
|
||||
// IGNORE_K2
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package usages
|
||||
|
||||
import a.b.c.Dep.Companion.MY_CONSTANT_FROM_DEP
|
||||
|
||||
val Int.foo: String get() = "hello"
|
||||
|
||||
fun callFoo() = MY_CONSTANT_FROM_DEP.foo
|
||||
fun t() {
|
||||
val s = MY_CONSTANT_FROM_DEP.foo + " world"
|
||||
}
|
||||
-2
@@ -8,5 +8,3 @@ fun callFoo() = Dep.MY_CONSTANT_FROM_DEP.foo
|
||||
fun t() {
|
||||
val s = Dep.MY_CONSTANT_FROM_DEP.foo + " world"
|
||||
}
|
||||
|
||||
// IGNORE_K2
|
||||
+1
-3
@@ -5,6 +5,4 @@ val <caret>foo: String get() = "hello"
|
||||
fun callFoo() = foo
|
||||
fun t() {
|
||||
val s = foo + " world"
|
||||
}
|
||||
|
||||
// IGNORE_K2
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package usages
|
||||
|
||||
import a.b.c.Dep.Companion.MY_CONSTANT_FROM_DEP
|
||||
|
||||
val Int.foo: String get() = "hello"
|
||||
|
||||
fun callFoo() = MY_CONSTANT_FROM_DEP.foo
|
||||
fun t() {
|
||||
val s = MY_CONSTANT_FROM_DEP.foo + " world"
|
||||
}
|
||||
-2
@@ -8,5 +8,3 @@ fun callFoo() = Dep.MY_CONSTANT_FROM_DEP.foo
|
||||
fun t() {
|
||||
val s = Dep.MY_CONSTANT_FROM_DEP.foo + " world"
|
||||
}
|
||||
|
||||
// IGNORE_K2
|
||||
+1
-3
@@ -5,6 +5,4 @@ val <caret>foo: String get() = "hello"
|
||||
fun callFoo() = foo
|
||||
fun t() {
|
||||
val s = foo + " world"
|
||||
}
|
||||
|
||||
// IGNORE_K2
|
||||
}
|
||||
+11
-6
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.analysis.api.lifetime.allowAnalysisOnEdt
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtErrorType
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.idea.codeinsight.utils.AddQualifiersUtil
|
||||
import org.jetbrains.kotlin.idea.k2.refactoring.changeSignature.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinModifiableMethodDescriptor.Kind
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.ui.*
|
||||
@@ -115,11 +116,11 @@ internal class KotlinChangeSignatureDialog(
|
||||
|
||||
|
||||
override fun createRefactoringProcessor(): BaseRefactoringProcessor {
|
||||
val changeInfo = evaluateChangeSignatureInfo()
|
||||
val changeInfo = evaluateChangeSignatureInfo(false)
|
||||
return KotlinChangeSignatureProcessor(project, changeInfo)
|
||||
}
|
||||
|
||||
private fun evaluateChangeSignatureInfo(): KotlinChangeInfo {
|
||||
private fun evaluateChangeSignatureInfo(forPreview: Boolean): KotlinChangeInfo {
|
||||
val callable = myMethod.method
|
||||
var receiverInfo: KotlinParameterInfo? = null
|
||||
val parameters = parametersTableModel.items.map { parameter ->
|
||||
@@ -133,11 +134,15 @@ internal class KotlinChangeSignatureDialog(
|
||||
|
||||
val codeFragment = parameter.defaultValueCodeFragment as KtExpressionCodeFragment
|
||||
|
||||
//todo if (!forPreview) AddFullQualifierIntention.Holder.addQualifiersRecursively(codeFragment)
|
||||
if (!forPreview) AddQualifiersUtil.addQualifiersRecursively(codeFragment)
|
||||
|
||||
val oldDefaultValue = parameterInfo.defaultValueForCall
|
||||
if (codeFragment.text != (if (oldDefaultValue != null) oldDefaultValue.text else "") && parameter.parameter.defaultValueAsDefaultParameter) {
|
||||
parameterInfo.defaultValue = codeFragment.getContentElement()
|
||||
if (codeFragment.text != (if (oldDefaultValue != null) oldDefaultValue.text else "")) {
|
||||
if (parameter.parameter.defaultValueAsDefaultParameter) {
|
||||
parameterInfo.defaultValue = codeFragment.getContentElement()
|
||||
} else {
|
||||
parameterInfo.defaultValueForCall = codeFragment.getContentElement()
|
||||
}
|
||||
}
|
||||
|
||||
parameterInfo
|
||||
@@ -188,7 +193,7 @@ internal class KotlinChangeSignatureDialog(
|
||||
}
|
||||
|
||||
override fun calculateSignature(): String {
|
||||
val changeSignatureInfo = evaluateChangeSignatureInfo()
|
||||
val changeSignatureInfo = evaluateChangeSignatureInfo(true)
|
||||
return changeSignatureInfo.getNewSignature()
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -21,5 +21,6 @@
|
||||
<orderEntry type="module" module-name="kotlin.refactorings.common.tests" scope="TEST" />
|
||||
<orderEntry type="module" module-name="kotlin.refactorings.k2" scope="TEST" />
|
||||
<orderEntry type="module" module-name="kotlin.fir.frontend-independent.tests" scope="TEST" />
|
||||
<orderEntry type="module" module-name="kotlin.code-insight.utils" scope="TEST" />
|
||||
</component>
|
||||
</module>
|
||||
+2
-1
@@ -4,6 +4,7 @@ package org.jetbrains.kotlin.idea.k2.refactoring.changeSignature
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.idea.codeinsight.utils.AddQualifiersUtil
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.BaseKotlinChangeSignatureTest
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
@@ -29,7 +30,7 @@ class KotlinFirChangeSignatureTest :
|
||||
}
|
||||
|
||||
override fun addFullQualifier(fragment: KtExpressionCodeFragment) {
|
||||
TODO("Not yet implemented")
|
||||
AddQualifiersUtil.addQualifiersRecursively(fragment)
|
||||
}
|
||||
|
||||
override fun KotlinChangeInfo.createKotlinParameter(
|
||||
|
||||
Reference in New Issue
Block a user