mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
Fix two canonicalization crashes: try() and overflowed tuple indexes
This commit is contained in:
parent
7d464a2989
commit
e0ef01fa82
11 changed files with 114 additions and 16 deletions
|
@ -1003,7 +1003,16 @@ pub fn desugar_expr<'a>(
|
|||
},
|
||||
loc_args,
|
||||
) => {
|
||||
let result_expr = if loc_args.len() == 1 {
|
||||
let result_expr = if loc_args.is_empty() {
|
||||
env.problem(Problem::UnderAppliedTry {
|
||||
region: loc_expr.region,
|
||||
});
|
||||
// Replace with a dummy expression to avoid cascading errors
|
||||
env.arena.alloc(Loc {
|
||||
value: Record(Collection::empty()),
|
||||
region: loc_expr.region,
|
||||
})
|
||||
} else if loc_args.len() == 1 {
|
||||
desugar_expr(env, scope, loc_args.items[0])
|
||||
} else {
|
||||
let function = desugar_expr(env, scope, loc_args.items.first().unwrap());
|
||||
|
|
|
@ -1268,21 +1268,9 @@ pub fn canonicalize_expr<'a>(
|
|||
output,
|
||||
)
|
||||
}
|
||||
ast::Expr::AccessorFunction(field) => (
|
||||
RecordAccessor(StructAccessorData {
|
||||
name: scope.gen_unique_symbol(),
|
||||
function_var: var_store.fresh(),
|
||||
record_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
closure_var: var_store.fresh(),
|
||||
field_var: var_store.fresh(),
|
||||
field: match field {
|
||||
Accessor::RecordField(field) => IndexOrField::Field((*field).into()),
|
||||
Accessor::TupleIndex(index) => IndexOrField::Index(index.parse().unwrap()),
|
||||
},
|
||||
}),
|
||||
Output::default(),
|
||||
),
|
||||
ast::Expr::AccessorFunction(field) => {
|
||||
canonicalize_accessor_function(env, var_store, scope, field, region)
|
||||
}
|
||||
ast::Expr::TupleAccess(tuple_expr, field) => {
|
||||
let (loc_expr, output) = canonicalize_expr(env, var_store, scope, region, tuple_expr);
|
||||
|
||||
|
@ -1656,6 +1644,37 @@ pub fn canonicalize_expr<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
fn canonicalize_accessor_function(
|
||||
env: &mut Env<'_>,
|
||||
var_store: &mut VarStore,
|
||||
scope: &mut Scope,
|
||||
field: &Accessor<'_>,
|
||||
region: Region,
|
||||
) -> (Expr, Output) {
|
||||
(
|
||||
Expr::RecordAccessor(StructAccessorData {
|
||||
name: scope.gen_unique_symbol(),
|
||||
function_var: var_store.fresh(),
|
||||
record_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
closure_var: var_store.fresh(),
|
||||
field_var: var_store.fresh(),
|
||||
field: match field {
|
||||
Accessor::RecordField(field) => IndexOrField::Field((*field).into()),
|
||||
Accessor::TupleIndex(index) => match index.parse() {
|
||||
Ok(index) => IndexOrField::Index(index),
|
||||
Err(_) => {
|
||||
let error = roc_problem::can::RuntimeError::InvalidTupleIndex(region);
|
||||
env.problem(Problem::RuntimeError(error.clone()));
|
||||
return (Expr::RuntimeError(error), Output::default());
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
Output::default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn canonicalize_record<'a>(
|
||||
env: &mut Env<'a>,
|
||||
var_store: &mut VarStore,
|
||||
|
|
|
@ -225,6 +225,9 @@ pub enum Problem {
|
|||
OverAppliedDbg {
|
||||
region: Region,
|
||||
},
|
||||
UnderAppliedTry {
|
||||
region: Region,
|
||||
},
|
||||
FileProblem {
|
||||
filename: PathBuf,
|
||||
error: io::ErrorKind,
|
||||
|
@ -339,6 +342,7 @@ impl Problem {
|
|||
Problem::OverAppliedCrash { .. } => RuntimeError,
|
||||
Problem::UnappliedDbg { .. } => RuntimeError,
|
||||
Problem::OverAppliedDbg { .. } => RuntimeError,
|
||||
Problem::UnderAppliedTry { .. } => Warning,
|
||||
Problem::DefsOnlyUsedInRecursion(_, _) => Warning,
|
||||
Problem::FileProblem { .. } => Fatal,
|
||||
Problem::ReturnOutsideOfFunction { .. } => Warning,
|
||||
|
@ -467,6 +471,7 @@ impl Problem {
|
|||
| Problem::UnappliedCrash { region }
|
||||
| Problem::OverAppliedDbg { region }
|
||||
| Problem::UnappliedDbg { region }
|
||||
| Problem::UnderAppliedTry { region }
|
||||
| Problem::DefsOnlyUsedInRecursion(_, region)
|
||||
| Problem::ReturnOutsideOfFunction { region, .. }
|
||||
| Problem::StatementsAfterReturn { region }
|
||||
|
@ -681,6 +686,7 @@ pub enum RuntimeError {
|
|||
},
|
||||
|
||||
NonFunctionHostedAnnotation(Region),
|
||||
InvalidTupleIndex(Region),
|
||||
}
|
||||
|
||||
impl RuntimeError {
|
||||
|
@ -718,6 +724,7 @@ impl RuntimeError {
|
|||
| RuntimeError::InvalidFloat(_, region, _)
|
||||
| RuntimeError::InvalidInt(_, _, region, _)
|
||||
| RuntimeError::EmptySingleQuote(region)
|
||||
| RuntimeError::InvalidTupleIndex(region)
|
||||
| RuntimeError::MultipleCharsInSingleQuote(region)
|
||||
| RuntimeError::DegenerateBranch(region)
|
||||
| RuntimeError::InvalidInterpolation(region)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
try() t
|
|
@ -0,0 +1,18 @@
|
|||
@0-6 SpaceAfter(
|
||||
Apply(
|
||||
@0-5 PncApply(
|
||||
@0-3 Try,
|
||||
[],
|
||||
),
|
||||
[
|
||||
@5-6 Var {
|
||||
module_name: "",
|
||||
ident: "t",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
)
|
|
@ -0,0 +1 @@
|
|||
try()t
|
|
@ -0,0 +1 @@
|
|||
.18888888888888888888
|
|
@ -0,0 +1,10 @@
|
|||
@0-21 SpaceAfter(
|
||||
AccessorFunction(
|
||||
TupleIndex(
|
||||
"18888888888888888888",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
)
|
|
@ -0,0 +1 @@
|
|||
.18888888888888888888
|
|
@ -436,6 +436,7 @@ mod test_snapshots {
|
|||
pass/empty_record_newline_assign.expr,
|
||||
pass/empty_record_update.expr,
|
||||
pass/empty_string.expr,
|
||||
pass/empty_try_pnc.expr,
|
||||
pass/equals.expr,
|
||||
pass/equals_with_spaces.expr,
|
||||
pass/expect.expr,
|
||||
|
@ -485,6 +486,7 @@ mod test_snapshots {
|
|||
pass/int_with_underscore.expr,
|
||||
pass/lambda_in_chain.expr,
|
||||
pass/lambda_indent.expr,
|
||||
pass/large_tuple_index.expr,
|
||||
pass/list_closing_indent_not_enough.expr,
|
||||
pass/list_closing_same_indent_no_trailing_comma.expr,
|
||||
pass/list_closing_same_indent_with_trailing_comma.expr,
|
||||
|
|
|
@ -1338,6 +1338,21 @@ pub fn can_problem<'b>(
|
|||
]);
|
||||
title = "OVERAPPLIED DBG".to_string();
|
||||
}
|
||||
Problem::UnderAppliedTry { region } => {
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This "),
|
||||
alloc.keyword("try"),
|
||||
alloc.reflow(" has too few values given to it:"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region), severity),
|
||||
alloc.concat([
|
||||
alloc.keyword("try"),
|
||||
alloc.reflow(" must be given exactly one value to try."),
|
||||
]),
|
||||
]);
|
||||
title = "UNDERAPPLIED TRY".to_string();
|
||||
}
|
||||
Problem::FileProblem { filename, error } => {
|
||||
let report = to_file_problem_report(alloc, filename, error);
|
||||
doc = report.doc;
|
||||
|
@ -2480,6 +2495,20 @@ fn pretty_runtime_error<'b>(
|
|||
|
||||
title = NUMBER_UNDERFLOWS_SUFFIX;
|
||||
}
|
||||
RuntimeError::InvalidTupleIndex(region) => {
|
||||
doc = alloc.stack([
|
||||
alloc.concat([alloc
|
||||
.reflow("This tuple accessor index is invalid:")]),
|
||||
alloc.region(lines.convert_region(region), severity),
|
||||
alloc.tip().append(alloc.concat([
|
||||
alloc.reflow("Note that .2 in Roc is a function that extracts the tuple element at index 2 from its argument."),
|
||||
alloc.reflow("If you mean to create a floating point number, make sure it starts with a digit like 0.2"),
|
||||
alloc.reflow("."),
|
||||
])),
|
||||
]);
|
||||
|
||||
title = NUMBER_UNDERFLOWS_SUFFIX;
|
||||
}
|
||||
RuntimeError::InvalidOptionalValue {
|
||||
field_name,
|
||||
field_region,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue