IDEA-292945 New Inspection: \d is redundant with \w in the same class (\D with \W)

GitOrigin-RevId: 7c2e57a4e3bf8a61e08bf326460576766f851a98
This commit is contained in:
Filippova Maria
2022-05-17 22:24:30 +00:00
committed by intellij-monorepo-bot
parent 7e8c6b7725
commit 677bd7e889
5 changed files with 133 additions and 0 deletions
@@ -80,5 +80,8 @@
<localInspection language="RegExp" shortName="RegExpSimplifiable" enabledByDefault="true" level="WEAK WARNING"
bundle="messages.RegExpBundle" groupKey="inspection.group.name.regexp" key="inspection.name.simplifiable.expression"
implementationClass="org.intellij.lang.regexp.inspection.RegExpSimplifiableInspection"/>
<localInspection language="RegExp" shortName="RegExpRedundantClassElement" enabledByDefault="true" level="WEAK WARNING"
bundle="messages.RegExpBundle" groupKey="inspection.group.name.regexp" key="inspection.name.redundant.digit.class.element"
implementationClass="org.intellij.lang.regexp.inspection.RegExpRedundantClassElementInspection"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,16 @@
<html>
<body>
Reports redundant <code>\d</code> or <code>[:digit:]</code> that are used in one class with <code>\w</code> or <code>[:word:]</code>
(<code>\D</code> with <code>\W</code>) and can be removed.
<p><b>Example:</b></p>
<pre><code>
[\w\d]
</code></pre>
<p>After the quick-fix is applied:</p>
<pre><code>
[\w]
</code></pre>
<!-- tooltip end -->
<p><small>New in 2022.2</small>
</body>
</html>
@@ -78,6 +78,7 @@ inspection.name.simplifiable.expression=Regular expression can be simplified
inspection.name.single.character.alternation=Single character alternation
inspection.name.suspicious.backref=Suspicious back reference
inspection.name.unnecessary.non.capturing.group=Unnecessary non-capturing group
inspection.name.redundant.digit.class.element=Redundant '\\d', '[:digit:]', or '\\D' class elements
inspection.option.ignore.escaped.closing.brackets=Ignore escaped closing brackets '}' and ']'
inspection.quick.fix.remove.duplicate.0.from.character.class=Remove duplicate ''{0}'' from character class
inspection.quick.fix.remove.duplicate.branch=Remove duplicate branch
@@ -85,6 +86,8 @@ inspection.quick.fix.remove.duplicate.element.from.character.class=Remove duplic
inspection.quick.fix.remove.empty.branch=Remove empty branch
inspection.quick.fix.remove.redundant.escape=Remove redundant escape
inspection.quick.fix.remove.unnecessary.non.capturing.group=Unwrap unnecessary non-capturing group
inspection.quick.fix.remove.redundant.0.class.element=Remove redundant ''{0}''
inspection.quick.fix.remove.redundant.class.element=Remove redundant class element
inspection.quick.fix.replace.alternation.with.character.class=Replace alternation with character class
inspection.quick.fix.replace.redundant.character.class.with.contents=Replace redundant character class with contents
inspection.quick.fix.replace.with.character.inside.class=Replace with character inside class
@@ -105,6 +108,7 @@ inspection.warning.octal.escape.code.ref.code.in.regexp=Octal escape <code>#ref<
inspection.warning.potential.exponential.backtracking=Potential exponential backtracking
inspection.warning.redundant.character.escape.0.in.regexp=Redundant character escape <code>{0}</code> in RegExp
inspection.warning.redundant.nested.character.class=Redundant nested character class
inspection.warning.redundant.class.element=Redundant ''{0}'' in RegExp
inspection.warning.single.character.alternation.in.regexp=Single character alternation in RegExp
inspection.warning.unnecessary.non.capturing.group=Unnecessary non-capturing group <code>{0}</code>
intention.name.check.regexp=Check RegExp
@@ -0,0 +1,83 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.intellij.lang.regexp.inspection;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import org.intellij.lang.regexp.RegExpBundle;
import org.intellij.lang.regexp.psi.*;
import org.jetbrains.annotations.NotNull;
import static org.intellij.lang.regexp.psi.RegExpSimpleClass.Kind.*;
import static org.intellij.lang.regexp.psi.RegExpSimpleClass.Kind.NON_WORD;
public class RegExpRedundantClassElementInspection extends LocalInspectionTool {
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new RegExpElementVisitor() {
@Override
public void visitRegExpClass(RegExpClass regExpClass) {
RegExpClass regExp = ObjectUtils.tryCast(regExpClass, RegExpClass.class);
if (regExp == null) return;
RegExpClassElement[] classElements = regExp.getElements();
boolean containsNonWordCharacterClass =
ContainerUtil.exists(classElements, RegExpRedundantClassElementInspection::isAnyNonWordCharacter);
boolean containsWordCharacterClass =
ContainerUtil.exists(classElements, RegExpRedundantClassElementInspection::isAnyWordCharacter);
for (RegExpClassElement element : classElements) {
if (containsWordCharacterClass && isAnyDigit(element) || containsNonWordCharacterClass && isAnyNonDigit(element)) {
String elementText = element.getText();
holder.registerProblem(element, RegExpBundle.message("inspection.warning.redundant.class.element", elementText), new RemoveRedundantClassElement(elementText));
}
}
}
};
}
private static boolean isAnyDigit(RegExpClassElement classElement) {
return classElement instanceof RegExpSimpleClass && ((RegExpSimpleClass)classElement).getKind().equals(DIGIT) ||
classElement instanceof RegExpPosixBracketExpression && ((RegExpPosixBracketExpression)classElement).getClassName().equals("digit");
}
private static boolean isAnyNonDigit(RegExpClassElement classElement) {
return classElement instanceof RegExpSimpleClass && ((RegExpSimpleClass)classElement).getKind().equals(NON_DIGIT);
}
private static boolean isAnyWordCharacter(RegExpClassElement classElement) {
return classElement instanceof RegExpSimpleClass && ((RegExpSimpleClass)classElement).getKind().equals(WORD) ||
classElement instanceof RegExpPosixBracketExpression && ((RegExpPosixBracketExpression)classElement).getClassName().equals("word");
}
private static boolean isAnyNonWordCharacter(RegExpClassElement classElement) {
return classElement instanceof RegExpSimpleClass && ((RegExpSimpleClass)classElement).getKind().equals(NON_WORD);
}
private static class RemoveRedundantClassElement implements LocalQuickFix {
private final String myClassElementText;
private RemoveRedundantClassElement(String text) { myClassElementText = text; }
@Override
public @NotNull String getFamilyName() {
return RegExpBundle.message("inspection.quick.fix.remove.redundant.class.element");
}
@Override
public @NotNull String getName() {
return RegExpBundle.message("inspection.quick.fix.remove.redundant.0.class.element", myClassElementText);
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getPsiElement();
if (element == null) return;
element.delete();
}
}
}
@@ -0,0 +1,27 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.intellij.lang.regexp.inspection;
import com.intellij.codeInspection.LocalInspectionTool;
import org.intellij.lang.regexp.RegExpBundle;
import org.jetbrains.annotations.NotNull;
public class RegExpRedundantClassElementInspectionTest extends RegExpInspectionTestCase {
@Override
protected @NotNull LocalInspectionTool getInspection() {
return new RegExpRedundantClassElementInspection();
}
public void testAnyDigit() {
quickfixTest("[\\w.<weak_warning descr=\"Redundant '\\d' in RegExp\"><caret>\\d</weak_warning>]",
"[\\w.]", RegExpBundle.message("inspection.quick.fix.remove.redundant.0.class.element", "\\d"));
}
public void testAnyNonDigit() {
quickfixTest("^[\\W.<weak_warning descr=\"Redundant '\\D' in RegExp\"><caret>\\D</weak_warning>]",
"^[\\W.]", RegExpBundle.message("inspection.quick.fix.remove.redundant.0.class.element", "\\D"));
}
public void testNoHighlighting() {
highlightTest("\\w{2}[.,]?[\\d\\W]");
}
}