IG: leave cast alone when it is necessary (IDEA-173430)

This commit is contained in:
Bas Leijdekkers
2017-06-06 10:39:43 +02:00
parent 6dc7596e56
commit 547abc95e5
3 changed files with 103 additions and 15 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -18,7 +18,7 @@ package com.intellij.psi.util;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -102,8 +102,8 @@ public class PsiConcatenationUtil {
formatParameters.add(getBoxedArgument(expression));
}
private static PsiExpression getBoxedArgument(PsiExpression arg) throws IncorrectOperationException {
arg = PsiUtil.deparenthesizeExpression(arg);
private static PsiExpression getBoxedArgument(PsiExpression arg) {
arg = unwrapExpression(arg);
assert arg != null;
if (PsiUtil.isLanguageLevel5OrHigher(arg)) {
return arg;
@@ -130,4 +130,35 @@ public class PsiConcatenationUtil {
return newExpr;
}
@Nullable
private static PsiExpression unwrapExpression(PsiExpression expression) {
while (true) {
if (expression instanceof PsiParenthesizedExpression) {
expression = ((PsiParenthesizedExpression)expression).getExpression();
continue;
}
if (expression instanceof PsiTypeCastExpression) {
final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)expression;
final PsiType castType = typeCastExpression.getType();
if (TypeConversionUtil.isNumericType(castType)) {
final PsiExpression operand = typeCastExpression.getOperand();
if (operand == null) {
return expression;
}
final PsiType operandType = operand.getType();
if (operandType == null) {
return expression;
}
final int castRank = TypeConversionUtil.getTypeRank(castType);
final int operandRank = TypeConversionUtil.getTypeRank(operandType);
if (castRank < operandRank || castRank == TypeConversionUtil.CHAR_RANK && operandRank != castRank) {
return expression;
}
}
expression = typeCastExpression.getOperand();
continue;
}
return expression;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2008-2015 Bas Leijdekkers
* Copyright 2008-2017 Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@ package com.siyeh.ipp.concatenation;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiConcatenationUtil;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ig.PsiReplacementUtil;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ipp.base.Intention;
@@ -36,7 +35,7 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention {
}
@Override
protected void processIntention(@NotNull PsiElement element) throws IncorrectOperationException {
protected void processIntention(@NotNull PsiElement element) {
PsiPolyadicExpression expression = (PsiPolyadicExpression)element;
PsiElement parent = expression.getParent();
while (ExpressionUtils.isConcatenation(parent)) {
@@ -44,7 +43,7 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention {
parent = expression.getParent();
}
final StringBuilder formatString = new StringBuilder();
final List<PsiExpression> formatParameters = new ArrayList();
final List<PsiExpression> formatParameters = new ArrayList<>();
PsiConcatenationUtil.buildFormatString(expression, formatString, formatParameters, true);
if (replaceWithPrintfExpression(expression, formatString, formatParameters)) {
return;
@@ -62,7 +61,7 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention {
}
private static boolean replaceWithPrintfExpression(PsiExpression expression, CharSequence formatString,
List<PsiExpression> formatParameters) throws IncorrectOperationException {
List<PsiExpression> formatParameters) {
final PsiElement expressionParent = expression.getParent();
if (!(expressionParent instanceof PsiExpressionList)) {
return false;
@@ -100,18 +99,15 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention {
final StringBuilder newExpression = new StringBuilder();
final PsiExpression qualifier = methodExpression.getQualifierExpression();
if (qualifier != null) {
newExpression.append(qualifier.getText());
newExpression.append('.');
newExpression.append(qualifier.getText()).append('.');
}
newExpression.append("printf(\"");
newExpression.append(formatString);
newExpression.append("printf(\"").append(formatString);
if (insertNewline) {
newExpression.append("%n");
}
newExpression.append('\"');
for (PsiExpression formatParameter : formatParameters) {
newExpression.append(", ");
newExpression.append(formatParameter.getText());
newExpression.append(", ").append(formatParameter.getText());
}
newExpression.append(')');
PsiReplacementUtil.replaceExpression(methodCallExpression, newExpression.toString());
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2017 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 com.siyeh.ipp.concatenation;
import com.siyeh.ipp.IPPTestCase;
import junit.framework.TestCase;
/**
* @author Bas Leijdekkers
*/
public class ReplaceConcatenationWithFormatStringIntentionTest extends IPPTestCase {
public void testNarrowingCast() {
doTest("class X {" +
" String s = (byte)321 +/*_Replace '+' with 'String.format()'*/ \" parsecs\";" +
"}",
"class X {" +
" String s = String.format(\"%s parsecs\", (byte) 321);" +
"}"
);
}
public void testWideningCast() {
doTest("class X {" +
" String s = (long)42 /*_Replace '+' with 'String.format()'*/+ \" the answer to life, the universe and everything\";" +
"}",
"class X {" +
" String s = String.format(\"%d the answer to life, the universe and everything\", 42);" +
"}");
}
public void testCastToChar() {
doTest("class X {" +
" String deepThought(byte b) {" +
" return (char)b/*_Replace '+' with 'String.format()'*/ + \" the answer to life, the universe and everything\";" +
" }" +
"}",
"class X {" +
" String deepThought(byte b) {" +
" return String.format(\"%s the answer to life, the universe and everything\", (char) b);" +
" }" +
"}");
}
}