@@ -383,6 +383,34 @@ impl Expr {
383383
384384/// Used for merging sets during parsing. For example:
385385/// { a.b = 1; a.c = 2; } => { a = { b = 1; c = 2; }; }
386+ ///
387+ /// Nix only allows merging several such KeyValuePairs when they correspond
388+ /// to bare literals. Inserting a mere indirection through let or a function trips
389+ /// it of:
390+ /// ```text
391+ /// nix-repl> :p { a = { b = 1; }; a.c = 2; }
392+ /// { a = { b = 1; c = 2; }; }
393+ ///
394+ /// nix-repl> :p { a = (x: x){ b = 1; }; a.c = 2; }
395+ /// error: attribute 'a.c' already defined at (string):1:3
396+ ///
397+ /// at «string»:1:25:
398+ ///
399+ /// 1| { a = (x: x){ b = 1; }; a.c = 2; }
400+ /// | ^
401+ ///
402+ /// nix-repl> :p let y = { b = 1; }; in { a = y; a.c = 2; }
403+ /// error: attribute 'a.c' already defined at (string):1:26
404+ ///
405+ /// at «string»:1:33:
406+ ///
407+ /// 1| let y = { b = 1; }; in { a = y; a.c = 2; }
408+ /// | ^
409+ ///
410+ /// ```
411+ /// This function takes a and b, attempts to interpret them as litteral
412+ /// key value pairs or literal attrset values, and if it failed returns
413+ /// such an error.
386414pub fn merge_set_literal ( name : String , a : Gc < Expr > , b : Gc < Expr > ) -> Result < Gc < Expr > , EvalError > {
387415 // evaluate literal attr sets only, otherwise error
388416 let eval_literal = |src : Gc < Expr > | {
@@ -391,18 +419,25 @@ pub fn merge_set_literal(name: String, a: Gc<Expr>, b: Gc<Expr>) -> Result<Gc<Ex
391419 } else {
392420 src
393421 } ;
394- if let ExprSource :: AttrSet { .. } = & src. source {
395- src. eval ( ) ?. as_map ( )
396- } else {
397- // We cannot merge a literal with a non-literal. This error is
398- // caused by incorrect expressions such as:
399- // ```
400- // repl> let x = { y = 1; }; in { a = x; a.z = 2; }
401- // error: attribute 'a.z' at (string):1:33 already defined at (string):1:26
402- // ```
403- // The above would be caught because `x` is an ExprSource::Ident (as
404- // opposed to being an ExprSource::AttrSet literal).
405- Err ( EvalError :: Value ( ValueError :: AttrAlreadyDefined ( name. to_string ( ) ) ) )
422+ match & src. source {
423+ ExprSource :: AttrSet { .. } => src. eval ( ) ?. as_map ( ) ,
424+ ExprSource :: Literal {
425+ value : NixValue :: Map ( m) ,
426+ ..
427+ } => Ok ( m. clone ( ) ) ,
428+ _ => {
429+ // We cannot merge a literal with a non-literal. This error is
430+ // caused by incorrect expressions such as:
431+ // ```
432+ // repl> let x = { y = 1; }; in { a = x; a.z = 2; }
433+ // error: attribute 'a.z' at (string):1:33 already defined at (string):1:26
434+ // ```
435+ // The above would be caught because `x` is an ExprSource::Ident (as
436+ // opposed to being an ExprSource::AttrSet literal).
437+ Err ( EvalError :: Value ( ValueError :: AttrAlreadyDefined (
438+ name. to_string ( ) ,
439+ ) ) )
440+ }
406441 }
407442 } ;
408443
0 commit comments