Specify type explicitly: suggest also types from overridden

So #KT-22092 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-05-08 16:24:50 +03:00
parent bf15d22ef1
commit 9a1253c5c7
5 changed files with 54 additions and 8 deletions
@@ -119,7 +119,11 @@ class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention<KtCallableDec
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 bindingContext = resolutionFacade.analyze(contextElement, BodyResolveMode.PARTIAL)
val scope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
@@ -133,13 +137,24 @@ class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention<KtCallableDec
}
}
val types = with(exprType.getResolvableApproximations(scope, checkTypeParameters).toList()) {
when {
exprType.isNullabilityFlexible() -> flatMap {
listOf(TypeUtils.makeNotNullable(it), TypeUtils.makeNullable(it))
fun KotlinType.toResolvableApproximations(): List<KotlinType> =
with(getResolvableApproximations(scope, checkTypeParameters).toList()) {
when {
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 }
if (ApplicationManager.getApplication().isUnitTestMode) {
@@ -150,6 +165,14 @@ class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention<KtCallableDec
val targetType = types.firstOrNull { it.isMarkedNullable } ?: types.first()
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())
@@ -204,7 +227,7 @@ class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention<KtCallableDec
assert(!exprType.isError) { "Unexpected error type, should have been checked before: " + declaration.getElementTextWithContext() + ", type = " + exprType }
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())
@@ -343,7 +343,7 @@ public class KotlinInplaceVariableIntroducer<D extends KtCallableDeclaration> ex
protected void addTypeReferenceVariable(TemplateBuilderImpl builder) {
KtTypeReference typeReference = myDeclaration.getTypeReference();
Expression expression = SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType, myDeclaration);
Expression expression = SpecifyTypeExplicitlyIntention.Companion.createTypeExpressionForTemplate(myExprType, myDeclaration, false);
if (typeReference != null && expression != null) {
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");
}
@TestMetadata("overriddenAsNull.kt")
public void testOverriddenAsNull() throws Exception {
runTest("idea/testData/intentions/specifyTypeExplicitly/overriddenAsNull.kt");
}
@TestMetadata("overrideNotNullFunction.kt")
public void testOverrideNotNullFunction() throws Exception {
runTest("idea/testData/intentions/specifyTypeExplicitly/overrideNotNullFunction.kt");