Inline refactoring: improve resolve
#KT-39705 Fixed #KT-19459 Fixed
This commit is contained in:
@@ -537,9 +537,11 @@ class CodeInliner<TCallElement : KtElement>(
|
||||
}
|
||||
|
||||
private fun removeExplicitTypeArguments(result: KtElement) {
|
||||
result.collectDescendantsOfType<KtTypeArgumentList>(canGoInside = { !it[USER_CODE_KEY] }) {
|
||||
RemoveExplicitTypeArgumentsIntention.isApplicableTo(it, approximateFlexible = true)
|
||||
}.forEach { it.delete() }
|
||||
for (typeArgumentList in result.collectDescendantsOfType<KtTypeArgumentList>(canGoInside = { !it[USER_CODE_KEY] })) {
|
||||
if (RemoveExplicitTypeArgumentsIntention.isApplicableTo(typeArgumentList, approximateFlexible = true)) {
|
||||
typeArgumentList.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun simplifySpreadArrayOfArguments(result: KtElement) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||
@@ -50,14 +49,14 @@ class CodeToInlineBuilder(
|
||||
fun prepareCodeToInline(
|
||||
mainExpression: KtExpression?,
|
||||
statementsBefore: List<KtExpression>,
|
||||
analyze: () -> BindingContext,
|
||||
reformat: Boolean
|
||||
analyze: (KtExpression) -> BindingContext,
|
||||
reformat: Boolean,
|
||||
): CodeToInline {
|
||||
var bindingContext = analyze()
|
||||
val alwaysKeepMainExpression = when (val descriptor = mainExpression.getResolvedCall(bindingContext)?.resultingDescriptor) {
|
||||
is PropertyDescriptor -> descriptor.getter?.isDefault == false
|
||||
else -> false
|
||||
}
|
||||
val alwaysKeepMainExpression =
|
||||
when (val descriptor = mainExpression?.getResolvedCall(analyze(mainExpression))?.resultingDescriptor) {
|
||||
is PropertyDescriptor -> descriptor.getter?.isDefault == false
|
||||
else -> false
|
||||
}
|
||||
|
||||
val codeToInline = MutableCodeToInline(
|
||||
mainExpression,
|
||||
@@ -66,14 +65,14 @@ class CodeToInlineBuilder(
|
||||
alwaysKeepMainExpression
|
||||
)
|
||||
|
||||
bindingContext = insertExplicitTypeArguments(codeToInline, bindingContext, analyze)
|
||||
insertExplicitTypeArguments(codeToInline, analyze)
|
||||
|
||||
processReferences(codeToInline, bindingContext, reformat)
|
||||
processReferences(codeToInline, analyze, reformat)
|
||||
|
||||
if (mainExpression != null) {
|
||||
val functionLiteralExpression = mainExpression.unpackFunctionLiteral(true)
|
||||
if (functionLiteralExpression != null) {
|
||||
val functionLiteralParameterTypes = getParametersForFunctionLiteral(functionLiteralExpression, bindingContext)
|
||||
val functionLiteralParameterTypes = getParametersForFunctionLiteral(functionLiteralExpression, analyze)
|
||||
if (functionLiteralParameterTypes != null) {
|
||||
codeToInline.addPostInsertionAction(mainExpression) { inlinedExpression ->
|
||||
addFunctionLiteralParameterTypes(functionLiteralParameterTypes, inlinedExpression)
|
||||
@@ -85,7 +84,11 @@ class CodeToInlineBuilder(
|
||||
return codeToInline.toNonMutable()
|
||||
}
|
||||
|
||||
private fun getParametersForFunctionLiteral(functionLiteralExpression: KtLambdaExpression, context: BindingContext): String? {
|
||||
private fun getParametersForFunctionLiteral(
|
||||
functionLiteralExpression: KtLambdaExpression,
|
||||
analyze: (KtExpression) -> BindingContext
|
||||
): String? {
|
||||
val context = analyze(functionLiteralExpression)
|
||||
val lambdaDescriptor = context.get(BindingContext.FUNCTION, functionLiteralExpression.functionLiteral)
|
||||
if (lambdaDescriptor == null ||
|
||||
ErrorUtils.containsErrorTypeInParameters(lambdaDescriptor) ||
|
||||
@@ -126,43 +129,36 @@ class CodeToInlineBuilder(
|
||||
}
|
||||
}
|
||||
|
||||
private fun insertExplicitTypeArguments(
|
||||
codeToInline: MutableCodeToInline,
|
||||
bindingContext: BindingContext,
|
||||
analyze: () -> BindingContext
|
||||
): BindingContext {
|
||||
val typeArgsToAdd = ArrayList<Pair<KtCallElement, KtTypeArgumentList>>()
|
||||
codeToInline.forEachDescendantOfType<KtCallElement> {
|
||||
private fun insertExplicitTypeArguments(codeToInline: MutableCodeToInline, analyze: (KtExpression) -> BindingContext) {
|
||||
val typeArgsToAdd = ArrayList<Pair<KtCallExpression, KtTypeArgumentList>>()
|
||||
codeToInline.forEachDescendantOfType<KtCallExpression> {
|
||||
val expression = it.parent as? KtQualifiedExpression ?: it
|
||||
val bindingContext = analyze(expression)
|
||||
if (InsertExplicitTypeArgumentsIntention.isApplicableTo(it, bindingContext)) {
|
||||
typeArgsToAdd.add(it to InsertExplicitTypeArgumentsIntention.createTypeArguments(it, bindingContext)!!)
|
||||
}
|
||||
}
|
||||
|
||||
if (typeArgsToAdd.isEmpty()) return bindingContext
|
||||
|
||||
if (typeArgsToAdd.isEmpty()) return
|
||||
for ((callExpr, typeArgs) in typeArgsToAdd) {
|
||||
callExpr.addAfter(typeArgs, callExpr.calleeExpression)
|
||||
}
|
||||
|
||||
// reanalyze expression - new usages of type parameters may be added
|
||||
return analyze()
|
||||
}
|
||||
|
||||
private fun processReferences(codeToInline: MutableCodeToInline, bindingContext: BindingContext, reformat: Boolean) {
|
||||
private fun processReferences(codeToInline: MutableCodeToInline, analyze: (KtExpression) -> BindingContext, reformat: Boolean) {
|
||||
val receiversToAdd = ArrayList<Triple<KtExpression, KtExpression, KotlinType>>()
|
||||
val targetDispatchReceiverType = targetCallable.dispatchReceiverParameter?.value?.type
|
||||
val targetExtensionReceiverType = targetCallable.extensionReceiverParameter?.value?.type
|
||||
|
||||
codeToInline.forEachDescendantOfType<KtSimpleNameExpression> { expression ->
|
||||
val target = bindingContext[BindingContext.REFERENCE_TARGET, expression] ?: return@forEachDescendantOfType
|
||||
val bindingContext = analyze(expression)
|
||||
val target = bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, expression]
|
||||
?: bindingContext[BindingContext.REFERENCE_TARGET, expression]
|
||||
?: return@forEachDescendantOfType
|
||||
|
||||
//TODO: other types of references ('[]' etc)
|
||||
if (expression.canBeResolvedViaImport(target, bindingContext)) {
|
||||
val importableFqName = if (target.isCompanionObject()) {
|
||||
target.containingDeclaration?.importableFqName
|
||||
} else {
|
||||
target.importableFqName
|
||||
}
|
||||
val importableFqName = target.importableFqName
|
||||
|
||||
if (importableFqName != null) {
|
||||
val lexicalScope = (expression.containingFile as? KtFile)?.getResolutionScope(bindingContext, resolutionFacade)
|
||||
|
||||
+11
-3
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.KtUserType
|
||||
@@ -67,10 +68,17 @@ object ReplaceWithAnnotationAnalyzer {
|
||||
|
||||
val expressionTypingServices = resolutionFacade.getFrontendService(module, ExpressionTypingServices::class.java)
|
||||
|
||||
fun analyzeExpression() = expression.analyzeInContext(scope, expressionTypingServices = expressionTypingServices)
|
||||
fun analyzeExpression(ignore: KtExpression) = expression.analyzeInContext(
|
||||
scope,
|
||||
expressionTypingServices = expressionTypingServices
|
||||
)
|
||||
|
||||
return CodeToInlineBuilder(symbolDescriptor, resolutionFacade)
|
||||
.prepareCodeToInline(expression, emptyList(), ::analyzeExpression, reformat)
|
||||
return CodeToInlineBuilder(symbolDescriptor, resolutionFacade).prepareCodeToInline(
|
||||
expression,
|
||||
emptyList(),
|
||||
::analyzeExpression,
|
||||
reformat
|
||||
)
|
||||
}
|
||||
|
||||
fun analyzeClassifierReplacement(
|
||||
|
||||
@@ -20,13 +20,13 @@ import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import com.intellij.refactoring.util.RefactoringMessageDialog
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInliner.CodeToInline
|
||||
import org.jetbrains.kotlin.idea.codeInliner.CodeToInlineBuilder
|
||||
import org.jetbrains.kotlin.idea.core.copied
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.ContainerChangeInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.ContainerInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages
|
||||
@@ -126,8 +126,9 @@ internal fun buildCodeToInline(
|
||||
else
|
||||
TypeUtils.NO_EXPECTED_TYPE
|
||||
|
||||
fun analyzeBodyCopy(): BindingContext = bodyCopy.analyzeInContext(
|
||||
bodyOrInitializer.getResolutionScope(),
|
||||
val scope by lazy { bodyOrInitializer.getResolutionScope() }
|
||||
fun analyzeExpressionInContext(expression: KtExpression): BindingContext = expression.analyzeInContext(
|
||||
scope = scope,
|
||||
contextExpression = bodyOrInitializer,
|
||||
expectedType = expectedType
|
||||
)
|
||||
@@ -141,7 +142,7 @@ internal fun buildCodeToInline(
|
||||
val returnStatements = bodyCopy.collectDescendantsOfType<KtReturnExpression> {
|
||||
val function = it.getStrictParentOfType<KtFunction>()
|
||||
if (function != null && function != declaration) return@collectDescendantsOfType false
|
||||
it.getLabelName().let { it == null || it == declaration.name }
|
||||
it.getLabelName().let { label -> label == null || label == declaration.name }
|
||||
}
|
||||
|
||||
val lastReturn = statements.lastOrNull() as? KtReturnExpression
|
||||
@@ -166,10 +167,10 @@ internal fun buildCodeToInline(
|
||||
|
||||
return builder.prepareCodeToInline(
|
||||
lastReturn?.returnedExpression,
|
||||
statements.dropLast(returnStatements.size), ::analyzeBodyCopy, reformat = true
|
||||
statements.dropLast(returnStatements.size), ::analyzeExpressionInContext, reformat = true
|
||||
)
|
||||
} else {
|
||||
return builder.prepareCodeToInline(bodyCopy, emptyList(), ::analyzeBodyCopy, reformat = true)
|
||||
return builder.prepareCodeToInline(bodyCopy, emptyList(), ::analyzeExpressionInContext, reformat = true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace with '{ this.bar }()'" "true"
|
||||
|
||||
private class A {
|
||||
val bar = 1
|
||||
|
||||
@Deprecated("t", ReplaceWith("{ this.bar }()"))
|
||||
fun foooo() {
|
||||
{ bar }()
|
||||
}
|
||||
}
|
||||
|
||||
private fun test(a: A) {
|
||||
a.<caret>foooo()
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace with '{ this.bar }()'" "true"
|
||||
|
||||
private class A {
|
||||
val bar = 1
|
||||
|
||||
@Deprecated("t", ReplaceWith("{ this.bar }()"))
|
||||
fun foooo() {
|
||||
{ bar }()
|
||||
}
|
||||
}
|
||||
|
||||
private fun test(a: A) {
|
||||
{ a.bar }()
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
private class A {
|
||||
val bar = 1
|
||||
val parent: A
|
||||
get() = null!!
|
||||
}
|
||||
|
||||
fun <T> myrun(f: () -> T) = f()
|
||||
|
||||
private fun A.<caret>foo() = myrun { bar }
|
||||
|
||||
private fun test(a: A) {
|
||||
a.foo()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
private class A {
|
||||
val bar = 1
|
||||
val parent: A
|
||||
get() = null!!
|
||||
}
|
||||
|
||||
fun <T> myrun(f: () -> T) = f()
|
||||
|
||||
private fun test(a: A) {
|
||||
myrun { a.bar }
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
private class A {
|
||||
fun <caret>foooo() {
|
||||
{ bar }()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val bar = 4
|
||||
}
|
||||
}
|
||||
|
||||
private fun test(a: A) {
|
||||
a.foooo()
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import A.Companion.bar
|
||||
|
||||
private class A {
|
||||
|
||||
companion object {
|
||||
const val bar = 4
|
||||
}
|
||||
}
|
||||
|
||||
private fun test(a: A) {
|
||||
{ bar }()
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package abc
|
||||
|
||||
private class A {
|
||||
fun <caret>foooo() {
|
||||
{ bar }()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val bar = 4
|
||||
}
|
||||
}
|
||||
|
||||
private fun test(a: A) {
|
||||
val bar = 42
|
||||
a.foooo()
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package abc
|
||||
|
||||
private class A {
|
||||
|
||||
companion object {
|
||||
const val bar = 4
|
||||
}
|
||||
}
|
||||
|
||||
private fun test(a: A) {
|
||||
val bar = 42
|
||||
{ A.bar }()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
private class A {
|
||||
val bar = 1
|
||||
fun <caret>foooo() {
|
||||
{ bar }()
|
||||
}
|
||||
}
|
||||
|
||||
private fun test(a: A) {
|
||||
a.foooo()
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
private class A {
|
||||
val bar = 1
|
||||
}
|
||||
|
||||
private fun test(a: A) {
|
||||
{ a.bar }()
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
private class A {
|
||||
val bar = 1
|
||||
fun <caret>foooo() {
|
||||
{ this.bar }()
|
||||
bar
|
||||
}
|
||||
}
|
||||
|
||||
private fun test(a: A) {
|
||||
a.foooo()
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
private class A {
|
||||
val bar = 1
|
||||
}
|
||||
|
||||
private fun test(a: A) {
|
||||
{ a.bar }()
|
||||
a.bar
|
||||
}
|
||||
+7
-7
@@ -10,13 +10,13 @@ object Foo {
|
||||
internal object Bar {
|
||||
@JvmStatic fun main(args: Array<String>) {
|
||||
bar(object : Runnable {
|
||||
override fun run() {
|
||||
doRun()
|
||||
}
|
||||
override fun run() {
|
||||
doRun()
|
||||
}
|
||||
|
||||
private fun doRun() {
|
||||
// Woo-hoo
|
||||
}
|
||||
})
|
||||
private fun doRun() {
|
||||
// Woo-hoo
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Vendored
-2
@@ -1,5 +1,3 @@
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:-NewInference
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
|
||||
+1
-3
@@ -1,7 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:-NewInference
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
fun f() {
|
||||
ArrayList(ArrayList<Int>(listOf()))
|
||||
ArrayList(ArrayList(listOf<Int>()))
|
||||
}
|
||||
|
||||
idea/testData/refactoring/inline/inlineVariableOrProperty/explicateTypeArgument/DeeperNestedCall2.kt
Vendored
-2
@@ -1,5 +1,3 @@
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:-NewInference
|
||||
|
||||
fun foo(f: List<Int>) {}
|
||||
|
||||
fun f() {
|
||||
|
||||
+1
-3
@@ -1,7 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:-NewInference
|
||||
|
||||
fun foo(f: List<Int>) {}
|
||||
|
||||
fun f() {
|
||||
foo(ArrayList<Int>(listOf()))
|
||||
foo(ArrayList(listOf()))
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ class DeprecatedSymbolUsageFixSpecialTest : KotlinLightCodeInsightFixtureTestCas
|
||||
override fun getProjectDescriptor() = ProjectDescriptorWithStdlibSources.INSTANCE
|
||||
|
||||
fun testMemberInCompiledClass() {
|
||||
doTest("matches(input)")
|
||||
doTest("this.matches(input)")
|
||||
}
|
||||
|
||||
fun testDefaultParameterValuesFromLibrary() {
|
||||
|
||||
@@ -6445,6 +6445,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
runTest("idea/testData/quickfix/deprecatedSymbolUsage/usageInDerivedClassGeneric.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withInnerFunction.kt")
|
||||
public void testWithInnerFunction() throws Exception {
|
||||
runTest("idea/testData/quickfix/deprecatedSymbolUsage/withInnerFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/argumentSideEffects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+25
@@ -55,6 +55,11 @@ public class InlineTestGenerated extends AbstractInlineTest {
|
||||
runTest("idea/testData/refactoring/inline/function/InStringTemplates.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Kt19459.kt")
|
||||
public void testKt19459() throws Exception {
|
||||
runTest("idea/testData/refactoring/inline/function/Kt19459.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("LocalCapturing.kt")
|
||||
public void testLocalCapturing() throws Exception {
|
||||
runTest("idea/testData/refactoring/inline/function/LocalCapturing.kt");
|
||||
@@ -85,6 +90,26 @@ public class InlineTestGenerated extends AbstractInlineTest {
|
||||
runTest("idea/testData/refactoring/inline/function/ReferenceReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ReferenceToCompanionInsideLambda.kt")
|
||||
public void testReferenceToCompanionInsideLambda() throws Exception {
|
||||
runTest("idea/testData/refactoring/inline/function/ReferenceToCompanionInsideLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ReferenceToCompanionInsideLambda2.kt")
|
||||
public void testReferenceToCompanionInsideLambda2() throws Exception {
|
||||
runTest("idea/testData/refactoring/inline/function/ReferenceToCompanionInsideLambda2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ReferenceToReceiverInsideLambda.kt")
|
||||
public void testReferenceToReceiverInsideLambda() throws Exception {
|
||||
runTest("idea/testData/refactoring/inline/function/ReferenceToReceiverInsideLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ReferenceToReceiverInsideLambdaWithExplicitThis.kt")
|
||||
public void testReferenceToReceiverInsideLambdaWithExplicitThis() throws Exception {
|
||||
runTest("idea/testData/refactoring/inline/function/ReferenceToReceiverInsideLambdaWithExplicitThis.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ReturnNotInTheEnd.kt")
|
||||
public void testReturnNotInTheEnd() throws Exception {
|
||||
runTest("idea/testData/refactoring/inline/function/ReturnNotInTheEnd.kt");
|
||||
|
||||
Reference in New Issue
Block a user