Defending against "redundant pattern" warnings in a pattern macro


#1

The following snippet (playground)

// (note everything is public. changing this is not on the table)
pub struct HCons<A, B> { pub head: A, pub tail: B }

#[macro_export]
macro_rules! pat {
    ($a:pat, $b:pat) => { HCons { head: $a, tail: $b } };
}    

fn main() {
    let list = HCons { head: 0, tail: 0 };
    let pat!(head, tail) = list;
    println!("{:?} {:?}", head, tail);
}

produces these warnings:

   Compiling playground v0.0.1 (file:///playground)
warning: the `head:` in this pattern is redundant
  --> src/main.rs:5:18
   |
5  |     => { HCons { head: $a, tail: $b }};
   |                  ^^^^ help: remove this
...
11 |     let pat!(head, tail) = list;
   |         ---------------- in this macro invocation
   |
   = note: #[warn(non_shorthand_field_patterns)] on by default

warning: the `tail:` in this pattern is redundant
  --> src/main.rs:5:28
   |
5  |     => { HCons { head: $a, tail: $b }};
   |                            ^^^^ help: remove this
...
11 |     let pat!(head, tail) = list;
   |         ---------------- in this macro invocation

    Finished dev [unoptimized + debuginfo] target(s) in 0.50 secs
     Running `target/debug/playground`

It does not appear that I can do anything inside the macro to prevent this. Patterns cannot contain attributes, and I can’t think of any sort of trick that would let me bind these to some temporary names first (it’s a pattern macro!). Any ideas?


#2

Okay, I came up with a terrible solution. Basically, for patterns that are idents (and those patterns ONLY), I rewrite the pattern as $pat@_. Please tell me this isn’t how it has to be.

pub struct Value<A> { value: A }

macro_rules! pat {
    // rewrite ident patterns to dodge #[warn(non_shorthand_field_patterns)]
    ($a:ident) => { Value { value: $a@_ }};
    
    // if it is any other kind of pattern, use it normally
    ($a:pat)   => { Value { value: $a }};
}    

fn main() {
    #[derive(Copy, Clone)]
    struct NotAnIdent(u8);
    
    let thing = Value { value: NotAnIdent(0) };
    // No warning. Whew!
    let pat!(value) = thing;
    let _ = value;

    // demonstrate that the macro can backtrack out of the ident branches
    // even on patterns that begin with an ident
    let pat!(NotAnIdent(_a)) = thing; // ok. Whew!
}

Edit: simplified example


Edit 2: this hack is also not viable in general, because it won’t handle named constants correctly.


#3

This has only gotten worse and worse. I’ve opened an issue:


#4

Interesting! I didn’t know that there is pattern destruction for structs!