From c43ff03e4175dafec0980fa96b2696475e38d881 Mon Sep 17 00:00:00 2001 From: Agustin Zubiaga Date: Mon, 15 May 2023 22:49:09 -0300 Subject: [PATCH] Prevent record builder field names from shadowing other symbols --- crates/compiler/can/src/operator.rs | 25 +++++++++++------ crates/compiler/can/tests/test_can.rs | 40 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/crates/compiler/can/src/operator.rs b/crates/compiler/can/src/operator.rs index 60b3d46786..65ec1d2d8a 100644 --- a/crates/compiler/can/src/operator.rs +++ b/crates/compiler/can/src/operator.rs @@ -503,11 +503,19 @@ fn record_builder_arg<'a>( RecordBuilderField::Value(label, spaces, expr) => { break AssignedField::RequiredValue(label, spaces, expr) } - RecordBuilderField::ApplyValue(label, _spaces, expr) => { + RecordBuilderField::ApplyValue(label, spaces, expr) => { apply_field_names.push(label); apply_exprs.push(expr); - break AssignedField::LabelOnly(label); + let var = arena.alloc(Loc { + region: label.region, + value: Expr::Var { + module_name: "", + ident: arena.alloc("#".to_owned() + label.value), + }, + }); + + break AssignedField::RequiredValue(label, spaces, var); } RecordBuilderField::LabelOnly(label) => break AssignedField::LabelOnly(label), RecordBuilderField::SpaceBefore(sub_field, _) => { @@ -537,16 +545,17 @@ fn record_builder_arg<'a>( // Construct the builder's closure // - // { x, y, z: 3 } - // \y -> { x, y, z: 3 } - // \x -> \y -> { x, y, z: 3 } + // { x: #x, y: #y, z: 3 } + // \#y -> { x: #x, y: #y, z: 3 } + // \#x -> \#y -> { x: #x, y: #y, z: 3 } - for name in apply_field_names.iter().rev() { - let ident = roc_parse::ast::Pattern::Identifier(name.value); + for label in apply_field_names.iter().rev() { + let name = arena.alloc("#".to_owned() + label.value); + let ident = roc_parse::ast::Pattern::Identifier(name); let arg_pattern = arena.alloc(Loc { value: ident, - region: name.region, + region: label.region, }); body = arena.alloc(Loc { diff --git a/crates/compiler/can/tests/test_can.rs b/crates/compiler/can/tests/test_can.rs index 1351af7e95..654c5088eb 100644 --- a/crates/compiler/can/tests/test_can.rs +++ b/crates/compiler/can/tests/test_can.rs @@ -773,6 +773,46 @@ mod test_can { } } + #[test] + fn record_builder_field_names_do_not_shadow() { + let src = indoc!( + r#" + succeed = \_ -> crash "succeed" + parse = \_ -> crash "parse" + + number = "42" + + succeed { + number <- parse number, + raw: number, + } + "# + ); + let arena = Bump::new(); + let out = can_expr_with(&arena, test_home(), src); + + assert_eq!(out.problems.len(), 0); + + let (_, number_to_succeed) = simplify_curried_call(&out.loc_expr.value); + let (_, number_closure) = simplify_curried_call(number_to_succeed); + let (apply_number_sym, record) = simplify_builder_closure(number_closure); + + match record { + Record { fields, .. } => { + assert_eq!(get_field_var_sym(fields, "number"), apply_number_sym); + + match get_field_expr(fields, "raw") { + Var(number_sym, _) => { + assert_ne!(number_sym.ident_id(), apply_number_sym.ident_id()); + assert_eq!(number_sym.as_str(&out.interns), "number") + } + expr => panic!("a is not a Num: {:?}", expr), + } + } + _ => panic!("Closure body wasn't a Record: {:?}", record), + } + } + #[test] fn multiple_record_builders_error() { let src = indoc!(