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(),
|
||||
});
|
||||
}
|
||||
BitLiteral(v) => {
|
||||
all_tests.push(IsBit(*v));
|
||||
BitLiteral { value, .. } => {
|
||||
all_tests.push(IsBit(*value));
|
||||
}
|
||||
EnumLiteral { tag_id, enum_size } => {
|
||||
EnumLiteral { tag_id, union, .. } => {
|
||||
all_tests.push(IsByte {
|
||||
tag_id: *tag_id,
|
||||
num_alts: *enum_size as usize,
|
||||
num_alts: union.alternatives.len(),
|
||||
});
|
||||
}
|
||||
IntLiteral(v) => {
|
||||
|
@ -629,7 +629,7 @@ fn to_relevant_branch_help<'a>(
|
|||
_ => None,
|
||||
},
|
||||
|
||||
BitLiteral(bit) => match test {
|
||||
BitLiteral { value: bit, .. } => match test {
|
||||
IsBit(test_bit) if bit == *test_bit => {
|
||||
start.extend(end);
|
||||
Some(Branch {
|
||||
|
@ -714,7 +714,7 @@ fn needs_tests<'a>(pattern: &Pattern<'a>) -> bool {
|
|||
|
||||
RecordDestructure(_, _)
|
||||
| AppliedTag { .. }
|
||||
| BitLiteral(_)
|
||||
| BitLiteral { .. }
|
||||
| EnumLiteral { .. }
|
||||
| IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
|
|
|
@ -854,7 +854,7 @@ fn store_pattern<'a>(
|
|||
// Since _ is never read, it's safe to reassign it.
|
||||
// stored.push((Symbol::UNDERSCORE, layout, Expr::Load(outer_symbol)))
|
||||
}
|
||||
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {}
|
||||
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } => {}
|
||||
AppliedTag {
|
||||
union, arguments, ..
|
||||
} => {
|
||||
|
@ -886,7 +886,7 @@ fn store_pattern<'a>(
|
|||
Underscore => {
|
||||
// ignore
|
||||
}
|
||||
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {}
|
||||
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } => {}
|
||||
_ => {
|
||||
// store the field in a symbol, and continue matching on it
|
||||
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.
|
||||
}
|
||||
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {}
|
||||
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } => {}
|
||||
_ => {
|
||||
let symbol = env.fresh_symbol();
|
||||
stored.push((symbol, destruct.layout.clone(), load));
|
||||
|
@ -1416,10 +1416,15 @@ pub enum Pattern<'a> {
|
|||
|
||||
IntLiteral(i64),
|
||||
FloatLiteral(u64),
|
||||
BitLiteral(bool),
|
||||
BitLiteral {
|
||||
value: bool,
|
||||
tag_name: TagName,
|
||||
union: crate::pattern::Union,
|
||||
},
|
||||
EnumLiteral {
|
||||
tag_id: u8,
|
||||
enum_size: u8,
|
||||
tag_name: TagName,
|
||||
union: crate::pattern::Union,
|
||||
},
|
||||
StrLiteral(Box<str>),
|
||||
|
||||
|
@ -1479,6 +1484,7 @@ fn from_can_pattern<'a>(
|
|||
..
|
||||
} => {
|
||||
use crate::layout::UnionVariant::*;
|
||||
use crate::pattern::Union;
|
||||
|
||||
let variant =
|
||||
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 `[]`"),
|
||||
Unit => Pattern::EnumLiteral {
|
||||
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) => {
|
||||
let tag_id = tag_names
|
||||
.iter()
|
||||
.position(|key| key == tag_name)
|
||||
.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 {
|
||||
tag_id: tag_id as u8,
|
||||
enum_size: tag_names.len() as u8,
|
||||
tag_name: tag_name.clone(),
|
||||
union,
|
||||
}
|
||||
}
|
||||
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
|
||||
// TODO: use the hash or some other integer to discriminate between constructors
|
||||
BitLiteral(b) => {
|
||||
let union = Union {
|
||||
alternatives: vec![
|
||||
Ctor {
|
||||
name: TagName::Global("False".into()),
|
||||
arity: 0,
|
||||
},
|
||||
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![])
|
||||
}
|
||||
BitLiteral {
|
||||
tag_name, union, ..
|
||||
} => Ctor(union.clone(), tag_name.clone(), vec![]),
|
||||
EnumLiteral {
|
||||
tag_name, union, ..
|
||||
} => Ctor(union.clone(), tag_name.clone(), vec![]),
|
||||
|
||||
Underscore => Anything,
|
||||
Identifier(_) => Anything,
|
||||
|
|
|
@ -155,7 +155,7 @@ pub fn mono_problem<'b>(
|
|||
PatternProblem(Incomplete(region, context, missing)) => match context {
|
||||
BadArg => {
|
||||
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.reflow("Other possibilities include:"),
|
||||
unhandled_patterns_to_doc_block(alloc, missing),
|
||||
|
@ -177,7 +177,7 @@ pub fn mono_problem<'b>(
|
|||
}
|
||||
BadDestruct => {
|
||||
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.reflow("Other possibilities include:"),
|
||||
unhandled_patterns_to_doc_block(alloc, missing),
|
||||
|
@ -203,7 +203,7 @@ pub fn mono_problem<'b>(
|
|||
alloc.concat(vec![
|
||||
alloc.reflow("This "),
|
||||
alloc.keyword("when"),
|
||||
alloc.reflow(" does not cover all the possibilities"),
|
||||
alloc.reflow(" does not cover all the possibilities:"),
|
||||
]),
|
||||
alloc.region(region),
|
||||
alloc.reflow("Other possibilities include:"),
|
||||
|
|
|
@ -160,7 +160,7 @@ fn to_expr_report<'b>(
|
|||
None => (alloc.text("this"), alloc.nil()),
|
||||
};
|
||||
|
||||
let mut region = None;
|
||||
let mut sub_region = None;
|
||||
|
||||
let thing = match annotation_source {
|
||||
TypedIfBranch {
|
||||
|
@ -189,7 +189,7 @@ fn to_expr_report<'b>(
|
|||
alloc.text(" expression:"),
|
||||
]),
|
||||
TypedBody { region: ann_region } => {
|
||||
region = Some(ann_region);
|
||||
sub_region = Some(ann_region);
|
||||
alloc.concat(vec![
|
||||
alloc.text("body of "),
|
||||
the_name_text,
|
||||
|
@ -222,9 +222,10 @@ fn to_expr_report<'b>(
|
|||
filename,
|
||||
doc: alloc.stack(vec![
|
||||
alloc.text("Something is off with the ").append(thing),
|
||||
match region {
|
||||
match sub_region {
|
||||
None => alloc.region(expr_region),
|
||||
Some(ann_region) => {
|
||||
// for typed bodies, include the line(s) have the signature
|
||||
let joined =
|
||||
roc_region::all::Region::span_across(&ann_region, &expr_region);
|
||||
alloc.region_with_subregion(joined, expr_region)
|
||||
|
|
|
@ -2045,7 +2045,7 @@ mod test_reporting {
|
|||
r#"
|
||||
-- UNSAFE PATTERN --------------------------------------------------------------
|
||||
|
||||
This pattern does not cover all the possibilities
|
||||
This pattern does not cover all the possibilities:
|
||||
|
||||
7 ┆ f = \Left v -> v
|
||||
┆ ^^^^
|
||||
|
@ -2080,7 +2080,7 @@ mod test_reporting {
|
|||
r#"
|
||||
-- UNSAFE PATTERN --------------------------------------------------------------
|
||||
|
||||
This pattern does not cover all the possibilities
|
||||
This pattern does not cover all the possibilities:
|
||||
|
||||
5 ┆ (Left y) = x
|
||||
┆ ^^^^
|
||||
|
@ -2110,7 +2110,7 @@ mod test_reporting {
|
|||
r#"
|
||||
-- UNSAFE PATTERN --------------------------------------------------------------
|
||||
|
||||
This `when` does not cover all the possibilities
|
||||
This `when` does not cover all the possibilities:
|
||||
|
||||
1 ┆> when 0x1 is
|
||||
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]
|
||||
fn patterns_int_redundant() {
|
||||
report_problem_as(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue