1
1
use 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} ;
3
3
use clippy_utils:: visitors:: for_each_expr;
4
4
use clippy_utils:: { is_in_cfg_test, is_test_function} ;
5
5
use rustc_errors:: Applicability ;
6
6
use rustc_hir:: intravisit:: FnKind ;
7
7
use rustc_hir:: { self as hir, Body , ExprKind , FnDecl } ;
8
+ use rustc_lexer:: is_ident;
8
9
use rustc_lint:: { LateContext , LateLintPass } ;
9
10
use rustc_session:: impl_lint_pass;
10
- use rustc_span:: Span ;
11
11
use rustc_span:: def_id:: LocalDefId ;
12
+ use rustc_span:: { Span , Symbol , edition} ;
12
13
use std:: ops:: ControlFlow ;
13
14
14
15
declare_clippy_lint ! {
@@ -37,20 +38,18 @@ declare_clippy_lint! {
37
38
/// ```
38
39
#[ clippy:: version = "1.84.0" ]
39
40
pub REDUNDANT_TEST_PREFIX ,
40
- pedantic ,
41
+ restriction ,
41
42
"redundant `test_` prefix in test function name"
42
43
}
43
44
44
45
pub struct RedundantTestPrefix {
45
46
check_outside_test_cfg : bool ,
46
- custom_sufix : String ,
47
47
}
48
48
49
49
impl RedundantTestPrefix {
50
50
pub fn new ( conf : & ' static Conf ) -> Self {
51
51
Self {
52
52
check_outside_test_cfg : conf. redundant_test_prefix_check_outside_cfg_test ,
53
- custom_sufix : conf. redundant_test_prefix_custom_suffix . clone ( ) ,
54
53
}
55
54
}
56
55
}
@@ -72,31 +71,64 @@ impl<'tcx> LateLintPass<'tcx> for RedundantTestPrefix {
72
71
return ;
73
72
} ;
74
73
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
+
75
84
// Skip the lint if the function is not within a node marked with `#[cfg(test)]` attribute.
76
85
// Continue if the function is inside the node marked with `#[cfg(test)]` or lint is enforced
77
86
// via configuration (most likely to include integration tests in lint's scope).
78
87
if !( self . check_outside_test_cfg || is_in_cfg_test ( cx. tcx , body. id ( ) . hir_id ) ) {
79
88
return ;
80
89
}
81
90
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" ;
83
93
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
+ )
90
131
}
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
- ) ;
100
132
}
101
133
}
102
134
}
@@ -111,11 +143,11 @@ fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: &
111
143
let id = body. id ( ) . hir_id ;
112
144
113
145
// 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) ) ;
115
147
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 {
119
151
// Name conflict found
120
152
return true ;
121
153
}
@@ -141,3 +173,8 @@ fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: &
141
173
} )
142
174
. is_some ( )
143
175
}
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