Repository: rust-lang/rust-enhanced (language: rust)

Issue:
Expand macro parsing to repeated patterns
### Sublime Text Version
Sublime Text 3 (Build 3176)

### Rust Enhanced Version
v2.15.0

### Operating system
macOS v10.13.4 (high sierra)

### References
Improvement number 25 on #284 is also something that should be updated

### Request
highlight repeated patterns:

```rust
macro_rules! foo {
	($($foo:expr),*) => {}
}
```

It'd be nice if `$foo:expr` would highlight as if i didn't have the `$(),*` surrounding it.


Discussion / hints:
I have a fix for this, I'll be posting the PR soon.

Fix patch:
diff --git a/RustEnhanced.sublime-syntax b/RustEnhanced.sublime-syntax
index d4744917..4dff6aa5 100644
--- a/RustEnhanced.sublime-syntax
+++ b/RustEnhanced.sublime-syntax
@@ -37,8 +37,7 @@ contexts:
         1: entity.name.label.rust
         2: punctuation.separator.rust
 
-    - match: '{{lifetime}}'
-      scope: storage.modifier.lifetime.rust
+    - include: lifetime
 
     - match: '\b(mod)\s+({{identifier}})\b'
       captures:
@@ -102,47 +101,11 @@ contexts:
       scope: storage.type.enum.rust
       push: enum-identifier
 
-    - match: \b(let|const|static)\b
-      scope: storage.type.rust
-
-    - match: \bfn\b
-      scope: storage.type.function.rust
-
-    - match: \bmod\b
-      scope: storage.type.module.rust
-
-    - match: \btype\b
-      scope: storage.type.type.rust
-
-    - match: \btrait\b
-      scope: storage.type.trait.rust
-
-    - match: \b(mut|pub|unsafe|move|ref)\b
-      scope: storage.modifier.rust
-
-    - match: \b(crate|extern|use|where)\b
-      scope: keyword.other.rust
-
-    - match: \b(else|for|if|loop|match|while|yield)\b
-      scope: keyword.control.rust
-
     - match: '\b(break|continue)(?:\s+(''{{identifier}}))?'
       captures:
         1: keyword.control.rust
         2: entity.name.label.rust
 
-    - match: \breturn\b
-      scope: keyword.control.rust
-
-    - match: \b(as|in|box)\b
-      scope: keyword.operator.rust
-
-    - match: \b(virtual|become|priv|typeof|unsized|do|abstract|final|override|macro)\b
-      scope: invalid.illegal.rust
-
-    - match: \b(true|false)\b
-      scope: constant.language.rust
-
     - include: type
 
     - match: '\b(macro_rules!)\s+([[:alpha:]_][[:alnum:]_]*)\b'
@@ -156,9 +119,6 @@ contexts:
     - include: strings
     - include: chars
 
-    - match: '\b[[:lower:]_][[:lower:][:digit:]_]*(?=\()'
-      scope: support.function.rust
-
     # macros which take format specs as the only parameter
     - match: '\b((?:format(?:_args)?|e?print(?:ln)?|panic|unreachable|unimplemented)!)\s*(\()'
       captures:
@@ -218,37 +178,10 @@ contexts:
         - include: statements
 
     - include: return-type
-
-    # Making this an operator helps visually break up large
-    # match blocks containing just enums
-    - match: '=>'
-      scope: keyword.operator.rust
-
-    - match: '=(?!=)'
-      scope: keyword.operator.rust
-
-    - match: '[;,]'
-
-    - match: ':'
-      scope: punctuation.separator.rust
-
-    - match: '\.\.\.'
-      scope: keyword.operator.rust
-
-    - match: '\.\.'
-      scope: keyword.operator.rust
-
-    - match: '<<=|>>=|<<|>>'
-      scope: keyword.operator.rust
-
-    - match: '>=|<=|==|!=|&&|\|\|'
-      scope: keyword.operator.rust
-
-    - match: '\*=|/=|\+=|-=|%=|\^='
-      scope: keyword.operator.rust
-
-    - match: '[-=<>&|!~@?+*/%^''#$]'
-      scope: keyword.operator.rust
+    - include: symbols
+    - include: keywords
+    - match: '\b[[:lower:]_][[:lower:][:digit:]_]*(?=\()'
+      scope: support.function.rust
 
   visibility:
     - match: '\b(pub)\s*(\()'
