Skip to content

Commit

Permalink
frontend: Support named fields when displaying patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
dinfuehr committed Dec 25, 2024
1 parent bb88f0a commit ecee14b
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 30 deletions.
140 changes: 114 additions & 26 deletions dora-frontend/src/exhaustiveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use dora_parser::ast::visit::{self, Visitor};
use dora_parser::Span;

use crate::sema::{
AnalysisData, ClassDefinitionId, EnumDefinitionId, IdentType, Sema, SourceFileId,
StructDefinitionId,
AnalysisData, ClassDefinitionId, ElementWithFields, EnumDefinitionId, IdentType, Sema,
SourceFileId, StructDefinitionId,
};
use crate::ErrorMessage;

Expand Down Expand Up @@ -140,32 +140,34 @@ fn display_pattern(sa: &Sema, pattern: Pattern, output: &mut String) -> fmt::Res
constructor_id,
params,
..
} => {
let mut is_tuple = false;

match constructor_id {
ConstructorId::Bool => unreachable!(),

ConstructorId::Enum(enum_id, variant_id) => {
let enum_ = sa.enum_(enum_id);
let variant = enum_.variants()[variant_id].name;
write!(output, "{}::{}", enum_.name(sa), sa.interner.str(variant))?;
}
} => match constructor_id {
ConstructorId::Bool => unreachable!(),

ConstructorId::Class(class_id) => {
let class = sa.class(class_id);
write!(output, "{}", class.name(sa))?;
}
ConstructorId::Enum(enum_id, variant_id) => {
let enum_ = sa.enum_(enum_id);
let variant = &enum_.variants[variant_id];
write!(
output,
"{}::{}",
enum_.name(sa),
sa.interner.str(variant.name)
)?;
display_params(sa, variant, params, output)
}

ConstructorId::Struct(struct_id) => {
let struct_ = sa.struct_(struct_id);
write!(output, "{}", struct_.name(sa))?;
}
ConstructorId::Class(class_id) => {
let class = sa.class(class_id);
write!(output, "{}", class.name(sa))?;
display_params(sa, class, params, output)
}

ConstructorId::Tuple => is_tuple = true,
ConstructorId::Struct(struct_id) => {
let struct_ = sa.struct_(struct_id);
write!(output, "{}", struct_.name(sa))?;
display_params(sa, struct_, params, output)
}

if !params.is_empty() || is_tuple {
ConstructorId::Tuple => {
let mut first = true;
write!(output, "(")?;

Expand All @@ -177,13 +179,42 @@ fn display_pattern(sa: &Sema, pattern: Pattern, output: &mut String) -> fmt::Res
first = false;
}

write!(output, ")")?;
write!(output, ")")
}
},

Ok(())
Pattern::Guard => unreachable!(),
}
}

fn display_params(
sa: &Sema,
element: &dyn ElementWithFields,
params: Vec<Pattern>,
output: &mut String,
) -> fmt::Result {
if element.fields_len() > 0 {
let mut first = true;
write!(output, "(")?;
let emit_names = element.field_name_style().is_named();

for (field, param) in element.fields().zip(params.into_iter().rev()) {
if !first {
output.write_str(", ")?;
}

if emit_names {
let name = field.name.expect("missing name");
write!(output, "{} = ", sa.interner.str(name))?;
}

display_pattern(sa, param, output)?;
first = false;
}

Pattern::Guard => unreachable!(),
write!(output, ")")
} else {
Ok(())
}
}

Expand Down Expand Up @@ -1923,6 +1954,63 @@ mod tests {
");
}

#[test]
fn non_exhaustive_enum_with_named_fields() {
err(
"
enum Foo { A { a: Bool, b: Int }, B(Int, Int) }
fn f(v: Foo) {
match v {
Foo::A(a = true, b = Int) => {}
Foo::B(_, _) => {}
}
}
",
(4, 23),
ErrorMessage::NonExhaustiveMatch(vec!["Foo::A(a = false, b = _)".into()]),
);
}

