diff --git a/RegExpSupport/resources/META-INF/RegExpPlugin.xml b/RegExpSupport/resources/META-INF/RegExpPlugin.xml
index 96f0d73ec80c..dc012b8e9110 100644
--- a/RegExpSupport/resources/META-INF/RegExpPlugin.xml
+++ b/RegExpSupport/resources/META-INF/RegExpPlugin.xml
@@ -71,6 +71,9 @@
[a-c[x-z]], which is equivalent too [a-cx-z].
+
+New in 2020.2
+
+
\ No newline at end of file
diff --git a/RegExpSupport/resources/messages/RegExpBundle.properties b/RegExpSupport/resources/messages/RegExpBundle.properties
index a213da895e9a..d0efe8fc5618 100644
--- a/RegExpSupport/resources/messages/RegExpBundle.properties
+++ b/RegExpSupport/resources/messages/RegExpBundle.properties
@@ -68,6 +68,7 @@ inspection.name.empty.branch.in.alternation=Empty branch in alternation
inspection.name.escaped.meta.character=Escaped meta character
inspection.name.octal.escape=Octal escape
inspection.name.redundant.character.escape=Redundant character escape
+inspection.name.redundant.nested.character.class=Redundant nested character class
inspection.name.single.character.alternation=Single character alternation
inspection.quick.fix.remove.duplicate.0.from.character.class=Remove duplicate ''{0}'' from character class
inspection.quick.fix.remove.duplicate.branch=Remove duplicate branch
@@ -75,6 +76,7 @@ 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.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
inspection.quick.fix.replace.with.hexadecimal.escape=Replace with hexadecimal escape
inspection.quick.fix.replace.with.space.and.repeated.quantifier=Replace with space and repeated quantifier
@@ -87,6 +89,7 @@ inspection.warning.numeric.back.reference=Numeric back reference
inspection.warning.octal.escape.code.ref.code.in.regexp=Octal escape #ref in RegExp
inspection.warning.potential.exponential.backtracking=Potential exponential backtracking
inspection.warning.redundant.character.escape.0.in.regexp=Redundant character escape {0} in RegExp
+inspection.warning.redundant.nested.character.class=Redundant nested character class
inspection.warning.single.character.alternation.in.regexp=Single character alternation in RegExp
intention.balloon.bad.pattern=Bad pattern
intention.balloon.matches=Matches!
diff --git a/RegExpSupport/src/org/intellij/lang/regexp/inspection/RedundantNestedCharacterClassInspection.java b/RegExpSupport/src/org/intellij/lang/regexp/inspection/RedundantNestedCharacterClassInspection.java
new file mode 100644
index 000000000000..5e751f51613c
--- /dev/null
+++ b/RegExpSupport/src/org/intellij/lang/regexp/inspection/RedundantNestedCharacterClassInspection.java
@@ -0,0 +1,82 @@
+// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+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.openapi.util.NlsContexts;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import org.intellij.lang.regexp.RegExpBundle;
+import org.intellij.lang.regexp.psi.RegExpClass;
+import org.intellij.lang.regexp.psi.RegExpClassElement;
+import org.intellij.lang.regexp.psi.RegExpElementVisitor;
+import org.intellij.lang.regexp.psi.RegExpIntersection;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Bas Leijdekkers
+ */
+public class RedundantNestedCharacterClassInspection extends LocalInspectionTool {
+
+ @Override
+ public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
+ return new RedundantNestedCharacterClassVisitor(holder);
+ }
+
+ private static class RedundantNestedCharacterClassVisitor extends RegExpElementVisitor {
+
+ private final ProblemsHolder myHolder;
+
+ RedundantNestedCharacterClassVisitor(ProblemsHolder holder) {
+ myHolder = holder;
+ }
+
+ @Override
+ public void visitRegExpClass(RegExpClass regExpClass) {
+ super.visitRegExpClass(regExpClass);
+ final PsiElement parent = regExpClass.getParent();
+ if (parent instanceof RegExpClass) {
+ final RegExpClass parentClass = (RegExpClass)parent;
+ if (parentClass.isNegated() == regExpClass.isNegated()) {
+ myHolder.registerProblem(regExpClass.getFirstChild(), RegExpBundle.message("inspection.warning.redundant.nested.character.class"),
+ new RedundantNestedCharacterClassFix());
+ }
+ }
+ else if (parent instanceof RegExpIntersection) {
+ final PsiElement grandParent = parent.getParent();
+ if (grandParent instanceof RegExpClass) {
+ final RegExpClass parentClass = (RegExpClass)grandParent;
+ if (parentClass.isNegated() == regExpClass.isNegated()) {
+ myHolder.registerProblem(regExpClass.getFirstChild(), RegExpBundle.message("inspection.warning.redundant.nested.character.class"),
+ new RedundantNestedCharacterClassFix());
+ }
+ }
+ }
+ }
+
+ private static class RedundantNestedCharacterClassFix implements LocalQuickFix {
+
+ @Override
+ public @NlsContexts.ListItem @NotNull String getFamilyName() {
+ return RegExpBundle.message("inspection.quick.fix.replace.redundant.character.class.with.contents");
+ }
+
+ @Override
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ final PsiElement element = descriptor.getPsiElement().getParent();
+ if (element instanceof RegExpClass) {
+ final RegExpClass regExpClass = (RegExpClass)element;
+ final RegExpClassElement[] elements = regExpClass.getElements();
+ final PsiElement parent = regExpClass.getParent();
+ for (RegExpClassElement classElement : elements) {
+ parent.addBefore(classElement, regExpClass);
+ }
+ regExpClass.delete();
+ }
+ }
+ }
+ }
+}
diff --git a/RegExpSupport/test/org/intellij/lang/regexp/inspection/RedundantNestedCharacterClassInspectionTest.java b/RegExpSupport/test/org/intellij/lang/regexp/inspection/RedundantNestedCharacterClassInspectionTest.java
new file mode 100644
index 000000000000..b7161e5154ff
--- /dev/null
+++ b/RegExpSupport/test/org/intellij/lang/regexp/inspection/RedundantNestedCharacterClassInspectionTest.java
@@ -0,0 +1,37 @@
+// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.intellij.lang.regexp.inspection;
+
+import com.intellij.codeInspection.LocalInspectionTool;
+import org.intellij.lang.regexp.RegExpBundle;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Bas Leijdekkers
+ */
+public class RedundantNestedCharacterClassInspectionTest extends RegExpInspectionTestCase {
+
+ public void testConjunction() {
+ quickfixTest("[a