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