diff --git a/text/3649-macros-named-capture-groups.md b/text/3649-macros-named-capture-groups.md
new file mode 100644
index 00000000000..9c1d369ae04
--- /dev/null
+++ b/text/3649-macros-named-capture-groups.md
@@ -0,0 +1,808 @@
+- Feature Name: `macros-named-capture-groups`
+- Start Date: 2024-05-28
+- RFC PR: [rust-lang/rfcs#3649](https://github.com/rust-lang/rfcs/pull/3649)
+- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)
+
+# Summary
+[summary]: #summary
+
+It will now be possible to give names to capture (repetition) groups in macro
+patterns, which can then be referred to directly in the macro body and macro
+metavariable expressions.
+
+_Rustc usually refers to these groups as "repetitions" in diagnostics. This
+RFC uses "capture groups" which is more general (they don't always repeat),
+and more in line with regex._
+
+# Motivation
+[motivation]: #motivation
+
+Rust has no way to refer to capture groups directly, so it uses the variables
+they capture to refer to them indirectly. This leads to confusing or limited
+behavior in a few places:
+
+- Expansion with multiple capture groups is extremely limited. In many cases,
+ the ordering and nesting of different groups is restricted based on what can
+ be inferred by the contained variables, since the groups themselves are
+ ambiguous.
+- Repetition-related diagnostics are suboptimal because the compiler has limited
+ ability to guess what a capture group _should_ refer to when a captured
+ groups and variables do not align correctly.
+- Repetition mismatch diagnostics can only be emitted after the macro is
+ instantiated, rather than when the macro is written. (E.g. "meta-variable
+ `foo` repeats 2 times, but `bar` repeats 1 time")
+- As a result of the above, using repetition is somewhat fragile; small
+ adjustments can break working patterns with little indication of what exactly
+ is wrong. Reading code with multiple capture groups can also be confusing.
+- Metavariable expressions as they currently exist use an unintuitive format:
+ syntax like `${count($var, n)}` is used to refer to the `n`th ancestor group
+ of the smallest group that captures `$var`. Referring to groups directly would
+ be more straightforward than using a proxy.
+
+It is expected that named capture groups will provide a way to remove ambiguity
+in expansion and metavariable expressions, as well as unblock diagnostics that
+do a better job of guiding the macro mental model.
+
+# Guide-level explanation
+[guide-level-explanation]: #guide-level-explanation
+
+Capture groups can now take a name by providing an identifier between the `$`
+and opening `(`. This group can then be referred to by name in the expansion:
+
+```rust
+macro_rules! foo {
+ ( $group1( $a:ident ),+ ) => {
+ $group1( println!("{}", $a); )+
+ }
+}
+```
+
+This would be approximately equal to the following procedural code:
+
+```rust
+let mut ret = TokenStream::new();
+
+// Append an expansion for each time group1 is matched
+for Group1Captures { a } in group1 {
+ ret += quote!{ println!("{}", #a); };
+}
+```
+
+Named groups can be used to create code that depends on nested repetitions:
+
+```rust
+macro_rules! make_functions {
+ (
+ // Create a function for each name
+ names: [ $names($name:ident),+ ],
+ // Optionally specify a greeting
+ $greetings( greeting: $greeting:literal, )?
+ ) => {
+ $names(
+ // Create a function with the specified name
+ fn $name() {
+ println!("function {} called", stringify!($name));
+ // If a greeting is provided, print it in every function
+ $greetings( println!("{}", $greeting) )?
+ }
+ )+
+ }
+}
+
+fn main() {
+ make_functions! {
+ names: [foo, bar],
+ greeting: "hello!",
+ }
+
+ foo();
+ bar();
+
+ // output:
+ // function foo called
+ // hello!
+ // function bar called
+ // hello!
+}
+
+```
+
+This expansion is not easily possible without named capture groups because
+of ambiguity regarding which groups are referred to.
+
+Expansion of the above will approximately follow this procedural model:
+
+```rust
+let mut ret = TokenStream::new();
+
+// Append an expansion for each time group1 is matched
+for NamesCaptures { name } in greetings {
+ let mut fn_body = quote! { println!("function {} called", stringify!($name)); };
+
+ // Append the greeting for each
+ for GreetingCaptures { greeting } in greetings {
+ fn_body += quote! { println!("{}", #greeting) };
+ }
+
+ // Construct the function item and append to returned tokens
+ ret += quote! { fn #name() { #fn_body } };
+}
+```
+
+Groups can also be used in the expansion without `(...)` to emit their entire
+capture.
+
+This works well with a new "match exactly once" grouping that takes no kleene
+operator (as opposed to matching zero or more times (`*`), matching once or
+more (`+`), or matching zero or one times (`?`)).
+
+```rust
+macro_rules! rename_fn {
+ (
+ $newname:ident;
+ $pub(pub)? fn $oldname:ident( $args( $mut(mut)? $arg:ident: $ty:ty )* );
+ ) => {
+ $pub fn $newname( $args );
+ }
+}
+```
+
+_TODO: as pointed out in the comments, this syntax is ambivuous between `$group
+()` (recreate the group, add `()` after ) and `$group()` (expand the group).
+`$...group` was proposed as an alterantive._
+
+# Reference-level explanation
+[reference-level-explanation]: #reference-level-explanation
+
+Macro captures currently include the following grammar node:
+
+> `$` ( _MacroMatch+_ ) _MacroRepSep_? _MacroRepOp_
+
+This will be expanded to the following:
+
+> `$` ( IDENTIFIER_OR_KEYWORD except crate | RAW_IDENTIFIER | _ )? ( _MacroMatch+_ ) _MacroRepSep_? _MacroRepOp_
+
+As a result, `$identifier( /* ... */ ) /* sep and kleene */` will allow naming
+a capture group. It can then be used in expansion:
+
+```rust
+$identifier(
+ /* expansion within group */
+) /* sep and kleene */
+```
+
+Group and metavariables share the same namespace; all groups and metavariables
+must have a unique name within the macro capture pattern.
+
+Names will remain optional; however, if a capture group is given a name, it
+_must_ also be referred to by name during expansion. That is, an unnamed
+group in expansion will never be matched to a named group in the pattern.
+
+To make expansion rules easier, _it is forbidden to mix named and unnamed
+groups_ within the same macro.
+
+## Overview of changes
+
+A summary of the implications of this language addition is provided before
+explaining detailed semantics.
+
+### Nesting repetition expansions
+
+Nesting or intermixing repetition groups is currently not possible, mostly due
+to ambiguity of capture group expansions. Using an example from above:
+
+```rust
+macro_rules! make_functions {
+ (
+ // ↓ group 1
+ names: [ $($name:ident),+ ],
+ // ↓ group 2
+ $( greeting: $greeting:literal, )?
+ ) => {
+ $( // <- this expansion contains both `$name` and `$greeting`. So is this
+ // an expansion of capture group 1 or 2?
+ fn $name() {
+ println!("function {} called", stringify!($name));
+ $( println!("{}", $greeting) )?
+ }
+ )+
+ }
+}
+```
+
+Adding named capture groups makes this work, since ambiguity is removed.
+
+It is likely possible to adjust the rules for expansion such that the above
+would work with no additional syntax. However, this RFC posits that referring
+to groups by name provides an overall better user experience than changing
+the rules (more clear code, better diagnostics, and an easier model to follow).
+
+### Zero-length capture groups
+
+As a side effect of more precise repetition, groups in expansion that do not
+contain any metavariables will become more straightforward. For example, this
+simple counter is not possible as written:
+
+```rust
+macro_rules! count {
+ ( $( $i:ident ),* ) => {{
+ // Error: attempted to repeat an expression containing no syntax variables
+ //↓ the compiler does not know which group this refers to (here there
+ // is only one choice, but that is not always the case).
+ 0 $( + 1 )*
+ }};
+}
+```
+
+Using named groups removes ambiguity so should work:
+
+```rust
+// Note: this is just a simple example. Metavariable expressions will provide a
+// better way to get the same result with `${count(...)}`.
+macro_rules! count {
+ ( $idents( $i:ident ),* ) => {{
+ 0 $idents( + 1 )*
+ }};
+}
+```
+
+Metavariable expressions provide an `${ignore($var)}` operation that enables
+the same behavior; `ignore(...)` will simply not be needed with named groups.
+
+There is also no way to act on capture groups that bind only exact tokens but
+no variables. An example is extracting the `mut` from a function or binding
+signature:
+
+
+```rust
+/// Sample macro that captures exact syntax and tweaks it
+macro_rules! match_fn {
+ // ↓ We need to be aware of mutability
+ (fn $name:ident ($(mut)? $a:ident: u32)) => {
+ // ↓ we want to reproduce the `mut` here
+ fn $name($(mut)? $a: u32) {
+ // ^^^^^^^
+ // Error: attempted to repeat an expression containing no syntax variables
+ println!("hello {}!", $a);
+ }
+ }
+}
+fn main() {
+ match_fn!(fn foo(a: u32));
+ foo(10);
+}
+```
+
+Adding named capture groups to the above would allow it to work.
+`${ignore(...)}` does not directly help here.
+
+### Metavariable expressions
+
+Metavariable expressions currently use a combination of location within the
+expansion (i.e. which capture groups contain it), variables captured, and
+an index to change the indicated group. For example, `index()` returns the
+number of the current expansion.
+
+```rust
+macro_rules! innermost1 {
+ ( $( $a:ident: $( $b:literal ),* );+ ) => {
+ [$( $( ${ignore($b)} ${index(1)}, )* )+]
+ };
+}
+```
+
+In order to understand what `index(1)` is referring to here, one must do the
+following:
+
+- Note how many repetition groups exist in the match expression (2).
+- Count how many repetition groups the `index(1)`` call is nested in (2).
+- Backtrack by one to figure out what exactly is getting indexed (1).
+
+After doing the above, it can be noted that `${index(1)}` in this position
+will indicate the current expansion of the outer cature group (the group
+containing only `$a`).
+
+Rewritten to use named groups instead:
+
+```rust
+macro_rules! innermost1 {
+ ( $outer_rep( $a:ident: $inner_rep( $b:literal ),* );+ ) => {
+ [$outer_rep( $inner_rep( ${index($outer_rep)}, )* )+]
+ };
+}
+```
+
+It is significantly easier to see what the call to `index` is referring to. As
+an added benefit, its meaning will not change if its position is moved in the
+code (e.g. moving to be within `$outer_rep`, but not `$inner_rep`).
+
+This RFC proposes that `count`, `index`, and `len` will accept group names
+in place of a variable and an index, since these three expressions relate more
+to how entire _groups_ are expanded than the variables they take as arguments.
+
+Further reading:
+
+- [`macro_metavar_expr` RFC][`macro_metavar_expr`] and
+ [tracking issue](https://github.com/rust-lang/rust/issues/83527)
+- [Proposal for possible specific behavior](https://github.com/rust-lang/rust/pull/122808#issuecomment-2124471027)
+
+### "Exactly one" matching and full group emission
+
+If a group is specified without a kleene operator (`*`, `+`, `?`), it will now
+be assumed to match exactly once. This will be most useful with the ability to
+emit an entire matched group.
+
+```rust
+macro_rules! check_and_pass {
+ // `group1` will get matched exactly once
+ ($group1(a b $v:ident c $group2($tt:tt)* )) => {
+ // All tokens including exact `a` `b` will get passed to `other_macro`
+ other_macro!($group1)
+ }
+}
+```
+
+This should make it much easier to work with optional exact matches. Currently
+there is no way to do anything useful with capture groups that don't capture
+metavariables (such as `$pub` and `$mut` in the
+[Guide-level explanation](#guide-level-explanation) example).
+
+_TODO: will this preserve the coercion of tokens to fragments
+(e.g. tt -> ident)?_
+
+## Detailed semantics
+
+To illustrate detailed semantics, an example will be used in which the
+token pattern approximately mirrors the named groups:
+
+```rust
+macro_rules! m {
+ ( $outer_a(oa[ $middle(m[ $inner(i[ $x:ident ])* ])* ])*; $outer_b(ob[ $y:ident ])*)
+ => { println!("{}", stringify!(/* relevant expansion */)) }
+}
+
+m!( oa[ m[i[x0] i[x1]] ] oa[] oa[m[] m[]]; ob[y0] );
+```
+
+This can be thought of as a tree structure that loosely matches the token
+`Group` of the captured items.
+
+
+```text
+ Level 0 | Level 1 | Level 2 | Level 3 | Level 4
+
+ /- $inner[0,0,0] --- $x[0,0,0,0]
+ |-- $outer_a[0] --- $middle[0,0] -| `i[v0]` `x0`
+ | `oa[...]` `m[..]` |
+ | \- $inner[0,0,1] --- $x[0,0,0,1]
+ | `i[v1]` `x1`
+ |
+ |-- $outer_a[1]
+ | `oa[...]`
+ $root -|
+(entire macro) | /- $middle[2,0]
+ |-- $outer_a[2] -| `m[..]`
+ | `oa[...]` |
+ | \- $middle[2,1]
+ | `m[..]`
+ |
+ |-- $outer_b[0] --- $y[0,0,0,1]
+ `ob[...]` `y0`
+
+Summary of captures:
+
+- `$outer_a`: captured 3 times
+- `$middle`: captured 3 times
+- `$inner`: captured 2 times
+- `$x`: captured 2 times
+- `$outer_b`: captured 1 time
+- `$y`: captured 1 time
+```
+
+In the above diagram, `$metavar[i_n, ..., i_1, i_0]` shows that this is the
+`i`th
+
+
+the `i`th
+capture for the `n`th ancestor
+captured instance of `$metavar`
+
+
+ within the `i_1`th instance of its parent group
+capture.
+
+Some of this section intends to solidify rules that are currently implemented
+but not well described.
+
+### Definitions
+
+This section uses some common terms to refer to relevant ideas.
+
+- "Pattern" or "capture pattern": the left hand side of the macro that defines
+ metavariables and gets pattern matched against code.
+- "Captured": when a token or tokens are matched by a variable or group.
+ This has some overlap with what rustc refers to as "repeating n times" in
+ error messages, but this RFC seeks to make this less ambiguous.
+- "Expansion": the right hand side of the macro that uses metavariables and new
+ tokens to update the file's AST.
+- "Contents": Whatever is contained within a group
+- "Level": a nesting level (or generation in the tree diagram. Adding a new
+ group around a metavarible increases its nesting level.
+- "Parent": any group that is at a higher level of the subject ("child") and
+ shares a direct lineage. E.g. `$inner` and `$middle` are both parents of `$v`.
+- "Immediate parent": parent exactly one level above. E.g. `$inner` is an
+ immediate parent of `$v`, `$middle` is not.
+- "Capture parent", "capture level", "capture contents": a parent, level, or
+ group contents in the capture, which may not exist or be the same in the
+ expansion.
+- "Expansion parent", "expansion level", "expansion contents": a parent, level,
+ or group contents in the expansion, which may not exist or be the same in the
+ capture.
+
+### Expansion rules
+
+In current macro expansion, the following rules are observed:
+
+1. Groups must contain at least one metavariable (`$()*` fails with "repetition
+ matches empty token tree").
+2. Metavariables must be expanded with the same level of nesting in which they
+ are captured. That is, if a metavariable is captured within two nested
+ groups as `$($($v:ident),*),*`, it may only be expanded as `$($($v)*)*`.
+3. Metavariables _or groups_ at the same level of the capture group must be
+ be captured the same number of times.
+
+This is easier to visualize with examples. Named capture groups are used to
+make things more clear, even though this is discussing the current unnamed
+groups.
+
+```rust
+// Possible expansions for the above sample macro
+
+// Ok: prints `x0 x1`
+$outer_a( $middle( $inner( $x )* )* )*
+
+// Ok: prints `y0`
+$outer_b( $y )*
+
+// Forbidden: "variable 'x' is still repeating at this depth"
+$??( $x )*
+$x
+
+// Forbidden: "attempted to repeat an expression containing no syntax variables
+// matched as repeating at this depth"
+// Basically, it is unable to determine what the groups are supposed to refer to.
+$outer_b( $??( $??( $y )* )* )*
+
+// Forbidden: "`x` repeats 3 times, but `y` repeats 1 time"
+// This is an example diagnostic where referring to groups by their captures
+// doesn't work well; `x` is actually only captured twice, but _`$middle`_ is
+// captured three times.
+$??( $??( $??( $x $y )* )* )*
+
+// Forbidden: "`x` repeats 3 times, but `y` repeats 1 time"
+// Makes sense; if `$combined` must refer to only a single group then it has
+// no way to pick between `$outer_a` and `$outer_b`.
+$combined( $middle( $inner( $x )* )* $y )*
+
+// ...except the above actually works with the following invocation, printing
+// `x0 y0`, because `$middle` (level 2) and `$y` (also level 2) are captured
+// the same number of times. This is an example of invocation-dependent
+// expansion correctness that this RFC hopes to minimize.
+m!( oa[ m[i[x0]] ]; ob[y0] );
+```
+
+With named repetition groups, these rules will be changed to the following:
+
+1. Group expansions no longer need to contain any metavariables.
+2. In expansion, the group will repeat as many times as its entire pattern
+ was captured, independent of whatever its expansion contents are.
+3. Captured variables or groups may only be expanded within their parent group,
+ if any. However, the capture parent does not need to be the immediate
+ expansion parent.
+5. If a group name is given in the expansion with no `(...)`, the entire
+ capture of that group is reemitted _including exact literals_.
+
+These are detailed in the following sections.
+
+#### Expansion within an immediate parent
+
+If a group or metavariable has capture parents, it must be scoped within those
+same parents for expansion (though they need not be immediate).
+
+```rust
+// Correct: prints `x0 x1`
+$outer_a( $middle( $inner( $x )* )* )*
+
+// Correct, emits entire middle group. Result: `m[i[x0] i[x1]] m[] m[]`
+$outer_a( $middle )*
+
+// Skipping a group level
+// Error: `$inner` must be contained within a `$middle` group, but it is within
+// `$outer_a`
+$outer_a( $inner( $x )* )*
+
+// No grouping at all
+// Error: `$x` must be within an `$inner` group, but it is not within any group
+$x
+```
+
+_TODO: if `foo[x0] foo[] foo[x2]` is captured and the expansion is
+`$foo( $x ),+`, should `x0, x2` be emitted of `x0, , x2` (extra comma)?
+Probably the first one._
+
+A possible relaxation of this rule is to allow groups or variables to expand to
+all captures within that level when not nested within the immediate parent.
+
+TBD whether this should be part of this RFC or a future possibility
+
+```rust
+// Expands to `x0 x1`
+$x
+
+// Expands to `x0 x1`
+$outer_a( $middle( $x )* )*
+```
+
+#### Expansion within non-parent groups
+
+If a group or metavariable is nested within a group that is not a capture
+parent, it should be repeated as many times as that group. It must still have
+its capture parents as expansion parents so as not to break other rules, but
+they need not be the immediate parents.
+
+In order to avoid edge cases with metavariable expressions, a group is not
+allowed to be nested within itself.
+
+```rust
+// Correct: prints `y0 y0 y0`
+// This is because the _entire_ expansion of `$outer_b` (one instance of $y)
+// is repeated once for each `$outer_a` (three instances)
+$outer_a( $outer_b( $y ) )
+
+// Correct: prints `ob[ oa[y0 i[x0 y0] oa[x1 y0]] oa[y0] oa[y0]]`.
+// Explanation:
+// - `root > outer_a > middle > inner > x` and `outer_b > y` ordering are both
+// still respected, even though they are interleaved
+// - `outer_b` repeats once within root
+// - `outer_a` repeats three times within root, so repeats 3x within `outer_b`
+// - `inner` repeats `[2, 0, 0]` times within the `outer_a` instances. This
+// drives how often `x` and `y` get repeated within that group.
+$outer_b( ob[$outer_a( oa[$y $middle( $inner( i[$x $y] ) )] )] )
+
+// Forbidden: `x` is missing parent `outer_a`
+$outer_b( $middle( $inner( $x $y ) ) ) )
+
+// Forbidden: group nesting within itself
+$outer_b( $outer_b( $y ) )
+$outer_b( $outer_b )
+```
+
+#### Single matches
+
+Capture groups must currently specify a kleene operator (`*`, `+`, or `?`) that
+determines if the group should match zero or more times, one or more times, or
+up to one time. This RFC will allow omitting the kleene operator to indicate
+that a group must be captured exactly once. That is, `$group(foo)` is a valid
+pattern (with current rules, `$(foo)` is forbidden).
+
+With this "exactly once" match there is no purpose in having a repetition token
+(e.g. the comma in `$(...),*`), so it must be omitted.
+
+#### Entire group expansion
+
+Since groups are named, it is now possible to write a group name to reemit its
+captured contents with no further expansion. This syntax uses the group name
+but omits the `(/* expansion pattern */)/* kleene */`:
+
+```rust
+// Ok: prints `ob[y0]`
+$outer_b
+```
+
+The entire contents of the capture group are emitted, including both exact
+tokens and anything that would be bound to a metavariable. Span from the
+macro invocation can be kept here, which should improve the diagnosability of
+some macros.
+
+The above rules regarding allowed group usage locations must still be followed.
+
+### Metavariable Expressions
+
+This RFC proposes some changes to metavariable expressions that will leverage
+named groups to hopefully make them more user-friendly.
+
+_At time of writing, part of macro metavariable expressions are under
+consideration for stabilization. Depending on what is selected, these rules may
+need to change slightly._
+
+- `${index($group)}`: Return the number of times that the group has been
+ _expanded_ so far. Must be used within the group that is given as an
+ argument.
+- `${count($metavar)}`: Return the number of times a group or metavariable was
+ _captured_.
+- `${len($metavar)}`: Because `count` becomes more flexible, `len` is no longer
+ needed and can be removed.
+- `${ignore($metavar)}`: if this RFC is accepted then `ignore` can be removed.
+ It is used to specify which capture group an expansion group belongs to when
+ no metavariables are used in the expansion; with named groups, however, this
+ is specified by the group name rather than by contained metavariables.
+
+#### `${index($group)}`
+
+The `index` metavariable expression is used to indicate the number of times
+a group has been expanded so far. It can be thought of a form of `enumerate`.
+
+- Arguments: one required argument, `$group`
+- Allowed usage: may only be used within `$group`
+- Output: The number of times the _current expansion of `$group`_ has repeated
+ so far. That is, if `$group` is captured twice but used >2 times in the
+ expansion, `${index($group)}` will still only ever return 0 or 1.
+- Changes from current implementation:
+ - Takes a group as an argument, not a depth
+ - The argument is no longer optional
+
+In the tree diagram, this can be thought of as returning the final number for
+the given group in the `[i_n, ..., i_0]` index list.
+
+```rust
+// Ok: prints `o0 m0 i0 i1; o1; o2 m0 m1;`
+$outer_a( o ${index($outer_a)} $middle( m ${index($middle)} $inner( i ${index($inner)})* ),* );*
+
+// Ok: prints `o m outer_idx 0; o; o m outer_idx 2 m outer_idx 2`
+$outer_a( o $middle( m outer_idx ${index($outer_a)} ),* );*
+
+// Ok: prints `ob oa0 oa1 oa2`
+// The outer repetition (`outer_b`) has no influence on `index`
+$outer_b( ob $outer_a( oa ${index($outer_a)} ),* )*
+
+// Forbidden: not used within a `$middle` group
+$outer_a( ${index($middle)} $middle );*
+```
+
+The location of `index` within its group does not affect its output. That is,
+all of the below will return 0:
+
+```rust
+// For this example, `$g1` is captured exactly once. All other groups are
+// captured any number of times
+
+// Prints `0`
+$g1( ${index($g1)} )
+
+// Increasing the nestin does nothing; still returns `0` for each `g2` capture
+$g1( $g2( ${index($g1)} )* )
+
+// Still returns `0`
+$g1( $g2( $g3 ( ${index($g1)} )* )* )
+```
+
+#### `${count($name)}`
+
+`count` is used to return the number of times a group or variable has been
+captured. It can be used in any location, but its exact behavior is location-
+dependent.
+
+- Arguments: one required argument, `$group` or `$metavariable`
+- Allowed usage: may be used anywhere within the expansion, but some arguments
+ may be disallowed.
+- Output: this returns the number of times a group or metavariable was
+ captured, with some scoping specifics.
+- Changes from current implementation:
+ - Can take a group as an argument
+ - Functionality combined with `len`
+
+Looking at a group or variable that is more deeply nested will return how many
+of that variable were captured in the current repetition. Looking at a variable
+or group that is less deeply nested will return the total times that group was
+captured.
+
+This can be represented as a simple tree walking algorithm to the _capture_
+tree to determine what gets counted. The starting position in the _expansion_
+determines where to start, and then the following rules are applied:
+
+- If `level($name)` >= `level(expression)` (more deeply nested), walk all
+ descendents, including those of neighbors, and count each `$name`
+- If `level($name) < `level(expression)` (less deeply nested):
+ - Walk the entire tree and count each `$name`
+ - Reject code with an error if `$name` is not an ancestor
+
+_TODO: this was meant to be compatible with the existing metavariable
+expressions, but after talking to Josh, this should probably be split to
+two separate MVEs. Maybe something along the lines of `count_parents` and
+`count_children` could work._
+
+```rust
+/* looking at descendents */
+
+// Ok: prints `[oa 3, m 3, i 2, x 2; ob 1, y 1]`
+// Demo printing totals. Expansion is at the root (level 0), so each variable
+// is at a higher level; the entire tree is walked and all instances are counted.
+[
+ oa ${count($outer_a)}, m ${count($middle)}, i ${count($inner)}, x ${count($x)};
+ ob ${count($outer_b)}, y ${count($y)},
+]
+
+// Ok: prints `[1 0 2]`
+[$outer_a( ${count($middle} )*]
+
+// Ok: prints `[2 0 0]`
+[$outer_a( ${count($inner} )*]
+
+// Ok: prints `[2 0 0]`
+[$outer_a( ${count($x} )*]
+
+// Ok: prints `o[m[x 2]] o[] o[m[x 0] m[x 0]]`
+// `$middle[0,0]``"sees" two `$x` captures. `$middle[2,0]` and `$middle[2,1]` both
+// don't see any.
+$outer_a( o[$middle( m[x ${count($x)}] )*] )*
+
+/* looking at ancestors */
+
+// Ok: prints [3 3 3]
+// `count` is used at level 2 (one deeper than `outer_a`), so it sees all
+// `outer_a`s each time.
+[$outer_a( ${count($outer_a)} )*]
+
+// Ok: prints [[3] [] [3 3]]
+// Similar to the above
+[$outer_a( [$middle( ${count($outer_a)} )*] )*]
+
+/* errors */
+
+// Error: trying to count a variable that is neither a descendent or anscestor
+[$outer_a( ${count($y} )*]
+```
+
+TODO: we could relax the final rule and allow counting siblings of ancestors
+
+# Drawbacks
+[drawbacks]: #drawbacks
+
+Why should we *not* do this?
+
+- If [`macro_metavar_expr`] stabilizes before this merges, this will add a
+ duplicate way of using those expressions. If this RFC is accepted,
+ stabilizing only a subset of metavariable expressions that does not conflict
+ should be considered.
+
+# Rationale and alternatives
+[rationale-and-alternatives]: #rationale-and-alternatives
+
+- In macro metavariable syntax, using named capture groups, we could treat `count` and `index` as *fields* rather than *functions*. For instance, we could write `${$group.index}` rather than `${index($group)}`. This would be consistent with the [macro fragment fields](https://github.com/rust-lang/rfcs/pull/3714) proposal.
+- Since we no longer need both `count` and `len`, we could choose to use either name for the remaining function we still provide. We should consider whether `count` or `len` best describes this functionality.
+- Variable definition syntax could be `$ident:(/* ... */)` rather than
+ `$ident(/* ... */)`. Including the `:` is proposed to be more consistent
+ with existing fragment specifiers.
+- There is room for macros to become smarter in their expansions without adding
+ named capture groups. As mentioned elsewhere in this RFC, it seems like
+ adding named groups is a cleaner solution with less cognitive overhead.
+
+# Prior art
+[prior-art]: #prior-art
+
+- Regex allows the naming of reepeating capture groups for expansion, including
+ those that do not capture anything else.
+
+# Unresolved questions
+[unresolved-questions]: #unresolved-questions
+
+- Syntax: the original proposal was to include a colon, e.g.
+ `$group1:(/* ... */)`. A label-like syntax of `$'group1 $(/* ... */)` was
+ also proposed.
+
+**Possibly edition-sensitive** the proposed syntaxes are currently rejected
+under the `missing_fragment_specifier` lint. That means that
+`#![allow(missing_fragment_specifier)]` makes rustc accept the proposed syntax
+as valid, which could conflict with this proposal.
+
+# Future possibilities
+[future-possibilities]: #future-possibilities
+
+- Macros 2.0: if accepted, the same rules expressed in this RFC should also
+ apply to Macros 2.0. Macros 2.0 may even opt to forbid unnamed capture
+ groups.
+- A `${count_in($var, $group)}` expression that allows further scoping of
+ `count` (TODO: describe this better).
+
+[original proposal]: https://github.com/rust-lang/rfcs/pull/3649#discussion_r1618998153
+[`macro_metavar_expr`]: https://rust-lang.github.io/rfcs/3086-macro-metavar-expr.html