Change:
Suppose I've written code like: The variable is unused, and the error message I get looks like: The help is incorrect; is invalid. It should suggest replacing with
This bad suggestion was fixed in most cases by , but this case would seem to have evaded it. (Notice that on Nightly you can omit the ampersands from the match guards (due to the ergonomic) and receive the correct suggestion!)

Discussion:
use std::io; use std::rc::Rc; use syntax::ast::{self, NodeId}; <ins> use syntax::ptr::P; </ins> use syntax::symbol::keywords; use syntax_pos::Span;

lsets.warn_about_unused_args(body, entry_ln); } <del> fn visit_local<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, local: &'tcx hir::Local) { local.pat.each_binding(|_, p_id, sp, path1| { debug!("adding local variable {}", p_id); </del> <ins> fn add_from_pat<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, pat: &P<hir::Pat>) { // For struct patterns, take note of which fields used shorthand // (`x` rather than `x: x`). // // FIXME: according to the rust-lang-nursery/rustc-guide book, `NodeId`s are to be // phased out in favor of `HirId`s; however, we need to match the signature of // `each_binding`, which uses `NodeIds`. let mut shorthand_field_ids = NodeSet(); let mut pats = VecDeque::new(); pats.push_back(pat); while let Some(pat) = pats.pop_front() { use hir::PatKind::*; match pat.node { Binding(_, _, _, ref inner_pat) => { pats.extend(inner_pat.iter()); } Struct(_, ref fields, _) => { for field in fields { if field.node.is_shorthand { shorthand_field_ids.insert(field.node.pat.id); } } } Ref(ref inner_pat, _) | Box(ref inner_pat) => { pats.push_back(inner_pat); } TupleStruct(_, ref inner_pats, _) | Tuple(ref inner_pats, _) => { pats.extend(inner_pats.iter()); } Slice(ref pre_pats, ref inner_pat, ref post_pats) => { pats.extend(pre_pats.iter()); pats.extend(inner_pat.iter()); pats.extend(post_pats.iter()); } _ => {} } } pat.each_binding(|_bm, p_id, _sp, path1| { </ins> let name = path1.node; <del> ir.add_live_node_for_node(p_id, VarDefNode(sp)); </del> <ins> ir.add_live_node_for_node(p_id, VarDefNode(path1.span)); </ins> ir.add_variable(Local(LocalInfo { id: p_id, name, <del> is_shorthand: false, </del> <ins> is_shorthand: shorthand_field_ids.contains(&p_id) </ins> })); }); <ins> } fn visit_local<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, local: &'tcx hir::Local) { add_from_pat(ir, &local.pat); </ins> intravisit::walk_local(ir, local); } fn visit_arm<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, arm: &'tcx hir::Arm) { <del> for mut pat in &arm.pats { // For struct patterns, take note of which fields used shorthand // (`x` rather than `x: x`). // // FIXME: according to the rust-lang-nursery/rustc-guide book, `NodeId`s are to be // phased out in favor of `HirId`s; however, we need to match the signature of // `each_binding`, which uses `NodeIds`. let mut shorthand_field_ids = NodeSet(); let mut pats = VecDeque::new(); pats.push_back(pat); while let Some(pat) = pats.pop_front() { use hir::PatKind::*; match pat.node { Binding(_, _, _, ref inner_pat) => { pats.extend(inner_pat.iter()); } Struct(_, ref fields, _) => { for field in fields { if field.node.is_shorthand { shorthand_field_ids.insert(field.node.pat.id); } } } Ref(ref inner_pat, _) | Box(ref inner_pat) => { pats.push_back(inner_pat); } TupleStruct(_, ref inner_pats, _) | Tuple(ref inner_pats, _) => { pats.extend(inner_pats.iter()); } Slice(ref pre_pats, ref inner_pat, ref post_pats) => { pats.extend(pre_pats.iter()); pats.extend(inner_pat.iter()); pats.extend(post_pats.iter()); } _ => {} } } pat.each_binding(|bm, p_id, _sp, path1| { debug!("adding local variable {} from match with bm {:?}", p_id, bm); let name = path1.node; ir.add_live_node_for_node(p_id, VarDefNode(path1.span)); ir.add_variable(Local(LocalInfo { id: p_id, name: name, is_shorthand: shorthand_field_ids.contains(&p_id) })); }) </del> <ins> for pat in &arm.pats { add_from_pat(ir, pat); </ins> } intravisit::walk_arm(ir, arm); }

