Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-diagnostics/src/handlers/unused_variables.rs')
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/unused_variables.rs | 82 |
1 files changed, 71 insertions, 11 deletions
diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs index e6bbff05f7..84e63acbc0 100644 --- a/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -6,7 +6,7 @@ use ide_db::{ label::Label, source_change::SourceChange, }; -use syntax::{Edition, TextRange}; +use syntax::{AstNode, Edition, TextRange, ToSmolStr}; use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; @@ -24,15 +24,21 @@ pub(crate) fn unused_variables( } let diagnostic_range = ctx.sema.diagnostics_display_range(ast); // 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. - let name_range = d - .local - .primary_source(ctx.sema.db) + let primary_source = d.local.primary_source(ctx.sema.db); + let name_range = primary_source .name() .map(|v| v.syntax().original_file_range_rooted(ctx.sema.db)) .filter(|it| { Some(it.file_id) == ast.file_id.file_id() && diagnostic_range.range.contains_range(it.range) }); + let is_shorthand_field = primary_source + .source + .value + .left() + .and_then(|name| name.syntax().parent()) + .and_then(syntax::ast::RecordPatField::cast) + .is_some_and(|field| field.colon_token().is_none()); let var_name = d.local.name(ctx.sema.db); Some( Diagnostic::new_with_syntax_node_ptr( @@ -48,6 +54,7 @@ pub(crate) fn unused_variables( it.range, diagnostic_range, ast.file_id.is_macro(), + is_shorthand_field, ctx.edition, ) })), @@ -60,24 +67,24 @@ fn fixes( name_range: TextRange, diagnostic_range: FileRange, is_in_marco: bool, + is_shorthand_field: bool, edition: Edition, ) -> Option<Vec<Assist>> { if is_in_marco { return None; } + let name = var_name.display(db, edition).to_smolstr(); + let name = name.strip_prefix("r#").unwrap_or(&name); + let new_name = if is_shorthand_field { format!("{name}: _{name}") } else { format!("_{name}") }; Some(vec![Assist { id: AssistId::quick_fix("unscore_unused_variable_name"), - label: Label::new(format!( - "Rename unused {} to _{}", - var_name.display(db, edition), - var_name.display(db, edition) - )), + label: Label::new(format!("Rename unused {name} to {new_name}")), group: None, target: diagnostic_range.range, source_change: Some(SourceChange::from_text_edit( diagnostic_range.file_id, - TextEdit::replace(name_range, format!("_{}", var_name.display(db, edition))), + TextEdit::replace(name_range, new_name), )), command: None, }]) @@ -220,13 +227,26 @@ struct Foo { f1: i32, f2: i64 } fn main() { let f = Foo { f1: 0, f2: 0 }; match f { - Foo { _f1, f2 } => { + Foo { f1: _f1, f2 } => { _ = f2; } } } "#, ); + + check_fix( + r#" +fn main() { + let $0r#type = 2; +} +"#, + r#" +fn main() { + let _type = 2; +} +"#, + ); } #[test] @@ -263,6 +283,46 @@ fn main() { ); } + #[test] + fn unused_variable_in_record_field() { + check_fix( + r#" +struct S { field : u32 } +fn main() { + let s = S { field : 2 }; + let S { field: $0x } = s +} +"#, + r#" +struct S { field : u32 } +fn main() { + let s = S { field : 2 }; + let S { field: _x } = s +} +"#, + ); + } + + #[test] + fn unused_variable_in_shorthand_record_field() { + check_fix( + r#" +struct S { field : u32 } +fn main() { + let s = S { field : 2 }; + let S { $0field } = s +} +"#, + r#" +struct S { field : u32 } +fn main() { + let s = S { field : 2 }; + let S { field: _field } = s +} +"#, + ); + } + // regression test as we used to panic in this scenario #[test] fn unknown_struct_pattern_param_type() { |