From 16d9abf5466979d7f0a5c5824ac560e80e95e2c0 Mon Sep 17 00:00:00 2001 From: Wojciech Lopata Date: Wed, 15 May 2013 23:02:36 +0200 Subject: [PATCH] QuickFix: change type of overridden function to type of overridding function --- .../quickfix/ChangeFunctionReturnTypeFix.java | 51 +++++++++++++++---- ...terChangeReturnTypeOfOverriddenFunction.kt | 12 +++++ ...antChangeReturnTypeOfOverriddenFunction.kt | 13 +++++ ...oreChangeReturnTypeOfOverriddenFunction.kt | 12 +++++ .../quickfix/QuickFixTestGenerated.java | 10 ++++ 5 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 idea/testData/quickfix/override/typeMismatchOnOverride/afterChangeReturnTypeOfOverriddenFunction.kt create mode 100644 idea/testData/quickfix/override/typeMismatchOnOverride/beforeCantChangeReturnTypeOfOverriddenFunction.kt create mode 100644 idea/testData/quickfix/override/typeMismatchOnOverride/beforeChangeReturnTypeOfOverriddenFunction.kt diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java index b9483a3e818..c9a974790aa 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor; import org.jetbrains.jet.lang.diagnostics.Diagnostic; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters3; import org.jetbrains.jet.lang.psi.*; @@ -38,6 +39,7 @@ import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.plugin.JetBundle; import org.jetbrains.jet.plugin.caches.resolve.KotlinCacheManagerUtil; @@ -45,6 +47,9 @@ import org.jetbrains.jet.plugin.intentions.SpecifyTypeExplicitlyAction; import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; import org.jetbrains.jet.renderer.DescriptorRenderer; +import java.util.LinkedList; +import java.util.List; + public class ChangeFunctionReturnTypeFix extends JetIntentionAction { private final JetType type; private final String renderedType; @@ -175,7 +180,8 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction JetBinaryExpression expression = QuickFixUtil.getParentElementOfType(diagnostic, JetBinaryExpression.class); assert expression != null : "COMPARE_TO_TYPE_MISMATCH reported on element that is not within any expression"; BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) expression.getContainingFile()).getBindingContext(); - ResolvedCall resolvedCall = context.get(BindingContext.RESOLVED_CALL, expression.getOperationReference()); + ResolvedCall resolvedCall = context.get(BindingContext.RESOLVED_CALL, + expression.getOperationReference()); if (resolvedCall == null) return null; PsiElement compareTo = BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getCandidateDescriptor()); if (!(compareTo instanceof JetFunction)) return null; @@ -185,16 +191,43 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction } @NotNull - public static JetSingleIntentionActionFactory createFactoryForReturnTypeMismatchOnOverride() { - return new JetSingleIntentionActionFactory() { - @Nullable + public static JetIntentionActionsFactory createFactoryForReturnTypeMismatchOnOverride() { + return new JetIntentionActionsFactory() { + @NotNull @Override - public IntentionAction createAction(Diagnostic diagnostic) { + public List createActions(Diagnostic diagnostic) { + List actions = new LinkedList(); + JetFunction function = QuickFixUtil.getParentElementOfType(diagnostic, JetFunction.class); - if (function == null) return null; - BindingContext context = KotlinCacheManagerUtil.getDeclarationsBindingContext(function); - JetType matchingReturnType = QuickFixUtil.findLowerBoundOfOverriddenCallablesReturnTypes(context, function); - return matchingReturnType == null ? null : new ChangeFunctionReturnTypeFix(function, matchingReturnType); + if (function != null) { + BindingContext context = KotlinCacheManagerUtil.getDeclarationsBindingContext(function); + JetType matchingReturnType = QuickFixUtil.findLowerBoundOfOverriddenCallablesReturnTypes(context, function); + if (matchingReturnType != null) { + actions.add(new ChangeFunctionReturnTypeFix(function, matchingReturnType)); + } + + SimpleFunctionDescriptor descriptor = context.get(BindingContext.FUNCTION, function); + if (descriptor == null) return actions; + JetType functionType = descriptor.getReturnType(); + if (functionType == null) return actions; + + List overriddenMismatchingFunctions = new LinkedList(); + for (FunctionDescriptor overriddenFunction: descriptor.getOverriddenDescriptors()) { + JetType overriddenFunctionType = overriddenFunction.getReturnType(); + if (overriddenFunctionType == null) continue; + if (!JetTypeChecker.INSTANCE.isSubtypeOf(functionType, overriddenFunctionType)) { + overriddenMismatchingFunctions.add(overriddenFunction); + } + } + + if (overriddenMismatchingFunctions.size() == 1) { + PsiElement overriddenFunction = BindingContextUtils.descriptorToDeclaration(context, overriddenMismatchingFunctions.get(0)); + if (overriddenFunction instanceof JetFunction) { + actions.add(new ChangeFunctionReturnTypeFix((JetFunction) overriddenFunction, functionType)); + } + } + } + return actions; } }; } diff --git a/idea/testData/quickfix/override/typeMismatchOnOverride/afterChangeReturnTypeOfOverriddenFunction.kt b/idea/testData/quickfix/override/typeMismatchOnOverride/afterChangeReturnTypeOfOverriddenFunction.kt new file mode 100644 index 00000000000..0efefb16139 --- /dev/null +++ b/idea/testData/quickfix/override/typeMismatchOnOverride/afterChangeReturnTypeOfOverriddenFunction.kt @@ -0,0 +1,12 @@ +// "Change 'A.foo' function return type to 'Long'" "true" +trait A { + fun foo(): Long +} + +trait B { + fun foo(): Number +} + +trait C : A, B { + override fun foo(): Long +} \ No newline at end of file diff --git a/idea/testData/quickfix/override/typeMismatchOnOverride/beforeCantChangeReturnTypeOfOverriddenFunction.kt b/idea/testData/quickfix/override/typeMismatchOnOverride/beforeCantChangeReturnTypeOfOverriddenFunction.kt new file mode 100644 index 00000000000..302ed8d5be4 --- /dev/null +++ b/idea/testData/quickfix/override/typeMismatchOnOverride/beforeCantChangeReturnTypeOfOverriddenFunction.kt @@ -0,0 +1,13 @@ +// "Change 'A.foo' function return type to 'Long'" "false" +// ERROR: Return type is 'jet.Long', which is not a subtype of overridden
internal abstract fun foo(): jet.Int defined in A +trait A { + fun foo(): Int +} + +trait B { + fun foo(): String +} + +trait C : A, B { + override fun foo(): Long +} \ No newline at end of file diff --git a/idea/testData/quickfix/override/typeMismatchOnOverride/beforeChangeReturnTypeOfOverriddenFunction.kt b/idea/testData/quickfix/override/typeMismatchOnOverride/beforeChangeReturnTypeOfOverriddenFunction.kt new file mode 100644 index 00000000000..a8ccbc2551c --- /dev/null +++ b/idea/testData/quickfix/override/typeMismatchOnOverride/beforeChangeReturnTypeOfOverriddenFunction.kt @@ -0,0 +1,12 @@ +// "Change 'A.foo' function return type to 'Long'" "true" +trait A { + fun foo(): Int +} + +trait B { + fun foo(): Number +} + +trait C : A, B { + override fun foo(): Long +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java index 9b22cc5d85a..8e766d00b3a 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java @@ -1033,6 +1033,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest("idea/testData/quickfix/override/typeMismatchOnOverride/beforeCantChangePropertyTypeToMatchOverridenProperties.kt"); } + @TestMetadata("beforeCantChangeReturnTypeOfOverriddenFunction.kt") + public void testCantChangeReturnTypeOfOverriddenFunction() throws Exception { + doTest("idea/testData/quickfix/override/typeMismatchOnOverride/beforeCantChangeReturnTypeOfOverriddenFunction.kt"); + } + @TestMetadata("beforeChangeOverriddenPropertyType1.kt") public void testChangeOverriddenPropertyType1() throws Exception { doTest("idea/testData/quickfix/override/typeMismatchOnOverride/beforeChangeOverriddenPropertyType1.kt"); @@ -1048,6 +1053,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest("idea/testData/quickfix/override/typeMismatchOnOverride/beforeChangeOverridingPropertyTypeToFunctionType.kt"); } + @TestMetadata("beforeChangeReturnTypeOfOverriddenFunction.kt") + public void testChangeReturnTypeOfOverriddenFunction() throws Exception { + doTest("idea/testData/quickfix/override/typeMismatchOnOverride/beforeChangeReturnTypeOfOverriddenFunction.kt"); + } + @TestMetadata("beforePropertyReturnTypeMismatchOnOverride.kt") public void testPropertyReturnTypeMismatchOnOverride() throws Exception { doTest("idea/testData/quickfix/override/typeMismatchOnOverride/beforePropertyReturnTypeMismatchOnOverride.kt");