diff --git a/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java b/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java index 925ab0b7bd43..86d18b66a363 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java @@ -16,24 +16,22 @@ package com.intellij.refactoring.util; import com.intellij.codeInsight.ChangeContextUtil; -import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.RedundantCastUtil; -import com.intellij.psi.util.TypeConversionUtil; import com.intellij.refactoring.RefactoringBundle; import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -53,7 +51,6 @@ public class InlineUtil { PsiClass thisClass = RefactoringChangeUtil.getThisClass(initializer); PsiClass refParent = RefactoringChangeUtil.getThisClass(ref); final PsiElement parent = ref.getParent(); - boolean insertCastWhenUnchecked = parent instanceof PsiForeachStatement; final PsiType varType = variable.getType(); initializer = RefactoringUtil.convertInitializerToNormalExpression(initializer, varType); if (initializer instanceof PsiPolyadicExpression) { @@ -75,12 +72,9 @@ public class InlineUtil { PsiThisExpression thisAccessExpr = createThisExpression(manager, thisClass, refParent); expr = (PsiExpression)ChangeContextUtil.decodeContextInfo(expr, thisClass, thisAccessExpr); - PsiType exprType = expr.getType(); - if (exprType != null && (!varType.equals(exprType) && (varType instanceof PsiPrimitiveType || exprType instanceof PsiPrimitiveType) - || !TypeConversionUtil.isAssignable(varType, exprType) - || insertCastWhenUnchecked && JavaGenericsUtil.isRawToGeneric(varType, exprType))) { + PsiType exprType = RefactoringUtil.getTypeByExpression(expr); + if (exprType != null && !exprType.equals(varType)) { boolean matchedTypes = false; - //try explicit type arguments final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory(); if (expr instanceof PsiCallExpression && ((PsiCallExpression)expr).getTypeArguments().length == 0) { final JavaResolveResult resolveResult = ((PsiCallExpression)initializer).resolveMethodGenerics(); @@ -116,53 +110,11 @@ public class InlineUtil { } } - if (!matchedTypes) { - if (varType instanceof PsiEllipsisType && ((PsiEllipsisType)varType).getComponentType().equals(exprType)) { //convert vararg to array - - final PsiExpressionList argumentList = PsiTreeUtil.getParentOfType(expr, PsiExpressionList.class); - LOG.assertTrue(argumentList != null); - final PsiExpression[] arguments = argumentList.getExpressions(); - - @NonNls final StringBuilder builder = new StringBuilder("new "); - builder.append(exprType.getCanonicalText()); - builder.append("[]{"); - builder.append(StringUtil.join(Arrays.asList(arguments), new Function() { - @Override - public String fun(final PsiExpression expr) { - return expr.getText(); - } - }, ",")); - builder.append('}'); - - expr.replace(JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createExpressionFromText(builder.toString(), argumentList)); - - } else { - //try cast - PsiTypeCastExpression cast = (PsiTypeCastExpression)elementFactory.createExpressionFromText("(t)a", null); - PsiTypeElement castTypeElement = cast.getCastType(); - assert castTypeElement != null; - castTypeElement.replace(variable.getTypeElement()); - final PsiExpression operand = cast.getOperand(); - assert operand != null; - operand.replace(expr); - PsiExpression exprCopy = (PsiExpression)expr.copy(); - cast = (PsiTypeCastExpression)expr.replace(cast); - if (!RedundantCastUtil.isCastRedundant(cast)) { - expr = cast; - } - else { - PsiElement toReplace = cast; - while (toReplace.getParent() instanceof PsiParenthesizedExpression) { - toReplace = toReplace.getParent(); - } - expr = (PsiExpression)toReplace.replace(exprCopy); - } - } + boolean insertCastWhenUnchecked = + !(exprType instanceof PsiClassType && ((PsiClassType)exprType).isRaw() && parent instanceof PsiExpressionList); + if (!matchedTypes && (expr instanceof PsiFunctionalExpression || !PsiPolyExpressionUtil.isPolyExpression(expr) && insertCastWhenUnchecked)) { + expr = surroundWithCast(variable, expr); } - } else if (exprType instanceof PsiLambdaExpressionType) { - expr = surroundWithCast(variable, expr, ((PsiLambdaExpressionType)exprType).getExpression()); - } else if (exprType instanceof PsiMethodReferenceType) { - expr = surroundWithCast(variable, expr, ((PsiMethodReferenceType)exprType).getExpression()); } ChangeContextUtil.clearContextInfo(initializer); @@ -170,21 +122,19 @@ public class InlineUtil { return expr; } - private static PsiExpression surroundWithCast(PsiVariable variable, PsiExpression expr, PsiExpression expression) { - final PsiElement parent = expression.getParent(); - if (parent instanceof PsiReferenceExpression || parent instanceof PsiExpressionList) { - PsiTypeCastExpression cast = (PsiTypeCastExpression)JavaPsiFacade.getElementFactory(expr.getProject()).createExpressionFromText("(t)a", null); - PsiTypeElement castTypeElement = cast.getCastType(); - assert castTypeElement != null; - castTypeElement.replace(variable.getTypeElement()); - final PsiExpression operand = cast.getOperand(); - assert operand != null; - operand.replace(expr); - expr = (PsiTypeCastExpression)expr.replace(cast); - if (RedundantCastUtil.isCastRedundant((PsiTypeCastExpression)expr)) { - expr = (PsiExpression)expr.replace(((PsiTypeCastExpression)expr).getOperand()); - } + private static PsiExpression surroundWithCast(PsiVariable variable, PsiExpression expr) { + PsiTypeCastExpression cast = (PsiTypeCastExpression)JavaPsiFacade.getElementFactory(expr.getProject()).createExpressionFromText("(t)a", null); + PsiTypeElement castTypeElement = cast.getCastType(); + assert castTypeElement != null; + castTypeElement.replace(variable.getTypeElement()); + final PsiExpression operand = cast.getOperand(); + assert operand != null; + operand.replace(expr); + expr = (PsiTypeCastExpression)expr.replace(cast); + if (RedundantCastUtil.isCastRedundant((PsiTypeCastExpression)expr)) { + return RedundantCastUtil.removeCast((PsiTypeCastExpression)expr); } + return expr; } diff --git a/java/java-psi-api/src/com/intellij/psi/util/RedundantCastUtil.java b/java/java-psi-api/src/com/intellij/psi/util/RedundantCastUtil.java index 124fcea2ce62..57c6d99f73c7 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/RedundantCastUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/RedundantCastUtil.java @@ -71,29 +71,30 @@ public class RedundantCastUtil { return arg; } - public static void removeCast(PsiTypeCastExpression castExpression) { - if (castExpression == null) return; + public static PsiExpression removeCast(PsiTypeCastExpression castExpression) { + if (castExpression == null) return null; PsiExpression operand = castExpression.getOperand(); if (operand instanceof PsiParenthesizedExpression) { final PsiParenthesizedExpression parExpr = (PsiParenthesizedExpression)operand; operand = parExpr.getExpression(); } - if (operand == null) return; + if (operand == null) return null; - PsiElement toBeReplaced = castExpression; + PsiExpression toBeReplaced = castExpression; PsiElement parent = castExpression.getParent(); while (parent instanceof PsiParenthesizedExpression) { - toBeReplaced = parent; + toBeReplaced = (PsiExpression)parent; parent = parent.getParent(); } try { - toBeReplaced.replace(operand); + return (PsiExpression)toBeReplaced.replace(operand); } catch (IncorrectOperationException e) { LOG.error(e); } + return toBeReplaced; } private static class MyCollectingVisitor extends MyIsRedundantVisitor { diff --git a/java/java-tests/testData/refactoring/inlineLocal/InsertCastToGenericTypeToProvideValidReturnType.java b/java/java-tests/testData/refactoring/inlineLocal/InsertCastToGenericTypeToProvideValidReturnType.java new file mode 100644 index 000000000000..560d619bd73e --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineLocal/InsertCastToGenericTypeToProvideValidReturnType.java @@ -0,0 +1,12 @@ + +interface Vector { + M get(int i); +} +class Test { + + private static void call(Vector args_) { + Vector args = args_; + + String s = args.get(0); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineLocal/InsertCastToGenericTypeToProvideValidReturnType.java.after b/java/java-tests/testData/refactoring/inlineLocal/InsertCastToGenericTypeToProvideValidReturnType.java.after new file mode 100644 index 000000000000..929fedc25a99 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineLocal/InsertCastToGenericTypeToProvideValidReturnType.java.after @@ -0,0 +1,11 @@ + +interface Vector { + M get(int i); +} +class Test { + + private static void call(Vector args_) { + + String s = ((Vector) args_).get(0); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineLocal/InsertNarrowingCastToAvoidSemanticsChange.java b/java/java-tests/testData/refactoring/inlineLocal/InsertNarrowingCastToAvoidSemanticsChange.java new file mode 100644 index 000000000000..4d29fad2b191 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineLocal/InsertNarrowingCastToAvoidSemanticsChange.java @@ -0,0 +1,18 @@ + +class Cat {} +class DomesticCat extends Cat {} + +class Test { + public static void main(String[] args) { + Cat cat = new DomesticCat(); + petCat(cat); + } + + static void petCat(Cat cat) { + System.out.println("A Cat"); + } + + static void petCat(DomesticCat domesticCat) { + System.out.println("A DomesticCat"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineLocal/InsertNarrowingCastToAvoidSemanticsChange.java.after b/java/java-tests/testData/refactoring/inlineLocal/InsertNarrowingCastToAvoidSemanticsChange.java.after new file mode 100644 index 000000000000..521a626a23b8 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineLocal/InsertNarrowingCastToAvoidSemanticsChange.java.after @@ -0,0 +1,17 @@ + +class Cat {} +class DomesticCat extends Cat {} + +class Test { + public static void main(String[] args) { + petCat((Cat) new DomesticCat()); + } + + static void petCat(Cat cat) { + System.out.println("A Cat"); + } + + static void petCat(DomesticCat domesticCat) { + System.out.println("A DomesticCat"); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java index 808c679d9635..7fe317b5cb64 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java @@ -264,6 +264,14 @@ public class InlineLocalTest extends LightCodeInsightTestCase { doTest(false); } + public void testInsertNarrowingCastToAvoidSemanticsChange() throws Exception { + doTest(false); + } + + public void testInsertCastToGenericTypeToProvideValidReturnType() throws Exception { + doTest(false); + } + public void testLocalVarInsideLambdaBodyWriteUsage() throws Exception { doTest(true, "Cannot perform refactoring.\n" + "Variable 'hello' is accessed for writing");