Skip to content

Commit e9740a4

Browse files
committed
Insert space after float literal ending with . in pretty printer
The pretty printer was collapsing unsuffixed float literals (like `0.`) with adjacent dot tokens, producing ambiguous or invalid output: - `0. ..45.` (range) became `0...45.` - `0. ..=360.` (inclusive range) became `0...=360.` - `0. .to_string()` (method call) became `0..to_string()` The fix inserts a space after an unsuffixed float literal ending with `.` when it appears as the start expression of a range operator or the receiver of a method call or field access, preventing the trailing dot from merging with the following `.`, `..`, or `..=` token. This is skipped when the expression is already parenthesized, since the closing `)` naturally provides separation. Signed-off-by: Andrew V. Teylu <andrew.teylu@vector.com>
1 parent fd0c901 commit e9740a4

2 files changed

Lines changed: 45 additions & 11 deletions

File tree

compiler/rustc_ast_pretty/src/pprust/state/expr.rs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -260,12 +260,15 @@ impl<'a> State<'a> {
260260
//
261261
// loop { break x; }.method();
262262
//
263-
self.print_expr_cond_paren(
264-
receiver,
265-
receiver.precedence() < ExprPrecedence::Unambiguous,
266-
fixup.leftmost_subexpression_with_dot(),
267-
);
263+
let needs_paren = receiver.precedence() < ExprPrecedence::Unambiguous;
264+
self.print_expr_cond_paren(receiver, needs_paren, fixup.leftmost_subexpression_with_dot());
268265

266+
// If the receiver is an unsuffixed float literal like `0.`, insert
267+
// a space so the `.` of the method call doesn't merge with the
268+
// trailing dot: `0. .method()` instead of `0..method()`.
269+
if !needs_paren && expr_ends_with_dot(receiver) {
270+
self.word(" ");
271+
}
269272
self.word(".");
270273
self.print_ident(segment.ident);
271274
if let Some(args) = &segment.args {
@@ -658,11 +661,15 @@ impl<'a> State<'a> {
658661
);
659662
}
660663
ast::ExprKind::Field(expr, ident) => {
664+
let needs_paren = expr.precedence() < ExprPrecedence::Unambiguous;
661665
self.print_expr_cond_paren(
662666
expr,
663-
expr.precedence() < ExprPrecedence::Unambiguous,
667+
needs_paren,
664668
fixup.leftmost_subexpression_with_dot(),
665669
);
670+
if !needs_paren && expr_ends_with_dot(expr) {
671+
self.word(" ");
672+
}
666673
self.word(".");
667674
self.print_ident(*ident);
668675
}
@@ -685,11 +692,15 @@ impl<'a> State<'a> {
685692
let fake_prec = ExprPrecedence::LOr;
686693
if let Some(e) = start {
687694
let start_fixup = fixup.leftmost_subexpression_with_operator(true);
688-
self.print_expr_cond_paren(
689-
e,
690-
start_fixup.precedence(e) < fake_prec,
691-
start_fixup,
692-
);
695+
let needs_paren = start_fixup.precedence(e) < fake_prec;
696+
self.print_expr_cond_paren(e, needs_paren, start_fixup);
697+
// If the start expression is a float literal ending with
698+
// `.`, we need a space before `..` or `..=` so that the
699+
// dots don't merge. E.g. `0. ..45.` must not become
700+
// `0...45.`.
701+
if !needs_paren && expr_ends_with_dot(e) {
702+
self.word(" ");
703+
}
693704
}
694705
match limits {
695706
ast::RangeLimits::HalfOpen => self.word(".."),
@@ -1025,3 +1036,18 @@ fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> String
10251036
template.push('"');
10261037
template
10271038
}
1039+
1040+
/// Returns `true` if the printed representation of this expression ends with
1041+
/// a `.` character — specifically, an unsuffixed float literal like `0.` or
1042+
/// `45.`. This is used to insert whitespace before range operators (`..`,
1043+
/// `..=`) so that the dots don't merge (e.g. `0. ..45.` instead of `0...45.`).
1044+
fn expr_ends_with_dot(expr: &ast::Expr) -> bool {
1045+
match &expr.kind {
1046+
ast::ExprKind::Lit(token_lit) => {
1047+
token_lit.kind == token::Float
1048+
&& token_lit.suffix.is_none()
1049+
&& token_lit.symbol.as_str().ends_with('.')
1050+
}
1051+
_ => false,
1052+
}
1053+
}

tests/pretty/float-trailing-dot.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//@ pp-exact
2+
3+
fn main() {
4+
let _ = 0. ..45.;
5+
let _ = 0. ..=360.;
6+
let _ = 0. ..;
7+
let _ = 0. .to_string();
8+
}

0 commit comments

Comments
 (0)