Prevent record builder field names from shadowing other symbols

This commit is contained in:
Agustin Zubiaga 2023-05-15 22:49:09 -03:00
parent 232f5fd84c
commit c43ff03e41
No known key found for this signature in database
2 changed files with 57 additions and 8 deletions

View file

@ -503,11 +503,19 @@ fn record_builder_arg<'a>(
RecordBuilderField::Value(label, spaces, expr) => { RecordBuilderField::Value(label, spaces, expr) => {
break AssignedField::RequiredValue(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_field_names.push(label);
apply_exprs.push(expr); 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::LabelOnly(label) => break AssignedField::LabelOnly(label),
RecordBuilderField::SpaceBefore(sub_field, _) => { RecordBuilderField::SpaceBefore(sub_field, _) => {
@ -537,16 +545,17 @@ fn record_builder_arg<'a>(
// Construct the builder's closure // Construct the builder's closure
// //
// { x, y, z: 3 } // { x: #x, y: #y, z: 3 }
// \y -> { x, y, z: 3 } // \#y -> { x: #x, y: #y, z: 3 }
// \x -> \y -> { x, y, z: 3 } // \#x -> \#y -> { x: #x, y: #y, z: 3 }
for name in apply_field_names.iter().rev() { for label in apply_field_names.iter().rev() {
let ident = roc_parse::ast::Pattern::Identifier(name.value); let name = arena.alloc("#".to_owned() + label.value);
let ident = roc_parse::ast::Pattern::Identifier(name);
let arg_pattern = arena.alloc(Loc { let arg_pattern = arena.alloc(Loc {
value: ident, value: ident,
region: name.region, region: label.region,
}); });
body = arena.alloc(Loc { body = arena.alloc(Loc {

View file

@ -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] #[test]
fn multiple_record_builders_error() { fn multiple_record_builders_error() {
let src = indoc!( let src = indoc!(