@@ -26,6 +26,25 @@ declare_clippy_lint! {
26
26
"long integer literal without underscores"
27
27
}
28
28
29
+ /// **What it does:** Warns for mistyped suffix in literals
30
+ ///
31
+ /// **Why is this bad?** This is most probably a typo
32
+ ///
33
+ /// **Known problems:**
34
+ /// - Recommends unsigned suffix even if it's a negative number. Should only recommend the signed suffix on those cases.
35
+ /// - Assumes `_128` should have been `_i128`, even though `_128` is a valid grouping for decimal and octal numbers
36
+ ///
37
+ /// **Example:**
38
+ ///
39
+ /// ```rust
40
+ /// 2_32
41
+ /// ```
42
+ declare_clippy_lint ! {
43
+ pub MISTYPED_LITERAL_SUFFIXES ,
44
+ correctness,
45
+ "mistyped literal suffix"
46
+ }
47
+
29
48
/// **What it does:** Warns if an integral or floating-point constant is
30
49
/// grouped inconsistently with underscores.
31
50
///
@@ -136,17 +155,22 @@ impl<'a> DigitInfo<'a> {
136
155
} ;
137
156
138
157
let mut last_d = '\0' ;
158
+ let len = sans_prefix. len ( ) ;
139
159
for ( d_idx, d) in sans_prefix. char_indices ( ) {
140
- if !float && ( d == 'i' || d == 'u' ) || float && ( d == 'f' || d == 'e' || d == 'E' ) {
160
+ if !float && ( d == 'i' || d == 'u' ) ||
161
+ float && ( d == 'f' || d == 'e' || d == 'E' ) ||
162
+ ( !float && is_possible_suffix_index ( & d, d_idx, len) ) {
141
163
let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx } ;
142
164
let ( digits, suffix) = sans_prefix. split_at ( suffix_start) ;
143
- return Self {
144
- digits,
145
- radix,
146
- prefix,
147
- suffix : Some ( suffix) ,
148
- float,
149
- } ;
165
+ if d != '_' || is_mistyped_suffix ( suffix) {
166
+ return Self {
167
+ digits,
168
+ radix,
169
+ prefix,
170
+ suffix : Some ( suffix) ,
171
+ float,
172
+ } ;
173
+ }
150
174
}
151
175
last_d = d
152
176
}
@@ -161,7 +185,7 @@ impl<'a> DigitInfo<'a> {
161
185
}
162
186
}
163
187
164
- /// Returns digits grouped in a sensible way.
188
+ /// Returns literal formatted in a sensible way.
165
189
crate fn grouping_hint ( & self ) -> String {
166
190
let group_size = self . radix . suggest_grouping ( ) ;
167
191
if self . digits . contains ( '.' ) {
@@ -211,11 +235,18 @@ impl<'a> DigitInfo<'a> {
211
235
if self . radix == Radix :: Hexadecimal && nb_digits_to_fill != 0 {
212
236
hint = format ! ( "{:0>4}{}" , & hint[ ..nb_digits_to_fill] , & hint[ nb_digits_to_fill..] ) ;
213
237
}
238
+ let suffix_hint = match self . suffix {
239
+ Some ( suffix) if is_mistyped_suffix ( suffix) => {
240
+ format ! ( "_i{}" , & suffix[ 1 ..] )
241
+ } ,
242
+ Some ( suffix) => suffix. to_string ( ) ,
243
+ None => String :: new ( )
244
+ } ;
214
245
format ! (
215
246
"{}{}{}" ,
216
247
self . prefix. unwrap_or( "" ) ,
217
248
hint,
218
- self . suffix . unwrap_or ( "" )
249
+ suffix_hint
219
250
)
220
251
}
221
252
}
@@ -226,11 +257,22 @@ enum WarningType {
226
257
InconsistentDigitGrouping ,
227
258
LargeDigitGroups ,
228
259
DecimalRepresentation ,
260
+ MistypedLiteralSuffix
229
261
}
230
262
231
263
impl WarningType {
232
264
crate fn display ( & self , grouping_hint : & str , cx : & EarlyContext < ' _ > , span : syntax_pos:: Span ) {
233
265
match self {
266
+ WarningType :: MistypedLiteralSuffix => {
267
+ span_lint_and_sugg (
268
+ cx,
269
+ MISTYPED_LITERAL_SUFFIXES ,
270
+ span,
271
+ "mistyped literal suffix" ,
272
+ "did you mean to write" ,
273
+ grouping_hint. to_string ( )
274
+ )
275
+ } ,
234
276
WarningType :: UnreadableLiteral => span_lint_and_sugg (
235
277
cx,
236
278
UNREADABLE_LITERAL ,
@@ -303,7 +345,7 @@ impl LiteralDigitGrouping {
303
345
if char :: to_digit( firstch, 10 ) . is_some( ) ;
304
346
then {
305
347
let digit_info = DigitInfo :: new( & src, false ) ;
306
- let _ = Self :: do_lint( digit_info. digits) . map_err( |warning_type| {
348
+ let _ = Self :: do_lint( digit_info. digits, digit_info . suffix ) . map_err( |warning_type| {
307
349
warning_type. display( & digit_info. grouping_hint( ) , cx, lit. span)
308
350
} ) ;
309
351
}
@@ -325,12 +367,12 @@ impl LiteralDigitGrouping {
325
367
326
368
// Lint integral and fractional parts separately, and then check consistency of digit
327
369
// groups if both pass.
328
- let _ = Self :: do_lint( parts[ 0 ] )
370
+ let _ = Self :: do_lint( parts[ 0 ] , None )
329
371
. map( |integral_group_size| {
330
372
if parts. len( ) > 1 {
331
373
// Lint the fractional part of literal just like integral part, but reversed.
332
374
let fractional_part = & parts[ 1 ] . chars( ) . rev( ) . collect:: <String >( ) ;
333
- let _ = Self :: do_lint( fractional_part)
375
+ let _ = Self :: do_lint( fractional_part, None )
334
376
. map( |fractional_group_size| {
335
377
let consistent = Self :: parts_consistent( integral_group_size,
336
378
fractional_group_size,
@@ -373,7 +415,12 @@ impl LiteralDigitGrouping {
373
415
374
416
/// Performs lint on `digits` (no decimal point) and returns the group
375
417
/// size on success or `WarningType` when emitting a warning.
376
- fn do_lint ( digits : & str ) -> Result < usize , WarningType > {
418
+ fn do_lint ( digits : & str , suffix : Option < & str > ) -> Result < usize , WarningType > {
419
+ if let Some ( suffix) = suffix {
420
+ if is_mistyped_suffix ( suffix) {
421
+ return Err ( WarningType :: MistypedLiteralSuffix ) ;
422
+ }
423
+ }
377
424
// Grab underscore indices with respect to the units digit.
378
425
let underscore_positions: Vec < usize > = digits
379
426
. chars ( )
@@ -504,3 +551,11 @@ impl LiteralRepresentation {
504
551
Ok ( ( ) )
505
552
}
506
553
}
554
+
555
+ fn is_mistyped_suffix ( suffix : & str ) -> bool {
556
+ [ "_8" , "_16" , "_32" , "_64" , "_128" ] . contains ( & suffix)
557
+ }
558
+
559
+ fn is_possible_suffix_index ( d : & char , d_idx : usize , len : usize ) -> bool {
560
+ * d == '_' && ( ( len > 4 && d_idx == len - 4 ) || ( len > 3 && d_idx == len - 3 ) || ( len > 2 && d_idx == len - 2 ) )
561
+ }
0 commit comments