ChangeFunctionParameterTypeFix for TYPE_MISMATCH error
This commit is contained in:
@@ -62,6 +62,7 @@ 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.function.parameter.type=Change parameter ''{0}'' type of function ''{1}'' to ''{2}''
|
||||
change.type=Change type from ''{0}'' to ''{1}''
|
||||
change.type.family=Change Type
|
||||
add.parameters.to.function=Add parameter{0} to function ''{1}''
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2010-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.plugin.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.name.NameUtils;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.plugin.JetBundle;
|
||||
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer;
|
||||
|
||||
public class ChangeFunctionParameterTypeFix extends JetIntentionAction<JetParameter> {
|
||||
private final String renderedType;
|
||||
private final String containingFunctionName;
|
||||
|
||||
public ChangeFunctionParameterTypeFix(@NotNull JetParameter element, @NotNull JetType type) {
|
||||
super(element);
|
||||
renderedType = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(type);
|
||||
JetFunction function = PsiTreeUtil.getParentOfType(element, JetFunction.class);
|
||||
FqName functionFQName = function == null ? null : JetPsiUtil.getFQName(function);
|
||||
containingFunctionName = functionFQName == null ? null : functionFQName.getFqName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
return super.isAvailable(project, editor, file) && containingFunctionName != null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return JetBundle.message("change.function.parameter.type", element.getName(), containingFunctionName, renderedType);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return JetBundle.message("change.type.family");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, JetFile file) throws IncorrectOperationException {
|
||||
JetTypeReference typeReference = element.getTypeReference();
|
||||
assert typeReference != null : "Parameter without type annotation cannot cause type mismatch";
|
||||
typeReference.replace(JetPsiFactory.createType(project, renderedType));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetIntentionActionFactory createFactory() {
|
||||
return new JetIntentionActionFactory() {
|
||||
@Nullable
|
||||
@Override
|
||||
public IntentionAction createAction(Diagnostic diagnostic) {
|
||||
// Change type of function parameter in case TYPE_MISMATCH is reported on expression passed as value argument of call
|
||||
JetParameter correspondingParameter = null;
|
||||
JetType type = null;
|
||||
|
||||
BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) diagnostic.getPsiFile()).getBindingContext();
|
||||
JetFunctionLiteralExpression functionLiteralExpression =
|
||||
QuickFixUtil.getParentElementOfType(diagnostic, JetFunctionLiteralExpression.class);
|
||||
|
||||
if (functionLiteralExpression != null && diagnostic.getPsiElement() == functionLiteralExpression.getBodyExpression()) {
|
||||
correspondingParameter =
|
||||
QuickFixUtil.getFunctionParameterCorrespondingToFunctionLiteralPassedOutsideArgumentList(functionLiteralExpression);
|
||||
type = context.get(BindingContext.EXPRESSION_TYPE, functionLiteralExpression);
|
||||
}
|
||||
else {
|
||||
JetValueArgument valueArgument = QuickFixUtil.getParentElementOfType(diagnostic, JetValueArgument.class);
|
||||
if (valueArgument != null && valueArgument.getArgumentExpression() == diagnostic.getPsiElement()) {
|
||||
correspondingParameter = QuickFixUtil.getFunctionParameterCorrespondingToValueArgumentPassedInCall(valueArgument);
|
||||
type = context.get(BindingContext.EXPRESSION_TYPE, valueArgument.getArgumentExpression());
|
||||
}
|
||||
}
|
||||
if (correspondingParameter != null && type != null) {
|
||||
return new ChangeFunctionParameterTypeFix(correspondingParameter, type);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
|
||||
import org.jetbrains.jet.lang.psi.JetDeclaration;
|
||||
import org.jetbrains.jet.lang.psi.JetFile;
|
||||
import org.jetbrains.jet.lang.psi.JetNamedDeclaration;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.types.DeferredType;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
||||
@@ -93,4 +93,60 @@ public class QuickFixUtil {
|
||||
public static boolean canModifyElement(@NotNull PsiElement element) {
|
||||
return element.isWritable() && !BuiltInsReferenceResolver.isFromBuiltIns(element);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JetParameterList getParameterListOfCalledFunction(@NotNull JetCallExpression callExpression) {
|
||||
BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) callExpression.getContainingFile()).getBindingContext();
|
||||
ResolvedCall<? extends CallableDescriptor> resolvedCall = context.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression());
|
||||
if (resolvedCall == null) return null;
|
||||
PsiElement functionDeclaration = BindingContextUtils.descriptorToDeclaration(context, resolvedCall.getCandidateDescriptor());
|
||||
if (functionDeclaration instanceof JetFunction) {
|
||||
return ((JetFunction) functionDeclaration).getValueParameterList();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JetParameter getFunctionParameterCorrespondingToFunctionLiteralPassedOutsideArgumentList(@NotNull JetFunctionLiteralExpression functionLiteralExpression) {
|
||||
if (!(functionLiteralExpression.getParent() instanceof JetCallExpression)) {
|
||||
return null;
|
||||
}
|
||||
JetCallExpression callExpression = (JetCallExpression) functionLiteralExpression.getParent();
|
||||
JetParameterList parameterList = getParameterListOfCalledFunction(callExpression);
|
||||
if (parameterList == null) return null;
|
||||
return parameterList.getParameters().get(parameterList.getParameters().size() - 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JetParameter getFunctionParameterCorrespondingToValueArgumentPassedInCall(@NotNull JetValueArgument valueArgument) {
|
||||
if (!(valueArgument.getParent() instanceof JetValueArgumentList)) {
|
||||
return null;
|
||||
}
|
||||
JetValueArgumentList valueArgumentList = (JetValueArgumentList) valueArgument.getParent();
|
||||
if (!(valueArgumentList.getParent() instanceof JetCallExpression)) {
|
||||
return null;
|
||||
}
|
||||
JetCallExpression callExpression = (JetCallExpression) valueArgumentList.getParent();
|
||||
JetParameterList parameterList = getParameterListOfCalledFunction(callExpression);
|
||||
if (parameterList == null) return null;
|
||||
int position = valueArgumentList.getArguments().indexOf(valueArgument);
|
||||
if (position == -1) return null;
|
||||
|
||||
if (valueArgument.isNamed()) {
|
||||
JetValueArgumentName valueArgumentName = valueArgument.getArgumentName();
|
||||
JetSimpleNameExpression referenceExpression = valueArgumentName == null ? null : valueArgumentName.getReferenceExpression();
|
||||
String valueArgumentNameAsString = referenceExpression == null ? null : referenceExpression.getReferencedName();
|
||||
if (valueArgumentNameAsString == null) return null;
|
||||
|
||||
for (JetParameter parameter: parameterList.getParameters()) {
|
||||
if (valueArgumentNameAsString.equals(parameter.getName())) {
|
||||
return parameter;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return parameterList.getParameters().get(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,9 @@ public class QuickFixes {
|
||||
|
||||
factories.put(EXPECTED_PARAMETER_TYPE_MISMATCH, ChangeTypeFix.createFactoryForExpectedParameterTypeMismatch());
|
||||
factories.put(EXPECTED_RETURN_TYPE_MISMATCH, ChangeTypeFix.createFactoryForExpectedReturnTypeMismatch());
|
||||
|
||||
|
||||
factories.put(TYPE_MISMATCH, ChangeFunctionParameterTypeFix.createFactory());
|
||||
|
||||
factories.put(AUTOCAST_IMPOSSIBLE, CastExpressionFix.createFactoryForAutoCastImpossible());
|
||||
factories.put(TYPE_MISMATCH, CastExpressionFix.createFactoryForTypeMismatch());
|
||||
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// "Change parameter 'x' type of function 'bar.foo' to '(String) -> Int'" "true"
|
||||
package bar
|
||||
fun foo(w: Int = 0, x: (String) -> Int, y: Int = 0, z: (Int) -> Int = {42}) {
|
||||
foo(1, {(a: String) -> 42}<caret>, 1)
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Change parameter 'y' type of function 'foo' to 'String'" "true"
|
||||
fun foo(v: Int, w: Int = 0, x: Int = 0, y: String, z: (Int) -> Int = {42}) {
|
||||
foo(0, 1, y = ""<caret>)
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Change parameter 'z' type of function 'foo' to '(Int) -> Unit'" "true"
|
||||
fun foo(w: Int = 0, x: Int, y: Int = 0, z: (Int) -> Unit) {
|
||||
foo(0, 1) {<caret>}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// "Change parameter 'x' type of function 'bar.foo' to '(String) -> Int'" "true"
|
||||
package bar
|
||||
fun foo(w: Int = 0, x: Int, y: Int = 0, z: (Int) -> Int = {42}) {
|
||||
foo(1, {(a: String) -> 42}<caret>, 1)
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Change parameter 'y' type of function 'foo' to 'String'" "true"
|
||||
fun foo(v: Int, w: Int = 0, x: Int = 0, y: Int, z: (Int) -> Int = {42}) {
|
||||
foo(0, 1, y = ""<caret>)
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Change parameter 'z' type of function 'foo' to '(Int) -> Unit'" "true"
|
||||
fun foo(w: Int = 0, x: Int, y: Int = 0, z: (Int) -> String) {
|
||||
foo(0, 1) {<caret>}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Change parameter 'z' type of function 'foo' to '(Int) -> String'" "false"
|
||||
// ERROR: <html>Type mismatch.<table><tr><td>Required:</td><td>jet.Int</td></tr><tr><td>Found:</td><td>jet.String</td></tr></table></html>
|
||||
fun foo(y: Int = 0, z: (Int) -> String = {""}) {
|
||||
foo {
|
||||
""<caret>: Int
|
||||
""
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// "Change parameter 'y' type of function 'foo' to 'Int'" "false"
|
||||
// ERROR: <html>Type mismatch.<table><tr><td>Required:</td><td>jet.Int</td></tr><tr><td>Found:</td><td>jet.String</td></tr></table></html>
|
||||
fun foo(y: Int = 0, z: (Int) -> String = {""}) {
|
||||
foo(""<caret>: Int)
|
||||
}
|
||||
@@ -1240,7 +1240,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeMismatch")
|
||||
@InnerTestClasses({TypeMismatch.Casts.class, TypeMismatch.ComponentFunctionReturnTypeMismatch.class, TypeMismatch.TypeMismatchOnReturnedExpression.class})
|
||||
@InnerTestClasses({TypeMismatch.Casts.class, TypeMismatch.ComponentFunctionReturnTypeMismatch.class, TypeMismatch.FunctionParameterTypeMismatch.class, TypeMismatch.TypeMismatchOnReturnedExpression.class})
|
||||
public static class TypeMismatch extends AbstractQuickFixTest {
|
||||
public void testAllFilesPresentInTypeMismatch() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/quickfix/typeMismatch"), Pattern.compile("^before(\\w+)\\.kt$"), true);
|
||||
@@ -1377,6 +1377,39 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeMismatch/functionParameterTypeMismatch")
|
||||
public static class FunctionParameterTypeMismatch extends AbstractQuickFixTest {
|
||||
public void testAllFilesPresentInFunctionParameterTypeMismatch() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/quickfix/typeMismatch/functionParameterTypeMismatch"), Pattern.compile("^before(\\w+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("beforeChangeFunctionParameterType1.kt")
|
||||
public void testChangeFunctionParameterType1() throws Exception {
|
||||
doTest("idea/testData/quickfix/typeMismatch/functionParameterTypeMismatch/beforeChangeFunctionParameterType1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeChangeFunctionParameterType2.kt")
|
||||
public void testChangeFunctionParameterType2() throws Exception {
|
||||
doTest("idea/testData/quickfix/typeMismatch/functionParameterTypeMismatch/beforeChangeFunctionParameterType2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeChangeFunctionParameterType3.kt")
|
||||
public void testChangeFunctionParameterType3() throws Exception {
|
||||
doTest("idea/testData/quickfix/typeMismatch/functionParameterTypeMismatch/beforeChangeFunctionParameterType3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeChangeFunctionParameterType4.kt")
|
||||
public void testChangeFunctionParameterType4() throws Exception {
|
||||
doTest("idea/testData/quickfix/typeMismatch/functionParameterTypeMismatch/beforeChangeFunctionParameterType4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeChangeFunctionParameterType5.kt")
|
||||
public void testChangeFunctionParameterType5() throws Exception {
|
||||
doTest("idea/testData/quickfix/typeMismatch/functionParameterTypeMismatch/beforeChangeFunctionParameterType5.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression")
|
||||
public static class TypeMismatchOnReturnedExpression extends AbstractQuickFixTest {
|
||||
public void testAllFilesPresentInTypeMismatchOnReturnedExpression() throws Exception {
|
||||
@@ -1405,6 +1438,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
suite.addTestSuite(TypeMismatch.class);
|
||||
suite.addTestSuite(Casts.class);
|
||||
suite.addTestSuite(ComponentFunctionReturnTypeMismatch.class);
|
||||
suite.addTestSuite(FunctionParameterTypeMismatch.class);
|
||||
suite.addTestSuite(TypeMismatchOnReturnedExpression.class);
|
||||
return suite;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user