#[test]
fn non_exhaustive_class_named_fields() {
err(
"
class Foo {
a: Bool,
b: Int
}
fn f(v: Foo) {
match v {
Foo(a = true, ..) => {}
}
}
",
(8, 23),
ErrorMessage::NonExhaustiveMatch(vec!["Foo(a = false, b = _)".into()]),
);
}

#[test]
fn non_exhaustive_struct_named_fields() {
err(
"
struct Foo {
a: Int,
b: Bool
}
fn f(v: Foo) {
match v {
Foo(b = true, ..) => {}
}
}
",
(8, 23),
ErrorMessage::NonExhaustiveMatch(vec!["Foo(a = _, b = false)".into()]),
);
}

#[test]
fn exhaustive_enum_with_payload_and_rest() {
ok("
Expand Down
6 changes: 5 additions & 1 deletion dora-frontend/src/sema/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ impl ElementAccess for ClassDefinition {
}

impl ElementWithFields for ClassDefinition {
fn field_name_style(&self) -> ast::FieldNameStyle {
self.field_name_style
}

fn field_name(&self, idx: usize) -> Option<Name> {
self.fields[idx].name
}
Expand All @@ -236,7 +240,7 @@ impl ElementWithFields for ClassDefinition {
self.fields.len()
}

fn fields<'a>(&'a self) -> Box<dyn Iterator<Item = ElementField> + 'a> {
fn fields<'a>(&'a self) -> Box<dyn DoubleEndedIterator<Item = ElementField> + 'a> {
Box::new(self.fields.iter().map(|f| ElementField {
id: f.id.to_usize(),
name: f.name,
Expand Down
5 changes: 4 additions & 1 deletion dora-frontend/src/sema/elements.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::rc::Rc;

use dora_parser::ast;

use crate::sema::{
AliasDefinition, AliasDefinitionId, AliasParent, ClassDefinitionId, ConstDefinitionId,
EnumDefinitionId, ExtensionDefinitionId, FctDefinition, FctDefinitionId, FctParent,
Expand Down Expand Up @@ -72,8 +74,9 @@ pub trait ElementAccess {
}

pub trait ElementWithFields {
fn field_name_style(&self) -> ast::FieldNameStyle;
fn fields_len(&self) -> usize;
fn fields<'a>(&'a self) -> Box<dyn Iterator<Item = ElementField> + 'a>;
fn fields<'a>(&'a self) -> Box<dyn DoubleEndedIterator<Item = ElementField> + 'a>;
fn field_name(&self, idx: usize) -> Option<Name>;
}

Expand Down
6 changes: 5 additions & 1 deletion dora-frontend/src/sema/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,15 @@ pub struct EnumVariant {
}

impl ElementWithFields for EnumVariant {
fn field_name_style(&self) -> ast::FieldNameStyle {
self.field_name_style
}

fn field_name(&self, idx: usize) -> Option<Name> {
self.fields[idx].name
}

fn fields<'a>(&'a self) -> Box<dyn Iterator<Item = super::ElementField> + 'a> {
fn fields<'a>(&'a self) -> Box<dyn DoubleEndedIterator<Item = ElementField> + 'a> {
Box::new(self.fields.iter().enumerate().map(|(id, f)| ElementField {
id,
name: f.name,
Expand Down
6 changes: 5 additions & 1 deletion dora-frontend/src/sema/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ impl ElementAccess for StructDefinition {
}

impl ElementWithFields for StructDefinition {
fn field_name_style(&self) -> ast::FieldNameStyle {
self.field_name_style
}

fn fields_len(&self) -> usize {
self.fields.len()
}
Expand All @@ -190,7 +194,7 @@ impl ElementWithFields for StructDefinition {
self.fields[idx].name
}

fn fields<'a>(&'a self) -> Box<dyn Iterator<Item = super::ElementField> + 'a> {
fn fields<'a>(&'a self) -> Box<dyn DoubleEndedIterator<Item = ElementField> + 'a> {
Box::new(self.fields.iter().map(|f| ElementField {
id: f.id.to_usize(),
name: f.name,
Expand Down

0 comments on commit ecee14b

Please sign in to comment.