Regex Grapheme Cluster In Class
ID |
java.regex_grapheme_cluster_in_class |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Java |
Tags |
code-style |
Description
Reports a regex character class that combines a base character with a following Unicode combining mark — visually one grapheme, encoded as two separate class members.
Rationale
Some visible characters are not a single Unicode code point. ö, for instance, is the letter o followed by the combining diaeresis U+0308; it looks like one character on screen but is two in the source.
A character class is an enumeration of its members, never a sequence, so writing that pair inside […] does not name the combined grapheme — it names two independent alternatives, the base letter or the bare mark:
"nöñn".replaceAll("[öñ]", "X");
// intent: replace the accented "ö" and "ñ"
// actual: [öñ] enumerates {o, ̈, n, ̃}, so every plain 'o', every plain 'n'
// and every bare combining mark is replaced too
Remediation
Use an alternation instead of a class — ö|ñ matches the two-code-point grapheme as a unit, the way the class was meant to:
"nöñn".replaceAll("ö|ñ", "X");
What is not reported
A base+mark pair outside a character class — for example already written as an alternation — is the documented fix and is never reported. A mark spelled out as an explicit escape, whether a four-hex-digit Unicode code-point escape (backslash, u, 0308) or a named escape (\N{COMBINING DIAERESIS}), is treated as a deliberate, individually-enumerated code point rather than an accidental grapheme, and is not reported either. A run of marks with no base character in front of them, or a class holding a single character on its own, is not a cluster.