diff --git a/RegExpSupport/resources/META-INF/RegExpPlugin.xml b/RegExpSupport/resources/META-INF/RegExpPlugin.xml
index ae910d4a9c0b..6dd0016171bf 100644
--- a/RegExpSupport/resources/META-INF/RegExpPlugin.xml
+++ b/RegExpSupport/resources/META-INF/RegExpPlugin.xml
@@ -74,6 +74,9 @@
Everybody be cool, (?:this) is a robbery!Everybody be cool, this is a robbery!.
+
+New in 2021.1
+
+
\ No newline at end of file
diff --git a/RegExpSupport/resources/messages/RegExpBundle.properties b/RegExpSupport/resources/messages/RegExpBundle.properties
index a7f6edf0cfe5..87ebb0aa49db 100644
--- a/RegExpSupport/resources/messages/RegExpBundle.properties
+++ b/RegExpSupport/resources/messages/RegExpBundle.properties
@@ -73,11 +73,13 @@ 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.name.unnecessary.non.capturing.group=Unnecessary non-capturing group
inspection.quick.fix.remove.duplicate.0.from.character.class=Remove duplicate ''{0}'' from character class
inspection.quick.fix.remove.duplicate.branch=Remove duplicate branch
inspection.quick.fix.remove.duplicate.element.from.character.class=Remove duplicate element from character class
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.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
@@ -95,6 +97,7 @@ inspection.warning.potential.exponential.backtracking=Potential exponential back
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
+inspection.warning.unnecessary.non.capturing.group=Unnecessary non-capturing group {0}
intention.name.check.regexp=Check RegExp
intention.name.simplify.quantifier=Simplify quantifier
label.regexp=&RegExp:
diff --git a/RegExpSupport/src/org/intellij/lang/regexp/inspection/UnnecessaryNonCapturingGroupInspection.java b/RegExpSupport/src/org/intellij/lang/regexp/inspection/UnnecessaryNonCapturingGroupInspection.java
new file mode 100644
index 000000000000..dbbb1b79385c
--- /dev/null
+++ b/RegExpSupport/src/org/intellij/lang/regexp/inspection/UnnecessaryNonCapturingGroupInspection.java
@@ -0,0 +1,95 @@
+// 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.codeInspection.util.IntentionFamilyName;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import org.intellij.lang.regexp.RegExpBundle;
+import org.intellij.lang.regexp.psi.RegExpBranch;
+import org.intellij.lang.regexp.psi.RegExpElementVisitor;
+import org.intellij.lang.regexp.psi.RegExpGroup;
+import org.intellij.lang.regexp.psi.RegExpPattern;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Bas Leijdekkers
+ */
+public class UnnecessaryNonCapturingGroupInspection extends LocalInspectionTool {
+
+ @Override
+ public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
+ return new UnnecessaryNonCapturingGroupVisitor(holder);
+ }
+
+ private static class UnnecessaryNonCapturingGroupVisitor extends RegExpElementVisitor {
+
+ private final ProblemsHolder myHolder;
+
+ private UnnecessaryNonCapturingGroupVisitor(ProblemsHolder holder) {
+ myHolder = holder;
+ }
+
+ @Override
+ public void visitRegExpGroup(RegExpGroup group) {
+ super.visitRegExpGroup(group);
+ if (group.getType() != RegExpGroup.Type.NON_CAPTURING) {
+ return;
+ }
+ final PsiElement parent = group.getParent();
+ if (parent instanceof RegExpBranch) {
+ if (hasOneBranch(group.getPattern())) {
+ registerProblem(group);
+ }
+ else {
+ final PsiElement grandParent = parent.getParent();
+ if (grandParent instanceof RegExpPattern && hasSingleAtom((RegExpPattern)grandParent)) {
+ registerProblem(group);
+ }
+ }
+ }
+ else if (hasSingleAtom(group.getPattern())) {
+ registerProblem(group);
+ }
+ }
+
+ void registerProblem(RegExpGroup group) {
+ myHolder.registerProblem(group.getFirstChild(),
+ RegExpBundle.message("inspection.warning.unnecessary.non.capturing.group", group.getText()),
+ new UnnecessaryNonCapturingGroupFix());
+ }
+ }
+
+ private static boolean hasOneBranch(RegExpPattern pattern) {
+ return pattern != null && pattern.getBranches().length == 1;
+ }
+
+ private static boolean hasSingleAtom(RegExpPattern pattern) {
+ if (pattern == null) {
+ return false;
+ }
+ final RegExpBranch[] branches = pattern.getBranches();
+ return branches.length == 1 && branches[0].getAtoms().length == 1;
+ }
+
+ private static class UnnecessaryNonCapturingGroupFix implements LocalQuickFix {
+ @Override
+ public @IntentionFamilyName @NotNull String getFamilyName() {
+ return RegExpBundle.message("inspection.quick.fix.remove.unnecessary.non.capturing.group");
+ }
+
+ @Override
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ final PsiElement element = descriptor.getPsiElement().getParent();
+ if (!(element instanceof RegExpGroup)) {
+ return;
+ }
+ final RegExpGroup group = (RegExpGroup)element;
+ RegExpReplacementUtil.replaceInContext(group, group.getPattern().getUnescapedText());
+ }
+ }
+}
diff --git a/RegExpSupport/test/org/intellij/lang/regexp/inspection/UnnecessaryNonCapturingGroupInspectionTest.java b/RegExpSupport/test/org/intellij/lang/regexp/inspection/UnnecessaryNonCapturingGroupInspectionTest.java
new file mode 100644
index 000000000000..d52594d50bea
--- /dev/null
+++ b/RegExpSupport/test/org/intellij/lang/regexp/inspection/UnnecessaryNonCapturingGroupInspectionTest.java
@@ -0,0 +1,48 @@
+// 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 UnnecessaryNonCapturingGroupInspectionTest extends RegExpInspectionTestCase {
+
+ public void testSimple() {
+ quickfixTest("abc