Specify Type Explicitly: Filter out types which can't be resolved in the target scope
#KT-10066 Fixed
This commit is contained in:
@@ -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())
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user