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