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:
Zalim Bashorov
2013-04-25 05:02:13 -07:00
15 changed files with 113 additions and 21 deletions
@@ -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")