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(),
});
}
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(_)

View file

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

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

View file

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

View file

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

View file

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