Change:
Consider the following file in a cargo package: Cargo will warn that is not used. Upon running , cargo will change the second parameter from to , making the compilation fail. Even worse, if contained a field then would change the variable without producing any warning. a new cargo binary project with the above snippet in (presumably with ) No response No response
just applies any  suggestion produced by . In this case, the suggestion from is to replace with . I went ahead and ran to see if it could help me understand what is going on better: ` (I cleaned up the expanded code slightly, but it is the same) Looking at the expanded code, it makes me think that is seeing in as the source of the problem since it expands to and is unused in this case. The normal suggestion is to add an underscore to so it would not be "unused". So suggests to change the being passed to the macro call as it does not understand that the warning is really coming from within the macro, not the call itself. It also could be suggesting this as is not valid rust (macro?) syntax. If you change your example slightly, correctly suggests the problem comes from within the macro call. To me, this seems like a problem with , but I do not know enough about the compiler internals to be 100% certain.
Transferred to rust-lang/rust, since the suggestions are generated by the compiler.
Yes, this issue comes from rustc. The lint is emitted from here Maybe we only point out that there is an unused variable coming from , but without the suggestion? I'm not sure what is a proper lint for this test case. any thoughts on it?
The quick and straightforward way to fix this would be to only emit the suggestion in this lint if the span has the root context, but then a subset of valid suggestions wouldn't be emitted. I had an older PR that I'm having trouble finding that would automatically do that for every diagnostic, but IIRC it had some negative perf impact when not implementing it very simplistic. In this we can probably instead check that all of the spans involved have the same . If they don't, don't suggest changes.

Discussion:
passes_unused_var_remove_field = unused variable: `{$name}` passes_unused_var_remove_field_suggestion = try removing the field <ins> passes_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable </ins> passes_unused_variable_try_ignore = unused variable: `{$name}` .suggestion = try ignoring the field

.label = unused variable .suggestion = if this is intentional, prefix it with an underscore passes_used_compiler_linker = `used(compiler)` and `used(linker)` can't be used together

#[subdiagnostic] pub string_interp: Vec<UnusedVariableStringInterp>, #[subdiagnostic] <del> pub sugg: UnusedVariableTryPrefixSugg, </del> <ins> pub sugg: UnusedVariableSugg, pub name: String, </ins> } #[derive(Subdiagnostic)] <del> #[multipart_suggestion(passes_suggestion, applicability = "machine-applicable")] pub struct UnusedVariableTryPrefixSugg { #[suggestion_part(code = "_{name}")] pub spans: Vec<Span>, pub name: String, </del> <ins> pub enum UnusedVariableSugg { #[multipart_suggestion(passes_suggestion, applicability = "machine-applicable")] TryPrefixSugg { #[suggestion_part(code = "_{name}")] spans: Vec<Span>, name: String, }, #[help(passes_unused_variable_args_in_macro)] NoSugg { #[primary_span] span: Span, name: String, }, </ins> } pub struct UnusedVariableStringInterp {

opt_body: Option<&hir::Body<'_>>, ) { let first_hir_id = hir_ids_and_spans[0].0; if let Some(name) = self.should_warn(var).filter(|name| name != "self") { // annoying: for parameters in funcs like `fn(x: i32) // {ret}`, there is only one node, so asking about

}, ); } else { <ins> // #117284, when `pat_span` and `ident_span` have different contexts // we can't provide a good suggestion, instead we pointed out the spans from macro let from_macro = non_shorthands .iter() .find(|(_, pat_span, ident_span)| { pat_span.ctxt() != ident_span.ctxt() && pat_span.from_expansion() }) .map(|(_, pat_span, _)| *pat_span); </ins> let non_shorthands = non_shorthands .into_iter() .map(|(_, _, ident_span)| ident_span) .collect::<Vec<_>>(); let suggestions = self.string_interp_suggestions(&name, opt_body); <ins> let sugg = if let Some(span) = from_macro { errors::UnusedVariableSugg::NoSugg { span, name: name.clone() } } else { errors::UnusedVariableSugg::TryPrefixSugg { spans: non_shorthands, name: name.clone(), } }; </ins> self.ir.tcx.emit_spanned_lint( lint::builtin::UNUSED_VARIABLES, first_hir_id,

.collect::<Vec<_>>(), errors::UnusedVariableTryPrefix { label: if !suggestions.is_empty() { Some(pat.span) } else { None }, <del> sugg: errors::UnusedVariableTryPrefixSugg { spans: non_shorthands, name, }, </del> <ins> name, sugg, </ins> string_interp: suggestions, }, );

<ins> #![deny(unused_variables)] macro_rules! make_var { ($struct:ident, $var:ident) => { let $var = $struct.$var; }; } #[allow(unused)] struct MyStruct { var: i32, } fn main() { let s = MyStruct { var: 42 }; make_var!(s, var); //~ ERROR unused variable: `var` let a = 1; //~ ERROR unused variable: `a` } </ins>

<ins> error: unused variable: `var` --> $DIR/issue-117284-arg-in-macro.rs:15:18 | LL |     make_var!(s, var); |                  ^^^ | help: `var` is captured in macro and introduced a unused variable --> $DIR/issue-117284-arg-in-macro.rs:4:13 | LL |         let $var = $struct.$var; |             ^^^^ ... LL |     make_var!(s, var); |     ----------------- in this macro invocation note: the lint level is defined here --> $DIR/issue-117284-arg-in-macro.rs:1:9 | LL | #![deny(unused_variables)] |         ^^^^^^^^^^^^^^^^ = note: this error originates in the macro `make_var` (in Nightly builds, run with -Z macro-backtrace for more info) error: unused variable: `a` --> $DIR/issue-117284-arg-in-macro.rs:16:9 | LL |     let a = 1; |         ^ help: if this is intentional, prefix it with an underscore: `_a` error: aborting due to 2 previous errors </ins>