From ccc210eeb20b2d2e6e3c322345c2e0881a132acc Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 17 Dec 2021 16:46:29 +0100 Subject: [PATCH] RegExp: don't report \{ as redundant escape and add option to not report \} and \] (IDEA-243874, WEB-54046, PY-47380) GitOrigin-RevId: 1ce7073269aaa2a1cd843765f97820bca8155747 --- .../RegExpRedundantEscape.html | 9 ++++++++ .../messages/RegExpBundle.properties | 5 ++-- .../RegExpRedundantEscapeInspection.java | 23 +++++++++++++++++-- .../RedundantEscapeInspectionTest.java | 6 ++++- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/RegExpSupport/resources/inspectionDescriptions/RegExpRedundantEscape.html b/RegExpSupport/resources/inspectionDescriptions/RegExpRedundantEscape.html index 4087d9ce428f..edba6fd7c4aa 100644 --- a/RegExpSupport/resources/inspectionDescriptions/RegExpRedundantEscape.html +++ b/RegExpSupport/resources/inspectionDescriptions/RegExpRedundantEscape.html @@ -2,6 +2,11 @@ Reports character escapes that are replaceable with the unescaped character without a change in meaning. Note that inside the square brackets of a character class, many escapes are unnecessary that would be necessary outside of a character class. +

+This inspection doesn't warn on opening curly braces ({) outside of character classes ([]). +Removing the escape before an opening curly brace can cause confusion, +even though this is allowed in e.g. the Javascript and Python regex dialects. +It would also make the regex pattern less portable, because many other dialects don't allow unescaped curly braces as characters.

Example:


   \-\;[\.]
@@ -11,6 +16,10 @@ Note that inside the square brackets of a character class, many escapes are unne
   -;[.]
 
+

+Use the Ignore escaped closing brackets '}' and ']' option +to specify if '\}' and '\]' should be reported when they are not inside a character class +and they are allowed to be unescaped by the RegExp dialect.

New in 2017.3 \ No newline at end of file diff --git a/RegExpSupport/resources/messages/RegExpBundle.properties b/RegExpSupport/resources/messages/RegExpBundle.properties index 3d289bbae58a..790599d4cbcc 100644 --- a/RegExpSupport/resources/messages/RegExpBundle.properties +++ b/RegExpSupport/resources/messages/RegExpBundle.properties @@ -26,7 +26,7 @@ error.atomic.groups.are.not.supported.in.this.regex.dialect=Atomic groups are no error.back.reference.is.nested.into.the.capturing.group.it.refers.to=Back reference is nested into the capturing group it refers to error.conditional.group.reference.not.allowed.inside.lookbehind=Conditional group reference not allowed inside lookbehind error.conditionals.are.not.supported.in.this.regex.dialect=Conditionals are not supported in this regex dialect -error.dangling.metacharacter=Dangling metacharacter +error.dangling.metacharacter=Dangling quantifier ''{0}'' error.embedded.comments.are.not.supported.in.this.regex.dialect=Embedded comments are not supported in this regex dialect error.empty.group=Empty group error.group.reference.is.nested.into.the.named.group.it.refers.to=Group reference is nested into the named group it refers to @@ -77,6 +77,7 @@ inspection.name.redundant.nested.character.class=Redundant nested character clas 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.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 inspection.quick.fix.remove.duplicate.element.from.character.class=Remove duplicate element from character class @@ -137,7 +138,7 @@ parse.error.unclosed.options.group=Unclosed options group parse.error.unclosed.posix.bracket.expression=Unclosed POSIX bracket expression parse.error.unclosed.property=Unclosed property parse.error.unicode.character.name.expected=Unicode character name expected -parse.error.unmatched.closing.parenthesis=Unmatched closing ')' +parse.error.unmatched.closing.bracket=Unmatched closing ''{0}'' surrounder.atomic.group.pattern=Atomic Group (?:pattern) surrounder.capturing.group.pattern=Capturing Group (pattern) surrounder.negative.lookahead.pattern=Negative Lookahead (?!pattern) diff --git a/RegExpSupport/src/org/intellij/lang/regexp/inspection/RegExpRedundantEscapeInspection.java b/RegExpSupport/src/org/intellij/lang/regexp/inspection/RegExpRedundantEscapeInspection.java index a7dc85f02cf8..9a25c7ebbec1 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/inspection/RegExpRedundantEscapeInspection.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/inspection/RegExpRedundantEscapeInspection.java @@ -1,10 +1,11 @@ -// 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. +// Copyright 2000-2021 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.codeInspection.ui.SingleCheckboxOptionsPanel; import com.intellij.lang.ASTNode; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; @@ -12,22 +13,34 @@ import org.intellij.lang.regexp.RegExpBundle; import org.intellij.lang.regexp.RegExpLanguageHosts; import org.intellij.lang.regexp.RegExpTT; import org.intellij.lang.regexp.psi.RegExpChar; +import org.intellij.lang.regexp.psi.RegExpClass; import org.intellij.lang.regexp.psi.RegExpElementVisitor; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; /** * @author Bas Leijdekkers */ public class RegExpRedundantEscapeInspection extends LocalInspectionTool { + public boolean ignoreEscapedMetaCharacters = false; + + @Override + public @Nullable JComponent createOptionsPanel() { + return new SingleCheckboxOptionsPanel(RegExpBundle.message("inspection.option.ignore.escaped.closing.brackets"), + this, "ignoreEscapedMetaCharacters"); + } + @NotNull @Override public RegExpElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { return new RedundantEscapeVisitor(holder); } - private static class RedundantEscapeVisitor extends RegExpElementVisitor { + private class RedundantEscapeVisitor extends RegExpElementVisitor { private final ProblemsHolder myHolder; @@ -41,6 +54,12 @@ public class RegExpRedundantEscapeInspection extends LocalInspectionTool { if (!text.startsWith("\\") || !RegExpLanguageHosts.getInstance().isRedundantEscape(ch, text)) { return; } + if (text.equals("\\{") && !(ch.getParent() instanceof RegExpClass)) { + return; + } + if (ignoreEscapedMetaCharacters && (text.equals("\\}") || text.equals("\\]")) && !(ch.getParent() instanceof RegExpClass)) { + return; + } final ASTNode astNode = ch.getNode().getFirstChildNode(); if (astNode == null || astNode.getElementType() != RegExpTT.REDUNDANT_ESCAPE) { return; diff --git a/RegExpSupport/test/org/intellij/lang/regexp/inspection/RedundantEscapeInspectionTest.java b/RegExpSupport/test/org/intellij/lang/regexp/inspection/RedundantEscapeInspectionTest.java index 611f9120e88d..60c61c39d9ed 100644 --- a/RegExpSupport/test/org/intellij/lang/regexp/inspection/RedundantEscapeInspectionTest.java +++ b/RegExpSupport/test/org/intellij/lang/regexp/inspection/RedundantEscapeInspectionTest.java @@ -1,4 +1,4 @@ -// 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. +// Copyright 2000-2021 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; @@ -33,6 +33,10 @@ public class RedundantEscapeInspectionTest extends RegExpInspectionTestCase { highlightTest("\\#(?x)\\#"); } + public void testCurlyBrace() { + highlightTest("\\{TEST}", RegExpFileType.forLanguage(EcmaScriptRegexpLanguage.INSTANCE)); + } + @NotNull @Override protected LocalInspectionTool getInspection() {