Single factory for TYPE_MISMATCH error

This commit is contained in:
Wojciech Lopata
2013-05-09 22:07:31 +02:00
parent 6c8096138a
commit ed6e734c14
20 changed files with 227 additions and 91 deletions
@@ -20,7 +20,6 @@ import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
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;
@@ -94,32 +93,4 @@ public class CastExpressionFix extends JetIntentionAction<JetExpression> {
}
};
}
@NotNull
public static JetSingleIntentionActionFactory createFactoryForTypeMismatch() {
return new JetSingleIntentionActionFactory() {
@Nullable
@Override
public IntentionAction createAction(Diagnostic diagnostic) {
assert diagnostic.getFactory() == Errors.TYPE_MISMATCH;
@SuppressWarnings("unchecked")
DiagnosticWithParameters2<JetExpression, JetType, JetType> diagnosticWithParameters =
(DiagnosticWithParameters2<JetExpression, JetType, JetType>) diagnostic;
JetExpression expression = diagnosticWithParameters.getPsiElement();
// we don't want to cast a cast:
if (expression instanceof JetBinaryExpressionWithTypeRHS) {
return null;
}
// 'x: Int' - TYPE_MISMATCH might be reported on 'x', and we don't want this quickfix to be available:
JetBinaryExpressionWithTypeRHS parentExpressionWithTypeRHS =
PsiTreeUtil.getParentOfType(expression, JetBinaryExpressionWithTypeRHS.class, true);
if (parentExpressionWithTypeRHS != null && parentExpressionWithTypeRHS.getLeft() == expression) {
return null;
}
return new CastExpressionFix(expression, diagnosticWithParameters.getA());
}
};
}
}
@@ -16,21 +16,16 @@
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.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.diagnostics.Diagnostic;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.name.FqName;
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> {
@@ -68,38 +63,4 @@ public class ChangeFunctionParameterTypeFix extends JetIntentionAction<JetParame
assert typeReference != null : "Parameter without type annotation cannot cause type mismatch";
typeReference.replace(JetPsiFactory.createType(project, renderedType));
}
@NotNull
public static JetSingleIntentionActionFactory createFactory() {
return new JetSingleIntentionActionFactory() {
@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;
}
};
}
}
@@ -210,24 +210,4 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction<JetFunction>
}
};
}
@NotNull
public static JetSingleIntentionActionFactory createFactoryForTypeMismatch() {
return new JetSingleIntentionActionFactory() {
@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;
}
};
}
}
@@ -0,0 +1,84 @@
/*
* 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.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters2;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import java.util.LinkedList;
import java.util.List;
public class QuickFixFactoryForTypeMismatchError implements JetIntentionActionsFactory {
@NotNull
@Override
public List<IntentionAction> createActions(Diagnostic diagnostic) {
List<IntentionAction> actions = new LinkedList<IntentionAction>();
assert diagnostic.getFactory() == Errors.TYPE_MISMATCH;
@SuppressWarnings("unchecked")
DiagnosticWithParameters2<JetExpression, JetType, JetType> diagnosticWithParameters =
(DiagnosticWithParameters2<JetExpression, JetType, JetType>) diagnostic;
JetExpression expression = diagnosticWithParameters.getPsiElement();
JetType expectedType = diagnosticWithParameters.getA();
JetType expressionType = diagnosticWithParameters.getB();
BindingContext context = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) diagnostic.getPsiFile()).getBindingContext();
// We don't want to cast a cast or type-asserted expression:
if (!(expression instanceof JetBinaryExpressionWithTypeRHS) && !(expression.getParent() instanceof JetBinaryExpressionWithTypeRHS)) {
actions.add(new CastExpressionFix(expression, expectedType));
}
// Mismatch in returned expression:
JetFunction function = PsiTreeUtil.getParentOfType(expression, JetFunction.class, true);
if (function != null && QuickFixUtil.canFunctionReturnExpression(function, expression)) {
actions.add(new ChangeFunctionReturnTypeFix(function, expressionType));
}
// Change type of a function parameter in case TYPE_MISMATCH is reported on expression passed as value argument of call.
// 1) When an argument is a dangling function literal:
JetFunctionLiteralExpression functionLiteralExpression =
QuickFixUtil.getParentElementOfType(diagnostic, JetFunctionLiteralExpression.class);
if (functionLiteralExpression != null && functionLiteralExpression.getBodyExpression() == expression) {
JetParameter correspondingParameter =
QuickFixUtil.getFunctionParameterCorrespondingToFunctionLiteralPassedOutsideArgumentList(functionLiteralExpression);
JetType functionLiteralExpressionType = context.get(BindingContext.EXPRESSION_TYPE, functionLiteralExpression);
if (correspondingParameter != null && functionLiteralExpressionType != null) {
actions.add(new ChangeFunctionParameterTypeFix(correspondingParameter, functionLiteralExpressionType));
}
}
// 2) When an argument is passed inside value argument list:
else {
JetValueArgument valueArgument = QuickFixUtil.getParentElementOfType(diagnostic, JetValueArgument.class);
if (valueArgument != null && valueArgument.getArgumentExpression() == expression) {
JetParameter correspondingParameter = QuickFixUtil.getFunctionParameterCorrespondingToValueArgumentPassedInCall(valueArgument);
JetType valueArgumentType = context.get(BindingContext.EXPRESSION_TYPE, valueArgument.getArgumentExpression());
if (correspondingParameter != null && valueArgumentType != null) {
actions.add(new ChangeFunctionParameterTypeFix(correspondingParameter, valueArgumentType));
}
}
}
return actions;
}
}
@@ -174,4 +174,19 @@ public class QuickFixUtil {
}
return true;
}
public static boolean canFunctionReturnExpression(@NotNull JetFunction function, @NotNull JetExpression expression) {
if (function instanceof JetFunctionLiteral) {
JetBlockExpression functionLiteralBody = ((JetFunctionLiteral) function).getBodyExpression();
PsiElement returnedElement = functionLiteralBody == null ? null : functionLiteralBody.getLastChild();
return returnedElement instanceof JetExpression && canEvaluateTo((JetExpression) returnedElement, expression);
}
else {
if (function instanceof JetWithExpressionInitializer && canEvaluateTo(((JetWithExpressionInitializer) function).getInitializer(), expression)) {
return true;
}
JetReturnExpression returnExpression = PsiTreeUtil.getParentOfType(expression, JetReturnExpression.class);
return returnExpression != null && canEvaluateTo(returnExpression.getReturnedExpression(), expression);
}
}
}
@@ -226,7 +226,6 @@ public class QuickFixes {
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(TOO_MANY_ARGUMENTS, ChangeFunctionSignatureFix.createFactory());
factories.put(NO_VALUE_FOR_PARAMETER, ChangeFunctionSignatureFix.createFactory());
@@ -240,10 +239,9 @@ public class QuickFixes {
factories.put(EXPECTED_TYPE_MISMATCH, changeFunctionLiteralReturnTypeFix);
factories.put(ASSIGNMENT_TYPE_MISMATCH, changeFunctionLiteralReturnTypeFix);
factories.put(TYPE_MISMATCH, ChangeFunctionParameterTypeFix.createFactory());
factories.put(TYPE_MISMATCH, new QuickFixFactoryForTypeMismatchError());
factories.put(AUTOCAST_IMPOSSIBLE, CastExpressionFix.createFactoryForAutoCastImpossible());
factories.put(TYPE_MISMATCH, CastExpressionFix.createFactoryForTypeMismatch());
factories.put(PLATFORM_CLASS_MAPPED_TO_KOTLIN, MapPlatformClassToKotlinFix.createFactory());
@@ -0,0 +1,7 @@
// "Change function literal return type to 'String'" "true"
fun foo(f: (String) -> Any) {
foo {
(s: String): String ->
s<caret>
}
}
@@ -0,0 +1,7 @@
// "Change function literal return type to 'String'" "true"
fun foo() {
val f: (String) -> String = {
(s: Any): String ->
""<caret>
}
}
@@ -0,0 +1,4 @@
// "Change 'foo' function return type to '() -> Any'" "true"
fun foo(x: Any): () -> Any {
return {x<caret>}
}
@@ -0,0 +1,6 @@
// "Change parameter 'f' type of function 'foo' to '() -> String'" "true"
fun foo(f: () -> String) {
foo {
""<caret>
}
}
@@ -0,0 +1,7 @@
// "Change 'boo' function return type to 'String'" "true"
fun boo(): String {
return ((if (true) {
val a = ""
<caret>a
} else ""))
}
@@ -0,0 +1,12 @@
// "Change 'f' type to '(Int, Int) -> (String) -> Int'" "true"
fun foo() {
val f: (Int, Int) -> (String) -> Int = {
(a: Int, b: Int): (String) -> Int ->
val x = {(s: String) -> 42}
if (true) x
else if (true) x else {
var y = 42
if (true) x<caret> else x
}
}
}
@@ -0,0 +1,7 @@
// "Change function literal return type to 'String'" "true"
fun foo(f: (String) -> Any) {
foo {
(s: String): Int ->
s<caret>
}
}
@@ -0,0 +1,7 @@
// "Change function literal return type to 'String'" "true"
fun foo() {
val f: (String) -> String = {
(s: Any): Int ->
""<caret>
}
}
@@ -0,0 +1,4 @@
// "Change 'foo' function return type to '() -> Any'" "true"
fun foo(x: Any): () -> Int {
return {x<caret>}
}
@@ -0,0 +1,6 @@
// "Change function 'foo' return type to '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>
// ACTION: Disable 'Replace 'if' with 'when''
// ACTION: Edit intention settings
// ACTION: Replace 'if' with 'when'
fun foo(): Int = if (true) ""<caret>: Int else 4
@@ -0,0 +1,6 @@
// "Change parameter 'f' type of function 'foo' to '() -> String'" "true"
fun foo(f: () -> Int) {
foo {
""<caret>
}
}
@@ -0,0 +1,7 @@
// "Change 'boo' function return type to 'String'" "true"
fun boo(): Int {
return ((if (true) {
val a = ""
<caret>a
} else ""))
}
@@ -0,0 +1,12 @@
// "Change 'f' type to '(Int, Int) -> (String) -> Int'" "true"
fun foo() {
val f: () -> Long = {
(a: Int, b: Int): Long ->
val x = {(s: String) -> 42}
if (true) x
else if (true) x else {
var y = 42
if (true) x<caret> else x
}
}
}
@@ -1421,16 +1421,51 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/beforeAssignmentTypeMismatch.kt");
}
@TestMetadata("beforeChangeFunctionLiteralTypeWithoutChangingFunctionParameterType.kt")
public void testChangeFunctionLiteralTypeWithoutChangingFunctionParameterType() throws Exception {
doTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/beforeChangeFunctionLiteralTypeWithoutChangingFunctionParameterType.kt");
}
@TestMetadata("beforeChangeFunctionLiteralTypeWithoutChangingPropertyType.kt")
public void testChangeFunctionLiteralTypeWithoutChangingPropertyType() throws Exception {
doTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/beforeChangeFunctionLiteralTypeWithoutChangingPropertyType.kt");
}
@TestMetadata("beforeChangeFunctionReturnTypeToFunctionType.kt")
public void testChangeFunctionReturnTypeToFunctionType() throws Exception {
doTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/beforeChangeFunctionReturnTypeToFunctionType.kt");
}
@TestMetadata("beforeChangeFunctionReturnTypeToMatchReturnTypeOfReturnedLiteral.kt")
public void testChangeFunctionReturnTypeToMatchReturnTypeOfReturnedLiteral() throws Exception {
doTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/beforeChangeFunctionReturnTypeToMatchReturnTypeOfReturnedLiteral.kt");
}
@TestMetadata("beforeExpectedTypeMismatch.kt")
public void testExpectedTypeMismatch() throws Exception {
doTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/beforeExpectedTypeMismatch.kt");
}
@TestMetadata("beforeReturnedExpresionCantEvaluateToExpresionThatTypeMismatch.kt")
public void testReturnedExpresionCantEvaluateToExpresionThatTypeMismatch() throws Exception {
doTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/beforeReturnedExpresionCantEvaluateToExpresionThatTypeMismatch.kt");
}
@TestMetadata("beforeReturnedExpressionTypeMismatchFunctionParameterType.kt")
public void testReturnedExpressionTypeMismatchFunctionParameterType() throws Exception {
doTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/beforeReturnedExpressionTypeMismatchFunctionParameterType.kt");
}
@TestMetadata("beforeTypeMismatchInIfStatementReturnedByFunction.kt")
public void testTypeMismatchInIfStatementReturnedByFunction() throws Exception {
doTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/beforeTypeMismatchInIfStatementReturnedByFunction.kt");
}
@TestMetadata("beforeTypeMismatchInIfStatementReturnedByLiteral.kt")
public void testTypeMismatchInIfStatementReturnedByLiteral() throws Exception {
doTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/beforeTypeMismatchInIfStatementReturnedByLiteral.kt");
}
@TestMetadata("beforeTypeMismatchInInitializer.kt")
public void testTypeMismatchInInitializer() throws Exception {
doTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/beforeTypeMismatchInInitializer.kt");