fix: copy path to store in concatStringsWithContext
This commit is contained in:
@@ -84,23 +84,20 @@ export const withContext = <T>(message: string, span: string, fn: () => T): T =>
|
||||
* - Path mode: Store contexts are forbidden (will throw error)
|
||||
* - String mode: All contexts are preserved and merged
|
||||
*
|
||||
* If first element is a path, result is a path (with constraint: no store context allowed)
|
||||
*
|
||||
* @param parts - Array of values to concatenate
|
||||
* @param forceString - If true, result is always a string (paths are copied to store)
|
||||
* @returns String or Path with merged contexts from all parts
|
||||
*/
|
||||
export const concatStringsWithContext = (parts: NixValue[]): NixString | NixPath => {
|
||||
export const concatStringsWithContext = (parts: NixValue[], forceString: boolean): NixString | NixPath => {
|
||||
if (parts.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const forced = parts.map(force);
|
||||
|
||||
// Check if first element is a path
|
||||
const firstIsPath = isNixPath(forced[0]);
|
||||
const firstIsPath = !forceString && isNixPath(forced[0]);
|
||||
|
||||
if (firstIsPath) {
|
||||
// Path concatenation mode: result will be a path
|
||||
let result = (forced[0] as NixPath).value;
|
||||
|
||||
for (let i = 1; i < forced.length; i++) {
|
||||
@@ -111,13 +108,11 @@ export const concatStringsWithContext = (parts: NixValue[]): NixString | NixPath
|
||||
} else if (typeof part === "string") {
|
||||
result += part;
|
||||
} else if (isStringWithContext(part)) {
|
||||
// Lix constraint: cannot mix store context with paths
|
||||
if (part.context.size > 0) {
|
||||
throw new TypeError("a string that refers to a store path cannot be appended to a path");
|
||||
}
|
||||
result += part.value;
|
||||
} else {
|
||||
// Coerce to string
|
||||
const tempContext: NixStringContext = new Set();
|
||||
const coerced = coerceToString(part, StringCoercionMode.Interpolation, false, tempContext);
|
||||
|
||||
@@ -132,19 +127,15 @@ export const concatStringsWithContext = (parts: NixValue[]): NixString | NixPath
|
||||
return mkPath(result);
|
||||
}
|
||||
|
||||
// String concatenation mode
|
||||
// Note: firstIsPath is already false at this point, otherwise we would have
|
||||
// returned in the path concatenation branch above
|
||||
const context: NixStringContext = new Set();
|
||||
const strParts: string[] = [];
|
||||
|
||||
for (const part of parts) {
|
||||
const forced = force(part);
|
||||
if (isNixPath(forced)) {
|
||||
const str = coerceToString(forced, StringCoercionMode.Interpolation, true, context);
|
||||
for (const part of forced) {
|
||||
if (isNixPath(part)) {
|
||||
const str = coerceToString(part, StringCoercionMode.Interpolation, true, context);
|
||||
strParts.push(str);
|
||||
} else {
|
||||
const str = coerceToString(forced, StringCoercionMode.Interpolation, false, context);
|
||||
const str = coerceToString(part, StringCoercionMode.Interpolation, false, context);
|
||||
strParts.push(str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +165,10 @@ export const parseContextToInfoMap = (context: NixStringContext): Map<string, Pa
|
||||
}
|
||||
}
|
||||
|
||||
for (const info of result.values()) {
|
||||
info.outputs.sort();
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
@@ -141,6 +141,12 @@ impl<Ctx: CodegenContext> Compile<Ctx> for usize {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for bool {
|
||||
fn compile(&self, _ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
let _ = write!(buf, "{self}");
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for Quoted<'_> {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
code!(buf, ctx; "\"" escaped(self.0) "\"")
|
||||
@@ -616,7 +622,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for ConcatStrings {
|
||||
"Nix.withContext(\"while evaluating a path segment\"," part.span() ",()=>(" part "))"
|
||||
);
|
||||
})
|
||||
"])"
|
||||
"]," self.force_string ")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ ir! {
|
||||
If { pub cond: ExprId, pub consq: ExprId, pub alter: ExprId },
|
||||
Call { pub func: ExprId, pub arg: ExprId },
|
||||
Assert { pub assertion: ExprId, pub expr: ExprId, pub assertion_raw: String },
|
||||
ConcatStrings { pub parts: Vec<ExprId> },
|
||||
ConcatStrings { pub parts: Vec<ExprId>, pub force_string: bool },
|
||||
Path { pub expr: ExprId },
|
||||
Func { pub body: ExprId, pub param: Option<Param>, pub arg: ExprId, pub thunks: Vec<(ExprId, ExprId)> },
|
||||
TopLevel { pub body: ExprId, pub thunks: Vec<(ExprId, ExprId)> },
|
||||
|
||||
@@ -139,7 +139,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Path {
|
||||
part
|
||||
}
|
||||
} else {
|
||||
ctx.new_expr(ConcatStrings { parts, span }.to_ir())
|
||||
ctx.new_expr(ConcatStrings { parts, span, force_string: false }.to_ir())
|
||||
};
|
||||
Ok(ctx.new_expr(Path { expr, span }.to_ir()))
|
||||
}
|
||||
@@ -166,7 +166,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Str {
|
||||
Ok(if is_single_literal {
|
||||
parts.into_iter().next().unwrap()
|
||||
} else {
|
||||
ctx.new_expr(ConcatStrings { parts, span }.to_ir())
|
||||
ctx.new_expr(ConcatStrings { parts, span, force_string: true }.to_ir())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,7 +442,7 @@ pub fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Resul
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
Ok(Attr::Dynamic(
|
||||
ctx.new_expr(ConcatStrings { parts, span }.to_ir()),
|
||||
ctx.new_expr(ConcatStrings { parts, span, force_string: true }.to_ir()),
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user