fix(codegen): string escape

This commit is contained in:
2026-01-12 17:44:19 +08:00
parent 3b6804dde6
commit a8683e720b

View File

@@ -11,10 +11,15 @@ pub(crate) trait CodegenContext {
fn get_sym(&self, id: SymId) -> &str;
}
fn escape_quote_string(s: &str) -> String {
let mut escaped = String::with_capacity(s.len() + 2);
trait EscapeQuote {
fn escape_quote(&self) -> String;
}
impl EscapeQuote for str {
fn escape_quote(&self) -> String {
let mut escaped = String::with_capacity(self.len() + 2);
escaped.push('"');
for c in s.chars() {
for c in self.chars() {
match c {
'\\' => escaped.push_str("\\\\"),
'\"' => escaped.push_str("\\\""),
@@ -27,13 +32,14 @@ fn escape_quote_string(s: &str) -> String {
escaped.push('"');
escaped
}
}
impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
fn compile(&self, ctx: &Ctx) -> String {
match self {
Ir::Int(int) => format!("{int}n"), // Generate BigInt literal
Ir::Float(float) => float.to_string(),
Ir::Str(s) => escape_quote_string(&s.val),
Ir::Str(s) => s.val.escape_quote(),
Ir::Path(p) => {
// Path needs runtime resolution for interpolated paths
let path_expr = ctx.get_ir(p.expr).compile(ctx);
@@ -62,7 +68,9 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
format!("expr{}", expr_id.0)
}
Ir::Builtins(_) => "Nix.builtins".to_string(),
&Ir::Builtin(Builtin(name)) => format!("Nix.builtins[\"{}\"]", ctx.get_sym(name)),
&Ir::Builtin(Builtin(name)) => {
format!("Nix.builtins[{}]", ctx.get_sym(name).escape_quote())
}
Ir::ConcatStrings(x) => x.compile(ctx),
Ir::HasAttr(x) => x.compile(ctx),
&Ir::Assert(Assert { assertion, expr }) => {
@@ -145,7 +153,7 @@ impl Func {
let required = if let Some(req) = &self.param.required {
let keys: Vec<_> = req
.iter()
.map(|&sym| format!("\"{}\"", ctx.get_sym(sym)))
.map(|&sym| ctx.get_sym(sym).escape_quote())
.collect();
format!("[{}]", keys.join(","))
} else {
@@ -156,7 +164,7 @@ impl Func {
let allowed = if let Some(allow) = &self.param.allowed {
let keys: Vec<_> = allow
.iter()
.map(|&sym| format!("\"{}\"", ctx.get_sym(sym)))
.map(|&sym| ctx.get_sym(sym).escape_quote())
.collect();
format!("[{}]", keys.join(","))
} else {
@@ -226,17 +234,17 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Select {
let is_last = i == attr_count - 1;
result = match attr {
Attr::Str(sym) => {
let key = ctx.get_sym(*sym);
let key = ctx.get_sym(*sym).escape_quote();
if let Some(default) = self.default
&& is_last
{
let default_val = ctx.get_ir(default).compile(ctx);
format!(
"Nix.selectWithDefault({}, \"{}\", {})",
"Nix.selectWithDefault({}, {}, {})",
result, key, default_val
)
} else {
format!("Nix.select({}, \"{}\")", result, key)
format!("Nix.select({}, {})", result, key)
}
}
Attr::Dynamic(expr_id) => {
@@ -267,7 +275,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
for (&sym, &expr) in &self.stcs {
let key = ctx.get_sym(sym);
let value = ctx.get_ir(expr).compile(ctx);
attrs.push(format!("{}: {}", escape_quote_string(key), value));
attrs.push(format!("{}: {}", key.escape_quote(), value));
}
for (key_expr, value_expr) in &self.dyns {
@@ -310,9 +318,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for HasAttr {
.rhs
.iter()
.map(|attr| match attr {
Attr::Str(sym) => {
format!("\"{}\"", ctx.get_sym(*sym))
}
Attr::Str(sym) => ctx.get_sym(*sym).escape_quote(),
Attr::Dynamic(expr_id) => ctx.get_ir(*expr_id).compile(ctx),
})
.join(",");