-
Notifications
You must be signed in to change notification settings - Fork 173
Scanner treats backslash as escape character outside quoted strings, diverging from Jinja2 behaviour #1304
Description
In Jinja2, the template scanner is not responsible for backslash interpretation. It locates block/variable/comment delimiters, extracts the raw content between them, and passes it to Python's expression parser. Backslash handling inside string literals is Python's concern, not the scanner's. A bare backslash outside a string literal is simply invalid Python, so Jinja2 never needs to handle it at the scanner level.
Jinjava's TokenScanner currently handles backslash at the scanner level unconditionally:
if (c == '\\') {
++currPost; // skips the next character regardless of quote context
continue;
}This causes two problems:
- Inside quoted strings: it works by accident for simple cases, but the scanner pre-consumes the escaped character before JUEL sees it, meaning the expression language never receives the original token intact.
- Outside quoted strings: a
\that is legitimately part of a Java expression (e.g. in a regex or a custom EL function) is silently swallowed by the scanner before the expression parser can interpret it.
The correct behaviour, matching Jinja2, is to treat \ as significant only inside quoted string literals — so that "\"" closes the string at the right place — and leave all other backslashes untouched for the expression parser. This affects both TokenScanner's char-based path and the new string-based path added in #1303. A fix would be a one-line change in both: move the backslash check inside the inQuote branch and remove it from the outer block-scanning logic
Behaviour change concern
This is a breaking change for any template that currently relies on \}} or \%} to prevent a closing delimiter from being recognized. We are not aware of Jinja2 supporting this pattern — in Python, embedding a literal }} inside a block expression would be a syntax error regardless — but Jinjava users may have come to depend on it as an undocumented escape mechanism.
We would like to ask the maintainers: is this use case known and intentional? If so, would the fix require a compatibility flag, or is a clean break acceptable given that the current behaviour diverges from Jinja2? We are happy to submit a PR once the preferred approach is clear.