Java: fix IOE on replacing single character string operation

GitOrigin-RevId: 26ba697294eebb109148b86672a22e7aa2e921e4
This commit is contained in:
Bas Leijdekkers
2022-08-29 15:14:56 +02:00
committed by intellij-monorepo-bot
parent 26bbaa3c5f
commit e7c55493a1
7 changed files with 64 additions and 65 deletions

View File

@@ -101,14 +101,16 @@ public final class PsiLiteralUtil {
}
}
// convert text to number according to radix specified
// if number is more than maxBits bits long, throws NumberFormatException
/**
* convert text to number according to radix specified
* if number is more than maxBits bits long, throws NumberFormatException
*/
public static long parseDigits(final String text, final int bitsInRadix, final int maxBits) throws NumberFormatException {
final int radix = 1 << bitsInRadix;
final int textLength = text.length();
if (textLength == 0) {
throw new NumberFormatException(text);
}
final int radix = 1 << bitsInRadix;
long integer = textLength == 1 ? 0 : Long.parseLong(text.substring(0, textLength - 1), radix);
if ((integer & (-1L << (maxBits - bitsInRadix))) != 0) {
throw new NumberFormatException(text);
@@ -143,29 +145,29 @@ public final class PsiLiteralUtil {
}
/**
* Convert a string that contains a character (e.g. ""\n"" or ""\\"", etc.)
* Convert a string that contains a single character (e.g. ""\n"" or ""\\"", etc.)
* to a character literal string (e.g. "'\n'" or "'\\'", etc.)
*
* @param text a string to convert
* @return the converted string
* @param expression a single character string
* @return the character literal string
*/
@NotNull
public static String charLiteralForCharString(@NotNull final String text) {
final int length = text.length();
if (length <= 1) return text;
final String character = text.substring(1, length - 1);
final String charLiteral;
if ("'".equals(character)) {
charLiteral = "'\\''";
@Nullable
public static String charLiteralString(PsiLiteralExpression expression) {
final Object value = expression.getValue();
if (!(value instanceof String) || ((CharSequence)value).length() > 1) {
throw new IllegalArgumentException();
}
else if ("\\\"".equals(character)) {
charLiteral = "'\"'";
final String content = expression.isTextBlock()
? getTextBlockText(expression)
: getStringLiteralContent(expression);
if (content == null) {
return null;
}
else {
charLiteral = '\'' + character + '\'';
switch (content) {
case "'": return "'\\''";
case "\\\"": return "'\"'";
default: return '\'' + content + '\'';
}
return charLiteral;
}
/**
@@ -293,10 +295,9 @@ public final class PsiLiteralUtil {
int i = parseBackSlash(s, start);
if (i == -1) return -1;
int prev = start;
int nextIdx;
int nSlashes = 1;
while (i < s.length()) {
nextIdx = parseBackSlash(s, i);
int nextIdx = parseBackSlash(s, i);
if (nextIdx != -1) {
result.append(s, prev, i);
prev = i;
@@ -517,8 +518,8 @@ public final class PsiLiteralUtil {
if (Character.isWhitespace(s.charAt(index))) return index;
index = parseUnicodeEscapeBackwards(s, index, Character::isWhitespace);
if (index < 0) return -1;
int nBackSlashes = 1;
index--;
int nBackSlashes = 1;
if (index >= 0 && s.charAt(index) == '\\') {
nBackSlashes++;
nBackSlashes += countBackSlashes(s, index - 1);
@@ -724,7 +725,6 @@ public final class PsiLiteralUtil {
curOffset += linePrefixLength;
int charIdx;
int nextIdx = 0;
while (true) {
if (from == charsSoFar) {
@@ -733,7 +733,7 @@ public final class PsiLiteralUtil {
if (to == charsSoFar) {
return new TextRange(mappedFrom, curOffset + nextIdx);
}
charIdx = nextIdx;
int charIdx = nextIdx;
nextIdx = getCharEndIndex(line, charIdx);
if (nextIdx == -1) break;
charsSoFar++;

View File

@@ -0,0 +1,6 @@
// "Fix all 'Redundant 'String' operation' problems in file" "true"
class X {
void x(String message) {
boolean underline = message.charAt(1) == '_';
}
}

View File

@@ -0,0 +1,7 @@
// "Fix all 'Redundant 'String' operation' problems in file" "true"
class X {
void x(String message) {
boolean underline = message.<caret>substring(1, 2).equals("""
_""");
}
}

View File

@@ -22,6 +22,6 @@ public class RedundantStringOperationInspectionFixTest extends LightQuickFixPara
@Override
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
return LightJavaCodeInsightFixtureTestCase.JAVA_11;
return LightJavaCodeInsightFixtureTestCase.JAVA_15;
}
}

View File

@@ -19,7 +19,8 @@ import com.intellij.codeInspection.CleanupLocalInspectionTool;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PsiLiteralUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
@@ -43,9 +44,7 @@ public class LengthOneStringsInConcatenationInspection extends BaseInspection im
public String buildErrorString(Object... infos) {
final String string = (String)infos[0];
final String escapedString = StringUtil.escapeStringCharacters(string);
return InspectionGadgetsBundle.message(
"expression.can.be.replaced.problem.descriptor",
escapedString);
return InspectionGadgetsBundle.message("expression.can.be.replaced.problem.descriptor", escapedString);
}
@Override
@@ -53,24 +52,24 @@ public class LengthOneStringsInConcatenationInspection extends BaseInspection im
return new ReplaceStringsWithCharsFix();
}
private static class ReplaceStringsWithCharsFix
extends InspectionGadgetsFix {
private static class ReplaceStringsWithCharsFix extends InspectionGadgetsFix {
@Override
@NotNull
public String getFamilyName() {
return InspectionGadgetsBundle.message(
"length.one.strings.in.concatenation.replace.quickfix");
return InspectionGadgetsBundle.message("length.one.strings.in.concatenation.replace.quickfix");
}
@Override
public void doFix(Project project, ProblemDescriptor descriptor) {
final PsiExpression expression = (PsiExpression)descriptor.getPsiElement();
final PsiLiteralExpression expression = (PsiLiteralExpression)descriptor.getPsiElement();
if (ExpressionUtils.isConversionToStringNecessary(expression, false)) {
return;
}
final String text = expression.getText();
final String charLiteral = PsiLiteralUtil.charLiteralForCharString(text);
final String charLiteral = PsiLiteralUtil.charLiteralString(expression);
if (charLiteral == null) {
return;
}
PsiReplacementUtil.replaceExpression(expression, charLiteral);
}
}
@@ -80,8 +79,7 @@ public class LengthOneStringsInConcatenationInspection extends BaseInspection im
return new LengthOneStringsInConcatenationVisitor();
}
private static class LengthOneStringsInConcatenationVisitor
extends BaseInspectionVisitor {
private static class LengthOneStringsInConcatenationVisitor extends BaseInspectionVisitor {
@Override
public void visitLiteralExpression(@NotNull PsiLiteralExpression expression) {

View File

@@ -627,8 +627,7 @@ public class RedundantStringOperationInspection extends AbstractBaseJavaLocalIns
@NotNull private final String myConverted;
private final boolean myEquality;
SubstringToCharAtQuickFix(@NotNull final String text,
final @NotNull String converted, boolean equality) {
SubstringToCharAtQuickFix(@NotNull final String text, final @NotNull String converted, boolean equality) {
myText = text;
myConverted = converted;
myEquality = equality;
@@ -683,7 +682,7 @@ public class RedundantStringOperationInspection extends AbstractBaseJavaLocalIns
}
private static @NonNls @Nullable String getTargetString(@NotNull final PsiMethodCallExpression call,
@NotNull Function<@NotNull PsiElement, @NotNull String> textExtractor) {
@NotNull Function<@NotNull PsiElement, @NotNull String> textExtractor) {
final PsiMethodCallExpression qualifierCall = MethodCallUtils.getQualifierMethodCall(call);
if (qualifierCall == null) return null;
@@ -693,23 +692,18 @@ public class RedundantStringOperationInspection extends AbstractBaseJavaLocalIns
final PsiExpression[] args = qualifierCall.getArgumentList().getExpressions();
if (args.length != 2) return null;
final PsiExpression equalTo = call.getArgumentList().getExpressions()[0];
final String eqSign = isNegated(call, false) ? "!=" : "==";
final String equalToValue = PsiLiteralUtil.charLiteralForCharString(textExtractor.apply(equalTo));
final PsiLiteralExpression equalTo = (PsiLiteralExpression)call.getArgumentList().getExpressions()[0];
final String equalToValue = PsiLiteralUtil.charLiteralString(equalTo);
return String.format("%s.charAt(%s) %s %s",
textExtractor.apply(receiver),
textExtractor.apply(args[0]),
eqSign,
isNegated(call, false) ? "!=" : "==",
equalToValue
);
}
}
}
private static class RemoveRedundantChangeCaseFix implements LocalQuickFix {
private final @NotNull String caseRedundant;
private final @NotNull PlaceCaseEqualType myPlaceCaseEqualType;
@@ -916,14 +910,9 @@ public class RedundantStringOperationInspection extends AbstractBaseJavaLocalIns
private final @IntentionName String myName;
private StringConstructorFix(boolean noArguments) {
if (noArguments) {
myName = InspectionGadgetsBundle.message(
"inspection.redundant.string.replace.with.empty.fix.name");
}
else {
myName = InspectionGadgetsBundle.message(
"inspection.redundant.string.replace.with.arg.fix.name");
}
myName = noArguments
? InspectionGadgetsBundle.message("inspection.redundant.string.replace.with.empty.fix.name")
: InspectionGadgetsBundle.message("inspection.redundant.string.replace.with.arg.fix.name");
}
@Override

View File

@@ -9,44 +9,43 @@ import com.siyeh.ig.performance.LengthOneStringsInConcatenationInspection;
/**
* @author Fabrice TIERCELIN
*/
@SuppressWarnings("SingleCharacterStringConcatenation")
public class LengthOneStringsInConcatenationFixTest extends IGQuickFixesTestCase {
@SuppressWarnings("SingleCharacterStringConcatenation")
public void testFirstConcatenationOperand() {
doExpressionTest(InspectionGadgetsBundle.message("length.one.strings.in.concatenation.replace.quickfix"),
"\"f\"/**/ + \"bar\"", "'f' + \"bar\"");
}
@SuppressWarnings("SingleCharacterStringConcatenation")
public void testSecondConcatenationOperand() {
doExpressionTest(InspectionGadgetsBundle.message("length.one.strings.in.concatenation.replace.quickfix"),
"\"foo\" + /**/\"b\"", "\"foo\" + 'b'");
}
@SuppressWarnings("SingleCharacterStringConcatenation")
public void testThirdConcatenationOperand() {
doExpressionTest(InspectionGadgetsBundle.message("length.one.strings.in.concatenation.replace.quickfix"),
"\"foo\" + 1 + /**/\"c\"", "\"foo\" + 1 + 'c'");
}
@SuppressWarnings("SingleCharacterStringConcatenation")
public void testAppendMethodParameter() {
doExpressionTest(InspectionGadgetsBundle.message("length.one.strings.in.concatenation.replace.quickfix"),
"new StringBuilder().append(/**/\"c\")", "new StringBuilder().append('c')");
}
@SuppressWarnings("SingleCharacterStringConcatenation")
public void testNewLine() {
doExpressionTest(InspectionGadgetsBundle.message("length.one.strings.in.concatenation.replace.quickfix"),
"\"\\n\"/**/ + \"bar\"", "'\\n' + \"bar\"");
}
@SuppressWarnings("SingleCharacterStringConcatenation")
public void testTextBlock() {
doExpressionTest(InspectionGadgetsBundle.message("length.one.strings.in.concatenation.replace.quickfix"),
"\"string\" + /**/\"\"\"\n !\"\"\"", "\"string\" + '!'");
}
public void testQuote() {
doExpressionTest(InspectionGadgetsBundle.message("length.one.strings.in.concatenation.replace.quickfix"),
"\"\\'\"/**/ + \"bar\"", "'\\'' + \"bar\"");
}
@SuppressWarnings("SingleCharacterStringConcatenation")
public void testDoubleQuote() {
doExpressionTest(InspectionGadgetsBundle.message("length.one.strings.in.concatenation.replace.quickfix"),
"\"\\\"\"/**/ + \"bar\"", "'\"' + \"bar\"");