diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java index 65587bff315..322e7667cf2 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -133,13 +133,6 @@ public class JetFlowInformationProvider { List 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 rootUnreachableElements = collectUnreachableCode(); diff --git a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties index ec12e968fb3..ee84d2f38ec 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties +++ b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties @@ -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 diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java index f6fc8ceec87..246d3c322e5 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java @@ -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 { +public class ChangeFunctionReturnTypeFix extends JetIntentionAction { 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 @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 BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) entry.getContainingFile().getContainingFile()).getBindingContext(); ResolvedCall 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 BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) expression.getContainingFile()).getBindingContext(); ResolvedCall 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 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; - 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 @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 } }; } + + @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; + } + }; + } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java index 552c32e62f3..d67c7059070 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java @@ -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()); diff --git a/idea/testData/quickfix/typeMismatch/afterChangeReturnTypeWhenFunctionNameIsMissing.kt b/idea/testData/quickfix/typeMismatch/afterChangeReturnTypeWhenFunctionNameIsMissing.kt new file mode 100644 index 00000000000..824275c22e9 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/afterChangeReturnTypeWhenFunctionNameIsMissing.kt @@ -0,0 +1,4 @@ +// "Remove explicitly specified function return type" "true" +fun () { + return +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/afterNoReturnInFunctionWithBlockBody.kt b/idea/testData/quickfix/typeMismatch/afterNoReturnInFunctionWithBlockBody.kt new file mode 100644 index 00000000000..e8876c8bdeb --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/afterNoReturnInFunctionWithBlockBody.kt @@ -0,0 +1,3 @@ +// "Remove explicitly specified return type in 'foo' function" "true" +fun foo() { +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/afterReturnTypeMismatch.kt b/idea/testData/quickfix/typeMismatch/afterReturnTypeMismatch.kt new file mode 100644 index 00000000000..d0cc2249cb8 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/afterReturnTypeMismatch.kt @@ -0,0 +1,4 @@ +// "Remove explicitly specified return type in 'foo' function" "true" +fun foo() { + return +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/afterTypeMismatchInInitializer.kt b/idea/testData/quickfix/typeMismatch/afterTypeMismatchInInitializer.kt new file mode 100644 index 00000000000..4528d211219 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/afterTypeMismatchInInitializer.kt @@ -0,0 +1,2 @@ +// "Change 'foo' function return type to 'String'" "true" +fun foo(): String = "" \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/afterTypeMismatchInReturnStatement.kt b/idea/testData/quickfix/typeMismatch/afterTypeMismatchInReturnStatement.kt new file mode 100644 index 00000000000..eb37598a97b --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/afterTypeMismatchInReturnStatement.kt @@ -0,0 +1,4 @@ +// "Change 'foo' function return type to 'String'" "true" +fun foo(): String { + return "" +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/beforeChangeReturnTypeWhenFunctionNameIsMissing.kt b/idea/testData/quickfix/typeMismatch/beforeChangeReturnTypeWhenFunctionNameIsMissing.kt new file mode 100644 index 00000000000..5a7e745cead --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/beforeChangeReturnTypeWhenFunctionNameIsMissing.kt @@ -0,0 +1,4 @@ +// "Remove explicitly specified function return type" "true" +fun (): Int { + return +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/beforeNoReturnInFunctionWithBlockBody.kt b/idea/testData/quickfix/typeMismatch/beforeNoReturnInFunctionWithBlockBody.kt new file mode 100644 index 00000000000..e254b4f61ed --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/beforeNoReturnInFunctionWithBlockBody.kt @@ -0,0 +1,3 @@ +// "Remove explicitly specified return type in 'foo' function" "true" +fun foo(): Int { +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/beforeReturnTypeMismatch.kt b/idea/testData/quickfix/typeMismatch/beforeReturnTypeMismatch.kt new file mode 100644 index 00000000000..7995f7a3e67 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/beforeReturnTypeMismatch.kt @@ -0,0 +1,4 @@ +// "Remove explicitly specified return type in 'foo' function" "true" +fun foo(): Int { + return +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/beforeTypeMismatchInInitializer.kt b/idea/testData/quickfix/typeMismatch/beforeTypeMismatchInInitializer.kt new file mode 100644 index 00000000000..7fc2c617bd9 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/beforeTypeMismatchInInitializer.kt @@ -0,0 +1,2 @@ +// "Change 'foo' function return type to 'String'" "true" +fun foo(): Int = "" \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/beforeTypeMismatchInReturnStatement.kt b/idea/testData/quickfix/typeMismatch/beforeTypeMismatchInReturnStatement.kt new file mode 100644 index 00000000000..71a0a7aa945 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/beforeTypeMismatchInReturnStatement.kt @@ -0,0 +1,4 @@ +// "Change 'foo' function return type to 'String'" "true" +fun foo() { + return "" +} \ 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 27ed4fe997f..e26c3ac86b9 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java @@ -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")