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