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