Jens Geyer created THRIFT-6236:
----------------------------------
Summary: Generated string constants are not escaped correctly in
several target languages
Key: THRIFT-6236
URL: https://issues.apache.org/jira/browse/THRIFT-6236
Project: Thrift
Issue Type: Bug
Components: C glib - Compiler, Kotlin - Compiler, Rust - Compiler,
Lua - Compiler, Common LISP - Compiler, Delphi - Compiler, Ruby - Compiler,
Dart - Compiler
Reporter: Jens Geyer
Several code generators write string constants in a form that does not fit the
string syntax of the target language. A constant that contains a quote, a
backslash, a line break or a dollar sign then produces generated code that does
not compile, or a constant with a different value. Since THRIFT-4244 the
compiler tells users to double a backslash to get a literal one, and in the
generators below exactly that input breaks.
h3. Cause
Most generators pass the value through {{t_generator::escape_string()}}, which
fits C-like string syntax.
* rs, c_glib, cl, kotlin and lua write the raw value.
* dart, rb and delphi escape it, but not for their own string syntax: dart puts
the C-style escapes inside single quotes and escapes neither {{'}} nor {{$}};
rb puts them into a {{%q}} string, where only the delimiter and the backslash
itself can be escaped; delphi only doubles single quotes.
h3. How this was checked
One constant per IDL file, generated with current master (81103bf9c):
{noformat}
namespace * esc
const string X = "say \"hi\"" # value: say "hi"
const string X = 'don\'t' # value: don't
const string X = "C:\\Temp" # value: C:\Temp
const string X = "a\nb" # value: a, line feed, b
const string X = "a\tb" # value: a, tab, b
const string X = "a$b" # value: a$b
{noformat}
The generated constant was fed to the real toolchain wherever the
{{thrift:jammy}} build image has one. As a control, {{const string X =
"plain"}} compiled (or parsed) and kept its value in every one of them.
h3. Results
{noformat}
gen value generated code result
rs say "hi" pub const X: &str = "say "hi""; rustc 1.83:
expected one of `.`, `;`, `?`, `where`,
or an
operator, found `""`
rs C:\Temp pub const X: &str = "C:\Temp"; rustc: unknown
character escape: `T`
c_glib say "hi" #define ESC_X g_strdup ("say "hi"") gcc: expected
')' before 'hi'
c_glib C:\Temp #define ESC_X g_strdup ("C:\Temp") gcc: unknown
escape sequence: '\T' (a warning)
c_glib a<LF>b line break inside the string literal gcc: missing
terminating " character
lua don't X = 'don't' luac 5.4:
unfinished string
lua C:\Temp X = 'C:\Temp' luac: invalid
escape sequence
lua a<LF>b line break inside the string literal luac:
unfinished string
cl say "hi" (thrift:def-constant "X" "say "hi"") SBCL reads 4
arguments instead of 2
cl C:\Temp (thrift:def-constant "X" "C:\Temp") SBCL reads the
value as C:Temp
dart don't static final String X = 'don't'; dart 2.7:
String starting with ' must end with '.
dart a$b static final String X = 'a$b'; dart: Getter
not found: 'b'.
rb a<LF>b X = %q"a\nb" ruby: the
value is a\nb (backslash, n); same for \t
{noformat}
Not compiled, as the image has no Kotlin or Delphi compiler, but the output is
visibly invalid:
{noformat}
kotlin say "hi" const val X: kotlin.String = "say "hi""
kotlin C:\Temp const val X: kotlin.String = "C:\Temp" \T is not a
Kotlin escape sequence
kotlin a<LF>b line break inside the string literal
kotlin a$b const val X: kotlin.String = "a$b" $b is a string
template
delphi a<LF>b line break inside the string literal
{noformat}
Generators not listed here were not checked systematically. perl and php do
escape {{$}} (perl also {{@}}). The JavaScript generator had the same kind of
problem before, see THRIFT-2495 and THRIFT-2594.
_Drafted with AI assistance (Claude Opus 5); reviewed and filed by Jens Geyer._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)