1
- use clippy_utils:: {
2
- diagnostics:: span_lint_and_then,
3
- get_parent_expr, is_from_proc_macro, last_path_segment,
4
- msrvs:: { self , Msrv } ,
5
- } ;
6
- use itertools:: Itertools ;
7
- use rustc_data_structures:: fx:: FxHashMap ;
1
+ use clippy_utils:: diagnostics:: { span_lint_and_then, span_lint_hir_and_then} ;
2
+ use clippy_utils:: msrvs:: { Msrv , NUMERIC_ASSOCIATED_CONSTANTS } ;
3
+ use clippy_utils:: source:: snippet_opt;
4
+ use clippy_utils:: { get_parent_expr, is_from_proc_macro, last_path_segment, std_or_core} ;
8
5
use rustc_errors:: Applicability ;
9
- use rustc_hir:: {
10
- def:: Res ,
11
- def_id:: DefId ,
12
- intravisit:: { walk_expr, Visitor } ,
13
- Item , UseKind ,
14
- } ;
15
- use rustc_hir:: { Expr , ExprKind , ItemKind , PrimTy , QPath , TyKind } ;
6
+ use rustc_hir:: def:: { DefKind , Res } ;
7
+ use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
8
+ use rustc_hir:: { Expr , ExprKind , Item , ItemKind , PrimTy , QPath , TyKind , UseKind } ;
16
9
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
17
- use rustc_middle:: { hir:: nested_filter:: OnlyBodies , lint:: in_external_macro} ;
10
+ use rustc_middle:: hir:: nested_filter:: OnlyBodies ;
11
+ use rustc_middle:: lint:: in_external_macro;
18
12
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
19
- use rustc_span:: { sym, Span , Symbol } ;
13
+ use rustc_span:: symbol:: kw;
14
+ use rustc_span:: { sym, Symbol } ;
20
15
21
16
declare_clippy_lint ! {
22
17
/// ### What it does
@@ -25,7 +20,7 @@ declare_clippy_lint! {
25
20
///
26
21
/// ### Why is this bad?
27
22
/// All of these have been superceded by the associated constants on their respective types,
28
- /// such as `i128::MAX`. These legacy constants may be deprecated in a future version of rust.
23
+ /// such as `i128::MAX`. These legacy items may be deprecated in a future version of rust.
29
24
///
30
25
/// ### Example
31
26
/// ```rust
@@ -42,64 +37,64 @@ declare_clippy_lint! {
42
37
}
43
38
pub struct LegacyNumericConstants {
44
39
msrv : Msrv ,
45
- use_stmts : FxHashMap < Symbol , Vec < Span > > ,
46
- glob_use_stmts : Vec < Span > ,
47
40
}
48
41
49
42
impl LegacyNumericConstants {
50
43
#[ must_use]
51
44
pub fn new ( msrv : Msrv ) -> Self {
52
- Self {
53
- msrv,
54
- use_stmts : FxHashMap :: default ( ) ,
55
- glob_use_stmts : vec ! [ ] ,
56
- }
45
+ Self { msrv }
57
46
}
58
47
}
59
48
60
49
impl_lint_pass ! ( LegacyNumericConstants => [ LEGACY_NUMERIC_CONSTANTS ] ) ;
61
50
62
51
impl < ' tcx > LateLintPass < ' tcx > for LegacyNumericConstants {
63
52
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
64
- let Self {
65
- msrv : _,
66
- use_stmts,
67
- glob_use_stmts,
68
- } = self ;
69
-
70
- if !item. span . is_dummy ( ) && let ItemKind :: Use ( path, kind) = item. kind {
71
- match kind {
72
- UseKind :: Single => {
73
- for res in & path. res {
74
- if let Some ( def_id) = res. opt_def_id ( )
75
- && let Some ( module_name) = is_path_in_integral_module ( cx, def_id)
76
- && let _ = use_stmts. insert ( module_name, vec ! [ ] )
77
- && let Some ( use_stmts) = use_stmts. get_mut ( & module_name)
78
- {
79
- use_stmts. push ( item. span ) ;
80
- }
53
+ let Self { msrv } = self ;
54
+
55
+ if msrv. meets ( NUMERIC_ASSOCIATED_CONSTANTS )
56
+ && !in_external_macro ( cx. sess ( ) , item. span )
57
+ && let ItemKind :: Use ( path, kind @ ( UseKind :: Single | UseKind :: Glob ) ) = item. kind
58
+ // These modules are "TBD" deprecated, and the contents are too, so lint on the `use`
59
+ // statement directly
60
+ && let def_path = cx. get_def_path ( path. res [ 0 ] . def_id ( ) )
61
+ && is_path_in_numeric_module ( & def_path, true )
62
+ {
63
+ let plurality = matches ! (
64
+ kind,
65
+ UseKind :: Glob | UseKind :: Single if matches!( path. res[ 0 ] , Res :: Def ( DefKind :: Mod , _) ) ,
66
+ ) ;
67
+
68
+ span_lint_and_then (
69
+ cx,
70
+ LEGACY_NUMERIC_CONSTANTS ,
71
+ path. span ,
72
+ if plurality {
73
+ "importing legacy numeric constants"
74
+ } else {
75
+ "importing a legacy numeric constant"
76
+ } ,
77
+ |diag| {
78
+ if item. ident . name != kw:: Underscore {
79
+ let msg = if plurality && let [ .., module_name] = & * def_path {
80
+ format ! ( "use the associated constants on `{module_name}` instead at their usage" )
81
+ } else if let [ .., module_name, name] = & * def_path {
82
+ format ! ( "use the associated constant `{module_name}::{name}` instead at its usage" )
83
+ } else {
84
+ return ;
85
+ } ;
86
+
87
+ diag. help ( msg) ;
81
88
}
82
89
} ,
83
- UseKind :: Glob => glob_use_stmts. push ( item. span ) ,
84
- UseKind :: ListStem => { } ,
85
- }
90
+ ) ;
86
91
}
87
92
}
88
93
89
94
fn check_crate_post ( & mut self , cx : & LateContext < ' tcx > ) {
90
- let Self {
91
- msrv,
92
- use_stmts,
93
- glob_use_stmts,
94
- } = self ;
95
-
96
- let mut v = V {
97
- cx,
98
- msrv,
99
- use_stmts,
100
- glob_use_stmts,
101
- } ;
102
- cx. tcx . hir ( ) . visit_all_item_likes_in_crate ( & mut v) ;
95
+ let Self { msrv } = self ;
96
+
97
+ cx. tcx . hir ( ) . visit_all_item_likes_in_crate ( & mut V { cx, msrv } ) ;
103
98
}
104
99
105
100
extract_msrv_attr ! ( LateContext ) ;
@@ -108,8 +103,6 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
108
103
struct V < ' a , ' tcx > {
109
104
cx : & ' a LateContext < ' tcx > ,
110
105
msrv : & ' a Msrv ,
111
- use_stmts : & ' a FxHashMap < Symbol , Vec < Span > > ,
112
- glob_use_stmts : & ' a Vec < Span > ,
113
106
}
114
107
115
108
impl < ' tcx > Visitor < ' tcx > for V < ' _ , ' tcx > {
@@ -121,35 +114,36 @@ impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
121
114
122
115
fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
123
116
walk_expr ( self , expr) ;
124
- let Self {
125
- cx,
126
- msrv,
127
- use_stmts,
128
- glob_use_stmts,
129
- } = * self ;
130
-
131
- if !msrv. meets ( msrvs:: STD_INTEGRAL_CONSTANTS ) || in_external_macro ( cx. sess ( ) , expr. span ) {
117
+ let Self { cx, msrv } = * self ;
118
+
119
+ if !msrv. meets ( NUMERIC_ASSOCIATED_CONSTANTS ) || in_external_macro ( cx. sess ( ) , expr. span ) {
132
120
return ;
133
121
}
134
122
let ExprKind :: Path ( qpath) = expr. kind else {
135
123
return ;
136
124
} ;
137
125
138
126
// `std::<integer>::<CONST>` check
139
- let ( span, sugg, is_method , use_stmts ) = if let QPath :: Resolved ( _ , path) = qpath
127
+ let ( span, sugg, msg ) = if let QPath :: Resolved ( None , path) = qpath
140
128
&& let Some ( def_id) = path. res . opt_def_id ( )
141
- && let Some ( name) = path. segments . iter ( ) . last ( ) . map ( |segment| segment. ident . name )
142
- && let Some ( module_name) = is_path_in_integral_module ( cx, def_id)
129
+ && let path = cx. get_def_path ( def_id)
130
+ && is_path_in_numeric_module ( & path, false )
131
+ && let [ .., module_name, name] = & * path
132
+ && let Some ( snippet) = snippet_opt ( cx, expr. span )
133
+ && let is_float_module = ( * module_name == sym:: f32 || * module_name == sym:: f64)
134
+ // Skip linting if this usage looks identical to the associated constant, since this
135
+ // would only require removing a `use` import. We don't ignore ones from `f32` or `f64`, however.
136
+ && let identical = snippet == format ! ( "{module_name}::{name}" )
137
+ && ( !identical || is_float_module)
143
138
{
144
139
(
145
140
expr. span ,
146
- format ! ( "{module_name}::{name}" ) ,
147
- false ,
148
- if path. segments . get ( 0 ) . is_some_and ( |segment| segment. ident . name == module_name) {
149
- use_stmts. get ( & module_name)
150
- } else {
141
+ if identical {
151
142
None
152
- }
143
+ } else {
144
+ Some ( format ! ( "{module_name}::{name}" ) )
145
+ } ,
146
+ "usage of a legacy numeric constant" ,
153
147
)
154
148
// `<integer>::xxx_value` check
155
149
} else if let QPath :: TypeRelative ( ty, _) = qpath
@@ -164,43 +158,40 @@ impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
164
158
{
165
159
(
166
160
qpath. last_segment_span ( ) . with_hi ( par_expr. span . hi ( ) ) ,
167
- name[ ..=2 ] . to_ascii_uppercase ( ) ,
168
- true ,
169
- None ,
161
+ Some ( name[ ..=2 ] . to_ascii_uppercase ( ) ) ,
162
+ "usage of a legacy numeric method" ,
170
163
)
171
164
} else {
172
165
return ;
173
166
} ;
174
167
175
168
if !is_from_proc_macro ( cx, expr) {
176
- let msg = if is_method {
177
- "usage of a legacy numeric method"
178
- } else {
179
- "usage of a legacy numeric constant"
180
- } ;
181
-
182
- span_lint_and_then ( cx, LEGACY_NUMERIC_CONSTANTS , span, msg, |diag| {
183
- let app = if use_stmts. is_none ( ) {
184
- Applicability :: MachineApplicable
185
- } else {
186
- Applicability :: MaybeIncorrect
187
- } ;
188
- diag. span_suggestion ( span, "use the associated constant instead" , sugg, app) ;
189
- if let Some ( use_stmts) = use_stmts {
190
- diag. span_note (
191
- use_stmts. iter ( ) . chain ( glob_use_stmts) . copied ( ) . collect_vec ( ) ,
192
- "you may need to remove one of the following `use` statements" ,
169
+ span_lint_hir_and_then ( cx, LEGACY_NUMERIC_CONSTANTS , expr. hir_id , span, msg, |diag| {
170
+ if let Some ( sugg) = sugg {
171
+ diag. span_suggestion (
172
+ span,
173
+ "use the associated constant instead" ,
174
+ sugg,
175
+ Applicability :: MaybeIncorrect ,
193
176
) ;
177
+ } else if let Some ( std_or_core) = std_or_core ( cx)
178
+ && let QPath :: Resolved ( None , path) = qpath
179
+ {
180
+ diag. help ( format ! (
181
+ "remove the import that brings `{std_or_core}::{}` into scope" ,
182
+ // Must be `<module>::<CONST>` if `needs_import_removed` is true yet is
183
+ // being linted
184
+ path. segments[ 0 ] . ident. name,
185
+ ) ) ;
194
186
}
195
187
} ) ;
196
188
}
197
189
}
198
190
}
199
191
200
- fn is_path_in_integral_module ( cx : & LateContext < ' _ > , def_id : DefId ) -> Option < Symbol > {
201
- let path = cx. get_def_path ( def_id) ;
192
+ fn is_path_in_numeric_module ( path : & [ Symbol ] , ignore_float_modules : bool ) -> bool {
202
193
if let [
203
- sym:: core | sym :: std ,
194
+ sym:: core,
204
195
module @ ( sym:: u8
205
196
| sym:: i8
206
197
| sym:: u16
@@ -216,12 +207,17 @@ fn is_path_in_integral_module(cx: &LateContext<'_>, def_id: DefId) -> Option<Sym
216
207
| sym:: f32
217
208
| sym:: f64) ,
218
209
..,
219
- ] = & * cx. get_def_path ( def_id)
220
- // So `use` statements like `std::f32` also work
221
- && path. len ( ) <= 3
210
+ ] = path
211
+ && !path. get ( 2 ) . is_some_and ( |& s| s == sym ! ( consts) )
222
212
{
223
- return Some ( * module) ;
213
+ // If `ignore_float_modules` is `true`, return `None` for `_::f32` or `_::f64`, but not
214
+ // _::f64::MAX` or similar.
215
+ if ignore_float_modules && ( * module == sym:: f32 || * module == sym:: f64) && path. get ( 2 ) . is_none ( ) {
216
+ return false ;
217
+ }
218
+
219
+ return true ;
224
220
}
225
221
226
- None
222
+ false
227
223
}
0 commit comments