mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
improve formatting of records in pattern exhaustiveness errors
This commit is contained in:
parent
8284575a47
commit
3e36bea700
5 changed files with 81 additions and 30 deletions
|
@ -9,7 +9,7 @@ use roc_module::symbol::Symbol;
|
|||
use crate::expr::specialize_equality;
|
||||
use crate::layout::Builtin;
|
||||
use crate::layout::Layout;
|
||||
use crate::pattern::{Ctor, TagId, Union};
|
||||
use crate::pattern::{Ctor, RenderAs, TagId, Union};
|
||||
|
||||
/// COMPILE CASES
|
||||
|
||||
|
@ -381,7 +381,9 @@ fn test_at_path<'a>(selected_path: &Path, branch: Branch<'a>, all_tests: &mut Ve
|
|||
}
|
||||
|
||||
RecordDestructure(destructs, _) => {
|
||||
// not rendered, so pick the easiest
|
||||
let union = Union {
|
||||
render_as: RenderAs::Tag,
|
||||
alternatives: vec![Ctor {
|
||||
tag_id: TagId(0),
|
||||
name: TagName::Global("#Record".into()),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::layout::{Builtin, Layout};
|
||||
use crate::pattern::{Ctor, Guard, TagId};
|
||||
use crate::pattern::{Ctor, Guard, RenderAs, TagId};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_can;
|
||||
|
@ -1487,6 +1487,7 @@ fn from_can_pattern<'a>(
|
|||
tag_id: 0,
|
||||
tag_name: tag_name.clone(),
|
||||
union: Union {
|
||||
render_as: RenderAs::Tag,
|
||||
alternatives: vec![Ctor {
|
||||
tag_id: TagId(0),
|
||||
name: tag_name.clone(),
|
||||
|
@ -1498,6 +1499,7 @@ fn from_can_pattern<'a>(
|
|||
value: tag_name == &ttrue,
|
||||
tag_name: tag_name.clone(),
|
||||
union: Union {
|
||||
render_as: RenderAs::Tag,
|
||||
alternatives: vec![
|
||||
Ctor {
|
||||
tag_id: TagId(0),
|
||||
|
@ -1528,6 +1530,7 @@ fn from_can_pattern<'a>(
|
|||
}
|
||||
|
||||
let union = crate::pattern::Union {
|
||||
render_as: RenderAs::Tag,
|
||||
alternatives: ctors,
|
||||
};
|
||||
|
||||
|
@ -1539,6 +1542,7 @@ fn from_can_pattern<'a>(
|
|||
}
|
||||
Unwrapped(field_layouts) => {
|
||||
let union = crate::pattern::Union {
|
||||
render_as: RenderAs::Tag,
|
||||
alternatives: vec![Ctor {
|
||||
tag_id: TagId(0),
|
||||
name: tag_name.clone(),
|
||||
|
@ -1573,6 +1577,7 @@ fn from_can_pattern<'a>(
|
|||
}
|
||||
|
||||
let union = crate::pattern::Union {
|
||||
render_as: RenderAs::Tag,
|
||||
alternatives: ctors,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use roc_collections::all::{Index, MutMap};
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_region::all::{Located, Region};
|
||||
|
||||
use self::Pattern::*;
|
||||
|
@ -7,6 +7,14 @@ use self::Pattern::*;
|
|||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Union {
|
||||
pub alternatives: Vec<Ctor>,
|
||||
pub render_as: RenderAs,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum RenderAs {
|
||||
Tag,
|
||||
Record(Vec<Lowercase>),
|
||||
Guard,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
|
||||
|
@ -52,7 +60,20 @@ fn simplify<'a>(pattern: &crate::expr::Pattern<'a>) -> Pattern {
|
|||
Identifier(_) => Anything,
|
||||
RecordDestructure(destructures, _) => {
|
||||
let tag_id = TagId(0);
|
||||
let mut patterns = std::vec::Vec::with_capacity(destructures.len());
|
||||
let mut field_names = std::vec::Vec::with_capacity(destructures.len());
|
||||
|
||||
for destruct in destructures {
|
||||
field_names.push(destruct.label.clone());
|
||||
|
||||
match &destruct.guard {
|
||||
None => patterns.push(Anything),
|
||||
Some(guard) => patterns.push(simplify(guard)),
|
||||
}
|
||||
}
|
||||
|
||||
let union = Union {
|
||||
render_as: RenderAs::Record(field_names),
|
||||
alternatives: vec![Ctor {
|
||||
name: TagName::Global("#Record".into()),
|
||||
tag_id,
|
||||
|
@ -60,15 +81,6 @@ fn simplify<'a>(pattern: &crate::expr::Pattern<'a>) -> Pattern {
|
|||
}],
|
||||
};
|
||||
|
||||
let mut patterns = std::vec::Vec::with_capacity(destructures.len());
|
||||
|
||||
for destruct in destructures {
|
||||
match &destruct.guard {
|
||||
None => patterns.push(Anything),
|
||||
Some(guard) => patterns.push(simplify(guard)),
|
||||
}
|
||||
}
|
||||
|
||||
Ctor(union, tag_id, patterns)
|
||||
}
|
||||
|
||||
|
@ -306,6 +318,7 @@ fn to_nonredundant_rows<'a>(
|
|||
let tag_id = TagId(0);
|
||||
|
||||
let union = Union {
|
||||
render_as: RenderAs::Guard,
|
||||
alternatives: vec![Ctor {
|
||||
tag_id,
|
||||
name: TagName::Global("#Guard".into()),
|
||||
|
|
|
@ -133,6 +133,7 @@ fn pattern_to_doc_help<'b>(
|
|||
) -> RocDocBuilder<'b> {
|
||||
use roc_mono::pattern::Literal::*;
|
||||
use roc_mono::pattern::Pattern::*;
|
||||
use roc_mono::pattern::RenderAs;
|
||||
|
||||
match pattern {
|
||||
Anything => alloc.text("_"),
|
||||
|
@ -145,26 +146,55 @@ fn pattern_to_doc_help<'b>(
|
|||
Str(s) => alloc.string(s.into()),
|
||||
},
|
||||
Ctor(union, tag_id, args) => {
|
||||
let has_args = !args.is_empty();
|
||||
let arg_docs = args
|
||||
.into_iter()
|
||||
.map(|v| pattern_to_doc_help(alloc, v, true));
|
||||
match union.render_as {
|
||||
RenderAs::Guard => panic!("can this happen? inform Folkert"),
|
||||
RenderAs::Record(field_names) => {
|
||||
let mut arg_docs = Vec::with_capacity(args.len());
|
||||
|
||||
let tag = &union.alternatives[tag_id.0 as usize];
|
||||
let tag_name = tag.name.clone();
|
||||
for (label, v) in field_names.into_iter().zip(args.into_iter()) {
|
||||
match &v {
|
||||
Anything => {
|
||||
arg_docs.push(alloc.text(label.to_string()));
|
||||
}
|
||||
Literal(_) | Ctor(_, _, _) => {
|
||||
arg_docs.push(
|
||||
alloc
|
||||
.text(label.to_string())
|
||||
.append(alloc.reflow(": "))
|
||||
.append(pattern_to_doc_help(alloc, v, false)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We assume the alternatives are sorted. If not, this assert will trigger
|
||||
debug_assert!(tag_id == tag.tag_id);
|
||||
alloc
|
||||
.text("{ ")
|
||||
.append(alloc.intersperse(arg_docs, alloc.reflow(", ")))
|
||||
.append(" }")
|
||||
}
|
||||
RenderAs::Tag => {
|
||||
let has_args = !args.is_empty();
|
||||
let arg_docs = args
|
||||
.into_iter()
|
||||
.map(|v| pattern_to_doc_help(alloc, v, true));
|
||||
|
||||
let docs = std::iter::once(alloc.tag_name(tag_name)).chain(arg_docs);
|
||||
let tag = &union.alternatives[tag_id.0 as usize];
|
||||
let tag_name = tag.name.clone();
|
||||
|
||||
if in_type_param && has_args {
|
||||
alloc
|
||||
.text("(")
|
||||
.append(alloc.intersperse(docs, alloc.space()))
|
||||
.append(")")
|
||||
} else {
|
||||
alloc.intersperse(docs, alloc.space())
|
||||
// We assume the alternatives are sorted. If not, this assert will trigger
|
||||
debug_assert!(tag_id == tag.tag_id);
|
||||
|
||||
let docs = std::iter::once(alloc.tag_name(tag_name)).chain(arg_docs);
|
||||
|
||||
if in_type_param && has_args {
|
||||
alloc
|
||||
.text("(")
|
||||
.append(alloc.intersperse(docs, alloc.space()))
|
||||
.append(")")
|
||||
} else {
|
||||
alloc.intersperse(docs, alloc.space())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2254,6 +2254,7 @@ mod test_reporting {
|
|||
{ a: 4 } -> 4
|
||||
"#
|
||||
),
|
||||
// Hint: Looks like a record field guard is not exhaustive. Learn more about record pattern matches at TODO.
|
||||
indoc!(
|
||||
r#"
|
||||
-- UNSAFE PATTERN --------------------------------------------------------------
|
||||
|
@ -2265,7 +2266,7 @@ mod test_reporting {
|
|||
|
||||
Other possibilities include:
|
||||
|
||||
#Record _
|
||||
{ a }
|
||||
|
||||
I would have to crash if I saw one of those! Add branches for them!
|
||||
"#
|
||||
|
@ -2299,7 +2300,7 @@ mod test_reporting {
|
|||
|
||||
Other possibilities include:
|
||||
|
||||
#Record (Just _) _
|
||||
{ a: Just _, b }
|
||||
|
||||
I would have to crash if I saw one of those! Add branches for them!
|
||||
"#
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue