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.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<JetFunction> {
|
||||
private final JetType type;
|
||||
private final String renderedType;
|
||||
@@ -175,7 +180,8 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
|
||||
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<? 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;
|
||||
PsiElement compareTo = BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getCandidateDescriptor());
|
||||
if (!(compareTo instanceof JetFunction)) return null;
|
||||
@@ -185,16 +191,43 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
|
||||
}
|
||||
|
||||
@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<IntentionAction> createActions(Diagnostic diagnostic) {
|
||||
List<IntentionAction> actions = new LinkedList<IntentionAction>();
|
||||
|
||||
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<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");
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
Reference in New Issue
Block a user