diff options
Diffstat (limited to 'src/expr.rs')
| -rw-r--r-- | src/expr.rs | 474 |
1 files changed, 275 insertions, 199 deletions
diff --git a/src/expr.rs b/src/expr.rs index 75c75c523b0..d6646d48d3e 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3,33 +3,33 @@ use std::cmp::min; use itertools::Itertools; use rustc_ast::token::{Delimiter, Lit, LitKind}; -use rustc_ast::{ast, ptr, token, ForLoopKind, MatchKind}; +use rustc_ast::{ForLoopKind, MatchKind, ast, ptr, token}; use rustc_span::{BytePos, Span}; use tracing::debug; use crate::chains::rewrite_chain; use crate::closures; use crate::comment::{ - combine_strs_with_missing_comments, contains_comment, recover_comment_removed, rewrite_comment, - rewrite_missing_comment, CharClasses, FindUncommented, + CharClasses, FindUncommented, combine_strs_with_missing_comments, contains_comment, + recover_comment_removed, rewrite_comment, rewrite_missing_comment, }; use crate::config::lists::*; -use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, Version}; +use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, StyleEdition}; use crate::lists::{ - definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, - struct_lit_tactic, write_list, ListFormatting, Separator, + ListFormatting, Separator, definitive_tactic, itemize_list, shape_for_tactic, + struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list, }; -use crate::macros::{rewrite_macro, MacroPosition}; +use crate::macros::{MacroPosition, rewrite_macro}; use crate::matches::rewrite_match; use crate::overflow::{self, IntoOverflowableItem, OverflowableItem}; -use crate::pairs::{rewrite_all_pairs, rewrite_pair, PairParts}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::pairs::{PairParts, rewrite_all_pairs, rewrite_pair}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; use crate::stmt; -use crate::string::{rewrite_string, StringFormat}; -use crate::types::{rewrite_path, PathContext}; +use crate::string::{StringFormat, rewrite_string}; +use crate::types::{PathContext, rewrite_path}; use crate::utils::{ colon_spaces, contains_skip, count_newlines, filtered_str_fits, first_line_ends_with, inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes, @@ -40,6 +40,10 @@ use crate::visitor::FmtVisitor; impl Rewrite for ast::Expr { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { format_expr(self, ExprType::SubExpression, context, shape) } } @@ -59,14 +63,14 @@ pub(crate) fn format_expr( expr_type: ExprType, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { - skip_out_of_file_lines_range!(context, expr.span); +) -> RewriteResult { + skip_out_of_file_lines_range_err!(context, expr.span); if contains_skip(&*expr.attrs) { - return Some(context.snippet(expr.span()).to_owned()); + return Ok(context.snippet(expr.span()).to_owned()); } let shape = if expr_type == ExprType::Statement && semicolon_for_expr(context, expr) { - shape.sub_width(1)? + shape.sub_width(1).max_width_error(shape.width, expr.span)? } else { shape }; @@ -82,25 +86,25 @@ pub(crate) fn format_expr( None, ), ast::ExprKind::Lit(token_lit) => { - if let Some(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) { - Some(expr_rw) + if let Ok(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) { + Ok(expr_rw) } else { if let LitKind::StrRaw(_) = token_lit.kind { - Some(context.snippet(expr.span).trim().into()) + Ok(context.snippet(expr.span).trim().into()) } else { - None + Err(RewriteError::Unknown) } } } ast::ExprKind::Call(ref callee, ref args) => { let inner_span = mk_sp(callee.span.hi(), expr.span.hi()); - let callee_str = callee.rewrite(context, shape)?; + let callee_str = callee.rewrite_result(context, shape)?; rewrite_call(context, &callee_str, args, inner_span, shape) } ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape, expr.span), ast::ExprKind::Binary(op, ref lhs, ref rhs) => { // FIXME: format comments between operands and operator - rewrite_all_pairs(expr, shape, context).or_else(|| { + rewrite_all_pairs(expr, shape, context).or_else(|_| { rewrite_pair( &**lhs, &**rhs, @@ -138,7 +142,8 @@ pub(crate) fn format_expr( | ast::ExprKind::ForLoop { .. } | ast::ExprKind::Loop(..) | ast::ExprKind::While(..) => to_control_flow(expr, expr_type) - .and_then(|control_flow| control_flow.rewrite(context, shape)), + .unknown_error() + .and_then(|control_flow| control_flow.rewrite_result(context, shape)), ast::ExprKind::ConstBlock(ref anon_const) => { let rewrite = match anon_const.value.kind { ast::ExprKind::Block(ref block, opt_label) => { @@ -148,20 +153,20 @@ pub(crate) fn format_expr( // See https://github.com/rust-lang/rustfmt/issues/6158 rewrite_block(block, Some(&expr.attrs), opt_label, context, shape)? } - _ => anon_const.rewrite(context, shape)?, + _ => anon_const.rewrite_result(context, shape)?, }; - Some(format!("const {}", rewrite)) + Ok(format!("const {}", rewrite)) } ast::ExprKind::Block(ref block, opt_label) => { match expr_type { ExprType::Statement => { if is_unsafe_block(block) { rewrite_block(block, Some(&expr.attrs), opt_label, context, shape) - } else if let rw @ Some(_) = + } else if let Some(rw) = rewrite_empty_block(context, block, Some(&expr.attrs), opt_label, "", shape) { // Rewrite block without trying to put it in a single line. - rw + Ok(rw) } else { let prefix = block_prefix(context, block, shape)?; @@ -198,7 +203,7 @@ pub(crate) fn format_expr( Some(label) => format!(" {}", label.ident), None => String::new(), }; - Some(format!("continue{id_str}")) + Ok(format!("continue{id_str}")) } ast::ExprKind::Break(ref opt_label, ref opt_expr) => { let id_str = match *opt_label { @@ -209,14 +214,14 @@ pub(crate) fn format_expr( if let Some(ref expr) = *opt_expr { rewrite_unary_prefix(context, &format!("break{id_str} "), &**expr, shape) } else { - Some(format!("break{id_str}")) + Ok(format!("break{id_str}")) } } ast::ExprKind::Yield(ref opt_expr) => { if let Some(ref expr) = *opt_expr { rewrite_unary_prefix(context, "yield ", &**expr, shape) } else { - Some("yield".to_string()) + Ok("yield".to_string()) } } ast::ExprKind::Closure(ref cl) => closures::rewrite_closure( @@ -236,20 +241,21 @@ pub(crate) fn format_expr( | ast::ExprKind::MethodCall(..) | ast::ExprKind::Await(_, _) => rewrite_chain(expr, context, shape), ast::ExprKind::MacCall(ref mac) => { - rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|| { + rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|_| { wrap_str( context.snippet(expr.span).to_owned(), context.config.max_width(), shape, ) + .max_width_error(shape.width, expr.span) }) } - ast::ExprKind::Ret(None) => Some("return".to_owned()), + ast::ExprKind::Ret(None) => Ok("return".to_owned()), ast::ExprKind::Ret(Some(ref expr)) => { rewrite_unary_prefix(context, "return ", &**expr, shape) } ast::ExprKind::Become(ref expr) => rewrite_unary_prefix(context, "become ", &**expr, shape), - ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()), + ast::ExprKind::Yeet(None) => Ok("do yeet".to_owned()), ast::ExprKind::Yeet(Some(ref expr)) => { rewrite_unary_prefix(context, "do yeet ", &**expr, shape) } @@ -344,23 +350,23 @@ pub(crate) fn format_expr( }; rewrite_unary_suffix(context, &sp_delim, &*lhs, shape) } - (None, None) => Some(delim.to_owned()), + (None, None) => Ok(delim.to_owned()), } } // We do not format these expressions yet, but they should still // satisfy our width restrictions. // Style Guide RFC for InlineAsm variant pending // https://github.com/rust-dev-tools/fmt-rfcs/issues/152 - ast::ExprKind::InlineAsm(..) => Some(context.snippet(expr.span).to_owned()), + ast::ExprKind::InlineAsm(..) => Ok(context.snippet(expr.span).to_owned()), ast::ExprKind::TryBlock(ref block) => { - if let rw @ Some(_) = + if let rw @ Ok(_) = rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape) { rw } else { // 9 = `try ` let budget = shape.width.saturating_sub(9); - Some(format!( + Ok(format!( "{}{}", "try ", rewrite_block( @@ -379,7 +385,7 @@ pub(crate) fn format_expr( } else { "" }; - if let rw @ Some(_) = rewrite_single_line_block( + if let rw @ Ok(_) = rewrite_single_line_block( context, format!("{kind} {mover}").as_str(), block, @@ -391,7 +397,7 @@ pub(crate) fn format_expr( } else { // 6 = `async ` let budget = shape.width.saturating_sub(6); - Some(format!( + Ok(format!( "{kind} {mover}{}", rewrite_block( block, @@ -403,7 +409,7 @@ pub(crate) fn format_expr( )) } } - ast::ExprKind::Underscore => Some("_".to_owned()), + ast::ExprKind::Underscore => Ok("_".to_owned()), ast::ExprKind::FormatArgs(..) | ast::ExprKind::Type(..) | ast::ExprKind::IncludedBytes(..) @@ -412,16 +418,16 @@ pub(crate) fn format_expr( // rustfmt tries to parse macro arguments when formatting macros, so it's not totally // impossible for rustfmt to come across one of these nodes when formatting a file. // Also, rustfmt might get passed the output from `-Zunpretty=expanded`. - None + Err(RewriteError::Unknown) } - ast::ExprKind::Err(_) | ast::ExprKind::Dummy => None, + ast::ExprKind::Err(_) | ast::ExprKind::Dummy => Err(RewriteError::Unknown), }; expr_rw - .and_then(|expr_str| recover_comment_removed(expr_str, expr.span, context)) + .map(|expr_str| recover_comment_removed(expr_str, expr.span, context)) .and_then(|expr_str| { let attrs = outer_attributes(&expr.attrs); - let attrs_str = attrs.rewrite(context, shape)?; + let attrs_str = attrs.rewrite_result(context, shape)?; let span = mk_sp( attrs.last().map_or(expr.span.lo(), |attr| attr.span.hi()), expr.span.lo(), @@ -438,7 +444,7 @@ pub(crate) fn rewrite_array<'a, T: 'a + IntoOverflowableItem<'a>>( shape: Shape, force_separator_tactic: Option<SeparatorTactic>, delim_token: Option<Delimiter>, -) -> Option<String> { +) -> RewriteResult { overflow::rewrite_with_square_brackets( context, name, @@ -488,17 +494,20 @@ fn rewrite_empty_block( None } -fn block_prefix(context: &RewriteContext<'_>, block: &ast::Block, shape: Shape) -> Option<String> { - Some(match block.rules { +fn block_prefix(context: &RewriteContext<'_>, block: &ast::Block, shape: Shape) -> RewriteResult { + Ok(match block.rules { ast::BlockCheckMode::Unsafe(..) => { let snippet = context.snippet(block.span); - let open_pos = snippet.find_uncommented("{")?; + let open_pos = snippet.find_uncommented("{").unknown_error()?; // Extract comment between unsafe and block start. let trimmed = &snippet[6..open_pos].trim(); if !trimmed.is_empty() { // 9 = "unsafe {".len(), 7 = "unsafe ".len() - let budget = shape.width.checked_sub(9)?; + let budget = shape + .width + .checked_sub(9) + .max_width_error(shape.width, block.span)?; format!( "unsafe {} ", rewrite_comment( @@ -523,17 +532,19 @@ fn rewrite_single_line_block( attrs: Option<&[ast::Attribute]>, label: Option<ast::Label>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) { - let expr_shape = shape.offset_left(last_line_width(prefix))?; - let expr_str = block_expr.rewrite(context, expr_shape)?; + let expr_shape = shape + .offset_left(last_line_width(prefix)) + .max_width_error(shape.width, block_expr.span())?; + let expr_str = block_expr.rewrite_result(context, expr_shape)?; let label_str = rewrite_label(context, label); let result = format!("{prefix}{label_str}{{ {expr_str} }}"); if result.len() <= shape.width && !result.contains('\n') { - return Some(result); + return Ok(result); } } - None + Err(RewriteError::Unknown) } pub(crate) fn rewrite_block_with_visitor( @@ -544,9 +555,9 @@ pub(crate) fn rewrite_block_with_visitor( label: Option<ast::Label>, shape: Shape, has_braces: bool, -) -> Option<String> { - if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, prefix, shape) { - return rw; +) -> RewriteResult { + if let Some(rw_str) = rewrite_empty_block(context, block, attrs, label, prefix, shape) { + return Ok(rw_str); } let mut visitor = FmtVisitor::from_context(context); @@ -555,7 +566,7 @@ pub(crate) fn rewrite_block_with_visitor( match (block.rules, label) { (ast::BlockCheckMode::Unsafe(..), _) | (ast::BlockCheckMode::Default, Some(_)) => { let snippet = context.snippet(block.span); - let open_pos = snippet.find_uncommented("{")?; + let open_pos = snippet.find_uncommented("{").unknown_error()?; visitor.last_pos = block.span.lo() + BytePos(open_pos as u32) } (ast::BlockCheckMode::Default, None) => visitor.last_pos = block.span.lo(), @@ -569,11 +580,15 @@ pub(crate) fn rewrite_block_with_visitor( .skipped_range .borrow_mut() .append(&mut visitor_context.skipped_range.borrow_mut()); - Some(format!("{}{}{}", prefix, label_str, visitor.buffer)) + Ok(format!("{}{}{}", prefix, label_str, visitor.buffer)) } impl Rewrite for ast::Block { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { rewrite_block(self, None, None, context, shape) } } @@ -584,7 +599,7 @@ fn rewrite_block( label: Option<ast::Label>, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { rewrite_block_inner(block, attrs, label, true, context, shape) } @@ -595,27 +610,24 @@ fn rewrite_block_inner( allow_single_line: bool, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let prefix = block_prefix(context, block, shape)?; // shape.width is used only for the single line case: either the empty block `{}`, // or an unsafe expression `unsafe { e }`. - if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) { - return rw; + if let Some(rw_str) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) { + return Ok(rw_str); } - let result = rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true); - if let Some(ref result_str) = result { - if allow_single_line && result_str.lines().count() <= 3 { - if let rw @ Some(_) = - rewrite_single_line_block(context, &prefix, block, attrs, label, shape) - { - return rw; - } + let result_str = + rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true)?; + if allow_single_line && result_str.lines().count() <= 3 { + if let rw @ Ok(_) = rewrite_single_line_block(context, &prefix, block, attrs, label, shape) + { + return rw; } } - - result + Ok(result_str) } /// Rewrite the divergent block of a `let-else` statement. @@ -624,7 +636,7 @@ pub(crate) fn rewrite_let_else_block( allow_single_line: bool, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { rewrite_block_inner(block, None, None, allow_single_line, context, shape) } @@ -648,6 +660,7 @@ pub(crate) fn rewrite_cond( String::from("\n") + &shape.indent.block_only().to_string(context.config); control_flow .rewrite_cond(context, shape, &alt_block_sep) + .ok() .map(|rw| rw.0) }), } @@ -872,10 +885,12 @@ impl<'a> ControlFlow<'a> { expr: &ast::Expr, shape: Shape, offset: usize, - ) -> Option<String> { + ) -> RewriteResult { debug!("rewrite_pat_expr {:?} {:?} {:?}", shape, self.pat, expr); - let cond_shape = shape.offset_left(offset)?; + let cond_shape = shape + .offset_left(offset) + .max_width_error(shape.width, expr.span)?; if let Some(pat) = self.pat { let matcher = if self.matcher.is_empty() { self.matcher.to_owned() @@ -883,9 +898,10 @@ impl<'a> ControlFlow<'a> { format!("{} ", self.matcher) }; let pat_shape = cond_shape - .offset_left(matcher.len())? - .sub_width(self.connector.len())?; - let pat_string = pat.rewrite(context, pat_shape)?; + .offset_left(matcher.len()) + .and_then(|s| s.sub_width(self.connector.len())) + .max_width_error(cond_shape.width, pat.span)?; + let pat_string = pat.rewrite_result(context, pat_shape)?; let comments_lo = context .snippet_provider .span_after(self.span.with_lo(pat.span.hi()), self.connector.trim()); @@ -902,10 +918,10 @@ impl<'a> ControlFlow<'a> { ); } - let expr_rw = expr.rewrite(context, cond_shape); + let expr_rw = expr.rewrite_result(context, cond_shape); // The expression may (partially) fit on the current line. // We do not allow splitting between `if` and condition. - if self.keyword == "if" || expr_rw.is_some() { + if self.keyword == "if" || expr_rw.is_ok() { return expr_rw; } @@ -914,7 +930,7 @@ impl<'a> ControlFlow<'a> { .block_indent(context.config.tab_spaces()) .with_max_width(context.config); let nested_indent_str = nested_shape.indent.to_string_with_newline(context.config); - expr.rewrite(context, nested_shape) + expr.rewrite_result(context, nested_shape) .map(|expr_rw| format!("{}{}", nested_indent_str, expr_rw)) } @@ -923,7 +939,7 @@ impl<'a> ControlFlow<'a> { context: &RewriteContext<'_>, shape: Shape, alt_block_sep: &str, - ) -> Option<(String, usize)> { + ) -> Result<(String, usize), RewriteError> { // Do not take the rhs overhead from the upper expressions into account // when rewriting pattern. let new_width = context.budget(shape.used_width()); @@ -934,7 +950,9 @@ impl<'a> ControlFlow<'a> { let constr_shape = if self.nested_if { // We are part of an if-elseif-else chain. Our constraints are tightened. // 7 = "} else " .len() - fresh_shape.offset_left(7)? + fresh_shape + .offset_left(7) + .max_width_error(fresh_shape.width, self.span)? } else { fresh_shape }; @@ -970,7 +988,7 @@ impl<'a> ControlFlow<'a> { if let Some(cond_str) = trial { if cond_str.len() <= context.config.single_line_if_else_max_width() { - return Some((cond_str, 0)); + return Ok((cond_str, 0)); } } } @@ -1023,7 +1041,7 @@ impl<'a> ControlFlow<'a> { label_string.len() + self.keyword.len() + pat_expr_string.len() + 2 }; - Some(( + Ok(( format!( "{}{}{}{}{}", label_string, @@ -1089,13 +1107,17 @@ pub(crate) fn rewrite_else_kw_with_comments( impl<'a> Rewrite for ControlFlow<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { debug!("ControlFlow::rewrite {:?} {:?}", self, shape); let alt_block_sep = &shape.indent.to_string_with_newline(context.config); let (cond_str, used_width) = self.rewrite_cond(context, shape, alt_block_sep)?; // If `used_width` is 0, it indicates that whole control flow is written in a single line. if used_width == 0 { - return Some(cond_str); + return Ok(cond_str); } let block_width = shape.width.saturating_sub(used_width); @@ -1139,7 +1161,7 @@ impl<'a> Rewrite for ControlFlow<'a> { true, mk_sp(else_block.span.lo(), self.span.hi()), ) - .rewrite(context, shape) + .rewrite_result(context, shape) } _ => { last_in_chain = true; @@ -1164,7 +1186,7 @@ impl<'a> Rewrite for ControlFlow<'a> { result.push_str(&rewrite?); } - Some(result) + Ok(result) } } @@ -1177,7 +1199,7 @@ fn rewrite_label(context: &RewriteContext<'_>, opt_label: Option<ast::Label>) -> fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { match rewrite_missing_comment(span, shape, context) { - Some(ref comment) if !comment.is_empty() => Some(format!( + Ok(ref comment) if !comment.is_empty() => Some(format!( "{indent}{comment}{indent}", indent = shape.indent.to_string_with_newline(context.config) )), @@ -1248,7 +1270,7 @@ pub(crate) fn rewrite_literal( token_lit: token::Lit, span: Span, shape: Shape, -) -> Option<String> { +) -> RewriteResult { match token_lit.kind { token::LitKind::Str => rewrite_string_lit(context, span, shape), token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape), @@ -1256,11 +1278,12 @@ pub(crate) fn rewrite_literal( context.snippet(span).to_owned(), context.config.max_width(), shape, - ), + ) + .max_width_error(shape.width, span), } } -fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> Option<String> { +fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> RewriteResult { let string_lit = context.snippet(span); if !context.config.format_strings() { @@ -1268,11 +1291,12 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> .lines() .dropping_back(1) .all(|line| line.ends_with('\\')) - && context.config.version() == Version::Two + && context.config.style_edition() >= StyleEdition::Edition2024 { - return Some(string_lit.to_owned()); + return Ok(string_lit.to_owned()); } else { - return wrap_str(string_lit.to_owned(), context.config.max_width(), shape); + return wrap_str(string_lit.to_owned(), context.config.max_width(), shape) + .max_width_error(shape.width, span); } } @@ -1284,6 +1308,7 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> &StringFormat::new(shape.visual_indent(0), context.config), shape.width.saturating_sub(2), ) + .max_width_error(shape.width, span) } fn rewrite_int_lit( @@ -1291,7 +1316,7 @@ fn rewrite_int_lit( token_lit: token::Lit, span: Span, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let symbol = token_lit.symbol.as_str(); if let Some(symbol_stripped) = symbol.strip_prefix("0x") { @@ -1309,7 +1334,8 @@ fn rewrite_int_lit( ), context.config.max_width(), shape, - ); + ) + .max_width_error(shape.width, span); } } @@ -1318,6 +1344,7 @@ fn rewrite_int_lit( context.config.max_width(), shape, ) + .max_width_error(shape.width, span) } fn choose_separator_tactic(context: &RewriteContext<'_>, span: Span) -> Option<SeparatorTactic> { @@ -1338,7 +1365,7 @@ pub(crate) fn rewrite_call( args: &[ptr::P<ast::Expr>], span: Span, shape: Shape, -) -> Option<String> { +) -> RewriteResult { overflow::rewrite_with_parens( context, callee, @@ -1458,7 +1485,7 @@ pub(crate) fn rewrite_paren( mut subexpr: &ast::Expr, shape: Shape, mut span: Span, -) -> Option<String> { +) -> RewriteResult { debug!("rewrite_paren, shape: {:?}", shape); // Extract comments within parens. @@ -1487,11 +1514,14 @@ pub(crate) fn rewrite_paren( } // 1 = `(` and `)` - let sub_shape = shape.offset_left(1)?.sub_width(1)?; - let subexpr_str = subexpr.rewrite(context, sub_shape)?; + let sub_shape = shape + .offset_left(1) + .and_then(|s| s.sub_width(1)) + .max_width_error(shape.width, span)?; + let subexpr_str = subexpr.rewrite_result(context, sub_shape)?; let fits_single_line = !pre_comment.contains("//") && !post_comment.contains("//"); if fits_single_line { - Some(format!("({pre_comment}{subexpr_str}{post_comment})")) + Ok(format!("({pre_comment}{subexpr_str}{post_comment})")) } else { rewrite_paren_in_multi_line(context, subexpr, shape, pre_span, post_span) } @@ -1503,12 +1533,12 @@ fn rewrite_paren_in_multi_line( shape: Shape, pre_span: Span, post_span: Span, -) -> Option<String> { +) -> RewriteResult { let nested_indent = shape.indent.block_indent(context.config); let nested_shape = Shape::indented(nested_indent, context.config); let pre_comment = rewrite_missing_comment(pre_span, nested_shape, context)?; let post_comment = rewrite_missing_comment(post_span, nested_shape, context)?; - let subexpr_str = subexpr.rewrite(context, nested_shape)?; + let subexpr_str = subexpr.rewrite_result(context, nested_shape)?; let mut result = String::with_capacity(subexpr_str.len() * 2); result.push('('); @@ -1525,7 +1555,7 @@ fn rewrite_paren_in_multi_line( result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push(')'); - Some(result) + Ok(result) } fn rewrite_index( @@ -1533,8 +1563,8 @@ fn rewrite_index( index: &ast::Expr, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { - let expr_str = expr.rewrite(context, shape)?; +) -> RewriteResult { + let expr_str = expr.rewrite_result(context, shape)?; let offset = last_line_width(&expr_str) + 1; let rhs_overhead = shape.rhs_overhead(context.config); @@ -1549,37 +1579,45 @@ fn rewrite_index( .and_then(|shape| shape.sub_width(1)), IndentStyle::Visual => shape.visual_indent(offset).sub_width(offset + 1), } - }; - let orig_index_rw = index_shape.and_then(|s| index.rewrite(context, s)); + } + .max_width_error(shape.width, index.span()); + let orig_index_rw = index_shape.and_then(|s| index.rewrite_result(context, s)); // Return if index fits in a single line. match orig_index_rw { - Some(ref index_str) if !index_str.contains('\n') => { - return Some(format!("{expr_str}[{index_str}]")); + Ok(ref index_str) if !index_str.contains('\n') => { + return Ok(format!("{expr_str}[{index_str}]")); } _ => (), } // Try putting index on the next line and see if it fits in a single line. let indent = shape.indent.block_indent(context.config); - let index_shape = Shape::indented(indent, context.config).offset_left(1)?; - let index_shape = index_shape.sub_width(1 + rhs_overhead)?; - let new_index_rw = index.rewrite(context, index_shape); + let index_shape = Shape::indented(indent, context.config) + .offset_left(1) + .max_width_error(shape.width, index.span())?; + let index_shape = index_shape + .sub_width(1 + rhs_overhead) + .max_width_error(index_shape.width, index.span())?; + let new_index_rw = index.rewrite_result(context, index_shape); match (orig_index_rw, new_index_rw) { - (_, Some(ref new_index_str)) if !new_index_str.contains('\n') => Some(format!( + (_, Ok(ref new_index_str)) if !new_index_str.contains('\n') => Ok(format!( "{}{}[{}]", expr_str, indent.to_string_with_newline(context.config), new_index_str, )), - (None, Some(ref new_index_str)) => Some(format!( + (Err(_), Ok(ref new_index_str)) => Ok(format!( "{}{}[{}]", expr_str, indent.to_string_with_newline(context.config), new_index_str, )), - (Some(ref index_str), _) => Some(format!("{expr_str}[{index_str}]")), - _ => None, + (Ok(ref index_str), _) => Ok(format!("{expr_str}[{index_str}]")), + // When both orig_index_rw and new_index_rw result in errors, we currently propagate the + // error from the second attempt since it is more generous with width constraints. + // This decision is somewhat arbitrary and is open to change. + (Err(_), Err(new_index_rw_err)) => Err(new_index_rw_err), } } @@ -1596,7 +1634,7 @@ fn rewrite_struct_lit<'a>( attrs: &[ast::Attribute], span: Span, shape: Shape, -) -> Option<String> { +) -> RewriteResult { debug!("rewrite_struct_lit: shape {:?}", shape); enum StructLitField<'a> { @@ -1606,20 +1644,21 @@ fn rewrite_struct_lit<'a>( } // 2 = " {".len() - let path_shape = shape.sub_width(2)?; + let path_shape = shape.sub_width(2).max_width_error(shape.width, span)?; let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; let has_base_or_rest = match struct_rest { - ast::StructRest::None if fields.is_empty() => return Some(format!("{path_str} {{}}")), + ast::StructRest::None if fields.is_empty() => return Ok(format!("{path_str} {{}}")), ast::StructRest::Rest(_) if fields.is_empty() => { - return Some(format!("{path_str} {{ .. }}")); + return Ok(format!("{path_str} {{ .. }}")); } ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true, _ => false, }; // Foo { a: Foo } - indent is +3, width is -5. - let (h_shape, v_shape) = struct_lit_shape(shape, context, path_str.len() + 3, 2)?; + let (h_shape, v_shape) = struct_lit_shape(shape, context, path_str.len() + 3, 2) + .max_width_error(shape.width, span)?; let one_line_width = h_shape.map_or(0, |shape| shape.width); let body_lo = context.snippet_provider.span_after(span, "{"); @@ -1632,7 +1671,8 @@ fn rewrite_struct_lit<'a>( v_shape, mk_sp(body_lo, span.hi()), one_line_width, - )? + ) + .unknown_error()? } else { let field_iter = fields.iter().map(StructLitField::Regular).chain( match struct_rest { @@ -1661,14 +1701,24 @@ fn rewrite_struct_lit<'a>( let rewrite = |item: &StructLitField<'_>| match *item { StructLitField::Regular(field) => { // The 1 taken from the v_budget is for the comma. - rewrite_field(context, field, v_shape.sub_width(1)?, 0) + rewrite_field( + context, + field, + v_shape.sub_width(1).max_width_error(v_shape.width, span)?, + 0, + ) } StructLitField::Base(expr) => { // 2 = .. - expr.rewrite(context, v_shape.offset_left(2)?) - .map(|s| format!("..{}", s)) + expr.rewrite_result( + context, + v_shape + .offset_left(2) + .max_width_error(v_shape.width, span)?, + ) + .map(|s| format!("..{}", s)) } - StructLitField::Rest(_) => Some("..".to_owned()), + StructLitField::Rest(_) => Ok("..".to_owned()), }; let items = itemize_list( @@ -1703,7 +1753,7 @@ fn rewrite_struct_lit<'a>( let fields_str = wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?; - Some(format!("{path_str} {{{fields_str}}}")) + Ok(format!("{path_str} {{{fields_str}}}")) // FIXME if context.config.indent_style() == Visual, but we run out // of space, we should fall back to BlockIndent. @@ -1716,7 +1766,7 @@ pub(crate) fn wrap_struct_field( shape: Shape, nested_shape: Shape, one_line_width: usize, -) -> Option<String> { +) -> RewriteResult { let should_vertical = context.config.indent_style() == IndentStyle::Block && (fields_str.contains('\n') || !context.config.struct_lit_single_line() @@ -1725,7 +1775,7 @@ pub(crate) fn wrap_struct_field( let inner_attrs = &inner_attributes(attrs); if inner_attrs.is_empty() { if should_vertical { - Some(format!( + Ok(format!( "{}{}{}", nested_shape.indent.to_string_with_newline(context.config), fields_str, @@ -1733,13 +1783,13 @@ pub(crate) fn wrap_struct_field( )) } else { // One liner or visual indent. - Some(format!(" {fields_str} ")) + Ok(format!(" {fields_str} ")) } } else { - Some(format!( + Ok(format!( "{}{}{}{}{}", nested_shape.indent.to_string_with_newline(context.config), - inner_attrs.rewrite(context, shape)?, + inner_attrs.rewrite_result(context, shape)?, nested_shape.indent.to_string_with_newline(context.config), fields_str, shape.indent.to_string_with_newline(context.config) @@ -1756,38 +1806,40 @@ pub(crate) fn rewrite_field( field: &ast::ExprField, shape: Shape, prefix_max_width: usize, -) -> Option<String> { +) -> RewriteResult { if contains_skip(&field.attrs) { - return Some(context.snippet(field.span()).to_owned()); + return Ok(context.snippet(field.span()).to_owned()); } - let mut attrs_str = field.attrs.rewrite(context, shape)?; + let mut attrs_str = field.attrs.rewrite_result(context, shape)?; if !attrs_str.is_empty() { attrs_str.push_str(&shape.indent.to_string_with_newline(context.config)); }; let name = context.snippet(field.ident.span); if field.is_shorthand { - Some(attrs_str + name) + Ok(attrs_str + name) } else { let mut separator = String::from(struct_lit_field_separator(context.config)); for _ in 0..prefix_max_width.saturating_sub(name.len()) { separator.push(' '); } let overhead = name.len() + separator.len(); - let expr_shape = shape.offset_left(overhead)?; - let expr = field.expr.rewrite(context, expr_shape); + let expr_shape = shape + .offset_left(overhead) + .max_width_error(shape.width, field.span)?; + let expr = field.expr.rewrite_result(context, expr_shape); let is_lit = matches!(field.expr.kind, ast::ExprKind::Lit(_)); match expr { - Some(ref e) + Ok(ref e) if !is_lit && e.as_str() == name && context.config.use_field_init_shorthand() => { - Some(attrs_str + name) + Ok(attrs_str + name) } - Some(e) => Some(format!("{attrs_str}{name}{separator}{e}")), - None => { + Ok(e) => Ok(format!("{attrs_str}{name}{separator}{e}")), + Err(_) => { let expr_offset = shape.indent.block_indent(context.config); let expr = field .expr - .rewrite(context, Shape::indented(expr_offset, context.config)); + .rewrite_result(context, Shape::indented(expr_offset, context.config)); expr.map(|s| { format!( "{}{}:\n{}{}", @@ -1808,21 +1860,27 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( span: Span, shape: Shape, is_singleton_tuple: bool, -) -> Option<String> { +) -> RewriteResult { // In case of length 1, need a trailing comma debug!("rewrite_tuple_in_visual_indent_style {:?}", shape); if is_singleton_tuple { // 3 = "(" + ",)" - let nested_shape = shape.sub_width(3)?.visual_indent(1); + let nested_shape = shape + .sub_width(3) + .max_width_error(shape.width, span)? + .visual_indent(1); return items .next() .unwrap() - .rewrite(context, nested_shape) + .rewrite_result(context, nested_shape) .map(|s| format!("({},)", s)); } let list_lo = context.snippet_provider.span_after(span, "("); - let nested_shape = shape.sub_width(2)?.visual_indent(1); + let nested_shape = shape + .sub_width(2) + .max_width_error(shape.width, span)? + .visual_indent(1); let items = itemize_list( context.snippet_provider, items, @@ -1830,7 +1888,7 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( ",", |item| item.span().lo(), |item| item.span().hi(), - |item| item.rewrite(context, nested_shape), + |item| item.rewrite_result(context, nested_shape), list_lo, span.hi() - BytePos(1), false, @@ -1847,7 +1905,7 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( .ends_with_newline(false); let list_str = write_list(&item_vec, &fmt)?; - Some(format!("({list_str})")) + Ok(format!("({list_str})")) } fn rewrite_let( @@ -1855,14 +1913,16 @@ fn rewrite_let( shape: Shape, pat: &ast::Pat, expr: &ast::Expr, -) -> Option<String> { +) -> RewriteResult { let mut result = "let ".to_owned(); // TODO(ytmimi) comments could appear between `let` and the `pat` // 4 = "let ".len() - let pat_shape = shape.offset_left(4)?; - let pat_str = pat.rewrite(context, pat_shape)?; + let pat_shape = shape + .offset_left(4) + .max_width_error(shape.width, pat.span)?; + let pat_str = pat.rewrite_result(context, pat_shape)?; result.push_str(&pat_str); // TODO(ytmimi) comments could appear between `pat` and `=` @@ -1890,7 +1950,7 @@ pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>( span: Span, shape: Shape, is_singleton_tuple: bool, -) -> Option<String> { +) -> RewriteResult { debug!("rewrite_tuple {:?}", shape); if context.use_block_indent() { // We use the same rule as function calls for rewriting tuples. @@ -1919,31 +1979,35 @@ pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>( } } -pub(crate) fn rewrite_unary_prefix<R: Rewrite>( +pub(crate) fn rewrite_unary_prefix<R: Rewrite + Spanned>( context: &RewriteContext<'_>, prefix: &str, rewrite: &R, shape: Shape, -) -> Option<String> { +) -> RewriteResult { + let shape = shape + .offset_left(prefix.len()) + .max_width_error(shape.width, rewrite.span())?; rewrite - .rewrite(context, shape.offset_left(prefix.len())?) + .rewrite_result(context, shape) .map(|r| format!("{}{}", prefix, r)) } // FIXME: this is probably not correct for multi-line Rewrites. we should // subtract suffix.len() from the last line budget, not the first! -pub(crate) fn rewrite_unary_suffix<R: Rewrite>( +pub(crate) fn rewrite_unary_suffix<R: Rewrite + Spanned>( context: &RewriteContext<'_>, suffix: &str, rewrite: &R, shape: Shape, -) -> Option<String> { - rewrite - .rewrite(context, shape.sub_width(suffix.len())?) - .map(|mut r| { - r.push_str(suffix); - r - }) +) -> RewriteResult { + let shape = shape + .sub_width(suffix.len()) + .max_width_error(shape.width, rewrite.span())?; + rewrite.rewrite_result(context, shape).map(|mut r| { + r.push_str(suffix); + r + }) } fn rewrite_unary_op( @@ -1951,7 +2015,7 @@ fn rewrite_unary_op( op: ast::UnOp, expr: &ast::Expr, shape: Shape, -) -> Option<String> { +) -> RewriteResult { // For some reason, an UnOp is not spanned like BinOp! rewrite_unary_prefix(context, op.as_str(), expr, shape) } @@ -1990,15 +2054,21 @@ fn rewrite_assignment( rhs: &ast::Expr, op: Option<&ast::BinOp>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let operator_str = match op { Some(op) => context.snippet(op.span), None => "=", }; // 1 = space between lhs and operator. - let lhs_shape = shape.sub_width(operator_str.len() + 1)?; - let lhs_str = format!("{} {}", lhs.rewrite(context, lhs_shape)?, operator_str); + let lhs_shape = shape + .sub_width(operator_str.len() + 1) + .max_width_error(shape.width, lhs.span())?; + let lhs_str = format!( + "{} {}", + lhs.rewrite_result(context, lhs_shape)?, + operator_str + ); rewrite_assign_rhs( context, @@ -2029,7 +2099,7 @@ pub(crate) fn rewrite_assign_rhs<S: Into<String>, R: Rewrite>( ex: &R, rhs_kind: &RhsAssignKind<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { rewrite_assign_rhs_with(context, lhs, ex, shape, rhs_kind, RhsTactics::Default) } @@ -2040,7 +2110,7 @@ pub(crate) fn rewrite_assign_rhs_expr<R: Rewrite>( shape: Shape, rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, -) -> Option<String> { +) -> RewriteResult { let last_line_width = last_line_width(lhs).saturating_sub(if lhs.contains('\n') { shape.indent.width() } else { @@ -2062,7 +2132,7 @@ pub(crate) fn rewrite_assign_rhs_expr<R: Rewrite>( context, ex, orig_shape, - ex.rewrite(context, orig_shape), + ex.rewrite_result(context, orig_shape), rhs_kind, rhs_tactics, has_rhs_comment, @@ -2076,13 +2146,13 @@ pub(crate) fn rewrite_assign_rhs_with<S: Into<String>, R: Rewrite>( shape: Shape, rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, -) -> Option<String> { +) -> RewriteResult { let lhs = lhs.into(); let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; - Some(lhs + &rhs) + Ok(lhs + &rhs) } -pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite>( +pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite + Spanned>( context: &RewriteContext<'_>, lhs: S, ex: &R, @@ -2091,21 +2161,22 @@ pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite>( rhs_tactics: RhsTactics, between_span: Span, allow_extend: bool, -) -> Option<String> { +) -> RewriteResult { let lhs = lhs.into(); let contains_comment = contains_comment(context.snippet(between_span)); let shape = if contains_comment { - shape.block_left(context.config.tab_spaces())? + shape + .block_left(context.config.tab_spaces()) + .max_width_error(shape.width, between_span.with_hi(ex.span().hi()))? } else { shape }; let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; - if contains_comment { let rhs = rhs.trim_start(); combine_strs_with_missing_comments(context, &lhs, rhs, between_span, shape, allow_extend) } else { - Some(lhs + &rhs) + Ok(lhs + &rhs) } } @@ -2113,23 +2184,25 @@ fn choose_rhs<R: Rewrite>( context: &RewriteContext<'_>, expr: &R, shape: Shape, - orig_rhs: Option<String>, + orig_rhs: RewriteResult, _rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, has_rhs_comment: bool, -) -> Option<String> { +) -> RewriteResult { match orig_rhs { - Some(ref new_str) if new_str.is_empty() => Some(String::new()), - Some(ref new_str) - if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => - { - Some(format!(" {new_str}")) + Ok(ref new_str) if new_str.is_empty() => Ok(String::new()), + Ok(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => { + Ok(format!(" {new_str}")) } _ => { // Expression did not fit on the same line as the identifier. // Try splitting the line and see if that works better. - let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics)?; - let new_rhs = expr.rewrite(context, new_shape); + let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics) + // TODO(ding-young) Ideally, we can replace unknown_error() with max_width_error(), + // but this requires either implementing the Spanned trait for ast::GenericBounds + // or grabbing the span from the call site. + .unknown_error()?; + let new_rhs = expr.rewrite_result(context, new_shape); let new_indent_str = &shape .indent .block_indent(context.config) @@ -2137,24 +2210,27 @@ fn choose_rhs<R: Rewrite>( let before_space_str = if has_rhs_comment { "" } else { " " }; match (orig_rhs, new_rhs) { - (Some(ref orig_rhs), Some(ref new_rhs)) + (Ok(ref orig_rhs), Ok(ref new_rhs)) if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) => { - Some(format!("{before_space_str}{orig_rhs}")) + Ok(format!("{before_space_str}{orig_rhs}")) } - (Some(ref orig_rhs), Some(ref new_rhs)) + (Ok(ref orig_rhs), Ok(ref new_rhs)) if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) => { - Some(format!("{new_indent_str}{new_rhs}")) + Ok(format!("{new_indent_str}{new_rhs}")) } - (None, Some(ref new_rhs)) => Some(format!("{new_indent_str}{new_rhs}")), - (None, None) if rhs_tactics == RhsTactics::AllowOverflow => { + (Err(_), Ok(ref new_rhs)) => Ok(format!("{new_indent_str}{new_rhs}")), + (Err(_), Err(_)) if rhs_tactics == RhsTactics::AllowOverflow => { let shape = shape.infinite_width(); - expr.rewrite(context, shape) + expr.rewrite_result(context, shape) .map(|s| format!("{}{}", before_space_str, s)) } - (None, None) => None, - (Some(orig_rhs), _) => Some(format!("{before_space_str}{orig_rhs}")), + // When both orig_rhs and new_rhs result in errors, we currently propagate + // the error from the second attempt since it is more generous with + // width constraints. This decision is somewhat arbitrary and is open to change. + (Err(_), Err(new_rhs_err)) => Err(new_rhs_err), + (Ok(orig_rhs), _) => Ok(format!("{before_space_str}{orig_rhs}")), } } } @@ -2204,7 +2280,7 @@ fn rewrite_expr_addrof( mutability: ast::Mutability, expr: &ast::Expr, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let operator_str = match (mutability, borrow_kind) { (ast::Mutability::Not, ast::BorrowKind::Ref) => "&", (ast::Mutability::Not, ast::BorrowKind::Raw) => "&raw const ", |
