Specify Type Explicitly: Filter out types which can't be resolved in the target scope

#KT-10066 Fixed
This commit is contained in:
Alexey Sedunov
2015-12-24 14:51:44 +03:00
committed by Alexey
parent 21fd894d75
commit 00cc9d4150
8 changed files with 65 additions and 16 deletions
@@ -23,16 +23,15 @@ import com.intellij.psi.PsiDocumentManager
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.util.ShortenReferences import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.utils.SmartSet
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.utils.addToStdlib.singletonList
import java.util.*
public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableDeclaration>(KtCallableDeclaration::class.java, "Specify type explicitly"), LowPriorityAction { public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableDeclaration>(KtCallableDeclaration::class.java, "Specify type explicitly"), LowPriorityAction {
override fun isApplicableTo(element: KtCallableDeclaration, caretOffset: Int): Boolean { override fun isApplicableTo(element: KtCallableDeclaration, caretOffset: Int): Boolean {
@@ -65,14 +64,34 @@ public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableD
return type ?: ErrorUtils.createErrorType("null type") return type ?: ErrorUtils.createErrorType("null type")
} }
public fun createTypeExpressionForTemplate(exprType: KotlinType): Expression { public fun createTypeExpressionForTemplate(exprType: KotlinType, contextElement: KtElement): Expression {
val descriptor = exprType.constructor.declarationDescriptor val resolutionFacade = contextElement.getResolutionFacade()
val isAnonymous = descriptor != null && DescriptorUtils.isAnonymousObject(descriptor) val bindingContext = resolutionFacade.analyze(contextElement, BodyResolveMode.PARTIAL)
val scope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
val types = (exprType.singletonList() + TypeUtils.getAllSupertypes(exprType))
.filter { it.isResolvableInScope(scope, true) }
.mapNotNull mapArgs@ {
val resolvableArgs = it.arguments.filterTo(SmartSet.create()) { it.type.isResolvableInScope(scope, true) }
if (resolvableArgs.containsAll(it.arguments)) return@mapArgs it
val allSupertypes = TypeUtils.getAllSupertypes(exprType) val newArguments = (it.arguments zip it.constructor.parameters).map {
val types = if (isAnonymous) ArrayList<KotlinType>() else arrayListOf(exprType) val (arg, param) = it
types.addAll(allSupertypes) when {
arg in resolvableArgs -> arg
arg.projectionKind == Variance.OUT_VARIANCE ||
param.variance == Variance.OUT_VARIANCE -> TypeProjectionImpl(
arg.projectionKind,
arg.type.approximateWithResolvableType(scope, true)
)
else -> return@mapArgs null
}
}
object: DelegatingType() {
override fun getDelegate() = it
override fun getArguments() = newArguments
}
}
return object : ChooseValueExpression<KotlinType>(types, types.first()) { return object : ChooseValueExpression<KotlinType>(types, types.first()) {
override fun getLookupString(element: KotlinType) = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(element) override fun getLookupString(element: KotlinType) = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(element)
override fun getResult(element: KotlinType) = IdeDescriptorRenderers.SOURCE_CODE.renderType(element) override fun getResult(element: KotlinType) = IdeDescriptorRenderers.SOURCE_CODE.renderType(element)
@@ -103,7 +122,7 @@ public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableD
assert(!exprType.isError) { "Unexpected error type, should have been checked before: " + declaration.getElementTextWithContext() + ", type = " + exprType } assert(!exprType.isError) { "Unexpected error type, should have been checked before: " + declaration.getElementTextWithContext() + ", type = " + exprType }
val project = declaration.project val project = declaration.project
val expression = createTypeExpressionForTemplate(exprType) val expression = createTypeExpressionForTemplate(exprType, declaration)
declaration.setType(KotlinBuiltIns.FQ_NAMES.any.asString()) declaration.setType(KotlinBuiltIns.FQ_NAMES.any.asString())
@@ -356,7 +356,7 @@ public class KotlinInplaceVariableIntroducer<D extends KtCallableDeclaration> ex
if (typeReference != null) { if (typeReference != null) {
builder.replaceElement(typeReference, builder.replaceElement(typeReference,
TYPE_REFERENCE_VARIABLE_NAME, TYPE_REFERENCE_VARIABLE_NAME,
SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType), SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType, myDeclaration),
false); false);
} }
} }
@@ -109,7 +109,7 @@ public class KotlinVariableInplaceIntroducer(
addedVariable.typeReference?.let { addedVariable.typeReference?.let {
builder.replaceElement(it, builder.replaceElement(it,
"TypeReferenceVariable", "TypeReferenceVariable",
SpecifyTypeExplicitlyIntention.createTypeExpressionForTemplate(expressionType!!), SpecifyTypeExplicitlyIntention.createTypeExpressionForTemplate(expressionType!!, addedVariable),
false) false)
} }
} }
@@ -0,0 +1,3 @@
class A {
private fun foo() <caret>= { object {} }
}
@@ -0,0 +1,3 @@
class A {
private fun foo(): () -> Any<caret> = { object {} }
}
@@ -0,0 +1,6 @@
class A {
private fun bar() <caret>= {
class Local()
Local()
}
}
@@ -0,0 +1,6 @@
class A {
private fun bar(): () -> Any<caret> = {
class Local()
Local()
}
}
@@ -7906,6 +7906,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/specifyTypeExplicitly"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/specifyTypeExplicitly"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
} }
@TestMetadata("anonymousObject.kt")
public void testAnonymousObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/anonymousObject.kt");
doTest(fileName);
}
@TestMetadata("badCaretPosition.kt") @TestMetadata("badCaretPosition.kt")
public void testBadCaretPosition() throws Exception { public void testBadCaretPosition() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/badCaretPosition.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/badCaretPosition.kt");
@@ -7942,6 +7948,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("localClass.kt")
public void testLocalClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/localClass.kt");
doTest(fileName);
}
@TestMetadata("loopParameter.kt") @TestMetadata("loopParameter.kt")
public void testLoopParameter() throws Exception { public void testLoopParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/loopParameter.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/loopParameter.kt");