@@ -393,8 +326,7 @@ contexts:
       scope: keyword.operator.rust
     - match: \b(mut|ref)\b
       scope: storage.modifier.rust
-    - match: '{{lifetime}}'
-      scope: storage.modifier.lifetime.rust
+    - include: lifetime
 
   closure-return:
     - meta_content_scope: meta.function.closure.rust
@@ -495,8 +427,7 @@ contexts:
             - match: '(?=\S)'
               pop: true
         - include: type-any-identifier
-    - match: '{{lifetime}}'
-      scope: storage.modifier.lifetime.rust
+    - include: lifetime
     - match: '\b([[:upper:]]|_*[[:upper:]][[:alnum:]_]*[[:lower:]][[:alnum:]_]*)\b::'
       scope: meta.path.rust storage.type.rust
       captures:
@@ -548,6 +479,9 @@ contexts:
     - match: '{{identifier}}'
       scope: entity.name.struct.rust
       set: struct-body
+    - match: '(?=\S)'
+      # Abort on invalid character.
+      pop: true
 
   struct-body:
     - meta_scope: meta.struct.rust
@@ -558,7 +492,8 @@ contexts:
       push: struct-tuple
     - match: '(?=\{)'
       set: struct-classic
-    - match: '(?=;)'
+    - match: '(?=\S)'
+      # Semicolon is valid here, others should just abort.
       pop: true
 
   struct-tuple:
@@ -695,96 +630,183 @@ contexts:
 
   macro-block:
     - meta_scope: meta.macro.rust
-    - match: '\}'
-      scope: meta.block.rust punctuation.definition.block.end.rust
-      pop: true
-    - match: '[\])]'
-      scope: punctuation.definition.ground.end.rust
-      pop: true
+    - include: comments
     - match: '\{'
-      scope: punctuation.definition.block.begin.rust
-      push:
-        - meta_scope: meta.block.rust
-        - match: '(?=\})'
+      scope: punctuation.section.block.begin.rust
+      set:
+        - meta_scope: meta.macro.rust
+        - match: '\}'
+          scope: punctuation.section.block.end.rust
           pop: true
         - include: macro-block-contents
-    - match: '\['
-      scope: punctuation.definition.group.begin.rust
-      push:
-        - meta_scope: meta.group.rust
-        - match: '(?=\])'
+
+    # Note: ) and ] require a trailing semicolon, but this
+    # does not check for that.
+    - match: '\('
+      scope: punctuation.section.block.begin.rust
+      set:
+        - meta_scope: meta.macro.rust
+        - match: '\)'
+          scope: punctuation.section.block.end.rust
           pop: true
         - include: macro-block-contents
-    - match: '\('
-      scope: punctuation.definition.group.begin.rust
-      push:
-        - meta_scope: meta.group.rust
-        - match: '(?=\))'
+
+    - match: '\['
+      scope: punctuation.section.block.begin.rust
+      set:
+        - meta_scope: meta.macro.rust
+        - match: '\]'
+          scope: punctuation.section.block.end.rust
           pop: true
         - include: macro-block-contents
 
   macro-block-contents:
+    # Macro block consists of a series of rules separated by semicolon
+    # (trailing semicolon is optional).
+    #
+    # A rule is: BRACKET matchers BRACKET => BRACKET transcribers BRACKET
+    # where BRACKET needs to be matched () or [] or {}
     - include: comments
