Specify type explicitly: suggest also types from overridden
So #KT-22092 Fixed
This commit is contained in:
@@ -119,7 +119,11 @@ class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention<KtCallableDec
|
|||||||
return type ?: ErrorUtils.createErrorType("null type")
|
return type ?: ErrorUtils.createErrorType("null type")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createTypeExpressionForTemplate(exprType: KotlinType, contextElement: KtElement): Expression? {
|
fun createTypeExpressionForTemplate(
|
||||||
|
exprType: KotlinType,
|
||||||
|
contextElement: KtDeclaration,
|
||||||
|
useTypesFromOverridden: Boolean = false
|
||||||
|
): Expression? {
|
||||||
val resolutionFacade = contextElement.getResolutionFacade()
|
val resolutionFacade = contextElement.getResolutionFacade()
|
||||||
val bindingContext = resolutionFacade.analyze(contextElement, BodyResolveMode.PARTIAL)
|
val bindingContext = resolutionFacade.analyze(contextElement, BodyResolveMode.PARTIAL)
|
||||||
val scope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
|
val scope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
|
||||||
@@ -133,13 +137,24 @@ class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention<KtCallableDec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val types = with(exprType.getResolvableApproximations(scope, checkTypeParameters).toList()) {
|
fun KotlinType.toResolvableApproximations(): List<KotlinType> =
|
||||||
when {
|
with(getResolvableApproximations(scope, checkTypeParameters).toList()) {
|
||||||
exprType.isNullabilityFlexible() -> flatMap {
|
when {
|
||||||
listOf(TypeUtils.makeNotNullable(it), TypeUtils.makeNullable(it))
|
exprType.isNullabilityFlexible() -> flatMap {
|
||||||
|
listOf(TypeUtils.makeNotNullable(it), TypeUtils.makeNullable(it))
|
||||||
|
}
|
||||||
|
else -> this
|
||||||
}
|
}
|
||||||
else -> this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val overriddenTypes: List<KotlinType> = if (!useTypesFromOverridden) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
val declarationDescriptor = contextElement.resolveToDescriptorIfAny() as? CallableDescriptor
|
||||||
|
declarationDescriptor?.overriddenDescriptors?.mapNotNull { it.returnType }
|
||||||
|
} ?: emptyList()
|
||||||
|
val types = (listOf(exprType) + overriddenTypes).distinct().flatMap {
|
||||||
|
it.toResolvableApproximations()
|
||||||
}.ifEmpty { return null }
|
}.ifEmpty { return null }
|
||||||
|
|
||||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
@@ -150,6 +165,14 @@ class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention<KtCallableDec
|
|||||||
val targetType = types.firstOrNull { it.isMarkedNullable } ?: types.first()
|
val targetType = types.firstOrNull { it.isMarkedNullable } ?: types.first()
|
||||||
return TypeChooseValueExpression(listOf(targetType), targetType)
|
return TypeChooseValueExpression(listOf(targetType), targetType)
|
||||||
}
|
}
|
||||||
|
// This helps to be sure something except Nothing is suggested
|
||||||
|
if (contextElement.containingKtFile.findDescendantOfType<PsiComment>()?.takeIf {
|
||||||
|
it.text == "// DO_NOT_CHOOSE_NOTHING"
|
||||||
|
} != null) {
|
||||||
|
val targetType = types.firstOrNull { !KotlinBuiltIns.isNothingOrNullableNothing(it) } ?: types.first()
|
||||||
|
return TypeChooseValueExpression(listOf(targetType), targetType)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return TypeChooseValueExpression(types, types.first())
|
return TypeChooseValueExpression(types, types.first())
|
||||||
@@ -204,7 +227,7 @@ class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention<KtCallableDec
|
|||||||
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, declaration) ?: return
|
val expression = createTypeExpressionForTemplate(exprType, declaration, useTypesFromOverridden = true) ?: return
|
||||||
|
|
||||||
declaration.setType(KotlinBuiltIns.FQ_NAMES.any.asString())
|
declaration.setType(KotlinBuiltIns.FQ_NAMES.any.asString())
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -343,7 +343,7 @@ public class KotlinInplaceVariableIntroducer<D extends KtCallableDeclaration> ex
|
|||||||
|
|
||||||
protected void addTypeReferenceVariable(TemplateBuilderImpl builder) {
|
protected void addTypeReferenceVariable(TemplateBuilderImpl builder) {
|
||||||
KtTypeReference typeReference = myDeclaration.getTypeReference();
|
KtTypeReference typeReference = myDeclaration.getTypeReference();
|
||||||
Expression expression = SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType, myDeclaration);
|
Expression expression = SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType, myDeclaration, false);
|
||||||
if (typeReference != null && expression != null) {
|
if (typeReference != null && expression != null) {
|
||||||
builder.replaceElement(typeReference, TYPE_REFERENCE_VARIABLE_NAME, expression, false);
|
builder.replaceElement(typeReference, TYPE_REFERENCE_VARIABLE_NAME, expression, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// DO_NOT_CHOOSE_NOTHING
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
fun foo(): String?
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test : I {
|
||||||
|
override fun foo()<caret> = null
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// DO_NOT_CHOOSE_NOTHING
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
fun foo(): String?
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test : I {
|
||||||
|
override fun foo(): String? = null
|
||||||
|
}
|
||||||
@@ -14574,6 +14574,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
runTest("idea/testData/intentions/specifyTypeExplicitly/loopParameter.kt");
|
runTest("idea/testData/intentions/specifyTypeExplicitly/loopParameter.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("overriddenAsNull.kt")
|
||||||
|
public void testOverriddenAsNull() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/specifyTypeExplicitly/overriddenAsNull.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("overrideNotNullFunction.kt")
|
@TestMetadata("overrideNotNullFunction.kt")
|
||||||
public void testOverrideNotNullFunction() throws Exception {
|
public void testOverrideNotNullFunction() throws Exception {
|
||||||
runTest("idea/testData/intentions/specifyTypeExplicitly/overrideNotNullFunction.kt");
|
runTest("idea/testData/intentions/specifyTypeExplicitly/overrideNotNullFunction.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user