From 94528b7c672c94450b053ae1bf9b3d6ccbc25a45 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 28 Jul 2022 13:53:06 +0200 Subject: [PATCH] [java-inspections] EscapedSpaceInspection: fix 'Fix all' action (do all the fixes in a single literal even without fix-all) GitOrigin-RevId: d5fa9d51b3b8e911c599be1381b2a9e6a2041223 --- .../escapedSpace/afterMultiple.java | 8 ++ .../escapedSpace/afterMultipleFixAll.java | 8 ++ .../escapedSpace/afterSimple.java | 2 +- .../escapedSpace/beforeMultiple.java | 8 ++ .../escapedSpace/beforeMultipleFixAll.java | 8 ++ .../escapedSpace/beforeSeveral.java | 2 +- .../escapedSpace/beforeSimple.java | 2 +- .../escapedSpace/beforeSlashSlash.java | 2 +- .../escapedSpace/beforeTextBlock.java | 2 +- .../InspectionGadgetsBundle.properties | 2 +- .../ig/style/EscapedSpaceInspection.java | 81 +++++++++++-------- 11 files changed, 85 insertions(+), 40 deletions(-) create mode 100644 java/java-tests/testData/codeInspection/escapedSpace/afterMultiple.java create mode 100644 java/java-tests/testData/codeInspection/escapedSpace/afterMultipleFixAll.java create mode 100644 java/java-tests/testData/codeInspection/escapedSpace/beforeMultiple.java create mode 100644 java/java-tests/testData/codeInspection/escapedSpace/beforeMultipleFixAll.java diff --git a/java/java-tests/testData/codeInspection/escapedSpace/afterMultiple.java b/java/java-tests/testData/codeInspection/escapedSpace/afterMultiple.java new file mode 100644 index 000000000000..2b0a5bdeb3e2 --- /dev/null +++ b/java/java-tests/testData/codeInspection/escapedSpace/afterMultiple.java @@ -0,0 +1,8 @@ +// "Replace '\s' sequences with spaces" "true-preview" +class X { + void test(String str) { + if (str.matches(" a b c d")) { + + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInspection/escapedSpace/afterMultipleFixAll.java b/java/java-tests/testData/codeInspection/escapedSpace/afterMultipleFixAll.java new file mode 100644 index 000000000000..8671261b7686 --- /dev/null +++ b/java/java-tests/testData/codeInspection/escapedSpace/afterMultipleFixAll.java @@ -0,0 +1,8 @@ +// "Fix all 'Non-terminal use of '\s' escape sequence' problems in file" "true" +class X { + void test(String str) { + if (str.matches(" a b c d")) { + + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInspection/escapedSpace/afterSimple.java b/java/java-tests/testData/codeInspection/escapedSpace/afterSimple.java index 1e0358e2a20e..65c750516ccb 100644 --- a/java/java-tests/testData/codeInspection/escapedSpace/afterSimple.java +++ b/java/java-tests/testData/codeInspection/escapedSpace/afterSimple.java @@ -1,4 +1,4 @@ -// "Replace with a space" "true" +// "Replace '\s' sequences with spaces" "true-preview" class X { void test(String str) { if (str.matches(" +")) { diff --git a/java/java-tests/testData/codeInspection/escapedSpace/beforeMultiple.java b/java/java-tests/testData/codeInspection/escapedSpace/beforeMultiple.java new file mode 100644 index 000000000000..875356f1ccad --- /dev/null +++ b/java/java-tests/testData/codeInspection/escapedSpace/beforeMultiple.java @@ -0,0 +1,8 @@ +// "Replace '\s' sequences with spaces" "true-preview" +class X { + void test(String str) { + if (str.matches("\sa\sb\sc\sd")) { + + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInspection/escapedSpace/beforeMultipleFixAll.java b/java/java-tests/testData/codeInspection/escapedSpace/beforeMultipleFixAll.java new file mode 100644 index 000000000000..c760d1014187 --- /dev/null +++ b/java/java-tests/testData/codeInspection/escapedSpace/beforeMultipleFixAll.java @@ -0,0 +1,8 @@ +// "Fix all 'Non-terminal use of '\s' escape sequence' problems in file" "true" +class X { + void test(String str) { + if (str.matches("\sa\sb\sc\sd")) { + + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInspection/escapedSpace/beforeSeveral.java b/java/java-tests/testData/codeInspection/escapedSpace/beforeSeveral.java index d48a12a088f2..ef15c1150e7e 100644 --- a/java/java-tests/testData/codeInspection/escapedSpace/beforeSeveral.java +++ b/java/java-tests/testData/codeInspection/escapedSpace/beforeSeveral.java @@ -1,4 +1,4 @@ -// "Replace with a space" "false" +// "Replace '\s' sequences with spaces" "false" class X { void test() { String s = "\s\s\s!\s\s\s"; diff --git a/java/java-tests/testData/codeInspection/escapedSpace/beforeSimple.java b/java/java-tests/testData/codeInspection/escapedSpace/beforeSimple.java index 09a5b390cef8..b4d073d0e928 100644 --- a/java/java-tests/testData/codeInspection/escapedSpace/beforeSimple.java +++ b/java/java-tests/testData/codeInspection/escapedSpace/beforeSimple.java @@ -1,4 +1,4 @@ -// "Replace with a space" "true" +// "Replace '\s' sequences with spaces" "true-preview" class X { void test(String str) { if (str.matches("\s+")) { diff --git a/java/java-tests/testData/codeInspection/escapedSpace/beforeSlashSlash.java b/java/java-tests/testData/codeInspection/escapedSpace/beforeSlashSlash.java index 5296ee328be6..c85c8658ad21 100644 --- a/java/java-tests/testData/codeInspection/escapedSpace/beforeSlashSlash.java +++ b/java/java-tests/testData/codeInspection/escapedSpace/beforeSlashSlash.java @@ -1,4 +1,4 @@ -// "Replace with a space" "false" +// "Replace '\s' sequences with spaces" "false" class X { void test(String str) { if (str.matches("\\s+")) { diff --git a/java/java-tests/testData/codeInspection/escapedSpace/beforeTextBlock.java b/java/java-tests/testData/codeInspection/escapedSpace/beforeTextBlock.java index e55c45bcfed2..2fa7f40daa7c 100644 --- a/java/java-tests/testData/codeInspection/escapedSpace/beforeTextBlock.java +++ b/java/java-tests/testData/codeInspection/escapedSpace/beforeTextBlock.java @@ -1,4 +1,4 @@ -// "Replace with a space" "false" +// "Replace '\s' sequences with spaces" "false" class X { void test() { String s = """ diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/resources/messages/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/resources/messages/InspectionGadgetsBundle.properties index 383d16170349..e57495b129c7 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/resources/messages/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/resources/messages/InspectionGadgetsBundle.properties @@ -2439,7 +2439,7 @@ try.with.identical.catches.checkbox.different.comments=Do not report catch block inspection.non.strict.comparison.equality.message=Can be replaced with equality inspection.non.strict.comparison.equality.display.name=Non-strict inequality '>=' or '<=' can be replaced with '==' inspection.quirk.method.reference.return.type.message=Target method return type mentions inaccessible class {0}. This will cause IllegalAccessError at runtime. -inspection.use.of.slash.s.fix.family=Replace with a space +inspection.use.of.slash.s.fix.family=Replace '\\s' sequences with spaces inspection.use.of.slash.s.message=Use of '\\s' escape sequence in the middle of line is discouraged inspection.use.of.slash.s.display.name=Non-terminal use of '\\s' escape sequence negative.int.constant.in.long.context.display.name=Negative int hexadecimal constant in long context diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/EscapedSpaceInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/EscapedSpaceInspection.java index 38a334131c04..256b5e333eee 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/EscapedSpaceInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/EscapedSpaceInspection.java @@ -9,6 +9,9 @@ import com.intellij.psi.*; import com.intellij.util.ObjectUtils; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.psiutils.TypeUtils; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import one.util.streamex.IntStreamEx; import org.jetbrains.annotations.NotNull; public class EscapedSpaceInspection extends AbstractBaseJavaLocalInspectionTool implements CleanupLocalInspectionTool { @@ -20,45 +23,49 @@ public class EscapedSpaceInspection extends AbstractBaseJavaLocalInspectionTool public void visitLiteralExpression(@NotNull PsiLiteralExpression literal) { PsiType type = literal.getType(); if (!TypeUtils.isJavaLangString(type)) return; - boolean block = literal.isTextBlock(); - String text = literal.getText(); - int pos = 1; - while (true) { - pos = text.indexOf('\\', pos); - if (pos == -1 || pos == text.length() - 1) return; - char next = text.charAt(pos + 1); - if (next == 'u') { - // unicode escape - pos += 6; - continue; - } - pos += 2; - if (next >= '0' && next <= '9') { - // octal escape - if (pos < text.length() && text.charAt(pos) >= '0' && text.charAt(pos) <= '9') pos++; - if (pos < text.length() && text.charAt(pos) >= '0' && text.charAt(pos) <= '9') pos++; - continue; - } - if (next != 's') { - // other escapes - continue; - } - if (pos > 4 && text.startsWith("\\s", pos - 4)) continue; - if (text.startsWith("\\s", pos)) continue; - if (block && (pos == text.length() || text.charAt(pos) == '\n')) continue; - holder.registerProblem(literal, TextRange.create(pos - 2, pos), + for (int pos : findPositions(literal)) { + holder.registerProblem(literal, TextRange.create(pos, pos + 2), InspectionGadgetsBundle.message("inspection.use.of.slash.s.message"), - new ReplaceWithSpaceFix(pos - 2)); + new ReplaceWithSpaceFix()); } } }; } + private static int[] findPositions(@NotNull PsiLiteralExpression literal) { + boolean block = literal.isTextBlock(); + String text = literal.getText(); + int pos = 1; + IntList list = new IntArrayList(); + while (true) { + pos = text.indexOf('\\', pos); + if (pos == -1 || pos == text.length() - 1) break; + char next = text.charAt(pos + 1); + if (next == 'u') { + // unicode escape + pos += 6; + continue; + } + pos += 2; + if (next >= '0' && next <= '9') { + // octal escape + if (pos < text.length() && text.charAt(pos) >= '0' && text.charAt(pos) <= '9') pos++; + if (pos < text.length() && text.charAt(pos) >= '0' && text.charAt(pos) <= '9') pos++; + continue; + } + if (next != 's') { + // other escapes + continue; + } + if (pos > 4 && text.startsWith("\\s", pos - 4)) continue; + if (text.startsWith("\\s", pos)) continue; + if (block && (pos == text.length() || text.charAt(pos) == '\n')) continue; + list.add(pos - 2); + } + return list.toIntArray(); + } + private static class ReplaceWithSpaceFix implements LocalQuickFix { - private final int myPos; - - ReplaceWithSpaceFix(int pos) { myPos = pos; } - @Override public @NotNull String getFamilyName() { return InspectionGadgetsBundle.message("inspection.use.of.slash.s.fix.family"); @@ -68,9 +75,15 @@ public class EscapedSpaceInspection extends AbstractBaseJavaLocalInspectionTool public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { PsiLiteralExpression literal = ObjectUtils.tryCast(descriptor.getStartElement(), PsiLiteralExpression.class); if (literal == null) return; + int[] positions = findPositions(literal); String text = literal.getText(); - if (text.length() < myPos+2) return; - String newText = text.substring(0, myPos) + ' ' + text.substring(myPos + 2); + String newText = IntStreamEx.of(positions) + .takeWhile(pos -> pos < text.length() - 2) + .boxed() + .prepend(-2) + .append(text.length()) + .pairMap((start, end) -> text.substring(start + 2, end)) + .joining(" "); literal.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(newText, null)); } }