{"record":{"id":"3be6d93c890d1a5e","repo":"oxc-project/oxc","slug":"unexpected-combining-class-in-character-class","errorCode":null,"errorMessage":"Unexpected combining class in character class.","messagePattern":"Unexpected combining class in character class\\.","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"warning","filePath":"crates/oxc_linter/src/rules/eslint/no_misleading_character_class.rs","lineNumber":42,"sourceCode":"        RegexFlagsParseResult, get_regex_flags_span, get_regex_pattern_span, is_regexp_callee,\n        run_on_regex_node,\n    },\n};\n\nfn surrogate_pair_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Unexpected surrogate pair in character class.\")\n        .with_help(\"Use Unicode code point escapes (e.g., \\\\u{1F44D}) instead of surrogate pairs.\")\n        .with_label(span)\n}\n\nfn surrogate_pair_without_flag_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Unexpected surrogate pair in character class.\")\n        .with_help(\"Add the Unicode flag 'u'.\")\n        .with_label(span)\n}\n\nfn combining_class_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Unexpected combining class in character class.\")\n        .with_help(\"Replace the character with its normalized form (NFC) or use Unicode code point escapes instead of combining sequences.\")\n        .with_label(span)\n}\n\nfn emoji_modifiers_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Unexpected emoji modifier in character class.\")\n        .with_help(\"Use Unicode code point escapes (e.g., \\\\u{1F3FB} for the light skin tone modifier) instead of emoji modifier sequences in character classes.\")\n        .with_label(span)\n}\n\nfn regional_indicator_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Unexpected regional indicator in character class.\")\n        .with_help(\"Use Unicode code point escapes (e.g., \\\\u{1F1EF} for the regional indicator symbol for 'J') instead of regional indicator symbol pairs in character classes.\")\n        .with_label(span)\n}\n\nfn zwj_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Unexpected joined character sequence in character class.\")","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/eslint/no_misleading_character_class.rs#L24-L60","documentation":"Diagnostic from oxlint's eslint/no-misleading-character-class rule (crates/oxc_linter/src/rules/eslint/no_misleading_character_class.rs:42). It reports a combining character (combining diacritical mark, e.g. U+0300) inside a regex character class. A class like /[à]/ where the à is stored decomposed (a + U+0300) does not match the accented character — it matches either 'a' or the bare combining mark — so the author's intent is broken. The help says to use the NFC-normalized single code point or explicit \\u{...} escapes.","triggerScenarios":"Character classes containing decomposed accented letters typed or pasted in NFD form: /[a\\u0300]/ (same as decomposed à); /[é]/ where the source file stores e + U+0301; equivalent classes built via new RegExp from user-provided strings.","commonSituations":"Copy-pasting accented characters from macOS file names (which use NFD) into a regex; sanitizing French/German/Spanish input with an allow-list character class; text normalization pipelines mixing NFC and NFD data.","solutions":["Use the precomposed character from NFC normalization: /[à]/ where à is the single code point U+00E0","Or write explicit code point escapes: /[\\u{00E0}]/u","Normalize input with String.prototype.normalize('NFC') before matching so decomposed data cannot slip through"],"exampleFix":"// before (à stored as a + U+0300)\nconst accented = /[à]/; // matches 'a' or the bare mark, not à\n\n// after (single NFC code point)\nconst accented = /[\\u{00E0}]/u; // matches à (U+00E0)","handlingStrategy":"validation","validationCode":"// Reject combining marks in character classes; require precomposed/NFC forms\nimport { readFileSync } from 'node:fs';\nconst COMBINING = /[\\u0300-\\u036F\\u1AB0-\\u1AFF\\u1DC0-\\u1DFF\\u20D0-\\u20FF\\uFE20-\\uFE2F]/;\nconst src = readFileSync(process.argv[2], 'utf8');\nif (COMBINING.test(src)) {\n  console.error('combining mark in source; normalize to NFC or use \\\\u{...} escapes');\n  process.exitCode = 1;\n}","typeGuard":"const isNFC = (s) => s === s.normalize('NFC');","tryCatchPattern":null,"preventionTips":["Normalize all user input with .normalize('NFC') before regex matching","Write accented characters in character classes as \\u{...} code point escapes to avoid encoding surprises","Be careful pasting from macOS file names and NFD-heavy sources; lint the file afterward"],"tags":["lint","regex","unicode","combining-marks","normalization","javascript"],"backgroundTag":"regex-unicode-escapes","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}