mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Rollup merge of #154086 - aytey:fix-float-range-spacing, r=Kivooeo
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.
## Example
**before** (`rustc 1.96.0-nightly (3b1b0ef4d 2026-03-11)`):
```rust
#![feature(prelude_import)]
#![no_std]
extern crate std;
#[prelude_import]
use ::std::prelude::rust_2015::*;
//@ pp-exact
fn main() {
let _ = 0...45.;
let _ = 0...=360.;
let _ = 0...;
let _ = 0..to_string();
}
```
**after** (this branch):
```rust
#![feature(prelude_import)]
#![no_std]
extern crate std;
#[prelude_import]
use ::std::prelude::rust_2015::*;
//@ pp-exact
fn main() {
let _ = 0. ..45.;
let _ = 0. ..=360.;
let _ = 0. ..;
let _ = 0. .to_string();
}
```
Notice `0...45.` (ambiguous — looks like deprecated `...` inclusive range) becomes `0. ..45.` (clear: float `0.` followed by range `..`). Similarly `0..to_string()` (parsed as range) becomes `0. .to_string()` (method call on float).
This commit is contained in:
@@ -260,12 +260,15 @@ fn print_expr_method_call(
|
||||
//
|
||||
// loop { break x; }.method();
|
||||
//
|
||||
self.print_expr_cond_paren(
|
||||
receiver,
|
||||
receiver.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup.leftmost_subexpression_with_dot(),
|
||||
);
|
||||
let needs_paren = receiver.precedence() < ExprPrecedence::Unambiguous;
|
||||
self.print_expr_cond_paren(receiver, needs_paren, fixup.leftmost_subexpression_with_dot());
|
||||
|
||||
// If the receiver is an unsuffixed float literal like `0.`, insert
|
||||
// a space so the `.` of the method call doesn't merge with the
|
||||
// trailing dot: `0. .method()` instead of `0..method()`.
|
||||
if !needs_paren && expr_ends_with_dot(receiver) {
|
||||
self.word(" ");
|
||||
}
|
||||
self.word(".");
|
||||
self.print_ident(segment.ident);
|
||||
if let Some(args) = &segment.args {
|
||||
@@ -658,11 +661,15 @@ pub(super) fn print_expr_outer_attr_style(
|
||||
);
|
||||
}
|
||||
ast::ExprKind::Field(expr, ident) => {
|
||||
let needs_paren = expr.precedence() < ExprPrecedence::Unambiguous;
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||
needs_paren,
|
||||
fixup.leftmost_subexpression_with_dot(),
|
||||
);
|
||||
if !needs_paren && expr_ends_with_dot(expr) {
|
||||
self.word(" ");
|
||||
}
|
||||
self.word(".");
|
||||
self.print_ident(*ident);
|
||||
}
|
||||
@@ -685,11 +692,15 @@ pub(super) fn print_expr_outer_attr_style(
|
||||
let fake_prec = ExprPrecedence::LOr;
|
||||
if let Some(e) = start {
|
||||
let start_fixup = fixup.leftmost_subexpression_with_operator(true);
|
||||
self.print_expr_cond_paren(
|
||||
e,
|
||||
start_fixup.precedence(e) < fake_prec,
|
||||
start_fixup,
|
||||
);
|
||||
let needs_paren = start_fixup.precedence(e) < fake_prec;
|
||||
self.print_expr_cond_paren(e, needs_paren, start_fixup);
|
||||
// If the start expression is a float literal ending with
|
||||
// `.`, we need a space before `..` or `..=` so that the
|
||||
// dots don't merge. E.g. `0. ..45.` must not become
|
||||
// `0...45.`.
|
||||
if !needs_paren && expr_ends_with_dot(e) {
|
||||
self.word(" ");
|
||||
}
|
||||
}
|
||||
match limits {
|
||||
ast::RangeLimits::HalfOpen => self.word(".."),
|
||||
@@ -1025,3 +1036,18 @@ fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> String
|
||||
template.push('"');
|
||||
template
|
||||
}
|
||||
|
||||
/// Returns `true` if the printed representation of this expression ends with
|
||||
/// a `.` character — specifically, an unsuffixed float literal like `0.` or
|
||||
/// `45.`. This is used to insert whitespace before range operators (`..`,
|
||||
/// `..=`) so that the dots don't merge (e.g. `0. ..45.` instead of `0...45.`).
|
||||
fn expr_ends_with_dot(expr: &ast::Expr) -> bool {
|
||||
match &expr.kind {
|
||||
ast::ExprKind::Lit(token_lit) => {
|
||||
token_lit.kind == token::Float
|
||||
&& token_lit.suffix.is_none()
|
||||
&& token_lit.symbol.as_str().ends_with('.')
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
//@ pp-exact
|
||||
|
||||
fn main() {
|
||||
let _ = 0. ..45.;
|
||||
let _ = 0. ..=360.;
|
||||
let _ = 0. ..;
|
||||
let _ = 0. .to_string();
|
||||
}
|
||||
Reference in New Issue
Block a user