+    - match: '\{'
+      scope: punctuation.section.block.begin.rust
+      push:
+        - meta_include_prototype: false
+        - meta_scope: meta.macro.matchers.rust
+        - match: '\}'
+          scope: punctuation.section.block.end.rust
+          set: macro-rule-sep
+        - include: macro-matchers
+
     - match: '\('
-      scope: punctuation.definition.group.begin.rust
-      push: macro-matcher
-    - match: ';'
-      pop: true
+      scope: punctuation.section.block.begin.rust
+      push:
+        - meta_include_prototype: false
+        - meta_scope: meta.macro.matchers.rust
+        - match: '\)'
+          scope: punctuation.section.block.end.rust
+          set: macro-rule-sep
+        - include: macro-matchers
+
+    - match: '\['
+      scope: punctuation.section.block.begin.rust
+      push:
+        - meta_include_prototype: false
+        - meta_scope: meta.macro.matchers.rust
+        - match: '\]'
+          scope: punctuation.section.block.end.rust
+          set: macro-rule-sep
+        - include: macro-matchers
 
-  macro-matcher:
+  macro-matchers:
     - include: comments
-    - meta_include_prototype: false
-    - meta_scope: meta.group.rust
-    - match: '\)'
-      scope: punctuation.definition.group.end.rust
-      set: macro-match-operator
-    - match: '(\$)(\()'
+    - match: '\('
+      scope: punctuation.section.block.begin.rust
+      push:
+        - meta_include_prototype: false
+        - match: '\)'
+          scope: punctuation.section.block.end.rust
+          pop: true
+        - include: macro-matchers
+
+    - match: '\['
+      scope: punctuation.section.block.begin.rust
+      push:
+        - meta_include_prototype: false
+        - match: '\]'
+          scope: punctuation.section.block.end.rust
+          pop: true
+        - include: macro-matchers
+
+    - match: '\{'
+      scope: punctuation.section.block.begin.rust
+      push:
+        - meta_include_prototype: false
+        - match: '\}'
+          scope: punctuation.section.block.end.rust
+          pop: true
+        - include: macro-matchers
+
+    - match: '(\$\s*{{identifier}})\s*(:)\s*(ident|path|expr|ty|pat|stmt|block|item|meta|tt|lifetime)'
+      captures:
+        1: variable.parameter.rust
+        2: punctuation.separator.rust
+        3: storage.type.rust
+
+    - match: '(\$)\s*(\()'
+      # Kleene operator.
       captures:
         1: keyword.operator.rust
-        2: punctuation.definition.group.begin.rust
+        2: punctuation.section.group.begin.rust
       push:
-        - match: '(\))[^*+]?([*+])'
+        - meta_include_prototype: false
+        - match: '(\))\s*[^?*+]*\s*([?*+])'
           captures:
             1: punctuation.definition.group.end.rust
             2: keyword.operator.rust
           pop: true
-        - include: macro-metavariable
-    - include: macro-metavariable
+        - include: macro-matchers
+
+    # All other tokens except $ and delimiters are allowed here.
+    - include: numbers
+    - include: strings
+    - include: keywords
+    - include: lifetime
+    - include: chars
+    - include: symbols
 
-  macro-match-operator:
+  macro-rule-sep:
+    - include: comments
     - match: '=>'
       scope: keyword.operator.rust
+      set: macro-transcriber-block
+    - match: '(?=\S)'
+      # Abort on unexpected character.
+      pop: true
+
+  macro-transcriber-block:
+    - include: comments
+
     - match: '\{'
-      scope: punctuation.definition.block.begin.rust
+      scope: punctuation.section.block.begin.rust
       set:
-        - meta_scope: meta.block.rust
+        - meta_scope: meta.macro.transcribers.rust
         - match: '\}'
-          scope: punctuation.definition.block.end.rust
-          pop: true
+          scope: punctuation.section.block.end.rust
+          set: macro-semi-sep
         - include: statements
+
     - match: '\('
-      scope: punctuation.definition.group.begin.rust
+      scope: punctuation.section.block.begin.rust
       set:
-        - meta_scope: meta.group.rust
+        - meta_scope: meta.macro.transcribers.rust
         - match: '\)'
-          scope: punctuation.definition.group.end.rust
-          pop: true
+          scope: punctuation.section.block.end.rust
+          set: macro-semi-sep
         - include: statements
+
     - match: '\['
-      scope: punctuation.definition.group.begin.rust
+      scope: punctuation.section.block.begin.rust
       set:
-        - meta_scope: meta.group.rust
+        - meta_scope: meta.macro.transcribers.rust
         - match: '\]'
-          scope: punctuation.definition.group.end.rust
-          pop: true
+          scope: punctuation.section.block.end.rust
+          set: macro-semi-sep
         - include: statements
 
-  macro-metavariable:
-    - match: '(\${{identifier}})(:)(ident|path|expr|ty|pat|stmt|block|item|meta|tt)'
-      captures:
-        1: variable.parameter.rust
-        2: punctuation.separator.rust
-        3: storage.type.rust
+    - match: '(?=\S)'
+      # Abort on unexpected character.
+      pop: true
+
+  macro-semi-sep:
+    - include: comments
+    - match: ';'
+      scope: punctuation.terminator.rust
+      pop: true
+    - match: '(?=[})\]])'
+      pop: true
+    - match: '\S'
+      # This is intended to help make it evident when you forget a semicolon.
+      scope: invalid.illegal.rust
 
   impl-definition:
     - meta_scope: meta.impl.rust
@@ -832,8 +854,10 @@ contexts:
       scope: entity.name.impl.rust
     - match: '&'
       scope: keyword.operator.rust
-    - match: '{{lifetime}}'
-      scope: storage.modifier.lifetime.rust
+    - include: lifetime
+    - match: '(?=\S)'
+      # Abort on unexpected character.
+      pop: true
 
   impl-where:
     - meta_scope: meta.where.rust
@@ -850,8 +874,7 @@ contexts:
         - include: type-any-identifier
         - match: '&'
           scope: keyword.operator.rust
-        - match: '{{lifetime}}'
-          scope: storage.modifier.lifetime.rust
+        - include: lifetime
         - match: '(?=\S)'
           pop: true
     - include: type-any-identifier
@@ -928,8 +951,7 @@ contexts:
         - include: type-any-identifier
         - match: '&'
           scope: keyword.operator.rust
-        - match: '{{lifetime}}'
-          scope: storage.modifier.lifetime.rust
+        - include: lifetime
         - match: '(?=\S)'
           pop: true
     - include: type-any-identifier
@@ -1190,6 +1212,10 @@ contexts:
         1: constant.numeric.integer.binary.rust
         2: storage.type.numeric.rust
 
+  lifetime:
+    - match: '{{lifetime}}'
+      scope: storage.modifier.lifetime.rust
+
   basic-identifiers:
     - match: '\b([[:upper:]_][[:upper:][:digit:]_]+)\b'
       scope: constant.other.rust
@@ -1227,3 +1253,92 @@ contexts:
         push: generic-angles
       - match: ''
         pop: true
