From ecee14b65f949c59d5d3df9a9970601871399e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Inf=C3=BChr?= Date: Wed, 25 Dec 2024 09:01:07 +0100 Subject: [PATCH] frontend: Support named fields when displaying patterns --- dora-frontend/src/exhaustiveness.rs | 140 ++++++++++++++++++++++------ dora-frontend/src/sema/classes.rs | 6 +- dora-frontend/src/sema/elements.rs | 5 +- dora-frontend/src/sema/enums.rs | 6 +- dora-frontend/src/sema/structs.rs | 6 +- 5 files changed, 133 insertions(+), 30 deletions(-) diff --git a/dora-frontend/src/exhaustiveness.rs b/dora-frontend/src/exhaustiveness.rs index c88436e4e..cdb0ef637 100644 --- a/dora-frontend/src/exhaustiveness.rs +++ b/dora-frontend/src/exhaustiveness.rs @@ -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; @@ -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, "(")?; @@ -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, + 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(()) } } @@ -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(" diff --git a/dora-frontend/src/sema/classes.rs b/dora-frontend/src/sema/classes.rs index 5ff85ace7..6b6b15fdc 100644 --- a/dora-frontend/src/sema/classes.rs +++ b/dora-frontend/src/sema/classes.rs @@ -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 { self.fields[idx].name } @@ -236,7 +240,7 @@ impl ElementWithFields for ClassDefinition { self.fields.len() } - fn fields<'a>(&'a self) -> Box + 'a> { + fn fields<'a>(&'a self) -> Box + 'a> { Box::new(self.fields.iter().map(|f| ElementField { id: f.id.to_usize(), name: f.name, diff --git a/dora-frontend/src/sema/elements.rs b/dora-frontend/src/sema/elements.rs index cee9e43e1..96defc257 100644 --- a/dora-frontend/src/sema/elements.rs +++ b/dora-frontend/src/sema/elements.rs @@ -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, @@ -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 + 'a>; + fn fields<'a>(&'a self) -> Box + 'a>; fn field_name(&self, idx: usize) -> Option; } diff --git a/dora-frontend/src/sema/enums.rs b/dora-frontend/src/sema/enums.rs index e6841a5ae..6f4f4e62a 100644 --- a/dora-frontend/src/sema/enums.rs +++ b/dora-frontend/src/sema/enums.rs @@ -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 { self.fields[idx].name } - fn fields<'a>(&'a self) -> Box + 'a> { + fn fields<'a>(&'a self) -> Box + 'a> { Box::new(self.fields.iter().enumerate().map(|(id, f)| ElementField { id, name: f.name, diff --git a/dora-frontend/src/sema/structs.rs b/dora-frontend/src/sema/structs.rs index 26858292d..7fb8ab5f9 100644 --- a/dora-frontend/src/sema/structs.rs +++ b/dora-frontend/src/sema/structs.rs @@ -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() } @@ -190,7 +194,7 @@ impl ElementWithFields for StructDefinition { self.fields[idx].name } - fn fields<'a>(&'a self) -> Box + 'a> { + fn fields<'a>(&'a self) -> Box + 'a> { Box::new(self.fields.iter().map(|f| ElementField { id: f.id.to_usize(), name: f.name,