diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/core/quickfix/QuickFixUtil.java b/idea/ide-common/src/org/jetbrains/kotlin/idea/core/quickfix/QuickFixUtil.java index dc31f67cd00..19d8aed34d1 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/core/quickfix/QuickFixUtil.java +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/core/quickfix/QuickFixUtil.java @@ -121,13 +121,23 @@ public class QuickFixUtil { return null; } + // Returns true iff parent's value always or sometimes is evaluable to child's value, e.g. + // parent = (x), child = x; + // parent = if (...) x else y, child = x; + // parent = y.x, child = x public static boolean canEvaluateTo(KtExpression parent, KtExpression child) { if (parent == null || child == null) { return false; } while (parent != child) { - if (child.getParent() instanceof KtParenthesizedExpression) { - child = (KtExpression) child.getParent(); + PsiElement childParent = child.getParent(); + if (childParent instanceof KtParenthesizedExpression) { + child = (KtExpression) childParent; + continue; + } + if (childParent instanceof KtDotQualifiedExpression && + (child instanceof KtCallExpression || child instanceof KtDotQualifiedExpression)) { + child = (KtExpression) childParent; continue; } child = getParentIfForBranch(child); diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt index 9d4cb7dc6f6..c8da7979c11 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt @@ -58,29 +58,36 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() { val expectedType: KotlinType val expressionType: KotlinType? - if (diagnostic.factory === Errors.TYPE_MISMATCH) { - val diagnosticWithParameters = Errors.TYPE_MISMATCH.cast(diagnostic) - expectedType = diagnosticWithParameters.a - expressionType = diagnosticWithParameters.b - } - else if (diagnostic.factory === Errors.NULL_FOR_NONNULL_TYPE) { - val diagnosticWithParameters = Errors.NULL_FOR_NONNULL_TYPE.cast(diagnostic) - expectedType = diagnosticWithParameters.a - expressionType = expectedType.makeNullable() - } - else if (diagnostic.factory === Errors.CONSTANT_EXPECTED_TYPE_MISMATCH) { - val diagnosticWithParameters = Errors.CONSTANT_EXPECTED_TYPE_MISMATCH.cast(diagnostic) - expectedType = diagnosticWithParameters.b - expressionType = context.getType(diagnosticElement) - if (expressionType == null) { - LOG.error("No type inferred: " + diagnosticElement.text) + when (diagnostic.factory) { + Errors.TYPE_MISMATCH -> { + val diagnosticWithParameters = Errors.TYPE_MISMATCH.cast(diagnostic) + expectedType = diagnosticWithParameters.a + expressionType = diagnosticWithParameters.b + } + Errors.NULL_FOR_NONNULL_TYPE -> { + val diagnosticWithParameters = Errors.NULL_FOR_NONNULL_TYPE.cast(diagnostic) + expectedType = diagnosticWithParameters.a + expressionType = expectedType.makeNullable() + } + Errors.TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH -> { + val diagnosticWithParameters = Errors.TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH.cast(diagnostic) + expectedType = diagnosticWithParameters.a + expressionType = diagnosticWithParameters.b + } + Errors.CONSTANT_EXPECTED_TYPE_MISMATCH -> { + val diagnosticWithParameters = Errors.CONSTANT_EXPECTED_TYPE_MISMATCH.cast(diagnostic) + expectedType = diagnosticWithParameters.b + expressionType = context.getType(diagnosticElement) + if (expressionType == null) { + LOG.error("No type inferred: " + diagnosticElement.text) + return emptyList() + } + } + else -> { + LOG.error("Unexpected diagnostic: " + DefaultErrorMessages.render(diagnostic)) return emptyList() } } - else { - LOG.error("Unexpected diagnostic: " + DefaultErrorMessages.render(diagnostic)) - return emptyList() - } if (expressionType.isPrimitiveNumberType() && expectedType.isPrimitiveNumberType()) { var wrongPrimitiveLiteralFix: WrongPrimitiveLiteralFix? = null diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index e3563f338a5..f2c5322dfb7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -332,6 +332,7 @@ class QuickFixRegistrar : QuickFixContributor { TYPE_MISMATCH.registerFactory(factoryForTypeMismatchError) NULL_FOR_NONNULL_TYPE.registerFactory(factoryForTypeMismatchError) CONSTANT_EXPECTED_TYPE_MISMATCH.registerFactory(factoryForTypeMismatchError) + TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH.registerFactory(factoryForTypeMismatchError) SMARTCAST_IMPOSSIBLE.registerFactory(SmartCastImpossibleExclExclFixFactory) SMARTCAST_IMPOSSIBLE.registerFactory(CastExpressionFix.SmartCastImpossibleFactory) diff --git a/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeInference.kt b/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeInference.kt new file mode 100644 index 00000000000..a777c62dd6f --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeInference.kt @@ -0,0 +1,6 @@ +// "Change return type of enclosing function 'test2' to 'List'" "true" +// WITH_RUNTIME + +fun test2(ss: List) { + return ss.map { it } +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeInference.kt.after b/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeInference.kt.after new file mode 100644 index 00000000000..82d2a0295e8 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeInference.kt.after @@ -0,0 +1,6 @@ +// "Change return type of enclosing function 'test2' to 'List'" "true" +// WITH_RUNTIME + +fun test2(ss: List): List { + return ss.map { it } +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeMismatch.kt b/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeMismatch.kt new file mode 100644 index 00000000000..fd18f30e947 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeMismatch.kt @@ -0,0 +1,6 @@ +// "Change return type of enclosing function 'test1' to 'Int'" "true" +// WITH_RUNTIME + +fun test1(ss: List) { + return ss.map { it }.size +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeMismatch.kt.after b/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeMismatch.kt.after new file mode 100644 index 00000000000..eda4072643e --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeMismatch.kt.after @@ -0,0 +1,6 @@ +// "Change return type of enclosing function 'test1' to 'Int'" "true" +// WITH_RUNTIME + +fun test1(ss: List): Int { + return ss.map { it }.size +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 7fea0a4d1ca..8da1d8756ee 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -9006,6 +9006,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("changeReturnTypeForTypeInference.kt") + public void testChangeReturnTypeForTypeInference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeInference.kt"); + doTest(fileName); + } + + @TestMetadata("changeReturnTypeForTypeMismatch.kt") + public void testChangeReturnTypeForTypeMismatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/changeReturnTypeForTypeMismatch.kt"); + doTest(fileName); + } + @TestMetadata("changeReturnTypeNoFqNameForAnonymousObject.kt") public void testChangeReturnTypeNoFqNameForAnonymousObject() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/changeReturnTypeNoFqNameForAnonymousObject.kt");