diff options
| author | Lukas Wirth <lukastw97@gmail.com> | 2023-09-05 12:13:24 +0200 |
|---|---|---|
| committer | Lukas Wirth <lukastw97@gmail.com> | 2023-09-05 12:27:52 +0200 |
| commit | 15048304e3a806714cb7d64fbb6b40b5d76f0201 (patch) | |
| tree | 9a96328fa41a7a4f9203224a1087ac4192265b0e | |
| parent | 9b8eb807a365cfa42b8514c9ade73e9805ff54f1 (diff) | |
| download | rust-15048304e3a806714cb7d64fbb6b40b5d76f0201.tar.gz rust-15048304e3a806714cb7d64fbb6b40b5d76f0201.zip | |
Implement offset_of in hir-def and hir-ty
| -rw-r--r-- | crates/hir-def/src/body/lower.rs | 8 | ||||
| -rw-r--r-- | crates/hir-def/src/body/pretty.rs | 10 | ||||
| -rw-r--r-- | crates/hir-def/src/hir.rs | 9 | ||||
| -rw-r--r-- | crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs | 18 | ||||
| -rw-r--r-- | crates/hir-expand/src/builtin_fn_macro.rs | 6 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/closure.rs | 1 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/expr.rs | 1 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/mutability.rs | 1 | ||||
| -rw-r--r-- | crates/hir-ty/src/mir/lower.rs | 3 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/simple.rs | 89 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/traits.rs | 28 | ||||
| -rw-r--r-- | crates/parser/src/grammar/expressions/atom.rs | 19 | ||||
| -rw-r--r-- | crates/parser/test_data/parser/inline/ok/0132_box_expr.rast | 90 | ||||
| -rw-r--r-- | crates/parser/test_data/parser/inline/ok/0132_box_expr.rs | 5 | ||||
| -rw-r--r-- | crates/syntax/rust.ungram | 2 | ||||
| -rw-r--r-- | crates/syntax/src/ast/generated/nodes.rs | 3 | ||||
| -rw-r--r-- | crates/syntax/src/tests/sourcegen_ast.rs | 20 |
17 files changed, 120 insertions, 193 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 43f58a1bdb0..b536da2e57c 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -31,7 +31,7 @@ use crate::{ hir::{ dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, - Pat, PatId, RecordFieldPat, RecordLitField, Statement, + OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -649,7 +649,11 @@ impl ExprCollector<'_> { } ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), ast::Expr::AsmExpr(_) => self.missing_expr(), - ast::Expr::OffsetOfExpr(_) => self.missing_expr(), + ast::Expr::OffsetOfExpr(e) => { + let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); + let fields = e.fields().map(|it| it.as_name()).collect(); + self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr) + } ast::Expr::FormatArgsExpr(_) => self.missing_expr(), }) } diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 5d71abe37cc..6058fcbb652 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -3,6 +3,7 @@ use std::fmt::{self, Write}; use hir_expand::db::ExpandDatabase; +use itertools::Itertools; use syntax::ast::HasName; use crate::{ @@ -154,6 +155,15 @@ impl Printer<'_> { match expr { Expr::Missing => w!(self, "�"), Expr::Underscore => w!(self, "_"), + Expr::OffsetOf(offset_of) => { + w!(self, "builtin#offset_of!("); + self.print_type_ref(&offset_of.container); + w!( + self, + ", {})", + offset_of.fields.iter().format_with(".", |field, f| f(&field.display(self.db))) + ); + } Expr::Path(path) => self.print_path(path), Expr::If { condition, then_branch, else_branch } => { w!(self, "if "); diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 6591c92ac62..f03df800a05 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -281,6 +281,13 @@ pub enum Expr { Array(Array), Literal(Literal), Underscore, + OffsetOf(OffsetOf), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct OffsetOf { + pub container: Interned<TypeRef>, + pub fields: Box<[Name]>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -341,7 +348,7 @@ impl Expr { pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) { match self { Expr::Missing => {} - Expr::Path(_) => {} + Expr::Path(_) | Expr::OffsetOf(_) => {} Expr::If { condition, then_branch, else_branch } => { f(*condition); f(*then_branch); diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index ea02072ff5c..a350511c7a1 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -42,12 +42,22 @@ fn main() { } } "#, - expect![[r#" + expect![[r##" #[rustc_builtin_macro] -macro_rules! column {() => {}} +macro_rules! asm {() => {}} -fn main() { 0 as u32; } -"#]], +fn main() { + let i: u64 = 3; + let o: u64; + unsafe { + builtin #asm ( { + $crate::format_args!("mov {0}, {1}"); + $crate::format_args!("add {0}, 5"); + } + ); + } +} +"##]], ); } diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 7eec6361760..00ee5e8b9d0 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -419,9 +419,9 @@ fn asm_expand( let pound = quote! {@PUNCT '#'}; let expanded = quote! { - builtin #pound asm { - ##literals - } + builtin #pound asm ( + {##literals} + ) }; ExpandResult::ok(expanded) } diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index cb442de843a..ceb96a71653 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -452,6 +452,7 @@ impl InferenceContext<'_> { fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { match &self.body[tgt_expr] { + Expr::OffsetOf(_) => (), Expr::If { condition, then_branch, else_branch } => { self.consume_expr(*condition); self.consume_expr(*then_branch); diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 8b352141084..d139d929fc3 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -843,6 +843,7 @@ impl InferenceContext<'_> { }); expected } + Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), }; // use a new type variable if we got unknown here let ty = self.insert_type_vars_shallow(ty); diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 396ca0044ff..853c171a06a 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -35,6 +35,7 @@ impl InferenceContext<'_> { fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) { match &self.body[tgt_expr] { Expr::Missing => (), + Expr::OffsetOf(_) => (), &Expr::If { condition, then_branch, else_branch } => { self.infer_mut_expr(condition, Mutability::Not); self.infer_mut_expr(then_branch, Mutability::Not); diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 7c15aa42fd2..3410c87d391 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -370,6 +370,9 @@ impl<'ctx> MirLowerCtx<'ctx> { mut current: BasicBlockId, ) -> Result<Option<BasicBlockId>> { match &self.body.exprs[expr_id] { + Expr::OffsetOf(_) => { + not_supported!("builtin#offset_of") + } Expr::Missing => { if let DefWithBodyId::FunctionId(f) = self.owner { let assoc = f.lookup(self.db.upcast()); diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index 2ad7946c8ac..e7cb7cd4174 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -3,55 +3,6 @@ use expect_test::expect; use super::{check, check_infer, check_no_mismatches, check_types}; #[test] -fn infer_box() { - check_types( - r#" -//- /main.rs crate:main deps:std -fn test() { - let x = box 1; - let t = (x, box x, box &1, box [1]); - t; -} //^ (Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; 1]>) - -//- /std.rs crate:std -#[prelude_import] use prelude::*; -mod prelude {} - -mod boxed { - #[lang = "owned_box"] - pub struct Box<T: ?Sized> { - inner: *mut T, - } -} -"#, - ); -} - -#[test] -fn infer_box_with_allocator() { - check_types( - r#" -//- /main.rs crate:main deps:std -fn test() { - let x = box 1; - let t = (x, box x, box &1, box [1]); - t; -} //^ (Box<i32, {unknown}>, Box<Box<i32, {unknown}>, {unknown}>, Box<&i32, {unknown}>, Box<[i32; 1], {unknown}>) - -//- /std.rs crate:std -#[prelude_import] use prelude::*; -mod boxed { - #[lang = "owned_box"] - pub struct Box<T: ?Sized, A: Allocator> { - inner: *mut T, - allocator: A, - } -} -"#, - ); -} - -#[test] fn infer_adt_self() { check_types( r#" @@ -2763,8 +2714,8 @@ impl<T> [T] { } fn test() { - let vec = <[_]>::into_vec(box [1i32]); - let v: Vec<Box<dyn B>> = <[_]> :: into_vec(box [box Astruct]); + let vec = <[_]>::into_vec(#[rustc_box] Box::new([1i32])); + let v: Vec<Box<dyn B>> = <[_]> :: into_vec(#[rustc_box] Box::new([#[rustc_box] Box::new(Astruct)])); } trait B{} @@ -2774,20 +2725,20 @@ impl B for Astruct {} expect![[r#" 604..608 'self': Box<[T], A> 637..669 '{ ... }': Vec<T, A> - 683..796 '{ ...t]); }': () + 683..853 '{ ...])); }': () 693..696 'vec': Vec<i32, Global> 699..714 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global> - 699..726 '<[_]>:...1i32])': Vec<i32, Global> - 715..725 'box [1i32]': Box<[i32; 1], Global> - 719..725 '[1i32]': [i32; 1] - 720..724 '1i32': i32 - 736..737 'v': Vec<Box<dyn B, Global>, Global> - 757..774 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global> - 757..793 '<[_]> ...ruct])': Vec<Box<dyn B, Global>, Global> - 775..792 'box [b...truct]': Box<[Box<dyn B, Global>; 1], Global> - 779..792 '[box Astruct]': [Box<dyn B, Global>; 1] - 780..791 'box Astruct': Box<Astruct, Global> - 784..791 'Astruct': Astruct + 699..745 '<[_]>:...i32]))': Vec<i32, Global> + 715..744 '#[rust...1i32])': Box<[i32; 1], Global> + 737..743 '[1i32]': [i32; 1] + 738..742 '1i32': i32 + 755..756 'v': Vec<Box<dyn B, Global>, Global> + 776..793 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global> + 776..850 '<[_]> ...ct)]))': Vec<Box<dyn B, Global>, Global> + 794..849 '#[rust...uct)])': Box<[Box<dyn B, Global>; 1], Global> + 816..848 '[#[rus...ruct)]': [Box<dyn B, Global>; 1] + 817..847 '#[rust...truct)': Box<Astruct, Global> + 839..846 'Astruct': Astruct "#]], ) } @@ -3649,3 +3600,15 @@ fn main() { "#, ); } + +#[test] +fn offset_of() { + check_types( + r#" +fn main() { + builtin#offset_of((,), 0); + // ^^^^^^^^^^^^^^^^^^^^^^^^^ usize +} +"#, + ); +} diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 542df8b3468..d36b885ec14 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -162,16 +162,16 @@ unsafe impl Allocator for Global {} #[lang = "owned_box"] #[fundamental] -pub struct Box<T: ?Sized, A: Allocator = Global>; +pub struct Box<T: ?Sized, A: Allocator = Global>(T); impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {} fn send() -> Box<dyn Future<Output = ()> + Send + 'static>{ - box async move {} + Box(async move {}) } fn not_send() -> Box<dyn Future<Output = ()> + 'static> { - box async move {} + Box(async move {}) } "#, ); @@ -3057,7 +3057,7 @@ impl<T: ?Sized> core::ops::Deref for Box<T> { fn foo() { let s = None; - let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {}); + let f: Box<dyn FnOnce(&Option<i32>)> = Box { inner: &mut (|ps| {}) }; f(&s); }"#, expect![[r#" @@ -3068,19 +3068,19 @@ fn foo() { 186..197 '*self.inner': T 187..191 'self': &Box<T> 187..197 'self.inner': *mut T - 218..308 '{ ...&s); }': () + 218..324 '{ ...&s); }': () 228..229 's': Option<i32> 232..236 'None': Option<i32> 246..247 'f': Box<dyn FnOnce(&Option<i32>)> - 281..294 'box (|ps| {})': Box<impl Fn(&Option<i32>)> - 286..293 '|ps| {}': impl Fn(&Option<i32>) - 287..289 'ps': &Option<i32> - 291..293 '{}': () - 300..301 'f': Box<dyn FnOnce(&Option<i32>)> - 300..305 'f(&s)': () - 302..304 '&s': &Option<i32> - 303..304 's': Option<i32> - 281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<impl Fn(&Option<i32>)> + 281..310 'Box { ... {}) }': Box<dyn FnOnce(&Option<i32>)> + 294..308 '&mut (|ps| {})': &mut impl Fn(&Option<i32>) + 300..307 '|ps| {}': impl Fn(&Option<i32>) + 301..303 'ps': &Option<i32> + 305..307 '{}': () + 316..317 'f': Box<dyn FnOnce(&Option<i32>)> + 316..321 'f(&s)': () + 318..320 '&s': &Option<i32> + 319..320 's': Option<i32> "#]], ); } diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index 7a4f4e4777d..1dd68f556c4 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs @@ -220,24 +220,39 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { // fn foo() { // builtin#asm(0); // builtin#format_args(0); -// builtin#builtin(0); +// builtin#offset_of(Foo, bar.baz.0); // } fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> { let m = p.start(); p.bump_remap(T![builtin]); p.bump(T![#]); if p.at_contextual_kw(T![offset_of]) { + p.bump_remap(T![offset_of]); p.expect(T!['(']); type_(p); - p.bump(T![,]); + p.expect(T![,]); + while !p.at(EOF) && !p.at(T![')']) { + if p.at(IDENT) || p.at(INT_NUMBER) { + name_ref_or_index(p); + // } else if p.at(FLOAT_NUMBER) { + // FIXME: needs float hack + } else { + p.err_and_bump("expected field name or number"); + } + if !p.at(T![')']) { + p.expect(T![.]); + } + } p.expect(T![')']); Some(m.complete(p, OFFSET_OF_EXPR)) } else if p.at_contextual_kw(T![format_args]) { + p.bump_remap(T![format_args]); p.expect(T!['(']); expr(p); p.expect(T![')']); Some(m.complete(p, FORMAT_ARGS_EXPR)) } else if p.at_contextual_kw(T![asm]) { + p.bump_remap(T![asm]); p.expect(T!['(']); expr(p); p.expect(T![')']); diff --git a/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast b/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast deleted file mode 100644 index b21f37cd85a..00000000000 --- a/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast +++ /dev/null @@ -1,90 +0,0 @@ -SOURCE_FILE - FN - FN_KW "fn" - WHITESPACE " " - NAME - IDENT "foo" - PARAM_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE "\n " - LET_STMT - LET_KW "let" - WHITESPACE " " - IDENT_PAT - NAME - IDENT "x" - WHITESPACE " " - EQ "=" - WHITESPACE " " - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "1i32" - SEMICOLON ";" - WHITESPACE "\n " - LET_STMT - LET_KW "let" - WHITESPACE " " - IDENT_PAT - NAME - IDENT "y" - WHITESPACE " " - EQ "=" - WHITESPACE " " - TUPLE_EXPR - L_PAREN "(" - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "1i32" - COMMA "," - WHITESPACE " " - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "2i32" - R_PAREN ")" - SEMICOLON ";" - WHITESPACE "\n " - LET_STMT - LET_KW "let" - WHITESPACE " " - IDENT_PAT - NAME - IDENT "z" - WHITESPACE " " - EQ "=" - WHITESPACE " " - CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "Foo" - ARG_LIST - L_PAREN "(" - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "1i32" - COMMA "," - WHITESPACE " " - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "2i32" - R_PAREN ")" - SEMICOLON ";" - WHITESPACE "\n" - R_CURLY "}" - WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs b/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs deleted file mode 100644 index fc9923b7137..00000000000 --- a/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn foo() { - let x = box 1i32; - let y = (box 1i32, box 2i32); - let z = Foo(box 1i32, box 2i32); -} diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index bd9d2ad1784..d7b1c91216b 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -376,7 +376,7 @@ Expr = | UnderscoreExpr OffsetOfExpr = - Attr* 'builtin' '#' 'offset_of' '(' ')' + Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')' AsmExpr = Attr* 'builtin' '#' 'asm' '(' ')' diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 82b9c28a566..d817589223e 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -1014,6 +1014,9 @@ impl OffsetOfExpr { support::token(&self.syntax, T![offset_of]) } pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } + pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } + pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) } + pub fn fields(&self) -> AstChildren<NameRef> { support::children(&self.syntax) } pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } } diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs index c49c5fa108b..56227fce9b5 100644 --- a/crates/syntax/src/tests/sourcegen_ast.rs +++ b/crates/syntax/src/tests/sourcegen_ast.rs @@ -623,7 +623,7 @@ fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> { } fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, rule: &Rule) { - if lower_comma_list(acc, grammar, label, rule) { + if lower_seperated_list(acc, grammar, label, rule) { return; } @@ -689,7 +689,7 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r } // (T (',' T)* ','?) -fn lower_comma_list( +fn lower_seperated_list( acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, @@ -699,19 +699,23 @@ fn lower_comma_list( Rule::Seq(it) => it, _ => return false, }; - let (node, repeat, trailing_comma) = match rule.as_slice() { - [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => { - (node, repeat, trailing_comma) + let (node, repeat, trailing_sep) = match rule.as_slice() { + [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => { + (node, repeat, Some(trailing_sep)) } + [Rule::Node(node), Rule::Rep(repeat)] => (node, repeat, None), _ => return false, }; let repeat = match &**repeat { Rule::Seq(it) => it, _ => return false, }; - match repeat.as_slice() { - [comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (), - _ => return false, + if !matches!( + repeat.as_slice(), + [comma, Rule::Node(n)] + if trailing_sep.map_or(true, |it| comma == &**it) && n == node + ) { + return false; } let ty = grammar[*node].name.clone(); let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty))); |
