Merge pull request #256 from lopekpl/change_function_return_type_fixes
Quickfixes for RETURN_TYPE_MISMATCH and NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY Simple quickfix for TYPE_MISMATCH
This commit is contained in:
@@ -133,13 +133,6 @@ public class JetFlowInformationProvider {
|
||||
List<JetElement> returnedExpressions = Lists.newArrayList();
|
||||
collectReturnExpressions(returnedExpressions);
|
||||
|
||||
boolean nothingReturned = returnedExpressions.isEmpty();
|
||||
|
||||
returnedExpressions.remove(function); // This will be the only "expression" if the body is empty
|
||||
|
||||
if (expectedReturnType != NO_EXPECTED_TYPE && !KotlinBuiltIns.getInstance().isUnit(expectedReturnType) && returnedExpressions.isEmpty() && !nothingReturned) {
|
||||
trace.report(RETURN_TYPE_MISMATCH.on(bodyExpression, expectedReturnType));
|
||||
}
|
||||
final boolean blockBody = function.hasBlockBody();
|
||||
|
||||
final Set<JetElement> rootUnreachableElements = collectUnreachableCode();
|
||||
|
||||
@@ -58,7 +58,9 @@ rename.family=Rename
|
||||
add.semicolon.after.invocation=Add semicolon after invocation of ''{0}''
|
||||
add.semicolon.family=Add Semicolon
|
||||
change.function.return.type=Change ''{0}'' function return type to ''{1}''
|
||||
change.no.name.function.return.type=Change function return type to ''{0}''
|
||||
remove.function.return.type=Remove explicitly specified return type in ''{0}'' function
|
||||
remove.no.name.function.return.type=Remove explicitly specified function return type
|
||||
change.element.type=Change ''{0}'' type to ''{1}''
|
||||
change.type=Change type from ''{0}'' to ''{1}''
|
||||
change.type.family=Change Type
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorResolver;
|
||||
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.lang.KotlinBuiltIns;
|
||||
@@ -44,10 +45,10 @@ import org.jetbrains.jet.plugin.caches.resolve.KotlinCacheManagerUtil;
|
||||
import org.jetbrains.jet.plugin.intentions.SpecifyTypeExplicitlyAction;
|
||||
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
|
||||
|
||||
public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction> {
|
||||
public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetNamedFunction> {
|
||||
private final JetType type;
|
||||
|
||||
public ChangeFunctionReturnTypeFix(@NotNull JetFunction element, @NotNull JetType type) {
|
||||
public ChangeFunctionReturnTypeFix(@NotNull JetNamedFunction element, @NotNull JetType type) {
|
||||
super(element);
|
||||
this.type = type;
|
||||
}
|
||||
@@ -56,16 +57,17 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
|
||||
@Override
|
||||
public String getText() {
|
||||
String functionName = element.getName();
|
||||
BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext();
|
||||
SimpleFunctionDescriptor descriptor = context.get(BindingContext.FUNCTION, element);
|
||||
if (descriptor != null) {
|
||||
functionName = descriptor.getContainingDeclaration().getName() + "." + functionName;
|
||||
}
|
||||
FqName fqName = JetPsiUtil.getFQName(element);
|
||||
if (fqName != null) functionName = fqName.getFqName();
|
||||
|
||||
if (KotlinBuiltIns.getInstance().isUnit(type) && element.hasBlockBody()) {
|
||||
return JetBundle.message("remove.function.return.type", functionName);
|
||||
return functionName == null ?
|
||||
JetBundle.message("remove.no.name.function.return.type") :
|
||||
JetBundle.message("remove.function.return.type", functionName);
|
||||
}
|
||||
return JetBundle.message("change.function.return.type", functionName, type.toString());
|
||||
return functionName == null ?
|
||||
JetBundle.message("change.no.name.function.return.type", type.toString()) :
|
||||
JetBundle.message("change.function.return.type", functionName, type.toString());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -109,7 +111,7 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
|
||||
BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) entry.getContainingFile().getContainingFile()).getBindingContext();
|
||||
ResolvedCall<FunctionDescriptor> resolvedCall = context.get(BindingContext.COMPONENT_RESOLVED_CALL, entry);
|
||||
if (resolvedCall == null) return null;
|
||||
JetFunction componentFunction = (JetFunction) BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getCandidateDescriptor());
|
||||
JetNamedFunction componentFunction = (JetNamedFunction) BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getCandidateDescriptor());
|
||||
JetType expectedType = context.get(BindingContext.TYPE, entry.getTypeRef());
|
||||
if (componentFunction != null && expectedType != null) {
|
||||
return new ChangeFunctionReturnTypeFix(componentFunction, expectedType);
|
||||
@@ -130,7 +132,7 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
|
||||
BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) expression.getContainingFile()).getBindingContext();
|
||||
ResolvedCall<FunctionDescriptor> resolvedCall = context.get(BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, expression);
|
||||
if (resolvedCall == null) return null;
|
||||
JetFunction hasNextFunction = (JetFunction) BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getCandidateDescriptor());
|
||||
JetNamedFunction hasNextFunction = (JetNamedFunction) BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getCandidateDescriptor());
|
||||
if (hasNextFunction != null) {
|
||||
return new ChangeFunctionReturnTypeFix(hasNextFunction, KotlinBuiltIns.getInstance().getBooleanType());
|
||||
}
|
||||
@@ -151,8 +153,8 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
|
||||
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;
|
||||
return new ChangeFunctionReturnTypeFix((JetFunction) compareTo, KotlinBuiltIns.getInstance().getIntType());
|
||||
if (!(compareTo instanceof JetNamedFunction)) return null;
|
||||
return new ChangeFunctionReturnTypeFix((JetNamedFunction) compareTo, KotlinBuiltIns.getInstance().getIntType());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -163,7 +165,7 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
|
||||
@Nullable
|
||||
@Override
|
||||
public IntentionAction createAction(Diagnostic diagnostic) {
|
||||
JetFunction function = QuickFixUtil.getParentElementOfType(diagnostic, JetFunction.class);
|
||||
JetNamedFunction function = QuickFixUtil.getParentElementOfType(diagnostic, JetNamedFunction.class);
|
||||
if (function == null) return null;
|
||||
BindingContext context = KotlinCacheManagerUtil.getDeclarationsBindingContext(function);
|
||||
JetType matchingReturnType = QuickFixUtil.findLowerBoundOfOverriddenCallablesReturnTypes(context, function);
|
||||
@@ -171,4 +173,36 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetIntentionActionFactory createFactoryForChangingReturnTypeToUnit() {
|
||||
return new JetIntentionActionFactory() {
|
||||
@Nullable
|
||||
@Override
|
||||
public IntentionAction createAction(Diagnostic diagnostic) {
|
||||
JetNamedFunction function = QuickFixUtil.getParentElementOfType(diagnostic, JetNamedFunction.class);
|
||||
return function == null ? null : new ChangeFunctionReturnTypeFix(function, KotlinBuiltIns.getInstance().getUnitType());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetIntentionActionFactory createFactoryForTypeMismatch() {
|
||||
return new JetIntentionActionFactory() {
|
||||
@Nullable
|
||||
@Override
|
||||
public IntentionAction createAction(Diagnostic diagnostic) {
|
||||
JetExpression expression = (JetExpression) diagnostic.getPsiElement();
|
||||
JetNamedFunction function = QuickFixUtil.getParentElementOfType(diagnostic, JetNamedFunction.class);
|
||||
|
||||
if (function != null && (function.getInitializer() == expression || expression.getParent() instanceof JetReturnExpression)) {
|
||||
BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) diagnostic.getPsiFile()).getBindingContext();
|
||||
JetType type = context.get(BindingContext.EXPRESSION_TYPE, expression);
|
||||
assert type != null : "Expression type mismatch, but expression has no type";
|
||||
return new ChangeFunctionReturnTypeFix(function, type);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,10 +215,14 @@ public class QuickFixes {
|
||||
factories.put(PROPERTY_TYPE_MISMATCH_ON_OVERRIDE, changeVariableTypeFix);
|
||||
factories.put(COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH, ChangeVariableTypeFix.createFactoryForComponentFunctionReturnTypeMismatch());
|
||||
|
||||
JetIntentionActionFactory changeFunctionReturnTypeFix = ChangeFunctionReturnTypeFix.createFactoryForChangingReturnTypeToUnit();
|
||||
factories.put(RETURN_TYPE_MISMATCH, changeFunctionReturnTypeFix);
|
||||
factories.put(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY, changeFunctionReturnTypeFix);
|
||||
factories.put(RETURN_TYPE_MISMATCH_ON_OVERRIDE, ChangeFunctionReturnTypeFix.createFactoryForReturnTypeMismatchOnOverride());
|
||||
factories.put(COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH, ChangeFunctionReturnTypeFix.createFactoryForComponentFunctionReturnTypeMismatch());
|
||||
factories.put(HAS_NEXT_FUNCTION_TYPE_MISMATCH, ChangeFunctionReturnTypeFix.createFactoryForHasNextFunctionTypeMismatch());
|
||||
factories.put(COMPARE_TO_TYPE_MISMATCH, ChangeFunctionReturnTypeFix.createFactoryForCompareToTypeMismatch());
|
||||
factories.put(TYPE_MISMATCH, ChangeFunctionReturnTypeFix.createFactoryForTypeMismatch());
|
||||
|
||||
factories.put(EXPECTED_PARAMETER_TYPE_MISMATCH, ChangeTypeFix.createFactoryForExpectedParameterTypeMismatch());
|
||||
factories.put(EXPECTED_RETURN_TYPE_MISMATCH, ChangeTypeFix.createFactoryForExpectedReturnTypeMismatch());
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Remove explicitly specified function return type" "true"
|
||||
fun () {
|
||||
return<caret>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// "Remove explicitly specified return type in 'foo' function" "true"
|
||||
fun foo() {
|
||||
<caret>}
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Remove explicitly specified return type in 'foo' function" "true"
|
||||
fun foo() {
|
||||
return<caret>
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// "Change 'foo' function return type to 'String'" "true"
|
||||
fun foo(): String = <caret>""
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Change 'foo' function return type to 'String'" "true"
|
||||
fun foo(): String {
|
||||
return ""<caret>
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Remove explicitly specified function return type" "true"
|
||||
fun (): Int {
|
||||
return<caret>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// "Remove explicitly specified return type in 'foo' function" "true"
|
||||
fun foo(): Int {
|
||||
<caret>}
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Remove explicitly specified return type in 'foo' function" "true"
|
||||
fun foo(): Int {
|
||||
return<caret>
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// "Change 'foo' function return type to 'String'" "true"
|
||||
fun foo(): Int = <caret>""
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Change 'foo' function return type to 'String'" "true"
|
||||
fun foo() {
|
||||
return ""<caret>
|
||||
}
|
||||
@@ -998,6 +998,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/quickfix/typeMismatch"), Pattern.compile("^before(\\w+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("beforeChangeReturnTypeWhenFunctionNameIsMissing.kt")
|
||||
public void testChangeReturnTypeWhenFunctionNameIsMissing() throws Exception {
|
||||
doTest("idea/testData/quickfix/typeMismatch/beforeChangeReturnTypeWhenFunctionNameIsMissing.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeChangeReturnTypeWhenValueParameterListIsAbsent.kt")
|
||||
public void testChangeReturnTypeWhenValueParameterListIsAbsent() throws Exception {
|
||||
doTest("idea/testData/quickfix/typeMismatch/beforeChangeReturnTypeWhenValueParameterListIsAbsent.kt");
|
||||
@@ -1048,6 +1053,26 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
doTest("idea/testData/quickfix/typeMismatch/beforeHasNextFunctionReturnTypeMismatch.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeNoReturnInFunctionWithBlockBody.kt")
|
||||
public void testNoReturnInFunctionWithBlockBody() throws Exception {
|
||||
doTest("idea/testData/quickfix/typeMismatch/beforeNoReturnInFunctionWithBlockBody.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeReturnTypeMismatch.kt")
|
||||
public void testReturnTypeMismatch() throws Exception {
|
||||
doTest("idea/testData/quickfix/typeMismatch/beforeReturnTypeMismatch.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeTypeMismatchInInitializer.kt")
|
||||
public void testTypeMismatchInInitializer() throws Exception {
|
||||
doTest("idea/testData/quickfix/typeMismatch/beforeTypeMismatchInInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeTypeMismatchInReturnStatement.kt")
|
||||
public void testTypeMismatchInReturnStatement() throws Exception {
|
||||
doTest("idea/testData/quickfix/typeMismatch/beforeTypeMismatchInReturnStatement.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeProjection")
|
||||
|
||||
Reference in New Issue
Block a user