1use crate::{
4 buffer::*,
5 chunk::*,
6 comments::{
7 CommentPosition, CommentState, CommentStringExt, CommentType, CommentWithMetadata, Comments,
8 },
9 format_diagnostics_report,
10 helpers::import_path_string,
11 macros::*,
12 solang_ext::{pt::*, *},
13 string::{QuoteState, QuotedStringExt},
14 visit::{Visitable, Visitor},
15 FormatterConfig, InlineConfig, IntTypes,
16};
17use alloy_primitives::Address;
18use foundry_config::fmt::{HexUnderscore, MultilineFuncHeaderStyle, SingleLineBlockStyle};
19use itertools::{Either, Itertools};
20use solang_parser::diagnostics::Diagnostic;
21use std::{fmt::Write, path::PathBuf, str::FromStr};
22use thiserror::Error;
23
24type Result<T, E = FormatterError> = std::result::Result<T, E>;
25
26#[derive(Debug, Error)]
28pub enum FormatterError {
29 #[error(transparent)]
31 Fmt(#[from] std::fmt::Error),
32 #[error("encountered invalid parse tree item at {0:?}")]
34 InvalidParsedItem(Loc),
35 #[error("failed to parse file:\n{}", format_diagnostics_report(_0, _1.as_deref(), _2))]
37 Parse(String, Option<PathBuf>, Vec<Diagnostic>),
38 #[error(transparent)]
40 Custom(Box<dyn std::error::Error + Send + Sync>),
41}
42
43impl FormatterError {
44 fn fmt() -> Self {
45 Self::Fmt(std::fmt::Error)
46 }
47
48 fn custom(err: impl std::error::Error + Send + Sync + 'static) -> Self {
49 Self::Custom(Box::new(err))
50 }
51}
52
53#[expect(unused_macros)]
54macro_rules! format_err {
55 ($msg:literal $(,)?) => {
56 $crate::formatter::FormatterError::custom($msg.to_string())
57 };
58 ($err:expr $(,)?) => {
59 $crate::formatter::FormatterError::custom($err)
60 };
61 ($fmt:expr, $($arg:tt)*) => {
62 $crate::formatter::FormatterError::custom(format!($fmt, $($arg)*))
63 };
64}
65
66macro_rules! bail {
67 ($msg:literal $(,)?) => {
68 return Err($crate::formatter::format_err!($msg))
69 };
70 ($err:expr $(,)?) => {
71 return Err($err)
72 };
73 ($fmt:expr, $($arg:tt)*) => {
74 return Err($crate::formatter::format_err!($fmt, $(arg)*))
75 };
76}
77
78#[derive(Debug, Default)]
81struct Context {
82 contract: Option<ContractDefinition>,
83 function: Option<FunctionDefinition>,
84 if_stmt_single_line: Option<bool>,
85}
86
87impl Context {
88 pub(crate) fn is_constructor_function(&self) -> bool {
90 self.function.as_ref().is_some_and(|f| matches!(f.ty, FunctionTy::Constructor))
91 }
92}
93
94#[derive(Debug)]
96pub struct Formatter<'a, W> {
97 buf: FormatBuffer<W>,
98 source: &'a str,
99 config: FormatterConfig,
100 temp_bufs: Vec<FormatBuffer<String>>,
101 context: Context,
102 comments: Comments,
103 inline_config: InlineConfig,
104}
105
106impl<'a, W: Write> Formatter<'a, W> {
107 pub fn new(
108 w: W,
109 source: &'a str,
110 comments: Comments,
111 inline_config: InlineConfig,
112 config: FormatterConfig,
113 ) -> Self {
114 Self {
115 buf: FormatBuffer::new(w, config.tab_width),
116 source,
117 config,
118 temp_bufs: Vec::new(),
119 context: Context::default(),
120 comments,
121 inline_config,
122 }
123 }
124
125 fn buf(&mut self) -> &mut dyn Write {
127 match &mut self.temp_bufs[..] {
128 [] => &mut self.buf as &mut dyn Write,
129 [.., buf] => buf as &mut dyn Write,
130 }
131 }
132
133 unsafe fn buf_contents(&self) -> &String {
135 *(&self.buf.w as *const W as *const &mut String)
136 }
137
138 #[expect(dead_code)]
141 unsafe fn temp_buf_contents(&self) -> &String {
142 match &self.temp_bufs[..] {
143 [] => self.buf_contents(),
144 [.., buf] => &buf.w,
145 }
146 }
147
148 buf_fn! { fn indent(&mut self, delta: usize) }
149 buf_fn! { fn dedent(&mut self, delta: usize) }
150 buf_fn! { fn start_group(&mut self) }
151 buf_fn! { fn end_group(&mut self) }
152 buf_fn! { fn create_temp_buf(&self) -> FormatBuffer<String> }
153 buf_fn! { fn restrict_to_single_line(&mut self, restricted: bool) }
154 buf_fn! { fn current_line_len(&self) -> usize }
155 buf_fn! { fn total_indent_len(&self) -> usize }
156 buf_fn! { fn is_beginning_of_line(&self) -> bool }
157 buf_fn! { fn last_char(&self) -> Option<char> }
158 buf_fn! { fn last_indent_group_skipped(&self) -> bool }
159 buf_fn! { fn set_last_indent_group_skipped(&mut self, skip: bool) }
160 buf_fn! { fn write_raw(&mut self, s: impl AsRef<str>) -> std::fmt::Result }
161
162 fn with_temp_buf(
164 &mut self,
165 mut fun: impl FnMut(&mut Self) -> Result<()>,
166 ) -> Result<FormatBuffer<String>> {
167 self.temp_bufs.push(self.create_temp_buf());
168 let res = fun(self);
169 let out = self.temp_bufs.pop().unwrap();
170 res?;
171 Ok(out)
172 }
173
174 fn next_char_needs_space(&self, next_char: char) -> bool {
176 if self.is_beginning_of_line() {
177 return false
178 }
179 let last_char =
180 if let Some(last_char) = self.last_char() { last_char } else { return false };
181 if last_char.is_whitespace() || next_char.is_whitespace() {
182 return false
183 }
184 match last_char {
185 '{' => match next_char {
186 '{' | '[' | '(' => false,
187 '/' => true,
188 _ => self.config.bracket_spacing,
189 },
190 '(' | '.' | '[' => matches!(next_char, '/'),
191 '/' => true,
192 _ => match next_char {
193 '}' => self.config.bracket_spacing,
194 ')' | ',' | '.' | ';' | ']' => false,
195 _ => true,
196 },
197 }
198 }
199
200 fn will_it_fit(&self, text: impl AsRef<str>) -> bool {
202 let text = text.as_ref();
203 if text.is_empty() {
204 return true
205 }
206 if text.contains('\n') {
207 return false
208 }
209 let space: usize = self.next_char_needs_space(text.chars().next().unwrap()).into();
210 self.config.line_length >=
211 self.total_indent_len()
212 .saturating_add(self.current_line_len())
213 .saturating_add(text.chars().count() + space)
214 }
215
216 fn write_empty_brackets(&mut self) -> Result<()> {
219 let brackets = if self.config.bracket_spacing { "{ }" } else { "{}" };
220 write_chunk!(self, "{brackets}")?;
221 Ok(())
222 }
223
224 fn write_semicolon(&mut self) -> Result<()> {
226 write!(self.buf(), ";")?;
227 Ok(())
228 }
229
230 fn write_whitespace_separator(&mut self, multiline: bool) -> Result<()> {
233 if !self.is_beginning_of_line() {
234 write!(self.buf(), "{}", if multiline { "\n" } else { " " })?;
235 }
236 Ok(())
237 }
238
239 fn write_preserved_line(&mut self) -> Result<()> {
241 let last_indent_group_skipped = self.last_indent_group_skipped();
242 writeln!(self.buf())?;
243 self.set_last_indent_group_skipped(last_indent_group_skipped);
244 Ok(())
245 }
246
247 fn write_raw_src(&mut self, loc: Loc) -> Result<()> {
249 let disabled_stmts_src = String::from_utf8(self.source.as_bytes()[loc.range()].to_vec())
250 .map_err(FormatterError::custom)?;
251 self.write_raw(disabled_stmts_src.trim_end())?;
252 self.write_whitespace_separator(true)?;
253 let _ = self.comments.remove_all_comments_before(loc.end());
255 Ok(())
256 }
257
258 fn blank_lines(&self, start: usize, end: usize) -> usize {
260 if start > end {
262 return 0
263 }
264 self.source[start..end].trim_comments().matches('\n').count()
265 }
266
267 fn find_next_line(&self, byte_offset: usize) -> Option<usize> {
269 let mut iter = self.source[byte_offset..].char_indices();
270 while let Some((_, ch)) = iter.next() {
271 match ch {
272 '\n' => return iter.next().map(|(idx, _)| byte_offset + idx),
273 '\r' => {
274 return iter.next().and_then(|(idx, ch)| match ch {
275 '\n' => iter.next().map(|(idx, _)| byte_offset + idx),
276 _ => Some(byte_offset + idx),
277 })
278 }
279 _ => {}
280 }
281 }
282 None
283 }
284
285 fn find_next_in_src(&self, byte_offset: usize, needle: char) -> Option<usize> {
287 self.source[byte_offset..]
288 .comment_state_char_indices()
289 .position(|(state, _, ch)| needle == ch && state == CommentState::None)
290 .map(|p| byte_offset + p)
291 }
292
293 fn find_next_str_in_src(&self, byte_offset: usize, needle: &str) -> Option<usize> {
295 let subset = &self.source[byte_offset..];
296 needle.chars().next().and_then(|first_char| {
297 subset
298 .comment_state_char_indices()
299 .position(|(state, idx, ch)| {
300 first_char == ch &&
301 state == CommentState::None &&
302 idx + needle.len() <= subset.len() &&
303 subset[idx..idx + needle.len()] == *needle
304 })
305 .map(|p| byte_offset + p)
306 })
307 }
308
309 fn extend_loc_until(&self, loc: &mut Loc, needle: char) -> bool {
312 if let Some(end) = self.find_next_in_src(loc.end(), needle).map(|offset| offset + 1) {
313 *loc = loc.with_end(end);
314 true
315 } else {
316 false
317 }
318 }
319
320 fn should_attempt_block_single_line(
326 &mut self,
327 stmt: &mut Statement,
328 start_from: usize,
329 ) -> bool {
330 match self.config.single_line_statement_blocks {
331 SingleLineBlockStyle::Single => true,
332 SingleLineBlockStyle::Multi => false,
333 SingleLineBlockStyle::Preserve => {
334 let end_at = match stmt {
335 Statement::Block { statements, .. } if !statements.is_empty() => {
336 statements.first().as_ref().unwrap().loc().start()
337 }
338 Statement::Expression(loc, _) => loc.start(),
339 _ => stmt.loc().start(),
340 };
341
342 self.find_next_line(start_from).is_some_and(|loc| loc >= end_at)
343 }
344 }
345 }
346
347 fn chunk_at(
349 &mut self,
350 byte_offset: usize,
351 next_byte_offset: Option<usize>,
352 needs_space: Option<bool>,
353 content: impl std::fmt::Display,
354 ) -> Chunk {
355 Chunk {
356 postfixes_before: self.comments.remove_postfixes_before(byte_offset),
357 prefixes: self.comments.remove_prefixes_before(byte_offset),
358 content: content.to_string(),
359 postfixes: next_byte_offset
360 .map(|byte_offset| self.comments.remove_postfixes_before(byte_offset))
361 .unwrap_or_default(),
362 needs_space,
363 }
364 }
365
366 fn chunked(
368 &mut self,
369 byte_offset: usize,
370 next_byte_offset: Option<usize>,
371 mut fun: impl FnMut(&mut Self) -> Result<()>,
372 ) -> Result<Chunk> {
373 self.chunked_mono(byte_offset, next_byte_offset, &mut fun)
374 }
375
376 fn chunked_mono(
377 &mut self,
378 byte_offset: usize,
379 next_byte_offset: Option<usize>,
380 fun: &mut dyn FnMut(&mut Self) -> Result<()>,
381 ) -> Result<Chunk> {
382 let postfixes_before = self.comments.remove_postfixes_before(byte_offset);
383 let prefixes = self.comments.remove_prefixes_before(byte_offset);
384 let content = self.with_temp_buf(fun)?.w;
385 let postfixes = next_byte_offset
386 .map(|byte_offset| self.comments.remove_postfixes_before(byte_offset))
387 .unwrap_or_default();
388 Ok(Chunk { postfixes_before, prefixes, content, postfixes, needs_space: None })
389 }
390
391 fn visit_to_chunk(
393 &mut self,
394 byte_offset: usize,
395 next_byte_offset: Option<usize>,
396 visitable: &mut impl Visitable,
397 ) -> Result<Chunk> {
398 self.chunked(byte_offset, next_byte_offset, |fmt| {
399 visitable.visit(fmt)?;
400 Ok(())
401 })
402 }
403
404 fn items_to_chunks<'b>(
406 &mut self,
407 next_byte_offset: Option<usize>,
408 items: impl Iterator<Item = (Loc, &'b mut (impl Visitable + 'b))> + 'b,
409 ) -> Result<Vec<Chunk>> {
410 let mut items = items.peekable();
411 let mut out = Vec::with_capacity(items.size_hint().1.unwrap_or(0));
412 while let Some((loc, item)) = items.next() {
413 let chunk_next_byte_offset =
414 items.peek().map(|(loc, _)| loc.start()).or(next_byte_offset);
415
416 let chunk = if self.inline_config.is_disabled(loc) {
417 let mut disabled_loc = loc;
420 self.chunked(disabled_loc.start(), chunk_next_byte_offset, |fmt| {
421 while fmt.inline_config.is_disabled(disabled_loc) {
422 if let Some(next_line) = fmt.find_next_line(disabled_loc.end()) {
423 disabled_loc = disabled_loc.with_end(next_line);
424 } else {
425 break;
426 }
427 }
428 fmt.write_raw_src(disabled_loc)?;
429 Ok(())
430 })?
431 } else {
432 self.visit_to_chunk(loc.start(), chunk_next_byte_offset, item)?
433 };
434 out.push(chunk);
435 }
436 Ok(out)
437 }
438
439 fn items_to_chunks_sorted<'b>(
441 &mut self,
442 next_byte_offset: Option<usize>,
443 items: impl Iterator<Item = &'b mut (impl Visitable + CodeLocation + Ord + 'b)> + 'b,
444 ) -> Result<Vec<Chunk>> {
445 let mut items = items.peekable();
446 let mut out = Vec::with_capacity(items.size_hint().1.unwrap_or(0));
447 while let Some(item) = items.next() {
448 let chunk_next_byte_offset =
449 items.peek().map(|next| next.loc().start()).or(next_byte_offset);
450 let chunk = self.visit_to_chunk(item.loc().start(), chunk_next_byte_offset, item)?;
451 out.push((item, chunk));
452 }
453 out.sort_by(|(a, _), (b, _)| a.cmp(b));
454 Ok(out.into_iter().map(|(_, c)| c).collect())
455 }
456
457 fn write_comment(&mut self, comment: &CommentWithMetadata, is_first: bool) -> Result<()> {
461 if self.inline_config.is_disabled(comment.loc) {
462 return self.write_raw_comment(comment)
463 }
464
465 match comment.position {
466 CommentPosition::Prefix => self.write_prefix_comment(comment, is_first),
467 CommentPosition::Postfix => self.write_postfix_comment(comment),
468 }
469 }
470
471 fn write_prefix_comment(
473 &mut self,
474 comment: &CommentWithMetadata,
475 is_first: bool,
476 ) -> Result<()> {
477 if !self.is_beginning_of_line() {
478 self.write_preserved_line()?;
479 }
480 if !is_first && comment.has_newline_before {
481 self.write_preserved_line()?;
482 }
483
484 if matches!(comment.ty, CommentType::DocBlock) {
485 let mut lines = comment.contents().trim().lines();
486 writeln!(self.buf(), "{}", comment.start_token())?;
487 lines.try_for_each(|l| self.write_doc_block_line(comment, l))?;
488 write!(self.buf(), " {}", comment.end_token().unwrap())?;
489 self.write_preserved_line()?;
490 return Ok(())
491 }
492
493 write!(self.buf(), "{}", comment.start_token())?;
494
495 let mut wrapped = false;
496 let contents = comment.contents();
497 let mut lines = contents.lines().peekable();
498 while let Some(line) = lines.next() {
499 wrapped |= self.write_comment_line(comment, line)?;
500 if lines.peek().is_some() {
501 self.write_preserved_line()?;
502 }
503 }
504
505 if let Some(end) = comment.end_token() {
506 if !wrapped && comment.comment.lines().count() > contents.lines().count() {
508 self.write_preserved_line()?;
509 }
510 write!(self.buf(), "{end}")?;
511 }
512 if self.find_next_line(comment.loc.end()).is_some() {
513 self.write_preserved_line()?;
514 }
515
516 Ok(())
517 }
518
519 fn write_postfix_comment(&mut self, comment: &CommentWithMetadata) -> Result<()> {
521 let indented = self.is_beginning_of_line();
522 self.indented_if(indented, 1, |fmt| {
523 if !indented && fmt.next_char_needs_space('/') {
524 fmt.write_whitespace_separator(false)?;
525 }
526
527 write!(fmt.buf(), "{}", comment.start_token())?;
528 let start_token_pos = fmt.current_line_len();
529
530 let mut lines = comment.contents().lines().peekable();
531 fmt.grouped(|fmt| {
532 while let Some(line) = lines.next() {
533 fmt.write_comment_line(comment, line)?;
534 if lines.peek().is_some() {
535 fmt.write_whitespace_separator(true)?;
536 }
537 }
538 Ok(())
539 })?;
540
541 if let Some(end) = comment.end_token() {
542 if fmt.is_beginning_of_line() {
544 write!(fmt.buf(), "{}{end}", " ".repeat(start_token_pos))?;
545 } else {
546 write!(fmt.buf(), "{end}")?;
547 }
548 }
549
550 if comment.is_line() {
551 fmt.write_whitespace_separator(true)?;
552 }
553 Ok(())
554 })
555 }
556
557 fn write_doc_block_line(&mut self, comment: &CommentWithMetadata, line: &str) -> Result<()> {
559 if line.trim().starts_with('*') {
560 let line = line.trim().trim_start_matches('*');
561 let needs_space = line.chars().next().is_some_and(|ch| !ch.is_whitespace());
562 write!(self.buf(), " *{}", if needs_space { " " } else { "" })?;
563 self.write_comment_line(comment, line)?;
564 self.write_whitespace_separator(true)?;
565 return Ok(())
566 }
567
568 let indent_whitespace_count = line
569 .char_indices()
570 .take_while(|(idx, ch)| ch.is_whitespace() && *idx <= self.buf.current_indent_len())
571 .count();
572 let to_skip = indent_whitespace_count - indent_whitespace_count % self.config.tab_width;
573 write!(self.buf(), " *")?;
574 let content = &line[to_skip..];
575 if !content.trim().is_empty() {
576 write!(self.buf(), " ")?;
577 self.write_comment_line(comment, &line[to_skip..])?;
578 }
579 self.write_whitespace_separator(true)?;
580 Ok(())
581 }
582
583 fn write_comment_line(&mut self, comment: &CommentWithMetadata, line: &str) -> Result<bool> {
586 if self.will_it_fit(line) || !self.config.wrap_comments {
587 let start_with_ws =
588 line.chars().next().map(|ch| ch.is_whitespace()).unwrap_or_default();
589 if !self.is_beginning_of_line() || !start_with_ws {
590 write!(self.buf(), "{line}")?;
591 return Ok(false)
592 }
593
594 let indent = self.buf.current_indent_len();
597 let mut chars = line
598 .char_indices()
599 .skip_while(|(idx, ch)| ch.is_whitespace() && *idx < indent)
600 .map(|(_, ch)| ch);
601 let padded = format!("{}{}", " ".repeat(indent), chars.join(""));
602 self.write_raw(padded)?;
603 return Ok(false)
604 }
605
606 let mut words = line.split(' ').peekable();
607 while let Some(word) = words.next() {
608 if self.is_beginning_of_line() {
609 write!(self.buf(), "{}", word.trim_start())?;
610 } else {
611 self.write_raw(word)?;
612 }
613
614 if let Some(next) = words.peek() {
615 if !word.is_empty() && !self.will_it_fit(next) {
616 self.write_whitespace_separator(true)?;
619 write!(self.buf(), "{}", comment.wrap_token())?;
621 self.write_comment_line(comment, &words.join(" "))?;
622 return Ok(true)
623 }
624
625 self.write_whitespace_separator(false)?;
626 }
627 }
628 Ok(false)
629 }
630
631 fn write_raw_comment(&mut self, comment: &CommentWithMetadata) -> Result<()> {
634 self.write_raw(&comment.comment)?;
635 if comment.is_line() {
636 self.write_preserved_line()?;
637 }
638 Ok(())
639 }
640
641 fn write_comments<'b>(
644 &mut self,
645 comments: impl IntoIterator<Item = &'b CommentWithMetadata>,
646 ) -> Result<()> {
647 let mut comments = comments.into_iter().peekable();
648 let mut last_byte_written = match comments.peek() {
649 Some(comment) => comment.loc.start(),
650 None => return Ok(()),
651 };
652 let mut is_first = true;
653 for comment in comments {
654 let unwritten_whitespace_loc =
655 Loc::File(comment.loc.file_no(), last_byte_written, comment.loc.start());
656 if self.inline_config.is_disabled(unwritten_whitespace_loc) {
657 self.write_raw(&self.source[unwritten_whitespace_loc.range()])?;
658 self.write_raw_comment(comment)?;
659 last_byte_written = if comment.is_line() {
660 self.find_next_line(comment.loc.end()).unwrap_or_else(|| comment.loc.end())
661 } else {
662 comment.loc.end()
663 };
664 } else {
665 self.write_comment(comment, is_first)?;
666 }
667 is_first = false;
668 }
669 Ok(())
670 }
671
672 fn write_postfix_comments_before(&mut self, byte_end: usize) -> Result<()> {
674 let comments = self.comments.remove_postfixes_before(byte_end);
675 self.write_comments(&comments)
676 }
677
678 fn write_prefix_comments_before(&mut self, byte_end: usize) -> Result<()> {
680 let comments = self.comments.remove_prefixes_before(byte_end);
681 self.write_comments(&comments)
682 }
683
684 fn will_chunk_fit(&mut self, format_string: &str, chunk: &Chunk) -> Result<bool> {
686 if let Some(chunk_str) = self.simulate_to_single_line(|fmt| fmt.write_chunk(chunk))? {
687 Ok(self.will_it_fit(format_string.replacen("{}", &chunk_str, 1)))
688 } else {
689 Ok(false)
690 }
691 }
692
693 fn are_chunks_separated_multiline<'b>(
695 &mut self,
696 format_string: &str,
697 items: impl IntoIterator<Item = &'b Chunk>,
698 separator: &str,
699 ) -> Result<bool> {
700 let items = items.into_iter().collect_vec();
701 if let Some(chunks) = self.simulate_to_single_line(|fmt| {
702 fmt.write_chunks_separated(items.iter().copied(), separator, false)
703 })? {
704 Ok(!self.will_it_fit(format_string.replacen("{}", &chunks, 1)))
705 } else {
706 Ok(true)
707 }
708 }
709
710 fn write_chunk(&mut self, chunk: &Chunk) -> Result<()> {
715 self.write_comments(&chunk.postfixes_before)?;
717 self.write_comments(&chunk.prefixes)?;
718
719 let content = if chunk.content.starts_with('\n') {
721 let mut chunk = chunk.content.trim_start().to_string();
722 chunk.insert(0, '\n');
723 chunk
724 } else if chunk.content.starts_with(' ') {
725 let mut chunk = chunk.content.trim_start().to_string();
726 chunk.insert(0, ' ');
727 chunk
728 } else {
729 chunk.content.clone()
730 };
731
732 if !content.is_empty() {
733 let needs_space = chunk
735 .needs_space
736 .unwrap_or_else(|| self.next_char_needs_space(content.chars().next().unwrap()));
737 if needs_space {
738 if self.will_it_fit(&content) {
739 write!(self.buf(), " ")?;
740 } else {
741 writeln!(self.buf())?;
742 }
743 }
744
745 write!(self.buf(), "{content}")?;
747 }
748
749 self.write_comments(&chunk.postfixes)?;
751
752 Ok(())
753 }
754
755 fn write_chunks_separated<'b>(
758 &mut self,
759 chunks: impl IntoIterator<Item = &'b Chunk>,
760 separator: &str,
761 multiline: bool,
762 ) -> Result<()> {
763 let mut chunks = chunks.into_iter().peekable();
764 while let Some(chunk) = chunks.next() {
765 let mut chunk = chunk.clone();
766
767 self.write_comments(&std::mem::take(&mut chunk.postfixes_before))?;
769 if multiline && !self.is_beginning_of_line() {
770 writeln!(self.buf())?;
771 }
772
773 let postfixes = std::mem::take(&mut chunk.postfixes);
775
776 self.write_chunk(&chunk)?;
777
778 if chunks.peek().is_some() {
780 write!(self.buf(), "{separator}")?;
781 self.write_comments(&postfixes)?;
782 if multiline && !self.is_beginning_of_line() {
783 writeln!(self.buf())?;
784 }
785 } else {
786 self.write_comments(&postfixes)?;
787 }
788 }
789 Ok(())
790 }
791
792 fn indented(&mut self, delta: usize, fun: impl FnMut(&mut Self) -> Result<()>) -> Result<()> {
794 self.indented_if(true, delta, fun)
795 }
796
797 fn indented_if(
799 &mut self,
800 condition: bool,
801 delta: usize,
802 mut fun: impl FnMut(&mut Self) -> Result<()>,
803 ) -> Result<()> {
804 if condition {
805 self.indent(delta);
806 }
807 let res = fun(self);
808 if condition {
809 self.dedent(delta);
810 }
811 res?;
812 Ok(())
813 }
814
815 fn grouped(&mut self, mut fun: impl FnMut(&mut Self) -> Result<()>) -> Result<bool> {
818 self.start_group();
819 let res = fun(self);
820 let indented = !self.last_indent_group_skipped();
821 self.end_group();
822 res?;
823 Ok(indented)
824 }
825
826 fn with_function_context(
829 &mut self,
830 context: FunctionDefinition,
831 mut fun: impl FnMut(&mut Self) -> Result<()>,
832 ) -> Result<()> {
833 self.context.function = Some(context);
834 let res = fun(self);
835 self.context.function = None;
836 res
837 }
838
839 fn with_contract_context(
842 &mut self,
843 context: ContractDefinition,
844 mut fun: impl FnMut(&mut Self) -> Result<()>,
845 ) -> Result<()> {
846 self.context.contract = Some(context);
847 let res = fun(self);
848 self.context.contract = None;
849 res
850 }
851
852 fn transact<'b>(
855 &'b mut self,
856 fun: impl FnMut(&mut Self) -> Result<()>,
857 ) -> Result<Transaction<'b, 'a, W>> {
858 Transaction::new(self, fun)
859 }
860
861 fn simulate_to_string(&mut self, fun: impl FnMut(&mut Self) -> Result<()>) -> Result<String> {
863 Ok(self.transact(fun)?.buffer)
864 }
865
866 fn chunk_to_string(&mut self, chunk: &Chunk) -> Result<String> {
868 self.simulate_to_string(|fmt| fmt.write_chunk(chunk))
869 }
870
871 fn simulate_to_single_line(
874 &mut self,
875 mut fun: impl FnMut(&mut Self) -> Result<()>,
876 ) -> Result<Option<String>> {
877 let mut single_line = false;
878 let tx = self.transact(|fmt| {
879 fmt.restrict_to_single_line(true);
880 single_line = match fun(fmt) {
881 Ok(()) => true,
882 Err(FormatterError::Fmt(_)) => false,
883 Err(err) => bail!(err),
884 };
885 Ok(())
886 })?;
887 Ok(if single_line && tx.will_it_fit(&tx.buffer) { Some(tx.buffer) } else { None })
888 }
889
890 fn try_on_single_line(&mut self, mut fun: impl FnMut(&mut Self) -> Result<()>) -> Result<bool> {
894 let mut single_line = false;
895 let tx = self.transact(|fmt| {
896 fmt.restrict_to_single_line(true);
897 single_line = match fun(fmt) {
898 Ok(()) => true,
899 Err(FormatterError::Fmt(_)) => false,
900 Err(err) => bail!(err),
901 };
902 Ok(())
903 })?;
904 Ok(if single_line && tx.will_it_fit(&tx.buffer) {
905 tx.commit()?;
906 true
907 } else {
908 false
909 })
910 }
911
912 fn surrounded(
917 &mut self,
918 first: SurroundingChunk,
919 last: SurroundingChunk,
920 mut fun: impl FnMut(&mut Self, bool) -> Result<()>,
921 ) -> Result<()> {
922 let first_chunk =
923 self.chunk_at(first.loc_before(), first.loc_next(), first.spaced, first.content);
924 self.write_chunk(&first_chunk)?;
925
926 let multiline = !self.try_on_single_line(|fmt| {
927 fun(fmt, false)?;
928 let last_chunk =
929 fmt.chunk_at(last.loc_before(), last.loc_next(), last.spaced, &last.content);
930 fmt.write_chunk(&last_chunk)?;
931 Ok(())
932 })?;
933
934 if multiline {
935 self.indented(1, |fmt| {
936 fmt.write_whitespace_separator(true)?;
937 let stringified = fmt.with_temp_buf(|fmt| fun(fmt, true))?.w;
938 write_chunk!(fmt, "{}", stringified.trim_start())
939 })?;
940 if !last.content.trim_start().is_empty() {
941 self.indented(1, |fmt| fmt.write_whitespace_separator(true))?;
942 }
943 let last_chunk =
944 self.chunk_at(last.loc_before(), last.loc_next(), last.spaced, &last.content);
945 self.write_chunk(&last_chunk)?;
946 }
947
948 Ok(())
949 }
950
951 fn write_lined_visitable<'b, I, V, F>(
956 &mut self,
957 loc: Loc,
958 items: I,
959 needs_space_fn: F,
960 ) -> Result<()>
961 where
962 I: Iterator<Item = &'b mut V> + 'b,
963 V: Visitable + CodeLocation + 'b,
964 F: Fn(&V, &V) -> bool,
965 {
966 let mut items = items.collect::<Vec<_>>();
967 items.reverse();
968 let pop_next = |fmt: &mut Self, items: &mut Vec<&'b mut V>| {
970 let comment =
971 fmt.comments.iter().next().filter(|comment| comment.loc.end() < loc.end());
972 let item = items.last();
973 if let (Some(comment), Some(item)) = (comment, item) {
974 if comment.loc < item.loc() {
975 Some(Either::Left(fmt.comments.pop().unwrap()))
976 } else {
977 Some(Either::Right(items.pop().unwrap()))
978 }
979 } else if comment.is_some() {
980 Some(Either::Left(fmt.comments.pop().unwrap()))
981 } else if item.is_some() {
982 Some(Either::Right(items.pop().unwrap()))
983 } else {
984 None
985 }
986 };
987 let unwritten_whitespace = |from: usize, to: usize| {
990 let to = to.max(from);
991 let mut loc = Loc::File(loc.file_no(), from, to);
992 let src = &self.source[from..to];
993 if let Some(semi) = src.find(';') {
994 loc = loc.with_start(from + semi + 1);
995 }
996 (loc, &self.source[loc.range()])
997 };
998
999 let mut last_byte_written = match (
1000 self.comments.iter().next().filter(|comment| comment.loc.end() < loc.end()),
1001 items.last(),
1002 ) {
1003 (Some(comment), Some(item)) => comment.loc.min(item.loc()),
1004 (None, Some(item)) => item.loc(),
1005 (Some(comment), None) => comment.loc,
1006 (None, None) => return Ok(()),
1007 }
1008 .start();
1009
1010 let mut last_loc: Option<Loc> = None;
1011 let mut visited_locs: Vec<Loc> = Vec::new();
1012
1013 let mut needs_space = false;
1015 let mut last_comment = None;
1016
1017 while let Some(mut line_item) = pop_next(self, &mut items) {
1018 let loc = line_item.as_ref().either(|c| c.loc, |i| i.loc());
1019 let (unwritten_whitespace_loc, unwritten_whitespace) =
1020 unwritten_whitespace(last_byte_written, loc.start());
1021 let ignore_whitespace = if self.inline_config.is_disabled(unwritten_whitespace_loc) {
1022 trace!("Unwritten whitespace: {unwritten_whitespace:?}");
1023 self.write_raw(unwritten_whitespace)?;
1024 true
1025 } else {
1026 false
1027 };
1028 match line_item.as_mut() {
1029 Either::Left(comment) => {
1030 if ignore_whitespace {
1031 self.write_raw_comment(comment)?;
1032 if unwritten_whitespace.contains('\n') {
1033 needs_space = false;
1034 }
1035 } else {
1036 self.write_comment(comment, last_loc.is_none())?;
1037 if last_loc.is_some() && comment.has_newline_before {
1038 needs_space = false;
1039 }
1040 }
1041 }
1042 Either::Right(item) => {
1043 if !ignore_whitespace {
1044 self.write_whitespace_separator(true)?;
1045 if let Some(mut last_loc) = last_loc {
1046 if let Some(last_item) = visited_locs
1051 .iter()
1052 .rev()
1053 .find(|prev_item| prev_item.start() > last_loc.end())
1054 {
1055 last_loc = *last_item;
1056 }
1057
1058 let is_last_doc_comment = matches!(
1063 last_comment,
1064 Some(CommentWithMetadata { ty: CommentType::DocBlock, .. })
1065 );
1066
1067 if needs_space ||
1068 (!is_last_doc_comment &&
1069 self.blank_lines(last_loc.end(), loc.start()) > 1)
1070 {
1071 writeln!(self.buf())?;
1072 }
1073 }
1074 }
1075 if let Some(next_item) = items.last() {
1076 needs_space = needs_space_fn(item, next_item);
1077 }
1078 trace!("Visiting {}", {
1079 let n = std::any::type_name::<V>();
1080 n.strip_prefix("solang_parser::pt::").unwrap_or(n)
1081 });
1082 item.visit(self)?;
1083 }
1084 }
1085
1086 last_loc = Some(loc);
1087 visited_locs.push(loc);
1088
1089 last_comment = None;
1090
1091 last_byte_written = loc.end();
1092 if let Some(comment) = line_item.left() {
1093 if comment.is_line() {
1094 last_byte_written =
1095 self.find_next_line(last_byte_written).unwrap_or(last_byte_written);
1096 }
1097 last_comment = Some(comment);
1098 }
1099 }
1100
1101 let comments = self.comments.remove_prefixes_before(loc.end());
1103 for comment in comments {
1104 self.write_comment(&comment, false)?;
1105 }
1106
1107 let (unwritten_src_loc, mut unwritten_whitespace) =
1108 unwritten_whitespace(last_byte_written, loc.end());
1109 if self.inline_config.is_disabled(unwritten_src_loc) {
1110 if unwritten_src_loc.end() == self.source.len() {
1111 unwritten_whitespace = unwritten_whitespace
1113 .strip_suffix('\n')
1114 .map(|w| w.strip_suffix('\r').unwrap_or(w))
1115 .unwrap_or(unwritten_whitespace);
1116 }
1117 trace!("Unwritten whitespace: {unwritten_whitespace:?}");
1118 self.write_raw(unwritten_whitespace)?;
1119 }
1120
1121 Ok(())
1122 }
1123
1124 fn visit_assignment(&mut self, expr: &mut Expression) -> Result<()> {
1128 if self.try_on_single_line(|fmt| expr.visit(fmt))? {
1129 return Ok(())
1130 }
1131
1132 self.write_postfix_comments_before(expr.loc().start())?;
1133 self.write_prefix_comments_before(expr.loc().start())?;
1134
1135 if self.try_on_single_line(|fmt| fmt.indented(1, |fmt| expr.visit(fmt)))? {
1136 return Ok(())
1137 }
1138
1139 let mut fit_on_next_line = false;
1140 self.indented(1, |fmt| {
1141 let tx = fmt.transact(|fmt| {
1142 writeln!(fmt.buf())?;
1143 fit_on_next_line = fmt.try_on_single_line(|fmt| expr.visit(fmt))?;
1144 Ok(())
1145 })?;
1146 if fit_on_next_line {
1147 tx.commit()?;
1148 }
1149 Ok(())
1150 })?;
1151
1152 if !fit_on_next_line {
1153 self.indented_if(expr.is_unsplittable(), 1, |fmt| expr.visit(fmt))?;
1154 }
1155
1156 Ok(())
1157 }
1158
1159 fn visit_list<T>(
1163 &mut self,
1164 prefix: &str,
1165 items: &mut [T],
1166 start_offset: Option<usize>,
1167 end_offset: Option<usize>,
1168 paren_required: bool,
1169 ) -> Result<()>
1170 where
1171 T: Visitable + CodeLocation,
1172 {
1173 write_chunk!(self, "{}", prefix)?;
1174 let whitespace = if !prefix.is_empty() { " " } else { "" };
1175 let next_after_start_offset = items.first().map(|item| item.loc().start());
1176 let first_surrounding = SurroundingChunk::new("", start_offset, next_after_start_offset);
1177 let last_surronding = SurroundingChunk::new(")", None, end_offset);
1178 if items.is_empty() {
1179 if paren_required {
1180 write!(self.buf(), "{whitespace}(")?;
1181 self.surrounded(first_surrounding, last_surronding, |fmt, _| {
1182 write_chunk!(fmt, end_offset.unwrap_or_default(), "")?;
1184 Ok(())
1185 })?;
1186 }
1187 } else {
1188 write!(self.buf(), "{whitespace}(")?;
1189 self.surrounded(first_surrounding, last_surronding, |fmt, multiline| {
1190 let args =
1191 fmt.items_to_chunks(end_offset, items.iter_mut().map(|arg| (arg.loc(), arg)))?;
1192 let multiline =
1193 multiline && fmt.are_chunks_separated_multiline("{}", &args, ",")?;
1194 fmt.write_chunks_separated(&args, ",", multiline)?;
1195 Ok(())
1196 })?;
1197 }
1198 Ok(())
1199 }
1200
1201 fn visit_block<T>(
1206 &mut self,
1207 loc: Loc,
1208 statements: &mut [T],
1209 attempt_single_line: bool,
1210 attempt_omit_braces: bool,
1211 ) -> Result<bool>
1212 where
1213 T: Visitable + CodeLocation,
1214 {
1215 if attempt_single_line && statements.len() == 1 {
1216 let fits_on_single = self.try_on_single_line(|fmt| {
1217 if !attempt_omit_braces {
1218 write!(fmt.buf(), "{{ ")?;
1219 }
1220 statements.first_mut().unwrap().visit(fmt)?;
1221 if !attempt_omit_braces {
1222 write!(fmt.buf(), " }}")?;
1223 }
1224 Ok(())
1225 })?;
1226
1227 if fits_on_single {
1228 return Ok(true)
1229 }
1230 }
1231
1232 let is_start_disabled = self.inline_config.is_disabled(loc.with_end(loc.start()));
1234 let is_end_disabled = self.inline_config.is_disabled(loc.with_start(loc.end()));
1235 let end_of_first_line = self.find_next_line(loc.start()).unwrap_or_default();
1236 let end_of_last_line = self.find_next_line(loc.end()).unwrap_or_default();
1237
1238 if is_start_disabled {
1242 self.write_raw_src(loc.with_end(end_of_first_line))?;
1243 } else {
1244 write_chunk!(self, "{{")?;
1245 }
1246
1247 if statements.is_empty() {
1249 self.indented(1, |fmt| {
1250 fmt.write_prefix_comments_before(loc.end())?;
1251 fmt.write_postfix_comments_before(loc.end())?;
1252 Ok(())
1253 })?;
1254
1255 write_chunk!(self, "}}")?;
1256 return Ok(false)
1257 }
1258
1259 let writable_statments = match (
1263 statements.iter().rposition(|stmt| {
1264 is_start_disabled &&
1265 self.find_next_line(stmt.loc().end()).unwrap_or_default() ==
1266 end_of_first_line
1267 }),
1268 statements.iter().position(|stmt| {
1269 is_end_disabled &&
1270 self.find_next_line(stmt.loc().end()).unwrap_or_default() == end_of_last_line
1271 }),
1272 ) {
1273 (Some(start), Some(end)) => {
1275 if start == end || start + 1 == end {
1276 None
1277 } else {
1278 Some(&mut statements[start + 1..end])
1279 }
1280 }
1281 (Some(start), None) => {
1283 if start + 1 == statements.len() {
1284 None
1285 } else {
1286 Some(&mut statements[start + 1..])
1287 }
1288 }
1289 (None, Some(end)) => {
1291 if end == 0 {
1292 None
1293 } else {
1294 Some(&mut statements[..end])
1295 }
1296 }
1297 (None, None) => Some(statements),
1299 };
1300
1301 let mut statements_loc = loc;
1303 if let Some(writable_statements) = writable_statments {
1304 if let Some(first_statement) = writable_statements.first() {
1305 statements_loc = statements_loc.with_start(first_statement.loc().start());
1306 self.write_whitespace_separator(true)?;
1307 self.write_postfix_comments_before(statements_loc.start())?;
1308 }
1309 if is_end_disabled {
1311 if let Some(last_statement) = writable_statements.last() {
1312 statements_loc = statements_loc.with_end(
1313 self.find_next_line(last_statement.loc().end()).unwrap_or_default(),
1314 );
1315 }
1316 }
1317 self.indented(1, |fmt| {
1318 fmt.write_lined_visitable(
1319 statements_loc,
1320 writable_statements.iter_mut(),
1321 |_, _| false,
1322 )?;
1323 Ok(())
1324 })?;
1325 self.write_whitespace_separator(true)?;
1326 }
1327
1328 if is_end_disabled {
1333 self.write_raw_src(loc.with_start(statements_loc.end()).with_end(end_of_last_line))?;
1334 } else {
1335 if end_of_first_line != end_of_last_line {
1336 self.write_whitespace_separator(true)?;
1337 }
1338 write_chunk!(self, loc.end(), "}}")?;
1339 }
1340
1341 Ok(false)
1342 }
1343
1344 fn visit_stmt_as_block(
1346 &mut self,
1347 stmt: &mut Statement,
1348 attempt_single_line: bool,
1349 ) -> Result<bool> {
1350 match stmt {
1351 Statement::Block { loc, statements, .. } => {
1352 self.visit_block(*loc, statements, attempt_single_line, true)
1353 }
1354 _ => self.visit_block(stmt.loc(), &mut [stmt], attempt_single_line, true),
1355 }
1356 }
1357
1358 fn visit_member_access<'b, T, M>(
1362 &mut self,
1363 expr: &'b mut Box<T>,
1364 ident: &mut Identifier,
1365 mut matcher: M,
1366 ) -> Result<()>
1367 where
1368 T: CodeLocation + Visitable,
1369 M: FnMut(&mut Self, &'b mut Box<T>) -> Result<Option<(&'b mut Box<T>, &'b mut Identifier)>>,
1370 {
1371 let chunk_member_access = |fmt: &mut Self, ident: &mut Identifier, expr: &mut Box<T>| {
1372 fmt.chunked(ident.loc.start(), Some(expr.loc().start()), |fmt| ident.visit(fmt))
1373 };
1374
1375 let mut chunks: Vec<Chunk> = vec![chunk_member_access(self, ident, expr)?];
1376 let mut remaining = expr;
1377 while let Some((inner_expr, inner_ident)) = matcher(self, remaining)? {
1378 chunks.push(chunk_member_access(self, inner_ident, inner_expr)?);
1379 remaining = inner_expr;
1380 }
1381
1382 chunks.reverse();
1383 chunks.iter_mut().for_each(|chunk| chunk.content.insert(0, '.'));
1384
1385 if !self.try_on_single_line(|fmt| fmt.write_chunks_separated(&chunks, "", false))? {
1386 self.grouped(|fmt| fmt.write_chunks_separated(&chunks, "", true))?;
1387 }
1388 Ok(())
1389 }
1390
1391 fn visit_yul_string_with_ident(
1396 &mut self,
1397 loc: Loc,
1398 val: &str,
1399 ident: &mut Option<Identifier>,
1400 ) -> Result<()> {
1401 let ident =
1402 if let Some(ident) = ident { format!(":{}", ident.name) } else { String::new() };
1403 write_chunk!(self, loc.start(), loc.end(), "{val}{ident}")?;
1404 Ok(())
1405 }
1406
1407 fn quote_str(&self, loc: Loc, prefix: Option<&str>, string: &str) -> String {
1410 let get_og_quote = || {
1411 self.source[loc.range()]
1412 .quote_state_char_indices()
1413 .find_map(
1414 |(state, _, ch)| {
1415 if matches!(state, QuoteState::Opening(_)) {
1416 Some(ch)
1417 } else {
1418 None
1419 }
1420 },
1421 )
1422 .expect("Could not find quote character for quoted string")
1423 };
1424 let mut quote = self.config.quote_style.quote().unwrap_or_else(get_og_quote);
1425 let mut quoted = format!("{quote}{string}{quote}");
1426 if !quoted.is_quoted() {
1427 quote = get_og_quote();
1428 quoted = format!("{quote}{string}{quote}");
1429 }
1430 let prefix = prefix.unwrap_or("");
1431 format!("{prefix}{quoted}")
1432 }
1433
1434 fn write_quoted_str(&mut self, loc: Loc, prefix: Option<&str>, string: &str) -> Result<()> {
1436 write_chunk!(self, loc.start(), loc.end(), "{}", self.quote_str(loc, prefix, string))
1437 }
1438
1439 fn write_num_literal(
1442 &mut self,
1443 loc: Loc,
1444 value: &str,
1445 fractional: Option<&str>,
1446 exponent: &str,
1447 unit: &mut Option<Identifier>,
1448 ) -> Result<()> {
1449 let config = self.config.number_underscore;
1450
1451 let (value, fractional, exponent) = if config.is_preserve() {
1453 let source = &self.source[loc.start()..loc.end()];
1454 let (source, _) = source.split_once(' ').unwrap_or((source, ""));
1456 let (val, exp) = source.split_once(['e', 'E']).unwrap_or((source, ""));
1457 let (val, fract) =
1458 val.split_once('.').map(|(val, fract)| (val, Some(fract))).unwrap_or((val, None));
1459 (
1460 val.trim().to_string(),
1461 fract.map(|fract| fract.trim().to_string()),
1462 exp.trim().to_string(),
1463 )
1464 } else {
1465 (
1467 value.trim().replace('_', ""),
1468 fractional.map(|fract| fract.trim().replace('_', "")),
1469 exponent.trim().replace('_', ""),
1470 )
1471 };
1472
1473 let val = value.trim_start_matches('0');
1475 let fract = fractional.as_ref().map(|fract| fract.trim_end_matches('0'));
1476 let (exp_sign, mut exp) = if let Some(exp) = exponent.strip_prefix('-') {
1477 ("-", exp)
1478 } else {
1479 ("", exponent.as_str())
1480 };
1481 exp = exp.trim().trim_start_matches('0');
1482
1483 let add_underscores = |string: &str, reversed: bool| -> String {
1484 if !config.is_thousands() || string.len() < 5 {
1485 return string.to_string()
1486 }
1487 if reversed {
1488 Box::new(string.as_bytes().chunks(3)) as Box<dyn Iterator<Item = &[u8]>>
1489 } else {
1490 Box::new(string.as_bytes().rchunks(3).rev()) as Box<dyn Iterator<Item = &[u8]>>
1491 }
1492 .map(|chunk| std::str::from_utf8(chunk).expect("valid utf8 content."))
1493 .collect::<Vec<_>>()
1494 .join("_")
1495 };
1496
1497 let mut out = String::new();
1498 if val.is_empty() {
1499 out.push('0');
1500 } else {
1501 out.push_str(&add_underscores(val, false));
1502 }
1503 if let Some(fract) = fract {
1504 out.push('.');
1505 if fract.is_empty() {
1506 out.push('0');
1507 } else {
1508 out.push_str(fract)
1513 }
1514 }
1515 if !exp.is_empty() {
1516 out.push('e');
1517 out.push_str(exp_sign);
1518 out.push_str(&add_underscores(exp, false));
1519 }
1520
1521 write_chunk!(self, loc.start(), loc.end(), "{out}")?;
1522 self.write_unit(unit)
1523 }
1524
1525 fn write_hex_literal(&mut self, lit: &HexLiteral) -> Result<()> {
1527 let HexLiteral { loc, hex } = lit;
1528 match self.config.hex_underscore {
1529 HexUnderscore::Remove => self.write_quoted_str(*loc, Some("hex"), hex),
1530 HexUnderscore::Preserve => {
1531 let quote = &self.source[loc.start()..loc.end()].trim_start_matches("hex");
1532 let hex = "e[1..quote.len() - 1];
1535 self.write_quoted_str(*loc, Some("hex"), hex)
1536 }
1537 HexUnderscore::Bytes => {
1538 let hex = hex
1540 .chars()
1541 .chunks(2)
1542 .into_iter()
1543 .map(|chunk| chunk.collect::<String>())
1544 .collect::<Vec<_>>()
1545 .join("_");
1546 self.write_quoted_str(*loc, Some("hex"), &hex)
1547 }
1548 }
1549 }
1550
1551 fn write_unit(&mut self, unit: &mut Option<Identifier>) -> Result<()> {
1553 if let Some(unit) = unit {
1554 write_chunk!(self, unit.loc.start(), unit.loc.end(), "{}", unit.name)?;
1555 }
1556 Ok(())
1557 }
1558
1559 fn write_function_header(
1561 &mut self,
1562 func: &mut FunctionDefinition,
1563 body_loc: Option<Loc>,
1564 header_multiline: bool,
1565 ) -> Result<bool> {
1566 let func_name = if let Some(ident) = &func.name {
1567 format!("{} {}", func.ty, ident.name)
1568 } else {
1569 func.ty.to_string()
1570 };
1571
1572 let attrs_loc = func.attributes.first().map(|attr| attr.loc());
1574 let returns_loc = func.returns.first().map(|param| param.0);
1575
1576 let params_next_offset = attrs_loc
1577 .as_ref()
1578 .or(returns_loc.as_ref())
1579 .or(body_loc.as_ref())
1580 .map(|loc| loc.start());
1581 let attrs_end = returns_loc.as_ref().or(body_loc.as_ref()).map(|loc| loc.start());
1582 let returns_end = body_loc.as_ref().map(|loc| loc.start());
1583
1584 let mut params_multiline = false;
1585
1586 let params_loc = {
1587 let mut loc = func.loc.with_end(func.loc.start());
1588 self.extend_loc_until(&mut loc, ')');
1589 loc
1590 };
1591 let params_disabled = self.inline_config.is_disabled(params_loc);
1592 if params_disabled {
1593 let chunk = self.chunked(func.loc.start(), None, |fmt| fmt.visit_source(params_loc))?;
1594 params_multiline = chunk.content.contains('\n');
1595 self.write_chunk(&chunk)?;
1596 } else {
1597 let first_surrounding = SurroundingChunk::new(
1598 format!("{func_name}("),
1599 Some(func.loc.start()),
1600 Some(
1601 func.params
1602 .first()
1603 .map(|param| param.0.start())
1604 .unwrap_or_else(|| params_loc.end()),
1605 ),
1606 );
1607 self.surrounded(
1608 first_surrounding,
1609 SurroundingChunk::new(")", None, params_next_offset),
1610 |fmt, multiline| {
1611 let params = fmt.items_to_chunks(
1612 params_next_offset,
1613 func.params
1614 .iter_mut()
1615 .filter_map(|(loc, param)| param.as_mut().map(|param| (*loc, param))),
1616 )?;
1617 let after_params = if !func.attributes.is_empty() || !func.returns.is_empty() {
1618 ""
1619 } else if func.body.is_some() {
1620 " {"
1621 } else {
1622 ";"
1623 };
1624 let should_multiline = header_multiline &&
1625 matches!(
1626 fmt.config.multiline_func_header,
1627 MultilineFuncHeaderStyle::ParamsFirst |
1628 MultilineFuncHeaderStyle::ParamsFirstMulti |
1629 MultilineFuncHeaderStyle::All |
1630 MultilineFuncHeaderStyle::AllParams
1631 );
1632 params_multiline = should_multiline ||
1633 multiline ||
1634 fmt.are_chunks_separated_multiline(
1635 &format!("{{}}){after_params}"),
1636 ¶ms,
1637 ",",
1638 )?;
1639 let single_param_multiline = matches!(
1642 fmt.config.multiline_func_header,
1643 MultilineFuncHeaderStyle::ParamsFirst
1644 ) || params_multiline &&
1645 matches!(
1646 fmt.config.multiline_func_header,
1647 MultilineFuncHeaderStyle::AllParams
1648 );
1649 if params.len() == 1 && single_param_multiline {
1650 writeln!(fmt.buf())?;
1651 }
1652 fmt.write_chunks_separated(¶ms, ",", params_multiline)?;
1653 Ok(())
1654 },
1655 )?;
1656 }
1657
1658 let mut write_attributes = |fmt: &mut Self, multiline: bool| -> Result<()> {
1659 if !func.attributes.is_empty() {
1661 let attrs_loc = func
1662 .attributes
1663 .first()
1664 .unwrap()
1665 .loc()
1666 .with_end_from(&func.attributes.last().unwrap().loc());
1667 if fmt.inline_config.is_disabled(attrs_loc) {
1668 if params_disabled {
1670 fmt.write_whitespace_separator(false)?;
1671 let attrs_src =
1672 String::from_utf8(self.source.as_bytes()[attrs_loc.range()].to_vec())
1673 .map_err(FormatterError::custom)?;
1674 fmt.write_raw(attrs_src)?;
1675 } else {
1676 fmt.indented(1, |fmt| fmt.visit_source(attrs_loc))?;
1677 }
1678 } else {
1679 fmt.write_postfix_comments_before(attrs_loc.start())?;
1680 fmt.write_whitespace_separator(multiline)?;
1681 let attributes =
1682 fmt.items_to_chunks_sorted(attrs_end, func.attributes.iter_mut())?;
1683 fmt.indented(1, |fmt| {
1684 fmt.write_chunks_separated(&attributes, "", multiline)?;
1685 Ok(())
1686 })?;
1687 }
1688 }
1689
1690 if !func.returns.is_empty() {
1692 let returns_start_loc = func.returns.first().unwrap().0;
1693 let returns_loc = returns_start_loc.with_end_from(&func.returns.last().unwrap().0);
1694 if fmt.inline_config.is_disabled(returns_loc) {
1695 fmt.write_whitespace_separator(false)?;
1696 let returns_src =
1697 String::from_utf8(self.source.as_bytes()[returns_loc.range()].to_vec())
1698 .map_err(FormatterError::custom)?;
1699 fmt.write_raw(format!("returns ({returns_src})"))?;
1700 } else {
1701 let mut returns = fmt.items_to_chunks(
1702 returns_end,
1703 func.returns
1704 .iter_mut()
1705 .filter_map(|(loc, param)| param.as_mut().map(|param| (*loc, param))),
1706 )?;
1707
1708 for function_chunk in
1710 returns.iter_mut().filter(|chunk| chunk.content.starts_with("function("))
1711 {
1712 function_chunk.content = function_chunk
1715 .content
1716 .split('\n')
1717 .map(|s| s.trim_start())
1718 .collect::<Vec<_>>()
1719 .join("\n");
1720 }
1721
1722 fmt.write_postfix_comments_before(returns_loc.start())?;
1723 fmt.write_whitespace_separator(multiline)?;
1724 fmt.indented(1, |fmt| {
1725 fmt.surrounded(
1726 SurroundingChunk::new("returns (", Some(returns_loc.start()), None),
1727 SurroundingChunk::new(")", None, returns_end),
1728 |fmt, multiline_hint| {
1729 fmt.write_chunks_separated(&returns, ",", multiline_hint)?;
1730 Ok(())
1731 },
1732 )?;
1733 Ok(())
1734 })?;
1735 }
1736 }
1737 Ok(())
1738 };
1739
1740 let should_multiline = header_multiline &&
1741 if params_multiline {
1742 matches!(
1743 self.config.multiline_func_header,
1744 MultilineFuncHeaderStyle::All | MultilineFuncHeaderStyle::AllParams
1745 )
1746 } else {
1747 matches!(
1748 self.config.multiline_func_header,
1749 MultilineFuncHeaderStyle::AttributesFirst
1750 )
1751 };
1752 let attrs_multiline = should_multiline ||
1753 !self.try_on_single_line(|fmt| {
1754 write_attributes(fmt, false)?;
1755 if !fmt.will_it_fit(if func.body.is_some() { " {" } else { ";" }) {
1756 bail!(FormatterError::fmt())
1757 }
1758 Ok(())
1759 })?;
1760 if attrs_multiline {
1761 write_attributes(self, true)?;
1762 }
1763 Ok(attrs_multiline)
1764 }
1765
1766 fn write_if_stmt(
1768 &mut self,
1769 loc: Loc,
1770 cond: &mut Expression,
1771 if_branch: &mut Box<Statement>,
1772 else_branch: &mut Option<Box<Statement>>,
1773 ) -> Result<(), FormatterError> {
1774 let single_line_stmt_wide = self.context.if_stmt_single_line.unwrap_or_default();
1775
1776 visit_source_if_disabled_else!(self, loc.with_end(if_branch.loc().start()), {
1777 self.surrounded(
1778 SurroundingChunk::new("if (", Some(loc.start()), Some(cond.loc().start())),
1779 SurroundingChunk::new(")", None, Some(if_branch.loc().start())),
1780 |fmt, _| {
1781 fmt.write_prefix_comments_before(cond.loc().end())?;
1782 cond.visit(fmt)?;
1783 fmt.write_postfix_comments_before(if_branch.loc().start())
1784 },
1785 )?;
1786 });
1787
1788 let cond_close_paren_loc =
1789 self.find_next_in_src(cond.loc().end(), ')').unwrap_or_else(|| cond.loc().end());
1790 let attempt_single_line = single_line_stmt_wide &&
1791 self.should_attempt_block_single_line(if_branch.as_mut(), cond_close_paren_loc);
1792 let if_branch_is_single_line = self.visit_stmt_as_block(if_branch, attempt_single_line)?;
1793 if single_line_stmt_wide && !if_branch_is_single_line {
1794 bail!(FormatterError::fmt())
1795 }
1796
1797 if let Some(else_branch) = else_branch {
1798 self.write_postfix_comments_before(else_branch.loc().start())?;
1799 if if_branch_is_single_line {
1800 writeln!(self.buf())?;
1801 }
1802 write_chunk!(self, else_branch.loc().start(), "else")?;
1803 if let Statement::If(loc, cond, if_branch, else_branch) = else_branch.as_mut() {
1804 self.visit_if(*loc, cond, if_branch, else_branch, false)?;
1805 } else {
1806 let else_branch_is_single_line =
1807 self.visit_stmt_as_block(else_branch, attempt_single_line)?;
1808 if single_line_stmt_wide && !else_branch_is_single_line {
1809 bail!(FormatterError::fmt())
1810 }
1811 }
1812 }
1813 Ok(())
1814 }
1815
1816 fn sort_imports(&self, source_unit: &mut SourceUnit) {
1818 let mut import_groups = Vec::new();
1821 let mut current_group = Vec::new();
1822 let mut source_unit_parts = source_unit.0.iter().enumerate().peekable();
1823 while let Some((i, part)) = source_unit_parts.next() {
1824 if let SourceUnitPart::ImportDirective(_) = part {
1825 current_group.push(i);
1826 let current_loc = part.loc();
1827 if let Some((_, next_part)) = source_unit_parts.peek() {
1828 let next_loc = next_part.loc();
1829 if self.blank_lines(current_loc.end(), next_loc.start()) > 1 {
1832 import_groups.push(std::mem::take(&mut current_group));
1833 }
1834 }
1835 } else if !current_group.is_empty() {
1836 import_groups.push(std::mem::take(&mut current_group));
1837 }
1838 }
1839
1840 if !current_group.is_empty() {
1841 import_groups.push(current_group);
1842 }
1843
1844 if import_groups.is_empty() {
1845 return
1847 }
1848
1849 for group in &import_groups {
1851 let first = group[0];
1853 let last = group.last().copied().expect("group is not empty");
1854 let import_directives = &mut source_unit.0[first..=last];
1855
1856 for source_unit_part in import_directives.iter_mut() {
1859 if let SourceUnitPart::ImportDirective(Import::Rename(_, renames, _)) =
1860 source_unit_part
1861 {
1862 renames.sort_by_cached_key(|(og_ident, _)| og_ident.name.clone());
1863 }
1864 }
1865
1866 import_directives.sort_by_cached_key(|item| match item {
1867 SourceUnitPart::ImportDirective(import) => match import {
1868 Import::Plain(path, _) => path.to_string(),
1869 Import::GlobalSymbol(path, _, _) => path.to_string(),
1870 Import::Rename(path, _, _) => path.to_string(),
1871 },
1872 _ => {
1873 unreachable!("import group contains non-import statement")
1874 }
1875 });
1876 }
1877 }
1878}
1879
1880impl<W: Write> Visitor for Formatter<'_, W> {
1882 type Error = FormatterError;
1883
1884 #[instrument(name = "source", skip(self))]
1885 fn visit_source(&mut self, loc: Loc) -> Result<()> {
1886 let source = String::from_utf8(self.source.as_bytes()[loc.range()].to_vec())
1887 .map_err(FormatterError::custom)?;
1888 let mut lines = source.splitn(2, '\n');
1889
1890 write_chunk!(self, loc.start(), "{}", lines.next().unwrap())?;
1891 if let Some(remainder) = lines.next() {
1892 self.write_raw(format!("\n{remainder}"))?;
1895 }
1896
1897 let _ = self.comments.remove_all_comments_before(loc.end());
1898
1899 Ok(())
1900 }
1901
1902 #[instrument(name = "SU", skip_all)]
1903 fn visit_source_unit(&mut self, source_unit: &mut SourceUnit) -> Result<()> {
1904 if self.config.sort_imports {
1905 self.sort_imports(source_unit);
1906 }
1907 let loc = Loc::File(
1914 source_unit
1915 .loc_opt()
1916 .or_else(|| self.comments.iter().next().map(|comment| comment.loc))
1917 .map(|loc| loc.file_no())
1918 .unwrap_or_default(),
1919 0,
1920 self.source.len(),
1921 );
1922
1923 self.write_lined_visitable(
1924 loc,
1925 source_unit.0.iter_mut(),
1926 |last_unit, unit| match last_unit {
1927 SourceUnitPart::PragmaDirective(..) => {
1928 !matches!(unit, SourceUnitPart::PragmaDirective(..))
1929 }
1930 SourceUnitPart::ImportDirective(_) => {
1931 !matches!(unit, SourceUnitPart::ImportDirective(_))
1932 }
1933 SourceUnitPart::ErrorDefinition(_) => {
1934 !matches!(unit, SourceUnitPart::ErrorDefinition(_))
1935 }
1936 SourceUnitPart::Using(_) => !matches!(unit, SourceUnitPart::Using(_)),
1937 SourceUnitPart::VariableDefinition(_) => {
1938 !matches!(unit, SourceUnitPart::VariableDefinition(_))
1939 }
1940 SourceUnitPart::Annotation(_) => false,
1941 _ => true,
1942 },
1943 )?;
1944
1945 if self.last_char() != Some('\n') {
1947 writeln!(self.buf())?;
1948 }
1949
1950 Ok(())
1951 }
1952
1953 #[instrument(name = "contract", skip_all)]
1954 fn visit_contract(&mut self, contract: &mut ContractDefinition) -> Result<()> {
1955 return_source_if_disabled!(self, contract.loc);
1956
1957 self.with_contract_context(contract.clone(), |fmt| {
1958 let contract_name = contract.name.safe_unwrap();
1959
1960 visit_source_if_disabled_else!(
1961 fmt,
1962 contract.loc.with_end_from(
1963 &contract.base.first().map(|b| b.loc).unwrap_or(contract_name.loc)
1964 ),
1965 {
1966 fmt.grouped(|fmt| {
1967 write_chunk!(fmt, contract.loc.start(), "{}", contract.ty)?;
1968 write_chunk!(fmt, contract_name.loc.end(), "{}", contract_name.name)?;
1969 if !contract.base.is_empty() {
1970 write_chunk!(
1971 fmt,
1972 contract_name.loc.end(),
1973 contract.base.first().unwrap().loc.start(),
1974 "is"
1975 )?;
1976 }
1977 Ok(())
1978 })?;
1979 }
1980 );
1981
1982 if !contract.base.is_empty() {
1983 visit_source_if_disabled_else!(
1984 fmt,
1985 contract
1986 .base
1987 .first()
1988 .unwrap()
1989 .loc
1990 .with_end_from(&contract.base.last().unwrap().loc),
1991 {
1992 fmt.indented(1, |fmt| {
1993 let base_end = contract.parts.first().map(|part| part.loc().start());
1994 let bases = fmt.items_to_chunks(
1995 base_end,
1996 contract.base.iter_mut().map(|base| (base.loc, base)),
1997 )?;
1998 let multiline =
1999 fmt.are_chunks_separated_multiline("{}", &bases, ",")?;
2000 fmt.write_chunks_separated(&bases, ",", multiline)?;
2001 fmt.write_whitespace_separator(multiline)?;
2002 Ok(())
2003 })?;
2004 }
2005 );
2006 }
2007
2008 write_chunk!(fmt, "{{")?;
2009
2010 fmt.indented(1, |fmt| {
2011 if let Some(first) = contract.parts.first() {
2012 fmt.write_postfix_comments_before(first.loc().start())?;
2013 fmt.write_whitespace_separator(true)?;
2014 } else {
2015 return Ok(())
2016 }
2017
2018 if fmt.config.contract_new_lines {
2019 write_chunk!(fmt, "\n")?;
2020 }
2021
2022 fmt.write_lined_visitable(
2023 contract.loc,
2024 contract.parts.iter_mut(),
2025 |last_part, part| match last_part {
2026 ContractPart::ErrorDefinition(_) => {
2027 !matches!(part, ContractPart::ErrorDefinition(_))
2028 }
2029 ContractPart::EventDefinition(_) => {
2030 !matches!(part, ContractPart::EventDefinition(_))
2031 }
2032 ContractPart::VariableDefinition(_) => {
2033 !matches!(part, ContractPart::VariableDefinition(_))
2034 }
2035 ContractPart::TypeDefinition(_) => {
2036 !matches!(part, ContractPart::TypeDefinition(_))
2037 }
2038 ContractPart::EnumDefinition(_) => {
2039 !matches!(part, ContractPart::EnumDefinition(_))
2040 }
2041 ContractPart::Using(_) => !matches!(part, ContractPart::Using(_)),
2042 ContractPart::FunctionDefinition(last_def) => {
2043 if last_def.is_empty() {
2044 match part {
2045 ContractPart::FunctionDefinition(def) => !def.is_empty(),
2046 _ => true,
2047 }
2048 } else {
2049 true
2050 }
2051 }
2052 ContractPart::Annotation(_) => false,
2053 _ => true,
2054 },
2055 )
2056 })?;
2057
2058 if !contract.parts.is_empty() {
2059 fmt.write_whitespace_separator(true)?;
2060
2061 if fmt.config.contract_new_lines {
2062 write_chunk!(fmt, "\n")?;
2063 }
2064 }
2065
2066 write_chunk!(fmt, contract.loc.end(), "}}")?;
2067
2068 Ok(())
2069 })?;
2070
2071 Ok(())
2072 }
2073
2074 #[instrument(name = "annotation", skip_all)]
2076 fn visit_annotation(&mut self, annotation: &mut Annotation) -> Result<()> {
2077 return_source_if_disabled!(self, annotation.loc);
2078 let id = self.simulate_to_string(|fmt| annotation.id.visit(fmt))?;
2079 write!(self.buf(), "@{id}")?;
2080 write!(self.buf(), "(")?;
2081 annotation.value.visit(self)?;
2082 write!(self.buf(), ")")?;
2083 Ok(())
2084 }
2085
2086 #[instrument(name = "pragma", skip_all)]
2087 fn visit_pragma(
2088 &mut self,
2089 loc: Loc,
2090 ident: &mut Option<Identifier>,
2091 string: &mut Option<StringLiteral>,
2092 ) -> Result<()> {
2093 let (ident, string) = (ident.safe_unwrap(), string.safe_unwrap());
2094 return_source_if_disabled!(self, loc, ';');
2095
2096 let pragma_descriptor = if ident.name == "solidity" {
2097 &string.string
2102 } else {
2103 &string.string
2104 };
2105
2106 write_chunk!(self, string.loc.end(), "pragma {} {};", &ident.name, pragma_descriptor)?;
2107
2108 Ok(())
2109 }
2110
2111 #[instrument(name = "import_plain", skip_all)]
2112 fn visit_import_plain(&mut self, loc: Loc, import: &mut ImportPath) -> Result<()> {
2113 return_source_if_disabled!(self, loc, ';');
2114
2115 self.grouped(|fmt| {
2116 write_chunk!(fmt, loc.start(), import.loc().start(), "import")?;
2117 fmt.write_quoted_str(import.loc(), None, &import_path_string(import))?;
2118 fmt.write_semicolon()?;
2119 Ok(())
2120 })?;
2121 Ok(())
2122 }
2123
2124 #[instrument(name = "import_global", skip_all)]
2125 fn visit_import_global(
2126 &mut self,
2127 loc: Loc,
2128 global: &mut ImportPath,
2129 alias: &mut Identifier,
2130 ) -> Result<()> {
2131 return_source_if_disabled!(self, loc, ';');
2132
2133 self.grouped(|fmt| {
2134 write_chunk!(fmt, loc.start(), global.loc().start(), "import")?;
2135 fmt.write_quoted_str(global.loc(), None, &import_path_string(global))?;
2136 write_chunk!(fmt, loc.start(), alias.loc.start(), "as")?;
2137 alias.visit(fmt)?;
2138 fmt.write_semicolon()?;
2139 Ok(())
2140 })?;
2141 Ok(())
2142 }
2143
2144 #[instrument(name = "import_renames", skip_all)]
2145 fn visit_import_renames(
2146 &mut self,
2147 loc: Loc,
2148 imports: &mut [(Identifier, Option<Identifier>)],
2149 from: &mut ImportPath,
2150 ) -> Result<()> {
2151 return_source_if_disabled!(self, loc, ';');
2152
2153 if imports.is_empty() {
2154 self.grouped(|fmt| {
2155 write_chunk!(fmt, loc.start(), "import")?;
2156 fmt.write_empty_brackets()?;
2157 write_chunk!(fmt, loc.start(), from.loc().start(), "from")?;
2158 fmt.write_quoted_str(from.loc(), None, &import_path_string(from))?;
2159 fmt.write_semicolon()?;
2160 Ok(())
2161 })?;
2162 return Ok(())
2163 }
2164
2165 let imports_start = imports.first().unwrap().0.loc.start();
2166
2167 write_chunk!(self, loc.start(), imports_start, "import")?;
2168
2169 self.surrounded(
2170 SurroundingChunk::new("{", Some(imports_start), None),
2171 SurroundingChunk::new("}", None, Some(from.loc().start())),
2172 |fmt, _multiline| {
2173 let mut imports = imports.iter_mut().peekable();
2174 let mut import_chunks = Vec::new();
2175 while let Some((ident, alias)) = imports.next() {
2176 import_chunks.push(fmt.chunked(
2177 ident.loc.start(),
2178 imports.peek().map(|(ident, _)| ident.loc.start()),
2179 |fmt| {
2180 fmt.grouped(|fmt| {
2181 ident.visit(fmt)?;
2182 if let Some(alias) = alias {
2183 write_chunk!(fmt, ident.loc.end(), alias.loc.start(), "as")?;
2184 alias.visit(fmt)?;
2185 }
2186 Ok(())
2187 })?;
2188 Ok(())
2189 },
2190 )?);
2191 }
2192
2193 let multiline = fmt.are_chunks_separated_multiline(
2194 &format!("{{}} }} from \"{}\";", import_path_string(from)),
2195 &import_chunks,
2196 ",",
2197 )?;
2198 fmt.write_chunks_separated(&import_chunks, ",", multiline)?;
2199 Ok(())
2200 },
2201 )?;
2202
2203 self.grouped(|fmt| {
2204 write_chunk!(fmt, imports_start, from.loc().start(), "from")?;
2205 fmt.write_quoted_str(from.loc(), None, &import_path_string(from))?;
2206 fmt.write_semicolon()?;
2207 Ok(())
2208 })?;
2209
2210 Ok(())
2211 }
2212
2213 #[instrument(name = "enum", skip_all)]
2214 fn visit_enum(&mut self, enumeration: &mut EnumDefinition) -> Result<()> {
2215 return_source_if_disabled!(self, enumeration.loc);
2216
2217 let enum_name = enumeration.name.safe_unwrap_mut();
2218 let mut name =
2219 self.visit_to_chunk(enum_name.loc.start(), Some(enum_name.loc.end()), enum_name)?;
2220 name.content = format!("enum {} ", name.content);
2221 if enumeration.values.is_empty() {
2222 self.write_chunk(&name)?;
2223 self.write_empty_brackets()?;
2224 } else {
2225 name.content.push('{');
2226 self.write_chunk(&name)?;
2227
2228 self.indented(1, |fmt| {
2229 let values = fmt.items_to_chunks(
2230 Some(enumeration.loc.end()),
2231 enumeration.values.iter_mut().map(|ident| {
2232 let ident = ident.safe_unwrap_mut();
2233 (ident.loc, ident)
2234 }),
2235 )?;
2236 fmt.write_chunks_separated(&values, ",", true)?;
2237 writeln!(fmt.buf())?;
2238 Ok(())
2239 })?;
2240 write_chunk!(self, "}}")?;
2241 }
2242
2243 Ok(())
2244 }
2245
2246 #[instrument(name = "assembly", skip_all)]
2247 fn visit_assembly(
2248 &mut self,
2249 loc: Loc,
2250 dialect: &mut Option<StringLiteral>,
2251 block: &mut YulBlock,
2252 flags: &mut Option<Vec<StringLiteral>>,
2253 ) -> Result<(), Self::Error> {
2254 return_source_if_disabled!(self, loc);
2255
2256 write_chunk!(self, loc.start(), "assembly")?;
2257 if let Some(StringLiteral { loc, string, .. }) = dialect {
2258 write_chunk!(self, loc.start(), loc.end(), "\"{string}\"")?;
2259 }
2260 if let Some(flags) = flags {
2261 if !flags.is_empty() {
2262 let loc_start = flags.first().unwrap().loc.start();
2263 self.surrounded(
2264 SurroundingChunk::new("(", Some(loc_start), None),
2265 SurroundingChunk::new(")", None, Some(block.loc.start())),
2266 |fmt, _| {
2267 let mut flags = flags.iter_mut().peekable();
2268 let mut chunks = vec![];
2269 while let Some(flag) = flags.next() {
2270 let next_byte_offset =
2271 flags.peek().map(|next_flag| next_flag.loc.start());
2272 chunks.push(fmt.chunked(
2273 flag.loc.start(),
2274 next_byte_offset,
2275 |fmt| {
2276 write!(fmt.buf(), "\"{}\"", flag.string)?;
2277 Ok(())
2278 },
2279 )?);
2280 }
2281 fmt.write_chunks_separated(&chunks, ",", false)?;
2282 Ok(())
2283 },
2284 )?;
2285 }
2286 }
2287
2288 block.visit(self)
2289 }
2290
2291 #[instrument(name = "block", skip_all)]
2292 fn visit_block(
2293 &mut self,
2294 loc: Loc,
2295 unchecked: bool,
2296 statements: &mut Vec<Statement>,
2297 ) -> Result<()> {
2298 return_source_if_disabled!(self, loc);
2299 if unchecked {
2300 write_chunk!(self, loc.start(), "unchecked ")?;
2301 }
2302
2303 self.visit_block(loc, statements, false, false)?;
2304 Ok(())
2305 }
2306
2307 #[instrument(name = "args", skip_all)]
2308 fn visit_args(&mut self, loc: Loc, args: &mut Vec<NamedArgument>) -> Result<(), Self::Error> {
2309 return_source_if_disabled!(self, loc);
2310
2311 write!(self.buf(), "{{")?;
2312
2313 let mut args_iter = args.iter_mut().peekable();
2314 let mut chunks = Vec::new();
2315 while let Some(NamedArgument { loc: arg_loc, name, expr }) = args_iter.next() {
2316 let next_byte_offset = args_iter
2317 .peek()
2318 .map(|NamedArgument { loc: arg_loc, .. }| arg_loc.start())
2319 .unwrap_or_else(|| loc.end());
2320 chunks.push(self.chunked(arg_loc.start(), Some(next_byte_offset), |fmt| {
2321 fmt.grouped(|fmt| {
2322 write_chunk!(fmt, name.loc.start(), "{}: ", name.name)?;
2323 expr.visit(fmt)
2324 })?;
2325 Ok(())
2326 })?);
2327 }
2328
2329 if let Some(first) = chunks.first_mut() {
2330 if first.prefixes.is_empty() &&
2331 first.postfixes_before.is_empty() &&
2332 !self.config.bracket_spacing
2333 {
2334 first.needs_space = Some(false);
2335 }
2336 }
2337 let multiline = self.are_chunks_separated_multiline("{}}", &chunks, ",")?;
2338 self.indented_if(multiline, 1, |fmt| fmt.write_chunks_separated(&chunks, ",", multiline))?;
2339
2340 let prefix = if multiline && !self.is_beginning_of_line() {
2341 "\n"
2342 } else if self.config.bracket_spacing {
2343 " "
2344 } else {
2345 ""
2346 };
2347 let closing_bracket = format!("{prefix}{}", "}");
2348 if let Some(arg) = args.last() {
2349 write_chunk!(self, arg.loc.end(), "{closing_bracket}")?;
2350 } else {
2351 write_chunk!(self, "{closing_bracket}")?;
2352 }
2353
2354 Ok(())
2355 }
2356
2357 #[instrument(name = "expr", skip_all)]
2358 fn visit_expr(&mut self, loc: Loc, expr: &mut Expression) -> Result<()> {
2359 return_source_if_disabled!(self, loc);
2360
2361 match expr {
2362 Expression::Type(loc, typ) => match typ {
2363 Type::Address => write_chunk!(self, loc.start(), "address")?,
2364 Type::AddressPayable => write_chunk!(self, loc.start(), "address payable")?,
2365 Type::Payable => write_chunk!(self, loc.start(), "payable")?,
2366 Type::Bool => write_chunk!(self, loc.start(), "bool")?,
2367 Type::String => write_chunk!(self, loc.start(), "string")?,
2368 Type::Bytes(n) => write_chunk!(self, loc.start(), "bytes{}", n)?,
2369 Type::Rational => write_chunk!(self, loc.start(), "rational")?,
2370 Type::DynamicBytes => write_chunk!(self, loc.start(), "bytes")?,
2371 Type::Int(ref n) | Type::Uint(ref n) => {
2372 let int = if matches!(typ, Type::Int(_)) { "int" } else { "uint" };
2373 match n {
2374 256 => match self.config.int_types {
2375 IntTypes::Long => write_chunk!(self, loc.start(), "{int}{n}")?,
2376 IntTypes::Short => write_chunk!(self, loc.start(), "{int}")?,
2377 IntTypes::Preserve => self.visit_source(*loc)?,
2378 },
2379 _ => write_chunk!(self, loc.start(), "{int}{n}")?,
2380 }
2381 }
2382 Type::Mapping { loc, key, key_name, value, value_name } => {
2383 let arrow_loc = self.find_next_str_in_src(loc.start(), "=>");
2384 let close_paren_loc =
2385 self.find_next_in_src(value.loc().end(), ')').unwrap_or(loc.end());
2386 let first = SurroundingChunk::new(
2387 "mapping(",
2388 Some(loc.start()),
2389 Some(key.loc().start()),
2390 );
2391 let last = SurroundingChunk::new(")", Some(close_paren_loc), Some(loc.end()))
2392 .non_spaced();
2393 self.surrounded(first, last, |fmt, multiline| {
2394 fmt.grouped(|fmt| {
2395 key.visit(fmt)?;
2396
2397 if let Some(name) = key_name {
2398 let end_loc = arrow_loc.unwrap_or(value.loc().start());
2399 write_chunk!(fmt, name.loc.start(), end_loc, " {}", name)?;
2400 } else if let Some(arrow_loc) = arrow_loc {
2401 fmt.write_postfix_comments_before(arrow_loc)?;
2402 }
2403
2404 let mut write_arrow_and_value = |fmt: &mut Self| {
2405 write!(fmt.buf(), "=> ")?;
2406 value.visit(fmt)?;
2407 if let Some(name) = value_name {
2408 write_chunk!(fmt, name.loc.start(), " {}", name)?;
2409 }
2410 Ok(())
2411 };
2412
2413 let rest_str = fmt.simulate_to_string(&mut write_arrow_and_value)?;
2414 let multiline = multiline && !fmt.will_it_fit(rest_str);
2415 fmt.write_whitespace_separator(multiline)?;
2416
2417 write_arrow_and_value(fmt)?;
2418
2419 fmt.write_postfix_comments_before(close_paren_loc)?;
2420 fmt.write_prefix_comments_before(close_paren_loc)
2421 })?;
2422 Ok(())
2423 })?;
2424 }
2425 Type::Function { .. } => self.visit_source(*loc)?,
2426 },
2427 Expression::BoolLiteral(loc, val) => {
2428 write_chunk!(self, loc.start(), loc.end(), "{val}")?;
2429 }
2430 Expression::NumberLiteral(loc, val, exp, unit) => {
2431 self.write_num_literal(*loc, val, None, exp, unit)?;
2432 }
2433 Expression::HexNumberLiteral(loc, val, unit) => {
2434 let val = if val.len() == 42 {
2436 Address::from_str(val).expect("").to_string()
2437 } else {
2438 val.to_owned()
2439 };
2440 write_chunk!(self, loc.start(), loc.end(), "{val}")?;
2441 self.write_unit(unit)?;
2442 }
2443 Expression::RationalNumberLiteral(loc, val, fraction, exp, unit) => {
2444 self.write_num_literal(*loc, val, Some(fraction), exp, unit)?;
2445 }
2446 Expression::StringLiteral(vals) => {
2447 for StringLiteral { loc, string, unicode } in vals {
2448 let prefix = if *unicode { Some("unicode") } else { None };
2449 self.write_quoted_str(*loc, prefix, string)?;
2450 }
2451 }
2452 Expression::HexLiteral(vals) => {
2453 for val in vals {
2454 self.write_hex_literal(val)?;
2455 }
2456 }
2457 Expression::AddressLiteral(loc, val) => {
2458 self.write_quoted_str(*loc, Some("address"), val)?;
2460 }
2461 Expression::Parenthesis(loc, expr) => {
2462 self.surrounded(
2463 SurroundingChunk::new("(", Some(loc.start()), None),
2464 SurroundingChunk::new(")", None, Some(loc.end())),
2465 |fmt, _| expr.visit(fmt),
2466 )?;
2467 }
2468 Expression::ArraySubscript(_, ty_exp, index_expr) => {
2469 ty_exp.visit(self)?;
2470 write!(self.buf(), "[")?;
2471 index_expr.as_mut().map(|index| index.visit(self)).transpose()?;
2472 write!(self.buf(), "]")?;
2473 }
2474 Expression::ArraySlice(loc, expr, start, end) => {
2475 expr.visit(self)?;
2476 write!(self.buf(), "[")?;
2477 let mut write_slice = |fmt: &mut Self, multiline| -> Result<()> {
2478 if multiline {
2479 fmt.write_whitespace_separator(true)?;
2480 }
2481 fmt.grouped(|fmt| {
2482 start.as_mut().map(|start| start.visit(fmt)).transpose()?;
2483 write!(fmt.buf(), ":")?;
2484 if let Some(end) = end {
2485 let mut chunk =
2486 fmt.chunked(end.loc().start(), Some(loc.end()), |fmt| {
2487 end.visit(fmt)
2488 })?;
2489 if chunk.prefixes.is_empty() &&
2490 chunk.postfixes_before.is_empty() &&
2491 (start.is_none() || fmt.will_it_fit(&chunk.content))
2492 {
2493 chunk.needs_space = Some(false);
2494 }
2495 fmt.write_chunk(&chunk)?;
2496 }
2497 Ok(())
2498 })?;
2499 if multiline {
2500 fmt.write_whitespace_separator(true)?;
2501 }
2502 Ok(())
2503 };
2504
2505 if !self.try_on_single_line(|fmt| write_slice(fmt, false))? {
2506 self.indented(1, |fmt| write_slice(fmt, true))?;
2507 }
2508
2509 write!(self.buf(), "]")?;
2510 }
2511 Expression::ArrayLiteral(loc, exprs) => {
2512 write_chunk!(self, loc.start(), "[")?;
2513 let chunks = self.items_to_chunks(
2514 Some(loc.end()),
2515 exprs.iter_mut().map(|expr| (expr.loc(), expr)),
2516 )?;
2517 let multiline = self.are_chunks_separated_multiline("{}]", &chunks, ",")?;
2518 self.indented_if(multiline, 1, |fmt| {
2519 fmt.write_chunks_separated(&chunks, ",", multiline)?;
2520 if multiline {
2521 fmt.write_postfix_comments_before(loc.end())?;
2522 fmt.write_prefix_comments_before(loc.end())?;
2523 fmt.write_whitespace_separator(true)?;
2524 }
2525 Ok(())
2526 })?;
2527 write_chunk!(self, loc.end(), "]")?;
2528 }
2529 Expression::PreIncrement(..) |
2530 Expression::PostIncrement(..) |
2531 Expression::PreDecrement(..) |
2532 Expression::PostDecrement(..) |
2533 Expression::Not(..) |
2534 Expression::UnaryPlus(..) |
2535 Expression::Add(..) |
2536 Expression::Negate(..) |
2537 Expression::Subtract(..) |
2538 Expression::Power(..) |
2539 Expression::Multiply(..) |
2540 Expression::Divide(..) |
2541 Expression::Modulo(..) |
2542 Expression::ShiftLeft(..) |
2543 Expression::ShiftRight(..) |
2544 Expression::BitwiseNot(..) |
2545 Expression::BitwiseAnd(..) |
2546 Expression::BitwiseXor(..) |
2547 Expression::BitwiseOr(..) |
2548 Expression::Less(..) |
2549 Expression::More(..) |
2550 Expression::LessEqual(..) |
2551 Expression::MoreEqual(..) |
2552 Expression::And(..) |
2553 Expression::Or(..) |
2554 Expression::Equal(..) |
2555 Expression::NotEqual(..) => {
2556 let spaced = expr.has_space_around();
2557 let op = expr.operator().unwrap();
2558
2559 match expr.components_mut() {
2560 (Some(left), Some(right)) => {
2561 left.visit(self)?;
2562
2563 let right_chunk =
2564 self.chunked(right.loc().start(), Some(loc.end()), |fmt| {
2565 write_chunk!(fmt, right.loc().start(), "{op}")?;
2566 right.visit(fmt)?;
2567 Ok(())
2568 })?;
2569
2570 self.grouped(|fmt| fmt.write_chunk(&right_chunk))?;
2571 }
2572 (Some(left), None) => {
2573 left.visit(self)?;
2574 write_chunk_spaced!(self, loc.end(), Some(spaced), "{op}")?;
2575 }
2576 (None, Some(right)) => {
2577 write_chunk!(self, right.loc().start(), "{op}")?;
2578 let mut right_chunk =
2579 self.visit_to_chunk(right.loc().end(), Some(loc.end()), right)?;
2580 right_chunk.needs_space = Some(spaced);
2581 self.write_chunk(&right_chunk)?;
2582 }
2583 (None, None) => {}
2584 }
2585 }
2586 Expression::Assign(..) |
2587 Expression::AssignOr(..) |
2588 Expression::AssignAnd(..) |
2589 Expression::AssignXor(..) |
2590 Expression::AssignShiftLeft(..) |
2591 Expression::AssignShiftRight(..) |
2592 Expression::AssignAdd(..) |
2593 Expression::AssignSubtract(..) |
2594 Expression::AssignMultiply(..) |
2595 Expression::AssignDivide(..) |
2596 Expression::AssignModulo(..) => {
2597 let op = expr.operator().unwrap();
2598 let (left, right) = expr.components_mut();
2599 let (left, right) = (left.unwrap(), right.unwrap());
2600
2601 left.visit(self)?;
2602 write_chunk!(self, "{op}")?;
2603 self.visit_assignment(right)?;
2604 }
2605 Expression::ConditionalOperator(loc, cond, first_expr, second_expr) => {
2606 cond.visit(self)?;
2607
2608 let first_expr = self.chunked(
2609 first_expr.loc().start(),
2610 Some(second_expr.loc().start()),
2611 |fmt| {
2612 write_chunk!(fmt, "?")?;
2613 first_expr.visit(fmt)
2614 },
2615 )?;
2616 let second_expr =
2617 self.chunked(second_expr.loc().start(), Some(loc.end()), |fmt| {
2618 write_chunk!(fmt, ":")?;
2619 second_expr.visit(fmt)
2620 })?;
2621
2622 let chunks = vec![first_expr, second_expr];
2623 if !self.try_on_single_line(|fmt| fmt.write_chunks_separated(&chunks, "", false))? {
2624 self.grouped(|fmt| fmt.write_chunks_separated(&chunks, "", true))?;
2625 }
2626 }
2627 Expression::Variable(ident) => {
2628 write_chunk!(self, loc.end(), "{}", ident.name)?;
2629 }
2630 Expression::MemberAccess(_, expr, ident) => {
2631 self.visit_member_access(expr, ident, |fmt, expr| match expr.as_mut() {
2632 Expression::MemberAccess(_, inner_expr, inner_ident) => {
2633 Ok(Some((inner_expr, inner_ident)))
2634 }
2635 expr => {
2636 expr.visit(fmt)?;
2637 Ok(None)
2638 }
2639 })?;
2640 }
2641 Expression::List(loc, items) => {
2642 self.surrounded(
2643 SurroundingChunk::new(
2644 "(",
2645 Some(loc.start()),
2646 items.first().map(|item| item.0.start()),
2647 ),
2648 SurroundingChunk::new(")", None, Some(loc.end())),
2649 |fmt, _| {
2650 let items = fmt.items_to_chunks(
2651 Some(loc.end()),
2652 items.iter_mut().map(|(loc, item)| (*loc, item)),
2653 )?;
2654 let write_items = |fmt: &mut Self, multiline| {
2655 fmt.write_chunks_separated(&items, ",", multiline)
2656 };
2657 if !fmt.try_on_single_line(|fmt| write_items(fmt, false))? {
2658 write_items(fmt, true)?;
2659 }
2660 Ok(())
2661 },
2662 )?;
2663 }
2664 Expression::FunctionCall(loc, expr, exprs) => {
2665 self.visit_expr(expr.loc(), expr)?;
2666 self.visit_list("", exprs, Some(expr.loc().end()), Some(loc.end()), true)?;
2667 }
2668 Expression::NamedFunctionCall(loc, expr, args) => {
2669 self.visit_expr(expr.loc(), expr)?;
2670 write!(self.buf(), "(")?;
2671 self.visit_args(*loc, args)?;
2672 write!(self.buf(), ")")?;
2673 }
2674 Expression::FunctionCallBlock(_, expr, stmt) => {
2675 expr.visit(self)?;
2676 stmt.visit(self)?;
2677 }
2678 Expression::New(_, expr) => {
2679 write_chunk!(self, "new ")?;
2680 self.visit_expr(expr.loc(), expr)?;
2681 }
2682 _ => self.visit_source(loc)?,
2683 };
2684
2685 Ok(())
2686 }
2687
2688 #[instrument(name = "ident", skip_all)]
2689 fn visit_ident(&mut self, loc: Loc, ident: &mut Identifier) -> Result<()> {
2690 return_source_if_disabled!(self, loc);
2691 write_chunk!(self, loc.end(), "{}", ident.name)?;
2692 Ok(())
2693 }
2694
2695 #[instrument(name = "ident_path", skip_all)]
2696 fn visit_ident_path(&mut self, idents: &mut IdentifierPath) -> Result<(), Self::Error> {
2697 if idents.identifiers.is_empty() {
2698 return Ok(())
2699 }
2700 return_source_if_disabled!(self, idents.loc);
2701
2702 idents.identifiers.iter_mut().skip(1).for_each(|chunk| {
2703 if !chunk.name.starts_with('.') {
2704 chunk.name.insert(0, '.')
2705 }
2706 });
2707 let chunks = self.items_to_chunks(
2708 Some(idents.loc.end()),
2709 idents.identifiers.iter_mut().map(|ident| (ident.loc, ident)),
2710 )?;
2711 self.grouped(|fmt| {
2712 let multiline = fmt.are_chunks_separated_multiline("{}", &chunks, "")?;
2713 fmt.write_chunks_separated(&chunks, "", multiline)
2714 })?;
2715 Ok(())
2716 }
2717
2718 #[instrument(name = "emit", skip_all)]
2719 fn visit_emit(&mut self, loc: Loc, event: &mut Expression) -> Result<()> {
2720 return_source_if_disabled!(self, loc);
2721 write_chunk!(self, loc.start(), "emit")?;
2722 event.visit(self)?;
2723 self.write_semicolon()?;
2724 Ok(())
2725 }
2726
2727 #[instrument(name = "var_definition", skip_all)]
2728 fn visit_var_definition(&mut self, var: &mut VariableDefinition) -> Result<()> {
2729 return_source_if_disabled!(self, var.loc, ';');
2730
2731 var.ty.visit(self)?;
2732
2733 let multiline = self.grouped(|fmt| {
2734 let var_name = var.name.safe_unwrap_mut();
2735 let name_start = var_name.loc.start();
2736
2737 let attrs = fmt.items_to_chunks_sorted(Some(name_start), var.attrs.iter_mut())?;
2738 if !fmt.try_on_single_line(|fmt| fmt.write_chunks_separated(&attrs, "", false))? {
2739 fmt.write_chunks_separated(&attrs, "", true)?;
2740 }
2741
2742 let mut name = fmt.visit_to_chunk(name_start, Some(var_name.loc.end()), var_name)?;
2743 if var.initializer.is_some() {
2744 name.content.push_str(" =");
2745 }
2746 fmt.write_chunk(&name)?;
2747
2748 Ok(())
2749 })?;
2750
2751 var.initializer
2752 .as_mut()
2753 .map(|init| self.indented_if(multiline, 1, |fmt| fmt.visit_assignment(init)))
2754 .transpose()?;
2755
2756 self.write_semicolon()?;
2757
2758 Ok(())
2759 }
2760
2761 #[instrument(name = "var_definition_stmt", skip_all)]
2762 fn visit_var_definition_stmt(
2763 &mut self,
2764 loc: Loc,
2765 declaration: &mut VariableDeclaration,
2766 expr: &mut Option<Expression>,
2767 ) -> Result<()> {
2768 return_source_if_disabled!(self, loc, ';');
2769
2770 let declaration = self
2771 .chunked(declaration.loc.start(), None, |fmt| fmt.visit_var_declaration(declaration))?;
2772 let multiline = declaration.content.contains('\n');
2773 self.write_chunk(&declaration)?;
2774
2775 if let Some(expr) = expr {
2776 write!(self.buf(), " =")?;
2777 self.indented_if(multiline, 1, |fmt| fmt.visit_assignment(expr))?;
2778 }
2779
2780 self.write_semicolon()
2781 }
2782
2783 #[instrument(name = "var_declaration", skip_all)]
2784 fn visit_var_declaration(&mut self, var: &mut VariableDeclaration) -> Result<()> {
2785 return_source_if_disabled!(self, var.loc);
2786 self.grouped(|fmt| {
2787 var.ty.visit(fmt)?;
2788 if let Some(storage) = &var.storage {
2789 write_chunk!(fmt, storage.loc().end(), "{storage}")?;
2790 }
2791 let var_name = var.name.safe_unwrap();
2792 write_chunk!(fmt, var_name.loc.end(), "{var_name}")
2793 })?;
2794 Ok(())
2795 }
2796
2797 #[instrument(name = "return", skip_all)]
2798 fn visit_return(&mut self, loc: Loc, expr: &mut Option<Expression>) -> Result<(), Self::Error> {
2799 return_source_if_disabled!(self, loc, ';');
2800
2801 self.write_postfix_comments_before(loc.start())?;
2802 self.write_prefix_comments_before(loc.start())?;
2803
2804 if expr.is_none() {
2805 write_chunk!(self, loc.end(), "return;")?;
2806 return Ok(())
2807 }
2808
2809 let expr = expr.as_mut().unwrap();
2810 let expr_loc_start = expr.loc().start();
2811 let write_return = |fmt: &mut Self| -> Result<()> {
2812 write_chunk!(fmt, loc.start(), "return")?;
2813 fmt.write_postfix_comments_before(expr_loc_start)?;
2814 Ok(())
2815 };
2816
2817 let mut write_return_with_expr = |fmt: &mut Self| -> Result<()> {
2818 let fits_on_single = fmt.try_on_single_line(|fmt| {
2819 write_return(fmt)?;
2820 expr.visit(fmt)
2821 })?;
2822 if fits_on_single {
2823 return Ok(())
2824 }
2825
2826 let mut fit_on_next_line = false;
2827 let tx = fmt.transact(|fmt| {
2828 fmt.grouped(|fmt| {
2829 write_return(fmt)?;
2830 if !fmt.is_beginning_of_line() {
2831 fmt.write_whitespace_separator(true)?;
2832 }
2833 fit_on_next_line = fmt.try_on_single_line(|fmt| expr.visit(fmt))?;
2834 Ok(())
2835 })?;
2836 Ok(())
2837 })?;
2838 if fit_on_next_line {
2839 tx.commit()?;
2840 return Ok(())
2841 }
2842
2843 write_return(fmt)?;
2844 expr.visit(fmt)?;
2845 Ok(())
2846 };
2847
2848 write_return_with_expr(self)?;
2849 write_chunk!(self, loc.end(), ";")?;
2850 Ok(())
2851 }
2852
2853 #[instrument(name = "revert", skip_all)]
2854 fn visit_revert(
2855 &mut self,
2856 loc: Loc,
2857 error: &mut Option<IdentifierPath>,
2858 args: &mut Vec<Expression>,
2859 ) -> Result<(), Self::Error> {
2860 return_source_if_disabled!(self, loc, ';');
2861 write_chunk!(self, loc.start(), "revert")?;
2862 if let Some(error) = error {
2863 error.visit(self)?;
2864 }
2865 self.visit_list("", args, None, Some(loc.end()), true)?;
2866 self.write_semicolon()?;
2867
2868 Ok(())
2869 }
2870
2871 #[instrument(name = "revert_named_args", skip_all)]
2872 fn visit_revert_named_args(
2873 &mut self,
2874 loc: Loc,
2875 error: &mut Option<IdentifierPath>,
2876 args: &mut Vec<NamedArgument>,
2877 ) -> Result<(), Self::Error> {
2878 return_source_if_disabled!(self, loc, ';');
2879
2880 write_chunk!(self, loc.start(), "revert")?;
2881 let mut error_indented = false;
2882 if let Some(error) = error {
2883 if !self.try_on_single_line(|fmt| error.visit(fmt))? {
2884 error.visit(self)?;
2885 error_indented = true;
2886 }
2887 }
2888
2889 if args.is_empty() {
2890 write!(self.buf(), "({{}});")?;
2891 return Ok(())
2892 }
2893
2894 write!(self.buf(), "(")?;
2895 self.indented_if(error_indented, 1, |fmt| fmt.visit_args(loc, args))?;
2896 write!(self.buf(), ")")?;
2897 self.write_semicolon()?;
2898
2899 Ok(())
2900 }
2901
2902 #[instrument(name = "break", skip_all)]
2903 fn visit_break(&mut self, loc: Loc, semicolon: bool) -> Result<()> {
2904 if semicolon {
2905 return_source_if_disabled!(self, loc, ';');
2906 } else {
2907 return_source_if_disabled!(self, loc);
2908 }
2909 write_chunk!(self, loc.start(), loc.end(), "break{}", if semicolon { ";" } else { "" })
2910 }
2911
2912 #[instrument(name = "continue", skip_all)]
2913 fn visit_continue(&mut self, loc: Loc, semicolon: bool) -> Result<()> {
2914 if semicolon {
2915 return_source_if_disabled!(self, loc, ';');
2916 } else {
2917 return_source_if_disabled!(self, loc);
2918 }
2919 write_chunk!(self, loc.start(), loc.end(), "continue{}", if semicolon { ";" } else { "" })
2920 }
2921
2922 #[instrument(name = "try", skip_all)]
2923 fn visit_try(
2924 &mut self,
2925 loc: Loc,
2926 expr: &mut Expression,
2927 returns: &mut Option<(Vec<(Loc, Option<Parameter>)>, Box<Statement>)>,
2928 clauses: &mut Vec<CatchClause>,
2929 ) -> Result<(), Self::Error> {
2930 return_source_if_disabled!(self, loc);
2931
2932 let try_next_byte = clauses.first().map(|c| match c {
2933 CatchClause::Simple(loc, ..) => loc.start(),
2934 CatchClause::Named(loc, ..) => loc.start(),
2935 });
2936 let try_chunk = self.chunked(loc.start(), try_next_byte, |fmt| {
2937 write_chunk!(fmt, loc.start(), expr.loc().start(), "try")?;
2938 expr.visit(fmt)?;
2939 if let Some((params, stmt)) = returns {
2940 let mut params =
2941 params.iter_mut().filter(|(_, param)| param.is_some()).collect::<Vec<_>>();
2942 let byte_offset = params.first().map_or(stmt.loc().start(), |p| p.0.start());
2943 fmt.surrounded(
2944 SurroundingChunk::new("returns (", Some(byte_offset), None),
2945 SurroundingChunk::new(")", None, params.last().map(|p| p.0.end())),
2946 |fmt, _| {
2947 let chunks = fmt.items_to_chunks(
2948 Some(stmt.loc().start()),
2949 params.iter_mut().map(|(loc, ref mut ident)| (*loc, ident)),
2950 )?;
2951 let multiline = fmt.are_chunks_separated_multiline("{})", &chunks, ",")?;
2952 fmt.write_chunks_separated(&chunks, ",", multiline)?;
2953 Ok(())
2954 },
2955 )?;
2956 stmt.visit(fmt)?;
2957 }
2958 Ok(())
2959 })?;
2960
2961 let mut chunks = vec![try_chunk];
2962 for clause in clauses {
2963 let (loc, ident, mut param, stmt) = match clause {
2964 CatchClause::Simple(loc, param, stmt) => (loc, None, param.as_mut(), stmt),
2965 CatchClause::Named(loc, ident, param, stmt) => {
2966 (loc, Some(ident), Some(param), stmt)
2967 }
2968 };
2969
2970 let chunk = self.chunked(loc.start(), Some(stmt.loc().start()), |fmt| {
2971 write_chunk!(fmt, "catch")?;
2972 if let Some(ident) = ident.as_ref() {
2973 fmt.write_postfix_comments_before(
2974 param.as_ref().map(|p| p.loc.start()).unwrap_or_else(|| ident.loc.end()),
2975 )?;
2976 write_chunk!(fmt, ident.loc.start(), "{}", ident.name)?;
2977 }
2978 if let Some(param) = param.as_mut() {
2979 write_chunk_spaced!(fmt, param.loc.start(), Some(ident.is_none()), "(")?;
2980 fmt.surrounded(
2981 SurroundingChunk::new("", Some(param.loc.start()), None),
2982 SurroundingChunk::new(")", None, Some(stmt.loc().start())),
2983 |fmt, _| param.visit(fmt),
2984 )?;
2985 }
2986
2987 stmt.visit(fmt)?;
2988 Ok(())
2989 })?;
2990
2991 chunks.push(chunk);
2992 }
2993
2994 let multiline = self.are_chunks_separated_multiline("{}", &chunks, "")?;
2995 if !multiline {
2996 self.write_chunks_separated(&chunks, "", false)?;
2997 return Ok(())
2998 }
2999
3000 let mut chunks = chunks.iter_mut().peekable();
3001 let mut prev_multiline = false;
3002
3003 if let Some(chunk) = chunks.next() {
3005 let chunk_str = self.simulate_to_string(|fmt| fmt.write_chunk(chunk))?;
3006 write!(self.buf(), "{chunk_str}")?;
3007 prev_multiline = chunk_str.contains('\n');
3008 }
3009
3010 while let Some(chunk) = chunks.next() {
3011 let chunk_str = self.simulate_to_string(|fmt| fmt.write_chunk(chunk))?;
3012 let multiline = chunk_str.contains('\n');
3013 self.indented_if(!multiline, 1, |fmt| {
3014 chunk.needs_space = Some(false);
3015 let on_same_line = prev_multiline && (multiline || chunks.peek().is_none());
3016 let prefix = if fmt.is_beginning_of_line() {
3017 ""
3018 } else if on_same_line {
3019 " "
3020 } else {
3021 "\n"
3022 };
3023 let chunk_str = format!("{prefix}{chunk_str}");
3024 write!(fmt.buf(), "{chunk_str}")?;
3025 Ok(())
3026 })?;
3027 prev_multiline = multiline;
3028 }
3029 Ok(())
3030 }
3031
3032 #[instrument(name = "if", skip_all)]
3033 fn visit_if(
3034 &mut self,
3035 loc: Loc,
3036 cond: &mut Expression,
3037 if_branch: &mut Box<Statement>,
3038 else_branch: &mut Option<Box<Statement>>,
3039 is_first_stmt: bool,
3040 ) -> Result<(), Self::Error> {
3041 return_source_if_disabled!(self, loc);
3042
3043 if !is_first_stmt {
3044 self.write_if_stmt(loc, cond, if_branch, else_branch)?;
3045 return Ok(())
3046 }
3047
3048 self.context.if_stmt_single_line = Some(true);
3049 let mut stmt_fits_on_single = false;
3050 let tx = self.transact(|fmt| {
3051 stmt_fits_on_single = match fmt.write_if_stmt(loc, cond, if_branch, else_branch) {
3052 Ok(()) => true,
3053 Err(FormatterError::Fmt(_)) => false,
3054 Err(err) => bail!(err),
3055 };
3056 Ok(())
3057 })?;
3058
3059 if stmt_fits_on_single {
3060 tx.commit()?;
3061 } else {
3062 self.context.if_stmt_single_line = Some(false);
3063 self.write_if_stmt(loc, cond, if_branch, else_branch)?;
3064 }
3065 self.context.if_stmt_single_line = None;
3066
3067 Ok(())
3068 }
3069
3070 #[instrument(name = "do_while", skip_all)]
3071 fn visit_do_while(
3072 &mut self,
3073 loc: Loc,
3074 body: &mut Statement,
3075 cond: &mut Expression,
3076 ) -> Result<(), Self::Error> {
3077 return_source_if_disabled!(self, loc, ';');
3078 write_chunk!(self, loc.start(), "do ")?;
3079 self.visit_stmt_as_block(body, false)?;
3080 visit_source_if_disabled_else!(self, loc.with_start(body.loc().end()), {
3081 self.surrounded(
3082 SurroundingChunk::new("while (", Some(cond.loc().start()), None),
3083 SurroundingChunk::new(");", None, Some(loc.end())),
3084 |fmt, _| cond.visit(fmt),
3085 )?;
3086 });
3087 Ok(())
3088 }
3089
3090 #[instrument(name = "while", skip_all)]
3091 fn visit_while(
3092 &mut self,
3093 loc: Loc,
3094 cond: &mut Expression,
3095 body: &mut Statement,
3096 ) -> Result<(), Self::Error> {
3097 return_source_if_disabled!(self, loc);
3098 self.surrounded(
3099 SurroundingChunk::new("while (", Some(loc.start()), None),
3100 SurroundingChunk::new(")", None, Some(cond.loc().end())),
3101 |fmt, _| {
3102 cond.visit(fmt)?;
3103 fmt.write_postfix_comments_before(body.loc().start())
3104 },
3105 )?;
3106
3107 let cond_close_paren_loc =
3108 self.find_next_in_src(cond.loc().end(), ')').unwrap_or_else(|| cond.loc().end());
3109 let attempt_single_line = self.should_attempt_block_single_line(body, cond_close_paren_loc);
3110 self.visit_stmt_as_block(body, attempt_single_line)?;
3111 Ok(())
3112 }
3113
3114 #[instrument(name = "for", skip_all)]
3115 fn visit_for(
3116 &mut self,
3117 loc: Loc,
3118 init: &mut Option<Box<Statement>>,
3119 cond: &mut Option<Box<Expression>>,
3120 update: &mut Option<Box<Expression>>,
3121 body: &mut Option<Box<Statement>>,
3122 ) -> Result<(), Self::Error> {
3123 return_source_if_disabled!(self, loc);
3124
3125 let next_byte_end = update.as_ref().map(|u| u.loc().end());
3126 self.surrounded(
3127 SurroundingChunk::new("for (", Some(loc.start()), None),
3128 SurroundingChunk::new(")", None, next_byte_end),
3129 |fmt, _| {
3130 let mut write_for_loop_header = |fmt: &mut Self, multiline: bool| -> Result<()> {
3131 match init {
3132 Some(stmt) => stmt.visit(fmt),
3133 None => fmt.write_semicolon(),
3134 }?;
3135 if multiline {
3136 fmt.write_whitespace_separator(true)?;
3137 }
3138
3139 cond.visit(fmt)?;
3140 fmt.write_semicolon()?;
3141 if multiline {
3142 fmt.write_whitespace_separator(true)?;
3143 }
3144
3145 match update {
3146 Some(expr) => expr.visit(fmt),
3147 None => Ok(()),
3148 }
3149 };
3150 let multiline = !fmt.try_on_single_line(|fmt| write_for_loop_header(fmt, false))?;
3151 if multiline {
3152 write_for_loop_header(fmt, true)?;
3153 }
3154 Ok(())
3155 },
3156 )?;
3157 match body {
3158 Some(body) => {
3159 self.visit_stmt_as_block(body, false)?;
3160 }
3161 None => {
3162 self.write_empty_brackets()?;
3163 }
3164 };
3165 Ok(())
3166 }
3167
3168 #[instrument(name = "function", skip_all)]
3169 fn visit_function(&mut self, func: &mut FunctionDefinition) -> Result<()> {
3170 if func.body.is_some() {
3171 return_source_if_disabled!(self, func.loc());
3172 } else {
3173 return_source_if_disabled!(self, func.loc(), ';');
3174 }
3175
3176 self.with_function_context(func.clone(), |fmt| {
3177 fmt.write_postfix_comments_before(func.loc.start())?;
3178 fmt.write_prefix_comments_before(func.loc.start())?;
3179
3180 let body_loc = func.body.as_ref().map(CodeLocation::loc);
3181 let mut attrs_multiline = false;
3182 let fits_on_single = fmt.try_on_single_line(|fmt| {
3183 fmt.write_function_header(func, body_loc, false)?;
3184 Ok(())
3185 })?;
3186 if !fits_on_single {
3187 attrs_multiline = fmt.write_function_header(func, body_loc, true)?;
3188 }
3189
3190 match &mut func.body {
3192 Some(body) => {
3193 let body_loc = body.loc();
3194 if fmt.inline_config.is_disabled(body_loc.with_end(body_loc.start())) {
3196 match body {
3197 Statement::Block { statements, .. } if !statements.is_empty() => {
3198 fmt.write_whitespace_separator(false)?;
3199 fmt.visit_block(body_loc, statements, false, false)?;
3200 return Ok(())
3201 }
3202 _ => {
3203 attrs_multiline = false
3206 }
3207 }
3208 }
3209
3210 let byte_offset = body_loc.start();
3211 let body = fmt.visit_to_chunk(byte_offset, Some(body_loc.end()), body)?;
3212 fmt.write_whitespace_separator(
3213 attrs_multiline && !(func.attributes.is_empty() && func.returns.is_empty()),
3214 )?;
3215 fmt.write_chunk(&body)?;
3216 }
3217 None => fmt.write_semicolon()?,
3218 }
3219 Ok(())
3220 })?;
3221
3222 Ok(())
3223 }
3224
3225 #[instrument(name = "function_attribute", skip_all)]
3226 fn visit_function_attribute(&mut self, attribute: &mut FunctionAttribute) -> Result<()> {
3227 return_source_if_disabled!(self, attribute.loc());
3228
3229 match attribute {
3230 FunctionAttribute::Mutability(mutability) => {
3231 write_chunk!(self, mutability.loc().end(), "{mutability}")?
3232 }
3233 FunctionAttribute::Visibility(visibility) => {
3234 write_chunk!(self, visibility.loc_opt().unwrap().end(), "{visibility}")?
3236 }
3237 FunctionAttribute::Virtual(loc) => write_chunk!(self, loc.end(), "virtual")?,
3238 FunctionAttribute::Immutable(loc) => write_chunk!(self, loc.end(), "immutable")?,
3239 FunctionAttribute::Override(loc, args) => {
3240 write_chunk!(self, loc.start(), "override")?;
3241 if !args.is_empty() && self.config.override_spacing {
3242 self.write_whitespace_separator(false)?;
3243 }
3244 self.visit_list("", args, None, Some(loc.end()), false)?
3245 }
3246 FunctionAttribute::BaseOrModifier(loc, base) => {
3247 let is_constructor = self.context.is_constructor_function();
3253 let is_contract_base = self.context.contract.as_ref().is_some_and(|contract| {
3260 contract.base.iter().any(|contract_base| {
3261 contract_base
3262 .name
3263 .identifiers
3264 .iter()
3265 .zip(&base.name.identifiers)
3266 .all(|(l, r)| l.name == r.name)
3267 })
3268 });
3269
3270 if is_contract_base {
3271 base.visit(self)?;
3272 } else if is_constructor {
3273 let mut base_or_modifier =
3278 self.visit_to_chunk(loc.start(), Some(loc.end()), base)?;
3279 let is_lowercase =
3280 base_or_modifier.content.chars().next().is_some_and(|c| c.is_lowercase());
3281 if is_lowercase && base_or_modifier.content.ends_with("()") {
3282 base_or_modifier.content.truncate(base_or_modifier.content.len() - 2);
3283 }
3284
3285 self.write_chunk(&base_or_modifier)?;
3286 } else {
3287 let mut base_or_modifier =
3288 self.visit_to_chunk(loc.start(), Some(loc.end()), base)?;
3289 if base_or_modifier.content.ends_with("()") {
3290 base_or_modifier.content.truncate(base_or_modifier.content.len() - 2);
3291 }
3292 self.write_chunk(&base_or_modifier)?;
3293 }
3294 }
3295 FunctionAttribute::Error(loc) => self.visit_parser_error(*loc)?,
3296 };
3297
3298 Ok(())
3299 }
3300
3301 #[instrument(name = "var_attribute", skip_all)]
3302 fn visit_var_attribute(&mut self, attribute: &mut VariableAttribute) -> Result<()> {
3303 return_source_if_disabled!(self, attribute.loc());
3304
3305 let token = match attribute {
3306 VariableAttribute::Visibility(visibility) => Some(visibility.to_string()),
3307 VariableAttribute::Constant(_) => Some("constant".to_string()),
3308 VariableAttribute::Immutable(_) => Some("immutable".to_string()),
3309 VariableAttribute::Override(loc, idents) => {
3310 write_chunk!(self, loc.start(), "override")?;
3311 if !idents.is_empty() && self.config.override_spacing {
3312 self.write_whitespace_separator(false)?;
3313 }
3314 self.visit_list("", idents, Some(loc.start()), Some(loc.end()), false)?;
3315 None
3316 }
3317 };
3318 if let Some(token) = token {
3319 let loc = attribute.loc();
3320 write_chunk!(self, loc.start(), loc.end(), "{}", token)?;
3321 }
3322 Ok(())
3323 }
3324
3325 #[instrument(name = "base", skip_all)]
3326 fn visit_base(&mut self, base: &mut Base) -> Result<()> {
3327 return_source_if_disabled!(self, base.loc);
3328
3329 let name_loc = &base.name.loc;
3330 let mut name = self.chunked(name_loc.start(), Some(name_loc.end()), |fmt| {
3331 fmt.visit_ident_path(&mut base.name)?;
3332 Ok(())
3333 })?;
3334
3335 if base.args.is_none() || base.args.as_ref().unwrap().is_empty() {
3336 if self.context.function.is_some() {
3339 name.content.push_str("()");
3340 }
3341 self.write_chunk(&name)?;
3342 return Ok(())
3343 }
3344
3345 let args = base.args.as_mut().unwrap();
3346 let args_start = CodeLocation::loc(args.first().unwrap()).start();
3347
3348 name.content.push('(');
3349 let formatted_name = self.chunk_to_string(&name)?;
3350
3351 let multiline = !self.will_it_fit(&formatted_name);
3352
3353 self.surrounded(
3354 SurroundingChunk::new(&formatted_name, Some(args_start), None),
3355 SurroundingChunk::new(")", None, Some(base.loc.end())),
3356 |fmt, multiline_hint| {
3357 let args = fmt.items_to_chunks(
3358 Some(base.loc.end()),
3359 args.iter_mut().map(|arg| (arg.loc(), arg)),
3360 )?;
3361 let multiline = multiline ||
3362 multiline_hint ||
3363 fmt.are_chunks_separated_multiline("{}", &args, ",")?;
3364 fmt.write_chunks_separated(&args, ",", multiline)?;
3365 Ok(())
3366 },
3367 )?;
3368
3369 Ok(())
3370 }
3371
3372 #[instrument(name = "parameter", skip_all)]
3373 fn visit_parameter(&mut self, parameter: &mut Parameter) -> Result<()> {
3374 return_source_if_disabled!(self, parameter.loc);
3375 self.grouped(|fmt| {
3376 parameter.ty.visit(fmt)?;
3377 if let Some(storage) = ¶meter.storage {
3378 write_chunk!(fmt, storage.loc().end(), "{storage}")?;
3379 }
3380 if let Some(name) = ¶meter.name {
3381 write_chunk!(fmt, parameter.loc.end(), "{}", name.name)?;
3382 }
3383 Ok(())
3384 })?;
3385 Ok(())
3386 }
3387
3388 #[instrument(name = "struct", skip_all)]
3389 fn visit_struct(&mut self, structure: &mut StructDefinition) -> Result<()> {
3390 return_source_if_disabled!(self, structure.loc);
3391 self.grouped(|fmt| {
3392 let struct_name = structure.name.safe_unwrap_mut();
3393 write_chunk!(fmt, struct_name.loc.start(), "struct")?;
3394 struct_name.visit(fmt)?;
3395 if structure.fields.is_empty() {
3396 return fmt.write_empty_brackets()
3397 }
3398
3399 write!(fmt.buf(), " {{")?;
3400 fmt.surrounded(
3401 SurroundingChunk::new("", Some(struct_name.loc.end()), None),
3402 SurroundingChunk::new("}", None, Some(structure.loc.end())),
3403 |fmt, _multiline| {
3404 let chunks = fmt.items_to_chunks(
3405 Some(structure.loc.end()),
3406 structure.fields.iter_mut().map(|ident| (ident.loc, ident)),
3407 )?;
3408 for mut chunk in chunks {
3409 chunk.content.push(';');
3410 fmt.write_chunk(&chunk)?;
3411 fmt.write_whitespace_separator(true)?;
3412 }
3413 Ok(())
3414 },
3415 )
3416 })?;
3417
3418 Ok(())
3419 }
3420
3421 #[instrument(name = "event", skip_all)]
3422 fn visit_event(&mut self, event: &mut EventDefinition) -> Result<()> {
3423 return_source_if_disabled!(self, event.loc, ';');
3424
3425 let event_name = event.name.safe_unwrap_mut();
3426 let mut name =
3427 self.visit_to_chunk(event_name.loc.start(), Some(event.loc.end()), event_name)?;
3428 name.content = format!("event {}(", name.content);
3429
3430 let last_chunk = if event.anonymous { ") anonymous;" } else { ");" };
3431 if event.fields.is_empty() {
3432 name.content.push_str(last_chunk);
3433 self.write_chunk(&name)?;
3434 } else {
3435 let byte_offset = event.fields.first().unwrap().loc.start();
3436 let first_chunk = self.chunk_to_string(&name)?;
3437 self.surrounded(
3438 SurroundingChunk::new(first_chunk, Some(byte_offset), None),
3439 SurroundingChunk::new(last_chunk, None, Some(event.loc.end())),
3440 |fmt, multiline| {
3441 let params = fmt
3442 .items_to_chunks(None, event.fields.iter_mut().map(|arg| (arg.loc, arg)))?;
3443
3444 let multiline =
3445 multiline && fmt.are_chunks_separated_multiline("{}", ¶ms, ",")?;
3446 fmt.write_chunks_separated(¶ms, ",", multiline)
3447 },
3448 )?;
3449 }
3450
3451 Ok(())
3452 }
3453
3454 #[instrument(name = "event_parameter", skip_all)]
3455 fn visit_event_parameter(&mut self, param: &mut EventParameter) -> Result<()> {
3456 return_source_if_disabled!(self, param.loc);
3457
3458 self.grouped(|fmt| {
3459 param.ty.visit(fmt)?;
3460 if param.indexed {
3461 write_chunk!(fmt, param.loc.start(), "indexed")?;
3462 }
3463 if let Some(name) = ¶m.name {
3464 write_chunk!(fmt, name.loc.end(), "{}", name.name)?;
3465 }
3466 Ok(())
3467 })?;
3468 Ok(())
3469 }
3470
3471 #[instrument(name = "error", skip_all)]
3472 fn visit_error(&mut self, error: &mut ErrorDefinition) -> Result<()> {
3473 return_source_if_disabled!(self, error.loc, ';');
3474
3475 let error_name = error.name.safe_unwrap_mut();
3476 let mut name = self.visit_to_chunk(error_name.loc.start(), None, error_name)?;
3477 name.content = format!("error {}", name.content);
3478
3479 let formatted_name = self.chunk_to_string(&name)?;
3480 write!(self.buf(), "{formatted_name}")?;
3481 let start_offset = error.fields.first().map(|f| f.loc.start());
3482 self.visit_list("", &mut error.fields, start_offset, Some(error.loc.end()), true)?;
3483 self.write_semicolon()?;
3484
3485 Ok(())
3486 }
3487
3488 #[instrument(name = "error_parameter", skip_all)]
3489 fn visit_error_parameter(&mut self, param: &mut ErrorParameter) -> Result<()> {
3490 return_source_if_disabled!(self, param.loc);
3491 self.grouped(|fmt| {
3492 param.ty.visit(fmt)?;
3493 if let Some(name) = ¶m.name {
3494 write_chunk!(fmt, name.loc.end(), "{}", name.name)?;
3495 }
3496 Ok(())
3497 })?;
3498 Ok(())
3499 }
3500
3501 #[instrument(name = "type_definition", skip_all)]
3502 fn visit_type_definition(&mut self, def: &mut TypeDefinition) -> Result<()> {
3503 return_source_if_disabled!(self, def.loc, ';');
3504 self.grouped(|fmt| {
3505 write_chunk!(fmt, def.loc.start(), def.name.loc.start(), "type")?;
3506 def.name.visit(fmt)?;
3507 write_chunk!(fmt, def.name.loc.end(), CodeLocation::loc(&def.ty).start(), "is")?;
3508 def.ty.visit(fmt)?;
3509 fmt.write_semicolon()?;
3510 Ok(())
3511 })?;
3512 Ok(())
3513 }
3514
3515 #[instrument(name = "stray_semicolon", skip_all)]
3516 fn visit_stray_semicolon(&mut self) -> Result<()> {
3517 self.write_semicolon()
3518 }
3519
3520 #[instrument(name = "opening_paren", skip_all)]
3521 fn visit_opening_paren(&mut self) -> Result<()> {
3522 write_chunk!(self, "(")?;
3523 Ok(())
3524 }
3525
3526 #[instrument(name = "closing_paren", skip_all)]
3527 fn visit_closing_paren(&mut self) -> Result<()> {
3528 write_chunk!(self, ")")?;
3529 Ok(())
3530 }
3531
3532 #[instrument(name = "newline", skip_all)]
3533 fn visit_newline(&mut self) -> Result<()> {
3534 writeln_chunk!(self)?;
3535 Ok(())
3536 }
3537
3538 #[instrument(name = "using", skip_all)]
3539 fn visit_using(&mut self, using: &mut Using) -> Result<()> {
3540 return_source_if_disabled!(self, using.loc, ';');
3541
3542 write_chunk!(self, using.loc.start(), "using")?;
3543
3544 let ty_start = using.ty.as_mut().map(|ty| CodeLocation::loc(&ty).start());
3545 let global_start = using.global.as_mut().map(|global| global.loc.start());
3546 let loc_end = using.loc.end();
3547
3548 let (is_library, mut list_chunks) = match &mut using.list {
3549 UsingList::Library(library) => {
3550 (true, vec![self.visit_to_chunk(library.loc.start(), None, library)?])
3551 }
3552 UsingList::Functions(funcs) => {
3553 let mut funcs = funcs.iter_mut().peekable();
3554 let mut chunks = Vec::new();
3555 while let Some(func) = funcs.next() {
3556 let next_byte_end = funcs.peek().map(|func| func.loc.start());
3557 chunks.push(self.chunked(func.loc.start(), next_byte_end, |fmt| {
3558 fmt.visit_ident_path(&mut func.path)?;
3559 if let Some(op) = func.oper {
3560 write!(fmt.buf(), " as {op}")?;
3561 }
3562 Ok(())
3563 })?);
3564 }
3565 (false, chunks)
3566 }
3567 UsingList::Error => return self.visit_parser_error(using.loc),
3568 };
3569
3570 let for_chunk = self.chunk_at(
3571 using.loc.start(),
3572 Some(ty_start.or(global_start).unwrap_or(loc_end)),
3573 None,
3574 "for",
3575 );
3576 let ty_chunk = if let Some(ty) = &mut using.ty {
3577 self.visit_to_chunk(ty.loc().start(), Some(global_start.unwrap_or(loc_end)), ty)?
3578 } else {
3579 self.chunk_at(using.loc.start(), Some(global_start.unwrap_or(loc_end)), None, "*")
3580 };
3581 let global_chunk = using
3582 .global
3583 .as_mut()
3584 .map(|global| self.visit_to_chunk(global.loc.start(), Some(using.loc.end()), global))
3585 .transpose()?;
3586
3587 let write_for_def = |fmt: &mut Self| {
3588 fmt.grouped(|fmt| {
3589 fmt.write_chunk(&for_chunk)?;
3590 fmt.write_chunk(&ty_chunk)?;
3591 if let Some(global_chunk) = global_chunk.as_ref() {
3592 fmt.write_chunk(global_chunk)?;
3593 }
3594 Ok(())
3595 })?;
3596 Ok(())
3597 };
3598
3599 let simulated_for_def = self.simulate_to_string(write_for_def)?;
3600
3601 if is_library {
3602 let chunk = list_chunks.pop().unwrap();
3603 if self.will_chunk_fit(&format!("{{}} {simulated_for_def};"), &chunk)? {
3604 self.write_chunk(&chunk)?;
3605 write_for_def(self)?;
3606 } else {
3607 self.write_whitespace_separator(true)?;
3608 self.grouped(|fmt| {
3609 fmt.write_chunk(&chunk)?;
3610 Ok(())
3611 })?;
3612 self.write_whitespace_separator(true)?;
3613 write_for_def(self)?;
3614 }
3615 } else {
3616 self.surrounded(
3617 SurroundingChunk::new("{", Some(using.loc.start()), None),
3618 SurroundingChunk::new(
3619 "}",
3620 None,
3621 Some(ty_start.or(global_start).unwrap_or(loc_end)),
3622 ),
3623 |fmt, _multiline| {
3624 let multiline = fmt.are_chunks_separated_multiline(
3625 &format!("{{ {{}} }} {simulated_for_def};"),
3626 &list_chunks,
3627 ",",
3628 )?;
3629 fmt.write_chunks_separated(&list_chunks, ",", multiline)?;
3630 Ok(())
3631 },
3632 )?;
3633 write_for_def(self)?;
3634 }
3635
3636 self.write_semicolon()?;
3637
3638 Ok(())
3639 }
3640
3641 #[instrument(name = "yul_block", skip_all)]
3642 fn visit_yul_block(
3643 &mut self,
3644 loc: Loc,
3645 statements: &mut Vec<YulStatement>,
3646 attempt_single_line: bool,
3647 ) -> Result<(), Self::Error> {
3648 return_source_if_disabled!(self, loc);
3649 self.visit_block(loc, statements, attempt_single_line, false)?;
3650 Ok(())
3651 }
3652
3653 #[instrument(name = "yul_expr", skip_all)]
3654 fn visit_yul_expr(&mut self, expr: &mut YulExpression) -> Result<(), Self::Error> {
3655 return_source_if_disabled!(self, expr.loc());
3656
3657 match expr {
3658 YulExpression::BoolLiteral(loc, val, ident) => {
3659 let val = if *val { "true" } else { "false" };
3660 self.visit_yul_string_with_ident(*loc, val, ident)
3661 }
3662 YulExpression::FunctionCall(expr) => self.visit_yul_function_call(expr),
3663 YulExpression::HexNumberLiteral(loc, val, ident) => {
3664 self.visit_yul_string_with_ident(*loc, val, ident)
3665 }
3666 YulExpression::HexStringLiteral(val, ident) => self.visit_yul_string_with_ident(
3667 val.loc,
3668 &self.quote_str(val.loc, Some("hex"), &val.hex),
3669 ident,
3670 ),
3671 YulExpression::NumberLiteral(loc, val, expr, ident) => {
3672 let val = if expr.is_empty() { val.to_owned() } else { format!("{val}e{expr}") };
3673 self.visit_yul_string_with_ident(*loc, &val, ident)
3674 }
3675 YulExpression::StringLiteral(val, ident) => self.visit_yul_string_with_ident(
3676 val.loc,
3677 &self.quote_str(val.loc, None, &val.string),
3678 ident,
3679 ),
3680 YulExpression::SuffixAccess(_, expr, ident) => {
3681 self.visit_member_access(expr, ident, |fmt, expr| match expr.as_mut() {
3682 YulExpression::SuffixAccess(_, inner_expr, inner_ident) => {
3683 Ok(Some((inner_expr, inner_ident)))
3684 }
3685 expr => {
3686 expr.visit(fmt)?;
3687 Ok(None)
3688 }
3689 })
3690 }
3691 YulExpression::Variable(ident) => {
3692 write_chunk!(self, ident.loc.start(), ident.loc.end(), "{}", ident.name)
3693 }
3694 }
3695 }
3696
3697 #[instrument(name = "yul_assignment", skip_all)]
3698 fn visit_yul_assignment<T>(
3699 &mut self,
3700 loc: Loc,
3701 exprs: &mut Vec<T>,
3702 expr: &mut Option<&mut YulExpression>,
3703 ) -> Result<(), Self::Error>
3704 where
3705 T: Visitable + CodeLocation,
3706 {
3707 return_source_if_disabled!(self, loc);
3708
3709 self.grouped(|fmt| {
3710 let chunks =
3711 fmt.items_to_chunks(None, exprs.iter_mut().map(|expr| (expr.loc(), expr)))?;
3712
3713 let multiline = fmt.are_chunks_separated_multiline("{} := ", &chunks, ",")?;
3714 fmt.write_chunks_separated(&chunks, ",", multiline)?;
3715
3716 if let Some(expr) = expr {
3717 write_chunk!(fmt, expr.loc().start(), ":=")?;
3718 let chunk = fmt.visit_to_chunk(expr.loc().start(), Some(loc.end()), expr)?;
3719 if !fmt.will_chunk_fit("{}", &chunk)? {
3720 fmt.write_whitespace_separator(true)?;
3721 }
3722 fmt.write_chunk(&chunk)?;
3723 }
3724 Ok(())
3725 })?;
3726 Ok(())
3727 }
3728
3729 #[instrument(name = "yul_for", skip_all)]
3730 fn visit_yul_for(&mut self, stmt: &mut YulFor) -> Result<(), Self::Error> {
3731 return_source_if_disabled!(self, stmt.loc);
3732 write_chunk!(self, stmt.loc.start(), "for")?;
3733 self.visit_yul_block(stmt.init_block.loc, &mut stmt.init_block.statements, true)?;
3734 stmt.condition.visit(self)?;
3735 self.visit_yul_block(stmt.post_block.loc, &mut stmt.post_block.statements, true)?;
3736 self.visit_yul_block(stmt.execution_block.loc, &mut stmt.execution_block.statements, true)?;
3737 Ok(())
3738 }
3739
3740 #[instrument(name = "yul_function_call", skip_all)]
3741 fn visit_yul_function_call(&mut self, stmt: &mut YulFunctionCall) -> Result<(), Self::Error> {
3742 return_source_if_disabled!(self, stmt.loc);
3743 write_chunk!(self, stmt.loc.start(), "{}", stmt.id.name)?;
3744 self.visit_list("", &mut stmt.arguments, None, Some(stmt.loc.end()), true)
3745 }
3746
3747 #[instrument(name = "yul_fun_def", skip_all)]
3748 fn visit_yul_fun_def(&mut self, stmt: &mut YulFunctionDefinition) -> Result<(), Self::Error> {
3749 return_source_if_disabled!(self, stmt.loc);
3750
3751 write_chunk!(self, stmt.loc.start(), "function {}", stmt.id.name)?;
3752
3753 self.visit_list("", &mut stmt.params, None, None, true)?;
3754
3755 if !stmt.returns.is_empty() {
3756 self.grouped(|fmt| {
3757 write_chunk!(fmt, "->")?;
3758
3759 let chunks = fmt.items_to_chunks(
3760 Some(stmt.body.loc.start()),
3761 stmt.returns.iter_mut().map(|param| (param.loc, param)),
3762 )?;
3763 let multiline = fmt.are_chunks_separated_multiline("{}", &chunks, ",")?;
3764 fmt.write_chunks_separated(&chunks, ",", multiline)?;
3765 if multiline {
3766 fmt.write_whitespace_separator(true)?;
3767 }
3768 Ok(())
3769 })?;
3770 }
3771
3772 stmt.body.visit(self)?;
3773
3774 Ok(())
3775 }
3776
3777 #[instrument(name = "yul_if", skip_all)]
3778 fn visit_yul_if(
3779 &mut self,
3780 loc: Loc,
3781 expr: &mut YulExpression,
3782 block: &mut YulBlock,
3783 ) -> Result<(), Self::Error> {
3784 return_source_if_disabled!(self, loc);
3785 write_chunk!(self, loc.start(), "if")?;
3786 expr.visit(self)?;
3787 self.visit_yul_block(block.loc, &mut block.statements, true)
3788 }
3789
3790 #[instrument(name = "yul_leave", skip_all)]
3791 fn visit_yul_leave(&mut self, loc: Loc) -> Result<(), Self::Error> {
3792 return_source_if_disabled!(self, loc);
3793 write_chunk!(self, loc.start(), loc.end(), "leave")
3794 }
3795
3796 #[instrument(name = "yul_switch", skip_all)]
3797 fn visit_yul_switch(&mut self, stmt: &mut YulSwitch) -> Result<(), Self::Error> {
3798 return_source_if_disabled!(self, stmt.loc);
3799
3800 write_chunk!(self, stmt.loc.start(), "switch")?;
3801 stmt.condition.visit(self)?;
3802 writeln_chunk!(self)?;
3803 let mut cases = stmt.cases.iter_mut().peekable();
3804 while let Some(YulSwitchOptions::Case(loc, expr, block)) = cases.next() {
3805 write_chunk!(self, loc.start(), "case")?;
3806 expr.visit(self)?;
3807 self.visit_yul_block(block.loc, &mut block.statements, true)?;
3808 let is_last = cases.peek().is_none();
3809 if !is_last || stmt.default.is_some() {
3810 writeln_chunk!(self)?;
3811 }
3812 }
3813 if let Some(YulSwitchOptions::Default(loc, ref mut block)) = stmt.default {
3814 write_chunk!(self, loc.start(), "default")?;
3815 self.visit_yul_block(block.loc, &mut block.statements, true)?;
3816 }
3817 Ok(())
3818 }
3819
3820 #[instrument(name = "yul_var_declaration", skip_all)]
3821 fn visit_yul_var_declaration(
3822 &mut self,
3823 loc: Loc,
3824 idents: &mut Vec<YulTypedIdentifier>,
3825 expr: &mut Option<YulExpression>,
3826 ) -> Result<(), Self::Error> {
3827 return_source_if_disabled!(self, loc);
3828 self.grouped(|fmt| {
3829 write_chunk!(fmt, loc.start(), "let")?;
3830 fmt.visit_yul_assignment(loc, idents, &mut expr.as_mut())
3831 })?;
3832 Ok(())
3833 }
3834
3835 #[instrument(name = "yul_typed_ident", skip_all)]
3836 fn visit_yul_typed_ident(&mut self, ident: &mut YulTypedIdentifier) -> Result<(), Self::Error> {
3837 return_source_if_disabled!(self, ident.loc);
3838 self.visit_yul_string_with_ident(ident.loc, &ident.id.name, &mut ident.ty)
3839 }
3840
3841 #[instrument(name = "parser_error", skip_all)]
3842 fn visit_parser_error(&mut self, loc: Loc) -> Result<()> {
3843 Err(FormatterError::InvalidParsedItem(loc))
3844 }
3845}
3846
3847struct Transaction<'f, 'a, W> {
3849 fmt: &'f mut Formatter<'a, W>,
3850 buffer: String,
3851 comments: Comments,
3852}
3853
3854impl<'a, W> std::ops::Deref for Transaction<'_, 'a, W> {
3855 type Target = Formatter<'a, W>;
3856 fn deref(&self) -> &Self::Target {
3857 self.fmt
3858 }
3859}
3860
3861impl<W> std::ops::DerefMut for Transaction<'_, '_, W> {
3862 fn deref_mut(&mut self) -> &mut Self::Target {
3863 self.fmt
3864 }
3865}
3866
3867impl<'f, 'a, W: Write> Transaction<'f, 'a, W> {
3868 fn new(
3870 fmt: &'f mut Formatter<'a, W>,
3871 fun: impl FnMut(&mut Formatter<'a, W>) -> Result<()>,
3872 ) -> Result<Self> {
3873 let mut comments = fmt.comments.clone();
3874 let buffer = fmt.with_temp_buf(fun)?.w;
3875 comments = std::mem::replace(&mut fmt.comments, comments);
3876 Ok(Self { fmt, buffer, comments })
3877 }
3878
3879 fn commit(self) -> Result<String> {
3881 self.fmt.comments = self.comments;
3882 write_chunk!(self.fmt, "{}", self.buffer)?;
3883 Ok(self.buffer)
3884 }
3885}