Raw Control Character In Literal
ID |
java.escape_control_char_in_literal |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
Java |
Tags |
code_smell, readability, string |
Description
Reports a String or char literal that holds a raw control character or an invisible
character — an ASCII control, a Unicode whitespace character other than the plain space, or a
zero-width character such as the zero-width space or the byte order mark — where an escape
sequence for that character should have been written instead.
Rationale
These characters get into source by accident: a tab picked up from a terminal, a non-breaking space pasted from a document, a zero-width space carried over from an editor’s input method. Nothing marks them on screen, so the literal no longer matches what the reader sees. A comparison against the visually identical string fails, a map lookup never hits, and the review that should have caught it saw only whitespace.
They are also fragile: a reformat or a round trip through a tool that trims trailing whitespace can silently change or delete the character, so the same source behaves differently before and after an unrelated edit. Written as an escape sequence, the same character is explicit, greppable, and survives every editor.
Raw characters are told apart from their escaped spelling by reading the literal’s own text: an escaped tab and a raw tab decode to the same string, so only the way it was written can tell them apart. Because every Java escape sequence is made of printable ASCII, anything invisible found in the literal as written was typed raw.
Text blocks are not covered: their leading indentation and embedded newlines are part of how they
are meant to be written, so a raw tab or space there is not necessarily a mistake. The C1 range
(U+0080-U+009F) is not reported either, because legacy single-byte encodings put printable
characters at those code points, so a literal that came through such an encoding would be
flagged for a character its author can see. U+007F is still reported.
In the example below, the placeholders <TAB> and <ZWSP> stand for the character itself, typed
straight into the literal — which is exactly what makes the defect invisible in a real file.
String header = "id<TAB>name"; // Bad -- a raw tab
String code = "AB<ZWSP>12"; // Bad -- a raw zero-width space
String escaped = "id\tname"; // Good -- escaped, and visible as such
String plain = "id name"; // Good -- a plain space
Remediation
Replace the character with its escape sequence. A tab is \t; the other named controls are
\b, \f, \n and \r. Everything else takes a code point escape, written as a backslash,
u and four hex digits — \u00A0 for a non-breaking space, \u200B for a zero-width space.
If the character got in by accident, delete it or replace it with a plain space. When several
places need the same separator, lift it to a named constant so there is a single spot to inspect.