Skip to main content

krabka_java_regex/
lib.rs

1//! Java's `java.util.regex` in Rust: a [`JavaPattern`] compiles a
2//! `java.util.regex.Pattern` and answers `Matcher.matches()`,
3//! `String.replaceAll` and `String.replaceFirst` as JDK 25 does.
4//!
5//! Krabka reads three Apache Kafka configs whose values are Java regular
6//! expressions: `ssl.principal.mapping.rules`, the Kerberos `auth_to_local`
7//! rules, and the `match` patterns of a KIP-714 `CLIENT_METRICS`
8//! subscription. A rule that matches differently from Java maps a principal
9//! to another name, so the crate's job is Java's answer, quirks included.
10//!
11//! ```
12//! use {assert2::assert, krabka_java_regex::JavaPattern};
13//!
14//! let rule = JavaPattern::compile("CN=([^,]+),OU=(?i)ops.*")?;
15//! assert!(rule.matches("CN=alice,OU=OPS-EU")?);
16//! assert!(rule.replace("CN=alice,OU=ops", "$1", true)? == "alice");
17//! # Ok::<(), Box<dyn std::error::Error>>(())
18//! ```
19//!
20//! # How it reads a pattern as Java does
21//!
22//! The `regex` crate differs from Java in the two places these lean on.
23//! `Matcher.matches()` succeeds when *some* way of matching spans the whole
24//! input, including through backtracking and lazy quantifiers, where a
25//! leftmost-first search stops at the first match it finds. And
26//! `Matcher.replaceAll` reads `$1_x` as group 1 followed by `_x`, where the
27//! `regex` crate reads a group named `1_x`. [`JavaPattern`] gives both the
28//! Java answer.
29//!
30//! The pattern itself is rewritten by `translate_java` first, which also gives
31//! Java's reading of `\w`, `.`, and `(?i)`: Java folds ASCII case only, unless
32//! `(?u)` asks for Unicode case as well, so `(?i)service-` does not match
33//! `ΕΏervice-` (long s). A backreference under a case flag, `\R`, and a group
34//! inside a look-around or an atomic group match in ways `fancy_regex` cannot
35//! give, so a pattern that has one runs on the matcher in `backtrack`
36//! instead. So does a pattern `fancy_regex` cannot compile, such as a
37//! look-behind that is not of one length with `\R` in it, or a very large
38//! counted repeat. A pattern Java's `Pattern.compile` refuses is refused.
39//!
40//! # Limits
41//!
42//! A search takes at most 1,000,000 backtracking steps, `fancy_regex`'s
43//! default; on the backtracking matcher each iteration of a repeat is a step
44//! too, so `(){1000000000}` is an error and not a billion iterations. The
45//! backtracking matcher keeps its alternatives on a stack of its own, so a
46//! long input cannot overflow the thread's. A search past the limit is a
47//! [`MatchError`], not a no match. Java answers such a search, however
48//! slowly.
49//!
50//! The matcher answers a pattern `fancy_regex` cannot compile, where it
51//! knows every node of it: a look-behind that is not of one length with
52//! `\R`, a look-around, an atomic group or a group in it, such as
53//! `(?<=\R?x)y`, and a counted repeat too large for the `regex` crate to
54//! build at its default size limit, such as `x{1,1000000000}`, in a
55//! look-behind or outside one. It counts the iterations of a repeat, so such
56//! a pattern compiles in milliseconds, but it tries every start in Java's
57//! window of a look-behind, which for such a repeat is the whole input, and a
58//! long input can take it past the step limit.
59//!
60//! [`JavaPattern::compile`] refuses the patterns below, which Java compiles,
61//! rather than match them another way. Each is a [`PatternError`].
62//!
63//! - A Unicode block, such as `\p{InGreek}`, and `\X`.
64//! - A named char, `\N{name}`.
65//! - A quantifier of a look-around, such as `(?=(a))*`, which `fancy_regex`
66//!   cannot parse.
67//! - A backreference to a group the pattern does not have, such as `(a)\2`,
68//!   which `fancy_regex` refuses, and which never matches in Java.
69//! - A look-behind whose maximum length overflows Java's `int`, such as
70//!   `(?<=a*b*)`, where Java then tries fewer starts than the look-behind
71//!   allows. Where the overflowed maximum still covers every start, as in
72//!   `(?<=a*bc)`, the pattern compiles.
73//! - A look-behind whose maximum overflows, in a pattern with a char past
74//!   U+FFFF from the look-behind on, as in `(?<=a*b*c*x)yπŸ˜€`. Java then counts
75//!   the look-behind's window in code points (`BehindS`), with arithmetic the
76//!   crate does not follow.
77//!
78//! One more input limit applies at match time. Where an overflowed maximum is
79//! at least 2^30 and covers every start only up to that length, as in
80//! `(?<=a*b*c*x)`, an input of more UTF-16 units than the maximum is a
81//! [`MatchError`].
82//!
83//! Java reads a char past U+FFFF as two UTF-16 units, and its look-behind
84//! counts its window in units where the pattern has no such char from the
85//! look-behind on, so it may start between the two units and read a lone low
86//! surrogate there. An input with such a char goes to the backtracking matcher
87//! where the pattern has such a look-behind, and the matcher reads it as Java
88//! does. A group the look-behind starts or ends between the two units holds a
89//! lone surrogate in Java, which a Rust string cannot, so a replacement that
90//! takes its text is a [`MatchError`]. A search still starts at a char only,
91//! where Java's `Matcher.find` also tries the low surrogate of one: Java
92//! finds `(?<!^).` in `πŸ˜€x` at the low surrogate and at `x`, and the crate at
93//! `x` only.
94
95mod backtrack;
96mod look_behind;
97mod translate;
98
99use {
100    self::{
101        backtrack::Program,
102        look_behind::{LookBehinds, Refusal, check_look_behinds},
103        translate::{JavaTranslation, translate_java},
104    },
105    fancy_regex::{CompileError, Error as FancyError, Expr, Regex},
106    std::{collections::BTreeMap, fmt::Display, ops::Range, sync::Arc},
107};
108
109/// A `java.util.regex.Pattern` compiled for `matches()` and
110/// `replaceAll`/`replaceFirst`.
111#[derive(Debug, Clone)]
112pub struct JavaPattern(Arc<Compiled>);
113
114/// The engines behind a [`JavaPattern`], shared so that a rule holding one is
115/// small.
116#[derive(Debug)]
117struct Compiled {
118    /// The engine that answers.
119    engine: Engine,
120    /// The groups, group 0 included.
121    groups: usize,
122    /// Group name to index, for `${name}` in a replacement.
123    names: BTreeMap<String, usize>,
124    /// The most UTF-16 units of input the pattern is read for as Java reads
125    /// it, for a look-behind whose maximum length wraps in Java's `int`.
126    input_limit: Option<usize>,
127    /// What answers an input with a char past U+FFFF where `engine` is
128    /// `fancy_regex`.
129    wide_input: WideInput,
130}
131
132/// What answers an input with a char past U+FFFF, which Java reads as two
133/// UTF-16 units. A look-behind that Java counts in units (`Behind`) can then
134/// try a start that `fancy_regex`, which counts chars, does not, or start
135/// between the two units of the char.
136#[derive(Debug)]
137enum WideInput {
138    /// The engine, which reads such an input as Java does: the matcher, or
139    /// `fancy_regex` for a pattern with no look-behind Java counts in units.
140    Engine,
141    /// The matcher, in place of `fancy_regex`.
142    Program(Program),
143    /// Neither: the matcher does not know a node of the pattern.
144    Refused,
145}
146
147/// The engine that answers for a [`JavaPattern`].
148#[derive(Debug)]
149enum Engine {
150    /// `fancy_regex`, for a pattern it reads as Java does.
151    Fancy {
152        /// The pattern as written, for `replaceAll` and `replaceFirst`.
153        search: Regex,
154        /// The pattern pinned to the whole input, for `matches()`.
155        whole: Regex,
156    },
157    /// The backtracking matcher, for a pattern that `fancy_regex` does not
158    /// read as Java does, or cannot compile.
159    Backtrack(Program),
160}
161
162/// A pattern Java's `Pattern.compile` refuses with a
163/// `PatternSyntaxException`, or one this crate cannot read as Java does.
164#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
165#[error("{0}")]
166pub struct PatternError(String);
167
168/// A match this crate gives no answer for: a replacement string Java's
169/// `Matcher.appendReplacement` refuses, which Java reports as
170/// `IllegalArgumentException` or `IndexOutOfBoundsException`, or a search
171/// that ran past the backtrack limit.
172#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
173#[error("{0}")]
174pub struct MatchError(String);
175
176impl MatchError {
177    /// A search that gave no answer, as the error a caller rejects the name
178    /// by.
179    fn search(error: impl Display) -> Self { Self(error.to_string()) }
180}
181
182impl JavaPattern {
183    /// Compiles `pattern`, `Pattern.compile(pattern)`.
184    ///
185    /// # Errors
186    ///
187    /// [`PatternError`] for a pattern Java refuses, and for one this crate
188    /// cannot read as Java does, which it refuses rather than match it
189    /// another way.
190    pub fn compile(pattern: &str) -> Result<Self, PatternError> {
191        let JavaTranslation {
192            pattern: translated,
193            backref_folds,
194            linebreak_atoms,
195            group_repeats,
196            look_behind_code_points,
197        } = translate_java(pattern)
198            .ok_or_else(|| PatternError(format!("{pattern:?} is not a java.util.regex pattern")))?;
199        let tree =
200            Expr::parse_tree(&translated).map_err(|error| PatternError(error.to_string()))?;
201        let groups = 1 + count_groups(&tree.expr);
202        // `fancy_regex` refuses a reference to a group the pattern does not
203        // have. It is not asked where a look-behind is too large for it, and
204        // the matcher does not refuse one, so the check is made here.
205        if let Some(group) = backref_past(&tree.expr, groups) {
206            let error = FancyError::CompileError(Box::new(CompileError::InvalidBackref(group)));
207            return Err(PatternError(error.to_string()));
208        }
209        let refuse = |refusal: Refusal| PatternError(format!("{pattern:?} {}", refusal.reason()));
210        // `fancy_regex` would take minutes to build a look-behind too large
211        // for the `regex` crate, so it is not asked to.
212        let LookBehinds {
213            input_limit,
214            too_large,
215            windows,
216        } = check_look_behinds(&tree.expr, &look_behind_code_points).map_err(refuse)?;
217        let fancy = if too_large {
218            Err(FancyFailure::Unbuilt(refuse(Refusal::TooLarge)))
219        } else {
220            compile_fancy(&translated)
221        };
222        let names = tree.named_groups.into_iter().collect();
223        let program = Program::new(
224            &translated,
225            groups,
226            &backref_folds,
227            &linebreak_atoms,
228            &group_repeats,
229            &windows,
230        );
231        // A pattern `fancy_regex` reads as Java does, or one with a node the
232        // matcher does not know, keeps `fancy_regex`. The matcher answers
233        // for one `fancy_regex` cannot build.
234        let counts_units = windows.iter().any(|window| !window.code_points);
235        let (engine, wide_input) = match (fancy, program) {
236            (Ok(_), Some(program)) if program.differs_from_fancy() => {
237                (Engine::Backtrack(program), WideInput::Engine)
238            }
239            (Ok((search, whole)), program) => (Engine::Fancy { search, whole }, match program {
240                _ if !counts_units => WideInput::Engine,
241                Some(program) => WideInput::Program(program),
242                None => WideInput::Refused,
243            }),
244            (Err(FancyFailure::Unbuilt(_)), Some(program)) => {
245                (Engine::Backtrack(program), WideInput::Engine)
246            }
247            (Err(FancyFailure::Unbuilt(error) | FancyFailure::Refused(error)), _) => {
248                return Err(error);
249            }
250        };
251        Ok(Self(Arc::new(Compiled {
252            engine,
253            groups,
254            names,
255            input_limit,
256            wide_input,
257        })))
258    }
259
260    /// The number of capturing groups, `Matcher.groupCount()`.
261    #[must_use]
262    pub fn group_count(&self) -> usize { self.0.groups - 1 }
263
264    /// `Matcher.matches()`.
265    ///
266    /// # Errors
267    ///
268    /// A search that runs past the engine's backtrack limit is an error and
269    /// not a no match. Java answers such a pattern, however slowly, so
270    /// reading the limit as "does not match" would let a rule that Java
271    /// applies fall through to the next rule, or to `DEFAULT`, and map the
272    /// principal to a name the operator never meant for it. A caller rejects
273    /// the name instead.
274    ///
275    /// An input longer than the pattern's input limit is an error too: see
276    /// the crate's `# Limits`.
277    pub fn matches(&self, input: &str) -> Result<bool, MatchError> {
278        check_input_length(self.0.input_limit, input)?;
279        if let Some(program) = self.wide_program(input)? {
280            return program.matches(input).map_err(MatchError::search);
281        }
282        match &self.0.engine {
283            Engine::Backtrack(program) => program.matches(input).map_err(MatchError::search),
284            Engine::Fancy { whole, .. } => whole.is_match(input).map_err(MatchError::search),
285        }
286    }
287
288    /// The matcher that answers `input` in place of `fancy_regex`: for an
289    /// input with a char past U+FFFF, where a look-behind Java counts in
290    /// UTF-16 units reads it otherwise. `None` where the engine answers.
291    fn wide_program(&self, input: &str) -> Result<Option<&Program>, MatchError> {
292        // A char past U+FFFF is the only one with a four-byte UTF-8 form.
293        if !input.bytes().any(|byte| byte >= 0xF0) {
294            return Ok(None);
295        }
296        match &self.0.wide_input {
297            WideInput::Engine => Ok(None),
298            WideInput::Program(program) => Ok(Some(program)),
299            WideInput::Refused => Err(MatchError(
300                "the input has a char past U+FFFF, which a look-behind of the pattern reads as \
301                 two UTF-16 units, and the backtracking matcher cannot run the pattern"
302                    .to_owned(),
303            )),
304        }
305    }
306
307    /// `Matcher.find()`: the span of each group of the first match that starts
308    /// at `from` or after it, group 0 the whole match. `last` is where the
309    /// match before ended, or `from` for the first, which `\G` matches. Only
310    /// the backtracking matcher reads it: a pattern with `\G` keeps
311    /// `fancy_regex` only where the matcher does not know a node of it.
312    fn find(
313        &self,
314        input: &str,
315        from: usize,
316        last: usize,
317    ) -> Result<Option<Vec<Option<Range<usize>>>>, MatchError> {
318        if let Some(program) = self.wide_program(input)? {
319            return program.find(input, from, last).map_err(MatchError::search);
320        }
321        let search = match &self.0.engine {
322            Engine::Backtrack(program) => {
323                return program.find(input, from, last).map_err(MatchError::search);
324            }
325            Engine::Fancy { search, .. } => search,
326        };
327        let captures = search
328            .captures_from_pos(input, from)
329            .map_err(MatchError::search)?;
330        Ok(captures.map(|captures| {
331            captures
332                .iter()
333                .map(|group| group.map(|group| group.range()))
334                .collect()
335        }))
336    }
337
338    /// `input.replaceAll(pattern, replacement)` when `all`, and
339    /// `input.replaceFirst(pattern, replacement)` when not.
340    ///
341    /// The matches are `Matcher.find`'s, not the `regex` crate's iterator's.
342    /// After a match that is not empty, `find` may match the empty string at
343    /// the very position the match ended, and the crate's iterator does not:
344    /// `"alice".replaceAll("(.*)", "$1x")` is `alicexx` in Java. After an empty
345    /// match `find` starts one character on, and `\G` stays where that match
346    /// ended.
347    ///
348    /// # Errors
349    ///
350    /// [`MatchError`] for a replacement Java refuses, for a search past the
351    /// backtrack limit, and for an input longer than the pattern's input
352    /// limit.
353    pub fn replace(&self, input: &str, replacement: &str, all: bool) -> Result<String, MatchError> {
354        check_input_length(self.0.input_limit, input)?;
355        let mut out = String::with_capacity(input.len());
356        let mut copied = 0;
357        let mut search_from = 0;
358        while search_from <= input.len() {
359            let Some(groups) = self.find(input, search_from, copied)? else {
360                break;
361            };
362            let Some(whole) = groups.first().cloned().flatten() else {
363                break;
364            };
365            out.push_str(&input[copied..whole.start]);
366            self.append_replacement(&mut out, replacement, input, &groups)?;
367            copied = whole.end;
368            if !all {
369                break;
370            }
371            search_from = next_search_start(input, &whole);
372        }
373        out.push_str(&input[copied..]);
374        Ok(out)
375    }
376
377    /// `Matcher.appendReplacement`: `\x` is a literal `x`, `${name}` and `$n`
378    /// are groups, and `$n` takes as many further digits as still name a
379    /// group. A group that did not take part in the match adds nothing.
380    fn append_replacement(
381        &self,
382        out: &mut String,
383        replacement: &str,
384        input: &str,
385        groups: &[Option<Range<usize>>],
386    ) -> Result<(), MatchError> {
387        let refuse = |message: &str| MatchError(message.to_owned());
388        let group_count = self.group_count();
389        let mut chars = replacement.chars().peekable();
390        while let Some(character) = chars.next() {
391            match character {
392                '\\' => out.push(
393                    chars
394                        .next()
395                        .ok_or_else(|| refuse("character to be escaped is missing"))?,
396                ),
397                '$' => {
398                    let first = chars
399                        .next()
400                        .ok_or_else(|| refuse("illegal group reference: group index is missing"))?;
401                    let group = if first == '{' {
402                        let mut name = String::new();
403                        while let Some(next) = chars.next_if(char::is_ascii_alphanumeric) {
404                            name.push(next);
405                        }
406                        if name.is_empty() {
407                            return Err(refuse("named capturing group has 0 length name"));
408                        }
409                        if chars.next() != Some('}') {
410                            return Err(refuse("named capturing group is missing trailing '}'"));
411                        }
412                        *self
413                            .0
414                            .names
415                            .get(&name)
416                            .ok_or_else(|| MatchError(format!("no group with name {name}")))?
417                    } else {
418                        let mut group = first
419                            .to_digit(10)
420                            .ok_or_else(|| refuse("illegal group reference"))?
421                            as usize;
422                        if group > group_count {
423                            return Err(MatchError(format!("no group {group}")));
424                        }
425                        while let Some(digit) = chars.peek().and_then(|next| next.to_digit(10)) {
426                            let longer = group * 10 + digit as usize;
427                            if longer > group_count {
428                                break;
429                            }
430                            group = longer;
431                            chars.next();
432                        }
433                        group
434                    };
435                    if let Some(Some(text)) = groups.get(group) {
436                        // A look-behind may start a group between the two UTF-16
437                        // units of a char, which Java gives as a lone surrogate.
438                        out.push_str(input.get(text.clone()).ok_or_else(|| {
439                            MatchError(format!(
440                                "group {group} holds half of a char past U+FFFF, which a Rust \
441                                 string cannot hold"
442                            ))
443                        })?);
444                    }
445                }
446                other => out.push(other),
447            }
448        }
449        Ok(())
450    }
451}
452
453/// Why `fancy_regex` gives no engine for a pattern, with the error the
454/// pattern is refused by where the matcher does not answer either.
455#[derive(Debug)]
456enum FancyFailure {
457    /// `fancy_regex` cannot build the pattern: a look-behind that is not of
458    /// one length with a node it runs itself in it, such as `\R` or a
459    /// look-ahead, or a part too large for the `regex` crate. The matcher
460    /// answers instead where it knows every node.
461    Unbuilt(PatternError),
462    /// `fancy_regex` refuses the pattern for another reason, such as a
463    /// backreference to a group the pattern does not have.
464    Refused(PatternError),
465}
466
467/// `fancy_regex`'s pair for `translated`: the pattern as written, and the
468/// pattern pinned to the whole input.
469fn compile_fancy(translated: &str) -> Result<(Regex, Regex), FancyFailure> {
470    let compile = |source: &str| {
471        Regex::new(source).map_err(|error| {
472            let unbuilt = matches!(
473                &error,
474                FancyError::CompileError(compile_error) if matches!(
475                    **compile_error,
476                    CompileError::FeatureNotYetSupported(_)
477                        | CompileError::LookBehindNotConst
478                        | CompileError::InnerError(_)
479                        | CompileError::DfaBuildError(..)
480                )
481            );
482            let error = PatternError(error.to_string());
483            if unbuilt {
484                FancyFailure::Unbuilt(error)
485            } else {
486                FancyFailure::Refused(error)
487            }
488        })
489    };
490    Ok((
491        compile(translated)?,
492        compile(&format!("\\A(?:{translated})\\z"))?,
493    ))
494}
495
496/// The first group past the last of `groups`, group 0 included, that a
497/// backreference in `expr` refers to. `fancy_regex` refuses such a pattern.
498fn backref_past(expr: &Expr, groups: usize) -> Option<usize> {
499    match expr {
500        Expr::Backref { group, .. } if *group >= groups => Some(*group),
501        _ => expr
502            .children_iter()
503            .find_map(|child| backref_past(child, groups)),
504    }
505}
506
507/// The capturing groups in `expr`, which the parser numbers in order.
508fn count_groups(expr: &Expr) -> usize {
509    usize::from(matches!(expr, Expr::Group(_)))
510        + expr.children_iter().map(count_groups).sum::<usize>()
511}
512
513/// Refuses an `input` of more UTF-16 units than `limit`, the most a pattern
514/// is read for as Java reads it. The UTF-8 length is never less than the
515/// UTF-16 one, so the units are counted only past it.
516fn check_input_length(limit: Option<usize>, input: &str) -> Result<(), MatchError> {
517    match limit {
518        Some(limit) if input.len() > limit && input.encode_utf16().count() > limit => {
519            Err(MatchError(format!(
520                "the input is longer than the {limit} UTF-16 units that krabka reads a \
521                 look-behind of this pattern for as Java does"
522            )))
523        }
524        _ => Ok(()),
525    }
526}
527
528/// Where `Matcher.find` starts after the match `whole` of `input`: where the
529/// match ended, or one char on after an empty match, and past the end after an
530/// empty match at the end.
531fn next_search_start(input: &str, whole: &Range<usize>) -> usize {
532    if whole.is_empty() {
533        input[whole.end..]
534            .chars()
535            .next()
536            .map_or(input.len() + 1, |next| whole.end + next.len_utf8())
537    } else {
538        whole.end
539    }
540}
541
542#[cfg(test)]
543mod tests {
544    use {
545        super::*,
546        assert2::{assert, check},
547    };
548
549    fn pattern(source: &str) -> JavaPattern {
550        JavaPattern::compile(source).unwrap_or_else(|error| panic!("{source}: {error}"))
551    }
552
553    fn assert_pattern_matches(source: &str, input: &str, expected: bool) {
554        check!(
555            pattern(source).matches(input).ok() == Some(expected),
556            "{source:?} against {input:?}"
557        );
558    }
559
560    /// One whole-input match: whether `source` matches all of `input`.
561    struct MatchCase<'a> {
562        source: &'a str,
563        input: &'a str,
564        expected: bool,
565    }
566
567    /// A [`MatchCase`] of `source` against `input`.
568    const fn row<'a>(source: &'a str, input: &'a str, expected: bool) -> MatchCase<'a> {
569        MatchCase {
570            source,
571            input,
572            expected,
573        }
574    }
575
576    /// One replacement: `source` on `input` with `replacement` gives `expected`.
577    struct ReplaceCase<'a> {
578        source: &'a str,
579        input: &'a str,
580        replacement: &'a str,
581        expected: &'a str,
582    }
583
584    /// A [`ReplaceCase`] of `source` on `input` with `replacement`.
585    const fn replace_row<'a>(
586        source: &'a str,
587        input: &'a str,
588        replacement: &'a str,
589        expected: &'a str,
590    ) -> ReplaceCase<'a> {
591        ReplaceCase {
592            source,
593            input,
594            replacement,
595            expected,
596        }
597    }
598
599    fn check_matches<'a>(cases: impl IntoIterator<Item = MatchCase<'a>>, debug_names: bool) {
600        for MatchCase {
601            source,
602            input,
603            expected,
604        } in cases
605        {
606            let context = if debug_names {
607                format!("{source:?} against {input:?}")
608            } else {
609                format!("{source} against {input}")
610            };
611            check!(
612                pattern(source).matches(input).ok() == Some(expected),
613                "{context}"
614            );
615        }
616    }
617
618    fn check_replacements<'a>(
619        cases: impl IntoIterator<Item = ReplaceCase<'a>>,
620        all: bool,
621        include_replacement: bool,
622    ) {
623        for ReplaceCase {
624            source,
625            input,
626            replacement,
627            expected,
628        } in cases
629        {
630            let context = if include_replacement {
631                format!("{source} on {input} with {replacement}")
632            } else {
633                format!("{source} on {input}")
634            };
635            check!(
636                pattern(source)
637                    .replace(input, replacement, all)
638                    .ok()
639                    .as_deref()
640                    == Some(expected),
641                "{context}"
642            );
643        }
644    }
645
646    /// `Matcher.matches()` finds a whole-input match by backtracking, which a
647    /// leftmost-first search does not.
648    #[test]
649    fn matches_backtracks_to_span_the_whole_input() {
650        // (pattern, input, matches)
651        let cases = [
652            row("a|ab", "ab", true),
653            row("a|ab", "abc", false),
654            row("CN=(.*?)", "CN=abc", true),
655            row("(a+)+b", "aaab", true),
656            row("ali", "alice", false),
657            row("ali.*", "alice", true),
658        ];
659        check_matches(cases, false);
660    }
661
662    /// A search that runs past the engine's backtrack limit is an error. Java
663    /// answers the same pattern, slowly, so "no match" would send a rule that
664    /// Java applies on to the next rule, or to `DEFAULT`.
665    #[test]
666    fn matches_reports_a_search_the_engine_gave_up_on() {
667        let hostile = format!("{}b", "a".repeat(40));
668
669        let result = pattern("^(a|aa)+\\1$").matches(&hostile);
670
671        assert!(result.is_err());
672    }
673
674    /// `Matcher.find` may match the empty string at the position a non-empty
675    /// match ended, which the `regex` crate's iterator does not, and starts
676    /// one character on after an empty match. Each row is the result of
677    /// `input.replaceAll(pattern, replacement)` on JDK 21.
678    #[test]
679    fn replace_all_finds_an_empty_match_after_a_non_empty_one() {
680        // (pattern, input, replacement, result)
681        let cases = [
682            replace_row("(.*)", "alice", "$1x", "alicexx"),
683            replace_row("a*", "baaac", "-", "-b--c-"),
684            replace_row("x*", "abc", "-", "-a-b-c-"),
685            replace_row("\\w*", "ab cd", "[$0]", "[ab][] [cd][]"),
686            replace_row("(?:)", "Γ©!", "|", "|Γ©|!|"),
687            replace_row("", "", "|", "|"),
688            replace_row("a", "", "|", ""),
689            replace_row("b*", "aΓ©", "-", "-a-Γ©-"),
690        ];
691        check_replacements(cases, true, false);
692    }
693
694    /// `replaceFirst` stops after the first match, an empty one included.
695    #[test]
696    fn replace_first_stops_after_the_first_match_even_when_it_is_empty() {
697        // (pattern, input, replacement, result)
698        check_replacements(
699            [
700                replace_row("(.*)", "alice", "$1x", "alicex"),
701                replace_row("b*", "abc", "-", "-abc"),
702            ],
703            false,
704            false,
705        );
706    }
707
708    /// Java reads `\w`, `\d`, `\s` and `\b` as ASCII unless `(?U)` asks for
709    /// Unicode, and `.` stops at every line terminator unless `(?s)` or `(?d)`
710    /// says otherwise. Each row is `Pattern.matches` on JDK 21, except `\b`,
711    /// which reads ASCII from JDK 19.
712    #[test]
713    fn matches_reads_classes_as_ascii_and_dot_as_java_does() {
714        // (pattern, input, whole input matches)
715        let cases = [
716            row("\\w+", "abc_09", true),
717            row("\\w+", "Γ©", false),
718            row("\\W", "Γ©", true),
719            row("\\d", "Ω£", false),
720            row("\\D", "Ω£", true),
721            row("\\s", " \t\u{0B}", false),
722            row("\\s+", " \t\u{0B}", true),
723            row("\\s", "\u{a0}", false),
724            row("\\s", "\u{2003}", false),
725            row("\\S", "\u{a0}", true),
726            row("[\\w.]+", "a.b", true),
727            row("[\\w.]+", "Γ©", false),
728            row("[^\\w]", "Γ©", true),
729            row("[\\W]+", "Γ©!", true),
730            row("[\\d-]+", "1-2", true),
731            row("[\\s]", "\u{a0}", false),
732            row("(?U)\\w+", "Γ©", true),
733            row("(?U)\\d", "Ω£", true),
734            row("(?U)\\s", "\u{a0}", true),
735            row("(?U:\\w)\\w", "Γ©a", true),
736            row("(?U:\\w)\\w", "éé", false),
737            row("(?:(?U)\\w)\\w", "éé", false),
738            row("(?U)(?-U)\\w", "Γ©", false),
739            row("\\bfoo", "foo", true),
740            row("a\\b", "a", true),
741            row("\\Bfoo", "foo", false),
742            row("a\\bΓ©", "aΓ©", true),
743            row("\\bΓ©", "Γ©", false),
744            row("a\\BΓ©", "aΓ©", false),
745            row("a.b", "axb", true),
746            row("a.b", "a\nb", false),
747            row("a.b", "a\rb", false),
748            row("a.b", "a\u{85}b", false),
749            row("a.b", "a\u{2028}b", false),
750            row("a.b", "a\u{2029}b", false),
751            row(".*", "abc\rdef", false),
752            row("(?s)a.b", "a\rb", true),
753            row("(?s)a.b", "a\nb", true),
754            row("(?d)a.b", "a\rb", true),
755            row("(?d)a.b", "a\u{85}b", true),
756            row("(?d)a.b", "a\nb", false),
757            row("(?s:a.)b.", "a\nbx", true),
758            row("(?s:a.)b.", "a\nb\r", false),
759            row("(?s)(?-s).", "\n", false),
760            row("a[.]b", "a.b", true),
761            row("a\\.b", "axb", false),
762        ];
763        check_matches(cases, true);
764    }
765
766    /// `(?i)` folds ASCII case only, and `(?u)`, or `(?U)`, adds Unicode case.
767    /// `fancy_regex` folds Unicode whenever its `i` is on, so the translation
768    /// writes both cases itself. Each row is `Pattern.matches` on JDK 17, from
769    /// the `java.util.regex` documentation of `CASE_INSENSITIVE` and
770    /// `UNICODE_CASE`: long s (U+017F) and the Kelvin sign (U+212A) are the
771    /// letters that fold to `s` and `k` in Unicode and not in ASCII.
772    #[test]
773    fn matches_folds_case_as_java_does() {
774        // (pattern, input, whole input matches)
775        let cases = [
776            row("(?i)service-", "SERVICE-", true),
777            row("(?i)service-", "service-", true),
778            row("(?i)service-", "\u{17f}ervice-", false),
779            row("(?iu)service-", "\u{17f}ervice-", true),
780            row("(?iu)service-", "SERVICE-", true),
781            row("(?i)k", "\u{212a}", false),
782            row("(?iu)k", "\u{212a}", true),
783            row("(?ui)k", "\u{212a}", true),
784            row("(?u)k", "K", false),
785            row("(?i)Γ©", "Γ‰", false),
786            row("(?i)Γ©", "Γ©", true),
787            row("(?iu)Γ©", "Γ‰", true),
788            row("(?U)(?i)k", "\u{212a}", true),
789            row("(?iU)k", "\u{212a}", true),
790            row("(?iU)(?-U)k", "\u{212a}", false),
791            row("(?iU)(?-U)k", "K", true),
792            row("(?iU)(?-u)k", "\u{212a}", false),
793            row("(?iU)(?-u)\\w", "\u{e9}", true),
794            row("(?i)a(?u)k", "A\u{212a}", true),
795            row("(?i)a(?u)k", "AK", true),
796            row("(?iu)a(?-u)k", "A\u{212a}", false),
797            row("(?iu)a(?-u)k", "AK", true),
798            row("(?i)ss", "\u{df}", false),
799            row("(?iu)ss", "\u{df}", false),
800            row("(?i)i", "\u{130}", false),
801            row("(?i)i", "\u{131}", false),
802            row("(?i)\\u00E5", "\u{212b}", false),
803            row("(?iu)\\u00E5", "\u{212b}", true),
804            row("(?i)[a-f]+", "AbCdEf", true),
805            row("(?i)[a-f]", "\u{212a}", false),
806            row("(?i)[k]", "\u{212a}", false),
807            row("(?iu)[k]", "\u{212a}", true),
808            row("(?i)[^a]", "A", false),
809            row("(?i)[^a]", "b", true),
810            row("(?i)[^a]", "\u{e9}", true),
811            row("(?i)[^k]", "\u{212a}", true),
812            row("(?iu)[^k]", "\u{212a}", false),
813            row("(?i)[X-c]", "x", true),
814            row("(?i)[X-c]", "A", true),
815            row("(?i)[X-c]", "[", true),
816            row("(?i)[X-c]", "d", false),
817            row("(?i)[+-z]", "Q", true),
818            row("(?i)[a-c&&[^b]]", "B", false),
819            row("(?i)[a-c&&[^b]]", "C", true),
820            row("(?i)[a-c&&[^b]]", "A", true),
821            row("(?i)[a-c[^x-z]]", "Y", false),
822            row("(?i)[a-c[^x-z]]", "B", true),
823            row("(?i)[\\x41-\\x43]", "b", true),
824            row("(?i)[\\u0041-\\u0043]", "b", true),
825            row("(?i)[\\Qab\\E]", "B", true),
826            row("(?i)[a-]", "A", true),
827            row("(?i)[-a]", "A", true),
828            row("(?i)[]a]", "]", true),
829            row("(?i)[]a]", "A", true),
830            row("(?i)[a-c-e]", "E", true),
831            row("(?i)[a-c-e]", "-", true),
832            row("(?i)[\\w&&[^a]]", "A", false),
833            row("(?i)[\\w&&[^a]]", "B", true),
834            row("(?i)[a\\-c]", "C", true),
835            row("(?i)[a\\-c]", "-", true),
836            row("(?i)[\\0101-\\0103]", "b", true),
837            row("(?i)[\\0101-\\0103]", "d", false),
838            row("(?i)[\\x41-c]", "b", true),
839            row("(?i)[a-c[d-f]]", "E", true),
840            row("(?i)[\u{e9}-\u{eb}]", "\u{ca}", false),
841            row("(?ix) [ a - c ]", "B", true),
842            row("(?ix) [ a - c ] # x", "d", false),
843            row("(?i)a\\.b", "A.B", true),
844            row("(?i)\\[a\\]", "[A]", true),
845            row("(?i)a{1,2}b", "aAB", true),
846            row("(?i)\u{e9}+", "\u{e9}\u{e9}", true),
847            row("(?i)\\0101\\0102", "aB", true),
848            row("(?i)\\x41", "a", true),
849            row("(?i)\\u0041", "a", true),
850            row("(?i)\\x{41}", "a", true),
851            row("(?i)\\0101", "a", true),
852            row("(?i)\\c!", "A", true),
853            row("(?i)\\Qab\\E", "AB", true),
854            row("(?i)\\Q.b\\E", ".B", true),
855            row("(?i)\\Q.b\\E", "xB", false),
856            row("(?i)\\Qk\\E", "\u{212a}", false),
857            row("(?i)\\p{Lower}", "A", true),
858            row("(?i)\\p{Lower}", "\u{e9}", false),
859            row("(?i)\\p{Upper}", "a", true),
860            row("(?i)\\p{Upper}", "\u{c9}", false),
861            row("\\p{Lower}", "a", true),
862            row("\\p{Lower}", "A", false),
863            row("\\p{Lower}", "\u{e9}", false),
864            row("\\p{Upper}", "A", true),
865            row("\\p{Upper}", "\u{c9}", false),
866            row("(?i)\\p{Lu}", "a", true),
867            row("(?i)\\p{Lu}", "\u{e9}", true),
868            row("(?i)\\p{Lu}", "1", false),
869            row("(?i)\\P{Lu}", "a", false),
870            row("(?i)\\P{Lu}", "1", true),
871            row("\\p{Lu}", "a", false),
872            row("\\p{Lu}", "A", true),
873            row("(?i)[\\p{Lower}]", "A", true),
874            row("(?i)[\\P{Lower}]", "A", false),
875            row("(?i)[\\P{Lower}]", "1", true),
876            row("(?i)\\p{IsLowercase}", "A", true),
877            row("(?i)\\p{gc=Lu}", "a", true),
878            row("(?i)\\p{IsLu}", "a", true),
879            row("(?i:a)b", "Ab", true),
880            row("(?i:a)b", "AB", false),
881            row("a(?i)b", "aB", true),
882            row("a(?i)b", "Ab", false),
883            row("a(?i:b)c", "aBc", true),
884            row("a(?i:b)c", "aBC", false),
885            row("(?i)a(?-i)b", "Ab", true),
886            row("(?i)a(?-i)b", "AB", false),
887            row("((?i)a)b", "Ab", true),
888            row("((?i)a)b", "AB", false),
889            row("(?i)a|b", "B", true),
890            row("(?i)(?i)(?-i)a", "A", false),
891            row("(?i)(?i)(?-i)a", "a", true),
892            row("(?i)a{2}", "aA", true),
893            row("(?i)(?:a|b)+", "AbBa", true),
894            row("(?i)^ab$", "AB", true),
895            row("(?i)\\bab", "AB", true),
896            row("(?i)\\w", "A", true),
897            row("(?i)a.c", "AxC", true),
898            row("(?i)(?<Name>a)", "A", true),
899            row("(?i)(?=a)A", "A", true),
900            row("(?i)(?!a)b", "B", true),
901            row("(?ix) a b # comment", "AB", true),
902            row("(?x)a b # Comment here", "ab", true),
903            row("(?x)a b # Comment here", "AB", false),
904            row("(?iu)(a)\\1", "aA", true),
905            row("(?iu)(a)\\1", "ab", false),
906            row("(?i)(a)(?-i)\\1", "aA", false),
907            row("(?i)(a)(?-i)\\1", "aa", true),
908            row("(?i)(a)(?-i:\\1)", "aA", false),
909            row("(a)\\1", "aa", true),
910            row("(a)\\1", "aA", false),
911        ];
912        check_matches(cases, true);
913    }
914
915    /// A `\Q...\E` is read out before the pattern is, as Java's `RemoveQEQuoting`
916    /// does, so a quoted member takes part in a range. A `]` first in a class is a
917    /// member that may start a range, a backslash before `<` or `>` is the
918    /// character, `\w`, `\p{Lower}` and `\p{Upper}` are the ASCII letters under
919    /// `(?iu)`, and a backreference compares ASCII case-insensitively under
920    /// `(?i)`. Each row is `Pattern.matches` on JDK 17.
921    #[test]
922    fn matches_reads_quotes_classes_and_references_as_java_does() {
923        // (pattern, input, whole input matches)
924        let cases = [
925            // \Q...\E is expanded before the pattern is read, so a quoted member takes part in a range
926            row("(?i)[\\Qa\\E-c]", "B", true),
927            row("(?i)[\\Qa\\E-c]", "b", true),
928            row("(?i)[\\Qa\\E-c]", "A", true),
929            row("(?i)[\\Qa\\E-c]", "X", false),
930            row("(?i)[a-\\Qc\\E]", "B", true),
931            row("(?i)[a-\\Qc\\E]", "X", false),
932            row("(?i)[\\Qa\\E-\\Qc\\E]", "B", true),
933            row("(?i)[\\Qa\\E-\\Qc\\E]", "X", false),
934            row("(?i)[\\Qa\\Eb-d]", "C", true),
935            row("(?i)[\\Qa\\Eb-d]", "A", true),
936            row("(?i)[\\Qa\\Eb-d]", "X", false),
937            row("(?i)[\\Q\\Ea-c]", "B", true),
938            row("(?i)[a-\\Q\\Ec]", "B", true),
939            row("(?i)[a-\\Q\\Ec]", "X", false),
940            row("[\\Qa\\E-c]", "b", true),
941            row("[\\Qa\\E-c]", "B", false),
942            row("[\\Qa\\E-c]", "X", false),
943            row("(?i)[\\Q]\\E-c]", "A", true),
944            row("(?i)[\\Q]\\E-c]", "X", false),
945            row("(?i)[\\Q1\\E-3]", "2", true),
946            // a quoted dash is not a range
947            row("(?i)[\\Qa-c\\E]", "B", false),
948            row("(?i)[\\Qa-c\\E]", "-", true),
949            row("(?i)[\\Qa-c\\E]", "C", true),
950            row("(?i)[a\\Q-\\Ec]", "B", false),
951            row("(?i)[a\\Q-\\Ec]", "-", true),
952            row("(?i)[a\\Q-\\Ec]", "C", true),
953            // a quote with no \E runs to the end of the pattern
954            row("(?i)x\\Qa", "XA", true),
955            row("(?i)x\\Qa\\E", "XA", true),
956            row("(?i)a\\Q\\E+", "AA", true),
957            row("\\Q1\\E2", "12", true),
958            row("\\Qa\\\\E", "a\\", true),
959            row("\\Q\\\\E", "\\", true),
960            row("\\Qa.b\\E", "a.b", true),
961            row("\\Qa.b\\E", "axb", false),
962            row("(?i)\\Qk\\E", "\u{212a}", false),
963            row("(?iu)\\Qk\\E", "\u{212a}", true),
964            // a backslash before < or > is the character
965            row("\\<", "<", true),
966            row("\\>", ">", true),
967            row("[\\<]", "<", true),
968            row("[\\>]", ">", true),
969            row("\\Q<\\E", "<", true),
970            row("[\\Q<\\E]", "<", true),
971            row("\\Q>\\E", ">", true),
972            // a ] first in a class is a member, and starts a range
973            row("(?i)[]-c]", "A", true),
974            row("(?i)[]-c]", "B", true),
975            row("(?i)[]-c]", "_", true),
976            row("(?i)[]-c]", "]", true),
977            row("(?i)[]-c]", "X", false),
978            row("(?i)[]-c]", "d", false),
979            row("(?i)[]-c]", "D", false),
980            row("(?i)[^]-c]", "A", false),
981            row("(?i)[^]-c]", "X", true),
982            row("(?i)[^]-c]", "d", true),
983            row("(?i)[]-cx]", "X", true),
984            row("(?i)[]-]", "-", true),
985            row("(?i)[]-]", "]", true),
986            row("(?iu)[]-c]", "A", true),
987            row("(?ix)[ ]-c ]", "A", true),
988            row("[]-c]", "a", true),
989            row("[]-c]", "A", false),
990            row("[]-c]", "]", true),
991            // \p{Lower} and \p{Upper} are the ASCII letters under (?iu), whatever fancy_regex's i would fold
992            row("(?iu)\\p{Lower}", "\u{17f}", false),
993            row("(?iu)\\p{Lower}", "\u{212a}", false),
994            row("(?iu)\\p{Lower}", "A", true),
995            row("(?iu)\\p{Lower}", "a", true),
996            row("(?iu)\\p{Upper}", "\u{17f}", false),
997            row("(?iu)\\p{Upper}", "\u{212a}", false),
998            row("(?iu)\\p{Upper}", "a", true),
999            row("(?iu)\\p{Upper}", "\u{e9}", false),
1000            row("(?iu)\\P{Lower}", "\u{17f}", true),
1001            row("(?iu)\\P{Lower}", "A", false),
1002            row("(?iu)\\P{Lower}", "1", true),
1003            row("(?iu)\\P{Upper}", "\u{212a}", true),
1004            row("(?u)\\p{Lower}", "\u{17f}", false),
1005            row("(?u)\\p{Lower}", "A", false),
1006            // \w and \W are ASCII under (?iu), whatever fancy_regex's i would fold
1007            row("(?iu)\\w", "\u{17f}", false),
1008            row("(?iu)\\w", "\u{212a}", false),
1009            row("(?iu)\\w", "a", true),
1010            row("(?iu)\\w", "_", true),
1011            row("(?iu)\\W", "\u{17f}", true),
1012            row("(?iu)\\W", "\u{212a}", true),
1013            row("(?iu)\\W", "a", false),
1014            row("(?iu)\\W", "-", true),
1015            row("(?iu)\\w+", "k\u{212a}", false),
1016            row("(?iu)k\\w", "k\u{212a}", false),
1017            row("(?iu)\\w\\p{Lower}", "k\u{212a}", false),
1018            row("(?i)\\w", "\u{17f}", false),
1019            row("(?u)\\w", "\u{17f}", false),
1020            row("(?iU)\\w", "\u{17f}", true),
1021            // a backreference compares ASCII case-insensitively under (?i)
1022            row("(?i)(a)\\1", "aA", true),
1023            row("(?i)(a)\\1", "aa", true),
1024            row("(?i)(a)\\1", "ab", false),
1025            row("(?i)(\\d+)-\\1", "12-12", true),
1026            row("(?i)(\\d+)-\\1", "12-13", false),
1027            row("(?i)(?<x>a)\\k<x>", "aA", true),
1028            row("(?i)(?<x>a)\\k<x>", "ab", false),
1029            row("(?i)(a+)\\1", "aaAA", true),
1030            row("(?i)(a)\\1+", "aAAa", true),
1031            row("(?i)(a)(\\1)", "aA", true),
1032            row("(?i)(k)\\1", "kK", true),
1033            row("(?i)(k)\\1", "k\u{212a}", false),
1034            row("(?i:(a)\\1)", "aA", true),
1035            row("a(?i:(b)\\1)", "aBb", true),
1036            row("a(?i:(b)\\1)", "aBB", true),
1037            row("a(?i:(b)\\1)", "AbB", false),
1038            row("(?i)(a)((b))\\3\\2\\1", "aBBBA", true),
1039            row("(?i)(a)((b))\\3\\2\\1", "aBbBa", true),
1040        ];
1041        check_matches(cases, true);
1042    }
1043
1044    /// Under `(?iu)` a class member is the set that Java's predicates accept, and not what
1045    /// `fancy_regex`'s `i` folds a whole class to: `\w`, `\W`, `\p{Lower}` and `\p{Upper}` in a
1046    /// class are ASCII, a range that has `K` and not `k` leaves out the Kelvin sign, and `Δ°`
1047    /// and `Δ±` are members of a range that has `i`. Each row is `Pattern.matches` on JDK 17.
1048    #[test]
1049    fn matches_folds_unicode_case_in_a_class_as_java_does() {
1050        // (pattern, input, whole input matches)
1051        let cases = [
1052            // \w, \W, \p{Lower}, \p{Upper} in a class are ASCII under (?iu), so the letters that fold to s and k are not members
1053            row("(?iu)[\\w]", "\u{17f}", false),
1054            row("(?iu)[\\w]", "\u{212a}", false),
1055            row("(?iu)[\\w]", "a", true),
1056            row("(?iu)[\\w]", "K", true),
1057            row("(?iu)[\\w]", "_", true),
1058            row("(?iu)[\\w]", "\u{e9}", false),
1059            row("(?iu)[\\w-]", "\u{17f}", false),
1060            row("(?iu)[\\w-]", "\u{212a}", false),
1061            row("(?iu)[\\w-]", "-", true),
1062            row("(?iu)[\\w-]", "z", true),
1063            row("(?iu)[\\W]", "\u{17f}", true),
1064            row("(?iu)[\\W]", "\u{212a}", true),
1065            row("(?iu)[\\W]", "a", false),
1066            row("(?iu)[\\W]", "-", true),
1067            row("(?iu)[^\\w]", "\u{17f}", true),
1068            row("(?iu)[^\\w]", "k", false),
1069            row("(?iu)[\\p{Lower}]", "\u{17f}", false),
1070            row("(?iu)[\\p{Lower}]", "\u{212a}", false),
1071            row("(?iu)[\\p{Lower}]", "A", true),
1072            row("(?iu)[\\p{Lower}]", "a", true),
1073            row("(?iu)[\\p{Lower}]", "\u{e9}", false),
1074            row("(?iu)[\\p{Upper}]", "\u{17f}", false),
1075            row("(?iu)[\\p{Upper}]", "\u{212a}", false),
1076            row("(?iu)[\\p{Upper}]", "a", true),
1077            row("(?iu)[\\p{Upper}]", "\u{c9}", false),
1078            row("(?iu)[\\P{Lower}]", "\u{17f}", true),
1079            row("(?iu)[\\P{Lower}]", "A", false),
1080            row("(?iu)[\\P{Lower}]", "1", true),
1081            row("(?iu)[\\P{Upper}]", "\u{212a}", true),
1082            row("(?iu)[\\p{Lower}k]", "\u{17f}", false),
1083            row("(?iu)[\\p{Lower}k]", "\u{212a}", true),
1084            row("(?iu)[\\p{Lower}s]", "\u{17f}", true),
1085            row("(?iu)[\\w&&[^a]]", "A", false),
1086            row("(?iu)[\\w&&[^a]]", "b", true),
1087            row("(?iu)[\\w&&[^k]]", "\u{212a}", false),
1088            row("(?iu)[\\w&&[^k]]", "K", false),
1089            row("(?iu)[^\\p{Lower}]", "\u{17f}", true),
1090            row("(?iu)[^\\p{Lower}]", "a", false),
1091            row("(?iu)[^\\p{Lower}]", "1", true),
1092            // a range that holds K and not k, or s and not S, compares the upper case and the lower case of the upper case of the char
1093            row("(?iu)[a-z]", "\u{212a}", true),
1094            row("(?iu)[a-z]", "\u{17f}", true),
1095            row("(?iu)[a-z]", "\u{130}", true),
1096            row("(?iu)[a-z]", "\u{131}", true),
1097            row("(?iu)[a-z]", "K", true),
1098            row("(?iu)[a-z]", "\u{df}", false),
1099            row("(?iu)[A-Z]", "\u{212a}", false),
1100            row("(?iu)[A-Z]", "\u{17f}", true),
1101            row("(?iu)[A-Z]", "\u{130}", false),
1102            row("(?iu)[A-Z]", "\u{131}", true),
1103            row("(?iu)[A-Z]", "k", true),
1104            row("(?iu)[A-K]", "\u{212a}", false),
1105            row("(?iu)[A-K]", "k", true),
1106            row("(?iu)[A-K]", "\u{17f}", false),
1107            row("(?iu)[A-c]", "\u{212a}", false),
1108            row("(?iu)[A-c]", "k", true),
1109            row("(?iu)[A-c]", "C", true),
1110            row("(?iu)[A-c]", "d", true),
1111            row("(?iu)[A-k]", "\u{212a}", true),
1112            row("(?iu)[k-k]", "\u{212a}", true),
1113            row("(?iu)[K-K]", "\u{212a}", false),
1114            row("(?iu)[K-K]", "k", true),
1115            row("(?iu)[s-s]", "\u{17f}", true),
1116            row("(?iu)[S-S]", "\u{17f}", true),
1117            row("(?iu)[i-i]", "\u{130}", true),
1118            row("(?iu)[I-I]", "\u{131}", true),
1119            row("(?iu)[^A-Z]", "\u{212a}", true),
1120            row("(?iu)[^A-Z]", "\u{17f}", false),
1121            row("(?iu)[^a-z]", "\u{212a}", false),
1122            row("(?iu)[^A-K]", "\u{212a}", true),
1123            row("(?iu)[^A-K]", "k", false),
1124            row("(?iu)[\\x{E0}-\\x{FE}]", "\u{c9}", true),
1125            row("(?iu)[\\x{E0}-\\x{FE}]", "\u{178}", false),
1126            row("(?iu)[\\x{C0}-\\x{DE}]", "\u{ff}", false),
1127            row("(?iu)[\\x{C0}-\\x{DE}]", "\u{178}", false),
1128            row("(?iu)[\\x{391}-\\x{3A9}]", "\u{3c3}", true),
1129            row("(?iu)[\\x{391}-\\x{3A9}]", "\u{3c2}", true),
1130            row("(?iu)[\\x{3B1}-\\x{3C9}]", "\u{3a3}", true),
1131            row("(?iu)[\\x{410}-\\x{42F}]", "\u{44f}", true),
1132            row("(?iu)[\\x{410}-\\x{42F}]", "\u{436}", true),
1133            row("(?iu)[\\x{DF}-\\x{DF}]", "\u{1e9e}", true),
1134            row("(?iu)[\\x{1E9E}-\\x{1E9E}]", "\u{df}", false),
1135            // a single member: Latin-1 members go in a bit class, the other members compare keys
1136            row("(?iu)[k]", "\u{212a}", true),
1137            row("(?iu)[K]", "\u{212a}", true),
1138            row("(?iu)[s]", "\u{17f}", true),
1139            row("(?iu)[S]", "\u{17f}", true),
1140            row("(?iu)[i]", "\u{130}", true),
1141            row("(?iu)[i]", "\u{131}", true),
1142            row("(?iu)[I]", "\u{130}", true),
1143            row("(?iu)[I]", "\u{131}", true),
1144            row("(?iu)[\\x{E5}]", "\u{212b}", true),
1145            row("(?iu)[\\x{C5}]", "\u{212b}", true),
1146            row("(?iu)[\\x{FF}]", "\u{178}", true),
1147            row("(?iu)[\\x{B5}]", "\u{3bc}", true),
1148            row("(?iu)[\\x{B5}]", "\u{39c}", true),
1149            row("(?iu)[\\x{3BC}]", "\u{b5}", true),
1150            row("(?iu)[\\x{E9}]", "\u{c9}", true),
1151            row("(?iu)[\\x{C9}]", "\u{e9}", true),
1152            row("(?iu)[\\x{DF}]", "\u{1e9e}", false),
1153            row("(?iu)[\\x{1E9E}]", "\u{df}", true),
1154            row("(?iu)[\\x{3C3}]", "\u{3c2}", true),
1155            row("(?iu)[\\x{3C2}]", "\u{3a3}", true),
1156            row("(?iu)[\\x{3A3}]", "\u{3c2}", true),
1157            row("(?iu)[\\x{1C6}]", "\u{1c5}", true),
1158            row("(?iu)[\\x{1C5}]", "\u{1c4}", true),
1159            row("(?iu)[\\x{1F88}]", "\u{1f80}", true),
1160            row("(?iu)[\\x{1F80}]", "\u{1f88}", true),
1161            row("(?iu)[\\x{10428}]", "\u{10400}", true),
1162            row("(?iu)[\\x{10400}-\\x{1044F}]", "\u{10428}", true),
1163            row("(?iu)[z]", "Z", true),
1164            row("(?iu)[1]", "1", true),
1165            // with && and a nested class
1166            row("(?iu)[a-z&&[^k]]", "\u{212a}", false),
1167            row("(?iu)[a-z&&[^k]]", "K", false),
1168            row("(?iu)[a-z&&[^k]]", "j", true),
1169            row("(?iu)[a-z&&k]", "\u{212a}", true),
1170            row("(?iu)[a-z&&k]", "K", true),
1171            row("(?iu)[a-c[x-z]]", "X", true),
1172            row("(?iu)[a-c[^x-z]]", "Y", false),
1173            row("(?iu)[a-c[^x-z]]", "B", true),
1174            row("(?iu)[^a-z&&[^k]]", "k", true),
1175            row("(?iu)[^a-z&&[^k]]", "\u{212a}", true),
1176            row("(?iu)[a&&-1]", "-", false),
1177            row("(?iu)[a&&-1]", "a", false),
1178            row("(?iu)[a-c&&b-d]", "B", true),
1179            row("(?iu)[a-c&&b-d]", "A", false),
1180            // under (?iU) the classes are Unicode and the members fold the same
1181            row("(?iU)[\\w]", "\u{17f}", true),
1182            row("(?iU)[\\w]", "\u{e9}", true),
1183            row("(?iU)[a-z]", "\u{212a}", true),
1184            row("(?iU)[A-Z]", "\u{212a}", false),
1185            row("(?iU)[\\p{Lower}]", "A", true),
1186            row("(?iU)[\\p{Upper}]", "a", true),
1187            row("(?iU)[\\p{Lower}]", "\u{1c5}", true),
1188            row("(?iU)[\\p{Lu}]", "a", true),
1189            row("(?iU)[\\p{Lu}]", "1", false),
1190            // only the ASCII fold, which does not reach these
1191            row("(?i)[a-z]", "\u{212a}", false),
1192            row("(?i)[a-z]", "\u{17f}", false),
1193            row("(?i)[A-K]", "k", true),
1194            row("(?i)[k]", "\u{212a}", false),
1195            row("(?i)[\\w]", "\u{17f}", false),
1196            row("(?u)[a-z]", "A", false),
1197            row("(?u)[k]", "\u{212a}", false),
1198            row("[a-z]", "K", false),
1199            // the fold ends where the flag does
1200            row("(?iu)(?-i)[a-z]", "A", false),
1201            row("(?iu)(?-i)[a-z]", "\u{212a}", false),
1202            row("(?iu)(?-u)[a-z]", "A", true),
1203            row("(?iu)(?-u)[a-z]", "\u{212a}", false),
1204            row("(?iu)(?-u)[k]", "\u{212a}", false),
1205            row("(?iu:[a-z])A", "AA", true),
1206            row("(?iu:[a-z])A", "Aa", false),
1207            row("(?iu:[a-z])[A-Z]", "aa", false),
1208            row("(?iu:[a-z])[A-Z]", "\u{212a}a", false),
1209        ];
1210        check_matches(cases, true);
1211    }
1212
1213    /// The surrogates are not chars, so a range that has them in the middle, or at an end, holds
1214    /// the chars on either side, and a negated one leaves out the chars on either side. Each row
1215    /// is `Pattern.matches` on JDK 17.
1216    #[test]
1217    fn matches_reads_a_class_across_the_surrogates_as_java_does() {
1218        // (pattern, input, whole input matches)
1219        let cases = [
1220            row("(?iu)[^\\x{3c2}-\\x{10975}]", "\u{e000}", false),
1221            row("(?iu)[^\\x{3c2}-\\x{10975}]", "\u{d7ff}", false),
1222            row("(?iu)[^\\x{3c2}-\\x{10975}]", "\u{e001}", false),
1223            row("(?iu)[^\\x{3c2}-\\x{10975}]", "\u{10976}", true),
1224            row("(?iu)[^\\x{3c2}-\\x{10975}]", "a", true),
1225            row("(?iu)[\\x{D7FF}-\\x{E000}]", "\u{d7ff}", true),
1226            row("(?iu)[\\x{D7FF}-\\x{E000}]", "\u{e000}", true),
1227            row("(?iu)[\\x{D7FF}-\\x{E000}]", "\u{e001}", false),
1228            row("(?iu)[^\\x{D7FF}-\\x{E000}]", "\u{d7ff}", false),
1229            row("(?iu)[^\\x{D7FF}-\\x{E000}]", "\u{e000}", false),
1230            row("(?iu)[^\\x{D7FF}-\\x{E000}]", "\u{d7fe}", true),
1231            row("(?iu)[^\\x{D7FF}-\\x{E000}]", "\u{e001}", true),
1232            row("(?iu)[^\\x{D800}-\\x{E001}]", "\u{e000}", false),
1233            row("(?iu)[^\\x{D800}-\\x{E001}]", "\u{e002}", true),
1234            row("(?iu)[^\\x{41}-\\x{DBFF}]", "\u{d7ff}", false),
1235            row("(?iu)[^\\x{41}-\\x{DBFF}]", "\u{e000}", true),
1236            row("(?iu)[\\x{D800}-\\x{DFFF}]", "a", false),
1237            row("(?iu)[^\\x{D800}-\\x{DFFF}]", "a", true),
1238            row("(?iu)[^\\x{D800}-\\x{DFFF}]", "\u{e000}", true),
1239            row("(?iu)[a\\x{D800}-\\x{DFFF}]", "a", true),
1240            row("(?iu)[a\\x{D800}-\\x{DFFF}]", "\u{d7ff}", false),
1241            row("(?iu)[^a\\x{D800}-\\x{DFFF}]", "a", false),
1242            row("(?iu)[^a\\x{D800}-\\x{DFFF}]", "b", true),
1243        ];
1244        check_matches(cases, true);
1245    }
1246
1247    /// The POSIX classes are ASCII unless `(?U)` is on, when they are the Unicode properties, and
1248    /// no case flag folds them or the other properties, except that `Lower`, `Upper`, `Lu`, `Ll`
1249    /// and `Lt` take in every cased letter under `(?i)`. `fancy_regex` reads the names as Unicode
1250    /// and folds a property under its `i`. Each row is `Pattern.matches` on JDK 17.
1251    #[test]
1252    fn matches_reads_the_posix_classes_and_properties_as_java_does() {
1253        // (pattern, input, whole input matches)
1254        let cases = [
1255            // the POSIX classes are ASCII without (?U), where fancy_regex reads them as Unicode
1256            row("\\p{Alpha}", "a", true),
1257            row("\\p{Alpha}", "Z", true),
1258            row("\\p{Alpha}", "\u{e9}", false),
1259            row("\\p{Alpha}", "\u{3b1}", false),
1260            row("\\p{Alpha}", "1", false),
1261            row("\\p{Alpha}", "_", false),
1262            row("\\P{Alpha}", "\u{e9}", true),
1263            row("\\P{Alpha}", "a", false),
1264            row("\\p{Alnum}", "5", true),
1265            row("\\p{Alnum}", "q", true),
1266            row("\\p{Alnum}", "_", false),
1267            row("\\p{Alnum}", "\u{e9}", false),
1268            row("\\p{Alnum}", "\u{663}", false),
1269            row("\\p{Digit}", "7", true),
1270            row("\\p{Digit}", "\u{663}", false),
1271            row("\\p{Digit}", "\u{ff17}", false),
1272            row("\\p{Punct}", "!", true),
1273            row("\\p{Punct}", "$", true),
1274            row("\\p{Punct}", "_", true),
1275            row("\\p{Punct}", "~", true),
1276            row("\\p{Punct}", "`", true),
1277            row("\\p{Punct}", "@", true),
1278            row("\\p{Punct}", "a", false),
1279            row("\\p{Punct}", "\u{a1}", false),
1280            row("\\p{Punct}", "\u{2014}", false),
1281            row("\\P{Punct}", "\u{a1}", true),
1282            row("\\P{Punct}", "!", false),
1283            row("\\p{ASCII}", "a", true),
1284            row("\\p{ASCII}", "\u{7f}", true),
1285            row("\\p{ASCII}", "\u{0}", true),
1286            row("\\p{ASCII}", "\u{80}", false),
1287            row("\\p{ASCII}", "\u{e9}", false),
1288            row("\\P{ASCII}", "\u{e9}", true),
1289            row("\\p{XDigit}", "f", true),
1290            row("\\p{XDigit}", "F", true),
1291            row("\\p{XDigit}", "9", true),
1292            row("\\p{XDigit}", "g", false),
1293            row("\\p{XDigit}", "\u{ff11}", false),
1294            row("\\p{Blank}", " ", true),
1295            row("\\p{Blank}", "\t", true),
1296            row("\\p{Blank}", "\n", false),
1297            row("\\p{Blank}", "\u{a0}", false),
1298            row("\\p{Cntrl}", "\u{1f}", true),
1299            row("\\p{Cntrl}", "\u{7f}", true),
1300            row("\\p{Cntrl}", "\u{0}", true),
1301            row("\\p{Cntrl}", " ", false),
1302            row("\\p{Cntrl}", "\u{80}", false),
1303            row("\\p{Graph}", "!", true),
1304            row("\\p{Graph}", "~", true),
1305            row("\\p{Graph}", " ", false),
1306            row("\\p{Graph}", "\u{e9}", false),
1307            row("\\p{Print}", " ", true),
1308            row("\\p{Print}", "~", true),
1309            row("\\p{Print}", "\u{7f}", false),
1310            row("\\p{Print}", "\u{e9}", false),
1311            row("\\p{Space}", " ", true),
1312            row("\\p{Space}", "\u{b}", true),
1313            row("\\p{Space}", "\u{a0}", false),
1314            row("\\p{Space}", "\u{2003}", false),
1315            row("[\\p{Alpha}]", "\u{e9}", false),
1316            row("[\\p{Alpha}]", "b", true),
1317            row("[\\p{Alpha}\\d]", "7", true),
1318            row("[\\p{Alnum}_]", "_", true),
1319            row("[\\p{Alnum}_]", "\u{e9}", false),
1320            row("[\\p{Punct}]", "\u{a1}", false),
1321            row("[\\p{Punct}]", ";", true),
1322            row("[^\\p{Punct}]", ";", false),
1323            row("[^\\p{Punct}]", "\u{a1}", true),
1324            row("[\\P{Punct}]", ";", false),
1325            row("[\\p{ASCII}]", "\u{e9}", false),
1326            row("[\\p{ASCII}-]", "-", true),
1327            row("[\\p{XDigit}]", "\u{ff11}", false),
1328            row("[\\p{Blank}]", "\u{a0}", false),
1329            row("[\\p{Space}x]", "\u{a0}", false),
1330            row("[\\p{Cntrl}]", "\u{80}", false),
1331            row("[\\p{Graph}]", "\u{e9}", false),
1332            row("[\\p{Print}]", "\u{e9}", false),
1333            row("[\\p{Alpha}&&[^a]]", "a", false),
1334            row("[\\p{Alpha}&&[^a]]", "b", true),
1335            // the case flags do not fold them, and under (?i) Lower and Upper are the ASCII letters of both cases
1336            row("(?i)\\p{Alpha}", "\u{17f}", false),
1337            row("(?i)\\p{Alpha}", "\u{212a}", false),
1338            row("(?iu)\\p{Alpha}", "\u{17f}", false),
1339            row("(?iu)\\p{Alpha}", "\u{212a}", false),
1340            row("(?iu)\\p{ASCII}", "\u{17f}", false),
1341            row("(?iu)\\p{ASCII}", "\u{212a}", false),
1342            row("(?iu)\\p{ASCII}", "K", true),
1343            row("(?iu)\\p{Punct}", "\u{212a}", false),
1344            row("(?iu)\\p{Digit}", "1", true),
1345            row("(?iu)[\\p{Alnum}]", "\u{17f}", false),
1346            row("(?iu)[\\p{Alnum}]", "\u{212a}", false),
1347            row("(?iu)[\\p{ASCII}]", "\u{212a}", false),
1348            row("(?iu)[\\p{XDigit}]", "\u{212a}", false),
1349            row("(?iu)[\\p{Punct}k]", "\u{212a}", true),
1350            row("(?i)\\p{Lower}", "A", true),
1351            row("(?i)\\p{Upper}", "a", true),
1352            row("(?i)\\p{Lower}", "\u{e9}", false),
1353            row("(?iu)\\p{Lower}", "\u{17f}", false),
1354            row("(?iu)\\p{Upper}", "\u{212a}", false),
1355            // the properties that are not the ASCII ones are not folded either
1356            row("(?iu)\\p{IsGreek}", "\u{345}", false),
1357            row("(?iu)\\p{IsLatin}", "\u{212a}", true),
1358            row("(?iu)\\p{IsLatin}", "\u{17f}", true),
1359            row("(?iu)\\p{L}", "\u{17f}", true),
1360            row("(?iu)\\p{Nd}", "\u{663}", true),
1361            row("(?iu)[\\p{IsGreek}]", "\u{345}", false),
1362            row("(?iu)\\p{Lu}", "a", true),
1363            row("(?iu)\\p{Lu}", "\u{df}", true),
1364            row("(?iu)\\p{Lu}", "\u{138}", true),
1365            row("(?iu)\\p{Lu}", "1", false),
1366            row("(?iu)\\p{Ll}", "A", true),
1367            row("(?iu)\\p{Lt}", "a", true),
1368            row("(?iu)\\P{Lu}", "a", false),
1369            row("(?iu)\\P{Lu}", "1", true),
1370            row("(?iu)[\\p{Lu}]", "\u{df}", true),
1371            row("(?iu)[\\p{Lu}]", "\u{138}", true),
1372            row("(?iu)[^\\p{Lu}]", "\u{df}", false),
1373            row("(?iu)[^\\p{Lu}]", "1", true),
1374            row("(?iu)\\p{IsLowercase}", "A", true),
1375            row("(?iu)\\p{IsUppercase}", "a", true),
1376            row("(?iu)\\p{IsLowercase}", "\u{2102}", true),
1377            row("(?iu)\\p{gc=Lu}", "\u{138}", true),
1378            row("(?iu)\\p{IsLu}", "\u{138}", true),
1379            row("(?iu)\\p{Lower}", "A", true),
1380            // under (?U) the POSIX classes are the Unicode properties
1381            row("(?U)\\p{Alpha}", "\u{e9}", true),
1382            row("(?U)\\p{Alpha}", "1", false),
1383            row("(?U)\\p{Alnum}", "\u{e9}", true),
1384            row("(?U)\\p{Alnum}", "\u{663}", true),
1385            row("(?U)\\p{Alnum}", "_", false),
1386            row("(?U)\\p{Digit}", "\u{663}", true),
1387            row("(?U)\\p{Digit}", "a", false),
1388            row("(?U)\\p{Punct}", "\u{a1}", true),
1389            row("(?U)\\p{Punct}", "$", false),
1390            row("(?U)\\p{Punct}", "!", true),
1391            row("(?U)\\p{Space}", "\u{a0}", true),
1392            row("(?U)\\p{Space}", "\u{85}", true),
1393            row("(?U)\\p{Space}", "\u{2028}", true),
1394            row("(?U)\\p{Space}", "a", false),
1395            row("(?U)\\p{XDigit}", "\u{ff11}", true),
1396            row("(?U)\\p{XDigit}", "\u{663}", true),
1397            row("(?U)\\p{XDigit}", "g", false),
1398            row("(?U)\\p{Blank}", "\u{a0}", true),
1399            row("(?U)\\p{Blank}", "\t", true),
1400            row("(?U)\\p{Blank}", "\n", false),
1401            row("(?U)\\p{Cntrl}", "\u{80}", true),
1402            row("(?U)\\p{Cntrl}", " ", false),
1403            row("(?U)\\p{Graph}", "\u{e9}", true),
1404            row("(?U)\\p{Graph}", " ", false),
1405            row("(?U)\\p{Graph}", "\u{a0}", false),
1406            row("(?U)\\p{Graph}", "\u{2028}", false),
1407            row("(?U)\\p{Print}", "\u{e9}", true),
1408            row("(?U)\\p{Print}", " ", true),
1409            row("(?U)\\p{Print}", "\u{a0}", true),
1410            row("(?U)\\p{Print}", "\n", false),
1411            row("(?U)\\p{Print}", "\u{2028}", false),
1412            row("(?U)\\p{Lower}", "\u{e9}", true),
1413            row("(?U)\\p{Lower}", "A", false),
1414            row("(?U)\\p{Upper}", "\u{c9}", true),
1415            row("(?U)\\p{alpha}", "\u{e9}", true),
1416            row("(?U)\\P{Graph}", " ", true),
1417            row("(?U)\\P{Graph}", "a", false),
1418            row("(?U)[\\p{Alpha}]", "\u{e9}", true),
1419            row("(?U)[\\p{Graph}]", "\u{e9}", true),
1420            row("(?U)[\\P{Graph}x]", " ", true),
1421            row("(?U)[\\P{Graph}x]", "x", true),
1422            row("(?U)[\\P{Graph}x]", "a", false),
1423            row("(?iU)\\p{Lower}", "A", true),
1424            row("(?iU)\\p{Lower}", "\u{1c5}", true),
1425            row("(?iU)\\p{Upper}", "a", true),
1426            row("(?iU)\\p{Lower}", "\u{2102}", true),
1427            row("(?iU)\\p{Alpha}", "\u{212a}", true),
1428            row("(?iU)\\p{Punct}", "\u{a1}", true),
1429            row("(?iU)\\p{Graph}", "\u{212a}", true),
1430        ];
1431        check_matches(cases, true);
1432    }
1433
1434    /// Under `(?iu)` a literal matches the code points with its key, `Character.toLowerCase` of
1435    /// `Character.toUpperCase`, which is not the simple case folding that `fancy_regex`'s `i`
1436    /// compares: `Δ°` and `Δ±` match `i`. Each row is `Pattern.matches` on JDK 17.
1437    #[test]
1438    fn matches_folds_unicode_case_of_a_literal_as_java_does() {
1439        // (pattern, input, whole input matches)
1440        let cases = [
1441            // a literal compares Character.toLowerCase(Character.toUpperCase(ch)), not the simple case folding of fancy_regex
1442            row("(?iu)i", "\u{130}", true),
1443            row("(?iu)i", "\u{131}", true),
1444            row("(?iu)I", "\u{130}", true),
1445            row("(?iu)I", "\u{131}", true),
1446            row("(?iu)\u{130}", "i", true),
1447            row("(?iu)\u{130}", "\u{131}", true),
1448            row("(?iu)\u{131}", "I", true),
1449            row("(?iu)\u{131}", "\u{130}", true),
1450            row("(?iu)i", "I", true),
1451            row("(?iu)id", "\u{130}D", true),
1452            row("(?iu)k", "\u{212a}", true),
1453            row("(?iu)K", "\u{212a}", true),
1454            row("(?iu)\u{212a}", "k", true),
1455            row("(?iu)s", "\u{17f}", true),
1456            row("(?iu)\u{17f}", "S", true),
1457            row("(?iu)\u{e5}", "\u{212b}", true),
1458            row("(?iu)\u{212b}", "\u{c5}", true),
1459            row("(?iu)\u{3c3}", "\u{3c2}", true),
1460            row("(?iu)\u{3c2}", "\u{3a3}", true),
1461            row("(?iu)\u{3a3}", "\u{3c3}", true),
1462            row("(?iu)\u{b5}", "\u{39c}", true),
1463            row("(?iu)\u{b5}", "\u{3bc}", true),
1464            row("(?iu)\u{1c5}", "\u{1c6}", true),
1465            row("(?iu)\u{1f80}", "\u{1f88}", true),
1466            row("(?iu)\u{1f88}", "\u{1f80}", true),
1467            row("(?iu)\u{1f80}", "\u{1f80}", true),
1468            row("(?iu)\u{ff}", "\u{178}", true),
1469            row("(?iu)\u{10428}", "\u{10400}", true),
1470            row("(?iu)\u{e9}", "\u{c9}", true),
1471            row("(?iu)\u{436}", "\u{416}", true),
1472            row("(?iu)1", "1", true),
1473            row("(?iu)ss", "\u{df}", false),
1474            row("(?iu)SS", "\u{1e9e}", false),
1475            row("(?iu)\u{df}", "\u{df}", true),
1476            // with the fold off, in ASCII, or ended, the letters stay apart
1477            row("(?i)i", "\u{130}", false),
1478            row("(?i)i", "\u{131}", false),
1479            row("(?i)k", "\u{212a}", false),
1480            row("(?u)i", "I", false),
1481            row("(?u)i", "\u{130}", false),
1482            row("(?iu)(?-i)i", "I", false),
1483            row("(?iu)(?-i)i", "\u{130}", false),
1484            row("(?iu)(?-u)i", "\u{130}", false),
1485            row("(?iu)(?-u)i", "I", true),
1486            row("(?iu:i)", "\u{130}", true),
1487            row("(?iu:i)i", "\u{130}i", true),
1488            row("(?iu:i)i", "\u{130}I", false),
1489        ];
1490        check_matches(cases, true);
1491    }
1492
1493    /// Java reads a literal in a run of two or more as a slice, which also matches `ẞ` for `ß`,
1494    /// and a lone literal, or one that a quantifier follows, on its own. Each row is
1495    /// `Pattern.matches` on JDK 17.
1496    #[test]
1497    fn matches_reads_a_run_of_literals_as_java_does() {
1498        // (pattern, input, whole input matches)
1499        let cases = [
1500            // a lone ß matches only itself, and one in a run of two or more also matches ẞ
1501            row("(?iu)\u{df}", "\u{1e9e}", false),
1502            row("(?iu)\u{df}", "\u{df}", true),
1503            row("(?iu)\u{df}\u{df}", "\u{1e9e}\u{1e9e}", true),
1504            row("(?iu)\u{df}a", "\u{1e9e}a", true),
1505            row("(?iu)a\u{df}", "a\u{1e9e}", true),
1506            row("(?iu)\u{df}*", "\u{1e9e}", false),
1507            row("(?iu)\u{df}a*", "\u{1e9e}", false),
1508            row("(?iu)\u{df}a*", "\u{1e9e}a", false),
1509            row("(?iu)\u{df}ab*", "\u{1e9e}ab", true),
1510            row("(?iu)\u{df}ab*", "\u{1e9e}a", true),
1511            row("(?iu)a\u{df}*", "a\u{1e9e}", false),
1512            row("(?iu)a\u{df}*", "a", true),
1513            row("(?iu)a{2}\u{df}", "aa\u{1e9e}", false),
1514            row("(?iu)a{2}\u{df}\u{df}", "aa\u{1e9e}\u{1e9e}", true),
1515            row("(?iu)(\u{df})", "\u{1e9e}", false),
1516            row("(?iu)(\u{df}\u{df})", "\u{1e9e}\u{1e9e}", true),
1517            row("(?iu)\u{df}|\u{df}", "\u{1e9e}", false),
1518            row("(?iu)\u{df}.", "\u{1e9e}x", false),
1519            row("(?iu)\u{df}\\.", "\u{1e9e}.", true),
1520            row("(?iu)\u{df}\\w", "\u{1e9e}x", false),
1521            row("(?iu)\u{df}{1,2}", "\u{1e9e}", false),
1522            row("(?iu)\u{df}\u{df}?", "\u{1e9e}", false),
1523            row("(?iu)\u{df}\u{df}?", "\u{1e9e}\u{1e9e}", false),
1524            row("(?iu)x\u{df}", "x\u{1e9e}", true),
1525            row("(?iu)\u{df}+x", "\u{1e9e}x", false),
1526            row("(?iu)\u{df}x", "\u{1e9e}x", true),
1527            row("(?iu)(?:\u{df})\u{df}", "\u{1e9e}\u{1e9e}", false),
1528            row("(?iu)(?:\u{df})", "\u{1e9e}", false),
1529            row("(?iu)\u{df}$", "\u{1e9e}", false),
1530            row("(?iu)^\u{df}", "\u{1e9e}", false),
1531            row("(?iu)\u{df}[a]", "\u{1e9e}a", false),
1532            row("(?iu)\u{df}\\x{DF}", "\u{1e9e}\u{1e9e}", true),
1533            row("(?iu)\\x{DF}", "\u{1e9e}", false),
1534            row("(?iu)\\Q\u{df}\\E", "\u{1e9e}", false),
1535            row("(?iu)\\Q\u{df}\u{df}\\E", "\u{1e9e}\u{1e9e}", true),
1536            row("(?iux)\u{df} \u{df}", "\u{1e9e}\u{1e9e}", true),
1537            row("(?iux)\u{df} #c\n\u{df}", "\u{1e9e}\u{1e9e}", true),
1538            row("(?iux)\u{df} #c\n", "\u{1e9e}", false),
1539            row("(?iu)\u{1e9e}", "\u{df}", true),
1540            row("(?iu)\u{1e9e}\u{1e9e}", "\u{df}\u{df}", true),
1541        ];
1542        check_matches(cases, true);
1543    }
1544
1545    /// The flag `x` ends with the group it is set in, and the whitespace and the `#` after that
1546    /// are text. Each row is `Pattern.matches` on JDK 17.
1547    #[test]
1548    fn matches_ends_flag_x_with_its_group_as_java_does() {
1549        // (pattern, input, whole input matches)
1550        let cases = [
1551            // the flag x ends with the group it is set in
1552            row("(?u)(?<n>.(?x))A(?i)I ", "&A\u{131} ", true),
1553            row("(?u)(?<n>.(?x))A(?i)I ", "&A\u{131}", false),
1554            row("(?u)(?<n>.(?x))A(?i)I", "&A\u{131}", true),
1555            row("(?u)(.(?x))A#", "xA#", true),
1556            row("(?u)(.(?x))A#", "xA", false),
1557            row("(?x)(.(?x))A#c\nB", "xAB", true),
1558            row("(?x:a )b", "ab", true),
1559            row("(?x:a )b", "a b", false),
1560            row("(?x:a)#b", "a#b", true),
1561            row("(?x:a)#b", "a", false),
1562        ];
1563        check_matches(cases, true);
1564    }
1565
1566    /// A `^` that Java reads as a class member stays one under `(?x)`, where
1567    /// the white space and comments between it and the `[` are dropped, and a
1568    /// `-` that does not join two members is the character, whatever member
1569    /// comes before it, `\w` and `\s` included. Each row is `Pattern.matches`
1570    /// on JDK 17.
1571    #[test]
1572    fn matches_reads_a_literal_caret_and_dash_in_a_class_as_java_does() {
1573        // (pattern, input, whole input matches)
1574        let cases = [
1575            // `^` is a negation only right after the `[`
1576            row("(?x)[ ^a]", "^", true),
1577            row("(?x)[ ^a]", "a", true),
1578            row("(?x)[ ^a]", "b", false),
1579            row("(?ix)[ ^a]", "^", true),
1580            row("(?ix)[ ^a]", "A", true),
1581            row("(?ix)[ ^a]", "b", false),
1582            row("(?ix)[#c\n^a]", "^", true),
1583            row("(?ix)[#c\n^a]", "A", true),
1584            row("(?ix)[#c\n^a]", "b", false),
1585            row("(?x)[ ^-b]", "^", true),
1586            row("(?x)[ ^-b]", "a", true),
1587            row("(?x)[ ^-b]", "c", false),
1588            row("(?x)[a[ ^b]]", "^", true),
1589            row("(?x)[a[ ^b]]", "b", true),
1590            row("(?x)[a[ ^b]]", "c", false),
1591            row("(?x)[^ a]", "a", false),
1592            row("(?x)[^ a]", "b", true),
1593            row("(?x)[^ ^a]", "^", false),
1594            row("(?x)[^ ^a]", "b", true),
1595            row("[\\[^a]", "^", true),
1596            row("[\\[^a]", "b", false),
1597            // outside a class it is an anchor, after a `\[` too
1598            row("\\[^a", "[a", false),
1599            row("\\[^a", "[^a", false),
1600            row("(?x)\\[ ^a", "[^a", false),
1601            // a `-` that is not a range operator, after `\w`, `\s`, a folded
1602            // letter, a property, and a range
1603            row("[\\w-.]", "-", true),
1604            row("[\\w-.]", ".", true),
1605            row("[\\w-.]", "a", true),
1606            row("[\\w-.]", ",", false),
1607            row("[\\w-a]", "-", true),
1608            row("[\\w-a]", "a", true),
1609            row("[\\w-a]", "`", false),
1610            row("[\\s-.]", "-", true),
1611            row("[\\s-.]", ".", true),
1612            row("[\\s-.]", " ", true),
1613            row("[\\s-.]", ",", false),
1614            row("(?i)[a-[b]]", "-", true),
1615            row("(?i)[a-[b]]", "A", true),
1616            row("(?i)[a-[b]]", "B", true),
1617            row("(?i)[a-[b]]", "X", false),
1618            row("(?i)[a-[b]]", "[", false),
1619            row("[\\p{Lower}-z]", "-", true),
1620            row("[\\p{Lower}-z]", ".", false),
1621            row("[\\p{L}-z]", "-", true),
1622            row("[\\p{L}-z]", "\u{e9}", true),
1623            row("[\\p{L}-z]", ".", false),
1624            row("[\\w-\\d]", "-", true),
1625            row("[\\d-a]", "-", true),
1626            row("[\\d-a]", "a", true),
1627            row("[\\d-a]", "b", false),
1628            row("[a-b-c]", "-", true),
1629            row("[a-b-c]", "c", true),
1630            row("[a-b-c]", "d", false),
1631            row("(?i)[a-b-c]", "-", true),
1632            row("(?i)[a-b-c]", "C", true),
1633            row("(?i)[a-b-c]", "D", false),
1634            row("[a-c-e]", "-", true),
1635            row("[a-c-e]", "b", true),
1636            row("[a-c-e]", "d", false),
1637            row("[a-c-e]", "e", true),
1638            // first, last and negated
1639            row("[-a]", "-", true),
1640            row("[^-a]", "-", false),
1641            row("[^-a]", "b", true),
1642            row("[a-]", "-", true),
1643            // a `-` as either end of a range
1644            row("[+--]", ",", true),
1645            row("[+--]", "-", true),
1646            row("[+--]", ".", false),
1647            row("(?i)[+--]", "-", true),
1648            row("[--/]", ".", true),
1649            row("[--/]", "0", false),
1650            row("[!--]", ",", true),
1651            row("[!--]", ".", false),
1652            // outside a class it is a plain character
1653            row("a-b", "a-b", true),
1654            row("(?i)a-b", "A-B", true),
1655        ];
1656        check_matches(cases, true);
1657    }
1658
1659    /// Under `(?x)` Java tells a `-` that ends a class from one that starts a
1660    /// range by the char right after it, without skipping white space or a
1661    /// comment: `(?x)[+- ]]` is the range from `+` to the first `]`, then the
1662    /// class ends at the second. Each row is `Pattern.matches` on JDK 17.
1663    #[test]
1664    fn matches_reads_a_range_that_ends_past_x_flag_white_space_as_java_does() {
1665        // (pattern, input, whole input matches)
1666        let cases = [
1667            row("(?x)[+- ]]", ",", true),
1668            row("(?x)[+- ]]", "a", false),
1669            row("(?x)[+- ]]", "]", true),
1670            row("(?x)[+- ]]", "-", true),
1671            row("(?x)[+- [b]]", "X]", true),
1672            row("(?x)[+- [b]]", "b]", true),
1673            row("(?x)[+- [b]]", "c]", false),
1674            row("(?x)[+- [b]]", "-]", true),
1675            row("(?x)[+-#c\n]]", "a", false),
1676            row("(?x)[+-#c\n]]", ",", true),
1677            row("(?x)[+-#c\n]]", "]", true),
1678            row("(?x)[a-b- ]", "-", true),
1679            row("(?x)[a-b- ]", "b", true),
1680            row("(?x)[a-b- ]", "c", false),
1681            row("(?x)[a - c]", "b", true),
1682            row("(?x)[a - c]", "-", false),
1683            row("(?x)[a - c]", " ", false),
1684            row("(?x)[a-\n b]", "a", true),
1685            row("(?x)[a-\n b]", "b", true),
1686            row("(?x)[a-\n b]", "-", false),
1687            row("(?x)[a-#c\nb]", "b", true),
1688            row("(?x)[a-#c\nb]", "-", false),
1689            row("(?x)[a-]", "-", true),
1690            row("(?x)[a-[b]]", "-", true),
1691            row("(?x)[a-[b]]", "b", true),
1692            row("(?x)[a-[b]]", "c", false),
1693        ];
1694        check_matches(cases, true);
1695    }
1696
1697    /// The range ends at the `]` or the `[` past the white space, which Java
1698    /// refuses as a reversed range or an unclosed class.
1699    #[yare::parameterized(
1700        range_ends_at_space = { "(?x)[.- ]" },
1701        range_ends_at_space_after_letter = { "(?x)[a- ]" },
1702        range_ends_at_newline = { "(?x)[a-\n]" },
1703        range_ends_at_newline_after_dot = { "(?x)[.-\n]" },
1704        nested_class_range_ends_at_space = { "(?x)[_- [b]]" },
1705        comment_then_range_ends_at_close = { "(?x)[a-#c\n]]" },
1706    )]
1707    fn extended_class_ranges_ending_after_whitespace_are_refused(source: &str) {
1708        check!(JavaPattern::compile(source).is_err(), "{source:?}");
1709    }
1710
1711    /// A `)` with no `(` before it is `Unmatched closing ')'` in Java, which
1712    /// the `\A(?:...)\z` wrapper would otherwise read as the end of a group,
1713    /// and a range that ends in `\w`, `\s` or `\p{..}` is `Illegal character
1714    /// range` there. Each row is refused by `Pattern.compile` on JDK 17.
1715    #[yare::parameterized(
1716        alternation_with_stray_close = { "a)|(b" },
1717        simple_stray_close = { "a)" },
1718        double_stray_close = { "(a))" },
1719        flag_group_then_stray_close = { "a(?i))" },
1720        lone_close = { ")" },
1721        extended_mode_stray_close = { "(?x)a )" },
1722        noncapturing_group_then_stray_close = { "(?:a))" },
1723        lookahead_then_stray_close = { "(?=a))" },
1724        scoped_flag_group_then_stray_close = { "(?i:a))" },
1725        reversed_character_range = { "[a--b]" },
1726        range_to_word = { "[a-\\w]" },
1727        range_to_digit = { "[a-\\d]" },
1728        range_to_digit_from_bang = { "[!-\\d]" },
1729        range_to_word_from_plus = { "[+-\\w]" },
1730        range_to_unicode_property = { "[a-\\p{L}]" },
1731        range_to_space = { "[a-\\s]" },
1732        range_to_nonword = { "[!-\\W]" },
1733        range_from_hyphen_to_word = { "[--\\w]" },
1734        case_insensitive_range_to_word = { "(?i)[a-\\w]" },
1735        extended_mode_range_to_word = { "(?x)[a- \\w]" },
1736    )]
1737    fn a_stray_closing_paren_or_a_range_to_a_shorthand_does_not_compile(source: &str) {
1738        check!(JavaPattern::compile(source).is_err(), "{source:?}");
1739    }
1740
1741    /// A `)` that Java reads as a member, or as part of a comment, is not
1742    /// stray. Each row is `Pattern.matches` on JDK 17.
1743    #[yare::parameterized(
1744        close_inside_class = { "[)]", ")", true },
1745        escaped_close = { "\\)", ")", true },
1746        quoted_close = { "\\Q)\\E", ")", true },
1747        close_inside_comment = { "(?x)a #c)\n", "a", true },
1748        close_in_alternative = { "(a)|(b)", "b", true },
1749        close_in_case_insensitive_alternative = { "(?i)(a)|b", "B", true },
1750    )]
1751    fn a_closing_paren_that_closes_no_group_is_still_a_character_where_java_reads_one(
1752        source: &str,
1753        input: &str,
1754        expected: bool,
1755    ) {
1756        assert_pattern_matches(source, input, expected);
1757    }
1758
1759    /// A backreference under a case flag compares the text of its group as
1760    /// Java's `CIBackRef` does: under `(?i)` by `ASCII.toLower`, so a
1761    /// non-ASCII letter does not match its other case, and under `(?iu)` by
1762    /// `Character.toLowerCase` of `Character.toUpperCase`, so `i` matches `Δ°`
1763    /// and `Δ±`, and `s` matches `ΕΏ`, whatever their length in bytes. Each row is
1764    /// `Pattern.matches` on JDK 25.0.4.
1765    #[test]
1766    fn a_backreference_under_a_case_flag_compares_as_java_does() {
1767        // (pattern, input, whole input matches)
1768        let cases = [
1769            // (?i) folds ASCII letters only
1770            row("(?i)(\u{e9})\\1", "\u{e9}\u{c9}", false),
1771            row("(?i)(\u{e9})\\1", "\u{e9}\u{e9}", true),
1772            row("(?i)(\u{3c3})\\1", "\u{3c3}\u{3c2}", false),
1773            row("(?i)(a\u{e9})\\1", "a\u{e9}A\u{e9}", true),
1774            row("(?i)(a\u{e9})\\1", "A\u{e9}a\u{c9}", false),
1775            row("(?i)(k)\\1", "k\u{212a}", false),
1776            row("(?i)(s)\\1", "s\u{17f}", false),
1777            row("(?i)(i)\\1", "i\u{130}", false),
1778            row("(?i)(ab)\\1", "abAB", true),
1779            row("(?i)(\\w+)-\\1", "Ab-aB", true),
1780            row("(?i)(?<n>\u{e9})\\k<n>", "\u{e9}\u{c9}", false),
1781            // (?iu) compares Character.toLowerCase(Character.toUpperCase(ch))
1782            row("(?iu)(i)\\1", "i\u{130}", true),
1783            row("(?iu)(i)\\1", "i\u{131}", true),
1784            row("(?iu)(\u{130})\\1", "\u{130}i", true),
1785            row("(?iu)(\u{130})\\1", "\u{130}I", true),
1786            row("(?iu)(\u{131})\\1", "\u{131}I", true),
1787            row("(?iu)(\u{130})\\1", "\u{130}\u{131}", true),
1788            row("(?iu)(s)\\1", "s\u{17f}", true),
1789            row("(?iu)(S)\\1", "S\u{17f}", true),
1790            row("(?iu)(k)\\1", "k\u{212a}", true),
1791            row("(?iu)(\u{df})\\1", "\u{df}\u{1e9e}", true),
1792            row("(?iu)(\u{e5})\\1", "\u{e5}\u{212b}", true),
1793            row("(?iu)(a)\\1", "aA", true),
1794            row("(?iu)(a)\\1", "ab", false),
1795            row("(?iu)(\u{e9})\\1", "\u{e9}\u{c9}", true),
1796            row("(?iu)(\u{3c3})\\1", "\u{3c3}\u{3c2}", true),
1797            row("(?iu)(\u{3c3})\\1", "\u{3c3}\u{3a3}", true),
1798            row("(?iu)(\u{1c6})\\1", "\u{1c6}\u{1c5}", true),
1799            row("(?iu)(\u{3b8})\\1", "\u{3b8}\u{3d1}", true),
1800            row("(?iu)(\u{3c9})\\1", "\u{3c9}\u{2126}", true),
1801            row("(?iu)(\u{b5})\\1", "\u{b5}\u{3bc}", true),
1802            row("(?iu)(\\p{L}+)\\1", "\u{e9}\u{c9}", true),
1803            row("(?iu)(?<n>i)\\k<n>", "i\u{130}", true),
1804            row("(?iU)(s)\\1", "s\u{17f}", true),
1805            // one char for one char: no full case mapping
1806            row("(?iu)(ss)\\1", "ss\u{df}", false),
1807            row("(?iu)(\u{df})\\1", "\u{df}ss", false),
1808            row("(?iu)(\\w+)\\1", "sS", true),
1809            row("(?iu)(.+)\\1", "stra\u{df}eSTRA\u{1e9e}E", true),
1810            // the flags in force at the reference decide, not those at the group
1811            row("(?u)(?i:(s))\\1", "s\u{17f}", false),
1812            row("(?i)(?u:(s)\\1)", "s\u{17f}", true),
1813            row("(?iu)(s)(?-u)\\1", "s\u{17f}", false),
1814            row("(?iu)(s)(?-i)\\1", "s\u{17f}", false),
1815            // one code point for one outside the BMP too, where JDK 17 counted
1816            // UTF-16 units and threw past the input
1817            row("(?i)(.)\\1", "\u{10400}\u{10428}", false),
1818            row("(?iu)(.)\\1", "\u{10400}\u{10428}", true),
1819            row("(?iu)(.)\\1", "\u{10400}\u{10400}", true),
1820            row("(?iu)(.)\\1x", "\u{10400}\u{10428}x", true),
1821            row("(?iu)(.)\\1.", "\u{10400}\u{10400}\u{10400}", true),
1822            row("(?iu)(.)\\1.", "\u{10400}\u{10400}b", true),
1823            row("(?iu)(.)(.)\\1", "\u{10400}\u{10400}\u{10400}", true),
1824            row("(?iu)(.)\\1\\1", "\u{10400}\u{10400}\u{10400}", true),
1825            row("(?iu)(.)\\1", "\u{10400}a", false),
1826            row("(?iu)(a)\\1", "a\u{10400}", false),
1827            row("(?iu)(.\u{10428})\\1", "a\u{10400}A\u{10400}", true),
1828        ];
1829        check_matches(cases, true);
1830    }
1831
1832    /// `replaceAll` and `replaceFirst` find the matches of a pattern with a
1833    /// backreference under a case flag as Java does too.
1834    #[test]
1835    fn replace_compares_a_backreference_under_a_case_flag_as_java_does() {
1836        // (pattern, input, replacement, replaceAll result)
1837        let cases = [
1838            replace_row(
1839                "(?i)(\\w)\\1",
1840                "aAbB\u{e9}\u{c9}",
1841                "<$1>",
1842                "<a><b>\u{e9}\u{c9}",
1843            ),
1844            replace_row("(?iu)(\\w)\\1", "sS", "<$1>", "<s>"),
1845            replace_row("(?iu)(.)\\1", "i\u{130}s\u{17f}", "<$1>", "<i><s>"),
1846            replace_row(
1847                "(?iu)CN=(\\w+),OU=\\1",
1848                "CN=kim,OU=KIM",
1849                "$0",
1850                "CN=kim,OU=KIM",
1851            ),
1852            replace_row("(?iu)CN=(.+),OU=\\1", "CN=\u{131},OU=I", "$1", "\u{131}"),
1853            replace_row(
1854                "(?i)CN=(.+),OU=\\1",
1855                "CN=\u{131},OU=I",
1856                "$1",
1857                "CN=\u{131},OU=I",
1858            ),
1859            replace_row("(?i)\\G(a)\\1", "aAaAbaA", "x", "xxbaA"),
1860        ];
1861        check_replacements(cases, true, true);
1862    }
1863
1864    /// The matcher for a backreference under a case flag reads the rest of the
1865    /// pattern as `fancy_regex` does: look-around, atomic groups, possessive
1866    /// and lazy repeats, alternation and anchors. Each row is
1867    /// `Pattern.matches` on JDK 17.0.20.
1868    #[test]
1869    fn a_pattern_with_a_folded_backreference_reads_the_rest_as_java_does() {
1870        // (pattern, input, whole input matches)
1871        let cases = [
1872            row("(?i)(a|ab)(c|bcd)\\1(d*)", "abcdAd", true),
1873            row("(?i)(a|ab)(c|bcd)\\1(d*)", "abcdAbd", false),
1874            row("(?i)(a+?)\\1", "aaAA", true),
1875            row("(?i)(a+)a\\1", "aaA", true),
1876            row("(?i)(a++)a\\1", "aaA", false),
1877            row("(?i)(?>(a+))a\\1", "aaA", false),
1878            row("(?i)(a)(?=b)\\w\\1", "abA", true),
1879            row("(?i)(a)(?!b)\\w\\1", "abA", false),
1880            row("(?i)(a)\\w(?<=b)\\1", "abA", true),
1881            row("(?i)(a)\\w(?<!b)\\1", "abA", false),
1882            row("(?i)(a){2,3}\\1", "aaA", true),
1883            row("(?i)(a){2,3}\\1", "aA", false),
1884            row("(?i)(a){2,3}\\1", "aaaaA", false),
1885            row("(?i)(a|)*\\1", "aa", true),
1886            // an empty iteration counts toward the minimum of a repeat
1887            row("(?i)((?=())|(?=())){2}\\2\\3", "", true),
1888            row("(?i)((?=())|(?=())){2}\\2\\3", "x", false),
1889            row("(?i)(a?){3}\\1", "aA", true),
1890            row("(?i)(a|()){2}\\2b", "ab", true),
1891            row("(?i)(?:(a)|b?){2}\\1", "bA", false),
1892            row("(?i)(x)?\\1", "", false),
1893            row("(?i)^(a)\\1$", "aA", true),
1894            row("(?i)(a)\\1\\z", "aA", true),
1895            row("(?i)(?m)(a)$\\n^\\1", "a\nA", true),
1896            row("(?i)(?s)(.)\\1", "\n\n", true),
1897            row("(?i)(.)\\1", "\n\n", false),
1898            row("(?iU)(\\w+)\\b\\1", "ab", false),
1899            row("(?iU)(\\w)\\B\\1", "aA", true),
1900            row("(?i)\\G(a)\\1", "aA", true),
1901            row("(?i)(a)\\R\\1", "a\r\nA", true),
1902            row("(?i)(a)\\R\\1", "a\rA", true),
1903            row("(?i)(a)\\R\\1", "a\n\nA", false),
1904            row("(?i)(a)\\R\\1", "a\u{2028}A", true),
1905        ];
1906        check_matches(cases, true);
1907    }
1908
1909    /// The matcher keeps its alternatives on a stack of its own, so a long
1910    /// input does not overflow the thread's, and it gives up at the backtrack
1911    /// limit as `fancy_regex` does.
1912    #[test]
1913    fn a_pattern_with_a_folded_backreference_takes_a_long_input_and_gives_up_at_the_limit() {
1914        let long = format!("{}A", "a".repeat(200_000));
1915        check!(pattern("(?i)(a)(a*)\\1").matches(&long).ok() == Some(true));
1916        check!(pattern("(?i)(a*)*b\\1").matches(&"a".repeat(64)).is_err());
1917    }
1918
1919    /// A group that matched inside a look-around, an atomic group or a
1920    /// possessive quantifier keeps its text when the match backtracks out of
1921    /// it, as Java's does, and so does one inside a negated look-around whose
1922    /// condition matched. A group outside them does not. Java sets a group
1923    /// when it ends, so a reference inside it sees its text from before. Each
1924    /// row is `Pattern.matches` on JDK 17.0.20.
1925    #[test]
1926    fn a_group_keeps_its_text_where_java_keeps_it() {
1927        // (pattern, input, whole input matches)
1928        let cases = [
1929            row("(?:(?=(a))ax|a)\\1", "aa", true),
1930            row("(?:(?!(a))|a)\\1", "aa", true),
1931            row("a(?:(?<=(a))x|)\\1", "aa", true),
1932            row("a(?:(?<!(a))|)\\1", "aa", true),
1933            row("(?:(?>(a))x|a)\\1", "aa", true),
1934            row("(?:(a)++x|a)\\1", "aa", true),
1935            row("(?:(a)?+x|a)\\1", "aa", true),
1936            row("(?:(?:(a))*+x|a)\\1", "aa", true),
1937            row("(?:(a){1,2}+x|a)\\1", "aa", true),
1938            row("(?:(a)x|a)\\1", "aa", false),
1939            row("(?:(?=(a))ax|a)\\1", "a", false),
1940            row("((?=())|(?=()))*\\2\\3", "", true),
1941            row("((?=())|(?=())){1,2}\\2\\3", "", true),
1942            row("(a|b\\1)+", "aba", true),
1943            row("(a|b\\1)+", "abb", false),
1944            row("(?i)(a|b\\1)+", "abA", true),
1945            row("(a\\1?)+", "aaa", true),
1946            row("(a\\1?){2}", "aaa", true),
1947        ];
1948        check_matches(cases, true);
1949    }
1950
1951    /// `replaceAll` sees a group that kept its text, and `Matcher.find` clears
1952    /// the groups once for all the start positions it tries, so a group that
1953    /// kept its text at one start is still set at the next. Each row is
1954    /// `String.replaceAll` on JDK 17.0.20.
1955    #[test]
1956    fn replace_sees_a_group_that_kept_its_text_as_java_does() {
1957        // (pattern, input, replacement, replaceAll result)
1958        let cases = [
1959            replace_row("(?:(?=(b))bx|b)", "b", "[$1]", "[b]"),
1960            replace_row("(?:(?=(b))bx|b)", "ab", "[$1]", "a[b]"),
1961            replace_row("(?=(a)c)|b", "ab", "[$1]", "a[]"),
1962            replace_row("(?:(?=(a))ax|b)", "ab", "[$1]", "a[a]"),
1963            replace_row("(?:(?!(a))|b)", "ab", "[$1]", "a[a]b[]"),
1964            replace_row("\\R", "a\r\nb", "|", "a|b"),
1965            replace_row("\\R\\n", "a\r\nb", "|", "a|b"),
1966        ];
1967        check_replacements(cases, true, true);
1968    }
1969
1970    /// `\R` is `\r\n` or one line terminator, and gives back the `\n` of
1971    /// `\r\n` when the rest of the pattern needs it, as Java's `LineEnding`
1972    /// does. Under a quantifier of `\R`, or of a group with no alternation and
1973    /// no range of counts, Java takes the first way only, but a `?` of a group
1974    /// such as `(?:\R)` backtracks into it. Each row is
1975    /// `Pattern.matches` on JDK 17.0.20.
1976    #[test]
1977    fn a_linebreak_gives_back_its_newline_where_java_does() {
1978        // (pattern, input, whole input matches)
1979        let cases = [
1980            row("(a)\\R\\n\\1", "a\r\na", true),
1981            row("\\R", "\r", true),
1982            row("\\R\\R", "\r\n", true),
1983            row("\\R", "\r\n", true),
1984            row("\\R", "\n\r", false),
1985            row("\\R", "\u{2028}", true),
1986            row("\\R", "\u{85}", true),
1987            row("(?-u)\\R", "\u{2028}", true),
1988            row("\\R\\n", "\r\n", true),
1989            row("(?>\\R)\\n", "\r\n", false),
1990            row("\\R{2}", "\r\n", false),
1991            row("\\R?\\n", "\r\n", false),
1992            row("x\\R+\\n", "x\r\n\r\n", false),
1993            row("(\\R)?\\n", "\r\n", true),
1994            row("(?:a\\R){2}\\n", "a\r\na\r\n", false),
1995            row("(?:a\\R|b){2}\\n", "a\r\na\r\n", true),
1996            row("(?=\\R\\n)\\R", "\r\n", true),
1997            row("(?:\\R)?\\n", "\r\n", true),
1998            row("(?:\\R)??\\n", "\r\n", true),
1999            row("(?:\\R){0,1}\\n", "\r\n", true),
2000            row("(?:(?:\\R))?\\n", "\r\n", true),
2001            row("(?x)(?:\\R) ?\\n", "\r\n", true),
2002            row("(?x)\\R ?\\n", "\r\n", false),
2003            row("(?x)\\R #c\n?\\n", "\r\n", false),
2004            row("(?:\\R)?+\\n", "\r\n", false),
2005            row("(?:\\R)*\\n", "\r\n", false),
2006            row("(?:\\R)+\\n", "\r\n", false),
2007            row("(?:\\R?)?\\n", "\r\n", false),
2008            row("a(?:\\R)?\\n\\R?", "a\r\n\r\n", true),
2009        ];
2010        check_matches(cases, true);
2011    }
2012
2013    /// `$` and `\Z` match at the end, or before a line terminator that ends
2014    /// the input, and under `(?m)` `$` matches before any line terminator and
2015    /// `^` after one, as Java's `Dollar` and `Caret` do: `\r\n` is one line
2016    /// terminator, as are `\r`, U+0085, U+2028 and U+2029, and under `(?d)`
2017    /// only `\n` is. `(?m)^` never matches at the end of the input. A
2018    /// quantified anchor is the anchor, or nothing where the quantifier may
2019    /// match zero times. Each row is `Pattern.matches` on JDK 17.0.20.
2020    #[test]
2021    fn anchors_match_at_line_terminators_as_java_does() {
2022        // (pattern, input, whole input matches)
2023        let cases = [
2024            row("a$\\n", "a\n", true),
2025            row("a$\\r\\n", "a\r\n", true),
2026            row("a\\r$\\n", "a\r\n", false),
2027            row("a$\u{2028}", "a\u{2028}", true),
2028            row("a$\\u0085", "a\u{85}", true),
2029            row("a$\\r", "a\r", true),
2030            row("a$\\nb", "a\nb", false),
2031            row("a\\Z\\n", "a\n", true),
2032            row("a\\Z\\n\\n", "a\n\n", false),
2033            row("a$\\n\\n", "a\n\n", false),
2034            row("(?d)a$\\r", "a\r", false),
2035            row("(?d)a$\\n", "a\n", true),
2036            row("(?d)a\\Z\\n", "a\n", true),
2037            row("(?m)a$\\r\\nb", "a\r\nb", true),
2038            row("(?m)a\\r$\\nb", "a\r\nb", false),
2039            row("(?m)a$\u{2028}b", "a\u{2028}b", true),
2040            row("(?m)a$\\rb", "a\rb", true),
2041            row("(?md)a$\\rb", "a\rb", false),
2042            row("(?md)a$\\nb", "a\nb", true),
2043            row("(?m)a\\n^b", "a\nb", true),
2044            row("(?m)a\\r^b", "a\rb", true),
2045            row("(?m)a\\r^\\nb", "a\r\nb", false),
2046            row("(?m)a\\n^", "a\n", false),
2047            row("(?m)a\\u0085^b", "a\u{85}b", true),
2048            row("(?md)a\\r^b", "a\rb", false),
2049            row("(?md)a\\n^b", "a\nb", true),
2050            row("(?m)^", "", false),
2051            row("^", "", true),
2052            row("$", "", true),
2053            row("(?m)$", "", true),
2054            row("a$?b", "ab", true),
2055            row("(?m:a$)\\n^b", "a\nb", false),
2056            row("(?m:x)a$\\nb", "a\nb", false),
2057            row("a$*b", "ab", true),
2058            row("a$+b", "ab", false),
2059            row("a$+", "a", true),
2060            row("a\\Z?b", "ab", true),
2061            row("(?m)a\\n^?b", "a\nb", true),
2062            row("(?m)a^+b", "ab", false),
2063            row("(?m)a\\n^+b", "a\nb", true),
2064            row("(?x)a$ ?b", "ab", true),
2065        ];
2066        check_matches(cases, true);
2067    }
2068
2069    /// `replaceAll` finds the anchors where Java finds them. Each row is
2070    /// `String.replaceAll` on JDK 17.0.20.
2071    #[test]
2072    fn replace_finds_anchors_at_line_terminators_as_java_does() {
2073        // (pattern, input, replacement, replaceAll result)
2074        let cases = [
2075            replace_row("a$", "a\n", "X", "X\n"),
2076            replace_row("$", "a\n", "|", "a|\n|"),
2077            replace_row("(?m)$", "a\r\nb", "|", "a|\r\nb|"),
2078            replace_row("(?m)^", "a\r\nb", "|", "|a\r\n|b"),
2079            replace_row("(?m)^", "a\n", "|", "|a\n"),
2080            replace_row("\\Z", "a\r\n", "|", "a|\r\n|"),
2081            replace_row("\\h+", "a\u{a0}\u{3000}b", "_", "a_b"),
2082        ];
2083        check_replacements(cases, true, true);
2084    }
2085
2086    /// `\h` and `\v` are Java's horizontal and vertical whitespace, and `\H`
2087    /// and `\V` their negations, with or without `(?U)`. In a class `\v` is
2088    /// the char `\x0B` where a `-` follows it, as in Java. Each row is
2089    /// `Pattern.matches` on JDK 17.0.20.
2090    #[test]
2091    fn horizontal_and_vertical_whitespace_are_javas() {
2092        // (pattern, input, whole input matches)
2093        let cases = [
2094            row("\\h", "\u{a0}", true),
2095            row("\\h", "\u{3000}", true),
2096            row("\\h", "\u{2001}", true),
2097            row("\\h", "\n", false),
2098            row("\\H", "a", true),
2099            row("\\H", "\t", false),
2100            row("[\\h]", "\u{2000}", true),
2101            row("[\\H]", "\u{2000}", false),
2102            row("[\\h-a]", "-", true),
2103            row("(?U)\\h", "\u{a0}", true),
2104            row("\\v", "\n", true),
2105            row("\\v", "\u{2028}", true),
2106            row("\\v", "\u{c}", true),
2107            row("\\V", "\n", false),
2108            row("\\V", "a", true),
2109            row("[\\v]", "\r", true),
2110            row("[\\v-\\x0d]", "\u{c}", true),
2111            row("[\\v-]", "-", true),
2112            row("[\\v-]", "\n", false),
2113            row("[^\\h]", "a", true),
2114        ];
2115        check_matches(cases, true);
2116    }
2117
2118    /// Java reads `(?i)*` as a dangling quantifier: inline flags are no node,
2119    /// so nothing precedes the `*`.
2120    #[yare::parameterized(
2121        star_after_unicode_flags = { "(?iu)*" },
2122        plus_after_case_flag = { "(?i)+a" },
2123        optional_after_dotall_flag = { "(?d)?" },
2124        star_after_extended_flags = { "(?ix) *" },
2125    )]
2126    fn a_star_plus_or_question_mark_after_inline_flags_is_refused(source: &str) {
2127        check!(JavaPattern::compile(source).is_err(), "{source:?}");
2128    }
2129
2130    /// `\N{name}` is a character in Java, which `fancy_regex` reads as `\N` and
2131    /// the text of the name, so a rule that has one is refused and not misread.
2132    #[yare::parameterized(
2133        named_character = { "\\N{LATIN SMALL LETTER A}" },
2134        case_insensitive_named_character = { "(?i)\\N{LATIN SMALL LETTER A}" },
2135    )]
2136    fn a_named_character_is_refused(source: &str) {
2137        check!(JavaPattern::compile(source).is_err(), "{source:?}");
2138    }
2139
2140    /// `Matcher.appendReplacement` semantics for `replaceAll`.
2141    #[test]
2142    fn replace_all_expands_a_replacement_as_java_does() {
2143        // (pattern, input, replacement, result)
2144        let cases = [
2145            // `$1_$2` is group 1, an underscore and group 2, not a group named `1_`.
2146            replace_row("(a)-(b)", "a-b", "$1_$2", "a_b"),
2147            replace_row("(a)", "a", "$1suffix", "asuffix"),
2148            // Digits after `$n` join the group number only while it names a group.
2149            replace_row("(a)", "a", "$10", "a0"),
2150            replace_row("(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)", "abcdefghij", "$10", "j"),
2151            replace_row("(a)", "a", "$0!", "a!"),
2152            replace_row("(?<who>a)", "a", "${who}-$1", "a-a"),
2153            // A backslash makes the next character literal.
2154            replace_row("(a)", "a", "\\$1", "$1"),
2155            replace_row("a", "aa", "b", "bb"),
2156            // A group that took no part in the match adds nothing.
2157            replace_row("(a)|(b)", "b", "[$1]", "[]"),
2158            replace_row("x", "abc", "$9", "abc"),
2159        ];
2160        check_replacements(cases, true, true);
2161    }
2162
2163    /// The replacements Java throws on.
2164    #[yare::parameterized(
2165        group_number_out_of_range = { "(a)", "a", "$2" },
2166        group_number_without_groups = { "a", "a", "$1" },
2167        trailing_dollar = { "(a)", "a", "$" },
2168        nonnumeric_group_reference = { "(a)", "a", "$x" },
2169        trailing_backslash = { "(a)", "a", "\\" },
2170        unknown_named_group = { "(a)", "a", "${who}" },
2171        empty_named_group = { "(a)", "a", "${}" },
2172        unclosed_named_group = { "(a)", "a", "${who" },
2173        group_number_on_the_matcher_alone = { "(?<=\\R?x)y", "xy", "$1" },
2174        group_number_past_a_large_repeat = { "(?<n>x){1,1000000000}", "x", "$2" },
2175        unknown_named_group_past_a_large_repeat = { "(?<n>x){1,1000000000}", "x", "${m}" },
2176    )]
2177    fn replace_all_refuses_what_java_throws_on(source: &str, input: &str, replacement: &str) {
2178        check!(
2179            pattern(source).replace(input, replacement, true).is_err(),
2180            "{source} on {input} with {replacement}"
2181        );
2182    }
2183
2184    #[test]
2185    fn replace_first_stops_after_one_match() {
2186        let result = pattern("a").replace("aaa", "b", false);
2187        assert!(result.ok().as_deref() == Some("baa"));
2188    }
2189
2190    #[yare::parameterized(
2191        python_named_group = { "(?P<x>a)" },
2192        unclosed_character_class = { "[a-" },
2193        unclosed_group = { "(a" },
2194    )]
2195    fn a_pattern_java_refuses_does_not_compile(source: &str) {
2196        check!(JavaPattern::compile(source).is_err(), "{source}");
2197    }
2198
2199    /// What `fancy_regex` takes and Java's `Pattern.compile` refuses is refused:
2200    /// a group name already defined, a `\k<name>` of no named group before it,
2201    /// a quantifier after a quantifier and its `?` or `+`, a repeat range that
2202    /// is backwards or past `i32::MAX`, a boundary, anchor, `\R`, `\X` or
2203    /// reference in a class, a look-behind with no obvious maximum length, and
2204    /// a property name `Pattern.family` does not take. Each row is refused by
2205    /// `Pattern.compile` on JDK 25.0.4.
2206    #[test]
2207    fn a_pattern_java_refuses_where_fancy_regex_takes_it_does_not_compile() {
2208        let sources = [
2209            "(?<a>x)(?<a>y)",
2210            "(?<a>x)|(?<a>y)",
2211            "\\k<1>",
2212            "(x)\\k<1>",
2213            "\\k<a>(?<a>x)",
2214            "(?<a>x)\\k<b>",
2215            "(?<a>x)\\k<a",
2216            "(?<a>x)\\k<>",
2217            "(?<a>x)\\k",
2218            "(?<_a>x)",
2219            "(?<a_b>x)",
2220            "a*?+",
2221            "a**",
2222            "a*+*",
2223            "a*??",
2224            "a+?+",
2225            "a?*",
2226            "a??+",
2227            "a{2}++",
2228            "a{2}*",
2229            "a{2}?+",
2230            "(?x)a*? +",
2231            "(?x)a* * ",
2232            "a*(?i)+",
2233            "*a",
2234            "(*a)",
2235            "a|*",
2236            "x{2,1}",
2237            "x{3,2}",
2238            "x{,2}",
2239            "x{2147483648}",
2240            "x{1,2147483648}",
2241            "[\\R]",
2242            "[\\X]",
2243            "[\\b]",
2244            "[\\B]",
2245            "[\\A]",
2246            "[\\G]",
2247            "[\\z]",
2248            "[\\Z]",
2249            "[\\k<a>]",
2250            "[\\1]",
2251            "(a)(?<=\\1)",
2252            "(?<=(a)\\1)b",
2253            "(?<=(?:ab)*)c",
2254            "(?<=(ab)*)c",
2255            "(?<!(a)\\1)b",
2256            "\\p{IsHyphen}",
2257            "\\p{Greek}",
2258            "\\p{lu}",
2259            "\\p{LU}",
2260            "\\p{isLu}",
2261            "\\p{Islu}",
2262            "\\p{IsOldItalic}",
2263            "\\p{IsOld Italic}",
2264            "\\p{sc=OldItalic}",
2265            "\\p{gc=lu}",
2266            "\\p{gc=Uppercase_Letter}",
2267            "\\p{Uppercase_Letter}",
2268            "\\p{Letter}",
2269            "\\p{Alphabetic}",
2270            "\\p{IsWhite Space}",
2271            "\\p{White_Space}",
2272            "\\p{Emoji}",
2273            "\\p{Any}",
2274            "\\p{IsAny}",
2275            "\\p{Ascii}",
2276            "\\p{alpha}",
2277            "\\p{Hex_Digit}",
2278            "\\p{Ideographic}",
2279            "\\p{Han}",
2280            "\\p{Common}",
2281            "\\p{Decimal_Number}",
2282            "\\p{Math}",
2283            "\\p{IsMath}",
2284            "\\p{Dash}",
2285            "\\p{IsDash}",
2286            "\\p{Cased}",
2287            "\\p{IsCased}",
2288            "\\pl",
2289            "\\pX",
2290            "\\p{L&}",
2291            "\\p{IsL&}",
2292            "(?i)\\p{lu}",
2293            "(?<=a{0,1073741824}b{0,1073741824})c",
2294            "(?<=(?:ab){0,1073741824})c",
2295            "(?<=(?:a|b){1,2})c",
2296            "(?<=(a|b)*)c",
2297            "(?<=\\R*)c",
2298            "(?<=\\R{0,1073741824})c",
2299            "(?<=(?>ab)*)c",
2300        ];
2301        for source in sources {
2302            check!(JavaPattern::compile(source).is_err(), "{source:?}");
2303        }
2304    }
2305
2306    /// The patterns next to those Java refuses, which it takes, still compile
2307    /// and match as Java matches them: a second group of another name, a
2308    /// reference to a group opened before it, one `?` or `+` after a
2309    /// quantifier, a zero-width node under a quantifier, `\h`, `\v` and a
2310    /// property in a class, a look-behind whose maximum Java's study finds,
2311    /// and the property names `Pattern.family` takes in each spelling it
2312    /// takes. Each row is `Pattern.matches` on JDK 25.0.4.
2313    #[test]
2314    fn a_pattern_next_to_one_java_refuses_compiles_and_matches_as_java_does() {
2315        // (pattern, input, whole input matches)
2316        let cases = [
2317            row("(?<a>x)(?<b>y)", "xy", true),
2318            row("(?<ab>x)(?<a>y)", "xy", true),
2319            row("(?<a1>x)\\k<a1>", "xx", true),
2320            row("(?<a>\\k<a>x)", "x", false),
2321            row("(?<a>x)\\k<a>?", "x", true),
2322            row("a{2}+", "aa", true),
2323            row("a{2}{3}", "aaaaaa", false),
2324            row("(?x)a* +", "aa", true),
2325            row("^*", "", true),
2326            row("$*", "", true),
2327            row("\\b*", "", true),
2328            row("x{1,1}", "x", true),
2329            row("x{0,0}", "", true),
2330            row("[\\h]", " ", true),
2331            row("[\\v]", "\u{b}", true),
2332            row("[\\p{L}]", "a", true),
2333            row("(?<=a*)b", "b", true),
2334            row("(?<=a+)b", "ab", false),
2335            row("(?<=a{2,})b", "aab", false),
2336            row("(?<=a{0,1000})b", "b", true),
2337            row("(?<=.*)b", "b", true),
2338            row("(?<=a|bc)d", "bcd", false),
2339            row("(?<=(?:ab){1,2})c", "abc", false),
2340            row("(?<=\\b)a", "a", true),
2341            row("(?<=(?=a*)a)b", "ab", false),
2342            row("(?<=(?!a*)x)b", "xb", false),
2343            row("(?<!a*)b", "b", false),
2344            row("(?<=a*?)b", "b", true),
2345            row("(?<=a?)b", "b", true),
2346            row("(?<=\\w+)b", "ab", false),
2347            row("(?<=\\Ga*)b", "b", true),
2348            row("\\p{Lu}", "A", true),
2349            row("\\p{IsLu}", "A", true),
2350            row("\\p{IsLatin}", "a", true),
2351            row("\\p{Islatin}", "a", true),
2352            row("\\p{IsLATIN}", "a", true),
2353            row("\\p{IsLatn}", "a", true),
2354            row("\\p{Isold_italic}", "a", false),
2355            row("\\p{IsOld_Italic}", "\u{10300}", true),
2356            row("\\p{sc=Latin}", "a", true),
2357            row("\\p{SC=Latin}", "a", true),
2358            row("\\p{sc=latin}", "a", true),
2359            row("\\p{sc=Latn}", "a", true),
2360            row("\\p{Script=Latin}", "a", true),
2361            row("\\p{gc=Lu}", "A", true),
2362            row("\\p{general_category=Lu}", "A", true),
2363            row("\\p{gc=L}", "A", true),
2364            row("\\p{gc=LC}", "A", true),
2365            row("\\p{L}", "A", true),
2366            row("\\p{IsL}", "A", true),
2367            row("\\p{IsLetter}", "A", true),
2368            row("\\p{Isletter}", "A", true),
2369            row("\\p{IsAlphabetic}", "A", true),
2370            row("\\p{IsWhite_Space}", " ", true),
2371            row("\\p{IsWhiteSpace}", " ", true),
2372            row("\\p{IsEmoji}", "\u{1f600}", true),
2373            row("\\p{IsAssigned}", "a", true),
2374            row("\\p{ASCII}", "a", true),
2375            row("\\p{IsASCII}", "a", true),
2376            row("\\p{Alpha}", "a", true),
2377            row("(?U)\\p{alpha}", "a", true),
2378            row("(?U)\\p{ALPHA}", "a", true),
2379            row("\\p{IsAlpha}", "a", true),
2380            row("\\p{Isalpha}", "a", true),
2381            row("\\p{IsDigit}", "1", true),
2382            row("\\p{IsPunct}", "!", true),
2383            row("\\p{IsSpace}", " ", true),
2384            row("\\p{IsCntrl}", "\u{1}", true),
2385            row("\\p{IsLower}", "a", true),
2386            row("\\p{IsUpper}", "A", true),
2387            row("\\p{XDigit}", "F", true),
2388            row("\\p{IsHex_Digit}", "F", true),
2389            row("\\p{IsHexDigit}", "F", true),
2390            row("\\p{IsIdeographic}", "\u{4e00}", true),
2391            row("\\p{IsHan}", "\u{4e00}", true),
2392            row("\\p{IsHani}", "\u{4e00}", true),
2393            row("\\p{IsCommon}", "!", true),
2394            row("\\p{IsZyyy}", "!", true),
2395            row("\\p{IsInherited}", "\u{301}", true),
2396            row("\\p{Nd}", "1", true),
2397            row("\\p{IsNd}", "1", true),
2398            row("\\p{N}", "1", true),
2399            row("\\pL", "a", true),
2400            row("\\pN", "1", true),
2401            row("\\pZ", " ", true),
2402            row("\\pS", "+", true),
2403            row("\\pC", "\u{1}", true),
2404            row("\\pM", "\u{301}", true),
2405            row("\\pP", "!", true),
2406            row("\\p{Ll}", "a", true),
2407            row("\\p{Lo}", "\u{4e00}", true),
2408            row("\\p{Cn}", "\u{378}", true),
2409            row("\\p{Co}", "\u{e000}", true),
2410            row("\\p{Cs}", "a", false),
2411            row("\\p{Lm}", "\u{2b0}", true),
2412            row("(?<=ab*)c", "ac", false),
2413            row("(?<=a*b)c", "bc", false),
2414            row("(?<=(?:a){1,2})c", "ac", false),
2415            row("(?<=(?:a)*)c", "c", true),
2416            row("(?<=(a)*)c", "c", true),
2417            row("(?<=a|b*)c", "c", true),
2418            row("(?<=(?=b*)a*)c", "c", true),
2419            row("(?<=(?<=a*)b*)c", "c", true),
2420            row("(?<=[ab]*)c", "c", true),
2421            row("(?<=\\x{1F600}*)c", "c", true),
2422            row("(?<=(?i)a*)c", "c", true),
2423            row("(?<=(?iu)\\u00df*)c", "c", true),
2424            row("(?<=a*?b)c", "bc", false),
2425            row("(?<=a??b*)c", "c", true),
2426            row("(?<=a?b?)c", "c", true),
2427            row("(?<=(ab)?)c", "c", true),
2428        ];
2429        check_matches(cases, true);
2430    }
2431
2432    /// A class with `&&` is the set Java's `Pattern.clazz` builds: an empty
2433    /// side of `&&` takes the operand before it, or leaves the side after it,
2434    /// `&&` binds to the right, and the single chars below U+0100 of a class
2435    /// are one set that a char after the intersection still joins, so
2436    /// `[a&&&b]` matches `a`, `&` and `b`. Each row is `Pattern.matches` on
2437    /// JDK 25.0.4.
2438    #[test]
2439    fn a_class_intersection_with_an_empty_side_is_the_set_java_builds() {
2440        // (pattern, input, whole input matches)
2441        let cases = [
2442            row("[a-z&&]", "a", true),
2443            row("[a-z&&]", "A", false),
2444            row("[&&a]", "a", true),
2445            row("[&&a]", "b", false),
2446            row("[a&&&b]", "a", true),
2447            row("[a&&&b]", "b", true),
2448            row("[a&&&b]", "&", true),
2449            row("[a&&]", "a", true),
2450            row("[a&&]", "b", false),
2451            row("[^a&&]", "a", false),
2452            row("[^a&&]", "b", true),
2453            row("[^&&a]", "b", true),
2454            row("[a&&&&b]", "a", false),
2455            row("[a&&[b]&&]", "b", false),
2456            row("[a&&[b]&&]", "a", false),
2457            row("[[a]&&]", "a", true),
2458            row("[a&& ]", "a", false),
2459            row("[a&& ]", " ", false),
2460            row("(?x)[a&& ]", "a", true),
2461            row("(?x)[a&& b]", "a", false),
2462            row("[a-c&&&&b]", "b", true),
2463            row("[a-c&&&&b]", "a", false),
2464            row("[a&&\\w]", "a", true),
2465            row("[\\w&&]", "a", true),
2466            row("[&&[a]]", "a", true),
2467            row("[b&&[a]]", "a", false),
2468            row("[a[b]&&]", "b", true),
2469            row("[a[b]&&c]", "b", false),
2470            row("[a&&b[c]]", "c", false),
2471            row("[ab-c&&]", "a", false),
2472            row("[ab-c&&]", "b", true),
2473            row("[a&&[b]&c]", "a", true),
2474            row("[a&&[b]&c]", "b", false),
2475            row("[a&&[b]&c]", "c", true),
2476            row("[a&&[b]&c]", "&", true),
2477            row("[a&&b&&c]", "a", false),
2478            row("[a-c&&b-d&&c]", "c", true),
2479            row("[a-c&&b-d&&c]", "b", false),
2480            row("[^a-c&&b]", "b", false),
2481            row("[^a-c&&b]", "a", true),
2482            row("[^a-c&&]", "d", true),
2483            row("[^a-c&&]", "a", false),
2484            row("(?i)[a-c&&]", "B", true),
2485            row("(?i)[a&&]", "A", true),
2486            row("(?i)[A&&&b]", "B", true),
2487            row("(?iu)[k&&]", "\u{212a}", true),
2488            row("(?iu)[k&&&a]", "a", true),
2489            row("[\\w&&\\d]", "5", true),
2490            row("[\\w&&\\d]", "a", false),
2491            row("[\\d&&]", "5", true),
2492            row("[a\\d&&]", "a", false),
2493            row("[a\\d&&]", "5", true),
2494            row("[[a-c]&&]", "b", true),
2495            row("[x[a-c]&&]", "x", false),
2496            row("[x[a-c]&&]", "b", true),
2497            row("[a-z&&[^aeiou]]", "b", true),
2498            row("[a-z&&[^aeiou]]", "a", false),
2499            row("[a&&[b]c]", "c", false),
2500            row("[ab&&b]", "b", true),
2501            row("[ab&&b]", "a", false),
2502            row("[\u{100}&&]", "\u{100}", true),
2503            row("[a\u{100}&&]", "a", false),
2504            row("[a&&&&b]", "b", false),
2505            row("[a-c&&&&b]", "c", false),
2506            row("[&&a-c]", "b", true),
2507            row("[&&[a]&&]", "a", true),
2508            row("[a&&]]", "]", false),
2509            row("[]&&]]", "]", false),
2510            row("[]a&&]", "a", true),
2511        ];
2512        check_matches(cases, true);
2513    }
2514
2515    /// Where a side of `&&` is empty and there is no operand to take its
2516    /// place, Java answers `Bad class syntax` or `Bad intersection syntax`.
2517    /// Each row is refused by `Pattern.compile` on JDK 25.0.4.
2518    #[yare::parameterized(
2519        both_sides_empty = { "[&&]" },
2520        both_sides_empty_then_ampersand = { "[&&&a]" },
2521        empty_side_after_a_single_char = { "[a-cd&&]" },
2522        empty_side_after_a_single_char_after_a_class = { "[[a-c]x&&]" },
2523        empty_nested_class = { "[a&&[]]" },
2524    )]
2525    fn a_class_intersection_java_refuses_does_not_compile(source: &str) {
2526        check!(JavaPattern::compile(source).is_err(), "{source:?}");
2527    }
2528
2529    /// A repeat of a group with a group in it, whose last iteration matches
2530    /// nothing, keeps the groups that iteration set, as Java's `Loop` does,
2531    /// where `fancy_regex` takes no such iteration. Each row is
2532    /// `String.replaceAll` on JDK 25.0.4.
2533    #[test]
2534    fn replace_sees_the_groups_of_an_empty_last_iteration_as_java_does() {
2535        // (pattern, input, replacement, replaceAll result)
2536        let cases = [
2537            replace_row("(a*)*", "aa", "[$1]", "[][]"),
2538            replace_row("(a|)*", "aa", "[$1]", "[][]"),
2539            replace_row("(a*)+", "aa", "[$1]", "[][]"),
2540            replace_row("(a*){2,}", "aa", "[$1]", "[][]"),
2541            replace_row("(a*){1,3}", "aa", "[$1]", "[][]"),
2542            replace_row("(a*){0,5}", "aaa", "[$1]", "[][]"),
2543            replace_row("(a*)*?", "aa", "[$1]", "[]a[]a[]"),
2544            replace_row("(a*)*+", "aa", "[$1]", "[][]"),
2545            replace_row("(a*)+?b", "aab", "[$1]", "[aa]"),
2546            replace_row("(?:(a)|b|)*", "ab", "[$1]", "[a][]"),
2547            replace_row("(?:(a)|)*", "aa", "[$1]", "[a][]"),
2548            replace_row("(?:(a)?)*", "aa", "[$1]", "[a][]"),
2549            replace_row("(?:b|(a?))*", "ab", "[$1]", "[][]"),
2550            replace_row("((a)|)*", "aa", "[$1|$2]", "[|a][|]"),
2551            replace_row("(a?)*", "aa", "[$1]", "[][]"),
2552            replace_row("(a?)+", "a", "[$1]", "[][]"),
2553            replace_row("(a??)*", "aa", "[$1]", "[]a[]a[]"),
2554            replace_row("()*", "a", "[$1]", "[]a[]"),
2555            replace_row("(\\b)*", "a", "[$1]", "[]a[]"),
2556            replace_row("(a{0})*", "a", "[$1]", "[]a[]"),
2557            replace_row("(?:a(b*))*", "abab", "[$1]", "[b][]"),
2558            replace_row("(?:a(b*))*", "aba", "[$1]", "[][]"),
2559            replace_row("(?:(a*)b)*", "abb", "[$1]", "[][]"),
2560            replace_row("((a*)*)*", "aa", "[$1|$2]", "[|][|]"),
2561            replace_row("(a*)*x", "aax", "[$1]", "[]"),
2562            replace_row("(a|b*)*c", "abc", "[$1]", "[]"),
2563        ];
2564        check_replacements(cases, true, true);
2565    }
2566
2567    /// A backreference sees the group an empty last iteration set. Each row is
2568    /// `Pattern.matches` on JDK 25.0.4.
2569    #[test]
2570    fn a_reference_sees_the_group_of_an_empty_last_iteration_as_java_does() {
2571        // (pattern, input, whole input matches)
2572        let cases = [
2573            row("(a*)*\\1", "aa", true),
2574            row("(a*)+\\1", "aa", true),
2575            row("(a|)*\\1", "aa", true),
2576            row("(?:(a)|)*\\1", "aa", true),
2577            row("(a*)*b\\1", "aab", true),
2578            row("(a*)*b\\1", "aabaa", true),
2579            row("(a|)+b\\1", "ab", true),
2580            row("(a|)+b\\1", "aba", true),
2581            row("()*+\\1", "", true),
2582            row("(){1,}\\1", "", true),
2583            row("()+\\1", "", true),
2584            row("(()|a)*\\2", "aa", true),
2585            row("(?:())*\\1", "", true),
2586            row("(())*\\2", "", true),
2587        ];
2588        check_matches(cases, true);
2589    }
2590
2591    /// `\G` matches where the last match ended. After an empty match the next
2592    /// search starts one char on and `\G` stays behind it, so it matches no
2593    /// more, as Java's `LastMatch` does, in a look-around too. Each row is
2594    /// `String.replaceAll` on JDK 25.0.4.
2595    #[test]
2596    fn last_match_anchor_stays_where_the_last_match_ended() {
2597        // (pattern, input, replacement, replaceAll result)
2598        let cases = [
2599            replace_row("\\G", "aa", "-", "-aa"),
2600            replace_row("\\Ga", "aaba", "-", "--ba"),
2601            replace_row("\\G|a", "aa", "-", "-a--"),
2602            replace_row("\\G|b", "ab", "-", "-a--"),
2603            replace_row("a|\\G", "baa", "-", "-b---"),
2604            replace_row("(?<=\\G.)", "abc", "-", "a-b-c-"),
2605            replace_row("(?=\\G)", "aa", "-", "-aa"),
2606            replace_row("x*\\G", "aa", "-", "-aa"),
2607            replace_row("\\G(a|)", "aab", "[$1]", "[a][a][]b"),
2608            replace_row("a*?\\G", "aa", "-", "-aa"),
2609        ];
2610        check_replacements(cases, true, true);
2611    }
2612
2613    /// A look-behind whose maximum length wraps in Java's `int`, and whose
2614    /// window then leaves out starts the body can match from, is refused. The
2615    /// comment on each row gives its `replaceAll("<$0>")` on JDK 25.0.4, and
2616    /// then what the look-behind read without the wrap gives.
2617    #[yare::parameterized(
2618        // `rmax` wraps to -2: `abc`, not `ab<c>`.
2619        two_greedy_repeats = { "(?<=a*b*)c" },
2620        // `ab<c>`, not `abc`.
2621        two_greedy_repeats_negated = { "(?<!a*b*)c" },
2622        // `ab`, not `<>a<>b<>`.
2623        two_greedy_repeats_alone = { "(?<=a*b*)" },
2624        // `rmax` wraps to 1, under `rmin` 3: `cdef`, not `cde<f>`.
2625        wrapped_below_the_minimum = { "(?<=a*b*cde)f" },
2626        // `rmax` wraps to 0: `cdf`, not `cd<f>`.
2627        wrapped_to_zero = { "(?<=a*b*cd)f" },
2628        // `rmax` wraps to -1: `cf`, not `c<f>`.
2629        wrapped_to_minus_one = { "(?<=a*b*c)f" },
2630        // `de`, not `d<e>`.
2631        four_greedy_repeats = { "(?<=a*b*c*d*)e" },
2632        // `1x`, not `1<x>`.
2633        two_greedy_classes = { "(?<=\\w*\\d*)x" },
2634        // `abcd`, not `abc<d>`.
2635        four_greedy_repeats_with_a_class = { "(?<=a*b*c*\\w*)d" },
2636        // `rmax` wraps to 0, under `rmin` 2: `1bcd`, not `1bc<d>`.
2637        wrapped_to_zero_below_the_minimum = { "(?<=a*\\d*bc)d" },
2638        // The branch's maximum is 2: `cdef`, not `cde<f>`.
2639        wrapped_branch = { "(?<=xy|a*b*cde)f" },
2640        // Java counts the window in code points: `bcπŸ˜€d`, not `bcπŸ˜€<d>`.
2641        supplementary_literal = { "(?<=a*bcπŸ˜€)d" },
2642        // `abcπŸ˜€<d>`, as without the wrap, but counted in code points.
2643        supplementary_literal_wrapped_to_a_large_maximum = { "(?<=a*b*c*πŸ˜€)d" },
2644    )]
2645    fn a_look_behind_whose_maximum_wraps_is_refused_where_java_reads_it_otherwise(source: &str) {
2646        check!(
2647            JavaPattern::compile(source).err()
2648                == Some(PatternError(format!(
2649                    "{source:?} {}",
2650                    Refusal::MaximumOverflows.reason()
2651                )))
2652        );
2653    }
2654
2655    /// A look-behind whose maximum length wraps in Java's `int` still compiles
2656    /// where Java's window holds every start the body can match from: the
2657    /// body matches the empty string anywhere, the wrapped maximum is at
2658    /// least 2^30, or it is negative and the body is longer than the last `i`
2659    /// with an empty window. Each row is `String.replaceAll` on JDK 25.0.4.
2660    #[test]
2661    fn a_look_behind_whose_maximum_wraps_matches_as_java_does() {
2662        // (pattern, input, replacement, replaceAll result)
2663        let cases = [
2664            // The body matches the empty string at `i`.
2665            replace_row("(?<=a*b*c*)d", "abcd", "<$0>", "abc<d>"),
2666            replace_row("(?<=a*b*c*)d", "xd", "<$0>", "x<d>"),
2667            replace_row("(?<!a*b*c*)d", "abcd", "<$0>", "abcd"),
2668            replace_row("(?<!a*b*c*)d", "d", "<$0>", "d"),
2669            replace_row("(?<=b|a*c*)d", "bd", "<$0>", "b<d>"),
2670            replace_row("(?<=x|a*b*)c", "c", "<$0>", "<c>"),
2671            replace_row("(?<=x|a*b*)c", "abc", "<$0>", "ab<c>"),
2672            // `rmax` wraps to 2147483646.
2673            replace_row("(?<=a*b*c*x)y", "abcxy", "<$0>", "abcx<y>"),
2674            replace_row("(?<=a*b*c*x)y", "xy", "<$0>", "x<y>"),
2675            replace_row("(?<=a*b*c*x)y", "y", "<$0>", "y"),
2676            replace_row("(?<=(?:a*b*c*)?x)y", "xy", "<$0>", "x<y>"),
2677            // An escape is not a supplementary char of the pattern, so Java
2678            // counts in UTF-16 units.
2679            replace_row("(?<=a*b*c*\\x{1F600})d", "abcπŸ˜€d", "<$0>", "abcπŸ˜€<d>"),
2680            // `rmax` wraps to `i32::MIN + 1`, so the window is empty only at
2681            // `i` 0, where `bc` cannot end.
2682            replace_row("(?<=a*bc)d", "bcd", "<$0>", "bc<d>"),
2683            replace_row("(?<=a*bc)d", "d", "<$0>", "d"),
2684            replace_row("(?<!a*bc)d", "d", "<$0>", "<d>"),
2685            replace_row("(?<!a*bc)d", "abcd", "<$0>", "abcd"),
2686            replace_row("(?<!a*bc)d", "bdd", "<$0>", "b<d><d>"),
2687            replace_row("(?<=a*bc\\x{1F600})d", "bcπŸ˜€d", "<$0>", "bcπŸ˜€<d>"),
2688            // `rmax` wraps to `i32::MIN`, so the window is empty only below
2689            // `i` 0.
2690            replace_row("(?<=a??b*)c", "c", "<$0>", "<c>"),
2691            // No wrap.
2692            replace_row("(?<=a*)c", "aac", "<$0>", "aa<c>"),
2693        ];
2694        check_replacements(cases, true, false);
2695    }
2696
2697    /// Java's `Behind` counts its window in UTF-16 units, a class or `.` as
2698    /// one, so over a char past U+FFFF it tries starts that `fancy_regex`,
2699    /// which counts chars, does not: between the two units of the char, where
2700    /// it reads a lone low surrogate that `.` and `[^a]` match and `\p{So}`
2701    /// does not, and none before it for `(?<=..)`. With the char written in
2702    /// the pattern from the look-behind on, Java's `BehindS` counts code
2703    /// points. Each row is `replaceAll("<$0>")` on JDK 25.0.4.
2704    #[test]
2705    fn a_look_behind_over_a_char_past_u_ffff_matches_as_java_does() {
2706        let cases = [
2707            replace_row(".(?<=.)x", "πŸ˜€x", "<$0>", "<πŸ˜€x>"),
2708            replace_row(".(?<=.)x", "aπŸ˜€x", "<$0>", "a<πŸ˜€x>"),
2709            replace_row("a.(?<=a.)x", "aπŸ˜€x", "<$0>", "aπŸ˜€x"),
2710            replace_row("(?<=..)x", "aπŸ˜€x", "<$0>", "aπŸ˜€x"),
2711            replace_row("(?<=.{1,2})x", "aπŸ˜€x", "<$0>", "aπŸ˜€<x>"),
2712            replace_row(".(?<=[^a])x", "aπŸ˜€x", "<$0>", "a<πŸ˜€x>"),
2713            replace_row(".(?<=\\p{So})x", "aπŸ˜€x", "<$0>", "aπŸ˜€x"),
2714            replace_row("(?<=\\x{1F600})x", "πŸ˜€x", "<$0>", "πŸ˜€x"),
2715            replace_row("(?<!.)x", "πŸ˜€x", "<$0>", "πŸ˜€x"),
2716            replace_row("(?<!\\p{So})x", "πŸ˜€x", "<$0>", "πŸ˜€<x>"),
2717            replace_row("(?<=^.)x", "πŸ˜€x", "<$0>", "πŸ˜€x"),
2718            replace_row("(?<=\\b.)x", "aπŸ˜€x", "<$0>", "aπŸ˜€x"),
2719            // `BehindS`: the char in the pattern from the look-behind on.
2720            replace_row("(?<=πŸ˜€)x", "aπŸ˜€x", "<$0>", "aπŸ˜€<x>"),
2721            replace_row("(?<=\\p{So})xπŸ˜€?", "πŸ˜€x", "<$0>", "πŸ˜€<x>"),
2722            // An input with no char past U+FFFF reads as before.
2723            replace_row("(?<=..)x", "abx", "<$0>", "ab<x>"),
2724        ];
2725        check_replacements(cases, true, false);
2726    }
2727
2728    /// A group the look-behind starts between the two UTF-16 units of a char
2729    /// holds a lone surrogate in Java, which a Rust string cannot, so a
2730    /// replacement that takes its text is an error. The match itself stands.
2731    #[test]
2732    fn a_group_holding_half_a_char_is_refused_in_a_replacement() {
2733        let source = ".(?<=(.))x";
2734        check!(pattern(source).replace("πŸ˜€x", "<$0>", true).ok().as_deref() == Some("<πŸ˜€x>"));
2735        check!(pattern(source).replace("πŸ˜€x", "<$1>", true).is_err());
2736    }
2737
2738    /// A look-behind accepted with a wrapped maximum of at least 2^30 limits
2739    /// the input to that many UTF-16 units, the smallest such maximum where
2740    /// there are several.
2741    #[test]
2742    fn a_wrapped_look_behind_maximum_limits_the_input() {
2743        // (pattern, input limit)
2744        let cases = [
2745            ("(?<=a*b*c*x)y", Some(2_147_483_646)),
2746            ("(?<=(?:a*b*c*)?x)y", Some(2_147_483_646)),
2747            ("(?<=a*b*c*xy)z(?<=a*b*c*x)", Some(2_147_483_646)),
2748            // Four greedy repeats wrap to -4, and the counted repeat brings it
2749            // back to 1,099,999,996. The look-behind is too large for
2750            // `fancy_regex` too, and keeps its limit on the matcher.
2751            ("(?<=a*b*c*d*x{1,1100000000})y", Some(1_099_999_996)),
2752            ("(?<=a*b*c*)d", None),
2753            ("(?<=a*bc)d", None),
2754            ("(?<=a*)c", None),
2755        ];
2756        for (source, expected) in cases {
2757            check!(pattern(source).0.input_limit == expected, "{source}");
2758        }
2759    }
2760
2761    /// An input of more UTF-16 units than the limit is refused. A char past
2762    /// U+FFFF is two units in four bytes.
2763    #[test]
2764    fn an_input_past_the_limit_is_refused() {
2765        // (input limit, input, refused)
2766        let cases = [
2767            (None, "abc", false),
2768            (Some(3), "abc", false),
2769            (Some(2), "abc", true),
2770            (Some(0), "", false),
2771            (Some(1), "Γ©", false),
2772            (Some(2), "πŸ˜€", false),
2773            (Some(1), "πŸ˜€", true),
2774            (Some(3), "aπŸ˜€", false),
2775            (Some(2), "aπŸ˜€", true),
2776        ];
2777        for (limit, input, refused) in cases {
2778            check!(
2779                check_input_length(limit, input).is_err() == refused,
2780                "{limit:?} {input}"
2781            );
2782        }
2783    }
2784
2785    /// A look-behind that is not of one length, with a node in it that
2786    /// `fancy_regex` runs itself, such as `\R`, a look-around, an atomic
2787    /// group, a possessive quantifier, `\G` or a group, is one `fancy_regex`
2788    /// cannot build. The matcher answers for it. Each row is
2789    /// `String.replaceAll` on JDK 25.0.4.
2790    #[test]
2791    fn a_look_behind_fancy_regex_cannot_build_matches_as_java_does() {
2792        // (pattern, input, replacement, replaceAll result)
2793        let cases = [
2794            replace_row("(?<=\\R?x)y", "xy", "<$0>", "x<y>"),
2795            replace_row("(?<=\\R?x)y", "\nxy", "<$0>", "\nx<y>"),
2796            replace_row("(?<=\\R?x)y", "\r\nxy", "<$0>", "\r\nx<y>"),
2797            replace_row("(?<=\\R?x)y", "y", "<$0>", "y"),
2798            replace_row("(?<=\\R?x)y", "zy", "<$0>", "zy"),
2799            replace_row("(?<!\\R?x)y", "xy", "<$0>", "xy"),
2800            replace_row("(?<!\\R?x)y", "\nxy", "<$0>", "\nxy"),
2801            replace_row("(?<!\\R?x)y", "zy", "<$0>", "z<y>"),
2802            replace_row("(?<!\\R?x)y", "y", "<$0>", "<y>"),
2803            replace_row("(?<=\\Rx|y)z", "\nxz", "<$0>", "\nx<z>"),
2804            replace_row("(?<=\\Rx|y)z", "\r\nxz", "<$0>", "\r\nx<z>"),
2805            replace_row("(?<=\\Rx|y)z", "yz", "<$0>", "y<z>"),
2806            replace_row("(?<=\\Rx|y)z", "xz", "<$0>", "xz"),
2807            replace_row("(?<=\\R{1,2})x", "\r\nx", "<$0>", "\r\n<x>"),
2808            replace_row("(?<=\\R{1,2})x", "\n\nx", "<$0>", "\n\n<x>"),
2809            replace_row("(?<=\\R{1,2})x", "x", "<$0>", "x"),
2810            replace_row("(?<=a\\R?)b", "a\r\nb", "<$0>", "a\r\n<b>"),
2811            replace_row("(?<=a\\R?)b", "b", "<$0>", "b"),
2812            replace_row("(?<=(?=a)ab?)c", "ac", "<$0>", "a<c>"),
2813            replace_row("(?<=(?=a)ab?)c", "abc", "<$0>", "ab<c>"),
2814            replace_row("(?<=(?=a)ab?)c", "bc", "<$0>", "bc"),
2815            replace_row("(?<!(?=a)ab?)c", "abc", "<$0>", "abc"),
2816            replace_row("(?<!(?=a)ab?)c", "bc", "<$0>", "b<c>"),
2817            replace_row("(?<=a(?!b)c?)d", "acd", "<$0>", "ac<d>"),
2818            replace_row("(?<=a(?!b)c?)d", "abd", "<$0>", "abd"),
2819            replace_row("(?<=(?<=a)b?)c", "abc", "<$0>", "ab<c>"),
2820            replace_row("(?<=(?<=a)b?)c", "bc", "<$0>", "bc"),
2821            // The atomic group keeps its first way, so `ab` leaves no `b`.
2822            replace_row("(?<=(?>a|ab)b?)c", "ac", "<$0>", "a<c>"),
2823            replace_row("(?<=(?>a|ab)b?)c", "abc", "<$0>", "ab<c>"),
2824            replace_row("(?<=(?>a|ab)b?)c", "abbc", "<$0>", "abbc"),
2825            replace_row("(?<=(?>ab|a)b?)c", "abbc", "<$0>", "abb<c>"),
2826            replace_row("(?<=a{1,3}+b?)c", "aabc", "<$0>", "aab<c>"),
2827            replace_row("(?<=a{1,3}+b?)c", "bc", "<$0>", "bc"),
2828            replace_row("(?<=\\Gab?)c", "acac", "<$0>", "a<c>a<c>"),
2829            // A group in the look-behind keeps its text.
2830            replace_row("(?<=(a)|bc)d", "ad", "[$1$0]", "a[ad]"),
2831            replace_row("(?<=(a)|bc)d", "bcd", "[$1$0]", "bc[d]"),
2832            replace_row("(?<=\\R?(x))(y)", "xy", "[$2$1]", "x[yx]"),
2833            replace_row("(?<=\\R?(?<n>x))y", "\nxy", "[${n}$0]", "\nx[xy]"),
2834        ];
2835        check_replacements(cases, true, true);
2836    }
2837
2838    /// A counted repeat too large for the `regex` crate at its default size
2839    /// limit, in a look-behind or outside one, is one `fancy_regex` cannot
2840    /// build, and in a look-behind would build for minutes. The matcher
2841    /// counts the iterations instead. Each row is `Matcher.matches()` on
2842    /// JDK 25.0.4.
2843    #[test]
2844    fn a_counted_repeat_too_large_to_build_matches_as_java_does() {
2845        // (pattern, input, matches)
2846        let cases = [
2847            row("x{1,1000000000}", "xxx", true),
2848            row("x{1,1000000000}", "x", true),
2849            row("x{1,1000000000}", "", false),
2850            row("x{1,1000000000}", "y", false),
2851            row("x{1000000000}", "xxx", false),
2852            row("x{2,1000000000}y", "xxy", true),
2853            row("x{2,1000000000}y", "xy", false),
2854            row("(?:ab){1,600000000}", "abab", true),
2855            row("(?:ab){1,600000000}", "aba", false),
2856            row("(x){1,1000000000}", "xxx", true),
2857            row("(?i)x{1,1000000000}", "XXX", true),
2858        ];
2859        check_matches(cases, false);
2860    }
2861
2862    /// A look-behind too large for `fancy_regex` to build, and a counted
2863    /// repeat too large outside one, match on the matcher. Each row is
2864    /// `String.replaceAll` on JDK 25.0.4.
2865    #[test]
2866    fn a_look_behind_too_large_to_build_matches_as_java_does() {
2867        // (pattern, input, replacement, replaceAll result)
2868        let cases = [
2869            replace_row("(?<=x{1,1000000000})y", "xy", "<$0>", "x<y>"),
2870            replace_row("(?<=x{1,1000000000})y", "xxy", "<$0>", "xx<y>"),
2871            replace_row("(?<=x{1,1000000000})y", "y", "<$0>", "y"),
2872            replace_row("(?<!x{1,1000000000})y", "xy", "<$0>", "xy"),
2873            replace_row("(?<!x{1,1000000000})y", "y", "<$0>", "<y>"),
2874            replace_row("(?<=(?:ab){1,600000000})y", "aby", "<$0>", "ab<y>"),
2875            replace_row("(?<=(?:ab){1,600000000})y", "by", "<$0>", "by"),
2876            replace_row("(?<=a{0,2147483647}b)c", "abc", "<$0>", "ab<c>"),
2877            replace_row("(?<=a{0,2147483647}b)c", "bc", "<$0>", "b<c>"),
2878            replace_row("(?<=a{0,2147483647}b)c", "c", "<$0>", "c"),
2879            replace_row("(?<=a{1,2147483647})b", "ab", "<$0>", "a<b>"),
2880            replace_row("(?<=a{1,2147483647})b", "b", "<$0>", "b"),
2881            replace_row("(?<=[ab]{1,1500000000})c", "abc", "<$0>", "ab<c>"),
2882            replace_row("(?<=[ab]{1,1500000000})c", "c", "<$0>", "c"),
2883            replace_row("(?<=z|x{1,1000000000})y", "xy", "<$0>", "x<y>"),
2884            replace_row("(?<=z|x{1,1000000000})y", "zy", "<$0>", "z<y>"),
2885            replace_row("(?<=z|x{1,1000000000})y", "y", "<$0>", "y"),
2886            replace_row("(?<=(?=x)x{1,1000000000})y", "xy", "<$0>", "x<y>"),
2887            replace_row("(?<=(?=x)x{1,1000000000})y", "y", "<$0>", "y"),
2888            replace_row("(?<=(?i)x{1,1000000000})y", "Xy", "<$0>", "X<y>"),
2889            replace_row("(?<=x{1,1000000000})(y)", "xy", "[$1]", "x[y]"),
2890            replace_row("(?:ab){1,600000000}", "aba", "<$0>", "<ab>a"),
2891            replace_row("(x){1,1000000000}", "xxx", "[$1]", "[x]"),
2892            replace_row("(?<n>x){1,1000000000}", "xxx", "[${n}]", "[x]"),
2893        ];
2894        check_replacements(cases, true, true);
2895    }
2896
2897    /// A very large repeat of something that matches nothing ends in a
2898    /// [`MatchError`] at the step limit, and does not run a billion
2899    /// iterations and fill memory with their frames. Java answers `true` for
2900    /// each, after a billion iterations.
2901    #[yare::parameterized(
2902        empty_group = { "(){1000000000}" },
2903        in_an_alternation = { "(?:x{1,1000000000}|(){1000000000})" },
2904        empty_group_alone = { "(?:(){1000000000})+" },
2905    )]
2906    fn a_large_repeat_of_nothing_stops_at_the_step_limit(source: &str) {
2907        check!(pattern(source).matches("").is_err(), "{source:?}");
2908    }
2909
2910    /// A long input against a very large counted repeat ends in Java's
2911    /// answer or in a [`MatchError`] at the backtrack limit, never in
2912    /// another answer. Each `expected` is `Matcher.matches()` and
2913    /// `String.replaceAll` on JDK 25.0.4, with `x` 3,000 times as the input.
2914    #[test]
2915    fn a_long_input_against_a_large_repeat_gives_java_s_answer_or_an_error() {
2916        let xs = "x".repeat(3000);
2917        let xs_y = format!("{xs}y");
2918        // (pattern, input, matches, replaceAll result with `<$0>`)
2919        let cases = [
2920            ("x{1,1000000000}", xs.clone(), true, format!("<{xs}>")),
2921            ("x{1,1000000000}y", xs.clone(), false, xs.clone()),
2922            (
2923                "(?<=x{1,1000000000})y",
2924                xs_y.clone(),
2925                false,
2926                format!("{xs}<y>"),
2927            ),
2928            ("(?<!x{1,1000000000})y", xs_y.clone(), false, xs_y.clone()),
2929        ];
2930        for (source, input, matches, replaced) in cases {
2931            let compiled = pattern(source);
2932            check!(
2933                compiled.matches(&input).ok() != Some(!matches),
2934                "{source} matches"
2935            );
2936            check!(
2937                compiled
2938                    .replace(&input, "<$0>", true)
2939                    .map_or(true, |out| out == replaced),
2940                "{source} replaceAll"
2941            );
2942        }
2943    }
2944
2945    /// A pattern `fancy_regex` refuses for a reason other than a size or a
2946    /// look-behind it cannot build stays refused, the matcher answering or
2947    /// not: here a backreference to a group the pattern does not have, which
2948    /// JDK 25.0.4 compiles, and which never matches there.
2949    #[yare::parameterized(
2950        alone = { "(a)\\2" },
2951        beside_a_look_behind_too_large_to_build = { "(?<=x{1,1000000000})y(a)\\2" },
2952        beside_a_look_behind_fancy_regex_cannot_build = { "(?<=\\R?x)y(a)\\2" },
2953        after_a_large_repeat = { "x{1,1000000000}(a)\\2" },
2954    )]
2955    fn a_backreference_to_a_missing_group_is_refused(source: &str) {
2956        check!(
2957            JavaPattern::compile(source).err()
2958                == Some(PatternError(
2959                    "Error compiling regex: Invalid back reference to group 2".to_owned()
2960                ))
2961        );
2962    }
2963
2964    /// A large look-behind under the size limit still compiles. Each row is
2965    /// `String.replaceAll` on JDK 25.0.4.
2966    #[test]
2967    fn a_large_look_behind_under_the_size_limit_matches_as_java_does() {
2968        // (pattern, input, replacement, replaceAll result)
2969        let cases = [
2970            replace_row("(?<=x{2,20000})y", "xxy", "<$0>", "xx<y>"),
2971            replace_row("(?<=x{2,20000})y", "xy", "<$0>", "xy"),
2972        ];
2973        check_replacements(cases, true, false);
2974    }
2975}