2024-03-18 15:33:13 +00:00
use hir ::Name ;
2024-03-11 11:34:16 +00:00
use ide_db ::{
assists ::{ Assist , AssistId , AssistKind } ,
label ::Label ,
source_change ::SourceChange ,
2024-07-17 15:35:40 +00:00
FileRange , RootDatabase ,
2024-03-11 11:34:16 +00:00
} ;
2024-04-15 14:46:21 +00:00
use syntax ::TextRange ;
2024-03-11 11:34:16 +00:00
use text_edit ::TextEdit ;
2023-09-24 17:59:15 +00:00
use crate ::{ Diagnostic , DiagnosticCode , DiagnosticsContext } ;
// Diagnostic: unused-variables
//
// This diagnostic is triggered when a local variable is not used.
pub ( crate ) fn unused_variables (
ctx : & DiagnosticsContext < '_ > ,
d : & hir ::UnusedVariable ,
2024-03-17 11:03:24 +00:00
) -> Option < Diagnostic > {
2023-09-24 17:59:15 +00:00
let ast = d . local . primary_source ( ctx . sema . db ) . syntax_ptr ( ) ;
2024-03-17 11:03:24 +00:00
if ast . file_id . macro_file ( ) . is_some ( ) {
// FIXME: Our infra can't handle allow from within macro expansions rn
return None ;
}
2024-03-11 11:34:16 +00:00
let diagnostic_range = ctx . sema . diagnostics_display_range ( ast ) ;
2024-04-15 14:46:21 +00:00
// The range for the Actual Name. We don't want to replace the entire declarition. Using the diagnostic range causes issues within in Array Destructuring.
2024-04-15 18:11:45 +00:00
let name_range = d
. local
. primary_source ( ctx . sema . db )
. name ( )
. map ( | v | v . syntax ( ) . original_file_range_rooted ( ctx . sema . db ) )
2024-04-17 10:05:35 +00:00
. filter ( | it | {
Some ( it . file_id ) = = ast . file_id . file_id ( )
& & diagnostic_range . range . contains_range ( it . range )
} ) ;
2024-03-18 15:33:13 +00:00
let var_name = d . local . name ( ctx . sema . db ) ;
2024-03-17 11:03:24 +00:00
Some (
Diagnostic ::new_with_syntax_node_ptr (
ctx ,
DiagnosticCode ::RustcLint ( " unused_variables " ) ,
" unused variable " ,
ast ,
)
2024-04-17 10:05:35 +00:00
. with_fixes ( name_range . and_then ( | it | {
2024-07-17 15:35:40 +00:00
fixes ( ctx . sema . db , var_name , it . range , diagnostic_range . into ( ) , ast . file_id . is_macro ( ) )
2024-04-17 10:05:35 +00:00
} ) )
2024-03-17 11:03:24 +00:00
. experimental ( ) ,
2023-09-24 17:59:15 +00:00
)
}
2024-03-18 15:33:13 +00:00
fn fixes (
db : & RootDatabase ,
var_name : Name ,
2024-04-15 14:46:21 +00:00
name_range : TextRange ,
2024-03-18 15:33:13 +00:00
diagnostic_range : FileRange ,
is_in_marco : bool ,
) -> Option < Vec < Assist > > {
2024-03-11 11:34:16 +00:00
if is_in_marco {
return None ;
}
2024-04-15 14:46:21 +00:00
2024-03-11 11:34:16 +00:00
Some ( vec! [ Assist {
id : AssistId ( " unscore_unused_variable_name " , AssistKind ::QuickFix ) ,
2024-03-18 15:33:13 +00:00
label : Label ::new ( format! (
" Rename unused {} to _{} " ,
var_name . display ( db ) ,
var_name . display ( db )
) ) ,
2024-03-11 11:34:16 +00:00
group : None ,
target : diagnostic_range . range ,
source_change : Some ( SourceChange ::from_text_edit (
diagnostic_range . file_id ,
2024-04-15 14:46:21 +00:00
TextEdit ::replace ( name_range , format! ( " _ {} " , var_name . display ( db ) ) ) ,
2024-03-11 11:34:16 +00:00
) ) ,
2024-07-11 08:55:34 +00:00
command : None ,
2024-03-11 11:34:16 +00:00
} ] )
}
2023-09-24 17:59:15 +00:00
#[ cfg(test) ]
mod tests {
2024-03-17 11:03:24 +00:00
use crate ::tests ::{ check_diagnostics , check_fix } ;
2023-09-24 17:59:15 +00:00
#[ test ]
fn unused_variables_simple ( ) {
check_diagnostics (
r #"
//- minicore: fn
struct Foo { f1 : i32 , f2 : i64 }
fn f ( kkk : i32 ) { }
2024-03-11 11:34:58 +00:00
//^^^ 💡 warn: unused variable
2023-09-24 17:59:15 +00:00
fn main ( ) {
let a = 2 ;
2024-03-11 11:34:58 +00:00
//^ 💡 warn: unused variable
2023-09-24 17:59:15 +00:00
let b = 5 ;
// note: `unused variable` implies `unused mut`, so we should not emit both at the same time.
let mut c = f ( b ) ;
2024-03-11 11:34:58 +00:00
//^^^^^ 💡 warn: unused variable
2023-09-24 17:59:15 +00:00
let ( d , e ) = ( 3 , 5 ) ;
2024-03-11 11:34:58 +00:00
//^ 💡 warn: unused variable
2023-09-24 17:59:15 +00:00
let _ = e ;
let f1 = 2 ;
let f2 = 5 ;
let f = Foo { f1 , f2 } ;
match f {
Foo { f1 , f2 } = > {
2024-03-11 11:34:58 +00:00
//^^ 💡 warn: unused variable
2023-09-24 17:59:15 +00:00
_ = f2 ;
}
}
let g = false ;
if g { }
let h : fn ( ) -> i32 = | | 2 ;
let i = h ( ) ;
2024-03-11 11:34:58 +00:00
//^ 💡 warn: unused variable
2023-09-24 17:59:15 +00:00
}
" #,
) ;
}
#[ test ]
fn unused_self ( ) {
check_diagnostics (
r #"
struct S {
}
impl S {
fn owned_self ( self , u : i32 ) { }
2024-03-11 11:34:58 +00:00
//^ 💡 warn: unused variable
2023-09-24 17:59:15 +00:00
fn ref_self ( & self , u : i32 ) { }
2024-03-11 11:34:58 +00:00
//^ 💡 warn: unused variable
2023-09-24 17:59:15 +00:00
fn ref_mut_self ( & mut self , u : i32 ) { }
2024-03-11 11:34:58 +00:00
//^ 💡 warn: unused variable
2023-09-24 17:59:15 +00:00
fn owned_mut_self ( mut self ) { }
//^^^^^^^^ 💡 warn: variable does not need to be mutable
}
" #,
) ;
}
#[ test ]
fn allow_unused_variables_for_identifiers_starting_with_underline ( ) {
check_diagnostics (
r #"
fn main ( ) {
let _x = 2 ;
}
" #,
) ;
}
#[ test ]
fn respect_lint_attributes_for_unused_variables ( ) {
check_diagnostics (
r #"
fn main ( ) {
#[ allow(unused_variables) ]
let x = 2 ;
}
#[ deny(unused) ]
fn main2 ( ) {
let x = 2 ;
2024-03-11 11:34:58 +00:00
//^ 💡 error: unused variable
}
" #,
) ;
}
#[ test ]
fn fix_unused_variable ( ) {
check_fix (
r #"
fn main ( ) {
let x $ 0 = 2 ;
}
" #,
r #"
fn main ( ) {
let _x = 2 ;
}
" #,
) ;
check_fix (
r #"
fn main ( ) {
let ( $ 0 d , _e ) = ( 3 , 5 ) ;
}
" #,
r #"
fn main ( ) {
let ( _d , _e ) = ( 3 , 5 ) ;
}
" #,
) ;
check_fix (
r #"
struct Foo { f1 : i32 , f2 : i64 }
fn main ( ) {
let f = Foo { f1 : 0 , f2 : 0 } ;
match f {
Foo { f1 $ 0 , f2 } = > {
_ = f2 ;
}
}
}
" #,
r #"
struct Foo { f1 : i32 , f2 : i64 }
fn main ( ) {
let f = Foo { f1 : 0 , f2 : 0 } ;
match f {
Foo { _f1 , f2 } = > {
_ = f2 ;
}
}
}
" #,
) ;
}
#[ test ]
fn no_fix_for_marco ( ) {
2024-03-17 11:03:24 +00:00
check_diagnostics (
2024-03-11 11:34:58 +00:00
r #"
macro_rules ! my_macro {
( ) = > {
let x = 3 ;
} ;
}
fn main ( ) {
2024-03-17 11:03:24 +00:00
my_macro! ( ) ;
2023-09-24 17:59:15 +00:00
}
2024-04-15 14:46:21 +00:00
" #,
) ;
}
#[ test ]
fn unused_variable_in_array_destructure ( ) {
check_fix (
r #"
fn main ( ) {
let arr = [ 1 , 2 , 3 , 4 , 5 ] ;
let [ _x , y $ 0 @ .. ] = arr ;
}
" #,
r #"
fn main ( ) {
let arr = [ 1 , 2 , 3 , 4 , 5 ] ;
let [ _x , _y @ .. ] = arr ;
}
2023-09-24 17:59:15 +00:00
" #,
) ;
}
}