mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-14 09:12:22 +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]
|
||||
*/
|
||||
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,
|
||||
ALLOW_HORIZONTAL_WHITESPACE_CLASS,
|
||||
|
||||
@@ -456,7 +456,12 @@ public class RegExpParser implements PsiParser, LightPsiParser {
|
||||
}
|
||||
else {
|
||||
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) {
|
||||
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"));
|
||||
}
|
||||
|
||||
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) {
|
||||
builder.advanceLexer();
|
||||
checkMatches(builder, RegExpTT.NAME, RegExpBundle.message("parse.error.group.name.expected"));
|
||||
|
||||
@@ -168,6 +168,9 @@ public interface RegExpTT {
|
||||
/** \g'name' */
|
||||
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,
|
||||
ESC_CTRL_CHARACTER,
|
||||
ESC_CHARACTER,
|
||||
|
||||
@@ -5,8 +5,10 @@ import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.intellij.lang.regexp.RegExpBundle;
|
||||
import org.intellij.lang.regexp.psi.*;
|
||||
import org.intellij.lang.regexp.psi.impl.RegExpGroupImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -53,7 +55,7 @@ public class SuspiciousBackrefInspection extends LocalInspectionTool {
|
||||
return;
|
||||
}
|
||||
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 =
|
||||
RegExpBundle.message("inspection.warning.group.back.reference.are.in.different.branches", groupRef.getGroupName());
|
||||
myHolder.registerProblem(groupRef, message);
|
||||
@@ -63,5 +65,14 @@ public class SuspiciousBackrefInspection extends LocalInspectionTool {
|
||||
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();
|
||||
}
|
||||
|
||||
public static boolean isPcreConditionalGroup(ASTNode node) {
|
||||
return node != null && node.findChildByType(RegExpTT.PCRE_CONDITION) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupName() {
|
||||
final ASTNode nameNode = getNode().findChildByType(RegExpTT.NAME);
|
||||
|
||||
@@ -44,6 +44,7 @@ import static org.intellij.lang.regexp.RegExpCapability.*;
|
||||
private boolean allowOneHexCharEscape;
|
||||
private boolean allowMysqlBracketExpressions;
|
||||
private boolean allowPcreBackReferences;
|
||||
private boolean allowPcreConditions;
|
||||
private int maxOctal = 0777;
|
||||
private int minOctalDigits = 1;
|
||||
private boolean whitespaceInClass;
|
||||
@@ -65,6 +66,7 @@ import static org.intellij.lang.regexp.RegExpCapability.*;
|
||||
this.allowTransformationEscapes = capabilities.contains(TRANSFORMATION_ESCAPES);
|
||||
this.allowMysqlBracketExpressions = capabilities.contains(MYSQL_BRACKET_EXPRESSIONS);
|
||||
this.allowPcreBackReferences = capabilities.contains(PCRE_BACK_REFERENCES);
|
||||
this.allowPcreConditions = capabilities.contains(PCRE_CONDITIONS);
|
||||
if (capabilities.contains(MAX_OCTAL_177)) {
|
||||
maxOctal = 0177;
|
||||
}
|
||||
@@ -156,6 +158,8 @@ TRANSFORMATION= "l" | "L" | "U" | "E"
|
||||
|
||||
HEX_CHAR=[0-9a-fA-F]
|
||||
|
||||
PCRE_CONDITION=DEFINE|VERSION>?=\d*[.]?\d{0,2}
|
||||
|
||||
/* 999 back references should be enough for everybody */
|
||||
BACK_REFERENCES_GROUP = [1-9][0-9]{0,2}
|
||||
|
||||
@@ -486,6 +490,7 @@ BACK_REFERENCES_GROUP = [1-9][0-9]{0,2}
|
||||
}
|
||||
|
||||
<CONDITIONAL2> {
|
||||
{PCRE_CONDITION} { return allowPcreConditions ? RegExpTT.PCRE_CONDITION : RegExpTT.NAME; }
|
||||
{GROUP_NAME} { return RegExpTT.NAME; }
|
||||
[:digit:]+ { return RegExpTT.NUMBER; }
|
||||
"')" { yybegin(YYINITIAL); return RegExpTT.QUOTED_CONDITION_END; }
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.intellij.lang.regexp.*;
|
||||
import org.intellij.lang.regexp.psi.*;
|
||||
import org.intellij.lang.regexp.psi.impl.RegExpGroupImpl;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -232,7 +233,7 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
|
||||
public void visitRegExpGroup(RegExpGroup group) {
|
||||
final RegExpPattern pattern = group.getPattern();
|
||||
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 "(|)"
|
||||
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);
|
||||
}
|
||||
|
||||
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() {
|
||||
final RegExpLexer lexer = new RegExpLexer(EnumSet.noneOf(RegExpCapability.class));
|
||||
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