Inline refactoring: improve resolve

#KT-39705 Fixed
#KT-19459 Fixed
This commit is contained in:
Dmitry Gridin
2020-06-19 18:16:27 +07:00
parent 5ad94daaa5
commit 360a5bf348
24 changed files with 232 additions and 61 deletions
@@ -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)
@@ -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 }()
}
+13
View File
@@ -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 }
}
@@ -0,0 +1,13 @@
private class A {
fun <caret>foooo() {
{ bar }()
}
companion object {
const val bar = 4
}
}
private fun test(a: A) {
a.foooo()
}
@@ -0,0 +1,12 @@
import A.Companion.bar
private class A {
companion object {
const val bar = 4
}
}
private fun test(a: A) {
{ bar }()
}
@@ -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()
}
@@ -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()
}
@@ -0,0 +1,7 @@
private class A {
val bar = 1
}
private fun test(a: A) {
{ a.bar }()
}
@@ -0,0 +1,11 @@
private class A {
val bar = 1
fun <caret>foooo() {
{ this.bar }()
bar
}
}
private fun test(a: A) {
a.foooo()
}
@@ -0,0 +1,8 @@
private class A {
val bar = 1
}
private fun test(a: A) {
{ a.bar }()
a.bar
}
@@ -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
}
})
}
}
@@ -1,5 +1,3 @@
// COMPILER_ARGUMENTS: -XXLanguage:-NewInference
import java.util.ArrayList
fun f() {
@@ -1,7 +1,5 @@
// COMPILER_ARGUMENTS: -XXLanguage:-NewInference
import java.util.ArrayList
fun f() {
ArrayList(ArrayList<Int>(listOf()))
ArrayList(ArrayList(listOf<Int>()))
}
@@ -1,5 +1,3 @@
// COMPILER_ARGUMENTS: -XXLanguage:-NewInference
fun foo(f: List<Int>) {}
fun f() {
@@ -1,7 +1,5 @@
// COMPILER_ARGUMENTS: -XXLanguage:-NewInference
fun foo(f: List<Int>) {}
fun f() {
foo(ArrayList<Int>(listOf()))
foo(ArrayList(listOf()))
}
@@ -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)
@@ -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");