Skip to main content

oxc_regular_expression/ast_impl/
support.rs

1use crate::ast::{
2    Alternative, CharacterClass, CharacterClassContents, Disjunction, LookAroundAssertionKind,
3    Pattern, Term,
4};
5
6pub struct RegexUnsupportedPatterns {
7    pub named_capture_groups: bool,
8    pub unicode_property_escapes: bool,
9    pub look_behind_assertions: bool,
10    pub pattern_modifiers: bool,
11}
12
13/// Check if the regular expression contains any unsupported syntax.
14///
15/// Based on parsed regular expression pattern.
16pub fn has_unsupported_regular_expression_pattern(
17    pattern: &Pattern,
18    unsupported: &RegexUnsupportedPatterns,
19) -> bool {
20    disjunction_contains_unsupported(&pattern.body, unsupported)
21}
22
23fn disjunction_contains_unsupported(
24    disjunction: &Disjunction,
25    unsupported: &RegexUnsupportedPatterns,
26) -> bool {
27    disjunction
28        .body
29        .iter()
30        .any(|alternative| alternative_contains_unsupported(alternative, unsupported))
31}
32
33fn alternative_contains_unsupported(
34    alternative: &Alternative,
35    unsupported: &RegexUnsupportedPatterns,
36) -> bool {
37    alternative.body.iter().any(|term| term_contains_unsupported(term, unsupported))
38}
39
40fn term_contains_unsupported(term: &Term, unsupported: &RegexUnsupportedPatterns) -> bool {
41    match term {
42        Term::LookAroundAssertion(assertion) => {
43            if unsupported.look_behind_assertions
44                && matches!(
45                    assertion.kind,
46                    LookAroundAssertionKind::Lookbehind
47                        | LookAroundAssertionKind::NegativeLookbehind
48                )
49            {
50                return true;
51            }
52            disjunction_contains_unsupported(&assertion.body, unsupported)
53        }
54        Term::Quantifier(quantifier) => term_contains_unsupported(&quantifier.body, unsupported),
55        Term::UnicodePropertyEscape(_) => unsupported.unicode_property_escapes,
56        Term::CharacterClass(character_class) => {
57            unsupported.unicode_property_escapes
58                && character_class_has_unicode_property_escape(character_class)
59        }
60        Term::CapturingGroup(group) => {
61            if group.name.is_some() && unsupported.named_capture_groups {
62                return true;
63            }
64            disjunction_contains_unsupported(&group.body, unsupported)
65        }
66        Term::IgnoreGroup(group) => {
67            if group.modifiers.is_some() && unsupported.pattern_modifiers {
68                return true;
69            }
70            disjunction_contains_unsupported(&group.body, unsupported)
71        }
72        _ => false,
73    }
74}
75
76fn character_class_has_unicode_property_escape(character_class: &CharacterClass) -> bool {
77    character_class.body.iter().any(|element| match element {
78        CharacterClassContents::UnicodePropertyEscape(_) => true,
79        CharacterClassContents::NestedCharacterClass(character_class) => {
80            character_class_has_unicode_property_escape(character_class)
81        }
82        _ => false,
83    })
84}