fix enum and bool pattern match suggestions

This commit is contained in:
Folkert 2020-04-12 21:19:50 +02:00
parent 7632a4b484
commit c69b0d69e5
6 changed files with 136 additions and 50 deletions

View file

@ -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(_)

View file

@ -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) => {

View file

@ -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,

View file

@ -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:"),

View file

@ -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)

View file

@ -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(