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.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.util.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import java.util.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
||||
|
||||
public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableDeclaration>(KtCallableDeclaration::class.java, "Specify type explicitly"), LowPriorityAction {
|
||||
override fun isApplicableTo(element: KtCallableDeclaration, caretOffset: Int): Boolean {
|
||||
@@ -65,14 +64,34 @@ public class SpecifyTypeExplicitlyIntention : SelfTargetingIntention<KtCallableD
|
||||
return type ?: ErrorUtils.createErrorType("null type")
|
||||
}
|
||||
|
||||
public fun createTypeExpressionForTemplate(exprType: KotlinType): Expression {
|
||||
val descriptor = exprType.constructor.declarationDescriptor
|
||||
val isAnonymous = descriptor != null && DescriptorUtils.isAnonymousObject(descriptor)
|
||||
public fun createTypeExpressionForTemplate(exprType: KotlinType, contextElement: KtElement): Expression {
|
||||
val resolutionFacade = contextElement.getResolutionFacade()
|
||||
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 types = if (isAnonymous) ArrayList<KotlinType>() else arrayListOf(exprType)
|
||||
types.addAll(allSupertypes)
|
||||
val newArguments = (it.arguments zip it.constructor.parameters).map {
|
||||
val (arg, param) = it
|
||||
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()) {
|
||||
override fun getLookupString(element: KotlinType) = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.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 }
|
||||
|
||||
val project = declaration.project
|
||||
val expression = createTypeExpressionForTemplate(exprType)
|
||||
val expression = createTypeExpressionForTemplate(exprType, declaration)
|
||||
|
||||
declaration.setType(KotlinBuiltIns.FQ_NAMES.any.asString())
|
||||
|
||||
|
||||
+1
-1
@@ -356,7 +356,7 @@ public class KotlinInplaceVariableIntroducer<D extends KtCallableDeclaration> ex
|
||||
if (typeReference != null) {
|
||||
builder.replaceElement(typeReference,
|
||||
TYPE_REFERENCE_VARIABLE_NAME,
|
||||
SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType),
|
||||
SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType, myDeclaration),
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ public class KotlinVariableInplaceIntroducer(
|
||||
addedVariable.typeReference?.let {
|
||||
builder.replaceElement(it,
|
||||
"TypeReferenceVariable",
|
||||
SpecifyTypeExplicitlyIntention.createTypeExpressionForTemplate(expressionType!!),
|
||||
SpecifyTypeExplicitlyIntention.createTypeExpressionForTemplate(expressionType!!, addedVariable),
|
||||
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);
|
||||
}
|
||||
|
||||
@TestMetadata("anonymousObject.kt")
|
||||
public void testAnonymousObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/anonymousObject.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("badCaretPosition.kt")
|
||||
public void testBadCaretPosition() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/badCaretPosition.kt");
|
||||
@@ -7942,6 +7948,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
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")
|
||||
public void testLoopParameter() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitly/loopParameter.kt");
|
||||
|
||||
Reference in New Issue
Block a user