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