@@ -2,6 +2,8 @@ use std::borrow::Cow;
2
2
use std:: cmp:: min;
3
3
4
4
use itertools:: Itertools ;
5
+ use lazy_static:: lazy_static;
6
+ use regex:: Regex ;
5
7
use rustc_ast:: token:: { Delimiter , Lit , LitKind } ;
6
8
use rustc_ast:: { ast, ptr, token} ;
7
9
use rustc_span:: { BytePos , Span } ;
@@ -13,7 +15,9 @@ use crate::comment::{
13
15
rewrite_missing_comment, CharClasses , FindUncommented ,
14
16
} ;
15
17
use crate :: config:: lists:: * ;
16
- use crate :: config:: { Config , ControlBraceStyle , HexLiteralCase , IndentStyle , Version } ;
18
+ use crate :: config:: {
19
+ Config , ControlBraceStyle , FloatLiteralTrailingZero , HexLiteralCase , IndentStyle , Version ,
20
+ } ;
17
21
use crate :: lists:: {
18
22
definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape,
19
23
struct_lit_tactic, write_list, ListFormatting , Separator ,
@@ -37,6 +41,14 @@ use crate::utils::{
37
41
use crate :: vertical:: rewrite_with_alignment;
38
42
use crate :: visitor:: FmtVisitor ;
39
43
44
+ lazy_static ! {
45
+ // This regex may accept invalid float literals (such as `1`, `_` or `2.e3`). That's ok.
46
+ // We only use it to parse literals whose validity has already been established.
47
+ static ref FLOAT_LITERAL : Regex =
48
+ Regex :: new( r"^([0-9_]+)(?:\.([0-9_]+)?)?([eE][+-]?[0-9_]+)?$" ) . unwrap( ) ;
49
+ static ref ZERO_LITERAL : Regex = Regex :: new( r"^[0_]+$" ) . unwrap( ) ;
50
+ }
51
+
40
52
impl Rewrite for ast:: Expr {
41
53
fn rewrite ( & self , context : & RewriteContext < ' _ > , shape : Shape ) -> Option < String > {
42
54
format_expr ( self , ExprType :: SubExpression , context, shape)
@@ -1235,6 +1247,7 @@ pub(crate) fn rewrite_literal(
1235
1247
match token_lit. kind {
1236
1248
token:: LitKind :: Str => rewrite_string_lit ( context, span, shape) ,
1237
1249
token:: LitKind :: Integer => rewrite_int_lit ( context, token_lit, span, shape) ,
1250
+ token:: LitKind :: Float => rewrite_float_lit ( context, token_lit, span, shape) ,
1238
1251
_ => wrap_str (
1239
1252
context. snippet ( span) . to_owned ( ) ,
1240
1253
context. config . max_width ( ) ,
@@ -1276,6 +1289,11 @@ fn rewrite_int_lit(
1276
1289
shape : Shape ,
1277
1290
) -> Option < String > {
1278
1291
let symbol = token_lit. symbol . as_str ( ) ;
1292
+ let suffix = token_lit. suffix . as_ref ( ) . map ( |s| s. as_str ( ) ) ;
1293
+
1294
+ if suffix == Some ( "f32" ) || suffix == Some ( "f64" ) {
1295
+ return rewrite_float_lit ( context, token_lit, span, shape) ;
1296
+ }
1279
1297
1280
1298
if let Some ( symbol_stripped) = symbol. strip_prefix ( "0x" ) {
1281
1299
let hex_lit = match context. config . hex_literal_case ( ) {
@@ -1285,11 +1303,7 @@ fn rewrite_int_lit(
1285
1303
} ;
1286
1304
if let Some ( hex_lit) = hex_lit {
1287
1305
return wrap_str (
1288
- format ! (
1289
- "0x{}{}" ,
1290
- hex_lit,
1291
- token_lit. suffix. map_or( String :: new( ) , |s| s. to_string( ) )
1292
- ) ,
1306
+ format ! ( "0x{}{}" , hex_lit, suffix. unwrap_or( "" ) ) ,
1293
1307
context. config . max_width ( ) ,
1294
1308
shape,
1295
1309
) ;
@@ -1303,6 +1317,68 @@ fn rewrite_int_lit(
1303
1317
)
1304
1318
}
1305
1319
1320
+ fn rewrite_float_lit (
1321
+ context : & RewriteContext < ' _ > ,
1322
+ token_lit : token:: Lit ,
1323
+ span : Span ,
1324
+ shape : Shape ,
1325
+ ) -> Option < String > {
1326
+ if matches ! (
1327
+ context. config. float_literal_trailing_zero( ) ,
1328
+ FloatLiteralTrailingZero :: Preserve
1329
+ ) {
1330
+ return wrap_str (
1331
+ context. snippet ( span) . to_owned ( ) ,
1332
+ context. config . max_width ( ) ,
1333
+ shape,
1334
+ ) ;
1335
+ }
1336
+
1337
+ let symbol = token_lit. symbol . as_str ( ) ;
1338
+ let suffix = token_lit. suffix . as_ref ( ) . map ( |s| s. as_str ( ) ) ;
1339
+
1340
+ let caps = FLOAT_LITERAL . captures ( symbol) . unwrap ( ) ;
1341
+ let integer_part = caps. get ( 1 ) . unwrap ( ) . as_str ( ) ;
1342
+ let fractional_part = caps. get ( 2 ) . map ( |s| s. as_str ( ) ) ;
1343
+ let exponent = caps. get ( 3 ) . map ( |s| s. as_str ( ) ) ;
1344
+
1345
+ let has_postfix = exponent. is_some ( ) || suffix. is_some ( ) ;
1346
+ let fractional_part_nonzero = fractional_part. map_or ( false , |s| !ZERO_LITERAL . is_match ( s) ) ;
1347
+
1348
+ let ( include_period, include_fractional_part) =
1349
+ match context. config . float_literal_trailing_zero ( ) {
1350
+ FloatLiteralTrailingZero :: Preserve => unreachable ! ( "handled above" ) ,
1351
+ FloatLiteralTrailingZero :: Always => ( true , true ) ,
1352
+ FloatLiteralTrailingZero :: IfNoPostfix => (
1353
+ fractional_part_nonzero || !has_postfix,
1354
+ fractional_part_nonzero || !has_postfix,
1355
+ ) ,
1356
+ FloatLiteralTrailingZero :: Never => (
1357
+ fractional_part_nonzero || !has_postfix,
1358
+ fractional_part_nonzero,
1359
+ ) ,
1360
+ } ;
1361
+
1362
+ let period = if include_period { "." } else { "" } ;
1363
+ let fractional_part = if include_fractional_part {
1364
+ fractional_part. unwrap_or ( "0" )
1365
+ } else {
1366
+ ""
1367
+ } ;
1368
+ wrap_str (
1369
+ format ! (
1370
+ "{}{}{}{}{}" ,
1371
+ integer_part,
1372
+ period,
1373
+ fractional_part,
1374
+ exponent. unwrap_or( "" ) ,
1375
+ suffix. unwrap_or( "" ) ,
1376
+ ) ,
1377
+ context. config . max_width ( ) ,
1378
+ shape,
1379
+ )
1380
+ }
1381
+
1306
1382
fn choose_separator_tactic ( context : & RewriteContext < ' _ > , span : Span ) -> Option < SeparatorTactic > {
1307
1383
if context. inside_macro ( ) {
1308
1384
if span_ends_with_comma ( context, span) {
0 commit comments