mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
RegExp: new "Anonymous capturing group or numeric back reference" inspection
This commit is contained in:
@@ -45,5 +45,8 @@
|
||||
<localInspection groupName="RegExp" language="RegExp" shortName="EmptyAlternationBranch" displayName="Empty branch in alternation"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="org.intellij.lang.regexp.inspection.EmptyAlternationBranchInspection"/>
|
||||
<localInspection groupName="RegExp" language="RegExp" shortName="AnonymousGroup"
|
||||
displayName="Anonymous capturing group or numeric back reference" enabledByDefault="false" level="WARNING"
|
||||
implementationClass="org.intellij.lang.regexp.inspection.AnonymousGroupInspection"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports anonymous capturing groups and numeric back references in a RegExp.
|
||||
These are only reported when the RegExp dialect supports named group and named group references.
|
||||
Named groups and named back references improve code readability and are recommended to use instead.
|
||||
When a capture is not needed, matching can be more performant and use less memory by using a non-capturing group,
|
||||
i.e. <b>(?:xxx)</b> instead of <b>(xxx)</b>.
|
||||
<!-- tooltip end -->
|
||||
<p><small>New in 2017.2</small>
|
||||
</body>
|
||||
</html>
|
||||
@@ -20,16 +20,25 @@ import org.intellij.lang.regexp.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public interface RegExpLanguageHost {
|
||||
|
||||
EnumSet<RegExpGroup.Type> EMPTY_NAMED_GROUP_TYPES = EnumSet.noneOf(RegExpGroup.Type.class);
|
||||
|
||||
boolean characterNeedsEscaping(char c);
|
||||
boolean supportsPerl5EmbeddedComments();
|
||||
boolean supportsPossessiveQuantifiers();
|
||||
boolean supportsPythonConditionalRefs();
|
||||
boolean supportsNamedGroupSyntax(RegExpGroup group);
|
||||
boolean supportsNamedGroupRefSyntax(RegExpNamedGroupRef ref);
|
||||
@NotNull
|
||||
default EnumSet<RegExpGroup.Type> getSupportedNamedGroupTypes(RegExpElement context) {
|
||||
return EMPTY_NAMED_GROUP_TYPES;
|
||||
}
|
||||
boolean supportsExtendedHexCharacter(RegExpChar regExpChar);
|
||||
|
||||
default boolean isValidGroupName(String name, @NotNull PsiElement context) {
|
||||
@@ -116,7 +125,7 @@ public interface RegExpLanguageHost {
|
||||
/** Finite repetition inside lookbehind with different minimum, maximum values allowed */
|
||||
FINITE_REPETITION,
|
||||
|
||||
/** Full regex syntax inside lookbehind ,i.e. star (*) and plus (*) repetition and backreferences, allowed. */
|
||||
/** Full regex syntax inside lookbehind, i.e. star (*) and plus (*) repetition and backreferences, allowed. */
|
||||
FULL
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
@@ -106,6 +109,14 @@ public final class RegExpLanguageHosts extends ClassExtension<RegExpLanguageHost
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<RegExpGroup.Type> getSupportedNamedGroupTypes(RegExpElement context) {
|
||||
final RegExpLanguageHost host = findRegExpHost(context);
|
||||
if (host == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return host.getSupportedNamedGroupTypes(context);
|
||||
}
|
||||
|
||||
public boolean isValidGroupName(String name, @Nullable final PsiElement context) {
|
||||
final RegExpLanguageHost host = findRegExpHost(context);
|
||||
return host != null && host.isValidGroupName(name, context);
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.intellij.lang.regexp.inspection;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import org.intellij.lang.regexp.RegExpLanguageHosts;
|
||||
import org.intellij.lang.regexp.psi.RegExpBackref;
|
||||
import org.intellij.lang.regexp.psi.RegExpElementVisitor;
|
||||
import org.intellij.lang.regexp.psi.RegExpGroup;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
*/
|
||||
public class AnonymousGroupInspection extends LocalInspectionTool {
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Anonymous capturing group or numeric back reference";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new AnonymousGroupVisitor(holder);
|
||||
}
|
||||
|
||||
private static class AnonymousGroupVisitor extends RegExpElementVisitor {
|
||||
|
||||
private final ProblemsHolder myHolder;
|
||||
|
||||
public AnonymousGroupVisitor(ProblemsHolder holder) {
|
||||
myHolder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitRegExpGroup(RegExpGroup group) {
|
||||
if (group.getType() != RegExpGroup.Type.CAPTURING_GROUP) {
|
||||
return;
|
||||
}
|
||||
final Collection<RegExpGroup.Type> types = RegExpLanguageHosts.getInstance().getSupportedNamedGroupTypes(group);
|
||||
if (types.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
myHolder.registerProblem(group.getFirstChild(), "Anonymous capturing group");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitRegExpBackref(RegExpBackref backref) {
|
||||
final Collection<RegExpGroup.Type> types = RegExpLanguageHosts.getInstance().getSupportedNamedGroupTypes(backref);
|
||||
if (types.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
myHolder.registerProblem(backref, "Numeric back reference");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import org.intellij.lang.regexp.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
@@ -38,6 +39,7 @@ import java.util.Locale;
|
||||
*/
|
||||
public class JavaRegExpHost implements RegExpLanguageHost {
|
||||
|
||||
protected static final EnumSet<RegExpGroup.Type> SUPPORTED_NAMED_GROUP_TYPES = EnumSet.of(RegExpGroup.Type.NAMED_GROUP);
|
||||
private final DefaultRegExpPropertiesProvider myPropertiesProvider;
|
||||
|
||||
private final String[][] myPropertyNames = {
|
||||
@@ -171,6 +173,15 @@ public class JavaRegExpHost implements RegExpLanguageHost {
|
||||
return ref.isNamedGroupRef() && hasAtLeastJdkVersion(ref, JavaSdkVersion.JDK_1_7);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public EnumSet<RegExpGroup.Type> getSupportedNamedGroupTypes(RegExpElement context) {
|
||||
if (!hasAtLeastJdkVersion(context, JavaSdkVersion.JDK_1_7)) {
|
||||
return EMPTY_NAMED_GROUP_TYPES;
|
||||
}
|
||||
return SUPPORTED_NAMED_GROUP_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidGroupName(String name, @NotNull PsiElement context) {
|
||||
for (int i = 0, length = name.length(); i < length; i++) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.testFramework.IdeaTestUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.intellij.lang.regexp.inspection.AnonymousGroupInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -30,6 +31,11 @@ import org.jetbrains.annotations.NotNull;
|
||||
@SuppressWarnings("Annotator")
|
||||
public class RegExpHighlightingTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
public void testAnonymousCapturingGroupInspection() {
|
||||
myFixture.enableInspections(new AnonymousGroupInspection());
|
||||
doTest("<warning descr=\"Anonymous capturing group\">(</warning>moo)<warning descr=\"Numeric back reference\">\\1</warning>");
|
||||
}
|
||||
|
||||
public void testSingleRepetition() {
|
||||
doTest("a<weak_warning descr=\"Single repetition\">{1}</weak_warning>");
|
||||
}
|
||||
|
||||
@@ -17,13 +17,21 @@ package org.jetbrains.plugins.groovy.regexp;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleUtilCore;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.JavaRegExpHost;
|
||||
import org.intellij.lang.regexp.RegExpLanguageHost;
|
||||
import org.intellij.lang.regexp.psi.RegExpChar;
|
||||
import org.intellij.lang.regexp.psi.RegExpElement;
|
||||
import org.intellij.lang.regexp.psi.RegExpGroup;
|
||||
import org.intellij.lang.regexp.psi.RegExpNamedGroupRef;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
*/
|
||||
@@ -47,6 +55,16 @@ public class GroovyRegExpHost extends JavaRegExpHost {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public EnumSet<RegExpGroup.Type> getSupportedNamedGroupTypes(RegExpElement context) {
|
||||
final String version = getGroovyVersion(context);
|
||||
if (version == null || version.compareTo(GroovyConfigUtils.GROOVY2_0) < 0) {
|
||||
return EMPTY_NAMED_GROUP_TYPES;
|
||||
}
|
||||
return SUPPORTED_NAMED_GROUP_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsExtendedHexCharacter(RegExpChar regExpChar) {
|
||||
final String version = getGroovyVersion(regExpChar);
|
||||
|
||||
@@ -34,10 +34,7 @@ import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.intellij.lang.regexp.DefaultRegExpPropertiesProvider;
|
||||
import org.intellij.lang.regexp.RegExpLanguageHost;
|
||||
import org.intellij.lang.regexp.psi.RegExpChar;
|
||||
import org.intellij.lang.regexp.psi.RegExpGroup;
|
||||
import org.intellij.lang.regexp.psi.RegExpNamedGroupRef;
|
||||
import org.intellij.lang.regexp.psi.RegExpNumber;
|
||||
import org.intellij.lang.regexp.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -458,6 +455,12 @@ public class PyStringLiteralExpressionImpl extends PyElementImpl implements PySt
|
||||
return ref.isPythonNamedGroupRef();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public EnumSet<RegExpGroup.Type> getSupportedNamedGroupTypes(RegExpElement context) {
|
||||
return EnumSet.of(RegExpGroup.Type.PYTHON_NAMED_GROUP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsExtendedHexCharacter(RegExpChar regExpChar) {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user