Add type arguments more accurately during inlining

This commit is contained in:
Mikhail Glukhikh
2017-04-12 16:43:49 +03:00
parent f8e1f5e613
commit b8cc7c2ca6
9 changed files with 68 additions and 24 deletions
@@ -25,6 +25,8 @@ import org.jetbrains.kotlin.idea.core.asExpression
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.intentions.InsertExplicitTypeArgumentsIntention import org.jetbrains.kotlin.idea.intentions.InsertExplicitTypeArgumentsIntention
import org.jetbrains.kotlin.idea.intentions.SpecifyExplicitLambdaSignatureIntention import org.jetbrains.kotlin.idea.intentions.SpecifyExplicitLambdaSignatureIntention
import org.jetbrains.kotlin.idea.refactoring.addTypeArgumentsIfNeeded
import org.jetbrains.kotlin.idea.refactoring.getQualifiedTypeArgumentList
import org.jetbrains.kotlin.idea.references.canBeResolvedViaImport import org.jetbrains.kotlin.idea.references.canBeResolvedViaImport
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
@@ -57,15 +59,13 @@ class CodeToInlineBuilder(
analyze: () -> BindingContext, analyze: () -> BindingContext,
importFqNames: Collection<FqName> = emptyList() importFqNames: Collection<FqName> = emptyList()
): CodeToInline { ): CodeToInline {
var bindingContext = analyze() val bindingContext = analyze()
val codeToInline = MutableCodeToInline(mainExpression, statementsBefore.toMutableList(), importFqNames.toMutableSet()) val codeToInline = MutableCodeToInline(mainExpression, statementsBefore.toMutableList(), importFqNames.toMutableSet())
bindingContext = insertExplicitTypeArguments(codeToInline, bindingContext, analyze)
insertExplicitReceivers(codeToInline, bindingContext) insertExplicitReceivers(codeToInline, bindingContext)
if (mainExpression != null) { if (mainExpression != null && mainExpression in codeToInline) {
val functionLiteralExpression = mainExpression.unpackFunctionLiteral(true) val functionLiteralExpression = mainExpression.unpackFunctionLiteral(true)
if (functionLiteralExpression != null) { if (functionLiteralExpression != null) {
val functionLiteralParameterTypes = getParametersForFunctionLiteral(functionLiteralExpression, bindingContext) val functionLiteralParameterTypes = getParametersForFunctionLiteral(functionLiteralExpression, bindingContext)
@@ -75,8 +75,15 @@ class CodeToInlineBuilder(
} }
} }
} }
val typeArgumentsForCall = getQualifiedTypeArgumentList(mainExpression, bindingContext)
if (typeArgumentsForCall != null) {
codeToInline.addPostInsertionAction(mainExpression) { inlinedExpression ->
addTypeArgumentsIfNeeded(inlinedExpression, typeArgumentsForCall)
}
}
} }
return codeToInline.toNonMutable() return codeToInline.toNonMutable()
} }
@@ -117,24 +124,6 @@ class CodeToInlineBuilder(
} }
} }
private fun insertExplicitTypeArguments(codeToInline: MutableCodeToInline, bindingContext: BindingContext, analyze: () -> BindingContext): BindingContext {
val typeArgsToAdd = ArrayList<Pair<KtCallExpression, KtTypeArgumentList>>()
codeToInline.forEachDescendantOfType<KtCallExpression> {
if (InsertExplicitTypeArgumentsIntention.isApplicableTo(it, bindingContext)) {
typeArgsToAdd.add(it to InsertExplicitTypeArgumentsIntention.createTypeArguments(it, bindingContext)!!)
}
}
if (typeArgsToAdd.isEmpty()) return bindingContext
for ((callExpr, typeArgs) in typeArgsToAdd) {
callExpr.addAfter(typeArgs, callExpr.calleeExpression)
}
// reanalyze expression - new usages of type parameters may be added
return analyze()
}
private fun insertExplicitReceivers(codeToInline: MutableCodeToInline, bindingContext: BindingContext) { private fun insertExplicitReceivers(codeToInline: MutableCodeToInline, bindingContext: BindingContext) {
val receiversToAdd = ArrayList<Pair<KtExpression, KtExpression>>() val receiversToAdd = ArrayList<Pair<KtExpression, KtExpression>>()
@@ -778,8 +778,10 @@ fun dropOverrideKeywordIfNecessary(element: KtNamedDeclaration) {
} }
} }
fun getQualifiedTypeArgumentList(initializer: KtExpression): KtTypeArgumentList? { fun getQualifiedTypeArgumentList(
val context = initializer.analyze(BodyResolveMode.PARTIAL) initializer: KtExpression,
context: BindingContext = initializer.analyze(BodyResolveMode.PARTIAL)
): KtTypeArgumentList? {
val call = initializer.getResolvedCall(context) ?: return null val call = initializer.getResolvedCall(context) ?: return null
val typeArgumentMap = call.typeArguments val typeArgumentMap = call.typeArguments
val typeArguments = call.candidateDescriptor.typeParameters.mapNotNull { typeArgumentMap[it] } val typeArguments = call.candidateDescriptor.typeParameters.mapNotNull { typeArgumentMap[it] }
@@ -0,0 +1,5 @@
fun bar(): List<Int> = listOf()
fun f() {
val vv = <caret>bar()
}
@@ -0,0 +1,3 @@
fun f() {
val vv = listOf<Int>()
}
@@ -0,0 +1,10 @@
interface SomeFace
interface GeneOut<out T> {}
object Empty : GeneOut<Nothing>
fun <T> downUnder(): GeneOut<T> = Empty
fun downParameter(p: GeneOut<SomeFace>) = p
fun callDown() {
// BUG: KT-17402
val v2 = <caret>downParameter(downUnder())
}
@@ -0,0 +1,9 @@
interface SomeFace
interface GeneOut<out T> {}
object Empty : GeneOut<Nothing>
fun <T> downUnder(): GeneOut<T> = Empty
fun callDown() {
// BUG: KT-17402
val v2 = downUnder()
}
@@ -0,0 +1,5 @@
fun bar(): List<Int> = listOf()
fun ff() {
val copy: List<Int> = ArrayList(<caret>bar())
}
@@ -0,0 +1,3 @@
fun ff() {
val copy: List<Int> = ArrayList(listOf())
}
@@ -124,6 +124,24 @@ public class InlineTestGenerated extends AbstractInlineTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("explicitTypeArgument.kt")
public void testExplicitTypeArgument() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/explicitTypeArgument.kt");
doTest(fileName);
}
@TestMetadata("explicitTypeArgumentComplex.kt")
public void testExplicitTypeArgumentComplex() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/explicitTypeArgumentComplex.kt");
doTest(fileName);
}
@TestMetadata("explicitTypeArgumentNotNeeded.kt")
public void testExplicitTypeArgumentNotNeeded() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/explicitTypeArgumentNotNeeded.kt");
doTest(fileName);
}
@TestMetadata("FromUsage.kt") @TestMetadata("FromUsage.kt")
public void testFromUsage() throws Exception { public void testFromUsage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/FromUsage.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/FromUsage.kt");