mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
fix enum and bool pattern match suggestions
This commit is contained in:
parent
7632a4b484
commit
c69b0d69e5
6 changed files with 136 additions and 50 deletions
|
@ -420,13 +420,13 @@ fn test_at_path<'a>(selected_path: &Path, branch: Branch<'a>, all_tests: &mut Ve
|
||||||
arguments: arguments.to_vec(),
|
arguments: arguments.to_vec(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
BitLiteral(v) => {
|
BitLiteral { value, .. } => {
|
||||||
all_tests.push(IsBit(*v));
|
all_tests.push(IsBit(*value));
|
||||||
}
|
}
|
||||||
EnumLiteral { tag_id, enum_size } => {
|
EnumLiteral { tag_id, union, .. } => {
|
||||||
all_tests.push(IsByte {
|
all_tests.push(IsByte {
|
||||||
tag_id: *tag_id,
|
tag_id: *tag_id,
|
||||||
num_alts: *enum_size as usize,
|
num_alts: union.alternatives.len(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
IntLiteral(v) => {
|
IntLiteral(v) => {
|
||||||
|
@ -629,7 +629,7 @@ fn to_relevant_branch_help<'a>(
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
|
||||||
BitLiteral(bit) => match test {
|
BitLiteral { value: bit, .. } => match test {
|
||||||
IsBit(test_bit) if bit == *test_bit => {
|
IsBit(test_bit) if bit == *test_bit => {
|
||||||
start.extend(end);
|
start.extend(end);
|
||||||
Some(Branch {
|
Some(Branch {
|
||||||
|
@ -714,7 +714,7 @@ fn needs_tests<'a>(pattern: &Pattern<'a>) -> bool {
|
||||||
|
|
||||||
RecordDestructure(_, _)
|
RecordDestructure(_, _)
|
||||||
| AppliedTag { .. }
|
| AppliedTag { .. }
|
||||||
| BitLiteral(_)
|
| BitLiteral { .. }
|
||||||
| EnumLiteral { .. }
|
| EnumLiteral { .. }
|
||||||
| IntLiteral(_)
|
| IntLiteral(_)
|
||||||
| FloatLiteral(_)
|
| FloatLiteral(_)
|
||||||
|
|
|
@ -854,7 +854,7 @@ fn store_pattern<'a>(
|
||||||
// Since _ is never read, it's safe to reassign it.
|
// Since _ is never read, it's safe to reassign it.
|
||||||
// stored.push((Symbol::UNDERSCORE, layout, Expr::Load(outer_symbol)))
|
// stored.push((Symbol::UNDERSCORE, layout, Expr::Load(outer_symbol)))
|
||||||
}
|
}
|
||||||
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {}
|
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } => {}
|
||||||
AppliedTag {
|
AppliedTag {
|
||||||
union, arguments, ..
|
union, arguments, ..
|
||||||
} => {
|
} => {
|
||||||
|
@ -886,7 +886,7 @@ fn store_pattern<'a>(
|
||||||
Underscore => {
|
Underscore => {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {}
|
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } => {}
|
||||||
_ => {
|
_ => {
|
||||||
// store the field in a symbol, and continue matching on it
|
// store the field in a symbol, and continue matching on it
|
||||||
let symbol = env.fresh_symbol();
|
let symbol = env.fresh_symbol();
|
||||||
|
@ -963,7 +963,7 @@ fn store_record_destruct<'a>(
|
||||||
//
|
//
|
||||||
// internally. But `y` is never used, so we must make sure it't not stored/loaded.
|
// internally. But `y` is never used, so we must make sure it't not stored/loaded.
|
||||||
}
|
}
|
||||||
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {}
|
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } => {}
|
||||||
_ => {
|
_ => {
|
||||||
let symbol = env.fresh_symbol();
|
let symbol = env.fresh_symbol();
|
||||||
stored.push((symbol, destruct.layout.clone(), load));
|
stored.push((symbol, destruct.layout.clone(), load));
|
||||||
|
@ -1416,10 +1416,15 @@ pub enum Pattern<'a> {
|
||||||
|
|
||||||
IntLiteral(i64),
|
IntLiteral(i64),
|
||||||
FloatLiteral(u64),
|
FloatLiteral(u64),
|
||||||
BitLiteral(bool),
|
BitLiteral {
|
||||||
|
value: bool,
|
||||||
|
tag_name: TagName,
|
||||||
|
union: crate::pattern::Union,
|
||||||
|
},
|
||||||
EnumLiteral {
|
EnumLiteral {
|
||||||
tag_id: u8,
|
tag_id: u8,
|
||||||
enum_size: u8,
|
tag_name: TagName,
|
||||||
|
union: crate::pattern::Union,
|
||||||
},
|
},
|
||||||
StrLiteral(Box<str>),
|
StrLiteral(Box<str>),
|
||||||
|
|
||||||
|
@ -1479,6 +1484,7 @@ fn from_can_pattern<'a>(
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
use crate::layout::UnionVariant::*;
|
use crate::layout::UnionVariant::*;
|
||||||
|
use crate::pattern::Union;
|
||||||
|
|
||||||
let variant =
|
let variant =
|
||||||
crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.pointer_size);
|
crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.pointer_size);
|
||||||
|
@ -1487,18 +1493,52 @@ fn from_can_pattern<'a>(
|
||||||
Never => unreachable!("there is no pattern of type `[]`"),
|
Never => unreachable!("there is no pattern of type `[]`"),
|
||||||
Unit => Pattern::EnumLiteral {
|
Unit => Pattern::EnumLiteral {
|
||||||
tag_id: 0,
|
tag_id: 0,
|
||||||
enum_size: 1,
|
tag_name: tag_name.clone(),
|
||||||
|
union: Union {
|
||||||
|
alternatives: vec![Ctor {
|
||||||
|
name: tag_name.clone(),
|
||||||
|
arity: 0,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BoolUnion { ttrue, ffalse } => Pattern::BitLiteral {
|
||||||
|
value: tag_name == &ttrue,
|
||||||
|
tag_name: tag_name.clone(),
|
||||||
|
union: Union {
|
||||||
|
alternatives: vec![
|
||||||
|
Ctor {
|
||||||
|
name: ttrue,
|
||||||
|
arity: 0,
|
||||||
|
},
|
||||||
|
Ctor {
|
||||||
|
name: ffalse,
|
||||||
|
arity: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
BoolUnion { ttrue, .. } => Pattern::BitLiteral(tag_name == &ttrue),
|
|
||||||
ByteUnion(tag_names) => {
|
ByteUnion(tag_names) => {
|
||||||
let tag_id = tag_names
|
let tag_id = tag_names
|
||||||
.iter()
|
.iter()
|
||||||
.position(|key| key == tag_name)
|
.position(|key| key == tag_name)
|
||||||
.expect("tag must be in its own type");
|
.expect("tag must be in its own type");
|
||||||
|
|
||||||
|
let mut ctors = std::vec::Vec::with_capacity(tag_names.len());
|
||||||
|
for tag_name in &tag_names {
|
||||||
|
ctors.push(Ctor {
|
||||||
|
name: tag_name.clone(),
|
||||||
|
arity: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let union = crate::pattern::Union {
|
||||||
|
alternatives: ctors,
|
||||||
|
};
|
||||||
|
|
||||||
Pattern::EnumLiteral {
|
Pattern::EnumLiteral {
|
||||||
tag_id: tag_id as u8,
|
tag_id: tag_id as u8,
|
||||||
enum_size: tag_names.len() as u8,
|
tag_name: tag_name.clone(),
|
||||||
|
union,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Unwrapped(field_layouts) => {
|
Unwrapped(field_layouts) => {
|
||||||
|
|
|
@ -42,33 +42,12 @@ fn simplify<'a>(pattern: &crate::expr::Pattern<'a>) -> Pattern {
|
||||||
|
|
||||||
// To make sure these are exhaustive, we have to "fake" a union here
|
// To make sure these are exhaustive, we have to "fake" a union here
|
||||||
// TODO: use the hash or some other integer to discriminate between constructors
|
// TODO: use the hash or some other integer to discriminate between constructors
|
||||||
BitLiteral(b) => {
|
BitLiteral {
|
||||||
let union = Union {
|
tag_name, union, ..
|
||||||
alternatives: vec![
|
} => Ctor(union.clone(), tag_name.clone(), vec![]),
|
||||||
Ctor {
|
EnumLiteral {
|
||||||
name: TagName::Global("False".into()),
|
tag_name, union, ..
|
||||||
arity: 0,
|
} => Ctor(union.clone(), tag_name.clone(), vec![]),
|
||||||
},
|
|
||||||
Ctor {
|
|
||||||
name: TagName::Global("True".into()),
|
|
||||||
arity: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
Ctor(union, TagName::Global(format!("{}", b).into()), vec![])
|
|
||||||
}
|
|
||||||
EnumLiteral { tag_id, enum_size } => {
|
|
||||||
let alternatives = (0..*enum_size)
|
|
||||||
.map(|id| Ctor {
|
|
||||||
name: TagName::Global(format!("{}", id).into()),
|
|
||||||
arity: 0,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let union = Union { alternatives };
|
|
||||||
Ctor(union, TagName::Global(format!("{}", tag_id).into()), vec![])
|
|
||||||
}
|
|
||||||
|
|
||||||
Underscore => Anything,
|
Underscore => Anything,
|
||||||
Identifier(_) => Anything,
|
Identifier(_) => Anything,
|
||||||
|
|
|
@ -155,7 +155,7 @@ pub fn mono_problem<'b>(
|
||||||
PatternProblem(Incomplete(region, context, missing)) => match context {
|
PatternProblem(Incomplete(region, context, missing)) => match context {
|
||||||
BadArg => {
|
BadArg => {
|
||||||
let doc = alloc.stack(vec![
|
let doc = alloc.stack(vec![
|
||||||
alloc.reflow("This pattern does not cover all the possibilities"),
|
alloc.reflow("This pattern does not cover all the possibilities:"),
|
||||||
alloc.region(region),
|
alloc.region(region),
|
||||||
alloc.reflow("Other possibilities include:"),
|
alloc.reflow("Other possibilities include:"),
|
||||||
unhandled_patterns_to_doc_block(alloc, missing),
|
unhandled_patterns_to_doc_block(alloc, missing),
|
||||||
|
@ -177,7 +177,7 @@ pub fn mono_problem<'b>(
|
||||||
}
|
}
|
||||||
BadDestruct => {
|
BadDestruct => {
|
||||||
let doc = alloc.stack(vec![
|
let doc = alloc.stack(vec![
|
||||||
alloc.reflow("This pattern does not cover all the possibilities"),
|
alloc.reflow("This pattern does not cover all the possibilities:"),
|
||||||
alloc.region(region),
|
alloc.region(region),
|
||||||
alloc.reflow("Other possibilities include:"),
|
alloc.reflow("Other possibilities include:"),
|
||||||
unhandled_patterns_to_doc_block(alloc, missing),
|
unhandled_patterns_to_doc_block(alloc, missing),
|
||||||
|
@ -203,7 +203,7 @@ pub fn mono_problem<'b>(
|
||||||
alloc.concat(vec![
|
alloc.concat(vec![
|
||||||
alloc.reflow("This "),
|
alloc.reflow("This "),
|
||||||
alloc.keyword("when"),
|
alloc.keyword("when"),
|
||||||
alloc.reflow(" does not cover all the possibilities"),
|
alloc.reflow(" does not cover all the possibilities:"),
|
||||||
]),
|
]),
|
||||||
alloc.region(region),
|
alloc.region(region),
|
||||||
alloc.reflow("Other possibilities include:"),
|
alloc.reflow("Other possibilities include:"),
|
||||||
|
|
|
@ -160,7 +160,7 @@ fn to_expr_report<'b>(
|
||||||
None => (alloc.text("this"), alloc.nil()),
|
None => (alloc.text("this"), alloc.nil()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut region = None;
|
let mut sub_region = None;
|
||||||
|
|
||||||
let thing = match annotation_source {
|
let thing = match annotation_source {
|
||||||
TypedIfBranch {
|
TypedIfBranch {
|
||||||
|
@ -189,7 +189,7 @@ fn to_expr_report<'b>(
|
||||||
alloc.text(" expression:"),
|
alloc.text(" expression:"),
|
||||||
]),
|
]),
|
||||||
TypedBody { region: ann_region } => {
|
TypedBody { region: ann_region } => {
|
||||||
region = Some(ann_region);
|
sub_region = Some(ann_region);
|
||||||
alloc.concat(vec![
|
alloc.concat(vec![
|
||||||
alloc.text("body of "),
|
alloc.text("body of "),
|
||||||
the_name_text,
|
the_name_text,
|
||||||
|
@ -222,9 +222,10 @@ fn to_expr_report<'b>(
|
||||||
filename,
|
filename,
|
||||||
doc: alloc.stack(vec![
|
doc: alloc.stack(vec![
|
||||||
alloc.text("Something is off with the ").append(thing),
|
alloc.text("Something is off with the ").append(thing),
|
||||||
match region {
|
match sub_region {
|
||||||
None => alloc.region(expr_region),
|
None => alloc.region(expr_region),
|
||||||
Some(ann_region) => {
|
Some(ann_region) => {
|
||||||
|
// for typed bodies, include the line(s) have the signature
|
||||||
let joined =
|
let joined =
|
||||||
roc_region::all::Region::span_across(&ann_region, &expr_region);
|
roc_region::all::Region::span_across(&ann_region, &expr_region);
|
||||||
alloc.region_with_subregion(joined, expr_region)
|
alloc.region_with_subregion(joined, expr_region)
|
||||||
|
|
|
@ -2045,7 +2045,7 @@ mod test_reporting {
|
||||||
r#"
|
r#"
|
||||||
-- UNSAFE PATTERN --------------------------------------------------------------
|
-- UNSAFE PATTERN --------------------------------------------------------------
|
||||||
|
|
||||||
This pattern does not cover all the possibilities
|
This pattern does not cover all the possibilities:
|
||||||
|
|
||||||
7 ┆ f = \Left v -> v
|
7 ┆ f = \Left v -> v
|
||||||
┆ ^^^^
|
┆ ^^^^
|
||||||
|
@ -2080,7 +2080,7 @@ mod test_reporting {
|
||||||
r#"
|
r#"
|
||||||
-- UNSAFE PATTERN --------------------------------------------------------------
|
-- UNSAFE PATTERN --------------------------------------------------------------
|
||||||
|
|
||||||
This pattern does not cover all the possibilities
|
This pattern does not cover all the possibilities:
|
||||||
|
|
||||||
5 ┆ (Left y) = x
|
5 ┆ (Left y) = x
|
||||||
┆ ^^^^
|
┆ ^^^^
|
||||||
|
@ -2110,7 +2110,7 @@ mod test_reporting {
|
||||||
r#"
|
r#"
|
||||||
-- UNSAFE PATTERN --------------------------------------------------------------
|
-- UNSAFE PATTERN --------------------------------------------------------------
|
||||||
|
|
||||||
This `when` does not cover all the possibilities
|
This `when` does not cover all the possibilities:
|
||||||
|
|
||||||
1 ┆> when 0x1 is
|
1 ┆> when 0x1 is
|
||||||
2 ┆> 2 -> 0x3
|
2 ┆> 2 -> 0x3
|
||||||
|
@ -2125,6 +2125,72 @@ mod test_reporting {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn patterns_bool_not_exhaustive() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x : Bool
|
||||||
|
x = True
|
||||||
|
|
||||||
|
when x is
|
||||||
|
False -> 3
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
// should not give errors
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- UNSAFE PATTERN --------------------------------------------------------------
|
||||||
|
|
||||||
|
This `when` does not cover all the possibilities:
|
||||||
|
|
||||||
|
4 ┆> when x is
|
||||||
|
5 ┆> False -> 3
|
||||||
|
|
||||||
|
Other possibilities include:
|
||||||
|
|
||||||
|
True
|
||||||
|
|
||||||
|
I would have to crash if I saw one of those! Add branches for them!
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn patterns_enum_not_exhaustive() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x : [ Red, Green, Blue ]
|
||||||
|
x = Red
|
||||||
|
|
||||||
|
when x is
|
||||||
|
Red -> 0
|
||||||
|
Green -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
// should not give errors
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- UNSAFE PATTERN --------------------------------------------------------------
|
||||||
|
|
||||||
|
This `when` does not cover all the possibilities:
|
||||||
|
|
||||||
|
4 ┆> when x is
|
||||||
|
5 ┆> Red -> 0
|
||||||
|
6 ┆> Green -> 1
|
||||||
|
|
||||||
|
Other possibilities include:
|
||||||
|
|
||||||
|
Blue
|
||||||
|
|
||||||
|
I would have to crash if I saw one of those! Add branches for them!
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn patterns_int_redundant() {
|
fn patterns_int_redundant() {
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue