Repository: dtolnay/anyhow
Pull request #198 (2021-11-22 17:34:04)

Preserve spans of all punctuation and keyword tokens in ensure
This uses a technique that has worked well in `serde_json::json!` &mdash; we keep a double copy of the input tokens where we can match punctuation and keywords against one copy and capture the other copy as $:tt.

Relevant for compiler error messages that want to point to punctuation or keyword tokens; without this change the compiler would see the output tokens as originating from inside the macro implementation, rather than what was written by the user, resulting in confusing placement of diagnostics.


Patch:
diff --git a/src/ensure.rs b/src/ensure.rs
index a919ed3..d010a06 100644
--- a/src/ensure.rs
+++ b/src/ensure.rs
@@ -103,394 +103,390 @@ fn render(msg: &'static str, lhs: &dyn Debug, rhs: &dyn Debug) -> Error {
 #[doc(hidden)]
 #[macro_export]
 macro_rules! __parse_ensure {
-    (atom () $bail:tt $fuel:tt {($($rhs:tt)+) ($($lhs:tt)+) $op:tt} $(,)?) => {
+    (atom () $bail:tt $fuel:tt {($($rhs:tt)+) ($($lhs:tt)+) $op:tt} $dup:tt $(,)?) => {
         $crate::__fancy_ensure!($($lhs)+, $op, $($rhs)+)
     };
 
     // low precedence control flow constructs
 
-    (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt return $($rest:tt)*) => {
+    (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt $dup:tt return $($rest:tt)*) => {
         $crate::__fallback_ensure!($($bail)*)
     };
 
-    (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt break $($rest:tt)*) => {
+    (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt $dup:tt break $($rest:tt)*) => {
         $crate::__fallback_ensure!($($bail)*)
     };
 
-    (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt continue $($rest:tt)*) => {
+    (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt $dup:tt continue $($rest:tt)*) => {
         $crate::__fallback_ensure!($($bail)*)
     };
 
-    (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt yield $($rest:tt)*) => {
+    (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt $dup:tt yield $($rest:tt)*) => {
         $crate::__fallback_ensure!($($bail)*)
     };
 
-    (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt move $($rest:tt)*) => {
+    (0 $stack:tt ($($bail:tt)*) $fuel:tt $parse:tt $dup:tt move $($rest:tt)*) => {
         $crate::__fallback_ensure!($($bail)*)
     };
 
     // unary operators
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} * $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* *) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($deref:tt $($dup:tt)*) * $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $deref) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ! $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* !) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($not:tt $($dup:tt)*) ! $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $not) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} - $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* -) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($neg:tt $($dup:tt)*) - $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $neg) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} let $(|)? $($pat:pat)|+ = $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* let $($pat)|+ =) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($let:tt $($dup:tt)*) let $(|)? $($pat:pat)|+ = $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $let $($pat)|+ =) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $life:lifetime : $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $life :) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($life:tt $colon:tt $($dup:tt)*) $label:lifetime : $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $life $colon) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} &mut $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* &mut) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $mut:tt $($dup:tt)*) &mut $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $and $mut) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} & $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* &) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $($dup:tt)*) & $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $and) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} &&mut $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* &&mut) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($andand:tt $mut:tt $($dup:tt)*) &&mut $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $andand $mut) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} && $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* &&) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($andand:tt $($dup:tt)*) && $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $andand) $($parse)*} ($($rest)*) $($rest)*)
     };
 
     // control flow constructs
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} if $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* if) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($if:tt $($dup:tt)*) if $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* $if) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} match $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* match) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($match:tt $($dup:tt)*) match $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* $match) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} while $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* while) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($while:tt $($dup:tt)*) while $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* $while) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} for $(|)? $($pat:pat)|+ in $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* for $($pat)|+ in) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($for:tt $($dup:tt)*) for $(|)? $($pat:pat)|+ in $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* $for $($pat)|+ in) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom (cond $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} {$($block:tt)*} $($rest:tt)*) => {
-        $crate::__parse_ensure!(cond $stack $bail ($($fuel)*) {($($buf)* {$($block)*}) $($parse)*} $($rest)*)
+    (atom (cond $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($brace:tt $($dup:tt)*) {$($block:tt)*} $($rest:tt)*) => {
+        $crate::__parse_ensure!(cond $stack $bail ($($fuel)*) {($($buf)* $brace) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (cond $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} else if $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* else if) $($parse)*} $($rest)*)
+    (cond $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($else:tt $if:tt $($dup:tt)*) else if $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 (cond $stack) $bail ($($fuel)*) {($($buf)* $else $if) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (cond $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} else {$($block:tt)*} $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* else {$($block)*}) $($parse)*} $($rest)*)
+    (cond $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($else:tt $brace:tt $($dup:tt)*) else {$($block:tt)*} $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $else $brace) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (cond $stack:tt $bail:tt (~$($fuel:tt)*) $parse:tt $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) $parse $($rest)*)
+    (cond $stack:tt $bail:tt (~$($fuel:tt)*) $parse:tt $dup:tt $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) $parse $dup $($rest)*)
     };
 
     // atomic expressions
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($($paren:tt)*) $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* ($($paren)*)) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($paren:tt $($dup:tt)*) ($($content:tt)*) $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $paren) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} [$($array:tt)*] $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* [$($array)*]) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bracket:tt $($dup:tt)*) [$($array:tt)*] $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $bracket) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} {$($block:tt)*} $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* {$($block)*}) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($brace:tt $($dup:tt)*) {$($block:tt)*} $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $brace) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} loop {$($block:tt)*} $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* loop {$($block)*}) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($loop:tt $block:tt $($dup:tt)*) loop {$($body:tt)*} $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $loop $block) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} async {$($block:tt)*} $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* async {$($block)*}) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($async:tt $block:tt $($dup:tt)*) async {$($body:tt)*} $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $async $block) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} async move {$($block:tt)*} $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* async move {$($block)*}) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($async:tt $move:tt $block:tt $($dup:tt)*) async move {$($body:tt)*} $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $async $move $block) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} unsafe {$($block:tt)*} $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* unsafe {$($block)*}) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($unsafe:tt $block:tt $($dup:tt)*) unsafe {$($body:tt)*} $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $unsafe $block) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $lit:literal $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $lit) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $lit:literal $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $lit) $($parse)*} ($($rest)*) $($rest)*)
     };
 
     // path expressions
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} :: $($rest:tt)*) => {
-        $crate::__parse_ensure!(path $stack $bail ($($fuel)*) {($($buf)* ::) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: $($rest:tt)*) => {
+        $crate::__parse_ensure!(path $stack $bail ($($fuel)*) {($($buf)* $colons) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $ident:ident $($rest:tt)*) => {
-        $crate::__parse_ensure!(component $stack $bail ($($fuel)*) {($($buf)* $ident) $($parse)*} $($rest)*)
+    (0 $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $ident:ident $($rest:tt)*) => {
+        $crate::__parse_ensure!(component $stack $bail ($($fuel)*) {($($buf)* $ident) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (path $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $ident:ident $($rest:tt)*) => {
-        $crate::__parse_ensure!(component $stack $bail ($($fuel)*) {($($buf)* $ident) $($parse)*} $($rest)*)
+    (path $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $ident:ident $($rest:tt)*) => {
+        $crate::__parse_ensure!(component $stack $bail ($($fuel)*) {($($buf)* $ident) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} :: < $($rest:tt)*) => {
-        $crate::__parse_ensure!(generic (component $stack) $bail ($($fuel)*) {($($buf)* :: <) $($parse)*} $($rest)*)
+    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $langle:tt $($dup:tt)*) :: < $($rest:tt)*) => {
+        $crate::__parse_ensure!(generic (component $stack) $bail ($($fuel)*) {($($buf)* $colons $langle) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} :: << $($rest:tt)*) => {
-        $crate::__parse_ensure!(generic (component $stack) $bail ($($fuel)*) {($($buf)* :: <) $($parse)*} < $($rest)*)
+    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: << $($rest:tt)*) => {
+        $crate::__parse_ensure!(generic (component $stack) $bail ($($fuel)*) {($($buf)* $colons <) $($parse)*} (< $($rest)*) < $($rest)*)
     };
 
-    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} :: $($rest:tt)*) => {
-        $crate::__parse_ensure!(path $stack $bail ($($fuel)*) {($($buf)* ::) $($parse)*} $($rest)*)
+    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($colons:tt $($dup:tt)*) :: $($rest:tt)*) => {
+        $crate::__parse_ensure!(path $stack $bail ($($fuel)*) {($($buf)* $colons) $($parse)*} ($($rest)*) $($rest)*)
     };
 
     // macro invocations
 
-    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ! ($($mac:tt)*) $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* ! ($($mac)*)) $($parse)*} $($rest)*)
+    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bang:tt $args:tt $($dup:tt)*) ! ($($mac:tt)*) $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $bang $args) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ! [$($mac:tt)*] $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* ! [$($mac)*]) $($parse)*} $($rest)*)
+    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bang:tt $args:tt $($dup:tt)*) ! [$($mac:tt)*] $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $bang $args) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ! {$($mac:tt)*} $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* ! {$($mac)*}) $($parse)*} $($rest)*)
+    (component $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bang:tt $args:tt $($dup:tt)*) ! {$($mac:tt)*} $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $bang $args) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (component $stack:tt $bail:tt (~$($fuel:tt)*) $parse:tt $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) $parse $($rest)*)
+    (component $stack:tt $bail:tt (~$($fuel:tt)*) $parse:tt $dup:tt $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) $parse $dup $($rest)*)
     };
 
     // trailer expressions
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($($call:tt)*) $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* ($($call)*)) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($paren:tt $($dup:tt)*) ($($call:tt)*) $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $paren) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} [$($index:tt)*] $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* [$($index)*]) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bracket:tt $($dup:tt)*) [$($index:tt)*] $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $bracket) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} {$($init:tt)*} $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* {$($init)*}) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($brace:tt $($dup:tt)*) {$($init:tt)*} $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $brace) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ? $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* ?) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($question:tt $($dup:tt)*) ? $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $question) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} . $ident:ident :: < $($rest:tt)*) => {
-        $crate::__parse_ensure!(generic (atom $stack) $bail ($($fuel)*) {($($buf)* . $ident :: <) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($dot:tt $ident:tt $colons:tt $langle:tt $($dup:tt)*) . $i:ident :: < $($rest:tt)*) => {
+        $crate::__parse_ensure!(generic (atom $stack) $bail ($($fuel)*) {($($buf)* $dot $ident $colons $langle) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} . $ident:ident :: << $($rest:tt)*) => {
-        $crate::__parse_ensure!(generic (atom $stack) $bail ($($fuel)*) {($($buf)* . $ident :: <) $($parse)*} < $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($dot:tt $ident:tt $colons:tt $($dup:tt)*) . $i:ident :: << $($rest:tt)*) => {
+        $crate::__parse_ensure!(generic (atom $stack) $bail ($($fuel)*) {($($buf)* $dot $ident $colons <) $($parse)*} (< $($rest)*) < $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} . $ident:ident $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* . $ident) $($parse)*} $($rest)*)
-    };
-
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} . $lit:tt $($rest:tt)*) => {
-        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* . $lit) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($dot:tt $($dup:tt)*) . $index:tt $($rest:tt)*) => {
+        $crate::__parse_ensure!(atom $stack $bail ($($fuel)*) {($($buf)* $dot $index) $($parse)*} ($($rest)*) $($rest)*)
     };
 
     // angle bracketed generic arguments
 
-    (generic ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} > $($rest:tt)*) => {
-        $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* >) $($parse)*} $($rest)*)
+    (generic ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($rangle:tt $($dup:tt)*) > $($rest:tt)*) => {
+        $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $rangle) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $lit:literal $($rest:tt)*) => {
-        $crate::__parse_ensure!(arglist $stack $bail ($($fuel)*) {($($buf)* $lit) $($parse)*} $($rest)*)
+    (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $lit:literal $($rest:tt)*) => {
+        $crate::__parse_ensure!(arglist $stack $bail ($($fuel)*) {($($buf)* $lit) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} {$($block:tt)*} $($rest:tt)*) => {
-        $crate::__parse_ensure!(arglist $stack $bail ($($fuel)*) {($($buf)* {$($block)*}) $($parse)*} $($rest)*)
+    (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($brace:tt $($dup:tt)*) {$($block:tt)*} $($rest:tt)*) => {
+        $crate::__parse_ensure!(arglist $stack $bail ($($fuel)*) {($($buf)* $brace) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $life:lifetime $($rest:tt)*) => {
-        $crate::__parse_ensure!(arglist $stack $bail ($($fuel)*) {($($buf)* $life) $($parse)*} $($rest)*)
+    (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $life:lifetime $($rest:tt)*) => {
+        $crate::__parse_ensure!(arglist $stack $bail ($($fuel)*) {($($buf)* $life) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $ty:ty , $($rest:tt)*) => {
-        $crate::__parse_ensure!(generic $stack $bail ($($fuel)*) {($($buf)* $ty ,) $($parse)*} $($rest)*)
+    (generic $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $ty:ty , $($rest:tt)*) => {
+        $crate::__parse_ensure!(generic $stack $bail ($($fuel)*) {($($buf)* $ty ,) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (generic ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $ty:ty > $($rest:tt)*) => {
-        $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $ty >) $($parse)*} $($rest)*)
+    (generic ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} $dup:tt $ty:ty > $($rest:tt)*) => {
+        $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $ty >) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (arglist $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} , $($rest:tt)*) => {
-        $crate::__parse_ensure!(generic $stack $bail ($($fuel)*) {($($buf)* ,) $($parse)*} $($rest)*)
+    (arglist $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($comma:tt $($dup:tt)*) , $($rest:tt)*) => {
+        $crate::__parse_ensure!(generic $stack $bail ($($fuel)*) {($($buf)* $comma) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (arglist ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} > $($rest:tt)*) => {
-        $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* >) $($parse)*} $($rest)*)
+    (arglist ($pop:ident $stack:tt) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($rangle:tt $($dup:tt)*) > $($rest:tt)*) => {
+        $crate::__parse_ensure!($pop $stack $bail ($($fuel)*) {($($buf)* $rangle) $($parse)*} ($($rest)*) $($rest)*)
     };
 
     // high precedence binary operators
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} + $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* +) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($add:tt $($dup:tt)*) + $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $add) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} - $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* -) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($sub:tt $($dup:tt)*) - $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $sub) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} * $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* *) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($mul:tt $($dup:tt)*) * $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $mul) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} / $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* /) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($div:tt $($dup:tt)*) / $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $div) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} % $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* %) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($rem:tt $($dup:tt)*) % $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $rem) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ^ $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* ^) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitxor:tt $($dup:tt)*) ^ $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $bitxor) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} & $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* &) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitand:tt $($dup:tt)*) & $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $bitand) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} | $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* |) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitor:tt $($dup:tt)*) | $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $bitor) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} << $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* <<) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($shl:tt $($dup:tt)*) << $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $shl) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} >> $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* >>) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($shr:tt $($dup:tt)*) >> $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $shr) $($parse)*} ($($rest)*) $($rest)*)
     };
 
     // comparison binary operators
 
-    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} == $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) ==} $($rest)*)
+    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($eq:tt $($dup:tt)*) == $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $eq} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} == $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* ==) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($eq:tt $($dup:tt)*) == $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $eq) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} <= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) <=} $($rest)*)
+    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($le:tt $($dup:tt)*) <= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $le} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} <= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* <=) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($le:tt $($dup:tt)*) <= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $le) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} < $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) <} $($rest)*)
+    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($lt:tt $($dup:tt)*) < $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $lt} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} < $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* <) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($lt:tt $($dup:tt)*) < $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $lt) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} != $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) !=} $($rest)*)
+    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($ne:tt $($dup:tt)*) != $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $ne} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} != $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* !=) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($ne:tt $($dup:tt)*) != $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $ne) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} >= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) >=} $($rest)*)
+    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($ge:tt $($dup:tt)*) >= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $ge} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} >= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* >=) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($ge:tt $($dup:tt)*) >= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $ge) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} > $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) >} $($rest)*)
+    (atom () $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($gt:tt $($dup:tt)*) > $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 () $bail ($($fuel)*) {() $($parse)* ($($buf)*) $gt} ($($rest)*) $($rest)*)
     };
 
-    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} > $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* >) $($parse)*} $($rest)*)
+    (atom $stack:tt $bail:tt (~$($fuel:tt)*) {($($buf:tt)+) $($parse:tt)*} ($gt:tt $($dup:tt)*) > $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 $stack $bail ($($fuel)*) {($($buf)* $gt) $($parse)*} ($($rest)*) $($rest)*)
     };
 
     // low precedence binary operators
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} && $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* &&) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($and:tt $($dup:tt)*) && $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $and) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} || $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* ||) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($or:tt $($dup:tt)*) || $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $or) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} = $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* =) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($assign:tt $($dup:tt)*) = $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $assign) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} += $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* +=) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($addeq:tt $($dup:tt)*) += $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $addeq) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} -= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* -=) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($subeq:tt $($dup:tt)*) -= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $subeq) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} *= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* *=) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($muleq:tt $($dup:tt)*) *= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $muleq) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} /= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* /=) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($diveq:tt $($dup:tt)*) /= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $diveq) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} %= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* %=) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($remeq:tt $($dup:tt)*) %= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $remeq) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ^= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* ^=) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitxoreq:tt $($dup:tt)*) ^= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $bitxoreq) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} &= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* &=) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitandeq:tt $($dup:tt)*) &= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $bitandeq) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} |= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* |=) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($bitoreq:tt $($dup:tt)*) |= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $bitoreq) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} <<= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* <<=) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($shleq:tt $($dup:tt)*) <<= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $shleq) $($parse)*} ($($rest)*) $($rest)*)
     };
 
-    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} >>= $($rest:tt)*) => {
-        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* >>=) $($parse)*} $($rest)*)
+    (atom ($($stack:tt)+) $bail:tt (~$($fuel:tt)*) {($($buf:tt)*) $($parse:tt)*} ($shreq:tt $($dup:tt)*) >>= $($rest:tt)*) => {
+        $crate::__parse_ensure!(0 ($($stack)*) $bail ($($fuel)*) {($($buf)* $shreq) $($parse)*} ($($rest)*) $($rest)*)
     };
 
     // unrecognized expression
diff --git a/src/macros.rs b/src/macros.rs
index 140b91a..89dc4d2 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -170,6 +170,7 @@ macro_rules! ensure {
             /* bail */ ($($tt)*)
             /* fuel */ (~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~)
             /* parse */ {()}
+            /* dup */ ($($tt)*)
             /* rest */ $($tt)*
         )
     };


Test patch:
diff --git a/tests/test_ensure.rs b/tests/test_ensure.rs
index 22ec908..18bd865 100644
--- a/tests/test_ensure.rs
+++ b/tests/test_ensure.rs
@@ -1,9 +1,15 @@
 #![allow(
     clippy::diverging_sub_expression,
+    clippy::if_same_then_else,
+    clippy::ifs_same_cond,
     clippy::let_and_return,
     clippy::let_underscore_drop,
+    clippy::match_bool,
     clippy::never_loop,
-    clippy::unit_arg
+    clippy::redundant_closure_call,
+    clippy::redundant_pattern_matching,
+    clippy::unit_arg,
+    clippy::while_immutable_condition
 )]
 
 use anyhow::{anyhow, ensure, Chain, Error, Result};