+
+  symbols:
+    - match: '=>'
+      # Making this an operator helps visually break up large
+      # match blocks containing just enums
+      scope: keyword.operator.rust
+
+    - match: '=(?!=)'
+      scope: keyword.operator.rust
+
+    - match: '[;,]'
+
+    - match: ':'
+      scope: punctuation.separator.rust
+
+    - match: '\.\.\.'
+      scope: keyword.operator.rust
+
+    - match: '\.\.'
+      scope: keyword.operator.rust
+
+    - match: '<<=|>>=|<<|>>'
+      scope: keyword.operator.rust
+
+    - match: '>=|<=|==|!=|&&|\|\|'
+      scope: keyword.operator.rust
+
+    - match: '\*=|/=|\+=|-=|%=|\^='
+      scope: keyword.operator.rust
+
+    - match: '[-=<>&|!~@?+*/%^''#$]'
+      scope: keyword.operator.rust
+
+    - match: '<-|->'
+      scope: keyword.operator.rust
+
+  keywords:
+    # All keywords.  Note in `statements` some of these are superseded by more
+    # specific rules.
+    - match: \b(true|false)\b
+      scope: constant.language.rust
+
+    - match: \b(let|const|static)\b
+      scope: storage.type.rust
+
+    - match: \bfn\b
+      scope: storage.type.function.rust
+
+    - match: \bmod\b
+      scope: storage.type.module.rust
+
+    - match: \bstruct\b
+      scope: storage.type.struct.rust
+
+    - match: \bimpl\b
+      scope: storage.type.impl.rust
+
+    - match: \benum\b
+      scope: storage.type.enum.rust
+
+    - match: \btype\b
+      scope: storage.type.type.rust
+
+    - match: \btrait\b
+      scope: storage.type.trait.rust
+
+    - match: \b(mut|pub|unsafe|move|ref)\b
+      scope: storage.modifier.rust
+
+    - match: \b(crate|extern|use|where)\b
+      scope: keyword.other.rust
+
+    - match: \b(else|for|if|loop|match|while|yield)\b
+      scope: keyword.control.rust
+
+    - match: \b(break|continue)\b
+      scope: keyword.control.rust
+
+    - match: \breturn\b
+      scope: keyword.control.rust
+
+    - match: \b(as|in|box)\b
+      scope: keyword.operator.rust
+
+    - match: \b(virtual|become|priv|typeof|unsized|do|abstract|final|override|macro)\b
+      scope: invalid.illegal.rust
+
+    - match: \b(super|self|Self)\b
+      scope: keyword.other.rust


