mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 07:49:17 +03:00
Merge pull request #1028 from rtfeldman/parse-if-list
Improved error messages for list & if
This commit is contained in:
commit
1222c9aeaa
@ -230,9 +230,8 @@ mod cli_run {
|
||||
#[test]
|
||||
#[serial(astar)]
|
||||
fn run_astar_optimized_1() {
|
||||
check_output_with_stdin(
|
||||
check_output(
|
||||
&example_file("benchmarks", "AStarTests.roc"),
|
||||
"1",
|
||||
"astar-tests",
|
||||
&[],
|
||||
"True\n",
|
||||
|
@ -674,32 +674,43 @@ pub fn canonicalize_expr<'a>(
|
||||
Output::default(),
|
||||
)
|
||||
}
|
||||
ast::Expr::If(cond, then_branch, else_branch) => {
|
||||
let (loc_cond, mut output) =
|
||||
canonicalize_expr(env, var_store, scope, cond.region, &cond.value);
|
||||
let (loc_then, then_output) = canonicalize_expr(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
then_branch.region,
|
||||
&then_branch.value,
|
||||
);
|
||||
ast::Expr::If(if_thens, final_else_branch) => {
|
||||
let mut branches = Vec::with_capacity(1);
|
||||
let mut output = Output::default();
|
||||
|
||||
for (condition, then_branch) in if_thens.iter() {
|
||||
let (loc_cond, cond_output) =
|
||||
canonicalize_expr(env, var_store, scope, condition.region, &condition.value);
|
||||
|
||||
let (loc_then, then_output) = canonicalize_expr(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
then_branch.region,
|
||||
&then_branch.value,
|
||||
);
|
||||
|
||||
branches.push((loc_cond, loc_then));
|
||||
|
||||
output.references = output.references.union(cond_output.references);
|
||||
output.references = output.references.union(then_output.references);
|
||||
}
|
||||
|
||||
let (loc_else, else_output) = canonicalize_expr(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
else_branch.region,
|
||||
&else_branch.value,
|
||||
final_else_branch.region,
|
||||
&final_else_branch.value,
|
||||
);
|
||||
|
||||
output.references = output.references.union(then_output.references);
|
||||
output.references = output.references.union(else_output.references);
|
||||
|
||||
(
|
||||
If {
|
||||
cond_var: var_store.fresh(),
|
||||
branch_var: var_store.fresh(),
|
||||
branches: vec![(loc_cond, loc_then)],
|
||||
branches,
|
||||
final_else: Box::new(loc_else),
|
||||
},
|
||||
output,
|
||||
|
@ -290,16 +290,21 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
||||
}),
|
||||
)
|
||||
}
|
||||
If(condition, then_branch, else_branch)
|
||||
| Nested(If(condition, then_branch, else_branch)) => {
|
||||
// If does not get desugared yet so we can give more targetted error messages during
|
||||
// type checking.
|
||||
let desugared_cond = &*arena.alloc(desugar_expr(arena, &condition));
|
||||
let desugared_then = &*arena.alloc(desugar_expr(arena, &then_branch));
|
||||
let desugared_else = &*arena.alloc(desugar_expr(arena, &else_branch));
|
||||
If(if_thens, final_else_branch) | Nested(If(if_thens, final_else_branch)) => {
|
||||
// If does not get desugared into `when` so we can give more targetted error messages during type checking.
|
||||
let desugared_final_else = &*arena.alloc(desugar_expr(arena, &final_else_branch));
|
||||
|
||||
let mut desugared_if_thens = Vec::with_capacity_in(if_thens.len(), arena);
|
||||
|
||||
for (condition, then_branch) in if_thens.iter() {
|
||||
desugared_if_thens.push((
|
||||
desugar_expr(arena, condition).clone(),
|
||||
desugar_expr(arena, then_branch).clone(),
|
||||
));
|
||||
}
|
||||
|
||||
arena.alloc(Located {
|
||||
value: If(desugared_cond, desugared_then, desugared_else),
|
||||
value: If(desugared_if_thens.into_bump_slice(), desugared_final_else),
|
||||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
|
@ -58,8 +58,11 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||
loc_expr.is_multiline() || args.iter().any(|loc_arg| loc_arg.is_multiline())
|
||||
}
|
||||
|
||||
If(loc_cond, loc_if_true, loc_if_false) => {
|
||||
loc_cond.is_multiline() || loc_if_true.is_multiline() || loc_if_false.is_multiline()
|
||||
If(branches, final_else) => {
|
||||
final_else.is_multiline()
|
||||
|| branches
|
||||
.iter()
|
||||
.any(|(c, t)| c.is_multiline() || t.is_multiline())
|
||||
}
|
||||
|
||||
BinOp((loc_left, _, loc_right)) => {
|
||||
@ -257,8 +260,8 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||
// still print the return value.
|
||||
ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||
}
|
||||
If(loc_condition, loc_then, loc_else) => {
|
||||
fmt_if(buf, loc_condition, loc_then, loc_else, indent);
|
||||
If(branches, final_else) => {
|
||||
fmt_if(buf, branches, final_else, self.is_multiline(), indent);
|
||||
}
|
||||
When(loc_condition, branches) => fmt_when(buf, loc_condition, branches, indent),
|
||||
List {
|
||||
@ -629,15 +632,15 @@ fn fmt_when<'a>(
|
||||
|
||||
fn fmt_if<'a>(
|
||||
buf: &mut String<'a>,
|
||||
loc_condition: &'a Located<Expr<'a>>,
|
||||
loc_then: &'a Located<Expr<'a>>,
|
||||
loc_else: &'a Located<Expr<'a>>,
|
||||
branches: &'a [(Located<Expr<'a>>, Located<Expr<'a>>)],
|
||||
final_else: &'a Located<Expr<'a>>,
|
||||
is_multiline: bool,
|
||||
indent: u16,
|
||||
) {
|
||||
let is_multiline_then = loc_then.is_multiline();
|
||||
let is_multiline_else = loc_else.is_multiline();
|
||||
let is_multiline_condition = loc_condition.is_multiline();
|
||||
let is_multiline = is_multiline_then || is_multiline_else || is_multiline_condition;
|
||||
// let is_multiline_then = loc_then.is_multiline();
|
||||
// let is_multiline_else = final_else.is_multiline();
|
||||
// let is_multiline_condition = loc_condition.is_multiline();
|
||||
// let is_multiline = is_multiline_then || is_multiline_else || is_multiline_condition;
|
||||
|
||||
let return_indent = if is_multiline {
|
||||
indent + INDENT
|
||||
@ -645,80 +648,89 @@ fn fmt_if<'a>(
|
||||
indent
|
||||
};
|
||||
|
||||
buf.push_str("if");
|
||||
for (loc_condition, loc_then) in branches.iter() {
|
||||
let is_multiline_condition = loc_condition.is_multiline();
|
||||
|
||||
if is_multiline_condition {
|
||||
match &loc_condition.value {
|
||||
Expr::SpaceBefore(expr_below, spaces_above_expr) => {
|
||||
fmt_comments_only(buf, spaces_above_expr.iter(), NewlineAt::Top, return_indent);
|
||||
newline(buf, return_indent);
|
||||
buf.push_str("if");
|
||||
|
||||
match &expr_below {
|
||||
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
||||
expr_above.format(buf, return_indent);
|
||||
fmt_comments_only(
|
||||
buf,
|
||||
spaces_below_expr.iter(),
|
||||
NewlineAt::Top,
|
||||
return_indent,
|
||||
);
|
||||
newline(buf, indent);
|
||||
}
|
||||
if is_multiline_condition {
|
||||
match &loc_condition.value {
|
||||
Expr::SpaceBefore(expr_below, spaces_above_expr) => {
|
||||
fmt_comments_only(buf, spaces_above_expr.iter(), NewlineAt::Top, return_indent);
|
||||
newline(buf, return_indent);
|
||||
|
||||
_ => {
|
||||
expr_below.format(buf, return_indent);
|
||||
match &expr_below {
|
||||
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
||||
expr_above.format(buf, return_indent);
|
||||
fmt_comments_only(
|
||||
buf,
|
||||
spaces_below_expr.iter(),
|
||||
NewlineAt::Top,
|
||||
return_indent,
|
||||
);
|
||||
newline(buf, indent);
|
||||
}
|
||||
|
||||
_ => {
|
||||
expr_below.format(buf, return_indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
||||
newline(buf, return_indent);
|
||||
expr_above.format(buf, return_indent);
|
||||
fmt_comments_only(buf, spaces_below_expr.iter(), NewlineAt::Top, return_indent);
|
||||
newline(buf, indent);
|
||||
}
|
||||
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
|
||||
newline(buf, return_indent);
|
||||
expr_above.format(buf, return_indent);
|
||||
fmt_comments_only(buf, spaces_below_expr.iter(), NewlineAt::Top, return_indent);
|
||||
newline(buf, indent);
|
||||
}
|
||||
|
||||
_ => {
|
||||
newline(buf, return_indent);
|
||||
loc_condition.format(buf, return_indent);
|
||||
newline(buf, indent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buf.push(' ');
|
||||
loc_condition.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||
buf.push(' ');
|
||||
}
|
||||
|
||||
buf.push_str("then");
|
||||
|
||||
if is_multiline {
|
||||
match &loc_then.value {
|
||||
Expr::SpaceBefore(expr_below, spaces_below) => {
|
||||
// we want exactly one newline, user-inserted extra newlines are ignored.
|
||||
newline(buf, return_indent);
|
||||
fmt_comments_only(buf, spaces_below.iter(), NewlineAt::Bottom, return_indent);
|
||||
|
||||
match &expr_below {
|
||||
Expr::SpaceAfter(expr_above, spaces_above) => {
|
||||
expr_above.format(buf, return_indent);
|
||||
|
||||
fmt_comments_only(buf, spaces_above.iter(), NewlineAt::Top, return_indent);
|
||||
newline(buf, indent);
|
||||
}
|
||||
|
||||
_ => {
|
||||
expr_below.format(buf, return_indent);
|
||||
}
|
||||
_ => {
|
||||
newline(buf, return_indent);
|
||||
loc_condition.format(buf, return_indent);
|
||||
newline(buf, indent);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
loc_condition.format(buf, return_indent);
|
||||
}
|
||||
} else {
|
||||
buf.push(' ');
|
||||
loc_condition.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||
buf.push(' ');
|
||||
}
|
||||
|
||||
buf.push_str("then");
|
||||
|
||||
if is_multiline {
|
||||
match &loc_then.value {
|
||||
Expr::SpaceBefore(expr_below, spaces_below) => {
|
||||
// we want exactly one newline, user-inserted extra newlines are ignored.
|
||||
newline(buf, return_indent);
|
||||
fmt_comments_only(buf, spaces_below.iter(), NewlineAt::Bottom, return_indent);
|
||||
|
||||
match &expr_below {
|
||||
Expr::SpaceAfter(expr_above, spaces_above) => {
|
||||
expr_above.format(buf, return_indent);
|
||||
|
||||
fmt_comments_only(
|
||||
buf,
|
||||
spaces_above.iter(),
|
||||
NewlineAt::Top,
|
||||
return_indent,
|
||||
);
|
||||
newline(buf, indent);
|
||||
}
|
||||
|
||||
_ => {
|
||||
expr_below.format(buf, return_indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
loc_condition.format(buf, return_indent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buf.push_str(" ");
|
||||
loc_then.format(buf, return_indent);
|
||||
}
|
||||
} else {
|
||||
buf.push_str(" ");
|
||||
loc_then.format(buf, return_indent);
|
||||
}
|
||||
|
||||
if is_multiline {
|
||||
@ -728,7 +740,7 @@ fn fmt_if<'a>(
|
||||
buf.push_str(" else ");
|
||||
}
|
||||
|
||||
loc_else.format(buf, return_indent);
|
||||
final_else.format(buf, return_indent);
|
||||
}
|
||||
|
||||
pub fn fmt_closure<'a>(
|
||||
|
@ -127,7 +127,7 @@ pub enum Expr<'a> {
|
||||
UnaryOp(&'a Loc<Expr<'a>>, Loc<UnaryOp>),
|
||||
|
||||
// Conditionals
|
||||
If(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||
If(&'a [(Loc<Expr<'a>>, Loc<Expr<'a>>)], &'a Loc<Expr<'a>>),
|
||||
When(
|
||||
/// The condition
|
||||
&'a Loc<Expr<'a>>,
|
||||
|
@ -60,11 +60,12 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
pub fn space0_around_e<'a, P, S, E>(
|
||||
pub fn space0_around_ee<'a, P, S, E>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
space_problem: fn(BadInputError, Row, Col) -> E,
|
||||
indent_problem: fn(Row, Col) -> E,
|
||||
indent_before_problem: fn(Row, Col) -> E,
|
||||
indent_after_problem: fn(Row, Col) -> E,
|
||||
) -> impl Parser<'a, Located<S>, E>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
@ -75,8 +76,11 @@ where
|
||||
{
|
||||
parser::map_with_arena(
|
||||
and(
|
||||
space0_e(min_indent, space_problem, indent_problem),
|
||||
and(parser, space0_e(min_indent, space_problem, indent_problem)),
|
||||
space0_e(min_indent, space_problem, indent_before_problem),
|
||||
and(
|
||||
parser,
|
||||
space0_e(min_indent, space_problem, indent_after_problem),
|
||||
),
|
||||
),
|
||||
move |arena: &'a Bump,
|
||||
tuples: (
|
||||
|
@ -2,8 +2,8 @@ use crate::ast::{
|
||||
AssignedField, Attempting, CommentOrNewline, Def, Expr, Pattern, Spaceable, TypeAnnotation,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
line_comment, space0, space0_after, space0_after_e, space0_around, space0_around_e,
|
||||
space0_before, space0_before_e, space0_e, space1, space1_around, space1_before, spaces_exactly,
|
||||
line_comment, space0, space0_after, space0_after_e, space0_around, space0_around_ee,
|
||||
space0_before, space0_before_e, space0_e, space1, space1_before, spaces_exactly,
|
||||
};
|
||||
use crate::ident::{global_tag_or_ident, ident, lowercase_ident, Ident};
|
||||
use crate::keyword;
|
||||
@ -11,8 +11,8 @@ use crate::number_literal::number_literal;
|
||||
use crate::parser::{
|
||||
self, allocated, and_then_with_indent_level, ascii_char, ascii_string, attempt, backtrackable,
|
||||
fail, map, newline_char, not, not_followed_by, optional, sep_by1, specialize, specialize_ref,
|
||||
then, unexpected, unexpected_eof, word1, word2, EExpr, Either, ParseResult, Parser, State,
|
||||
SyntaxError, When,
|
||||
then, unexpected, unexpected_eof, word1, word2, BadInputError, EExpr, Either, If, List,
|
||||
ParseResult, Parser, State, SyntaxError, When,
|
||||
};
|
||||
use crate::pattern::loc_closure_param;
|
||||
use crate::type_annotation;
|
||||
@ -324,7 +324,7 @@ pub fn expr_to_pattern<'a>(
|
||||
| Expr::Closure(_, _)
|
||||
| Expr::BinOp(_)
|
||||
| Expr::Defs(_, _)
|
||||
| Expr::If(_, _, _)
|
||||
| Expr::If(_, _)
|
||||
| Expr::When(_, _)
|
||||
| Expr::MalformedClosure
|
||||
| Expr::PrecedenceConflict(_, _, _, _)
|
||||
@ -1029,14 +1029,15 @@ mod when {
|
||||
and!(
|
||||
when_with_indent(),
|
||||
skip_second!(
|
||||
space0_around_e(
|
||||
space0_around_ee(
|
||||
loc!(specialize_ref(
|
||||
When::Syntax,
|
||||
move |arena, state| parse_expr(min_indent, arena, state)
|
||||
)),
|
||||
min_indent,
|
||||
When::Space,
|
||||
When::IndentCondition
|
||||
When::IndentCondition,
|
||||
When::IndentIs,
|
||||
),
|
||||
parser::keyword_e(keyword::IS, When::Is)
|
||||
)
|
||||
@ -1182,13 +1183,14 @@ mod when {
|
||||
skip_first!(
|
||||
parser::keyword_e(keyword::IF, When::IfToken),
|
||||
// TODO we should require space before the expression but not after
|
||||
space0_around_e(
|
||||
space0_around_ee(
|
||||
loc!(specialize_ref(When::IfGuard, move |arena, state| {
|
||||
parse_expr(min_indent, arena, state)
|
||||
})),
|
||||
min_indent,
|
||||
When::Space,
|
||||
When::IndentIfGuard,
|
||||
When::IndentArrow,
|
||||
)
|
||||
),
|
||||
Some
|
||||
@ -1234,41 +1236,100 @@ mod when {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
map_with_arena!(
|
||||
and!(
|
||||
skip_first!(
|
||||
parser::keyword(keyword::IF, min_indent),
|
||||
space1_around(
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||
min_indent,
|
||||
)
|
||||
fn if_branch<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, (Located<Expr<'a>>, Located<Expr<'a>>), If<'a>> {
|
||||
move |arena, state| {
|
||||
// NOTE: only parse spaces before the expression
|
||||
let (_, cond, state) = space0_around_ee(
|
||||
specialize_ref(
|
||||
If::Syntax,
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||
),
|
||||
and!(
|
||||
skip_first!(
|
||||
parser::keyword(keyword::THEN, min_indent),
|
||||
space1_around(
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||
min_indent,
|
||||
)
|
||||
),
|
||||
skip_first!(
|
||||
parser::keyword(keyword::ELSE, min_indent),
|
||||
// NOTE changed this from space1_around to space1_before
|
||||
space1_before(
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||
min_indent,
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
|arena: &'a Bump, (condition, (then_branch, else_branch))| {
|
||||
Expr::If(
|
||||
&*arena.alloc(condition),
|
||||
&*arena.alloc(then_branch),
|
||||
&*arena.alloc(else_branch),
|
||||
)
|
||||
}
|
||||
min_indent,
|
||||
If::Space,
|
||||
If::IndentCondition,
|
||||
If::IndentThenToken,
|
||||
)
|
||||
.parse(arena, state)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
let (_, _, state) = parser::keyword_e(keyword::THEN, If::Then)
|
||||
.parse(arena, state)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
let (_, then_branch, state) = space0_around_ee(
|
||||
specialize_ref(
|
||||
If::Syntax,
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||
),
|
||||
min_indent,
|
||||
If::Space,
|
||||
If::IndentThenBranch,
|
||||
If::IndentElseToken,
|
||||
)
|
||||
.parse(arena, state)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
let (_, _, state) = parser::keyword_e(keyword::ELSE, If::Else)
|
||||
.parse(arena, state)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
Ok((MadeProgress, (cond, then_branch), state))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn if_expr_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, If<'a>> {
|
||||
move |arena: &'a Bump, state| {
|
||||
let (_, _, state) = parser::keyword_e(keyword::IF, If::If).parse(arena, state)?;
|
||||
|
||||
let mut branches = Vec::with_capacity_in(1, arena);
|
||||
|
||||
let mut loop_state = state;
|
||||
|
||||
let state_final_else = loop {
|
||||
let (_, (cond, then_branch), state) = if_branch(min_indent).parse(arena, loop_state)?;
|
||||
|
||||
branches.push((cond, then_branch));
|
||||
|
||||
// try to parse another `if`
|
||||
// NOTE this drops spaces between the `else` and the `if`
|
||||
let optional_if = and!(
|
||||
backtrackable(space0_e(min_indent, If::Space, If::IndentIf)),
|
||||
parser::keyword_e(keyword::IF, If::If)
|
||||
);
|
||||
|
||||
match optional_if.parse(arena, state) {
|
||||
Err((_, _, state)) => break state,
|
||||
Ok((_, _, state)) => {
|
||||
loop_state = state;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (_, else_branch, state) = space0_before_e(
|
||||
specialize_ref(
|
||||
If::Syntax,
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||
),
|
||||
min_indent,
|
||||
If::Space,
|
||||
If::IndentElseBranch,
|
||||
)
|
||||
.parse(arena, state_final_else)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
let expr = Expr::If(branches.into_bump_slice(), arena.alloc(else_branch));
|
||||
|
||||
Ok((MadeProgress, expr, state))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
specialize(
|
||||
|e, r, c| SyntaxError::Expr(EExpr::If(e, r, c)),
|
||||
if_expr_help(min_indent),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1632,37 +1693,42 @@ fn binop<'a>() -> impl Parser<'a, BinOp, SyntaxError<'a>> {
|
||||
map!(ascii_char(b'%'), |_| BinOp::Percent)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
let elems = collection_trailing_sep!(
|
||||
ascii_char(b'['),
|
||||
loc!(expr(min_indent)),
|
||||
ascii_char(b','),
|
||||
ascii_char(b']'),
|
||||
min_indent
|
||||
);
|
||||
|
||||
parser::attempt(
|
||||
Attempting::List,
|
||||
map_with_arena!(elems, |arena,
|
||||
(parsed_elems, final_comments): (
|
||||
Vec<'a, Located<Expr<'a>>>,
|
||||
&'a [CommentOrNewline<'a>]
|
||||
)| {
|
||||
let mut allocated = Vec::with_capacity_in(parsed_elems.len(), arena);
|
||||
|
||||
for parsed_elem in parsed_elems {
|
||||
allocated.push(&*arena.alloc(parsed_elem));
|
||||
}
|
||||
|
||||
Expr::List {
|
||||
items: allocated.into_bump_slice(),
|
||||
final_comments,
|
||||
}
|
||||
}),
|
||||
fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
specialize(
|
||||
|e, r, c| SyntaxError::Expr(EExpr::List(e, r, c)),
|
||||
list_literal_help(min_indent),
|
||||
)
|
||||
}
|
||||
|
||||
fn list_literal_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, List<'a>> {
|
||||
move |arena, state| {
|
||||
let (_, (parsed_elems, final_comments), state) = collection_trailing_sep_e!(
|
||||
word1(b'[', List::Open),
|
||||
specialize_ref(List::Syntax, loc!(expr(min_indent))),
|
||||
word1(b',', List::End),
|
||||
word1(b']', List::End),
|
||||
min_indent,
|
||||
List::Open,
|
||||
List::Space,
|
||||
List::IndentEnd
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
let mut allocated = Vec::with_capacity_in(parsed_elems.len(), arena);
|
||||
|
||||
for parsed_elem in parsed_elems {
|
||||
allocated.push(&*arena.alloc(parsed_elem));
|
||||
}
|
||||
|
||||
let expr = Expr::List {
|
||||
items: allocated.into_bump_slice(),
|
||||
final_comments,
|
||||
};
|
||||
|
||||
Ok((MadeProgress, expr, state))
|
||||
}
|
||||
}
|
||||
|
||||
// Parser<'a, Vec<'a, Located<AssignedField<'a, S>>>>
|
||||
fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
then(
|
||||
|
@ -378,12 +378,28 @@ pub enum EExpr<'a> {
|
||||
Space(BadInputError, Row, Col),
|
||||
|
||||
When(When<'a>, Row, Col),
|
||||
If(If<'a>, Row, Col),
|
||||
|
||||
List(List<'a>, Row, Col),
|
||||
|
||||
// EInParens(PInParens<'a>, Row, Col),
|
||||
IndentStart(Row, Col),
|
||||
IndentEnd(Row, Col),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum List<'a> {
|
||||
Open(Row, Col),
|
||||
End(Row, Col),
|
||||
Space(BadInputError, Row, Col),
|
||||
|
||||
Syntax(&'a SyntaxError<'a>, Row, Col),
|
||||
Expr(&'a EExpr<'a>, Row, Col),
|
||||
|
||||
IndentOpen(Row, Col),
|
||||
IndentEnd(Row, Col),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum When<'a> {
|
||||
Space(BadInputError, Row, Col),
|
||||
@ -408,6 +424,26 @@ pub enum When<'a> {
|
||||
PatternAlignment(u16, Row, Col),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum If<'a> {
|
||||
Space(BadInputError, Row, Col),
|
||||
If(Row, Col),
|
||||
Then(Row, Col),
|
||||
Else(Row, Col),
|
||||
// TODO make EEXpr
|
||||
Condition(&'a EExpr<'a>, Row, Col),
|
||||
ThenBranch(&'a EExpr<'a>, Row, Col),
|
||||
ElseBranch(&'a EExpr<'a>, Row, Col),
|
||||
Syntax(&'a SyntaxError<'a>, Row, Col),
|
||||
|
||||
IndentCondition(Row, Col),
|
||||
IndentIf(Row, Col),
|
||||
IndentThenToken(Row, Col),
|
||||
IndentElseToken(Row, Col),
|
||||
IndentThenBranch(Row, Col),
|
||||
IndentElseBranch(Row, Col),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EPattern<'a> {
|
||||
Record(PRecord<'a>, Row, Col),
|
||||
@ -1431,10 +1467,11 @@ macro_rules! collection_trailing_sep_e {
|
||||
and!(
|
||||
$crate::parser::trailing_sep_by0(
|
||||
$delimiter,
|
||||
$crate::blankspace::space0_around_e(
|
||||
$crate::blankspace::space0_around_ee(
|
||||
$elem,
|
||||
$min_indent,
|
||||
$space_problem,
|
||||
$indent_problem,
|
||||
$indent_problem
|
||||
)
|
||||
),
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::ast::Pattern;
|
||||
use crate::blankspace::{space0_around_e, space0_before_e, space0_e};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::ident::{ident, lowercase_ident, Ident};
|
||||
use crate::number_literal::number_literal;
|
||||
use crate::parser::Progress::{self, *};
|
||||
@ -133,11 +133,12 @@ fn loc_pattern_in_parens_help<'a>(
|
||||
) -> impl Parser<'a, Located<Pattern<'a>>, PInParens<'a>> {
|
||||
between!(
|
||||
word1(b'(', PInParens::Open),
|
||||
space0_around_e(
|
||||
space0_around_ee(
|
||||
move |arena, state| specialize_ref(PInParens::Syntax, loc_pattern(min_indent))
|
||||
.parse(arena, state),
|
||||
min_indent,
|
||||
PInParens::Space,
|
||||
PInParens::IndentOpen,
|
||||
PInParens::IndentEnd,
|
||||
),
|
||||
word1(b')', PInParens::End)
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::ast::{AssignedField, Tag, TypeAnnotation};
|
||||
use crate::blankspace::{space0_around_e, space0_before_e, space0_e};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::ident::join_module_parts;
|
||||
use crate::keyword;
|
||||
use crate::parser::{
|
||||
@ -146,11 +146,12 @@ fn loc_type_in_parens<'a>(
|
||||
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, TInParens<'a>> {
|
||||
between!(
|
||||
word1(b'(', TInParens::Open),
|
||||
space0_around_e(
|
||||
space0_around_ee(
|
||||
move |arena, state| specialize_ref(TInParens::Type, expression(min_indent))
|
||||
.parse(arena, state),
|
||||
min_indent,
|
||||
TInParens::Space,
|
||||
TInParens::IndentOpen,
|
||||
TInParens::IndentEnd,
|
||||
),
|
||||
word1(b')', TInParens::End)
|
||||
@ -436,11 +437,12 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
||||
let (p2, rest, state) = zero_or_more!(skip_first!(
|
||||
word1(b',', Type::TFunctionArgument),
|
||||
one_of![
|
||||
space0_around_e(
|
||||
space0_around_ee(
|
||||
term(min_indent),
|
||||
min_indent,
|
||||
Type::TSpace,
|
||||
Type::TIndentStart
|
||||
Type::TIndentStart,
|
||||
Type::TIndentEnd
|
||||
),
|
||||
|_, state: State<'a>| Err((
|
||||
NoProgress,
|
||||
|
@ -158,7 +158,10 @@ enum Context {
|
||||
enum Node {
|
||||
WhenCondition,
|
||||
WhenBranch,
|
||||
// WhenIfGuard,
|
||||
IfCondition,
|
||||
IfThenBranch,
|
||||
IfElseBranch,
|
||||
ListElement,
|
||||
}
|
||||
|
||||
fn to_expr_report<'a>(
|
||||
@ -173,10 +176,240 @@ fn to_expr_report<'a>(
|
||||
|
||||
match parse_problem {
|
||||
EExpr::When(when, row, col) => to_when_report(alloc, filename, context, &when, *row, *col),
|
||||
EExpr::If(if_, row, col) => to_if_report(alloc, filename, context, &if_, *row, *col),
|
||||
EExpr::List(list, row, col) => to_list_report(alloc, filename, context, &list, *row, *col),
|
||||
_ => todo!("unhandled parse error: {:?}", parse_problem),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_list_report<'a>(
|
||||
alloc: &'a RocDocAllocator<'a>,
|
||||
filename: PathBuf,
|
||||
context: Context,
|
||||
parse_problem: &roc_parse::parser::List<'a>,
|
||||
start_row: Row,
|
||||
start_col: Col,
|
||||
) -> Report<'a> {
|
||||
use roc_parse::parser::List;
|
||||
|
||||
match *parse_problem {
|
||||
List::Syntax(syntax, row, col) => to_syntax_report(alloc, filename, syntax, row, col),
|
||||
List::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
|
||||
|
||||
List::Expr(expr, row, col) => to_expr_report(
|
||||
alloc,
|
||||
filename,
|
||||
Context::InNode(Node::ListElement, start_row, start_col, Box::new(context)),
|
||||
expr,
|
||||
row,
|
||||
col,
|
||||
),
|
||||
|
||||
List::Open(row, col) | List::End(row, col) => {
|
||||
match dbg!(what_is_next(alloc.src_lines, row, col)) {
|
||||
Next::Other(Some(',')) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(
|
||||
r"I am partway through started parsing a list, but I got stuck here:",
|
||||
),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.concat(vec![
|
||||
alloc
|
||||
.reflow(r"I was expecting to see a list entry before this comma, "),
|
||||
alloc.reflow(r"so try adding a list entry"),
|
||||
alloc.reflow(r" and see if that helps?"),
|
||||
]),
|
||||
]);
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "UNFINISHED LIST".to_string(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(
|
||||
r"I am partway through started parsing a list, but I got stuck here:",
|
||||
),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(
|
||||
r"I was expecting to see a closing square bracket before this, ",
|
||||
),
|
||||
alloc.reflow(r"so try adding a "),
|
||||
alloc.parser_suggestion("]"),
|
||||
alloc.reflow(r" and see if that helps?"),
|
||||
]),
|
||||
alloc.concat(vec![
|
||||
alloc.note("When "),
|
||||
alloc.reflow(r"I get stuck like this, "),
|
||||
alloc.reflow(r"it usually means that there is a missing parenthesis "),
|
||||
alloc.reflow(r"or bracket somewhere earlier. "),
|
||||
alloc.reflow(r"It could also be a stray keyword or operator."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "UNFINISHED LIST".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List::IndentOpen(row, col) | List::IndentEnd(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(r"I cannot find the end of this list:"),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"You could change it to something like "),
|
||||
alloc.parser_suggestion("[ 1, 2, 3 ]"),
|
||||
alloc.reflow(" or even just "),
|
||||
alloc.parser_suggestion("[]"),
|
||||
alloc.reflow(". Anything where there is an open and a close square bracket, "),
|
||||
alloc.reflow("and where the elements of the list are separated by commas."),
|
||||
]),
|
||||
note_for_tag_union_type_indent(alloc),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "UNFINISHED LIST".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_if_report<'a>(
|
||||
alloc: &'a RocDocAllocator<'a>,
|
||||
filename: PathBuf,
|
||||
context: Context,
|
||||
parse_problem: &roc_parse::parser::If<'a>,
|
||||
start_row: Row,
|
||||
start_col: Col,
|
||||
) -> Report<'a> {
|
||||
use roc_parse::parser::If;
|
||||
|
||||
match *parse_problem {
|
||||
If::Syntax(syntax, row, col) => to_syntax_report(alloc, filename, syntax, row, col),
|
||||
If::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
|
||||
|
||||
If::Condition(expr, row, col) => to_expr_report(
|
||||
alloc,
|
||||
filename,
|
||||
Context::InNode(Node::IfCondition, start_row, start_col, Box::new(context)),
|
||||
expr,
|
||||
row,
|
||||
col,
|
||||
),
|
||||
|
||||
If::ThenBranch(expr, row, col) => to_expr_report(
|
||||
alloc,
|
||||
filename,
|
||||
Context::InNode(Node::IfThenBranch, start_row, start_col, Box::new(context)),
|
||||
expr,
|
||||
row,
|
||||
col,
|
||||
),
|
||||
|
||||
If::ElseBranch(expr, row, col) => to_expr_report(
|
||||
alloc,
|
||||
filename,
|
||||
Context::InNode(Node::IfElseBranch, start_row, start_col, Box::new(context)),
|
||||
expr,
|
||||
row,
|
||||
col,
|
||||
),
|
||||
|
||||
If::If(_row, _col) => unreachable!("another branch would be taken"),
|
||||
If::IndentIf(_row, _col) => unreachable!("another branch would be taken"),
|
||||
|
||||
If::Then(row, col) | If::IndentThenBranch(row, col) | If::IndentThenToken(row, col) => {
|
||||
to_unfinished_if_report(
|
||||
alloc,
|
||||
filename,
|
||||
row,
|
||||
col,
|
||||
start_row,
|
||||
start_col,
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I was expecting to see the "),
|
||||
alloc.keyword("then"),
|
||||
alloc.reflow(r" keyword next."),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
If::Else(row, col) | If::IndentElseBranch(row, col) | If::IndentElseToken(row, col) => {
|
||||
to_unfinished_if_report(
|
||||
alloc,
|
||||
filename,
|
||||
row,
|
||||
col,
|
||||
start_row,
|
||||
start_col,
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I was expecting to see the "),
|
||||
alloc.keyword("else"),
|
||||
alloc.reflow(r" keyword next."),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
If::IndentCondition(row, col) => to_unfinished_if_report(
|
||||
alloc,
|
||||
filename,
|
||||
row,
|
||||
col,
|
||||
start_row,
|
||||
start_col,
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I was expecting to see a expression next")
|
||||
]),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_unfinished_if_report<'a>(
|
||||
alloc: &'a RocDocAllocator<'a>,
|
||||
filename: PathBuf,
|
||||
row: Row,
|
||||
col: Col,
|
||||
start_row: Row,
|
||||
start_col: Col,
|
||||
message: RocDocBuilder<'a>,
|
||||
) -> Report<'a> {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I was partway through parsing an "),
|
||||
alloc.keyword("if"),
|
||||
alloc.reflow(r" expression, but I got stuck here:"),
|
||||
]),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
message,
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "UNFINISHED IF".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_when_report<'a>(
|
||||
alloc: &'a RocDocAllocator<'a>,
|
||||
filename: PathBuf,
|
||||
@ -792,6 +1025,23 @@ fn to_type_report<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
Type::TIndentEnd(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
||||
let region = Region::from_row_col(*row, *col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(r"I am partway through parsing a type, but I got stuck here:"),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.note("I may be confused by indentation"),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "UNFINISHED TYPE".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
Type::TAsIndentStart(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
||||
let region = Region::from_row_col(*row, *col);
|
||||
@ -1606,6 +1856,7 @@ fn to_space_report<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Next<'a> {
|
||||
Keyword(&'a str),
|
||||
// Operator(&'a str),
|
||||
|
@ -801,35 +801,36 @@ mod test_reporting {
|
||||
)
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn if_3_branch_mismatch() {
|
||||
// report_problem_as(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// if True then 2 else if False then 2 else "foo"
|
||||
// "#
|
||||
// ),
|
||||
// indoc!(
|
||||
// r#"
|
||||
// ── TYPE MISMATCH ───────────────────────────────────────────────────────────────
|
||||
|
||||
// The 2nd branch of this `if` does not match all the previous branches:
|
||||
|
||||
// 1│ if True then 2 else "foo"
|
||||
// ^^^^^
|
||||
|
||||
// The 2nd branch is a string of type
|
||||
|
||||
// Str
|
||||
|
||||
// But all the previous branches have the type
|
||||
|
||||
// Num a
|
||||
|
||||
// "#
|
||||
// ),
|
||||
// )
|
||||
// }
|
||||
#[test]
|
||||
fn if_3_branch_mismatch() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
if True then 2 else if False then 2 else "foo"
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── TYPE MISMATCH ───────────────────────────────────────────────────────────────
|
||||
|
||||
The 3rd branch of this `if` does not match all the previous branches:
|
||||
|
||||
1│ if True then 2 else if False then 2 else "foo"
|
||||
^^^^^
|
||||
|
||||
The 3rd branch is a string of type:
|
||||
|
||||
Str
|
||||
|
||||
But all the previous branches have type:
|
||||
|
||||
Num a
|
||||
|
||||
I need all branches in an `if` to have the same type!
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_branch_mismatch() {
|
||||
@ -4635,12 +4636,12 @@ mod test_reporting {
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED TYPE ─────────────────────────────────────────────────────────────
|
||||
|
||||
I just started parsing a type, but I got stuck here:
|
||||
|
||||
|
||||
I am partway through parsing a type, but I got stuck here:
|
||||
|
||||
1│ f : I64, I64
|
||||
^
|
||||
|
||||
|
||||
Note: I may be confused by indentation
|
||||
"#
|
||||
),
|
||||
@ -4949,4 +4950,138 @@ mod test_reporting {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_outdented_then() {
|
||||
// TODO I think we can do better here
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x =
|
||||
if 5 == 5
|
||||
then 2 else 3
|
||||
|
||||
x
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED IF ───────────────────────────────────────────────────────────────
|
||||
|
||||
I was partway through parsing an `if` expression, but I got stuck here:
|
||||
|
||||
2│ if 5 == 5
|
||||
^
|
||||
|
||||
I was expecting to see the `then` keyword next.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_missing_else() {
|
||||
// this should get better with time
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
if 5 == 5 then 2
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED IF ───────────────────────────────────────────────────────────────
|
||||
|
||||
I was partway through parsing an `if` expression, but I got stuck here:
|
||||
|
||||
1│ if 5 == 5 then 2
|
||||
^
|
||||
|
||||
I was expecting to see the `else` keyword next.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_double_comma() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
[ 1, 2, , 3 ]
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED LIST ─────────────────────────────────────────────────────────────
|
||||
|
||||
I am partway through started parsing a list, but I got stuck here:
|
||||
|
||||
1│ [ 1, 2, , 3 ]
|
||||
^
|
||||
|
||||
I was expecting to see a list entry before this comma, so try adding a
|
||||
list entry and see if that helps?
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_without_end() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
[ 1, 2,
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED LIST ─────────────────────────────────────────────────────────────
|
||||
|
||||
I am partway through started parsing a list, but I got stuck here:
|
||||
|
||||
1│ [ 1, 2,
|
||||
^
|
||||
|
||||
I was expecting to see a closing square bracket before this, so try
|
||||
adding a ] and see if that helps?
|
||||
|
||||
Note: When I get stuck like this, it usually means that there is a
|
||||
missing parenthesis or bracket somewhere earlier. It could also be a
|
||||
stray keyword or operator.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_bad_indent() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x = [ 1, 2,
|
||||
]
|
||||
|
||||
x
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED LIST ─────────────────────────────────────────────────────────────
|
||||
|
||||
I cannot find the end of this list:
|
||||
|
||||
1│ x = [ 1, 2,
|
||||
^
|
||||
|
||||
You could change it to something like [ 1, 2, 3 ] or even just [].
|
||||
Anything where there is an open and a close square bracket, and where
|
||||
the elements of the list are separated by commas.
|
||||
|
||||
Note: I may be confused by indentation
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -508,22 +508,31 @@ pub fn to_expr2<'a>(
|
||||
Output::default(),
|
||||
),
|
||||
|
||||
If(cond, then_branch, else_branch) => {
|
||||
let (cond, mut output) = to_expr2(env, scope, &cond.value, cond.region);
|
||||
If(branches, final_else) => {
|
||||
let mut new_branches = Vec::with_capacity(branches.len());
|
||||
let mut output = Output::default();
|
||||
|
||||
let (then_expr, then_output) =
|
||||
to_expr2(env, scope, &then_branch.value, then_branch.region);
|
||||
for (condition, then_branch) in branches.iter() {
|
||||
let (cond, cond_output) = to_expr2(env, scope, &condition.value, condition.region);
|
||||
|
||||
let (then_expr, then_output) =
|
||||
to_expr2(env, scope, &then_branch.value, then_branch.region);
|
||||
|
||||
output.references.union_mut(cond_output.references);
|
||||
output.references.union_mut(then_output.references);
|
||||
|
||||
new_branches.push((cond, then_expr));
|
||||
}
|
||||
|
||||
let (else_expr, else_output) =
|
||||
to_expr2(env, scope, &else_branch.value, else_branch.region);
|
||||
to_expr2(env, scope, &final_else.value, final_else.region);
|
||||
|
||||
output.references.union_mut(then_output.references);
|
||||
output.references.union_mut(else_output.references);
|
||||
|
||||
let expr = Expr2::If {
|
||||
cond_var: env.var_store.fresh(),
|
||||
expr_var: env.var_store.fresh(),
|
||||
branches: PoolVec::new(vec![(cond, then_expr)].into_iter(), env.pool),
|
||||
branches: PoolVec::new(new_branches.into_iter(), env.pool),
|
||||
final_else: env.pool.add(else_expr),
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user