"Change to return with label" quick fix: apply for type mismatch

#KT-32280 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-12-18 11:33:52 +09:00
committed by Yan Zhulanow
parent 016e78e483
commit f76e98868c
11 changed files with 145 additions and 3 deletions
@@ -9,15 +9,19 @@ import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.util.findLabelAndCall import org.jetbrains.kotlin.idea.util.findLabelAndCall
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
class ChangeToLabeledReturnFix( class ChangeToLabeledReturnFix(
element: KtReturnExpression, val labeledReturn: String element: KtReturnExpression, val labeledReturn: String
@@ -56,9 +60,24 @@ class ChangeToLabeledReturnFix(
} }
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> { override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> {
val expression = diagnostic.psiElement as? KtReturnExpression ?: return emptyList() val element = diagnostic.psiElement as? KtElement ?: return emptyList()
return findAccessibleLabels(expression.analyze(), expression).map { val context by lazy { element.analyze() }
ChangeToLabeledReturnFix(expression, labeledReturn = "return@${it.render()}") val returnExpression = when (diagnostic.factory) {
Errors.RETURN_NOT_ALLOWED -> {
diagnostic.psiElement as? KtReturnExpression
}
Errors.TYPE_MISMATCH, Errors.CONSTANT_EXPECTED_TYPE_MISMATCH, Errors.NULL_FOR_NONNULL_TYPE -> {
val returnExpression = diagnostic.psiElement.getStrictParentOfType<KtReturnExpression>() ?: return emptyList()
val lambda = returnExpression.getStrictParentOfType<KtLambdaExpression>() ?: return emptyList()
val lambdaReturnType = context[BindingContext.FUNCTION, lambda.functionLiteral]?.returnType ?: return emptyList()
val returnType = returnExpression.returnedExpression?.getType(context) ?: return emptyList()
if (!returnType.isSubtypeOf(lambdaReturnType)) return emptyList()
returnExpression
}
else -> null
} ?: return emptyList()
return findAccessibleLabels(context, returnExpression).map {
ChangeToLabeledReturnFix(returnExpression, labeledReturn = "return@${it.render()}")
} }
} }
} }
@@ -570,6 +570,9 @@ class QuickFixRegistrar : QuickFixContributor {
MUST_BE_INITIALIZED_OR_BE_ABSTRACT.registerFactory(AddModifierFix.AddLateinitFactory) MUST_BE_INITIALIZED_OR_BE_ABSTRACT.registerFactory(AddModifierFix.AddLateinitFactory)
RETURN_NOT_ALLOWED.registerFactory(ChangeToLabeledReturnFix) RETURN_NOT_ALLOWED.registerFactory(ChangeToLabeledReturnFix)
TYPE_MISMATCH.registerFactory(ChangeToLabeledReturnFix)
CONSTANT_EXPECTED_TYPE_MISMATCH.registerFactory(ChangeToLabeledReturnFix)
NULL_FOR_NONNULL_TYPE.registerFactory(ChangeToLabeledReturnFix)
WRONG_ANNOTATION_TARGET.registerFactory(AddAnnotationTargetFix) WRONG_ANNOTATION_TARGET.registerFactory(AddAnnotationTargetFix)
WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET.registerFactory(MoveReceiverAnnotationFix, AddAnnotationTargetFix) WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET.registerFactory(MoveReceiverAnnotationFix, AddAnnotationTargetFix)
@@ -0,0 +1,9 @@
// "Change to 'return@foo'" "true"
inline fun foo(f: (Int) -> Int) {}
fun test() {
foo { i ->
if (i == 1) return 1<caret>
0
}
}
@@ -0,0 +1,9 @@
// "Change to 'return@foo'" "true"
inline fun foo(f: (Int) -> Int) {}
fun test() {
foo { i ->
if (i == 1) return@foo 1
0
}
}
@@ -0,0 +1,11 @@
// "Change to 'return@foo'" "true"
inline fun foo(f: (Int) -> Int?) {}
fun baz(): Int = 0
fun test() {
foo { i ->
if (i == 1) return null<caret>
0
}
}
@@ -0,0 +1,11 @@
// "Change to 'return@foo'" "true"
inline fun foo(f: (Int) -> Int?) {}
fun baz(): Int = 0
fun test() {
foo { i ->
if (i == 1) return@foo null
0
}
}
@@ -0,0 +1,14 @@
// "Change to 'return@foo'" "false"
// ACTION: Add braces to 'if' statement
// ACTION: Change return type of enclosing function 'test' to 'Unit?'
// DISABLE-ERRORS
inline fun foo(f: (Int) -> Int) {}
fun baz(): Int = 0
fun test() {
foo { i ->
if (i == 1) return null<caret>
0
}
}
@@ -0,0 +1,11 @@
// "Change to 'return@foo'" "true"
inline fun foo(f: (Int) -> Int) {}
fun baz(): Int = 0
fun test() {
foo { i ->
if (i == 1) return baz()<caret>
0
}
}
@@ -0,0 +1,11 @@
// "Change to 'return@foo'" "true"
inline fun foo(f: (Int) -> Int) {}
fun baz(): Int = 0
fun test() {
foo { i ->
if (i == 1) return@foo baz()
0
}
}
@@ -0,0 +1,19 @@
// "Change to 'return@foo'" "false"
// ACTION: Add braces to 'if' statement
// ACTION: Change parameter 'f' type of function 'foo' to '(Int) -> Unit'
// ACTION: Change return type of called function 'baz' to 'Unit'
// ACTION: Change return type of enclosing function 'test' to 'String'
// ACTION: Convert to single-line lambda
// ACTION: Enable a trailing comma by default in the formatter
// ACTION: Move lambda argument into parentheses
// ACTION: Specify explicit lambda signature
// DISABLE-ERRORS
inline fun foo(f: (Int) -> Int) {}
fun baz(): String = ""
fun test() {
foo { i ->
if (i == 1) return baz()<caret>
}
}
@@ -2687,6 +2687,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/changeToLabeledReturn"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true); KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/changeToLabeledReturn"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
} }
@TestMetadata("constantExpectedTypeMismatch.kt")
public void testConstantExpectedTypeMismatch() throws Exception {
runTest("idea/testData/quickfix/changeToLabeledReturn/constantExpectedTypeMismatch.kt");
}
@TestMetadata("multipleInner.kt") @TestMetadata("multipleInner.kt")
public void testMultipleInner() throws Exception { public void testMultipleInner() throws Exception {
runTest("idea/testData/quickfix/changeToLabeledReturn/multipleInner.kt"); runTest("idea/testData/quickfix/changeToLabeledReturn/multipleInner.kt");
@@ -2706,6 +2711,26 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
public void testNormal() throws Exception { public void testNormal() throws Exception {
runTest("idea/testData/quickfix/changeToLabeledReturn/normal.kt"); runTest("idea/testData/quickfix/changeToLabeledReturn/normal.kt");
} }
@TestMetadata("nullForNonnullType.kt")
public void testNullForNonnullType() throws Exception {
runTest("idea/testData/quickfix/changeToLabeledReturn/nullForNonnullType.kt");
}
@TestMetadata("nullForNonnullType2.kt")
public void testNullForNonnullType2() throws Exception {
runTest("idea/testData/quickfix/changeToLabeledReturn/nullForNonnullType2.kt");
}
@TestMetadata("typeMismatch.kt")
public void testTypeMismatch() throws Exception {
runTest("idea/testData/quickfix/changeToLabeledReturn/typeMismatch.kt");
}
@TestMetadata("typeMismatch2.kt")
public void testTypeMismatch2() throws Exception {
runTest("idea/testData/quickfix/changeToLabeledReturn/typeMismatch2.kt");
}
} }
@TestMetadata("idea/testData/quickfix/changeToMutableCollection") @TestMetadata("idea/testData/quickfix/changeToMutableCollection")