mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 11:53:49 +07:00
WI-60779 Support PCRE "pseudo" conditions DEFINE and VERSION as conditions in conditional groups.
GitOrigin-RevId: bdf83d81e0235826d7980739025f90581bc4222c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c2abcf40d8
commit
a58330a1c1
File diff suppressed because it is too large
Load Diff
@@ -117,6 +117,11 @@ public enum RegExpCapability {
|
|||||||
* \g{[integer]} \g[unsigned integer]
|
* \g{[integer]} \g[unsigned integer]
|
||||||
*/
|
*/
|
||||||
PCRE_BACK_REFERENCES,
|
PCRE_BACK_REFERENCES,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow PCRE conditions DEFINE and VERSION[>]?=n.m in conditional groups
|
||||||
|
*/
|
||||||
|
PCRE_CONDITIONS,
|
||||||
;
|
;
|
||||||
static final EnumSet<RegExpCapability> DEFAULT_CAPABILITIES = EnumSet.of(NESTED_CHARACTER_CLASSES,
|
static final EnumSet<RegExpCapability> DEFAULT_CAPABILITIES = EnumSet.of(NESTED_CHARACTER_CLASSES,
|
||||||
ALLOW_HORIZONTAL_WHITESPACE_CLASS,
|
ALLOW_HORIZONTAL_WHITESPACE_CLASS,
|
||||||
|
|||||||
@@ -456,7 +456,12 @@ public class RegExpParser implements PsiParser, LightPsiParser {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (RegExpTT.GROUP_BEGIN == type) {
|
if (RegExpTT.GROUP_BEGIN == type) {
|
||||||
parseGroupReferenceCondition(builder, RegExpTT.GROUP_END);
|
if (builder.lookAhead(1) == RegExpTT.PCRE_CONDITION) {
|
||||||
|
parsePcreConditionalGroup(builder);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parseGroupReferenceCondition(builder, RegExpTT.GROUP_END);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (RegExpTT.QUOTED_CONDITION_BEGIN == type) {
|
else if (RegExpTT.QUOTED_CONDITION_BEGIN == type) {
|
||||||
parseGroupReferenceCondition(builder, RegExpTT.QUOTED_CONDITION_END);
|
parseGroupReferenceCondition(builder, RegExpTT.QUOTED_CONDITION_END);
|
||||||
@@ -502,6 +507,14 @@ public class RegExpParser implements PsiParser, LightPsiParser {
|
|||||||
checkMatches(builder, RegExpTT.GROUP_END, RegExpBundle.message("parse.error.unclosed.group"));
|
checkMatches(builder, RegExpTT.GROUP_END, RegExpBundle.message("parse.error.unclosed.group"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void parsePcreConditionalGroup(PsiBuilder builder) {
|
||||||
|
final PsiBuilder.Marker marker = builder.mark();
|
||||||
|
builder.advanceLexer();
|
||||||
|
builder.advanceLexer();
|
||||||
|
parseGroupEnd(builder);
|
||||||
|
marker.done(RegExpElementTypes.GROUP);
|
||||||
|
}
|
||||||
|
|
||||||
private static void parseNamedGroupRef(PsiBuilder builder, PsiBuilder.Marker marker, IElementType type) {
|
private static void parseNamedGroupRef(PsiBuilder builder, PsiBuilder.Marker marker, IElementType type) {
|
||||||
builder.advanceLexer();
|
builder.advanceLexer();
|
||||||
checkMatches(builder, RegExpTT.NAME, RegExpBundle.message("parse.error.group.name.expected"));
|
checkMatches(builder, RegExpTT.NAME, RegExpBundle.message("parse.error.group.name.expected"));
|
||||||
|
|||||||
@@ -168,6 +168,9 @@ public interface RegExpTT {
|
|||||||
/** \g'name' */
|
/** \g'name' */
|
||||||
IElementType RUBY_QUOTED_NAMED_GROUP_CALL = new RegExpElementType("RUBY_QUOTED_NAMED_GROUP_CALL");
|
IElementType RUBY_QUOTED_NAMED_GROUP_CALL = new RegExpElementType("RUBY_QUOTED_NAMED_GROUP_CALL");
|
||||||
|
|
||||||
|
/** DEFINE|VERSION[>]=n.m */
|
||||||
|
IElementType PCRE_CONDITION = new RegExpElementType("PCRE_CONDITION");
|
||||||
|
|
||||||
TokenSet CHARACTERS = TokenSet.create(CHARACTER,
|
TokenSet CHARACTERS = TokenSet.create(CHARACTER,
|
||||||
ESC_CTRL_CHARACTER,
|
ESC_CTRL_CHARACTER,
|
||||||
ESC_CHARACTER,
|
ESC_CHARACTER,
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import com.intellij.codeInspection.LocalInspectionTool;
|
|||||||
import com.intellij.codeInspection.ProblemsHolder;
|
import com.intellij.codeInspection.ProblemsHolder;
|
||||||
import com.intellij.psi.PsiElementVisitor;
|
import com.intellij.psi.PsiElementVisitor;
|
||||||
import com.intellij.psi.util.PsiTreeUtil;
|
import com.intellij.psi.util.PsiTreeUtil;
|
||||||
|
import com.intellij.util.ObjectUtils;
|
||||||
import org.intellij.lang.regexp.RegExpBundle;
|
import org.intellij.lang.regexp.RegExpBundle;
|
||||||
import org.intellij.lang.regexp.psi.*;
|
import org.intellij.lang.regexp.psi.*;
|
||||||
|
import org.intellij.lang.regexp.psi.impl.RegExpGroupImpl;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,7 +55,7 @@ public class SuspiciousBackrefInspection extends LocalInspectionTool {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final RegExpBranch branch = PsiTreeUtil.getParentOfType(target, RegExpBranch.class);
|
final RegExpBranch branch = PsiTreeUtil.getParentOfType(target, RegExpBranch.class);
|
||||||
if (!PsiTreeUtil.isAncestor(branch, groupRef, true)) {
|
if (!PsiTreeUtil.isAncestor(branch, groupRef, true) && !isPcreCondition(branch)) {
|
||||||
final String message =
|
final String message =
|
||||||
RegExpBundle.message("inspection.warning.group.back.reference.are.in.different.branches", groupRef.getGroupName());
|
RegExpBundle.message("inspection.warning.group.back.reference.are.in.different.branches", groupRef.getGroupName());
|
||||||
myHolder.registerProblem(groupRef, message);
|
myHolder.registerProblem(groupRef, message);
|
||||||
@@ -63,5 +65,14 @@ public class SuspiciousBackrefInspection extends LocalInspectionTool {
|
|||||||
myHolder.registerProblem(groupRef, message);
|
myHolder.registerProblem(groupRef, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isPcreCondition(RegExpBranch branch) {
|
||||||
|
if (branch == null) return false;
|
||||||
|
if (branch.getParent() instanceof RegExpConditional) {
|
||||||
|
RegExpGroup groupCondition = ObjectUtils.tryCast(((RegExpConditional)branch.getParent()).getCondition(), RegExpGroup.class);
|
||||||
|
return groupCondition != null && RegExpGroupImpl.isPcreConditionalGroup(groupCondition.getNode());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,10 @@ public class RegExpGroupImpl extends RegExpElementImpl implements RegExpGroup {
|
|||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isPcreConditionalGroup(ASTNode node) {
|
||||||
|
return node != null && node.findChildByType(RegExpTT.PCRE_CONDITION) != null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getGroupName() {
|
public String getGroupName() {
|
||||||
final ASTNode nameNode = getNode().findChildByType(RegExpTT.NAME);
|
final ASTNode nameNode = getNode().findChildByType(RegExpTT.NAME);
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import static org.intellij.lang.regexp.RegExpCapability.*;
|
|||||||
private boolean allowOneHexCharEscape;
|
private boolean allowOneHexCharEscape;
|
||||||
private boolean allowMysqlBracketExpressions;
|
private boolean allowMysqlBracketExpressions;
|
||||||
private boolean allowPcreBackReferences;
|
private boolean allowPcreBackReferences;
|
||||||
|
private boolean allowPcreConditions;
|
||||||
private int maxOctal = 0777;
|
private int maxOctal = 0777;
|
||||||
private int minOctalDigits = 1;
|
private int minOctalDigits = 1;
|
||||||
private boolean whitespaceInClass;
|
private boolean whitespaceInClass;
|
||||||
@@ -65,6 +66,7 @@ import static org.intellij.lang.regexp.RegExpCapability.*;
|
|||||||
this.allowTransformationEscapes = capabilities.contains(TRANSFORMATION_ESCAPES);
|
this.allowTransformationEscapes = capabilities.contains(TRANSFORMATION_ESCAPES);
|
||||||
this.allowMysqlBracketExpressions = capabilities.contains(MYSQL_BRACKET_EXPRESSIONS);
|
this.allowMysqlBracketExpressions = capabilities.contains(MYSQL_BRACKET_EXPRESSIONS);
|
||||||
this.allowPcreBackReferences = capabilities.contains(PCRE_BACK_REFERENCES);
|
this.allowPcreBackReferences = capabilities.contains(PCRE_BACK_REFERENCES);
|
||||||
|
this.allowPcreConditions = capabilities.contains(PCRE_CONDITIONS);
|
||||||
if (capabilities.contains(MAX_OCTAL_177)) {
|
if (capabilities.contains(MAX_OCTAL_177)) {
|
||||||
maxOctal = 0177;
|
maxOctal = 0177;
|
||||||
}
|
}
|
||||||
@@ -156,6 +158,8 @@ TRANSFORMATION= "l" | "L" | "U" | "E"
|
|||||||
|
|
||||||
HEX_CHAR=[0-9a-fA-F]
|
HEX_CHAR=[0-9a-fA-F]
|
||||||
|
|
||||||
|
PCRE_CONDITION=DEFINE|VERSION>?=\d*[.]?\d{0,2}
|
||||||
|
|
||||||
/* 999 back references should be enough for everybody */
|
/* 999 back references should be enough for everybody */
|
||||||
BACK_REFERENCES_GROUP = [1-9][0-9]{0,2}
|
BACK_REFERENCES_GROUP = [1-9][0-9]{0,2}
|
||||||
|
|
||||||
@@ -486,6 +490,7 @@ BACK_REFERENCES_GROUP = [1-9][0-9]{0,2}
|
|||||||
}
|
}
|
||||||
|
|
||||||
<CONDITIONAL2> {
|
<CONDITIONAL2> {
|
||||||
|
{PCRE_CONDITION} { return allowPcreConditions ? RegExpTT.PCRE_CONDITION : RegExpTT.NAME; }
|
||||||
{GROUP_NAME} { return RegExpTT.NAME; }
|
{GROUP_NAME} { return RegExpTT.NAME; }
|
||||||
[:digit:]+ { return RegExpTT.NUMBER; }
|
[:digit:]+ { return RegExpTT.NUMBER; }
|
||||||
"')" { yybegin(YYINITIAL); return RegExpTT.QUOTED_CONDITION_END; }
|
"')" { yybegin(YYINITIAL); return RegExpTT.QUOTED_CONDITION_END; }
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.intellij.psi.util.PsiTreeUtil;
|
|||||||
import com.intellij.util.containers.ContainerUtil;
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
import org.intellij.lang.regexp.*;
|
import org.intellij.lang.regexp.*;
|
||||||
import org.intellij.lang.regexp.psi.*;
|
import org.intellij.lang.regexp.psi.*;
|
||||||
|
import org.intellij.lang.regexp.psi.impl.RegExpGroupImpl;
|
||||||
import org.jetbrains.annotations.Nls;
|
import org.jetbrains.annotations.Nls;
|
||||||
import org.jetbrains.annotations.NonNls;
|
import org.jetbrains.annotations.NonNls;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -232,7 +233,7 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
|
|||||||
public void visitRegExpGroup(RegExpGroup group) {
|
public void visitRegExpGroup(RegExpGroup group) {
|
||||||
final RegExpPattern pattern = group.getPattern();
|
final RegExpPattern pattern = group.getPattern();
|
||||||
final RegExpBranch[] branches = pattern.getBranches();
|
final RegExpBranch[] branches = pattern.getBranches();
|
||||||
if (isEmpty(branches) && group.getNode().getLastChildNode().getElementType() == RegExpTT.GROUP_END) {
|
if (!RegExpGroupImpl.isPcreConditionalGroup(group.getNode()) && isEmpty(branches) && group.getNode().getLastChildNode().getElementType() == RegExpTT.GROUP_END) {
|
||||||
// catches "()" as well as "(|)"
|
// catches "()" as well as "(|)"
|
||||||
myHolder.newAnnotation(HighlightSeverity.WARNING, RegExpBundle.message("error.empty.group")).create();
|
myHolder.newAnnotation(HighlightSeverity.WARNING, RegExpBundle.message("error.empty.group")).create();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,6 +331,21 @@ public class RegExpLexerTest extends LexerTestCase {
|
|||||||
doTest("(a)\\g-105", null, lexer);
|
doTest("(a)\\g-105", null, lexer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPcreConditionDefine() {
|
||||||
|
final RegExpLexer lexer = new RegExpLexer(EnumSet.of(PCRE_CONDITIONS));
|
||||||
|
doTest("(?(DEFINE)(?<Name>\\w+))(?P>Name)", null, lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPcreConditionVersion() {
|
||||||
|
final RegExpLexer lexer = new RegExpLexer(EnumSet.of(PCRE_CONDITIONS));
|
||||||
|
doTest("(?(VERSION>=10.7)yes|no)", null, lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNoPcreCondition() {
|
||||||
|
final RegExpLexer lexer = new RegExpLexer(EnumSet.noneOf(RegExpCapability.class));
|
||||||
|
doTest("(?(DEFINE)(?<Name>\\w+))(?P>Name)", null, lexer);
|
||||||
|
}
|
||||||
|
|
||||||
public void testNoNestedCharacterClasses1() {
|
public void testNoNestedCharacterClasses1() {
|
||||||
final RegExpLexer lexer = new RegExpLexer(EnumSet.noneOf(RegExpCapability.class));
|
final RegExpLexer lexer = new RegExpLexer(EnumSet.noneOf(RegExpCapability.class));
|
||||||
doTest("[[\\]]", "CLASS_BEGIN ('[')\n" +
|
doTest("[[\\]]", "CLASS_BEGIN ('[')\n" +
|
||||||
|
|||||||
14
RegExpSupport/testData/lexer/noPcreCondition.txt
Normal file
14
RegExpSupport/testData/lexer/noPcreCondition.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
CONDITIONAL ('(?')
|
||||||
|
GROUP_BEGIN ('(')
|
||||||
|
NAME ('DEFINE')
|
||||||
|
GROUP_END (')')
|
||||||
|
RUBY_NAMED_GROUP ('(?<')
|
||||||
|
NAME ('Name')
|
||||||
|
GT ('>')
|
||||||
|
CHAR_CLASS ('\w')
|
||||||
|
PLUS ('+')
|
||||||
|
GROUP_END (')')
|
||||||
|
GROUP_END (')')
|
||||||
|
PCRE_RECURSIVE_NAMED_GROUP ('(?P>')
|
||||||
|
NAME ('Name')
|
||||||
|
GROUP_END (')')
|
||||||
14
RegExpSupport/testData/lexer/pcreConditionDefine.txt
Normal file
14
RegExpSupport/testData/lexer/pcreConditionDefine.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
CONDITIONAL ('(?')
|
||||||
|
GROUP_BEGIN ('(')
|
||||||
|
PCRE_CONDITION ('DEFINE')
|
||||||
|
GROUP_END (')')
|
||||||
|
RUBY_NAMED_GROUP ('(?<')
|
||||||
|
NAME ('Name')
|
||||||
|
GT ('>')
|
||||||
|
CHAR_CLASS ('\w')
|
||||||
|
PLUS ('+')
|
||||||
|
GROUP_END (')')
|
||||||
|
GROUP_END (')')
|
||||||
|
PCRE_RECURSIVE_NAMED_GROUP ('(?P>')
|
||||||
|
NAME ('Name')
|
||||||
|
GROUP_END (')')
|
||||||
11
RegExpSupport/testData/lexer/pcreConditionVersion.txt
Normal file
11
RegExpSupport/testData/lexer/pcreConditionVersion.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
CONDITIONAL ('(?')
|
||||||
|
GROUP_BEGIN ('(')
|
||||||
|
PCRE_CONDITION ('VERSION>=10.7')
|
||||||
|
GROUP_END (')')
|
||||||
|
CHARACTER ('y')
|
||||||
|
CHARACTER ('e')
|
||||||
|
CHARACTER ('s')
|
||||||
|
UNION ('|')
|
||||||
|
CHARACTER ('n')
|
||||||
|
CHARACTER ('o')
|
||||||
|
GROUP_END (')')
|
||||||
Reference in New Issue
Block a user