QuickFix: change type of overridden function to type of overridding function
This commit is contained in:
@@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
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.Diagnostic;
|
||||||
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters3;
|
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters3;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
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.FqName;
|
||||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
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.lang.types.lang.KotlinBuiltIns;
|
||||||
import org.jetbrains.jet.plugin.JetBundle;
|
import org.jetbrains.jet.plugin.JetBundle;
|
||||||
import org.jetbrains.jet.plugin.caches.resolve.KotlinCacheManagerUtil;
|
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.plugin.project.AnalyzerFacadeWithCache;
|
||||||
import org.jetbrains.jet.renderer.DescriptorRenderer;
|
import org.jetbrains.jet.renderer.DescriptorRenderer;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction> {
|
public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction> {
|
||||||
private final JetType type;
|
private final JetType type;
|
||||||
private final String renderedType;
|
private final String renderedType;
|
||||||
@@ -175,7 +180,8 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
|
|||||||
JetBinaryExpression expression = QuickFixUtil.getParentElementOfType(diagnostic, JetBinaryExpression.class);
|
JetBinaryExpression expression = QuickFixUtil.getParentElementOfType(diagnostic, JetBinaryExpression.class);
|
||||||
assert expression != null : "COMPARE_TO_TYPE_MISMATCH reported on element that is not within any expression";
|
assert expression != null : "COMPARE_TO_TYPE_MISMATCH reported on element that is not within any expression";
|
||||||
BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) expression.getContainingFile()).getBindingContext();
|
BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) expression.getContainingFile()).getBindingContext();
|
||||||
ResolvedCall<? extends CallableDescriptor> resolvedCall = context.get(BindingContext.RESOLVED_CALL, expression.getOperationReference());
|
ResolvedCall<? extends CallableDescriptor> resolvedCall = context.get(BindingContext.RESOLVED_CALL,
|
||||||
|
expression.getOperationReference());
|
||||||
if (resolvedCall == null) return null;
|
if (resolvedCall == null) return null;
|
||||||
PsiElement compareTo = BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getCandidateDescriptor());
|
PsiElement compareTo = BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getCandidateDescriptor());
|
||||||
if (!(compareTo instanceof JetFunction)) return null;
|
if (!(compareTo instanceof JetFunction)) return null;
|
||||||
@@ -185,16 +191,43 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JetSingleIntentionActionFactory createFactoryForReturnTypeMismatchOnOverride() {
|
public static JetIntentionActionsFactory createFactoryForReturnTypeMismatchOnOverride() {
|
||||||
return new JetSingleIntentionActionFactory() {
|
return new JetIntentionActionsFactory() {
|
||||||
@Nullable
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public IntentionAction createAction(Diagnostic diagnostic) {
|
public List<IntentionAction> createActions(Diagnostic diagnostic) {
|
||||||
|
List<IntentionAction> actions = new LinkedList<IntentionAction>();
|
||||||
|
|
||||||
JetFunction function = QuickFixUtil.getParentElementOfType(diagnostic, JetFunction.class);
|
JetFunction function = QuickFixUtil.getParentElementOfType(diagnostic, JetFunction.class);
|
||||||
if (function == null) return null;
|
if (function != null) {
|
||||||
BindingContext context = KotlinCacheManagerUtil.getDeclarationsBindingContext(function);
|
BindingContext context = KotlinCacheManagerUtil.getDeclarationsBindingContext(function);
|
||||||
JetType matchingReturnType = QuickFixUtil.findLowerBoundOfOverriddenCallablesReturnTypes(context, function);
|
JetType matchingReturnType = QuickFixUtil.findLowerBoundOfOverriddenCallablesReturnTypes(context, function);
|
||||||
return matchingReturnType == null ? null : new ChangeFunctionReturnTypeFix(function, matchingReturnType);
|
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<FunctionDescriptor> overriddenMismatchingFunctions = new LinkedList<FunctionDescriptor>();
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -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<caret>
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// "Change 'A.foo' function return type to 'Long'" "false"
|
||||||
|
// ERROR: <html>Return type is 'jet.Long', which is not a subtype of overridden<br/><b>internal</b> <b>abstract</b> <b>fun</b> foo(): jet.Int <i>defined in</i> A</html>
|
||||||
|
trait A {
|
||||||
|
fun foo(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
trait B {
|
||||||
|
fun foo(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
trait C : A, B {
|
||||||
|
override fun foo(): Long
|
||||||
|
}
|
||||||
+12
@@ -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<caret>
|
||||||
|
}
|
||||||
@@ -1033,6 +1033,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest("idea/testData/quickfix/override/typeMismatchOnOverride/beforeCantChangePropertyTypeToMatchOverridenProperties.kt");
|
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")
|
@TestMetadata("beforeChangeOverriddenPropertyType1.kt")
|
||||||
public void testChangeOverriddenPropertyType1() throws Exception {
|
public void testChangeOverriddenPropertyType1() throws Exception {
|
||||||
doTest("idea/testData/quickfix/override/typeMismatchOnOverride/beforeChangeOverriddenPropertyType1.kt");
|
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");
|
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")
|
@TestMetadata("beforePropertyReturnTypeMismatchOnOverride.kt")
|
||||||
public void testPropertyReturnTypeMismatchOnOverride() throws Exception {
|
public void testPropertyReturnTypeMismatchOnOverride() throws Exception {
|
||||||
doTest("idea/testData/quickfix/override/typeMismatchOnOverride/beforePropertyReturnTypeMismatchOnOverride.kt");
|
doTest("idea/testData/quickfix/override/typeMismatchOnOverride/beforePropertyReturnTypeMismatchOnOverride.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user