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