@@ -6,7 +6,7 @@ use ide_db::{
66 label:: Label ,
77 source_change:: SourceChange ,
88} ;
9- use syntax:: { Edition , TextRange } ;
9+ use syntax:: { AstNode , Edition , TextRange } ;
1010
1111use crate :: { Diagnostic , DiagnosticCode , DiagnosticsContext } ;
1212
@@ -24,15 +24,21 @@ pub(crate) fn unused_variables(
2424 }
2525 let diagnostic_range = ctx. sema . diagnostics_display_range ( ast) ;
2626 // The range for the Actual Name. We don't want to replace the entire declaration. Using the diagnostic range causes issues within in Array Destructuring.
27- let name_range = d
28- . local
29- . primary_source ( ctx. sema . db )
27+ let primary_source = d. local . primary_source ( ctx. sema . db ) ;
28+ let name_range = primary_source
3029 . name ( )
3130 . map ( |v| v. syntax ( ) . original_file_range_rooted ( ctx. sema . db ) )
3231 . filter ( |it| {
3332 Some ( it. file_id ) == ast. file_id . file_id ( )
3433 && diagnostic_range. range . contains_range ( it. range )
3534 } ) ;
35+ let is_shorthand_field = primary_source
36+ . source
37+ . value
38+ . left ( )
39+ . and_then ( |name| name. syntax ( ) . parent ( ) )
40+ . and_then ( syntax:: ast:: RecordPatField :: cast)
41+ . is_some_and ( |field| field. colon_token ( ) . is_none ( ) ) ;
3642 let var_name = d. local . name ( ctx. sema . db ) ;
3743 Some (
3844 Diagnostic :: new_with_syntax_node_ptr (
@@ -48,6 +54,7 @@ pub(crate) fn unused_variables(
4854 it. range ,
4955 diagnostic_range,
5056 ast. file_id . is_macro ( ) ,
57+ is_shorthand_field,
5158 ctx. edition ,
5259 )
5360 } ) ) ,
@@ -60,24 +67,23 @@ fn fixes(
6067 name_range : TextRange ,
6168 diagnostic_range : FileRange ,
6269 is_in_marco : bool ,
70+ is_shorthand_field : bool ,
6371 edition : Edition ,
6472) -> Option < Vec < Assist > > {
6573 if is_in_marco {
6674 return None ;
6775 }
76+ let name = var_name. display ( db, edition) ;
77+ let new_name = if is_shorthand_field { format ! ( "{name}: _{name}" ) } else { format ! ( "_{name}" ) } ;
6878
6979 Some ( vec ! [ Assist {
7080 id: AssistId :: quick_fix( "unscore_unused_variable_name" ) ,
71- label: Label :: new( format!(
72- "Rename unused {} to _{}" ,
73- var_name. display( db, edition) ,
74- var_name. display( db, edition)
75- ) ) ,
81+ label: Label :: new( format!( "Rename unused {name} to {new_name}" ) ) ,
7682 group: None ,
7783 target: diagnostic_range. range,
7884 source_change: Some ( SourceChange :: from_text_edit(
7985 diagnostic_range. file_id,
80- TextEdit :: replace( name_range, format! ( "_{}" , var_name . display ( db , edition ) ) ) ,
86+ TextEdit :: replace( name_range, new_name ) ,
8187 ) ) ,
8288 command: None ,
8389 } ] )
@@ -220,7 +226,7 @@ struct Foo { f1: i32, f2: i64 }
220226fn main() {
221227 let f = Foo { f1: 0, f2: 0 };
222228 match f {
223- Foo { _f1, f2 } => {
229+ Foo { f1: _f1, f2 } => {
224230 _ = f2;
225231 }
226232 }
@@ -263,6 +269,46 @@ fn main() {
263269 ) ;
264270 }
265271
272+ #[ test]
273+ fn unused_variable_in_record_field ( ) {
274+ check_fix (
275+ r#"
276+ struct S { field : u32 }
277+ fn main() {
278+ let s = S { field : 2 };
279+ let S { field: $0x } = s
280+ }
281+ "# ,
282+ r#"
283+ struct S { field : u32 }
284+ fn main() {
285+ let s = S { field : 2 };
286+ let S { field: _x } = s
287+ }
288+ "# ,
289+ ) ;
290+ }
291+
292+ #[ test]
293+ fn unused_variable_in_shorthand_record_field ( ) {
294+ check_fix (
295+ r#"
296+ struct S { field : u32 }
297+ fn main() {
298+ let s = S { field : 2 };
299+ let S { $0field } = s
300+ }
301+ "# ,
302+ r#"
303+ struct S { field : u32 }
304+ fn main() {
305+ let s = S { field : 2 };
306+ let S { field: _field } = s
307+ }
308+ "# ,
309+ ) ;
310+ }
311+
266312 // regression test as we used to panic in this scenario
267313 #[ test]
268314 fn unknown_struct_pattern_param_type ( ) {
0 commit comments