From bb87a1b717a88d7cd0447b58bbb071f29ea8bcf5 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 21 Jun 2023 14:11:26 +0000 Subject: [PATCH] Rewrite float literals ending in dots with parens in method calls --- src/chains.rs | 74 +++++++++++++++++++++++++++++--------- src/expr.rs | 15 ++++---- tests/source/issue-5791.rs | 3 ++ tests/target/issue-5791.rs | 3 ++ 4 files changed, 71 insertions(+), 24 deletions(-) create mode 100644 tests/source/issue-5791.rs create mode 100644 tests/target/issue-5791.rs diff --git a/src/chains.rs b/src/chains.rs index 0afce7cf659..bf09d817ed1 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -153,7 +153,13 @@ enum CommentPosition { Top, } -// An expression plus trailing `?`s to be formatted together. +/// Information about an expression in a chain. +struct SubExpr { + expr: ast::Expr, + is_method_call_receiver: bool, +} + +/// An expression plus trailing `?`s to be formatted together. #[derive(Debug)] struct ChainItem { kind: ChainItemKind, @@ -166,7 +172,10 @@ struct ChainItem { // would remove a lot of cloning. #[derive(Debug)] enum ChainItemKind { - Parent(ast::Expr), + Parent { + expr: ast::Expr, + parens: bool, + }, MethodCall( ast::PathSegment, Vec, @@ -181,7 +190,7 @@ enum ChainItemKind { impl ChainItemKind { fn is_block_like(&self, context: &RewriteContext<'_>, reps: &str) -> bool { match self { - ChainItemKind::Parent(ref expr) => utils::is_block_expr(context, expr, reps), + ChainItemKind::Parent { expr, .. } => utils::is_block_expr(context, expr, reps), ChainItemKind::MethodCall(..) | ChainItemKind::StructField(..) | ChainItemKind::TupleField(..) @@ -199,7 +208,11 @@ fn is_tup_field_access(expr: &ast::Expr) -> bool { } } - fn from_ast(context: &RewriteContext<'_>, expr: &ast::Expr) -> (ChainItemKind, Span) { + fn from_ast( + context: &RewriteContext<'_>, + expr: &ast::Expr, + is_method_call_receiver: bool, + ) -> (ChainItemKind, Span) { let (kind, span) = match expr.kind { ast::ExprKind::MethodCall(ref call) => { let types = if let Some(ref generic_args) = call.seg.args { @@ -236,7 +249,19 @@ fn from_ast(context: &RewriteContext<'_>, expr: &ast::Expr) -> (ChainItemKind, S let span = mk_sp(nested.span.hi(), expr.span.hi()); (ChainItemKind::Await, span) } - _ => return (ChainItemKind::Parent(expr.clone()), expr.span), + _ => { + return ( + ChainItemKind::Parent { + expr: expr.clone(), + parens: is_method_call_receiver + && matches!( + &expr.kind, + ast::ExprKind::Lit(lit) if crate::expr::lit_ends_in_dot(lit) + ), + }, + expr.span, + ); + } }; // Remove comments from the span. @@ -249,7 +274,14 @@ impl Rewrite for ChainItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { let shape = shape.sub_width(self.tries)?; let rewrite = match self.kind { - ChainItemKind::Parent(ref expr) => expr.rewrite(context, shape)?, + ChainItemKind::Parent { + ref expr, + parens: true, + } => crate::expr::rewrite_paren(context, &expr, shape, expr.span)?, + ChainItemKind::Parent { + ref expr, + parens: false, + } => expr.rewrite(context, shape)?, ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => { Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)? } @@ -273,8 +305,9 @@ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option } impl ChainItem { - fn new(context: &RewriteContext<'_>, expr: &ast::Expr, tries: usize) -> ChainItem { - let (kind, span) = ChainItemKind::from_ast(context, expr); + fn new(context: &RewriteContext<'_>, expr: &SubExpr, tries: usize) -> ChainItem { + let (kind, span) = + ChainItemKind::from_ast(context, &expr.expr, expr.is_method_call_receiver); ChainItem { kind, tries, span } } @@ -327,7 +360,7 @@ fn from_ast(expr: &ast::Expr, context: &RewriteContext<'_>) -> Chain { let mut rev_children = vec![]; let mut sub_tries = 0; for subexpr in &subexpr_list { - match subexpr.kind { + match subexpr.expr.kind { ast::ExprKind::Try(_) => sub_tries += 1, _ => { rev_children.push(ChainItem::new(context, subexpr, sub_tries)); @@ -442,11 +475,14 @@ fn handle_post_comment( // Returns a Vec of the prefixes of the chain. // E.g., for input `a.b.c` we return [`a.b.c`, `a.b`, 'a'] - fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec { - let mut subexpr_list = vec![expr.clone()]; + fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec { + let mut subexpr_list = vec![SubExpr { + expr: expr.clone(), + is_method_call_receiver: false, + }]; while let Some(subexpr) = Self::pop_expr_chain(subexpr_list.last().unwrap(), context) { - subexpr_list.push(subexpr.clone()); + subexpr_list.push(subexpr); } subexpr_list @@ -454,12 +490,18 @@ fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec) -> Option { - match expr.kind { - ast::ExprKind::MethodCall(ref call) => Some(Self::convert_try(&call.receiver, context)), + fn pop_expr_chain(expr: &SubExpr, context: &RewriteContext<'_>) -> Option { + match expr.expr.kind { + ast::ExprKind::MethodCall(ref call) => Some(SubExpr { + expr: Self::convert_try(&call.receiver, context), + is_method_call_receiver: true, + }), ast::ExprKind::Field(ref subexpr, _) | ast::ExprKind::Try(ref subexpr) - | ast::ExprKind::Await(ref subexpr, _) => Some(Self::convert_try(subexpr, context)), + | ast::ExprKind::Await(ref subexpr, _) => Some(SubExpr { + expr: Self::convert_try(subexpr, context), + is_method_call_receiver: false, + }), _ => None, } } diff --git a/src/expr.rs b/src/expr.rs index 5b1b4fbd491..41cc3651992 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -2,7 +2,7 @@ use std::cmp::min; use itertools::Itertools; -use rustc_ast::token::{Delimiter, LitKind}; +use rustc_ast::token::{Delimiter, Lit, LitKind}; use rustc_ast::{ast, ptr, token}; use rustc_span::{BytePos, Span}; @@ -48,6 +48,10 @@ pub(crate) enum ExprType { SubExpression, } +pub(crate) fn lit_ends_in_dot(lit: &Lit) -> bool { + matches!(lit, Lit { kind: LitKind::Float, suffix: None, symbol } if symbol.as_str().ends_with('.')) +} + pub(crate) fn format_expr( expr: &ast::Expr, expr_type: ExprType, @@ -275,12 +279,7 @@ pub(crate) fn format_expr( fn needs_space_before_range(context: &RewriteContext<'_>, lhs: &ast::Expr) -> bool { match lhs.kind { - ast::ExprKind::Lit(token_lit) => match token_lit.kind { - token::LitKind::Float if token_lit.suffix.is_none() => { - context.snippet(lhs.span).ends_with('.') - } - _ => false, - }, + ast::ExprKind::Lit(token_lit) => lit_ends_in_dot(&token_lit), ast::ExprKind::Unary(_, ref expr) => needs_space_before_range(context, expr), _ => false, } @@ -1440,7 +1439,7 @@ pub(crate) fn span_ends_with_comma(context: &RewriteContext<'_>, span: Span) -> result } -fn rewrite_paren( +pub(crate) fn rewrite_paren( context: &RewriteContext<'_>, mut subexpr: &ast::Expr, shape: Shape, diff --git a/tests/source/issue-5791.rs b/tests/source/issue-5791.rs new file mode 100644 index 00000000000..40bc6daa9f9 --- /dev/null +++ b/tests/source/issue-5791.rs @@ -0,0 +1,3 @@ +pub fn main() { + 0. .to_string(); +} diff --git a/tests/target/issue-5791.rs b/tests/target/issue-5791.rs new file mode 100644 index 00000000000..3a44cf19a1b --- /dev/null +++ b/tests/target/issue-5791.rs @@ -0,0 +1,3 @@ +pub fn main() { + (0.).to_string(); +}