Merge pull request #19533 from Veykril/push-zxlpwkvzxzws chore: Set up a proper job matrix for rust-cross
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index 83857cf..9fbeace 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs
@@ -303,6 +303,19 @@ pub toolchain: Option<Version>, } +impl CrateWorkspaceData { + pub fn is_atleast_187(&self) -> bool { + const VERSION_187: Version = Version { + major: 1, + minor: 87, + patch: 0, + pre: Prerelease::EMPTY, + build: BuildMetadata::EMPTY, + }; + self.toolchain.as_ref().map_or(false, |v| *v >= VERSION_187) + } +} + fn toolchain_channel(db: &dyn RootQueryDb, krate: Crate) -> Option<ReleaseChannel> { krate.workspace_data(db).toolchain.as_ref().and_then(|v| ReleaseChannel::from_str(&v.pre)) }
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 2d6e8f0..1791a1c 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs
@@ -2321,54 +2321,99 @@ zero_pad, debug_hex, } = &placeholder.format_options; - let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' ')))); - let align = { - let align = LangItem::FormatAlignment.ty_rel_path( - self.db, - self.krate, - match alignment { - Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left.clone()), - Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right.clone()), - Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center.clone()), - None => Name::new_symbol_root(sym::Unknown.clone()), - }, - ); - match align { - Some(path) => self.alloc_expr_desugared(Expr::Path(path)), - None => self.missing_expr(), - } - }; - // This needs to match `Flag` in library/core/src/fmt/rt.rs. - let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32) - | (((sign == Some(FormatSign::Minus)) as u32) << 1) - | ((alternate as u32) << 2) - | ((zero_pad as u32) << 3) - | (((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4) - | (((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5); - let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - flags as u128, - Some(BuiltinUint::U32), - ))); - let precision = self.make_count(precision, argmap); - let width = self.make_count(width, argmap); + let precision_expr = self.make_count(precision, argmap); + let width_expr = self.make_count(width, argmap); - let format_placeholder_new = { - let format_placeholder_new = LangItem::FormatPlaceholder.ty_rel_path( - self.db, - self.krate, - Name::new_symbol_root(sym::new.clone()), - ); - match format_placeholder_new { - Some(path) => self.alloc_expr_desugared(Expr::Path(path)), - None => self.missing_expr(), - } - }; + if self.krate.workspace_data(self.db).is_atleast_187() { + // These need to match the constants in library/core/src/fmt/rt.rs. + let align = match alignment { + Some(FormatAlignment::Left) => 0, + Some(FormatAlignment::Right) => 1, + Some(FormatAlignment::Center) => 2, + None => 3, + }; + // This needs to match `Flag` in library/core/src/fmt/rt.rs. + let flags = fill.unwrap_or(' ') as u32 + | ((sign == Some(FormatSign::Plus)) as u32) << 21 + | ((sign == Some(FormatSign::Minus)) as u32) << 22 + | (alternate as u32) << 23 + | (zero_pad as u32) << 24 + | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25 + | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26 + | (width.is_some() as u32) << 27 + | (precision.is_some() as u32) << 28 + | align << 29 + | 1 << 31; // Highest bit always set. + let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( + flags as u128, + Some(BuiltinUint::U32), + ))); - self.alloc_expr_desugared(Expr::Call { - callee: format_placeholder_new, - args: Box::new([position, fill, align, flags, precision, width]), - }) + let position = RecordLitField { + name: Name::new_symbol_root(sym::position.clone()), + expr: position, + }; + let flags = + RecordLitField { name: Name::new_symbol_root(sym::flags.clone()), expr: flags }; + let precision = RecordLitField { + name: Name::new_symbol_root(sym::precision.clone()), + expr: precision_expr, + }; + let width = RecordLitField { + name: Name::new_symbol_root(sym::width.clone()), + expr: width_expr, + }; + self.alloc_expr_desugared(Expr::RecordLit { + path: LangItem::FormatPlaceholder.path(self.db, self.krate).map(Box::new), + fields: Box::new([position, flags, precision, width]), + spread: None, + }) + } else { + let format_placeholder_new = { + let format_placeholder_new = LangItem::FormatPlaceholder.ty_rel_path( + self.db, + self.krate, + Name::new_symbol_root(sym::new.clone()), + ); + match format_placeholder_new { + Some(path) => self.alloc_expr_desugared(Expr::Path(path)), + None => self.missing_expr(), + } + }; + // This needs to match `Flag` in library/core/src/fmt/rt.rs. + let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32) + | (((sign == Some(FormatSign::Minus)) as u32) << 1) + | ((alternate as u32) << 2) + | ((zero_pad as u32) << 3) + | (((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4) + | (((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5); + let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( + flags as u128, + Some(BuiltinUint::U32), + ))); + let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' ')))); + let align = { + let align = LangItem::FormatAlignment.ty_rel_path( + self.db, + self.krate, + match alignment { + Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left.clone()), + Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right.clone()), + Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center.clone()), + None => Name::new_symbol_root(sym::Unknown.clone()), + }, + ); + match align { + Some(path) => self.alloc_expr_desugared(Expr::Path(path)), + None => self.missing_expr(), + } + }; + self.alloc_expr_desugared(Expr::Call { + callee: format_placeholder_new, + args: Box::new([position, fill, align, flags, precision_expr, width_expr]), + }) + } } /// Generate a hir expression for a format_args Count.
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index ddf1a21..f990309 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -1979,3 +1979,51 @@ "#]], ); } + +#[test] +fn semicolon_does_not_glue() { + check( + r#" +macro_rules! bug { + ($id: expr) => { + true + }; + ($id: expr; $($attr: ident),*) => { + true + }; + ($id: expr; $($attr: ident),*; $norm: expr) => { + true + }; + ($id: expr; $($attr: ident),*;; $print: expr) => { + true + }; + ($id: expr; $($attr: ident),*; $norm: expr; $print: expr) => { + true + }; +} + +let _ = bug!(a;;;test); + "#, + expect![[r#" +macro_rules! bug { + ($id: expr) => { + true + }; + ($id: expr; $($attr: ident),*) => { + true + }; + ($id: expr; $($attr: ident),*; $norm: expr) => { + true + }; + ($id: expr; $($attr: ident),*;; $print: expr) => { + true + }; + ($id: expr; $($attr: ident),*; $norm: expr; $print: expr) => { + true + }; +} + +let _ = true; + "#]], + ); +}
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index 1bbed01..cb4fcd8 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -582,8 +582,8 @@ } impl <A: Arbitrary> $crate::arbitrary::Arbitrary for Vec<A> { - type Parameters = RangedParams1<A::Parameters>; - type Strategy = VecStrategy<A::Strategy>; + type Parameters = RangedParams1<A::Parameters> ; + type Strategy = VecStrategy<A::Strategy> ; fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { { let product_unpack![range, a] = args; vec(any_with::<A>(a), range)
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 0bd605c..0448ecd 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs
@@ -1534,10 +1534,6 @@ None => return (self.err_ty(), None), } }; - let Some(mod_path) = path.mod_path() else { - never!("resolver should always resolve lang item paths"); - return (self.err_ty(), None); - }; return match resolution { TypeNs::AdtId(AdtId::StructId(strukt)) => { let substs = path_ctx.substs_from_path(strukt.into(), true); @@ -1567,6 +1563,10 @@ let Some(remaining_idx) = unresolved else { drop(ctx); + let Some(mod_path) = path.mod_path() else { + never!("resolver should always resolve lang item paths"); + return (self.err_ty(), None); + }; return self.resolve_variant_on_alias(ty, None, mod_path); }; @@ -1630,6 +1630,10 @@ (ty, variant) } TypeNs::TypeAliasId(it) => { + let Some(mod_path) = path.mod_path() else { + never!("resolver should always resolve lang item paths"); + return (self.err_ty(), None); + }; let substs = path_ctx.substs_from_path_segment(it.into(), true, None); drop(ctx); let ty = self.db.ty(it.into());
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index afa1f6d..651ec15 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs
@@ -570,10 +570,17 @@ source_map: &hir_def::expr_store::BodySourceMap, ) -> Option<AnyDiagnostic> { let expr_syntax = |expr| { - source_map.expr_syntax(expr).inspect_err(|_| stdx::never!("synthetic syntax")).ok() + source_map + .expr_syntax(expr) + .inspect_err(|_| stdx::never!("inference diagnostic in desugared expr")) + .ok() }; - let pat_syntax = - |pat| source_map.pat_syntax(pat).inspect_err(|_| stdx::never!("synthetic syntax")).ok(); + let pat_syntax = |pat| { + source_map + .pat_syntax(pat) + .inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern")) + .ok() + }; let expr_or_pat_syntax = |id| match id { ExprOrPatId::ExprId(expr) => expr_syntax(expr), ExprOrPatId::PatId(pat) => pat_syntax(pat),
diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs index 4ae00cc..58e7f58 100644 --- a/crates/ide-completion/src/completions/item_list.rs +++ b/crates/ide-completion/src/completions/item_list.rs
@@ -114,6 +114,7 @@ add_keyword("trait", "trait $1 {\n $0\n}"); if no_vis_qualifiers { add_keyword("impl", "impl $1 {\n $0\n}"); + add_keyword("impl for", "impl $1 for $2 {\n $0\n}"); } } @@ -144,6 +145,7 @@ add_keyword("use", "use $0"); if no_vis_qualifiers { add_keyword("impl", "impl $1 {\n $0\n}"); + add_keyword("impl for", "impl $1 for $2 {\n $0\n}"); } }
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index 14b0d54..0397424 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs
@@ -56,6 +56,7 @@ kw extern kw fn kw impl + kw impl for kw trait "#]], );
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index 98da2cb..b30ac43 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs
@@ -171,6 +171,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -249,6 +250,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -300,6 +302,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -375,6 +378,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -961,6 +965,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -1003,6 +1008,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -1095,6 +1101,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -1137,6 +1144,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -1179,6 +1187,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -1231,6 +1240,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -1285,6 +1295,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -1529,6 +1540,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -2001,6 +2013,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -2073,6 +2086,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop
diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs index be2c37d..5568903 100644 --- a/crates/ide-completion/src/tests/item.rs +++ b/crates/ide-completion/src/tests/item.rs
@@ -284,6 +284,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop
diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs index 841c421..fcdf10c 100644 --- a/crates/ide-completion/src/tests/item_list.rs +++ b/crates/ide-completion/src/tests/item_list.rs
@@ -16,6 +16,7 @@ kw extern kw fn kw impl + kw impl for kw mod kw pub kw pub(crate) @@ -50,6 +51,7 @@ kw extern kw fn kw impl + kw impl for kw mod kw pub kw pub(crate) @@ -83,6 +85,7 @@ kw extern kw fn kw impl + kw impl for kw mod kw pub kw pub(crate) @@ -122,6 +125,7 @@ kw extern kw fn kw impl + kw impl for kw trait "#]], ); @@ -385,6 +389,7 @@ kw extern kw fn kw impl + kw impl for kw mod kw pub kw pub(crate)
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 70caeac..15518e9 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs
@@ -1008,6 +1008,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -1059,6 +1060,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -1184,6 +1186,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop @@ -1441,6 +1444,7 @@ kw if kw if let kw impl + kw impl for kw let kw letm kw loop
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index a01afd2..258d80e 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs
@@ -627,7 +627,7 @@ return Some((def, file, Some(format!("variant.{}", ev.name(db).as_str())))); } Definition::Const(c) => { - format!("const.{}.html", c.name(db)?.as_str()) + format!("constant.{}.html", c.name(db)?.as_str()) } Definition::Static(s) => { format!("static.{}.html", s.name(db).as_str())
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 4811f1f..7766897 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs
@@ -677,4 +677,26 @@ crate::Foo;"#]], ); } + + #[test] + fn semi_glueing() { + check( + r#" +macro_rules! __log_value { + ($key:ident :$capture:tt =) => {}; +} + +macro_rules! __log { + ($key:tt $(:$capture:tt)? $(= $value:expr)?; $($arg:tt)+) => { + __log_value!($key $(:$capture)* = $($value)*); + }; +} + +__log!(written:%; "Test"$0); + "#, + expect![[r#" + __log! + "#]], + ); + } }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 7d855cf..13b161e 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs
@@ -753,7 +753,7 @@ frange: FileRange, ) -> Cancellable<Vec<Assist>> { let include_fixes = match &assist_config.allowed { - Some(it) => it.iter().any(|&it| it == AssistKind::QuickFix), + Some(it) => it.contains(&AssistKind::QuickFix), None => true, };
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index 4841f48..a9ed185 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs
@@ -161,6 +161,7 @@ bitxor_assign, bitxor, bool, + bootstrap, box_free, Box, boxed, @@ -525,4 +526,8 @@ ignore_flyimport, ignore_flyimport_methods, ignore_methods, + position, + flags, + precision, + width, }
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index b1f542e..f3f9f29 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/crates/mbe/src/expander/transcriber.rs
@@ -389,8 +389,13 @@ match ctx.bindings.get_fragment(v, id, &mut ctx.nesting, marker) { Ok(fragment) => { match fragment { - Fragment::Tokens(tt) => builder.extend_with_tt(tt.strip_invisible()), - Fragment::TokensOwned(tt) => builder.extend_with_tt(tt.view().strip_invisible()), + // rustc spacing is not like ours. Ours is like proc macros', it dictates how puncts will actually be joined. + // rustc uses them mostly for pretty printing. So we have to deviate a bit from what rustc does here. + // Basically, a metavariable can never be joined with whatever after it. + Fragment::Tokens(tt) => builder.extend_with_tt_alone(tt.strip_invisible()), + Fragment::TokensOwned(tt) => { + builder.extend_with_tt_alone(tt.view().strip_invisible()) + } Fragment::Expr(sub) => { let sub = sub.strip_invisible(); let mut span = id; @@ -402,7 +407,7 @@ if wrap_in_parens { builder.open(tt::DelimiterKind::Parenthesis, span); } - builder.extend_with_tt(sub); + builder.extend_with_tt_alone(sub); if wrap_in_parens { builder.close(span); }
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index 7be49cb..8a2f124 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs
@@ -6,7 +6,10 @@ use arrayvec::ArrayVec; use intern::{Symbol, sym}; use span::{Edition, Span, SyntaxContext}; -use tt::iter::{TtElement, TtIter}; +use tt::{ + MAX_GLUED_PUNCT_LEN, + iter::{TtElement, TtIter}, +}; use crate::ParseError; @@ -96,7 +99,7 @@ delimiter: tt::Delimiter<Span>, }, Literal(tt::Literal<Span>), - Punct(Box<ArrayVec<tt::Punct<Span>, 3>>), + Punct(Box<ArrayVec<tt::Punct<Span>, MAX_GLUED_PUNCT_LEN>>), Ident(tt::Ident<Span>), } @@ -151,7 +154,7 @@ pub(crate) enum Separator { Literal(tt::Literal<Span>), Ident(tt::Ident<Span>), - Puncts(ArrayVec<tt::Punct<Span>, 3>), + Puncts(ArrayVec<tt::Punct<Span>, MAX_GLUED_PUNCT_LEN>), } // Note that when we compare a Separator, we just care about its textual value.
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index a0fb4a7..7a139ea 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs
@@ -1664,6 +1664,7 @@ vec![ CfgAtom::Flag(sym::debug_assertions.clone()), CfgAtom::Flag(sym::miri.clone()), + CfgAtom::Flag(sym::bootstrap.clone()), ], vec![CfgAtom::Flag(sym::test.clone())], ),
diff --git a/crates/tt/src/iter.rs b/crates/tt/src/iter.rs index 1d88218..0418c00 100644 --- a/crates/tt/src/iter.rs +++ b/crates/tt/src/iter.rs
@@ -6,7 +6,7 @@ use arrayvec::ArrayVec; use intern::sym; -use crate::{Ident, Leaf, Punct, Spacing, Subtree, TokenTree, TokenTreesView}; +use crate::{Ident, Leaf, MAX_GLUED_PUNCT_LEN, Punct, Spacing, Subtree, TokenTree, TokenTreesView}; #[derive(Clone)] pub struct TtIter<'a, S> { @@ -111,7 +111,7 @@ /// /// This method currently may return a single quotation, which is part of lifetime ident and /// conceptually not a punct in the context of mbe. Callers should handle this. - pub fn expect_glued_punct(&mut self) -> Result<ArrayVec<Punct<S>, 3>, ()> { + pub fn expect_glued_punct(&mut self) -> Result<ArrayVec<Punct<S>, MAX_GLUED_PUNCT_LEN>, ()> { let TtElement::Leaf(&Leaf::Punct(first)) = self.next().ok_or(())? else { return Err(()); }; @@ -145,7 +145,6 @@ } ('-' | '!' | '*' | '/' | '&' | '%' | '^' | '+' | '<' | '=' | '>' | '|', '=', _) | ('-' | '=' | '>', '>', _) - | (_, _, Some(';')) | ('<', '-', _) | (':', ':', _) | ('.', '.', _)
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs index 916e00b..36ccb67 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs
@@ -22,6 +22,8 @@ pub use text_size::{TextRange, TextSize}; +pub const MAX_GLUED_PUNCT_LEN: usize = 3; + #[derive(Clone, PartialEq, Debug)] pub struct Lit { pub kind: LitKind, @@ -243,6 +245,23 @@ self.token_trees.extend(tt.0.iter().cloned()); } + /// Like [`Self::extend_with_tt()`], but makes sure the new tokens will never be + /// joint with whatever comes after them. + pub fn extend_with_tt_alone(&mut self, tt: TokenTreesView<'_, S>) { + if let Some((last, before_last)) = tt.0.split_last() { + self.token_trees.reserve(tt.0.len()); + self.token_trees.extend(before_last.iter().cloned()); + let last = if let TokenTree::Leaf(Leaf::Punct(last)) = last { + let mut last = *last; + last.spacing = Spacing::Alone; + TokenTree::Leaf(Leaf::Punct(last)) + } else { + last.clone() + }; + self.token_trees.push(last); + } + } + pub fn expected_delimiters(&self) -> impl Iterator<Item = &Delimiter<S>> { self.unclosed_subtree_indices.iter().rev().map(|&subtree_idx| { let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else {