endless_and_singing: bool } <ins> struct LovelyAmbition { lips: usize, fire: usize } </ins> #[derive(Clone, Copy)] enum Large { Suit { case: () }

hours_are_suns = false; } <ins> let the_spirit = LovelyAmbition { lips: 1, fire: 2 }; let LovelyAmbition { lips, fire } = the_spirit; println!("{}", lips); </ins> let bag = Large::Suit { case: () };

warning: unused variable: `i_think_continually` <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:31:9 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:36:9 </ins> | LL |     let i_think_continually = 2; |         ^^^^^^^^^^^^^^^^^^^ help: consider using `_i_think_continually` instead

= note: #[warn(unused_variables)] implied by #[warn(unused)] warning: unused variable: `mut_unused_var` <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:38:13 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:43:13 </ins> | LL |     let mut mut_unused_var = 1; |             ^^^^^^^^^^^^^^ help: consider using `_mut_unused_var` instead warning: unused variable: `var` <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:40:14 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:45:14 </ins> | LL |     let (mut var, unused_var) = (1, 2); |              ^^^ help: consider using `_var` instead warning: unused variable: `unused_var` <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:40:19 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:45:19 </ins> | LL |     let (mut var, unused_var) = (1, 2); |                   ^^^^^^^^^^ help: consider using `_unused_var` instead warning: unused variable: `corridors_of_light` <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:42:26 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:47:26 </ins> | LL |     if let SoulHistory { corridors_of_light, |                          ^^^^^^^^^^^^^^^^^^ help: try ignoring the field: `corridors_of_light: _` warning: variable `hours_are_suns` is assigned to, but never used <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:43:30 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:48:30 </ins> | LL |                          mut hours_are_suns, |                              ^^^^^^^^^^^^^^

= note: consider using `_hours_are_suns` instead warning: value assigned to `hours_are_suns` is never read <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:45:9 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:50:9 </ins> | LL |         hours_are_suns = false; |         ^^^^^^^^^^^^^^

|         ^^^^^^ = note: #[warn(unused_assignments)] implied by #[warn(unused)] <ins> warning: unused variable: `fire` --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:54:32 | LL |     let LovelyAmbition { lips, fire } = the_spirit; |                                ^^^^ help: try ignoring the field: `fire: _` </ins> warning: unused variable: `case` <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:54:23 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:63:23 </ins> | LL |         Large::Suit { case } => {} |                       ^^^^ help: try ignoring the field: `case: _` warning: unused variable: `case` <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:59:24 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:68:24 </ins> | LL |         &Large::Suit { case } => {} |                        ^^^^ help: try ignoring the field: `case: _` warning: unused variable: `case` <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:64:27 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:73:27 </ins> | LL |         box Large::Suit { case } => {} |                           ^^^^ help: try ignoring the field: `case: _` warning: unused variable: `case` <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:69:24 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:78:24 </ins> | LL |         (Large::Suit { case },) => {} |                        ^^^^ help: try ignoring the field: `case: _` warning: unused variable: `case` <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:74:24 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:83:24 </ins> | LL |         [Large::Suit { case }] => {} |                        ^^^^ help: try ignoring the field: `case: _` warning: unused variable: `case` <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:79:29 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:88:29 </ins> | LL |         Tuple(Large::Suit { case }, ()) => {} |                             ^^^^ help: try ignoring the field: `case: _` warning: variable does not need to be mutable <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:38:9 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:43:9 </ins> | LL |     let mut mut_unused_var = 1; |         ----^^^^^^^^^^^^^^

= note: #[warn(unused_mut)] implied by #[warn(unused)] warning: variable does not need to be mutable <del> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:40:10 </del> <ins> --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:45:10 </ins> | LL |     let (mut var, unused_var) = (1, 2); |          ----^^^