Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,13 @@ impl<'ast> Visit<'ast> for CharacterSequenceCollector<'ast> {
}
}

fn leave_node(&mut self, _kind: RegExpAstKind<'ast>) {}
fn leave_node(&mut self, kind: RegExpAstKind<'ast>) {
// Flush the current sequence when leaving a character class to ensure
// sequences don't span across different character classes
if matches!(kind, RegExpAstKind::CharacterClass(_)) && !self.current_seq.is_empty() {
self.sequences.push(std::mem::take(&mut self.current_seq));
}
Comment on lines +153 to +158
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With sequences now flushed per CharacterClass, the rule can emit multiple identical diagnostics for a single regex (e.g. [👩‍👦][👩‍👦]), but diagnostics are still labeled with pattern.span, so each warning points at the same overall span and can’t identify which character class triggered it. Consider tracking the CharacterClass span alongside each collected sequence and using that span for the diagnostic label so the report is precise per class.

Copilot uses AI. Check for mistakes.
}
}

impl Rule for NoMisleadingCharacterClass {
Expand All @@ -163,11 +169,6 @@ impl Rule for NoMisleadingCharacterClass {
let mut collector = CharacterSequenceCollector::new();
collector.visit_pattern(pattern);

// Restore: push any remaining sequence after visiting
if !collector.current_seq.is_empty() {
collector.sequences.push(std::mem::take(&mut collector.current_seq));
}

for unfiltered_chars in &collector.sequences {
if self.allow_escape {
let has_escape = unfiltered_chars.iter().any(|c| {
Expand Down Expand Up @@ -456,6 +457,9 @@ fn test() {
),
// https://github.com/oxc-project/oxc/issues/19090
(r"/[\u200c\u200d\p{ID_Continue}.]/u", None),
// Separate character classes should not trigger warnings for cross-class sequences
(r"/[\u200c][\u200d][a]/", None),
(r"/[\uD83D][\uDC4D]/", None), // Solo surrogates in separate classes
];

let fail = vec![
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,18 @@ source: crates/oxc_linter/src/tester.rs
· ────────
╰────

⚠ eslint(no-misleading-character-class): Unexpected joined character sequence in character class.
╭─[no_misleading_character_class.tsx:1:10]
1 │ var r = /[👩‍👦][👩‍👦]/u
· ────────
╰────

⚠ eslint(no-misleading-character-class): Unexpected joined character sequence in character class.
╭─[no_misleading_character_class.tsx:1:10]
1 │ var r = /[👨‍👩‍👦]foo[👨‍👩‍👦]/u
· ───────────
╰────

⚠ eslint(no-misleading-character-class): Unexpected joined character sequence in character class.
╭─[no_misleading_character_class.tsx:1:10]
1 │ var r = /[👨‍👩‍👦]foo[👨‍👩‍👦]/u
Expand Down Expand Up @@ -265,6 +277,12 @@ source: crates/oxc_linter/src/tester.rs
· ─────────────────────────────────────────────────────────────────────────────────────────────
╰────

⚠ eslint(no-misleading-character-class): Unexpected joined character sequence in character class.
╭─[no_misleading_character_class.tsx:1:10]
1 │ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]foo[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u
· ─────────────────────────────────────────────────────────────────────────────────────────────
╰────

⚠ eslint(no-misleading-character-class): Unexpected surrogate pair in character class.
╭─[no_misleading_character_class.tsx:1:17]
1 │ var r = RegExp("[👍]", "")
Expand Down Expand Up @@ -439,6 +457,12 @@ source: crates/oxc_linter/src/tester.rs
· ────────────────────
╰────

⚠ eslint(no-misleading-character-class): Unexpected combining class in character class.
╭─[no_misleading_character_class.tsx:1:13]
1 │ new RegExp("[ \\ufe0f][ \\ufe0f]")
· ────────────────────
╰────

⚠ eslint(no-misleading-character-class): Unexpected combining class in character class.
╭─[no_misleading_character_class.tsx:1:21]
1 │ var r = new RegExp("[\\u2747\\uFE0F]", "")
Expand Down Expand Up @@ -589,6 +613,18 @@ source: crates/oxc_linter/src/tester.rs
· ────────
╰────

⚠ eslint(no-misleading-character-class): Unexpected joined character sequence in character class.
╭─[no_misleading_character_class.tsx:1:21]
1 │ var r = new RegExp("[👩‍👦][👩‍👦]", "u")
· ────────
╰────

⚠ eslint(no-misleading-character-class): Unexpected joined character sequence in character class.
╭─[no_misleading_character_class.tsx:1:21]
1 │ var r = new RegExp("[👨‍👩‍👦]foo[👨‍👩‍👦]", "u")
· ───────────
╰────

⚠ eslint(no-misleading-character-class): Unexpected joined character sequence in character class.
╭─[no_misleading_character_class.tsx:1:21]
1 │ var r = new RegExp("[👨‍👩‍👦]foo[👨‍👩‍👦]", "u")
Expand Down
Loading