diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 1b9fd0e520..05e99ca419 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -220,8 +220,16 @@ pub fn canonicalize_expr<'a>( (answer, output) } - Err(CanonicalizeFieldProblem::InvalidOptionalValue) => ( - Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalRecord), + Err(CanonicalizeRecordProblem::InvalidOptionalValue { + field_name, + field_region, + record_region, + }) => ( + Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue { + field_name, + field_region, + record_region, + }), Output::default(), ), } @@ -254,8 +262,16 @@ pub fn canonicalize_expr<'a>( }, output, ), - Err(CanonicalizeFieldProblem::InvalidOptionalValue) => ( - Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalRecord), + Err(CanonicalizeRecordProblem::InvalidOptionalValue { + field_name, + field_region, + record_region, + }) => ( + Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue { + field_name, + field_region, + record_region, + }), Output::default(), ), } @@ -979,13 +995,20 @@ where } } +enum CanonicalizeRecordProblem { + InvalidOptionalValue { + field_name: Lowercase, + field_region: Region, + record_region: Region, + }, +} fn canonicalize_fields<'a>( env: &mut Env<'a>, var_store: &mut VarStore, scope: &mut Scope, region: Region, fields: &'a [Located>>], -) -> Result<(SendMap, Output), CanonicalizeFieldProblem> { +) -> Result<(SendMap, Output), CanonicalizeRecordProblem> { let mut can_fields = SendMap::default(); let mut output = Output::default(); @@ -1011,9 +1034,20 @@ fn canonicalize_fields<'a>( output.references = output.references.union(field_out.references); } - Err(invalid_optional_value @ CanonicalizeFieldProblem::InvalidOptionalValue) => { - env.problems.push(Problem::InvalidOptionalRecord); - return Err(invalid_optional_value); + Err(CanonicalizeFieldProblem::InvalidOptionalValue { + field_name, + field_region, + }) => { + env.problems.push(Problem::InvalidOptionalValue { + field_name: field_name.clone(), + field_region, + record_region: region, + }); + return Err(CanonicalizeRecordProblem::InvalidOptionalValue { + field_name, + field_region, + record_region: region, + }); } } } @@ -1022,9 +1056,11 @@ fn canonicalize_fields<'a>( } enum CanonicalizeFieldProblem { - InvalidOptionalValue, + InvalidOptionalValue { + field_name: Lowercase, + field_region: Region, + }, } - fn canonicalize_field<'a>( env: &mut Env<'a>, var_store: &mut VarStore, @@ -1049,7 +1085,10 @@ fn canonicalize_field<'a>( )) } - OptionalValue(_, _, _) => Err(CanonicalizeFieldProblem::InvalidOptionalValue), + OptionalValue(label, _, loc_expr) => Err(CanonicalizeFieldProblem::InvalidOptionalValue { + field_name: Lowercase::from(label.value), + field_region: loc_expr.region, + }), // A label with no value, e.g. `{ name }` (this is sugar for { name: name }) LabelOnly(_) => { diff --git a/compiler/problem/src/can.rs b/compiler/problem/src/can.rs index 775948ced6..0a9ec9a263 100644 --- a/compiler/problem/src/can.rs +++ b/compiler/problem/src/can.rs @@ -40,7 +40,11 @@ pub enum Problem { field_region: Region, replaced_region: Region, }, - InvalidOptionalRecord, + InvalidOptionalValue { + field_name: Lowercase, + record_region: Region, + field_region: Region, + }, DuplicateTag { tag_name: TagName, @@ -103,7 +107,11 @@ pub enum RuntimeError { original_region: Region, shadow: Located, }, - InvalidOptionalRecord, + InvalidOptionalValue { + field_name: Lowercase, + record_region: Region, + field_region: Region, + }, // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments! UnsupportedPattern(Region), // Example: when 1 is 1.X -> 32 diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index 76f0317e2f..e7ea8bf3f0 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -179,8 +179,23 @@ pub fn can_problem<'b>( alloc.reflow(" definitions from this record."), ]), ]), - Problem::InvalidOptionalRecord => alloc.stack(vec![ - alloc.reflow("This record contains optional values, it shouldn't!") + Problem::InvalidOptionalValue { + field_name, + field_region, + record_region, + } => alloc.stack(vec![ + alloc.concat(vec![ + alloc.reflow("This record uses an optional value for the "), + alloc.record_field(field_name.clone()), + alloc.reflow(" field in an incorrect context!"), + ]), + alloc.region_all_the_things( + record_region, + field_region, + field_region, + Annotation::Error, + ), + alloc.reflow("You can only use optinal values in record destructuring."), ]), Problem::DuplicateRecordFieldType { field_name, @@ -537,9 +552,24 @@ fn pretty_runtime_error<'b>( tip, ]) } - RuntimeError::InvalidOptionalRecord => { - alloc.reflow("There is an inappropriate optional field in a record") - } + RuntimeError::InvalidOptionalValue { + field_name, + field_region, + record_region, + } => alloc.stack(vec![ + alloc.concat(vec![ + alloc.reflow("This record uses an optional value for the "), + alloc.record_field(field_name.clone()), + alloc.reflow(" field in an incorrect context!"), + ]), + alloc.region_all_the_things( + record_region, + field_region, + field_region, + Annotation::Error, + ), + alloc.reflow("You can only use optinal values in record destructuring."), + ]), RuntimeError::InvalidRecordUpdate { region } => alloc.stack(vec![ alloc.concat(vec![ alloc.reflow("This expression cannot be updated"),