11use  clippy_config:: Conf ; 
2- use  clippy_utils:: diagnostics:: span_lint_and_sugg; 
2+ use  clippy_utils:: diagnostics:: { span_lint_and_help ,   span_lint_and_sugg} ; 
33use  clippy_utils:: visitors:: for_each_expr; 
44use  clippy_utils:: { is_in_cfg_test,  is_test_function} ; 
55use  rustc_errors:: Applicability ; 
66use  rustc_hir:: intravisit:: FnKind ; 
77use  rustc_hir:: { self  as  hir,  Body ,  ExprKind ,  FnDecl } ; 
8+ use  rustc_lexer:: is_ident; 
89use  rustc_lint:: { LateContext ,  LateLintPass } ; 
910use  rustc_session:: impl_lint_pass; 
10- use  rustc_span:: Span ; 
1111use  rustc_span:: def_id:: LocalDefId ; 
12+ use  rustc_span:: { Span ,  Symbol ,  edition} ; 
1213use  std:: ops:: ControlFlow ; 
1314
1415declare_clippy_lint !  { 
@@ -37,20 +38,18 @@ declare_clippy_lint! {
3738     /// ``` 
3839     #[ clippy:: version = "1.84.0" ] 
3940    pub  REDUNDANT_TEST_PREFIX , 
40-     pedantic , 
41+     restriction , 
4142    "redundant `test_` prefix in test function name" 
4243} 
4344
4445pub  struct  RedundantTestPrefix  { 
4546    check_outside_test_cfg :  bool , 
46-     custom_sufix :  String , 
4747} 
4848
4949impl  RedundantTestPrefix  { 
5050    pub  fn  new ( conf :  & ' static  Conf )  -> Self  { 
5151        Self  { 
5252            check_outside_test_cfg :  conf. redundant_test_prefix_check_outside_cfg_test , 
53-             custom_sufix :  conf. redundant_test_prefix_custom_suffix . clone ( ) , 
5453        } 
5554    } 
5655} 
@@ -72,31 +71,64 @@ impl<'tcx> LateLintPass<'tcx> for RedundantTestPrefix {
7271            return ; 
7372        } ; 
7473
74+         // Skip the lint if the function is within a macro expansion. 
75+         if  ident. span . from_expansion ( )  { 
76+             return ; 
77+         } 
78+ 
79+         // Skip if the function name does not start with `test_`. 
80+         if  !ident. as_str ( ) . starts_with ( "test_" )  { 
81+             return ; 
82+         } 
83+ 
7584        // Skip the lint if the function is not within a node marked with `#[cfg(test)]` attribute. 
7685        // Continue if the function is inside the node marked with `#[cfg(test)]` or lint is enforced 
7786        // via configuration (most likely to include integration tests in lint's scope). 
7887        if  !( self . check_outside_test_cfg  || is_in_cfg_test ( cx. tcx ,  body. id ( ) . hir_id ) )  { 
7988            return ; 
8089        } 
8190
82-         if  is_test_function ( cx. tcx ,  cx. tcx . local_def_id_to_hir_id ( fn_def_id) )  && ident. as_str ( ) . starts_with ( "test_" )  { 
91+         if  is_test_function ( cx. tcx ,  cx. tcx . local_def_id_to_hir_id ( fn_def_id) )  { 
92+             let  msg = "redundant `test_` prefix in test function name" ; 
8393            let  mut  non_prefixed = ident. as_str ( ) . trim_start_matches ( "test_" ) . to_string ( ) ; 
84-             let  mut  help_msg = "consider removing the `test_` prefix" ; 
85-             // If `non_prefixed` conflicts with another function in the same module/scope, 
86-             // add extra suffix to avoid name conflict. 
87-             if  name_conflicts ( cx,  body,  & non_prefixed)  { 
88-                 non_prefixed. push_str ( & self . custom_sufix ) ; 
89-                 help_msg = "consider removing the `test_` prefix (suffix avoids name conflict)" ; 
94+ 
95+             if  is_invalid_ident ( & non_prefixed)  { 
96+                 // If the prefix-trimmed name is not a valid function name, do not provide an 
97+                 // automatic fix, just suggest renaming the function. 
98+                 span_lint_and_help ( 
99+                     cx, 
100+                     REDUNDANT_TEST_PREFIX , 
101+                     ident. span , 
102+                     msg, 
103+                     None , 
104+                     "consider function renaming (just removing `test_` prefix will produce invalid function name)" , 
105+                 ) ; 
106+             }  else  if  name_conflicts ( cx,  body,  & non_prefixed)  { 
107+                 // If `non_prefixed` conflicts with another function in the same module/scope, 
108+                 // do not provide an automatic fix, but still emit a fix suggestion. 
109+                 non_prefixed. push_str ( "_works" ) ; 
110+                 span_lint_and_sugg ( 
111+                     cx, 
112+                     REDUNDANT_TEST_PREFIX , 
113+                     ident. span , 
114+                     msg, 
115+                     "consider function renaming (just removing `test_` prefix will cause a name conflict)" , 
116+                     non_prefixed, 
117+                     Applicability :: HasPlaceholders , 
118+                 ) 
119+             }  else  { 
120+                 // If `non_prefixed` is a valid identifier and does not conflict with another function, 
121+                 // so we can suggest an auto-fix. 
122+                 span_lint_and_sugg ( 
123+                     cx, 
124+                     REDUNDANT_TEST_PREFIX , 
125+                     ident. span , 
126+                     msg, 
127+                     "consider removing the `test_` prefix" , 
128+                     non_prefixed, 
129+                     Applicability :: MachineApplicable , 
130+                 ) 
90131            } 
91-             span_lint_and_sugg ( 
92-                 cx, 
93-                 REDUNDANT_TEST_PREFIX , 
94-                 ident. span , 
95-                 "redundant `test_` prefix in test function name" , 
96-                 help_msg, 
97-                 non_prefixed, 
98-                 Applicability :: MachineApplicable , 
99-             ) ; 
100132        } 
101133    } 
102134} 
@@ -111,11 +143,11 @@ fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: &
111143    let  id = body. id ( ) . hir_id ; 
112144
113145    // Iterate over items in the same module/scope 
114-     let  ( module,  _module_span,  _module_hir)  = tcx. hir ( ) . get_module ( tcx. parent_module ( id) ) ; 
146+     let  ( module,  _module_span,  _module_hir)  = tcx. hir_get_module ( tcx. parent_module ( id) ) ; 
115147    for  item in  module. item_ids  { 
116-         let  item = tcx. hir ( ) . item ( * item) ; 
117-         if  let  hir:: ItemKind :: Fn ( .. )  = item. kind  { 
118-             if  item . ident . name . as_str ( )  == fn_name { 
148+         let  item = tcx. hir_item ( * item) ; 
149+         if  let  hir:: ItemKind :: Fn   {  ident ,  ..  }  = item. kind  { 
150+             if  ident. name . as_str ( )  == fn_name { 
119151                // Name conflict found 
120152                return  true ; 
121153            } 
@@ -141,3 +173,8 @@ fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: &
141173    } ) 
142174    . is_some ( ) 
143175} 
176+ 
177+ fn  is_invalid_ident ( ident :  & str )  -> bool  { 
178+     // The identifier is either a reserved keyword, or starts with an invalid sequence. 
179+     Symbol :: intern ( ident) . is_reserved ( || edition:: LATEST_STABLE_EDITION )  || !is_ident ( ident) 
180+ } 
0 commit comments