Test patch:
diff --git a/tests/syntax-rust/syntax_test_macros.rs b/tests/syntax-rust/syntax_test_macros.rs
index faf24753..d90cd62b 100644
--- a/tests/syntax-rust/syntax_test_macros.rs
+++ b/tests/syntax-rust/syntax_test_macros.rs
@@ -99,8 +99,91 @@ my_var = format!("Hello {name}, how are you?",
 //              ^ punctuation.definition.group.begin
 //               ^ punctuation.definition.group.end
 
+/*******************************************************************/
+// The outer brackets can be any type.
+macro_rules! brackets_curly {
+//                          ^ meta.macro punctuation.section.block.begin
+    ($i:ident) => ($i)
+//  ^^^^^^^^^^ meta.macro meta.macro.matchers
+//             ^^ meta.macro keyword.operator
+//                ^^^^ meta.macro meta.macro.transcribers
+}
+// <- meta.macro punctuation.section.block.end
+macro_rules! brackets_paren (
+//                          ^ meta.macro punctuation.section.block.begin
+    ($i:ident) => ($i)
+//  ^^^^^^^^^^ meta.macro meta.macro.matchers
+//             ^^ meta.macro keyword.operator
+//                ^^^^ meta.macro meta.macro.transcribers
+  );
+//^ meta.macro punctuation.section.block.end
+// ^ punctuation.terminator
+macro_rules! brackets_square [
+//                           ^ meta.macro punctuation.section.block.begin
+    ($i:ident) => ($i)
+//  ^^^^^^^^^^ meta.macro meta.macro.matchers
+//             ^^ meta.macro keyword.operator
+//                ^^^^ meta.macro meta.macro.transcribers
+  ];
+//^ meta.macro punctuation.section.block.end
+// ^ punctuation.terminator
+
+/*******************************************************************/
+// Matchers and transcribers can use any bracket type.
+macro_rules! brackets {
+//^^^^^^^^^^ meta.macro support.function
+//           ^^^^^^^^ meta.macro entity.name.macro
+//                    ^ meta.macro punctuation.section.block.begin
+    ($i:ident) => ($i);
+//  ^^^^^^^^^^ meta.macro meta.macro.matchers
+//                ^^^^ meta.macro meta.macro.transcribers
+//  ^ punctuation.section.block.begin
+//   ^^ variable.parameter
+//     ^ punctuation.separator
+//      ^^^^^ storage.type
+//           ^ punctuation.section.block.end
+//             ^^ meta.macro keyword.operator
+//                ^ punctuation.section.block.begin
+//                 ^^ variable.other
+//                   ^ punctuation.section.block.end
+//                    ^ meta.macro punctuation.terminator
+    {$i:ident} => {$i};
+//  ^^^^^^^^^^ meta.macro meta.macro.matchers
+//                ^^^^ meta.macro meta.macro.transcribers
+//  ^ punctuation.section.block.begin
+//   ^^ variable.parameter
+//      ^^^^^ storage.type
+//           ^ punctuation.section.block.end
+//                ^ punctuation.section.block.begin
+//                   ^ punctuation.section.block.end
+//                    ^ meta.macro punctuation.terminator
+    [$i:ident] => [$i];
+//  ^^^^^^^^^^ meta.macro meta.macro.matchers
+//                ^^^^ meta.macro meta.macro.transcribers
+//  ^ punctuation.section.block.begin
+//   ^^ variable.parameter
+//      ^^^^^ storage.type
+//           ^ punctuation.section.block.end
+//                ^ punctuation.section.block.begin
+//                   ^ punctuation.section.block.end
+    // And they don't have to match.
+    ($i:ident) => [$i];
+//  ^^^^^^^^^^ meta.macro meta.macro.matchers
+//                ^^^^ meta.macro meta.macro.transcribers
+    // Support nested brackets within matcher.
+    ((hello) ($i:ident) [foo] {bar}) => {$i};
+//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.macro meta.macro.matchers
+//            ^^ variable.parameter
+//               ^^^^^ storage.type
+//                                   ^^ meta.macro keyword.operator
+//                                      ^^^^ meta.macro meta.macro.transcribers
+//                                       ^^ meta.macro meta.macro.transcribers variable.other
+}
+
+/*******************************************************************/
+// Typical example with embedded rust code.
 macro_rules! forward_ref_binop [
-//                             ^ meta.macro meta.group punctuation.definition.group.begin
+//                             ^ meta.macro punctuation.section.block.begin
     (impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
 //        ^^^^ variable.parameter
 //             ^^^^^ storage.type
@@ -111,7 +194,7 @@ macro_rules! forward_ref_binop [
 //                                             ^^ variable.parameter
 //                                                ^^ storage.type
 //                                                    ^^ keyword.operator
-//                                                       ^ meta.macro meta.group meta.block punctuation.definition.block.begin
+//                                                       ^ meta.macro meta.macro.transcribers punctuation.section.block.begin
         impl<'a, 'b> $imp<&'a $u> for &'b $t {
 //      ^^^^ storage.type.impl
 //          ^^^^^^^^ meta.generic
@@ -126,7 +209,7 @@ macro_rules! forward_ref_binop [
 //                                    ^ keyword.operator
 //                                     ^^ storage.modifier.lifetime
 //                                        ^^ variable.other
-//                                           ^ meta.macro meta.group meta.block meta.impl meta.block punctuation.definition.block.begin
+//                                           ^ meta.macro meta.macro.transcribers meta.impl meta.block punctuation.definition.block.begin
             type Output = <$t as $imp<$u>>::Output;
 //                        ^^^^^^^^^^^^^^^^ meta.generic
 //                                        ^^ meta.path
@@ -143,7 +226,7 @@ macro_rules! forward_ref_binop [
 //                                          ^^ punctuation.separator
 //                                             ^^^^^^^^^^^^^^^^ meta.generic
 //                                                             ^^ meta.path
-//                                                                      ^ meta.macro meta.group meta.block meta.impl meta.block meta.block punctuation.definition.block.begin
+//                                                                      ^ meta.macro meta.macro.transcribers meta.impl meta.block meta.block punctuation.definition.block.begin
                 $imp::$method(*self, *other)
 //              ^^^^ variable.other
 //                    ^^^^^^^ variable.other
@@ -155,57 +238,134 @@ macro_rules! forward_ref_binop [
     }
 ]
 
-macro_rules! alternate_group (
-//                           ^ meta.macro meta.group punctuation.definition.group.begin
-    ($a:expr) => (
-//   ^^ variable.parameter
-//      ^^^^ storage.type
-        println!("Test {}!", $a)
-    )
-)
-
+/*******************************************************************/
+// Kleene operators.
 macro_rules! kleene_star {
     ($($arg:tt)+) => (
-//   ^ meta.macro meta.block meta.group keyword.operator
-//    ^ meta.macro meta.block meta.group punctuation.definition.group.begin
-//     ^^^^ meta.macro meta.block meta.group variable.other
-//         ^^^^^ meta.macro meta.block meta.group
-//              ^ meta.macro meta.block meta.group punctuation.definition.group.end
-//                ^ meta.macro meta.block keyword.operator
+//  ^^^^^^^^^^^^^ meta.macro meta.macro.matchers
+//   ^ keyword.operator
+//    ^ punctuation.section.group.begin
+//     ^^^^ variable.parameter
+//         ^ punctuation.separator
+//          ^^ storage.type
+//            ^ punctuation.definition.group.end
+//             ^ keyword.operator
+//              ^ punctuation.section.block.end
+//                ^^ meta.macro keyword.operator
+//                   ^ meta.macro meta.macro.transcribers punctuation.section.block.begin
         println!($($arg));
-    ),
+    );
     ($($arg:tt)*) => (
-//     ^^^^ meta.macro meta.block meta.group variable.other
-//         ^^^^^ meta.macro meta.block meta.group
-//              ^ meta.macro meta.block meta.group punctuation.definition.group.end
-//                ^ meta.macro meta.block keyword.operator
+//  ^^^^^^^^^^^^^ meta.macro meta.macro.matchers
+//     ^^^^ variable.parameter
+//             ^ keyword.operator
+//              ^ punctuation.section.block.end
+//                ^^ meta.macro keyword.operator
         println!($($arg)*);
-    ),
+    );
     ($($arg:tt);+) => (
-//     ^^^^ meta.macro meta.block meta.group variable.other
-//         ^^^^^^ meta.macro meta.block meta.group
-//               ^ meta.macro meta.block meta.group punctuation.definition.group.end
-//                 ^ meta.macro meta.block keyword.operator
+//  ^^^^^^^^^^^^^^ meta.macro meta.macro.matchers
+//     ^^^^ variable.parameter
+//          ^^ storage.type
+//             ^ meta.macro meta.macro.matchers
+//              ^ keyword.operator
+//               ^ punctuation.section.block.end
+//                 ^^ meta.macro keyword.operator
         println!($($arg));
-    ),
+    );
     ($($arg:tt),*) => (
-//     ^^^^ meta.macro meta.block meta.group variable.other
-//         ^^^^^^ meta.macro meta.block meta.group
-//               ^ meta.macro meta.block meta.group punctuation.definition.group.end
-//                 ^ meta.macro meta.block keyword.operator
+//  ^^^^^^^^^^^^^^ meta.macro meta.macro.matchers
+//     ^^^^  variable.parameter
+//          ^^  storage.type
+//             ^
+//              ^  keyword.operator
+//               ^ meta.macro meta.macro.matchers punctuation.section.block.end
+//                 ^^ meta.macro keyword.operator
         println!($($arg)*);
-    )
+    );
+    // Spacing should be ignored.
+    ( $ ( $ arg : tt ) , * ) => ();
+//  ^^^^^^^^^^^^^^^^^^^^^^^^ meta.macro meta.macro.matchers
+//  ^ punctuation.section.block.begin
+//    ^ keyword.operator
+//      ^ punctuation.section.group.begin
+//        ^^^^^ variable.parameter
+//              ^ punctuation.separator
+//                ^^ storage.type
+//                   ^ punctuation.definition.group.end
+//                       ^ keyword.operator
+//                         ^ punctuation.section.block.end
+//                           ^^ meta.macro keyword.operator
+    // Separators can be any token.
+    ($($foo:tt) else *) => ();
+//  ^^^^^^^^^^^^^^^^^^^ meta.macro meta.macro.matchers
+//                   ^ keyword.operator
+//                      ^^ meta.macro keyword.operator
+//                         ^^ meta.macro meta.macro.transcribers
+
+    // At-most-once is new in 2018.
+    // Note: This is in flux, but looks like they've landed on a final form.
+    // https://github.com/rust-lang/rust/issues/51934
+    ($($a:ident)? ; $num:expr) => {};
+//  ^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.macro meta.macro.matchers
+//              ^ keyword.operator
+//                  ^^^^ variable.parameter
+//                                ^^ meta.macro meta.macro.transcribers
+
 }
+// <- meta.macro punctuation.section.block.end
 
-macro_rules! eat_numbers {
-    ($count:expr, /*$comment:ident,*/  $msg:expr) => {{
-    //            ^^^^^^^^^^^^^^^^^^^ meta.macro meta.block meta.group comment.block
-    //                                               ^ meta.macro meta.block meta.block punctuation.definition.block.begin
-    //                                                ^ meta.macro meta.block meta.block meta.block punctuation.definition.block.begin
-        let parse_err = concat!("Err parsing value in ", $msg);
-        try!{ eat_numbers(&mut lines, $count, parse_err, missing_err, too_many) }
-    //  ^^^^ support.macro
-    //      ^ meta.macro meta.block meta.block meta.block meta.block punctuation.definition.block.begin
-    }}
-};
- // <- -meta.macro
+/*******************************************************************/
+// Different matcher types.
+macro_rules! designators {
+    ($i:item,
+//   ^^ variable.parameter
+//      ^^^^ storage.type
+     $b:block,
+//   ^^ variable.parameter
+//      ^^^^^ storage.type
+     $s:stmt,
+//   ^^ variable.parameter
+//      ^^^^ storage.type
+     $p:pat,
+//   ^^ variable.parameter
+//      ^^^ storage.type
+     $e:expr,
+//   ^^ variable.parameter
+//      ^^^^ storage.type
+     $t:ty,
+//   ^^ variable.parameter
+//      ^^ storage.type
+     $i:ident,
+//   ^^ variable.parameter
+//      ^^^^^ storage.type
+     $p:path,
+//   ^^ variable.parameter
+//      ^^^^ storage.type
+     $t:tt,
+//   ^^ variable.parameter
+//      ^^ storage.type
+     $m:meta,
+//   ^^ variable.parameter
+//      ^^^^ storage.type
+     $l:lifetime) => ();
+//   ^^ variable.parameter
+//      ^^^^^^^^ storage.type
+    // And various tokens
+    ("Any token" /*comment*/ true => 3.14 'life 'c' @ struct self) => ();
+//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.macro meta.macro.matchers
+//   ^^^^^^^^^^^ string.quoted.double
+//               ^^^^^^^^^^^ comment.block
+//                           ^^^^ constant.language
+//                                ^^ keyword.operator
+//                                   ^^^^ constant.numeric.float
+//                                        ^^^^^ storage.modifier.lifetime
+//                                              ^^^ string.quoted.single
+//                                                  ^ keyword.operator
+//                                                    ^^^^^^ storage.type.struct
+//                                                           ^^^^ keyword.other
+//                                                               ^ punctuation.section.block.end
+//                                                                 ^^ meta.macro keyword.operator
+//                                                                    ^^ meta.macro meta.macro.transcribers
+}
+//<- meta.macro punctuation.section.block.end