diff --git a/crates/doc/src/parser/item.rs b/crates/doc/src/parser/item.rs index 6c5e6db22985c..12bae5f94bc53 100644 --- a/crates/doc/src/parser/item.rs +++ b/crates/doc/src/parser/item.rs @@ -86,7 +86,6 @@ impl ParseItem { // TODO(rusowsky): ensure that this is equivalent to the old fmt output self.code = forge_fmt::format(source, config).into_result().map_err(ParserError::Formatter)?; - Ok(self) } diff --git a/crates/fmt/Cargo.toml b/crates/fmt/Cargo.toml index 6c806587ae645..ba78b18b1737c 100644 --- a/crates/fmt/Cargo.toml +++ b/crates/fmt/Cargo.toml @@ -18,7 +18,6 @@ foundry-common.workspace = true solar.workspace = true -# alloy-primitives.workspace = true itertools.workspace = true similar = { version = "2", features = ["inline"] } tracing.workspace = true diff --git a/crates/fmt/src/pp/convenience.rs b/crates/fmt/src/pp/convenience.rs index b3cca7c78522e..45e059efb9850 100644 --- a/crates/fmt/src/pp/convenience.rs +++ b/crates/fmt/src/pp/convenience.rs @@ -146,7 +146,6 @@ impl Printer { // Doesn't actually print trailing comma since it's not allowed in Solidity. pub fn trailing_comma(&mut self, is_last: bool) { if is_last { - // self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() }); self.zerobreak(); } else { self.word(","); diff --git a/crates/fmt/src/state/common.rs b/crates/fmt/src/state/common.rs index d751e962d5566..f2bfecc9e4093 100644 --- a/crates/fmt/src/state/common.rs +++ b/crates/fmt/src/state/common.rs @@ -125,15 +125,6 @@ impl<'ast> State<'_, 'ast> { } } if !exp.is_empty() { - // TODO: preserve the `E`? - /* - out.push(if source.contains('e') { - 'e' - } else { - debug_assert!(source.contains('E')); - 'E' - }); - */ out.push('e'); out.push_str(exp_sign); add_underscores(&mut out, config, exp, false); @@ -195,18 +186,19 @@ impl<'ast> State<'_, 'ast> { return; } - self.print_word("("); - self.s.cbox(self.ind); - if let Some(cmnt) = self.print_comments(pos_hi, CommentConfig::skip_ws().mixed_prev_space()) - { - if cmnt.is_mixed() { - self.s.offset(-self.ind); - } else { - self.break_offset_if_not_bol(0, -self.ind, false); + self.print_inside_parens(|state| { + state.s.cbox(state.ind); + if let Some(cmnt) = + state.print_comments(pos_hi, CommentConfig::skip_ws().mixed_prev_space()) + { + if cmnt.is_mixed() { + state.s.offset(-state.ind); + } else { + state.break_offset_if_not_bol(0, -state.ind, false); + } } - } - self.end(); - self.print_word(")"); + state.end(); + }); } pub(super) fn print_tuple<'a, T, P, S>( @@ -230,36 +222,36 @@ impl<'ast> State<'_, 'ast> { return; } + if !(values.len() == 1 && format.is_inline()) { + // Use commasep + self.print_inside_parens(|state| { + state.commasep(values, pos_lo, pos_hi, print, get_span, format) + }); + return; + } + // Format single-item inline lists directly without boxes - if values.len() == 1 && format.is_inline() { - self.print_word("("); - if let Some(span) = get_span(&values[0]) { - self.s.cbox(self.ind); + self.print_inside_parens(|state| match get_span(&values[0]) { + Some(span) => { + state.s.cbox(state.ind); let mut skip_break = true; - if self.peek_comment_before(span.hi()).is_some() { - self.hardbreak(); + if state.peek_comment_before(span.hi()).is_some() { + state.hardbreak(); skip_break = false; } - self.print_comments(span.lo(), CommentConfig::skip_ws().mixed_prev_space()); - print(self, &values[0]); - if !self.print_trailing_comment(span.hi(), None) && skip_break { - self.neverbreak(); + + state.print_comments(span.lo(), CommentConfig::skip_ws().mixed_prev_space()); + print(state, &values[0]); + + if !state.print_trailing_comment(span.hi(), None) && skip_break { + state.neverbreak(); } else { - self.break_offset_if_not_bol(0, -self.ind, false); + state.break_offset_if_not_bol(0, -state.ind, false); } - self.end(); - } else { - print(self, &values[0]); + state.end(); } - - self.print_word(")"); - return; - } - - // Otherwise, use commasep - self.print_word("("); - self.commasep(values, pos_lo, pos_hi, print, get_span, format); - self.print_word(")"); + None => print(state, &values[0]), + }); } pub(super) fn print_array<'a, T, P, S>( @@ -290,18 +282,15 @@ impl<'ast> State<'_, 'ast> { where S: FnMut(&T) -> Option, { - let pos = if let Some(span) = values.first().and_then(&mut get_span) { - span.lo() - } else { + let Some(span) = values.first().and_then(&mut get_span) else { return false; }; // Check for comments before the first item. if let Some((cmnt_span, cmnt_style)) = - self.peek_comment_before(pos).map(|c| (c.span, c.style)) + self.peek_comment_before(span.lo()).map(|c| (c.span, c.style)) { let cmnt_disabled = self.inline_config.is_disabled(cmnt_span); - // Handle special formatting for disabled code with isolated comments. if self.cursor.enabled && cmnt_disabled && cmnt_style.is_isolated() { self.print_sep(Separator::Hardbreak); @@ -310,41 +299,42 @@ impl<'ast> State<'_, 'ast> { } }; + let cmnt_config = if format.with_delimiters { + CommentConfig::skip_ws().mixed_no_break().mixed_prev_space() + } else { + CommentConfig::skip_ws().no_breaks().mixed_prev_space().offset(self.ind) + }; // Apply spacing based on comment styles. - if let Some(last_style) = self.print_comments( - pos, - if format.with_delimiters { - CommentConfig::skip_ws().mixed_no_break().mixed_prev_space() - } else { - CommentConfig::skip_ws().no_breaks().mixed_prev_space().offset(self.ind) - }, - ) { - if cmnt_style.is_mixed() && last_style.is_mixed() { - if format.breaks_cmnts { - self.hardbreak(); - } else { - self.space(); + if let Some(last_style) = self.print_comments(span.lo(), cmnt_config) { + match (cmnt_style.is_mixed(), last_style.is_mixed()) { + (true, true) => { + if format.breaks_cmnts { + self.hardbreak(); + } else { + self.space(); + } + if !format.with_delimiters && !cmnt_disabled { + self.s.offset(self.ind); + } + } + (false, true) => { + self.nbsp(); } - if !format.with_delimiters && !cmnt_disabled { + (false, false) if !format.with_delimiters && !cmnt_disabled => { + self.hardbreak(); self.s.offset(self.ind); } - } else if !cmnt_style.is_mixed() && last_style.is_mixed() { - self.nbsp(); - } else if !last_style.is_mixed() && !format.with_delimiters && !cmnt_disabled { - self.hardbreak(); - self.s.offset(self.ind); + _ => {} } } - if self.cursor.enabled { - self.cursor.advance_to(pos, true); + self.cursor.advance_to(span.lo(), true); } - return true; } if self.cursor.enabled { - self.cursor.advance_to(pos, true); + self.cursor.advance_to(span.lo(), true); } if !values.is_empty() && !format.with_delimiters { @@ -402,6 +392,7 @@ impl<'ast> State<'_, 'ast> { } else if !skip_first_break && !format.is_inline() { format.print_break(true, values.len(), &mut self.s); } + if format.is_compact() { self.s.cbox(0); } @@ -409,8 +400,8 @@ impl<'ast> State<'_, 'ast> { let mut skip_last_break = is_single_without_cmnts || !format.with_delimiters || format.is_inline(); for (i, value) in values.iter().enumerate() { - let (is_last, span) = (i == values.len() - 1, get_span(value)); - if let Some(span) = span + let is_last = i == values.len() - 1; + if let Some(span) = get_span(value) && self .print_comments(span.lo(), CommentConfig::skip_ws().mixed_prev_space()) .is_some_and(|cmnt| cmnt.is_mixed()) @@ -420,11 +411,14 @@ impl<'ast> State<'_, 'ast> { } print(self, value); + if !is_last { self.print_word(","); } + let next_span = if is_last { None } else { get_span(&values[i + 1]) }; let next_pos = next_span.map(Span::lo).unwrap_or(pos_hi); + if !is_last && format.breaks_cmnts && self.peek_comment_before(next_pos).is_some_and(|cmnt| { @@ -434,14 +428,14 @@ impl<'ast> State<'_, 'ast> { { self.hardbreak(); // trailing and isolated comments already hardbreak } - self.print_comments( - next_pos, - if !is_last || format.with_delimiters { - CommentConfig::skip_ws().mixed_no_break().mixed_prev_space() - } else { - CommentConfig::skip_ws().no_breaks().mixed_prev_space() - }, - ); + + // Print trailing comments. + let comment_config = if !is_last || format.with_delimiters { + CommentConfig::skip_ws().mixed_no_break().mixed_prev_space() + } else { + CommentConfig::skip_ws().no_breaks().mixed_prev_space() + }; + self.print_comments(next_pos, comment_config); if is_last && self.is_bol_or_only_ind() { // if a trailing comment is printed at the very end, we have to manually adjust @@ -449,6 +443,8 @@ impl<'ast> State<'_, 'ast> { self.break_offset_if_not_bol(0, -self.ind, false); skip_last_break = true; } + + // Final break if needed before the next value. if let Some(next_span) = next_span && !self.is_bol_or_only_ind() && !self.inline_config.is_disabled(next_span) @@ -475,6 +471,7 @@ impl<'ast> State<'_, 'ast> { self.nbsp(); self.word(sym); } + self.end(); self.cursor.advance_to(pos_hi, true); @@ -507,76 +504,20 @@ impl<'ast> State<'_, 'ast> { mut get_block_span: impl FnMut(&'ast T) -> Span, pos_hi: BytePos, ) { - // Attempt to print in a single line + // Attempt to print in a single line. if block_format.attempt_single_line() && block.len() == 1 { - self.s.cbox(self.ind); - if matches!(block_format, BlockFormat::Compact(true)) { - self.scan_break(BreakToken { pre_break: Some("{"), ..Default::default() }); - } else { - self.word("{"); - self.space(); - } - print(self, &block[0]); - self.print_comments(get_block_span(&block[0]).hi(), CommentConfig::default()); - if matches!(block_format, BlockFormat::Compact(true)) { - self.s.scan_break(BreakToken { post_break: Some("}"), ..Default::default() }); - self.s.offset(-self.ind); - } else { - self.space_if_not_bol(); - self.s.offset(-self.ind); - self.word("}"); - } - self.end(); + self.print_single_line_block(block, block_format, print, get_block_span); return; } - // Empty blocks with comments require special attention + // Empty blocks with comments require special attention. if block.is_empty() { - // Trailing comments are printed after the block - let cmnt = self.peek_comment_before(pos_hi); - if cmnt.is_none_or(|cmnt| cmnt.style.is_trailing()) { - if self.config.bracket_spacing { - if block_format.with_braces() { - self.word("{ }"); - } else { - self.nbsp(); - } - } else if block_format.with_braces() { - self.word("{}"); - } - self.print_comments(pos_hi, CommentConfig::skip_ws()); - } - // Other comments are printed inside the block - else { - if block_format.with_braces() { - self.word("{"); - } - let offset = - if let BlockFormat::NoBraces(Some(off)) = block_format { off } else { 0 }; - self.print_comments( - pos_hi, - self.cmnt_config() - .offset(offset) - .mixed_no_break() - .mixed_prev_space() - .mixed_post_nbsp(), - ); - self.print_comments( - pos_hi, - CommentConfig::default().mixed_no_break().mixed_prev_space().mixed_post_nbsp(), - ); - - if block_format.with_braces() { - self.word("}"); - } - } + self.print_empty_block(block_format, pos_hi); return; } - let first_stmt = get_block_span(&block[0]); - let block_lo = first_stmt.lo(); - let is_block_lo_disabled = - self.inline_config.is_disabled(Span::new(block_lo, block_lo + BytePos(1))); + // Print multiline block comments. + let block_lo = get_block_span(&block[0]).lo(); match block_format { BlockFormat::NoBraces(None) => { if !self.handle_span(self.cursor.span(block_lo), false) { @@ -585,34 +526,38 @@ impl<'ast> State<'_, 'ast> { self.s.cbox(0); } BlockFormat::NoBraces(Some(offset)) => { - let prev_cmnt = - self.peek_comment_before(block_lo).map(|cmnt| (cmnt.span, cmnt.style)); - if is_block_lo_disabled { - // We don't use `print_sep()` because we want to introduce the breakpoint - if prev_cmnt.is_none() && self.cursor.enabled { - Separator::Space.print(&mut self.s, &mut self.cursor); - self.s.offset(offset); - self.cursor.advance_to(block_lo, true); - } else if prev_cmnt.is_some_and(|(_, style)| style.is_isolated()) { - Separator::Space.print(&mut self.s, &mut self.cursor); - self.s.offset(offset); - } - } else if !self.handle_span(self.cursor.span(block_lo), false) { - if let Some((span, style)) = prev_cmnt { - if !self.inline_config.is_disabled(span) || style.is_isolated() { - self.cursor.advance_to(span.lo(), true); - self.break_offset(SIZE_INFINITY as usize, offset); + let enabled = + !self.inline_config.is_disabled(Span::new(block_lo, block_lo + BytePos(1))) + && !self.handle_span(self.cursor.span(block_lo), false); + match self.peek_comment_before(block_lo).map(|cmnt| (cmnt.span, cmnt.style)) { + Some((span, style)) => { + if enabled { + // Inline config is not disabled and span not handled + if !self.inline_config.is_disabled(span) || style.is_isolated() { + self.cursor.advance_to(span.lo(), true); + self.break_offset(SIZE_INFINITY as usize, offset); + } + if let Some(cmnt) = self + .print_comments(block_lo, CommentConfig::default().offset(offset)) + && !cmnt.is_mixed() + && !cmnt.is_blank() + { + self.s.offset(offset); + } + } else if style.is_isolated() { + Separator::Space.print(&mut self.s, &mut self.cursor); + self.s.offset(offset); } - if let Some(cmnt) = - self.print_comments(block_lo, CommentConfig::default().offset(offset)) - && !cmnt.is_mixed() - && !cmnt.is_blank() - { + } + None => { + if enabled { + self.zerobreak(); + self.s.offset(offset); + } else if self.cursor.enabled { + Separator::Space.print(&mut self.s, &mut self.cursor); self.s.offset(offset); + self.cursor.advance_to(block_lo, true); } - } else { - self.zerobreak(); - self.s.offset(offset); } } self.s.cbox(self.ind); @@ -630,23 +575,20 @@ impl<'ast> State<'_, 'ast> { } } + // Print multiline block statements. for (i, stmt) in block.iter().enumerate() { let is_last = i == block.len() - 1; print(self, stmt); let is_disabled = self.inline_config.is_disabled(get_block_span(stmt)); - let (next_enabled, next_lo) = if !is_last { + let mut next_enabled = false; + let mut next_lo = None; + if !is_last { let next_span = get_block_span(&block[i + 1]); - let next_lo = if self.peek_comment_before(next_span.lo()).is_none() { - Some(next_span.lo()) - } else { - None - }; - - (!self.inline_config.is_disabled(next_span), next_lo) - } else { - (false, None) - }; + next_enabled = !self.inline_config.is_disabled(next_span); + next_lo = + self.peek_comment_before(next_span.lo()).is_none().then_some(next_span.lo()); + } // when this stmt and the next one are enabled, break normally (except if last stmt) if !is_disabled @@ -669,6 +611,7 @@ impl<'ast> State<'_, 'ast> { self.hardbreak_if_not_bol() } } + self.print_comments( pos_hi, CommentConfig::skip_trailing_ws().mixed_no_break().mixed_prev_space(), @@ -684,6 +627,76 @@ impl<'ast> State<'_, 'ast> { self.print_word("}"); } } + + fn print_single_line_block( + &mut self, + block: &'ast [T], + block_format: BlockFormat, + mut print: impl FnMut(&mut Self, &'ast T), + mut get_block_span: impl FnMut(&'ast T) -> Span, + ) { + self.s.cbox(self.ind); + + match block_format { + BlockFormat::Compact(true) => { + self.scan_break(BreakToken { pre_break: Some("{"), ..Default::default() }); + print(self, &block[0]); + self.print_comments(get_block_span(&block[0]).hi(), CommentConfig::default()); + self.s.scan_break(BreakToken { post_break: Some("}"), ..Default::default() }); + self.s.offset(-self.ind); + } + _ => { + self.word("{"); + self.space(); + print(self, &block[0]); + self.print_comments(get_block_span(&block[0]).hi(), CommentConfig::default()); + self.space_if_not_bol(); + self.s.offset(-self.ind); + self.word("}"); + } + } + + self.end(); + } + + fn print_empty_block(&mut self, block_format: BlockFormat, pos_hi: BytePos) { + let has_braces = block_format.with_braces(); + + // Trailing comments are printed after the block + if self.peek_comment_before(pos_hi).is_none_or(|c| c.style.is_trailing()) { + if self.config.bracket_spacing { + if has_braces { + self.word("{ }"); + } else { + self.nbsp(); + } + } else if has_braces { + self.word("{}"); + } + self.print_comments(pos_hi, CommentConfig::skip_ws()); + return; + } + + // Non-trailing or mixed comments - print inside block + if has_braces { + self.word("{"); + } + let mut offset = 0; + if let BlockFormat::NoBraces(Some(off)) = block_format { + offset = off; + } + self.print_comments( + pos_hi, + self.cmnt_config().offset(offset).mixed_no_break().mixed_prev_space().mixed_post_nbsp(), + ); + self.print_comments( + pos_hi, + CommentConfig::default().mixed_no_break().mixed_prev_space().mixed_post_nbsp(), + ); + if has_braces { + self.word("}"); + } + } } /// Formatting style for comma-separated lists. diff --git a/crates/fmt/src/state/mod.rs b/crates/fmt/src/state/mod.rs index 3fcc536527717..67d9cc7aba9bc 100644 --- a/crates/fmt/src/state/mod.rs +++ b/crates/fmt/src/state/mod.rs @@ -359,6 +359,15 @@ impl State<'_, '_> { self.word(ident.to_string()); } + fn print_inside_parens(&mut self, f: F) + where + F: FnOnce(&mut Self), + { + self.print_word("("); + f(self); + self.print_word(")"); + } + fn estimate_size(&self, span: Span) -> usize { if let Ok(snip) = self.sm.span_to_snippet(span) { let (mut size, mut first_line) = (0, true); diff --git a/crates/fmt/src/state/sol.rs b/crates/fmt/src/state/sol.rs index dcf009c4ac6a2..9f8a4cd094303 100644 --- a/crates/fmt/src/state/sol.rs +++ b/crates/fmt/src/state/sol.rs @@ -1,5 +1,9 @@ #![allow(clippy::too_many_arguments)] +use super::{ + CommentConfig, Separator, State, + common::{BlockFormat, ListFormat}, +}; use crate::{ pp::SIZE_INFINITY, state::{CallContext, common::LitExt}, @@ -12,11 +16,6 @@ use solar::parse::{ }; use std::{collections::HashMap, fmt::Debug}; -use super::{ - CommentConfig, Separator, State, - common::{BlockFormat, ListFormat}, -}; - #[rustfmt::skip] macro_rules! get_span { () => { |value| Some(value.span) }; @@ -98,10 +97,7 @@ impl<'ast> State<'_, 'ast> { if advance { if self.peek_comment_before(span.lo()).is_some() { self.print_comments(span.lo(), CommentConfig::default()); - } else if self - .inline_config - .is_disabled(Span::new(span.lo(), span.lo() + BytePos(1))) - { + } else if self.inline_config.is_disabled(span.shrink_to_lo()) { self.hardbreak(); self.cursor.advance_to(span.lo(), true); } @@ -122,14 +118,17 @@ impl<'ast> State<'_, 'ast> { return; } - let add_zero_break = if skip_ws { - self.print_comments(span.lo(), CommentConfig::skip_leading_ws(false)) - } else { - self.print_comments(span.lo(), CommentConfig::default()) - } - .is_some_and(|cmnt| cmnt.is_mixed()); - - if add_zero_break { + if self + .print_comments( + span.lo(), + if skip_ws { + CommentConfig::skip_leading_ws(false) + } else { + CommentConfig::default() + }, + ) + .is_some_and(|cmnt| cmnt.is_mixed()) + { self.zerobreak(); } @@ -282,9 +281,9 @@ impl<'ast> State<'_, 'ast> { if let Some(first) = bases.first().map(|base| base.span()) && let Some(last) = bases.last().map(|base| base.span()) - && self.inline_config.is_disabled(Span::new(first.lo(), last.hi())) + && self.inline_config.is_disabled(first.to(last)) { - _ = self.handle_span(Span::new(first.lo(), last.hi()), false); + _ = self.handle_span(first.until(last), false); } else if !bases.is_empty() { self.word("is"); self.space(); @@ -744,21 +743,11 @@ impl<'ast> State<'_, 'ast> { self.s.ibox(self.ind); } self.print_ty(ty); - if let Some(visibility) = visibility { - self.print_sep(Separator::SpaceOrNbsp(is_var_def)); - self.print_word(visibility.to_str()); - pre_init_size += visibility.to_str().len() + 1; - } - if let Some(mutability) = mutability { - self.print_sep(Separator::SpaceOrNbsp(is_var_def)); - self.print_word(mutability.to_str()); - pre_init_size += mutability.to_str().len() + 1; - } - if let Some(data_location) = data_location { - self.print_sep(Separator::SpaceOrNbsp(is_var_def)); - self.print_word(data_location.to_str()); - pre_init_size += data_location.to_str().len() + 1; - } + + self.print_attribute(visibility.map(|v| v.to_str()), is_var_def, &mut pre_init_size); + self.print_attribute(mutability.map(|m| m.to_str()), is_var_def, &mut pre_init_size); + self.print_attribute(data_location.map(|d| d.to_str()), is_var_def, &mut pre_init_size); + if let Some(override_) = override_ { if self .print_comments(override_.span.lo(), CommentConfig::skip_ws().mixed_prev_space()) @@ -770,11 +759,11 @@ impl<'ast> State<'_, 'ast> { self.print_override(override_); pre_init_size += self.estimate_size(override_.span) + 1; } + if *indexed { - self.print_sep(Separator::SpaceOrNbsp(is_var_def)); - self.print_word("indexed"); - pre_init_size += 8; + self.print_attribute(indexed.then_some("indexed"), is_var_def, &mut pre_init_size); } + if let Some(ident) = name { self.print_sep(Separator::SpaceOrNbsp(is_var_def && override_.is_none())); self.print_comments( @@ -838,7 +827,7 @@ impl<'ast> State<'_, 'ast> { self.s.ibox(self.ind); } else { self.s.ibox(0); - } + }; if has_complex_successor(&init.kind, true) && !matches!(&init.kind, ast::ExprKind::Member(..)) @@ -876,6 +865,19 @@ impl<'ast> State<'_, 'ast> { self.end(); } + fn print_attribute( + &mut self, + attribute: Option<&'static str>, + is_var_def: bool, + size: &mut usize, + ) { + if let Some(s) = attribute { + self.print_sep(Separator::SpaceOrNbsp(is_var_def)); + self.print_word(s); + *size += s.len() + 1; + } + } + fn print_parameter_list( &mut self, parameters: &'ast [ast::VariableDefinition<'ast>], @@ -960,21 +962,21 @@ impl<'ast> State<'_, 'ast> { self.cbox(0); self.word("function"); self.print_parameter_list(parameters, parameters.span, ListFormat::inline()); - self.space(); if let Some(v) = visibility { + self.space(); self.word(v.to_str()); - self.nbsp(); } if let Some(sm) = state_mutability && !matches!(**sm, ast::StateMutability::NonPayable) { + self.space(); self.word(sm.to_str()); - self.nbsp(); } if let Some(ret) = returns && !ret.is_empty() { + self.nbsp(); self.word("returns"); self.nbsp(); self.print_parameter_list( @@ -1113,181 +1115,32 @@ impl<'ast> State<'_, 'ast> { } /* --- Expressions --- */ - + /// Prints an expression by matching on its variant and delegating to the appropriate + /// printer method, handling all Solidity expression kinds. fn print_expr(&mut self, expr: &'ast ast::Expr<'ast>) { let ast::Expr { span, ref kind } = *expr; if self.handle_span(span, false) { return; } - // println!( - // "\n{}\n\nEXPR: {expr:?}\n----------------", - // self.sm.span_to_snippet(span).unwrap() - // ); - match kind { ast::ExprKind::Array(exprs) => { self.print_array(exprs, expr.span, |this, e| this.print_expr(e), get_span!()) } - ast::ExprKind::Assign(lhs, None, rhs) => { - let cache = self.var_init; - self.var_init = true; - - let space_left = self.space_left(); - let lhs_size = self.estimate_size(lhs.span); - let rhs_size = self.estimate_size(rhs.span); - // 'lhs' + ' = ' + 'rhs' + ';' - let overflows = rhs_size + lhs_size + 4 >= space_left; - let fits_alone = rhs_size + self.config.tab_width < space_left; - - self.call_stack.add_precall(lhs_size); - - if (is_call_chain(&rhs.kind, false) && overflows && fits_alone) - || matches!(rhs.kind, ast::ExprKind::Lit(..) | ast::ExprKind::Ident(..)) - { - self.s.ibox(self.ind); - } else { - self.s.ibox(0); - }; - self.print_expr(lhs); - self.word(" ="); - - if let ast::ExprKind::Lit(lit, ..) = &rhs.kind - && lit.is_str_concatenation() - { - self.print_sep(Separator::Nbsp); - self.neverbreak(); - self.s.ibox(self.ind); - self.print_expr(rhs); - self.end(); - } else if (is_call_chain(&rhs.kind, false) && overflows && fits_alone) - || (matches!(rhs.kind, ast::ExprKind::Lit(..) | ast::ExprKind::Ident(..)) - && overflows) - { - self.print_sep(Separator::Space); - self.print_expr(rhs); - } else { - self.print_sep(Separator::Nbsp); - self.neverbreak(); - self.print_expr(rhs); - } - self.end(); - self.var_init = cache; - self.call_stack.reset_precall(); - } - - ast::ExprKind::Assign(lhs, Some(bin_op), rhs) - | ast::ExprKind::Binary(lhs, bin_op, rhs) => { - let cache = self.binary_expr; - let is_chain = cache.is_some_and(|prev| prev == bin_op.kind.group()); - - if !is_chain { - // start of a new operator chain --> open a box and set cache - self.binary_expr = Some(bin_op.kind.group()); - if let ast::ExprKind::Assign(..) = kind - && has_complex_successor(&rhs.kind, true) - { - self.s.ibox(0); - } else if self.call_stack.is_nested() - && is_call_chain(&lhs.kind, false) - && self.estimate_size(lhs.span) >= self.space_left() - { - self.s.ibox(0); - } else { - self.s.ibox(self.ind); - } - } - - self.print_expr(lhs); - - if let ast::ExprKind::Assign(..) = kind { - if !self.print_trailing_comment(lhs.span.hi(), Some(rhs.span.lo())) { - self.nbsp(); - } - self.word(bin_op.kind.to_str()); - self.word("= "); - self.print_expr(rhs); - } else { - if !self.print_trailing_comment(lhs.span.hi(), Some(rhs.span.lo())) - && self - .print_comments( - bin_op.span.lo(), - CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(), - ) - .is_none_or(|cmnt| cmnt.is_mixed()) - { - if !self.config.pow_no_space || !matches!(bin_op.kind, ast::BinOpKind::Pow) - { - self.space_if_not_bol(); - } else if !self.is_bol_or_only_ind() && !self.last_token_is_break() { - self.zerobreak(); - } - } - self.word(bin_op.kind.to_str()); - - if !self.config.pow_no_space || !matches!(bin_op.kind, ast::BinOpKind::Pow) { - self.nbsp(); - } - - if self - .peek_comment_before(rhs.span.lo()) - .is_some_and(|cmnt| cmnt.style.is_mixed()) - { - self.ibox(0); - self.print_expr(rhs); - self.end(); - } else { - self.print_expr(rhs); - } - } - - if !is_chain { - // top-level expression of the chain -> clear cache - self.binary_expr = cache; - self.end(); - } - } + ast::ExprKind::Assign(lhs, None, rhs) => self.print_assign_expr(lhs, rhs), + ast::ExprKind::Assign(lhs, Some(op), rhs) => self.print_bin_expr(lhs, op, rhs, true), + ast::ExprKind::Binary(lhs, op, rhs) => self.print_bin_expr(lhs, op, rhs, false), ast::ExprKind::Call(call_expr, call_args) => { - let callee_size = get_callee_head_size(call_expr); - let with_single_call_chain_child = if call_args.len() == 1 - && let Some(child) = &call_args.exprs().next() - && is_call_chain(&child.kind, false) - { - true - } else { - false - }; - self.print_member_or_call_chain(call_expr, None, |s| { - let (space_left, args_size) = ( - if s.space_left() <= s.call_stack.precall_size() { - s.space_left() - } else { - s.space_left() - s.call_stack.precall_size() - }, - s.estimate_size(call_args.span), - ); - let callee_fits = s.call_stack.size() + callee_size + 1 < space_left; - let all_fits = s.call_stack.size() + callee_size + args_size + 2 <= space_left; - let break_single = !all_fits - && call_args.len() == 1 - && !with_single_call_chain_child - && callee_fits; - let without_ind = (!all_fits - && (callee_fits || s.call_stack.first().is_some_and(|c| c.is_chained())) - && with_single_call_chain_child) - // exception for when returning a binary expr that has a call in one of its members - || s.return_bin_expr; - s.print_call_args( call_args, ListFormat::compact() .break_cmnts() - .break_single(break_single) - .without_ind(without_ind), - callee_size, + .break_single(true) + .without_ind(s.return_bin_expr), + get_callee_head_size(call_expr), ); - }); + }) } ast::ExprKind::CallOptions(expr, named_args) => { self.print_expr(expr); @@ -1298,79 +1151,7 @@ impl<'ast> State<'_, 'ast> { self.print_expr(expr); } ast::ExprKind::Ident(ident) => self.print_ident(ident), - ast::ExprKind::Index(expr, kind) => { - self.print_expr(expr); - self.word("["); - self.s.cbox(self.ind); - - let mut skip_break = false; - match kind { - ast::IndexKind::Index(expr) => { - if let Some(expr) = expr { - self.zerobreak(); - self.print_expr(expr); - } - } - ast::IndexKind::Range(expr0, expr1) => { - if let Some(expr0) = expr0 { - if self - .print_comments(expr0.span.lo(), CommentConfig::skip_ws()) - .is_none_or(|s| s.is_mixed()) - { - self.zerobreak(); - } - self.print_expr(expr0); - } else { - self.zerobreak(); - } - self.word(":"); - if let Some(expr1) = expr1 { - self.s.ibox(self.ind); - if expr0.is_some() { - self.zerobreak(); - } - self.print_comments( - expr1.span.lo(), - CommentConfig::skip_ws() - .mixed_prev_space() - .mixed_no_break() - .mixed_post_nbsp(), - ); - self.print_expr(expr1); - } - - let mut is_trailing = false; - if let Some(style) = self.print_comments( - span.hi(), - CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(), - ) { - skip_break = true; - is_trailing = style.is_trailing(); - } - - // Manually revert indentation if there is `expr1` and/or comments. - if skip_break && expr1.is_some() { - self.break_offset_if_not_bol(0, -2 * self.ind, false); - self.end(); - // if a trailing comment is printed at the very end, we have to manually - // adjust the offset to avoid having a double break. - if !is_trailing { - self.break_offset_if_not_bol(0, -self.ind, false); - } - } else if skip_break { - self.break_offset_if_not_bol(0, -self.ind, false); - } else if expr1.is_some() { - self.end(); - } - } - } - if !skip_break { - self.zerobreak(); - self.s.offset(-self.ind); - } - self.end(); - self.word("]"); - } + ast::ExprKind::Index(expr, kind) => self.print_index_expr(span, expr, kind), ast::ExprKind::Lit(lit, unit) => { self.print_lit(lit); if let Some(unit) = unit { @@ -1397,43 +1178,7 @@ impl<'ast> State<'_, 'ast> { self.word("payable"); self.print_call_args(args, ListFormat::compact().break_cmnts(), 7); } - ast::ExprKind::Ternary(cond, then, els) => { - self.s.cbox(self.ind); - // conditional expression - self.s.ibox(0); - self.print_comments(cond.span.lo(), CommentConfig::skip_ws()); - self.print_expr(cond); - let cmnt = self.peek_comment_before(then.span.lo()); - if cmnt.is_some() { - self.space(); - } - self.print_comments(then.span.lo(), CommentConfig::skip_ws()); - self.end(); - if !self.is_bol_or_only_ind() { - self.space(); - } - // then expression - self.s.ibox(0); - self.word("? "); - self.print_expr(then); - let cmnt = self.peek_comment_before(els.span.lo()); - if cmnt.is_some() { - self.space(); - } - self.print_comments(els.span.lo(), CommentConfig::skip_ws()); - self.end(); - if !self.is_bol_or_only_ind() { - self.space(); - } - // else expression - self.s.ibox(0); - self.word(": "); - self.print_expr(els); - self.end(); - self.neverbreak(); - self.s.offset(-self.ind); - self.end(); - } + ast::ExprKind::Ternary(cond, then, els) => self.print_ternary_expr(cond, then, els), ast::ExprKind::Tuple(exprs) => self.print_tuple( exprs, span.lo(), @@ -1474,105 +1219,363 @@ impl<'ast> State<'_, 'ast> { self.cursor.advance_to(span.hi(), true); } - // If `add_parens_if_empty` is true, then add parentheses `()` even if there are no arguments. - fn print_modifier_call( - &mut self, - modifier: &'ast ast::Modifier<'ast>, - add_parens_if_empty: bool, - ) { - let ast::Modifier { name, arguments } = modifier; - self.print_path(name, false); - if !arguments.is_empty() || add_parens_if_empty { - self.print_call_args( - arguments, - ListFormat::compact().break_cmnts(), - name.to_string().len(), - ); + /// Prints a simple assignment expression of the form `lhs = rhs`. + fn print_assign_expr(&mut self, lhs: &'ast ast::Expr<'ast>, rhs: &'ast ast::Expr<'ast>) { + let prev_var_init = self.var_init; + self.var_init = true; + + // Estimate layout constraints + let space_left = self.space_left(); + let lhs_size = self.estimate_size(lhs.span); + let rhs_size = self.estimate_size(rhs.span); + + let total_size = lhs_size + rhs_size + 4; // 'lhs' + ' = ' + 'rhs' + ';' + let overflows = total_size >= space_left; + let fits_alone = rhs_size + self.config.tab_width < space_left; + + self.call_stack.add_precall(lhs_size); + + let is_simple_rhs = matches!(rhs.kind, ast::ExprKind::Lit(..) | ast::ExprKind::Ident(..)); + let is_chain = is_call_chain(&rhs.kind, false); + + if (is_chain && overflows && fits_alone) || is_simple_rhs { + self.s.ibox(self.ind) + } else { + self.s.ibox(0) + } + + // Print LHS and '=' + self.print_expr(lhs); + self.word(" ="); + + // Handle RHS printing strategy + match &rhs.kind { + ast::ExprKind::Lit(lit, ..) if lit.is_str_concatenation() => { + self.print_sep(Separator::Nbsp); + self.neverbreak(); + self.s.ibox(self.ind); + self.print_expr(rhs); + self.end(); + } + _ if (is_chain && overflows && fits_alone) || (is_simple_rhs && overflows) => { + self.print_sep(Separator::Space); + self.print_expr(rhs); + } + _ => { + self.print_sep(Separator::Nbsp); + self.neverbreak(); + self.print_expr(rhs); + } } + + self.end(); + self.var_init = prev_var_init; + self.call_stack.reset_precall(); } - fn print_member_or_call_chain( + /// Prints a binary operator expression. Handles operator chains and formatting. + fn print_bin_expr( &mut self, - child_expr: &'ast ast::Expr<'ast>, - member_ident: Option<&ast::Ident>, // only members have `Ident`, calls don't - print_suffix: F, - ) where - F: FnOnce(&mut Self), - { - let parent_call = self.call_stack.last().copied(); + lhs: &'ast ast::Expr<'ast>, + bin_op: &ast::BinOp, + rhs: &'ast ast::Expr<'ast>, + is_assign: bool, + ) { + let prev_chain = self.binary_expr; + let is_chain = prev_chain.is_some_and(|prev| prev == bin_op.kind.group()); - // Determine the position of the formatted expression. - // When NOT in a chain, start a new one. - let parent_is_chain = if parent_call.is_some_and(|call| call.is_chained()) { - true - } else { - // Initialize the cache and the indent box. - let member_ident_size = member_ident.map_or(0, |i| self.estimate_size(i.span)); - let callee_size = get_callee_head_size(child_expr); - if is_call_chain(&child_expr.kind, false) { - self.call_stack.push(CallContext::chained(callee_size + member_ident_size)); - } + // Opening box if starting a new operator chain. + if !is_chain { + self.binary_expr = Some(bin_op.kind.group()); - let size = callee_size + member_ident_size + 2; - if !is_call_chain(&child_expr.kind, true) - && self.space_left() > size - && self - .peek_comment_before(child_expr.span.hi()) - .is_none_or(|cmnt| cmnt.style.is_mixed()) + let indent = if (is_assign && has_complex_successor(&rhs.kind, true)) + || self.call_stack.is_nested() + && is_call_chain(&lhs.kind, false) + && self.estimate_size(lhs.span) >= self.space_left() { - self.s.ibox(0); + 0 } else { - self.s.ibox(self.ind); - } - false - }; + self.ind + }; + self.s.ibox(indent); + } - // Recursively print the child/prefix expression. - self.print_expr(child_expr); + // Print LHS. + self.print_expr(lhs); - // Call the closure to print the suffix for the current link, with the calculated position. - print_suffix(self); + // Handle assignment (`+=`, etc.) vs binary ops (`+`, `*`, etc.). + let no_trailing_comment = !self.print_trailing_comment(lhs.span.hi(), Some(rhs.span.lo())); + if is_assign { + if no_trailing_comment { + self.nbsp(); + } + self.word(bin_op.kind.to_str()); + self.word("= "); + } else { + if no_trailing_comment + && self + .print_comments( + bin_op.span.lo(), + CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(), + ) + .is_none_or(|cmnt| cmnt.is_mixed()) + { + if !self.config.pow_no_space || !matches!(bin_op.kind, ast::BinOpKind::Pow) { + self.space_if_not_bol(); + } else if !self.is_bol_or_only_ind() && !self.last_token_is_break() { + self.zerobreak(); + } + } - // If a chain was started, clean up the state and end the box. - if !parent_is_chain { - if is_call_chain(&child_expr.kind, false) { - self.call_stack.pop(); + self.word(bin_op.kind.to_str()); + + if !self.config.pow_no_space || !matches!(bin_op.kind, ast::BinOpKind::Pow) { + self.nbsp(); } + } + + // Print RHS with optional ibox if mixed comment precedes. + let rhs_has_mixed_comment = + self.peek_comment_before(rhs.span.lo()).is_some_and(|cmnt| cmnt.style.is_mixed()); + if rhs_has_mixed_comment { + self.ibox(0); + self.print_expr(rhs); + self.end(); + } else { + self.print_expr(rhs); + } + + // End current box if this was top-level in the chain. + if !is_chain { + self.binary_expr = prev_chain; self.end(); } } - fn print_call_args( + /// Prints an indexing expression. + fn print_index_expr( &mut self, - args: &'ast ast::CallArgs<'ast>, - format: ListFormat, - callee_size: usize, + span: Span, + expr: &'ast ast::Expr<'ast>, + kind: &'ast ast::IndexKind<'ast>, ) { - let ast::CallArgs { span, ref kind } = *args; - if self.handle_span(span, true) { - return; - } - - self.call_stack.push(CallContext::nested(callee_size)); + self.print_expr(expr); + self.word("["); + self.s.cbox(self.ind); - // Clear the binary expression cache before the call. - let cache = self.binary_expr.take(); + let mut skip_break = false; match kind { - ast::CallArgsKind::Unnamed(exprs) => { - self.print_tuple( - exprs, - span.lo(), - span.hi(), - |this, e| this.print_expr(e), + ast::IndexKind::Index(Some(inner_expr)) => { + self.zerobreak(); + self.print_expr(inner_expr); + } + ast::IndexKind::Index(None) => {} + ast::IndexKind::Range(start, end) => { + if let Some(start_expr) = start { + if self + .print_comments(start_expr.span.lo(), CommentConfig::skip_ws()) + .is_none_or(|s| s.is_mixed()) + { + self.zerobreak(); + } + self.print_expr(start_expr); + } else { + self.zerobreak(); + } + + self.word(":"); + + if let Some(end_expr) = end { + self.s.ibox(self.ind); + if start.is_some() { + self.zerobreak(); + } + self.print_comments( + end_expr.span.lo(), + CommentConfig::skip_ws() + .mixed_prev_space() + .mixed_no_break() + .mixed_post_nbsp(), + ); + self.print_expr(end_expr); + } + + // Trailing comment handling. + let mut is_trailing = false; + if let Some(style) = self.print_comments( + span.hi(), + CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(), + ) { + skip_break = true; + is_trailing = style.is_trailing(); + } + + // Adjust indentation and line breaks. + match (skip_break, end.is_some()) { + (true, true) => { + self.break_offset_if_not_bol(0, -2 * self.ind, false); + self.end(); + if !is_trailing { + self.break_offset_if_not_bol(0, -self.ind, false); + } + } + (true, false) => { + self.break_offset_if_not_bol(0, -self.ind, false); + } + (false, true) => { + self.end(); + } + _ => {} + } + } + } + + if !skip_break { + self.zerobreak(); + self.s.offset(-self.ind); + } + + self.end(); + self.word("]"); + } + + /// Prints a ternary expression of the form `cond ? then : else`. + fn print_ternary_expr( + &mut self, + cond: &'ast ast::Expr<'ast>, + then: &'ast ast::Expr<'ast>, + els: &'ast ast::Expr<'ast>, + ) { + self.s.cbox(self.ind); + self.s.ibox(0); + + let mut print_ternary_expr = + |span_lo, prefix: Option<&'static str>, expr: &'ast ast::Expr<'ast>| { + match prefix { + Some(prefix) => { + if self.peek_comment_before(span_lo).is_some() { + self.space(); + } + self.print_comments(span_lo, CommentConfig::skip_ws()); + self.end(); + if !self.is_bol_or_only_ind() { + self.space(); + } + self.s.ibox(0); + self.word(prefix); + } + None => { + self.print_comments(expr.span.lo(), CommentConfig::skip_ws()); + } + }; + self.print_expr(expr); + }; + + // conditional expression + print_ternary_expr(then.span.lo(), None, cond); + // then expression + print_ternary_expr(then.span.lo(), Some("? "), then); + // else expression + print_ternary_expr(els.span.lo(), Some(": "), els); + + self.end(); + self.neverbreak(); + self.s.offset(-self.ind); + self.end(); + } + + // If `add_parens_if_empty` is true, then add parentheses `()` even if there are no arguments. + fn print_modifier_call( + &mut self, + modifier: &'ast ast::Modifier<'ast>, + add_parens_if_empty: bool, + ) { + let ast::Modifier { name, arguments } = modifier; + self.print_path(name, false); + if !arguments.is_empty() || add_parens_if_empty { + self.print_call_args( + arguments, + ListFormat::compact().break_cmnts(), + name.to_string().len(), + ); + } + } + + fn print_member_or_call_chain( + &mut self, + child_expr: &'ast ast::Expr<'ast>, + member_ident: Option<&ast::Ident>, // only members have `Ident`, calls don't + print_suffix: F, + ) where + F: FnOnce(&mut Self), + { + let parent_is_chain = self.call_stack.last().copied().is_some_and(|call| call.is_chained()); + if !parent_is_chain { + // Estimate sizes of callee and optional member + let member_size = member_ident.map_or(0, |i| self.estimate_size(i.span)); + let callee_size = get_callee_head_size(child_expr); + let total_size = callee_size + member_size + 2; + + // Start a new chain if needed + if is_call_chain(&child_expr.kind, false) { + self.call_stack.push(CallContext::chained(callee_size + member_size)); + } + + let fits_line = self.space_left() > total_size; + let no_mixed_comment = + self.peek_comment_before(child_expr.span.hi()).is_none_or(|c| c.style.is_mixed()); + + if !is_call_chain(&child_expr.kind, true) && fits_line && no_mixed_comment { + self.s.ibox(0); + } else { + self.s.ibox(self.ind); + } + } + + // Recursively print the child/prefix expression. + self.print_expr(child_expr); + + // Call the closure to print the suffix for the current link, with the calculated position. + print_suffix(self); + + // If a chain was started, clean up the state and end the box. + if !parent_is_chain { + if is_call_chain(&child_expr.kind, false) { + self.call_stack.pop(); + } + self.end(); + } + } + + fn print_call_args( + &mut self, + args: &'ast ast::CallArgs<'ast>, + format: ListFormat, + callee_size: usize, + ) { + let ast::CallArgs { span, ref kind } = *args; + if self.handle_span(span, true) { + return; + } + + self.call_stack.push(CallContext::nested(callee_size)); + // Clear the binary expression cache before the call. + let cache = self.binary_expr.take(); + + match kind { + ast::CallArgsKind::Unnamed(exprs) => { + self.print_tuple( + exprs, + span.lo(), + span.hi(), + |this, e| this.print_expr(e), get_span!(), format, ); } ast::CallArgsKind::Named(named_args) => { - self.print_word("("); - self.print_named_args(named_args, span.hi()); - self.print_word(")"); + self.print_inside_parens(|state| state.print_named_args(named_args, span.hi())); } } @@ -1619,7 +1622,7 @@ impl<'ast> State<'_, 'ast> { s.print_expr(arg.value); s.end(); }, - |arg| Some(ast::Span::new(arg.name.span.lo(), arg.value.span.hi())), + |arg| Some(arg.name.span.until(arg.value.span)), list_format.break_cmnts().break_single(true).without_ind(self.call_stack.is_chain()), ); self.word("}"); @@ -1630,16 +1633,12 @@ impl<'ast> State<'_, 'ast> { } /* --- Statements --- */ - + /// Prints the given statement in the source code, handling formatting, inline documentation, + /// trailing comments and layout logic for various statement kinds. fn print_stmt(&mut self, stmt: &'ast ast::Stmt<'ast>) { let ast::Stmt { ref docs, span, ref kind } = *stmt; self.print_docs(docs); - // println!( - // "\n{}\n\nSTMT: {stmt:?}\n----------------", - // self.sm.span_to_snippet(stmt.span).unwrap() - // ); - // Handle disabled statements. if self.handle_span(span, false) { self.print_trailing_comment_no_break(stmt.span.hi(), None); @@ -1652,68 +1651,11 @@ impl<'ast> State<'_, 'ast> { match kind { ast::StmtKind::Assembly(ast::StmtAssembly { dialect, flags, block }) => { - _ = self.handle_span(self.cursor.span(span.lo()), false); - if !self.handle_span(Span::new(span.lo(), block.span.lo()), false) { - self.cursor.advance_to(span.lo(), true); - self.print_word("assembly "); - if let Some(dialect) = dialect { - self.print_ast_str_lit(dialect); - self.print_sep(Separator::Nbsp); - } - if !flags.is_empty() { - self.print_tuple( - flags, - span.lo(), - block.span.lo(), - Self::print_ast_str_lit, - get_span!(), - ListFormat::consistent(), - ); - self.print_sep(Separator::Nbsp); - } - } - self.print_yul_block(block, block.span, false, false); + self.print_assembly_stmt(span, dialect, flags, block) } ast::StmtKind::DeclSingle(var) => self.print_var(var, true), ast::StmtKind::DeclMulti(vars, init_expr) => { - let space_left = self.space_left(); - - self.s.ibox(self.ind); - self.s.ibox(-self.ind); - self.print_tuple( - vars, - span.lo(), - init_expr.span.lo(), - |this, var| { - if let Some(var) = var { - this.print_var(var, true); - } - }, - |v| v.as_ref().map(|v| v.span), - ListFormat::consistent(), - ); - self.end(); - self.word(" ="); - - // '(' + var + ', ' + var + ')' + ' =' - let vars_size = vars.iter().fold(0, |acc, v| { - acc + v.as_ref().map_or(0, |v| self.estimate_size(v.span)) + 2 - }) + 2; - self.call_stack.add_precall(vars_size); - - if self.estimate_size(init_expr.span) + self.config.tab_width - <= std::cmp::max(space_left, self.space_left()) - { - self.print_sep(Separator::Space); - self.ibox(0); - } else { - self.print_sep(Separator::Nbsp); - self.neverbreak(); - self.s.ibox(-self.ind); - } - self.print_expr(init_expr); - self.end(); - self.end(); + self.print_multi_decl_stmt(span, vars, init_expr) } ast::StmtKind::Block(stmts) => self.print_block(stmts, span), ast::StmtKind::Break => self.word("break"), @@ -1727,237 +1669,13 @@ impl<'ast> State<'_, 'ast> { ast::StmtKind::Emit(path, args) => self.print_emit_or_revert("emit", path, args), ast::StmtKind::Expr(expr) => self.print_expr(expr), ast::StmtKind::For { init, cond, next, body } => { - self.cbox(0); - self.s.ibox(self.ind); - self.print_word("for ("); - self.zerobreak(); - self.s.cbox(0); - if let Some(init) = init { - self.print_stmt(init); - } else { - self.print_word(";"); - } - if let Some(cond) = cond { - self.print_sep(Separator::Space); - self.print_expr(cond); - } else { - self.zerobreak(); - } - self.print_word(";"); - if let Some(next) = next { - self.space(); - self.print_expr(next); - } else { - self.zerobreak(); - } - self.break_offset_if_not_bol(0, -self.ind, false); - self.end(); - self.print_word(") "); - self.neverbreak(); - self.end(); - self.print_comments(body.span.lo(), CommentConfig::skip_ws()); - self.print_stmt_as_block(body, span.hi(), false); - self.end(); - } - ast::StmtKind::If(cond, then, els_opt) => { - // Check if blocks should be inlined and update cache if necessary - let inline = self.is_single_line_block(cond, then, els_opt.as_ref()); - if !inline.is_cached && self.single_line_stmt.is_none() { - self.single_line_stmt = Some(inline.outcome); - } - - self.cbox(0); - self.ibox(0); - // Print if stmt - self.print_if_no_else(cond, then, inline.outcome); - // Print else (if) stmts, if any - let mut els_opt = els_opt.as_deref(); - while let Some(els) = els_opt { - if self.ends_with('}') { - match self.print_comments( - els.span.lo(), - CommentConfig::skip_ws().mixed_no_break(), - ) { - Some(cmnt) => { - if cmnt.is_mixed() { - self.hardbreak(); - } - } - None => self.nbsp(), - } - } else { - self.hardbreak_if_not_bol(); - if self - .print_comments(els.span.lo(), CommentConfig::skip_ws()) - .is_some_and(|cmnt| cmnt.is_mixed()) - { - self.hardbreak(); - }; - } - self.ibox(0); - self.print_word("else "); - if let ast::StmtKind::If(cond, then, els) = &els.kind { - self.print_if_no_else(cond, then, inline.outcome); - els_opt = els.as_deref(); - continue; - } else { - self.print_stmt_as_block(els, span.hi(), inline.outcome); - self.end(); - } - break; - } - self.end(); - - // Clear cache if necessary - if !inline.is_cached && self.single_line_stmt.is_some() { - self.single_line_stmt = None; - } - } - ast::StmtKind::Return(expr) => { - if force_break { - self.hardbreak_if_not_bol(); - } - - let space_left = self.space_left(); - let expr_size = expr.as_ref().map_or(0, |expr| self.estimate_size(expr.span)); - - // `return ' + expr + ';' - let overflows = space_left < 8 + expr_size; - let fits_alone = space_left > expr_size; - - if let Some(expr) = expr { - self.return_bin_expr = matches!(&expr.kind, ast::ExprKind::Binary(..)); - let is_lit_or_ident = - matches!(&expr.kind, ast::ExprKind::Lit(..) | ast::ExprKind::Ident(..)); - if is_lit_or_ident || (overflows && fits_alone) { - self.s.ibox(self.ind); - } else { - self.ibox(0); - } - self.print_word("return"); - if let Some(cmnt) = self.print_comments( - expr.span.lo(), - CommentConfig::skip_ws() - .mixed_no_break() - .mixed_prev_space() - .mixed_post_nbsp(), - ) { - if cmnt.is_trailing() && !is_lit_or_ident { - self.s.offset(self.ind); - } - } else { - // If overflows and fits alone, allow break - self.print_sep(Separator::SpaceOrNbsp(overflows && fits_alone)); - } - self.print_expr(expr); - self.end(); - self.return_bin_expr = false; - } else { - self.print_word("return"); - } + self.print_for_stmt(span, init, cond, next, body) } + ast::StmtKind::If(cond, then, els_opt) => self.print_if_stmt(span, cond, then, els_opt), + ast::StmtKind::Return(expr) => self.print_return_stmt(force_break, expr), ast::StmtKind::Revert(path, args) => self.print_emit_or_revert("revert", path, args), ast::StmtKind::Try(ast::StmtTry { expr, clauses }) => { - self.cbox(0); - if let Some((first, other)) = clauses.split_first() { - // Handle 'try' clause - let ast::TryCatchClause { args, block, span: try_span, .. } = first; - self.ibox(0); - self.print_word("try "); - self.print_comments(expr.span.lo(), CommentConfig::skip_ws()); - self.print_expr(expr); - self.print_comments( - args.first().map(|p| p.span.lo()).unwrap_or_else(|| expr.span.lo()), - CommentConfig::skip_ws(), - ); - if !self.is_beginning_of_line() { - self.nbsp(); - } - if !args.is_empty() { - self.print_word("returns "); - self.print_parameter_list( - args, - args.span.with_hi(block.span.lo()), - ListFormat::compact(), - ); - self.nbsp(); - } - if block.is_empty() { - self.print_block(block, *try_span); - self.end(); - } else { - self.print_word("{"); - self.end(); - self.neverbreak(); - self.print_trailing_comment_no_break(try_span.lo(), None); - self.print_block_without_braces(block, try_span.hi(), Some(self.ind)); - if self.cursor.enabled || self.cursor.pos < try_span.hi() { - self.print_word("}"); - self.cursor.advance_to(try_span.hi(), true); - } - } - - let mut skip_ind = false; - if self - .print_trailing_comment(try_span.hi(), other.first().map(|c| c.span.lo())) - { - // if a trailing comment is printed at the very end, we have to manually - // adjust the offset to avoid having a double break. - self.break_offset_if_not_bol(0, self.ind, false); - skip_ind = true; - }; - - let mut prev_block_multiline = self.is_multiline_block(block, false); - - // Handle 'catch' clauses - for (pos, ast::TryCatchClause { name, args, block, span: catch_span }) in - other.iter().delimited() - { - let current_block_multiline = self.is_multiline_block(block, false); - if !pos.is_first || !skip_ind { - if prev_block_multiline && (current_block_multiline || pos.is_last) { - self.nbsp(); - } else { - self.space(); - if !current_block_multiline { - self.s.offset(self.ind); - } - } - } - self.s.ibox(self.ind); - self.print_comments( - catch_span.lo(), - CommentConfig::skip_ws().mixed_no_break().mixed_post_nbsp(), - ); - self.print_word("catch "); - if !args.is_empty() { - self.print_comments( - args[0].span.lo(), - CommentConfig::skip_ws().mixed_no_break().mixed_post_nbsp(), - ); - if let Some(name) = name { - self.print_ident(name); - } - self.print_parameter_list( - args, - args.span.with_hi(block.span.lo()), - ListFormat::inline(), - ); - self.nbsp(); - } - self.print_word("{"); - self.end(); - self.print_trailing_comment_no_break(catch_span.lo(), None); - self.print_block_without_braces(block, catch_span.hi(), Some(self.ind)); - if self.cursor.enabled || self.cursor.pos < try_span.hi() { - self.print_word("}"); - self.cursor.advance_to(catch_span.hi(), true); - } - - prev_block_multiline = current_block_multiline; - } - } - self.end(); + self.print_try_stmt(expr, clauses) } ast::StmtKind::UncheckedBlock(block) => { self.word("unchecked "); @@ -1995,16 +1713,367 @@ impl<'ast> State<'_, 'ast> { self.print_trailing_comment_no_break(stmt.span.hi(), None); } + /// Prints an `assembly` statement, including optional dialect and flags, + /// followed by its Yul block. + fn print_assembly_stmt( + &mut self, + span: Span, + dialect: &'ast Option, + flags: &'ast [ast::StrLit], + block: &'ast ast::yul::Block<'ast>, + ) { + _ = self.handle_span(self.cursor.span(span.lo()), false); + if !self.handle_span(span.until(block.span), false) { + self.cursor.advance_to(span.lo(), true); + self.print_word("assembly "); + if let Some(dialect) = dialect { + self.print_ast_str_lit(dialect); + self.print_sep(Separator::Nbsp); + } + if !flags.is_empty() { + self.print_tuple( + flags, + span.lo(), + block.span.lo(), + Self::print_ast_str_lit, + get_span!(), + ListFormat::consistent(), + ); + self.print_sep(Separator::Nbsp); + } + } + self.print_yul_block(block, block.span, false); + } + + /// Prints a multiple-variable declaration with a single initializer expression, + /// formatted as a tuple-style assignment (e.g., `(a, b) = foo();`). + fn print_multi_decl_stmt( + &mut self, + span: Span, + vars: &'ast [Option>], + init_expr: &'ast ast::Expr<'ast>, + ) { + let space_left = self.space_left(); + + self.s.ibox(self.ind); + self.s.ibox(-self.ind); + self.print_tuple( + vars, + span.lo(), + init_expr.span.lo(), + |this, var| { + // NOTE(rusowsky): unless we add more spans to solar, it is not possible to print + // comments between the commas of unhandled vars + if let Some(var) = var { + this.print_var(var, true); + } + }, + |v| v.as_ref().map(|v| v.span), + ListFormat::consistent(), + ); + self.end(); + self.word(" ="); + + // '(' + var + ', ' + var + ')' + ' =' + let vars_size = vars + .iter() + .fold(0, |acc, v| acc + v.as_ref().map_or(0, |v| self.estimate_size(v.span)) + 2) + + 2; + self.call_stack.add_precall(vars_size); + + if self.estimate_size(init_expr.span) + self.config.tab_width + <= std::cmp::max(space_left, self.space_left()) + { + self.print_sep(Separator::Space); + self.ibox(0); + } else { + self.print_sep(Separator::Nbsp); + self.neverbreak(); + self.s.ibox(-self.ind); + } + self.print_expr(init_expr); + self.end(); + self.end(); + } + + /// Prints a `for` loop statement, including its initializer, condition, + /// increment expression, and loop body, with formatting and spacing. + fn print_for_stmt( + &mut self, + span: Span, + init: &'ast Option<&mut ast::Stmt<'ast>>, + cond: &'ast Option<&mut ast::Expr<'ast>>, + next: &'ast Option<&mut ast::Expr<'ast>>, + body: &'ast ast::Stmt<'ast>, + ) { + self.cbox(0); + self.s.ibox(self.ind); + self.print_word("for ("); + self.zerobreak(); + + // Print init. + self.s.cbox(0); + match init { + Some(init_stmt) => self.print_stmt(init_stmt), + None => self.print_word(";"), + } + + // Print condition. + match cond { + Some(cond_expr) => { + self.print_sep(Separator::Space); + self.print_expr(cond_expr); + } + None => self.zerobreak(), + } + self.print_word(";"); + + // Print next clause. + match next { + Some(next_expr) => { + self.space(); + self.print_expr(next_expr); + } + None => self.zerobreak(), + } + + // Close head. + self.break_offset_if_not_bol(0, -self.ind, false); + self.end(); + self.print_word(") "); + self.neverbreak(); + self.end(); + + // Print comments and body. + self.print_comments(body.span.lo(), CommentConfig::skip_ws()); + self.print_stmt_as_block(body, span.hi(), false); + self.end(); + } + + /// Prints an `if` statement, including its condition, `then` block, and any chained + /// `else` or `else if` branches, handling inline formatting decisions and comments. + fn print_if_stmt( + &mut self, + span: Span, + cond: &'ast ast::Expr<'ast>, + then: &'ast ast::Stmt<'ast>, + els_opt: &'ast Option<&mut ast::Stmt<'ast>>, + ) { + // Check if blocks should be inlined and update cache if necessary + let inline = self.is_single_line_block(cond, then, els_opt.as_ref()); + let set_inline_cache = !inline.is_cached && self.single_line_stmt.is_none(); + if set_inline_cache { + self.single_line_stmt = Some(inline.outcome); + } + + self.cbox(0); + self.ibox(0); + // Print if stmt + self.print_if_no_else(cond, then, inline.outcome); + + // Print else (if) stmts, if any + let mut current_else = els_opt.as_deref(); + while let Some(els) = current_else { + if self.ends_with('}') { + match self.print_comments(els.span.lo(), CommentConfig::skip_ws().mixed_no_break()) + { + Some(cmnt) => { + if cmnt.is_mixed() { + self.hardbreak(); + } + } + None => self.nbsp(), + } + } else { + self.hardbreak_if_not_bol(); + if self + .print_comments(els.span.lo(), CommentConfig::skip_ws()) + .is_some_and(|cmnt| cmnt.is_mixed()) + { + self.hardbreak(); + }; + } + + self.ibox(0); + self.print_word("else "); + match &els.kind { + ast::StmtKind::If(cond, then, next_else) => { + self.print_if_no_else(cond, then, inline.outcome); + current_else = next_else.as_deref(); + } + _ => { + self.print_stmt_as_block(els, span.hi(), inline.outcome); + self.end(); // end ibox for final else + break; + } + } + } + self.end(); + + // Clear inline cache if we set it earlier. + if set_inline_cache { + self.single_line_stmt = None; + } + } + + /// Prints a `return` statement, optionally including a return expression. + /// Handles spacing, line breaking, and formatting. + fn print_return_stmt(&mut self, force_break: bool, expr: &'ast Option<&mut ast::Expr<'ast>>) { + if force_break { + self.hardbreak_if_not_bol(); + } + + let space_left = self.space_left(); + let expr_size = expr.as_ref().map_or(0, |expr| self.estimate_size(expr.span)); + + // `return ' + expr + ';' + let overflows = space_left < 8 + expr_size; + let fits_alone = space_left > expr_size; + + if let Some(expr) = expr { + let is_simple = matches!(expr.kind, ast::ExprKind::Lit(..) | ast::ExprKind::Ident(..)); + let allow_break = overflows && fits_alone; + + self.return_bin_expr = matches!(expr.kind, ast::ExprKind::Binary(..)); + self.s.ibox(if is_simple || allow_break { self.ind } else { 0 }); + + self.print_word("return"); + + match self.print_comments( + expr.span.lo(), + CommentConfig::skip_ws().mixed_no_break().mixed_prev_space().mixed_post_nbsp(), + ) { + Some(cmnt) if cmnt.is_trailing() && !is_simple => self.s.offset(self.ind), + None => self.print_sep(Separator::SpaceOrNbsp(allow_break)), + _ => {} + } + + self.print_expr(expr); + self.end(); + self.return_bin_expr = false; + } else { + self.print_word("return"); + } + } + + /// Prints a `try` statement along with its associated `catch` clauses, + /// following Solidity's `try ... returns (...) { ... } catch (...) { ... }` syntax. + fn print_try_stmt( + &mut self, + expr: &'ast ast::Expr<'ast>, + clauses: &'ast [ast::TryCatchClause<'ast>], + ) { + self.cbox(0); + if let Some((first, other)) = clauses.split_first() { + // Print the 'try' clause + let ast::TryCatchClause { args, block, span: try_span, .. } = first; + self.ibox(0); + self.print_word("try "); + self.print_comments(expr.span.lo(), CommentConfig::skip_ws()); + self.print_expr(expr); + + // Print comments. + self.print_comments( + args.first().map(|p| p.span.lo()).unwrap_or_else(|| expr.span.lo()), + CommentConfig::skip_ws(), + ); + if !self.is_beginning_of_line() { + self.nbsp(); + } + + if !args.is_empty() { + self.print_word("returns "); + self.print_parameter_list( + args, + args.span.with_hi(block.span.lo()), + ListFormat::compact(), + ); + self.nbsp(); + } + if block.is_empty() { + self.print_block(block, *try_span); + self.end(); + } else { + self.print_word("{"); + self.end(); + self.neverbreak(); + self.print_trailing_comment_no_break(try_span.lo(), None); + self.print_block_without_braces(block, try_span.hi(), Some(self.ind)); + if self.cursor.enabled || self.cursor.pos < try_span.hi() { + self.print_word("}"); + self.cursor.advance_to(try_span.hi(), true); + } + } + + let mut skip_ind = false; + if self.print_trailing_comment(try_span.hi(), other.first().map(|c| c.span.lo())) { + // if a trailing comment is printed at the very end, we have to manually + // adjust the offset to avoid having a double break. + self.break_offset_if_not_bol(0, self.ind, false); + skip_ind = true; + }; + + let mut prev_block_multiline = self.is_multiline_block(block, false); + + // Handle 'catch' clauses + for (pos, ast::TryCatchClause { name, args, block, span: catch_span }) in + other.iter().delimited() + { + let current_block_multiline = self.is_multiline_block(block, false); + if !pos.is_first || !skip_ind { + if prev_block_multiline && (current_block_multiline || pos.is_last) { + self.nbsp(); + } else { + self.space(); + if !current_block_multiline { + self.s.offset(self.ind); + } + } + } + self.s.ibox(self.ind); + self.print_comments( + catch_span.lo(), + CommentConfig::skip_ws().mixed_no_break().mixed_post_nbsp(), + ); + + self.print_word("catch "); + if !args.is_empty() { + self.print_comments( + args[0].span.lo(), + CommentConfig::skip_ws().mixed_no_break().mixed_post_nbsp(), + ); + if let Some(name) = name { + self.print_ident(name); + } + self.print_parameter_list( + args, + args.span.with_hi(block.span.lo()), + ListFormat::inline(), + ); + self.nbsp(); + } + self.print_word("{"); + self.end(); + self.print_trailing_comment_no_break(catch_span.lo(), None); + self.print_block_without_braces(block, catch_span.hi(), Some(self.ind)); + if self.cursor.enabled || self.cursor.pos < try_span.hi() { + self.print_word("}"); + self.cursor.advance_to(catch_span.hi(), true); + } + + prev_block_multiline = current_block_multiline; + } + } + self.end(); + } + fn print_if_no_else( &mut self, cond: &'ast ast::Expr<'ast>, then: &'ast ast::Stmt<'ast>, inline: bool, ) { - // NOTE(rusowsky): unless we add bracket spans to solar, - // using `then.span.lo()` consumes "cmnt12" of the IfStatement test inside the preceding - // clause: `self.print_if_cond("if", cond, cond.span.hi());` - if !self.handle_span(Span::new(cond.span.lo(), then.span.lo()), true) { + if !self.handle_span(cond.span.until(then.span), true) { self.print_if_cond("if", cond, then.span.lo()); // if empty block without comments, ensure braces are inlined if let ast::StmtKind::Block(block) = &then.kind @@ -2181,7 +2250,7 @@ impl<'ast> State<'_, 'ast> { fn is_inline_stmt(&self, stmt: &'ast ast::Stmt<'ast>, cond_len: usize) -> bool { if let ast::StmtKind::If(cond, then, els_opt) = &stmt.kind { - let if_span = Span::new(cond.span.lo(), then.span.hi()); + let if_span = cond.span.to(then.span); if self.sm.is_multiline(if_span) && matches!( self.config.single_line_statement_blocks, diff --git a/crates/fmt/src/state/yul.rs b/crates/fmt/src/state/yul.rs index bbcbaec879a0a..eb475c5c05240 100644 --- a/crates/fmt/src/state/yul.rs +++ b/crates/fmt/src/state/yul.rs @@ -22,9 +22,7 @@ impl<'ast> State<'_, 'ast> { } match kind { - yul::StmtKind::Block(stmts) => { - self.print_yul_block(stmts, span, self.can_yul_block_be_inlined(stmts), false) - } + yul::StmtKind::Block(stmts) => self.print_yul_block(stmts, span, false), yul::StmtKind::AssignSingle(path, expr) => { self.print_path(path, false); self.word(" := "); @@ -54,22 +52,22 @@ impl<'ast> State<'_, 'ast> { self.word("if "); self.print_yul_expr(expr); self.nbsp(); - self.print_yul_block(stmts, span, self.can_yul_block_be_inlined(stmts), false); + self.print_yul_block(stmts, span, false); } yul::StmtKind::For { init, cond, step, body } => { self.ibox(0); self.word("for "); - self.print_yul_block(init, init.span, self.can_yul_block_be_inlined(init), false); + self.print_yul_block(init, init.span, false); self.space(); self.print_yul_expr(cond); self.space(); - self.print_yul_block(step, step.span, self.can_yul_block_be_inlined(step), false); + self.print_yul_block(step, step.span, false); self.space(); - self.print_yul_block(body, body.span, self.can_yul_block_be_inlined(body), false); + self.print_yul_block(body, body.span, false); self.end(); } @@ -96,7 +94,7 @@ impl<'ast> State<'_, 'ast> { ); self.word("default "); } - self.print_yul_block(body, *span, self.can_yul_block_be_inlined(body), false); + self.print_yul_block(body, *span, false); self.print_trailing_comment(selector.span.hi(), None); } @@ -141,12 +139,7 @@ impl<'ast> State<'_, 'ast> { ); } self.end(); - self.print_yul_block( - body, - span, - self.can_yul_block_be_inlined(body), - skip_opening_brace, - ); + self.print_yul_block(body, span, skip_opening_brace); self.end(); } yul::StmtKind::VarDecl(idents, expr) => { @@ -204,9 +197,8 @@ impl<'ast> State<'_, 'ast> { pub(super) fn print_yul_block( &mut self, - block: &'ast [yul::Stmt<'ast>], + block: &'ast yul::Block<'ast>, span: Span, - inline: bool, skip_opening_brace: bool, ) { if self.handle_span(span, false) { @@ -217,7 +209,10 @@ impl<'ast> State<'_, 'ast> { self.print_word("{"); } - if inline { + let can_inline_block = block.len() <= 1 + && !self.is_multiline_yul_block(block) + && self.estimate_size(block.span) <= self.space_left(); + if can_inline_block { self.neverbreak(); self.print_block_inner( block, @@ -278,14 +273,6 @@ impl<'ast> State<'_, 'ast> { self.print_trailing_comment(span.hi(), None); } - fn can_yul_block_be_inlined(&self, block: &'ast yul::Block<'ast>) -> bool { - if block.len() > 1 || self.is_multiline_yul_block(block) { - false - } else { - self.estimate_size(block.span) <= self.space_left() - } - } - /// Checks if a block statement `{ ... }` contains more than one line of actual code. fn is_multiline_yul_block(&self, block: &'ast yul::Block<'ast>) -> bool { if block.stmts.is_empty() { diff --git a/crates/fmt/testdata/FunctionType/fmt.sol b/crates/fmt/testdata/FunctionType/fmt.sol index 95dafb998b9cf..f2cb30800a72d 100644 --- a/crates/fmt/testdata/FunctionType/fmt.sol +++ b/crates/fmt/testdata/FunctionType/fmt.sol @@ -28,4 +28,12 @@ library ArrayUtils { r[i] = i; } } + + function _castToPure(function(bytes memory) internal view fnIn) + returns (function(bytes memory) pure fnOut) + { + assembly { + fnOut := fnIn + } + } } diff --git a/crates/fmt/testdata/FunctionType/original.sol b/crates/fmt/testdata/FunctionType/original.sol index 27b402d85ef5f..e3c408a79e653 100644 --- a/crates/fmt/testdata/FunctionType/original.sol +++ b/crates/fmt/testdata/FunctionType/original.sol @@ -28,4 +28,12 @@ library ArrayUtils { r[i] = i; } } + + function _castToPure(function(bytes memory) internal view fnIn) +returns (function(bytes memory) pure fnOut) + { + assembly { + fnOut := fnIn + } + } } diff --git a/crates/fmt/testdata/Repros/fmt.sol b/crates/fmt/testdata/Repros/fmt.sol index 8d8f3bb2ccab6..e944432a8ef1d 100644 --- a/crates/fmt/testdata/Repros/fmt.sol +++ b/crates/fmt/testdata/Repros/fmt.sol @@ -184,9 +184,9 @@ contract NestedCallsTest is Test { Vm constant vm = Vm(HEVM_ADDRESS); function test_nestedCalls() public { - vm._expectCheatcodeRevert(bytes(string.concat( - errMsg, ": ", left, " != ", right - ))); + vm._expectCheatcodeRevert( + bytes(string.concat(errMsg, ": ", left, " != ", right)) + ); } function test_assemblyFnComments() public { diff --git a/crates/fmt/testdata/Repros/sorted.fmt.sol b/crates/fmt/testdata/Repros/sorted.fmt.sol index a8853cbbe7055..304186c2222c8 100644 --- a/crates/fmt/testdata/Repros/sorted.fmt.sol +++ b/crates/fmt/testdata/Repros/sorted.fmt.sol @@ -185,9 +185,9 @@ contract NestedCallsTest is Test { Vm constant vm = Vm(HEVM_ADDRESS); function test_nestedCalls() public { - vm._expectCheatcodeRevert(bytes(string.concat( - errMsg, ": ", left, " != ", right - ))); + vm._expectCheatcodeRevert( + bytes(string.concat(errMsg, ": ", left, " != ", right)) + ); } function test_assemblyFnComments() public { diff --git a/crates/fmt/testdata/Repros/tab.fmt.sol b/crates/fmt/testdata/Repros/tab.fmt.sol index 0d3c9fc96d110..eb349c49c15ba 100644 --- a/crates/fmt/testdata/Repros/tab.fmt.sol +++ b/crates/fmt/testdata/Repros/tab.fmt.sol @@ -185,9 +185,9 @@ contract NestedCallsTest is Test { Vm constant vm = Vm(HEVM_ADDRESS); function test_nestedCalls() public { - vm._expectCheatcodeRevert(bytes(string.concat( - errMsg, ": ", left, " != ", right - ))); + vm._expectCheatcodeRevert( + bytes(string.concat(errMsg, ": ", left, " != ", right)) + ); } function test_assemblyFnComments() public { diff --git a/crates/fmt/testdata/ReprosCalls/110.fmt.sol b/crates/fmt/testdata/ReprosCalls/110.fmt.sol index 38ecb5f951331..b4f99279c1387 100644 --- a/crates/fmt/testdata/ReprosCalls/110.fmt.sol +++ b/crates/fmt/testdata/ReprosCalls/110.fmt.sol @@ -6,13 +6,18 @@ function test() public { "string mismatch" ); + address lerp = + LerpFactoryLike(lerpFab()).newLerp(_name, _target, _what, _startTime, _start, _end, _duration); + (oracleRouter, eVault) = execute( oracleRouterFactory, deployRouterForOracle, eVaultFactory, upgradable, asset, oracle, unitOfAccount ); if (eVault == address(0)) { - eVault = address(GenericFactory(eVaultFactory) - .createProxy(address(0), true, abi.encodePacked(asset, address(0), address(0)))); + eVault = address( + GenericFactory(eVaultFactory) + .createProxy(address(0), true, abi.encodePacked(asset, address(0), address(0))) + ); } content = string.concat( @@ -23,9 +28,9 @@ function test() public { "\"}\n" ); - oracleInfo = abi.encode(LidoOracleInfo({ - base: IOracle(oracleAddress).WSTETH(), quote: IOracle(oracleAddress).STETH() - })); + oracleInfo = abi.encode( + LidoOracleInfo({base: IOracle(oracleAddress).WSTETH(), quote: IOracle(oracleAddress).STETH()}) + ); return someFunction().getValue().modifyValue().negate().scaleBySomeFactor(1000).transformToTuple(); @@ -80,15 +85,3 @@ function returnLongBinaryOp() returns (bytes32) { uint256(Feature.unwrap(feature)) << 128 | uint256(block.chainid) << 64 | uint256(Nonce.unwrap(nonce)) ); } - -function new_y(uint256 x, uint256 dx, uint256 x_basis, uint256 y, uint256 y_basis) - external - pure - returns (uint256) -{ - return _get_y( - x * _VELODROME_TOKEN_BASIS / x_basis, - dx * _VELODROME_TOKEN_BASIS / x_basis, - y * _VELODROME_TOKEN_BASIS / y_basis - ); -} diff --git a/crates/fmt/testdata/ReprosCalls/120.fmt.sol b/crates/fmt/testdata/ReprosCalls/120.fmt.sol index 7f738b5a47407..41da784773f66 100644 --- a/crates/fmt/testdata/ReprosCalls/120.fmt.sol +++ b/crates/fmt/testdata/ReprosCalls/120.fmt.sol @@ -5,12 +5,15 @@ function test() public { "string mismatch" ); + address lerp = LerpFactoryLike(lerpFab()).newLerp(_name, _target, _what, _startTime, _start, _end, _duration); + (oracleRouter, eVault) = execute(oracleRouterFactory, deployRouterForOracle, eVaultFactory, upgradable, asset, oracle, unitOfAccount); if (eVault == address(0)) { - eVault = address(GenericFactory(eVaultFactory) - .createProxy(address(0), true, abi.encodePacked(asset, address(0), address(0)))); + eVault = address( + GenericFactory(eVaultFactory).createProxy(address(0), true, abi.encodePacked(asset, address(0), address(0))) + ); } content = string.concat( @@ -74,11 +77,3 @@ function returnLongBinaryOp() returns (bytes32) { return bytes32(uint256(Feature.unwrap(feature)) << 128 | uint256(block.chainid) << 64 | uint256(Nonce.unwrap(nonce))); } - -function new_y(uint256 x, uint256 dx, uint256 x_basis, uint256 y, uint256 y_basis) external pure returns (uint256) { - return _get_y( - x * _VELODROME_TOKEN_BASIS / x_basis, - dx * _VELODROME_TOKEN_BASIS / x_basis, - y * _VELODROME_TOKEN_BASIS / y_basis - ); -} diff --git a/crates/fmt/testdata/ReprosCalls/80.fmt.sol b/crates/fmt/testdata/ReprosCalls/80.fmt.sol index 552f360f6a79a..f34aa669af610 100644 --- a/crates/fmt/testdata/ReprosCalls/80.fmt.sol +++ b/crates/fmt/testdata/ReprosCalls/80.fmt.sol @@ -6,6 +6,9 @@ function test() public { "string mismatch" ); + address lerp = LerpFactoryLike(lerpFab()) + .newLerp(_name, _target, _what, _startTime, _start, _end, _duration); + (oracleRouter, eVault) = execute( oracleRouterFactory, deployRouterForOracle, @@ -17,12 +20,14 @@ function test() public { ); if (eVault == address(0)) { - eVault = address(GenericFactory(eVaultFactory) - .createProxy( - address(0), - true, - abi.encodePacked(asset, address(0), address(0)) - )); + eVault = address( + GenericFactory(eVaultFactory) + .createProxy( + address(0), + true, + abi.encodePacked(asset, address(0), address(0)) + ) + ); } content = string.concat( @@ -33,10 +38,12 @@ function test() public { "\"}\n" ); - oracleInfo = abi.encode(LidoOracleInfo({ - base: IOracle(oracleAddress).WSTETH(), - quote: IOracle(oracleAddress).STETH() - })); + oracleInfo = abi.encode( + LidoOracleInfo({ + base: IOracle(oracleAddress).WSTETH(), + quote: IOracle(oracleAddress).STETH() + }) + ); return someFunction().getValue().modifyValue().negate() .scaleBySomeFactor(1000).transformToTuple(); @@ -50,9 +57,9 @@ function test() public { (bool success, bytes memory data) = GenericFactory(eVaultFactory) .implementation() - .staticcall(abi.encodePacked( - EVCUtil.EVC.selector, uint256(0), uint256(0) - )); + .staticcall( + abi.encodePacked(EVCUtil.EVC.selector, uint256(0), uint256(0)) + ); IEVC.BatchItem[] memory items = new IEVC.BatchItem[](3); @@ -111,17 +118,3 @@ function returnLongBinaryOp() returns (bytes32) { | uint256(Nonce.unwrap(nonce)) ); } - -function new_y( - uint256 x, - uint256 dx, - uint256 x_basis, - uint256 y, - uint256 y_basis -) external pure returns (uint256) { - return _get_y( - x * _VELODROME_TOKEN_BASIS / x_basis, - dx * _VELODROME_TOKEN_BASIS / x_basis, - y * _VELODROME_TOKEN_BASIS / y_basis - ); -} diff --git a/crates/fmt/testdata/ReprosCalls/original.sol b/crates/fmt/testdata/ReprosCalls/original.sol index 639203cfd211b..7d8a2b1ac55ce 100644 --- a/crates/fmt/testdata/ReprosCalls/original.sol +++ b/crates/fmt/testdata/ReprosCalls/original.sol @@ -4,6 +4,8 @@ function test() public { "string mismatch" ); + address lerp = LerpFactoryLike(lerpFab()).newLerp(_name, _target, _what, _startTime, _start, _end, _duration); + (oracleRouter, eVault) = execute(oracleRouterFactory, deployRouterForOracle, eVaultFactory, upgradable, asset, oracle, unitOfAccount); diff --git a/crates/fmt/testdata/RevertStatement/fmt.sol b/crates/fmt/testdata/RevertStatement/fmt.sol index 1276596f21d8f..9f52fbceba927 100644 --- a/crates/fmt/testdata/RevertStatement/fmt.sol +++ b/crates/fmt/testdata/RevertStatement/fmt.sol @@ -31,12 +31,14 @@ contract RevertStatement { message /* comment9 */ ); /* comment10 */ // comment11 - revert(string.concat( - message, - someVeryLongFunctionNameToGetDynamicErrorMessageString( - /* comment12 */ + revert( + string.concat( + message, + someVeryLongFunctionNameToGetDynamicErrorMessageString( + /* comment12 */ + ) ) - )); + ); revert TestError(0, false, message); revert TestError( diff --git a/crates/fmt/tests/formatter.rs b/crates/fmt/tests/formatter.rs index ecf887daff32d..6e7f1eb5fdce9 100644 --- a/crates/fmt/tests/formatter.rs +++ b/crates/fmt/tests/formatter.rs @@ -118,20 +118,7 @@ fn test_formatter( comments_end: usize, ) { let path = &*expected_path.with_file_name("original.sol"); - let expected_data = || { - let data = Data::read_from(expected_path, None); - if cfg!(windows) { - let content = data - .to_string() - .replace("\r\n", "\n") - .replace(r"\'", r"/'") - .replace(r#"\""#, r#"/""#) - .replace("\\\n", "/\n"); - Data::text(content) - } else { - data.raw() - } - }; + let expected_data = || Data::read_from(expected_path, None).raw(); let mut source_formatted = format(source, path, config.clone()); // Inject `expected`'s comments, if any, so we can use the expected file as a snapshot. diff --git a/testdata/default/cheats/AttachDelegation.t.sol b/testdata/default/cheats/AttachDelegation.t.sol index 05c7461928c87..3d5873de4cf35 100644 --- a/testdata/default/cheats/AttachDelegation.t.sol +++ b/testdata/default/cheats/AttachDelegation.t.sol @@ -75,9 +75,7 @@ contract AttachDelegationTest is DSTest { calls[0] = SimpleDelegateContract.Call({to: address(token), data: abi.encodeCall(ERC20.mint, (50, bob)), value: 0}); calls[1] = SimpleDelegateContract.Call({ - to: address(token), - data: abi.encodeCall(ERC20.mint, (50, address(this))), - value: 0 + to: address(token), data: abi.encodeCall(ERC20.mint, (50, address(this))), value: 0 }); SimpleDelegateContract(alice).execute(calls); @@ -95,9 +93,7 @@ contract AttachDelegationTest is DSTest { calls[0] = SimpleDelegateContract.Call({to: address(token), data: abi.encodeCall(ERC20.mint, (50, bob)), value: 0}); calls[1] = SimpleDelegateContract.Call({ - to: address(token), - data: abi.encodeCall(ERC20.mint, (50, address(this))), - value: 0 + to: address(token), data: abi.encodeCall(ERC20.mint, (50, address(this))), value: 0 }); SimpleDelegateContract(alice).execute(calls); @@ -200,14 +196,10 @@ contract AttachDelegationTest is DSTest { vm.signAndAttachDelegation(address(implementation2), bob_pk); SimpleDelegateContract.Call[] memory calls = new SimpleDelegateContract.Call[](2); calls[0] = SimpleDelegateContract.Call({ - to: address(token), - data: abi.encodeCall(ERC20.mint, (50, address(this))), - value: 0 + to: address(token), data: abi.encodeCall(ERC20.mint, (50, address(this))), value: 0 }); calls[1] = SimpleDelegateContract.Call({ - to: address(token), - data: abi.encodeCall(ERC20.mint, (50, alice)), - value: 0 + to: address(token), data: abi.encodeCall(ERC20.mint, (50, alice)), value: 0 }); vm.broadcast(bob_pk); SimpleDelegateContract(alice).execute(calls); diff --git a/testdata/default/fork/DssExecLib.sol b/testdata/default/fork/DssExecLib.sol index 41becd090e658..5632d029d94fe 100644 --- a/testdata/default/fork/DssExecLib.sol +++ b/testdata/default/fork/DssExecLib.sol @@ -1356,8 +1356,8 @@ library DssExecLib { uint256 _end, uint256 _duration ) public returns (address) { - address lerp = - LerpFactoryLike(lerpFab()).newIlkLerp(_name, _target, _ilk, _what, _startTime, _start, _end, _duration); + address lerp = LerpFactoryLike(lerpFab()) + .newIlkLerp(_name, _target, _ilk, _what, _startTime, _start, _end, _duration); Authorizable(_target).rely(lerp); LerpLike(lerp).tick(); return lerp;