@@ -138,13 +138,21 @@ pub(super) struct DigitInfo<'a> {
138138
139139 /// The type suffix, including preceding underscore if present.
140140 crate suffix : Option < & ' a str > ,
141- /// True for floating-point literals.
142- crate float : bool ,
143141}
144142
145143impl < ' a > DigitInfo < ' a > {
144+ fn from_lit ( src : & ' a str , lit : & Lit ) -> Option < DigitInfo < ' a > > {
145+ if lit. kind . is_numeric ( ) && src. chars ( ) . next ( ) . map_or ( false , |c| c. is_digit ( 10 ) ) {
146+ let ( unsuffixed, suffix) = split_suffix ( & src, & lit. kind ) ;
147+ let float = if let LitKind :: Float ( ..) = lit. kind { true } else { false } ;
148+ Some ( DigitInfo :: new ( unsuffixed, suffix, float) )
149+ } else {
150+ None
151+ }
152+ }
153+
146154 #[ must_use]
147- crate fn new ( lit : & ' a str , float : bool ) -> Self {
155+ crate fn new ( lit : & ' a str , suffix : Option < & ' a str > , float : bool ) -> Self {
148156 // Determine delimiter for radix prefix, if present, and radix.
149157 let radix = if lit. starts_with ( "0x" ) {
150158 Radix :: Hexadecimal
@@ -157,35 +165,19 @@ impl<'a> DigitInfo<'a> {
157165 } ;
158166
159167 // Grab part of the literal after prefix, if present.
160- let ( prefix, sans_prefix) = if let Radix :: Decimal = radix {
168+ let ( prefix, mut sans_prefix) = if let Radix :: Decimal = radix {
161169 ( None , lit)
162170 } else {
163171 let ( p, s) = lit. split_at ( 2 ) ;
164172 ( Some ( p) , s)
165173 } ;
166174
167- let mut digits = sans_prefix;
168- let mut suffix = None ;
169-
170- let len = sans_prefix. len ( ) ;
171- let mut last_d = '\0' ;
172- for ( d_idx, d) in sans_prefix. char_indices ( ) {
173- let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx } ;
174- if float
175- && ( d == 'f'
176- || is_possible_float_suffix_index ( & sans_prefix, suffix_start, len)
177- || ( ( d == 'E' || d == 'e' ) && !has_possible_float_suffix ( & sans_prefix) ) )
178- || !float && ( d == 'i' || d == 'u' || is_possible_suffix_index ( & sans_prefix, suffix_start, len) )
179- {
180- let ( d, s) = sans_prefix. split_at ( suffix_start) ;
181- digits = d;
182- suffix = Some ( s) ;
183- break ;
184- }
185- last_d = d
175+ if suffix. is_some ( ) && sans_prefix. ends_with ( '_' ) {
176+ // The '_' before the suffix isn't part of the digits
177+ sans_prefix = & sans_prefix[ ..sans_prefix. len ( ) - 1 ] ;
186178 }
187179
188- let ( integer, fraction, exponent) = Self :: split_digit_parts ( digits , float) ;
180+ let ( integer, fraction, exponent) = Self :: split_digit_parts ( sans_prefix , float) ;
189181
190182 Self {
191183 radix,
@@ -194,7 +186,6 @@ impl<'a> DigitInfo<'a> {
194186 fraction,
195187 exponent,
196188 suffix,
197- float,
198189 }
199190 }
200191
@@ -256,15 +247,8 @@ impl<'a> DigitInfo<'a> {
256247 }
257248
258249 if let Some ( suffix) = self . suffix {
259- if self . float && is_mistyped_float_suffix ( suffix) {
260- output. push_str ( "_f" ) ;
261- output. push_str ( & suffix[ 1 ..] ) ;
262- } else if is_mistyped_suffix ( suffix) {
263- output. push_str ( "_i" ) ;
264- output. push_str ( & suffix[ 1 ..] ) ;
265- } else {
266- output. push_str ( suffix) ;
267- }
250+ output. push ( '_' ) ;
251+ output. push_str ( suffix) ;
268252 }
269253
270254 output
@@ -303,6 +287,34 @@ impl<'a> DigitInfo<'a> {
303287 }
304288}
305289
290+ fn split_suffix < ' a > ( src : & ' a str , lit_kind : & LitKind ) -> ( & ' a str , Option < & ' a str > ) {
291+ debug_assert ! ( lit_kind. is_numeric( ) ) ;
292+ if let Some ( suffix_length) = lit_suffix_length ( lit_kind) {
293+ let ( unsuffixed, suffix) = src. split_at ( src. len ( ) - suffix_length) ;
294+ ( unsuffixed, Some ( suffix) )
295+ } else {
296+ ( src, None )
297+ }
298+ }
299+
300+ fn lit_suffix_length ( lit_kind : & LitKind ) -> Option < usize > {
301+ debug_assert ! ( lit_kind. is_numeric( ) ) ;
302+ let suffix = match lit_kind {
303+ LitKind :: Int ( _, int_lit_kind) => match int_lit_kind {
304+ LitIntType :: Signed ( int_ty) => Some ( int_ty. name_str ( ) ) ,
305+ LitIntType :: Unsigned ( uint_ty) => Some ( uint_ty. name_str ( ) ) ,
306+ LitIntType :: Unsuffixed => None ,
307+ } ,
308+ LitKind :: Float ( _, float_lit_kind) => match float_lit_kind {
309+ LitFloatType :: Suffixed ( float_ty) => Some ( float_ty. name_str ( ) ) ,
310+ LitFloatType :: Unsuffixed => None ,
311+ } ,
312+ _ => None ,
313+ } ;
314+
315+ suffix. map ( str:: len)
316+ }
317+
306318enum WarningType {
307319 UnreadableLiteral ,
308320 InconsistentDigitGrouping ,
@@ -388,22 +400,13 @@ impl LiteralDigitGrouping {
388400
389401 if_chain ! {
390402 if let Some ( src) = snippet_opt( cx, lit. span) ;
391- if let Some ( firstch) = src. chars( ) . next( ) ;
392- if char :: is_digit( firstch, 10 ) ;
403+ if let Some ( mut digit_info) = DigitInfo :: from_lit( & src, & lit) ;
393404 then {
394-
395- let digit_info = match lit. kind {
396- LitKind :: Int ( ..) => DigitInfo :: new( & src, false ) ,
397- LitKind :: Float ( ..) => DigitInfo :: new( & src, true ) ,
398- _ => return ,
399- } ;
405+ if !Self :: check_for_mistyped_suffix( cx, lit. span, & mut digit_info) {
406+ return ;
407+ }
400408
401409 let result = ( || {
402- if let Some ( suffix) = digit_info. suffix {
403- if is_mistyped_suffix( suffix) {
404- return Err ( WarningType :: MistypedLiteralSuffix ) ;
405- }
406- }
407410
408411 let integral_group_size = Self :: get_group_size( digit_info. integer. split( '_' ) , in_macro) ?;
409412 if let Some ( fraction) = digit_info. fraction {
@@ -428,6 +431,39 @@ impl LiteralDigitGrouping {
428431 }
429432 }
430433
434+ // Returns `false` if the check fails
435+ fn check_for_mistyped_suffix (
436+ cx : & EarlyContext < ' _ > ,
437+ span : syntax_pos:: Span ,
438+ digit_info : & mut DigitInfo < ' _ > ,
439+ ) -> bool {
440+ if digit_info. suffix . is_some ( ) {
441+ return true ;
442+ }
443+
444+ let ( part, mistyped_suffixes, missing_char) = if let Some ( ( _, exponent) ) = & mut digit_info. exponent {
445+ ( exponent, & [ "32" , "64" ] [ ..] , 'f' )
446+ } else if let Some ( fraction) = & mut digit_info. fraction {
447+ ( fraction, & [ "32" , "64" ] [ ..] , 'f' )
448+ } else {
449+ ( & mut digit_info. integer , & [ "8" , "16" , "32" , "64" ] [ ..] , 'i' )
450+ } ;
451+
452+ let mut split = part. rsplit ( '_' ) ;
453+ let last_group = split. next ( ) . expect ( "At least one group" ) ;
454+ if split. next ( ) . is_some ( ) && mistyped_suffixes. contains ( & last_group) {
455+ * part = & part[ ..part. len ( ) - last_group. len ( ) ] ;
456+ let mut hint = digit_info. grouping_hint ( ) ;
457+ hint. push ( '_' ) ;
458+ hint. push ( missing_char) ;
459+ hint. push_str ( last_group) ;
460+ WarningType :: MistypedLiteralSuffix . display ( & hint, cx, span) ;
461+ false
462+ } else {
463+ true
464+ }
465+ }
466+
431467 /// Given the sizes of the digit groups of both integral and fractional
432468 /// parts, and the length
433469 /// of both parts, determine if the digits have been grouped consistently.
@@ -503,14 +539,12 @@ impl DecimalLiteralRepresentation {
503539 if_chain ! {
504540 if let LitKind :: Int ( val, _) = lit. kind;
505541 if let Some ( src) = snippet_opt( cx, lit. span) ;
506- if let Some ( firstch) = src. chars( ) . next( ) ;
507- if char :: is_digit( firstch, 10 ) ;
508- let digit_info = DigitInfo :: new( & src, false ) ;
542+ if let Some ( digit_info) = DigitInfo :: from_lit( & src, & lit) ;
509543 if digit_info. radix == Radix :: Decimal ;
510544 if val >= u128 :: from( self . threshold) ;
511545 then {
512546 let hex = format!( "{:#X}" , val) ;
513- let digit_info = DigitInfo :: new( & hex, false ) ;
547+ let digit_info = DigitInfo :: new( & hex, None , false ) ;
514548 let _ = Self :: do_lint( digit_info. integer) . map_err( |warning_type| {
515549 warning_type. display( & digit_info. grouping_hint( ) , cx, lit. span)
516550 } ) ;
@@ -563,28 +597,3 @@ impl DecimalLiteralRepresentation {
563597 Ok ( ( ) )
564598 }
565599}
566-
567- #[ must_use]
568- fn is_mistyped_suffix ( suffix : & str ) -> bool {
569- [ "_8" , "_16" , "_32" , "_64" ] . contains ( & suffix)
570- }
571-
572- #[ must_use]
573- fn is_possible_suffix_index ( lit : & str , idx : usize , len : usize ) -> bool {
574- ( ( len > 3 && idx == len - 3 ) || ( len > 2 && idx == len - 2 ) ) && is_mistyped_suffix ( lit. split_at ( idx) . 1 )
575- }
576-
577- #[ must_use]
578- fn is_mistyped_float_suffix ( suffix : & str ) -> bool {
579- [ "_32" , "_64" ] . contains ( & suffix)
580- }
581-
582- #[ must_use]
583- fn is_possible_float_suffix_index ( lit : & str , idx : usize , len : usize ) -> bool {
584- ( len > 3 && idx == len - 3 ) && is_mistyped_float_suffix ( lit. split_at ( idx) . 1 )
585- }
586-
587- #[ must_use]
588- fn has_possible_float_suffix ( lit : & str ) -> bool {
589- lit. ends_with ( "_32" ) || lit. ends_with ( "_64" )
590- }
0 commit comments