1#![allow(clippy::too_many_arguments)]
2
3use super::{
4 CommentConfig, Separator, State,
5 common::{BlockFormat, ListFormat},
6};
7use crate::{
8 pp::SIZE_INFINITY,
9 state::{CallContext, common::LitExt},
10};
11use foundry_common::{comments::Comment, iter::IterDelimited};
12use foundry_config::fmt::{self as config, MultilineFuncHeaderStyle};
13use solar::{
14 ast::BoxSlice,
15 interface::SpannedOption,
16 parse::{
17 ast::{self, Span},
18 interface::BytePos,
19 },
20};
21use std::{collections::HashMap, fmt::Debug};
22
23#[rustfmt::skip]
24macro_rules! get_span {
25 () => { |value| value.span };
26 (()) => { |value| value.span() };
27}
28
29impl<'ast> State<'_, 'ast> {
31 pub(crate) fn print_source_unit(&mut self, source_unit: &'ast ast::SourceUnit<'ast>) {
32 if let Some(item) = source_unit.items.first() {
34 self.check_crlf(item.span.to(source_unit.items.last().unwrap().span));
35 }
36
37 let mut items = source_unit.items.iter().peekable();
38 let mut is_first = true;
39 while let Some(item) = items.next() {
40 if !self.config.sort_imports || !matches!(item.kind, ast::ItemKind::Import(_)) {
42 self.print_item(item, is_first);
43 is_first = false;
44 if let Some(next_item) = items.peek() {
45 self.separate_items(next_item, false);
46 }
47 continue;
48 }
49
50 let mut import_group = vec![item];
52 while let Some(next_item) = items.peek() {
53 if !matches!(next_item.kind, ast::ItemKind::Import(_))
55 || self.has_comment_between(item.span.hi(), next_item.span.lo())
56 {
57 break;
58 }
59 import_group.push(items.next().unwrap());
60 }
61
62 import_group.sort_by_key(|item| {
63 if let ast::ItemKind::Import(import) = &item.kind {
64 import.path.value.as_str()
65 } else {
66 unreachable!("Expected an import item")
67 }
68 });
69
70 for (pos, group_item) in import_group.iter().delimited() {
71 self.print_item(group_item, is_first);
72 is_first = false;
73
74 if !pos.is_last {
75 self.hardbreak_if_not_bol();
76 }
77 }
78 if let Some(next_item) = items.peek() {
79 self.separate_items(next_item, false);
80 }
81 }
82
83 self.print_remaining_comments(is_first);
84 }
85
86 fn separate_items(&mut self, next_item: &'ast ast::Item<'ast>, advance: bool) {
88 if !item_needs_iso(&next_item.kind) {
89 return;
90 }
91 let span = next_item.span;
92
93 let cmnts = self
94 .comments
95 .iter()
96 .filter_map(|c| if c.pos() < span.lo() { Some(c.style) } else { None })
97 .collect::<Vec<_>>();
98
99 if let Some(first) = cmnts.first()
100 && let Some(last) = cmnts.last()
101 {
102 if !(first.is_blank() || last.is_blank()) {
103 self.hardbreak();
104 return;
105 }
106 if advance {
107 if self.peek_comment_before(span.lo()).is_some() {
108 self.print_comments(span.lo(), CommentConfig::default());
109 } else if self.inline_config.is_disabled(span.shrink_to_lo()) {
110 self.hardbreak();
111 self.cursor.advance_to(span.lo(), true);
112 }
113 }
114 } else {
115 self.hardbreak();
116 }
117 }
118
119 fn print_item(&mut self, item: &'ast ast::Item<'ast>, skip_ws: bool) {
120 let ast::Item { ref docs, span, ref kind } = *item;
121 self.print_docs(docs);
122
123 if self.handle_span(item.span, skip_ws) {
124 if !self.print_trailing_comment(span.hi(), None) {
125 self.print_sep(Separator::Hardbreak);
126 }
127 return;
128 }
129
130 if self
131 .print_comments(
132 span.lo(),
133 if skip_ws {
134 CommentConfig::skip_leading_ws(false)
135 } else {
136 CommentConfig::default()
137 },
138 )
139 .is_some_and(|cmnt| cmnt.is_mixed())
140 {
141 self.zerobreak();
142 }
143
144 match kind {
145 ast::ItemKind::Pragma(pragma) => self.print_pragma(pragma),
146 ast::ItemKind::Import(import) => self.print_import(import),
147 ast::ItemKind::Using(using) => self.print_using(using),
148 ast::ItemKind::Contract(contract) => self.print_contract(contract, span),
149 ast::ItemKind::Function(func) => self.print_function(func),
150 ast::ItemKind::Variable(var) => self.print_var_def(var),
151 ast::ItemKind::Struct(strukt) => self.print_struct(strukt, span),
152 ast::ItemKind::Enum(enm) => self.print_enum(enm, span),
153 ast::ItemKind::Udvt(udvt) => self.print_udvt(udvt),
154 ast::ItemKind::Error(err) => self.print_error(err),
155 ast::ItemKind::Event(event) => self.print_event(event),
156 }
157
158 self.cursor.advance_to(span.hi(), true);
159 self.print_comments(span.hi(), CommentConfig::default());
160 self.print_trailing_comment(span.hi(), None);
161 self.hardbreak_if_not_bol();
162 self.cursor.next_line(self.is_at_crlf());
163 }
164
165 fn print_pragma(&mut self, pragma: &'ast ast::PragmaDirective<'ast>) {
166 self.word("pragma ");
167 match &pragma.tokens {
168 ast::PragmaTokens::Version(ident, semver_req) => {
169 self.print_ident(ident);
170 self.nbsp();
171 self.word(semver_req.to_string());
172 }
173 ast::PragmaTokens::Custom(a, b) => {
174 self.print_ident_or_strlit(a);
175 if let Some(b) = b {
176 self.nbsp();
177 self.print_ident_or_strlit(b);
178 }
179 }
180 ast::PragmaTokens::Verbatim(tokens) => {
181 self.print_tokens(tokens);
182 }
183 }
184 self.word(";");
185 }
186
187 fn print_commasep_aliases<'a, I>(&mut self, aliases: I)
188 where
189 I: Iterator<Item = &'a (ast::Ident, Option<ast::Ident>)>,
190 'ast: 'a,
191 {
192 for (pos, (ident, alias)) in aliases.delimited() {
193 self.print_ident(ident);
194 if let Some(alias) = alias {
195 self.word(" as ");
196 self.print_ident(alias);
197 }
198 if !pos.is_last {
199 self.word(",");
200 self.space();
201 }
202 }
203 }
204
205 fn print_import(&mut self, import: &'ast ast::ImportDirective<'ast>) {
206 let ast::ImportDirective { path, items } = import;
207 self.word("import ");
208 match items {
209 ast::ImportItems::Plain(_) | ast::ImportItems::Glob(_) => {
210 self.print_ast_str_lit(path);
211 if let Some(ident) = items.source_alias() {
212 self.word(" as ");
213 self.print_ident(&ident);
214 }
215 }
216
217 ast::ImportItems::Aliases(aliases) => {
218 self.s.cbox(self.ind);
219 self.word("{");
220 self.braces_break();
221
222 if self.config.sort_imports {
223 let mut sorted: Vec<_> = aliases.iter().collect();
224 sorted.sort_by_key(|(ident, _alias)| ident.name.as_str());
225 self.print_commasep_aliases(sorted.into_iter());
226 } else {
227 self.print_commasep_aliases(aliases.iter());
228 };
229
230 self.braces_break();
231 self.s.offset(-self.ind);
232 self.word("}");
233 self.end();
234 self.word(" from ");
235 self.print_ast_str_lit(path);
236 }
237 }
238 self.word(";");
239 }
240
241 fn print_using(&mut self, using: &'ast ast::UsingDirective<'ast>) {
242 let ast::UsingDirective { list, ty, global } = using;
243 self.word("using ");
244 match list {
245 ast::UsingList::Single(path) => self.print_path(path, true),
246 ast::UsingList::Multiple(items) => {
247 self.s.cbox(self.ind);
248 self.word("{");
249 self.braces_break();
250 for (pos, (path, op)) in items.iter().delimited() {
251 self.print_path(path, true);
252 if let Some(op) = op {
253 self.word(" as ");
254 self.word(op.to_str());
255 }
256 if !pos.is_last {
257 self.word(",");
258 self.space();
259 }
260 }
261 self.braces_break();
262 self.s.offset(-self.ind);
263 self.word("}");
264 self.end();
265 }
266 }
267 self.word(" for ");
268 if let Some(ty) = ty {
269 self.print_ty(ty);
270 } else {
271 self.word("*");
272 }
273 if *global {
274 self.word(" global");
275 }
276 self.word(";");
277 }
278
279 fn print_contract(&mut self, c: &'ast ast::ItemContract<'ast>, span: Span) {
280 let ast::ItemContract { kind, name, layout, bases, body } = c;
281 self.contract = Some(c);
282 self.cursor.advance_to(span.lo(), true);
283
284 self.s.cbox(self.ind);
285 self.ibox(0);
286 self.cbox(0);
287 self.word_nbsp(kind.to_str());
288 self.print_ident(name);
289 self.nbsp();
290
291 if let Some(first) = bases.first().map(|base| base.span())
292 && let Some(last) = bases.last().map(|base| base.span())
293 && self.inline_config.is_disabled(first.to(last))
294 {
295 _ = self.handle_span(first.until(last), false);
296 } else if !bases.is_empty() {
297 self.word("is");
298 self.space();
299 for (pos, base) in bases.iter().delimited() {
300 if !self.handle_span(base.span(), false) {
301 self.print_modifier_call(base, false);
302 if !pos.is_last {
303 self.word(",");
304 self.space();
305 }
306 }
307 }
308 self.space();
309 self.s.offset(-self.ind);
310 }
311 self.end();
312 if let Some(layout) = layout
313 && !self.handle_span(layout.span, false)
314 {
315 self.word("layout at ");
316 self.print_expr(layout.slot);
317 self.print_sep(Separator::Space);
318 }
319
320 self.print_word("{");
321 self.end();
322 if !body.is_empty() {
323 self.block_depth += 1;
325
326 self.print_sep(Separator::Hardbreak);
327 if self.config.contract_new_lines {
328 self.hardbreak();
329 }
330 let body_lo = body[0].span.lo();
331 if self.peek_comment_before(body_lo).is_some() {
332 self.print_comments(body_lo, CommentConfig::skip_leading_ws(true));
333 }
334
335 let mut is_first = true;
336 let mut items = body.iter().peekable();
337 while let Some(item) = items.next() {
338 self.print_item(item, is_first);
339 is_first = false;
340 if let Some(next_item) = items.peek() {
341 if self.inline_config.is_disabled(next_item.span) {
342 _ = self.handle_span(next_item.span, false);
343 } else {
344 self.separate_items(next_item, true);
345 }
346 }
347 }
348
349 if let Some(cmnt) = self.print_comments(span.hi(), CommentConfig::skip_trailing_ws())
350 && self.config.contract_new_lines
351 && !cmnt.is_blank()
352 {
353 self.print_sep(Separator::Hardbreak);
354 }
355 self.s.offset(-self.ind);
356 self.end();
357 if self.config.contract_new_lines {
358 self.hardbreak_if_nonempty();
359 }
360
361 self.block_depth -= 1;
363 } else {
364 if self.print_comments(span.hi(), CommentConfig::skip_ws()).is_some() {
365 self.zerobreak();
366 } else if self.config.bracket_spacing {
367 self.nbsp();
368 };
369 self.end();
370 }
371 self.print_word("}");
372
373 self.cursor.advance_to(span.hi(), true);
374 self.contract = None;
375 }
376
377 fn print_struct(&mut self, strukt: &'ast ast::ItemStruct<'ast>, span: Span) {
378 let ast::ItemStruct { name, fields } = strukt;
379 let ind = if self.estimate_size(name.span) + 8 >= self.space_left() { self.ind } else { 0 };
380 self.s.ibox(self.ind);
381 self.word("struct");
382 self.space();
383 self.print_ident(name);
384 self.word(" {");
385 if !fields.is_empty() {
386 self.break_offset(SIZE_INFINITY as usize, ind);
387 }
388 self.s.ibox(0);
389 for var in fields.iter() {
390 self.print_var_def(var);
391 if !self.print_trailing_comment(var.span.hi(), None) {
392 self.hardbreak();
393 }
394 }
395 self.print_comments(span.hi(), CommentConfig::skip_ws());
396 if ind == 0 {
397 self.s.offset(-self.ind);
398 }
399 self.end();
400 self.end();
401 self.word("}");
402 }
403
404 fn print_enum(&mut self, enm: &'ast ast::ItemEnum<'ast>, span: Span) {
405 let ast::ItemEnum { name, variants } = enm;
406 self.s.cbox(self.ind);
407 self.word("enum ");
408 self.print_ident(name);
409 self.word(" {");
410 self.hardbreak_if_nonempty();
411 for (pos, ident) in variants.iter().delimited() {
412 self.print_comments(ident.span.lo(), CommentConfig::default());
413 self.print_ident(ident);
414 if !pos.is_last {
415 self.word(",");
416 }
417 if !self.print_trailing_comment(ident.span.hi(), None) {
418 self.hardbreak();
419 }
420 }
421 self.print_comments(span.hi(), CommentConfig::skip_ws());
422 self.s.offset(-self.ind);
423 self.end();
424 self.word("}");
425 }
426
427 fn print_udvt(&mut self, udvt: &'ast ast::ItemUdvt<'ast>) {
428 let ast::ItemUdvt { name, ty } = udvt;
429 self.word("type ");
430 self.print_ident(name);
431 self.word(" is ");
432 self.print_ty(ty);
433 self.word(";");
434 }
435
436 fn print_function(&mut self, func: &'ast ast::ItemFunction<'ast>) {
438 let ast::ItemFunction { kind, ref header, ref body, body_span } = *func;
439 let ast::FunctionHeader {
440 name,
441 ref parameters,
442 visibility,
443 state_mutability: sm,
444 virtual_,
445 ref override_,
446 ref returns,
447 ..
448 } = *header;
449
450 self.s.cbox(self.ind);
451
452 _ = self.handle_span(self.cursor.span(header.span.lo()), false);
454 self.print_word(kind.to_str());
455 if let Some(name) = name {
456 self.print_sep(Separator::Nbsp);
457 self.print_ident(&name);
458 self.cursor.advance_to(name.span.hi(), true);
459 }
460 self.s.cbox(-self.ind);
461 let header_style = self.config.multiline_func_header;
462 let params_format = match header_style {
463 MultilineFuncHeaderStyle::ParamsAlways => ListFormat::always_break(),
464 MultilineFuncHeaderStyle::AllParams
465 if !header.parameters.is_empty() && !self.can_header_be_inlined(header) =>
466 {
467 ListFormat::always_break()
468 }
469 _ => ListFormat::consistent().break_cmnts().break_single(
470 parameters.len() == 1
472 && matches!(
473 ¶meters[0].ty,
474 ast::Type { kind: ast::TypeKind::Custom(ty), .. } if ty.segments().len() > 1
475 ),
476 ),
477 };
478 self.print_parameter_list(parameters, parameters.span, params_format);
479 self.end();
480
481 let (mut map, attributes, first_attrib_pos) =
483 AttributeCommentMapper::new(returns.as_ref(), body_span.lo()).build(self, header);
484
485 let mut handle_pre_cmnts = |this: &mut Self, span: Span| -> bool {
486 if this.inline_config.is_disabled(span)
487 && let Some((pre_cmnts, ..)) = map.remove(&span.lo())
489 {
490 for (pos, cmnt) in pre_cmnts.into_iter().delimited() {
491 if pos.is_first && cmnt.style.is_isolated() && !this.is_bol_or_only_ind() {
492 this.print_sep(Separator::Hardbreak);
493 }
494 if let Some(cmnt) = this.handle_comment(cmnt, false) {
495 this.print_comment(cmnt, CommentConfig::skip_ws().mixed_post_nbsp());
496 }
497 if pos.is_last {
498 return true;
499 }
500 }
501 }
502 false
503 };
504
505 let skip_attribs = returns.as_ref().is_some_and(|ret| {
506 let attrib_span = Span::new(first_attrib_pos, ret.span.lo());
507 handle_pre_cmnts(self, attrib_span);
508 self.handle_span(attrib_span, false)
509 });
510 let skip_returns = {
511 let pos = if skip_attribs { self.cursor.pos } else { first_attrib_pos };
512 let ret_span = Span::new(pos, body_span.lo());
513 handle_pre_cmnts(self, ret_span);
514 self.handle_span(ret_span, false)
515 };
516
517 let attrib_box = self.config.multiline_func_header.params_first()
518 || (self.config.multiline_func_header.attrib_first()
519 && !self.can_header_params_be_inlined(header));
520 if attrib_box {
521 self.s.cbox(0);
522 }
523 if !(skip_attribs || skip_returns) {
524 if let Some(v) = visibility {
526 self.print_fn_attribute(v.span, &mut map, &mut |s| s.word(v.to_str()));
527 }
528 if let Some(sm) = sm
529 && !matches!(*sm, ast::StateMutability::NonPayable)
530 {
531 self.print_fn_attribute(sm.span, &mut map, &mut |s| s.word(sm.to_str()));
532 }
533 if let Some(v) = virtual_ {
534 self.print_fn_attribute(v, &mut map, &mut |s| s.word("virtual"));
535 }
536 if let Some(o) = override_ {
537 self.print_fn_attribute(o.span, &mut map, &mut |s| s.print_override(o));
538 }
539 for m in attributes.iter().filter(|a| matches!(a.kind, AttributeKind::Modifier(_))) {
540 if let AttributeKind::Modifier(modifier) = m.kind {
541 let is_base = self.is_modifier_a_base_contract(kind, modifier);
542 self.print_fn_attribute(m.span, &mut map, &mut |s| {
543 s.print_modifier_call(modifier, is_base)
544 });
545 }
546 }
547 }
548 if !skip_returns
549 && let Some(ret) = returns
550 && !ret.is_empty()
551 && let Some(ret) = returns
552 {
553 if !self.handle_span(self.cursor.span(ret.span.lo()), false) {
554 if !self.is_bol_or_only_ind() && !self.last_token_is_space() {
555 self.print_sep(Separator::Space);
556 }
557 self.cursor.advance_to(ret.span.lo(), true);
558 self.print_word("returns ");
559 }
560 self.print_parameter_list(
561 ret,
562 ret.span,
563 ListFormat::consistent(), );
565 }
566
567 if let Some(body) = body {
569 if self.handle_span(self.cursor.span(body_span.lo()), false) {
570 } else {
572 if let Some(cmnt) = self.peek_comment_before(body_span.lo()) {
573 if cmnt.style.is_mixed() {
574 self.space();
576 self.s.offset(-self.ind);
577 self.print_comments(body_span.lo(), CommentConfig::skip_ws());
578 } else {
579 self.zerobreak();
580 self.s.offset(-self.ind);
581 self.print_comments(body_span.lo(), CommentConfig::skip_ws());
582 self.s.offset(-self.ind);
583 }
584 } else {
585 if header.modifiers.is_empty()
587 && header.override_.is_none()
588 && returns.as_ref().is_none_or(|r| r.is_empty())
589 && (header.visibility().is_none() || body.is_empty())
590 {
591 self.nbsp();
592 } else {
593 self.space();
594 self.s.offset(-self.ind);
595 }
596 }
597 self.cursor.advance_to(body_span.lo(), true);
598 }
599 self.print_word("{");
600 self.end();
601 if attrib_box {
602 self.end();
603 }
604
605 self.print_block_without_braces(body, body_span.hi(), Some(self.ind));
606 if self.cursor.enabled || self.cursor.pos < body_span.hi() {
607 self.print_word("}");
608 self.cursor.advance_to(body_span.hi(), true);
609 }
610 } else {
611 self.print_comments(body_span.lo(), CommentConfig::skip_ws().mixed_prev_space());
612 self.end();
613 if attrib_box {
614 self.end();
615 }
616 self.neverbreak();
617 self.print_word(";");
618 }
619
620 if let Some(cmnt) = self.peek_trailing_comment(body_span.hi(), None) {
621 if cmnt.is_doc {
622 self.hardbreak();
625 self.hardbreak();
626 }
627 self.print_trailing_comment(body_span.hi(), None);
628 }
629 }
630
631 fn print_fn_attribute(
632 &mut self,
633 span: Span,
634 map: &mut AttributeCommentMap,
635 print_fn: &mut dyn FnMut(&mut Self),
636 ) {
637 match map.remove(&span.lo()) {
638 Some((pre_cmnts, inner_cmnts, post_cmnts)) => {
639 for cmnt in pre_cmnts {
641 let Some(cmnt) = self.handle_comment(cmnt, false) else {
642 continue;
643 };
644 self.print_comment(cmnt, CommentConfig::default());
645 }
646 for cmnt in inner_cmnts.into_iter().rev() {
649 self.comments.push_front(cmnt);
650 }
651 let mut enabled = false;
652 if !self.handle_span(span, false) {
653 if !self.is_bol_or_only_ind() {
654 self.space();
655 }
656 self.ibox(0);
657 print_fn(self);
658 self.cursor.advance_to(span.hi(), true);
659 enabled = true;
660 }
661 for cmnt in post_cmnts {
663 let Some(cmnt) = self.handle_comment(cmnt, false) else {
664 continue;
665 };
666 self.print_comment(cmnt, CommentConfig::default().mixed_prev_space());
667 }
668 if enabled {
669 self.end();
670 }
671 }
672 None => {
674 if !self.is_bol_or_only_ind() {
675 self.space();
676 }
677 print_fn(self);
678 self.cursor.advance_to(span.hi(), true);
679 }
680 }
681 }
682
683 fn is_modifier_a_base_contract(
684 &self,
685 kind: ast::FunctionKind,
686 modifier: &'ast ast::Modifier<'ast>,
687 ) -> bool {
688 let is_contract_base = self.contract.is_some_and(|contract| {
693 contract
694 .bases
695 .iter()
696 .any(|contract_base| contract_base.name.to_string() == modifier.name.to_string())
697 });
698 let is_constructor = matches!(kind, ast::FunctionKind::Constructor);
701 is_contract_base
703 || (is_constructor
704 && modifier.name.first().name.as_str().starts_with(char::is_uppercase))
705 }
706
707 fn print_error(&mut self, err: &'ast ast::ItemError<'ast>) {
708 let ast::ItemError { name, parameters } = err;
709 self.word("error ");
710 self.print_ident(name);
711 self.print_parameter_list(parameters, parameters.span, ListFormat::compact());
712 self.word(";");
713 }
714
715 fn print_event(&mut self, event: &'ast ast::ItemEvent<'ast>) {
716 let ast::ItemEvent { name, parameters, anonymous } = event;
717 self.word("event ");
718 self.print_ident(name);
719 self.print_parameter_list(parameters, parameters.span, ListFormat::compact().break_cmnts());
720 if *anonymous {
721 self.word(" anonymous");
722 }
723 self.word(";");
724 }
725
726 fn print_var_def(&mut self, var: &'ast ast::VariableDefinition<'ast>) {
727 self.print_var(var, true);
728 self.word(";");
729 }
730
731 fn print_var(&mut self, var: &'ast ast::VariableDefinition<'ast>, is_var_def: bool) {
732 let ast::VariableDefinition {
733 span,
734 ty,
735 visibility,
736 mutability,
737 data_location,
738 override_,
739 indexed,
740 name,
741 initializer,
742 } = var;
743
744 if self.handle_span(*span, false) {
745 return;
746 }
747
748 let init_space_left = self.space_left();
753 let mut pre_init_size = self.estimate_size(ty.span);
754
755 self.s.ibox(0);
757 if override_.is_some() {
758 self.s.cbox(self.ind);
759 } else {
760 self.s.ibox(self.ind);
761 }
762 self.print_ty(ty);
763
764 self.print_attribute(visibility.map(|v| v.to_str()), is_var_def, &mut pre_init_size);
765 self.print_attribute(mutability.map(|m| m.to_str()), is_var_def, &mut pre_init_size);
766 self.print_attribute(data_location.map(|d| d.to_str()), is_var_def, &mut pre_init_size);
767
768 if let Some(override_) = override_ {
769 if self
770 .print_comments(override_.span.lo(), CommentConfig::skip_ws().mixed_prev_space())
771 .is_none()
772 {
773 self.print_sep(Separator::SpaceOrNbsp(is_var_def));
774 }
775 self.ibox(0);
776 self.print_override(override_);
777 pre_init_size += self.estimate_size(override_.span) + 1;
778 }
779
780 if *indexed {
781 self.print_attribute(indexed.then_some("indexed"), is_var_def, &mut pre_init_size);
782 }
783
784 if let Some(ident) = name {
785 self.print_sep(Separator::SpaceOrNbsp(is_var_def && override_.is_none()));
786 self.print_comments(
787 ident.span.lo(),
788 CommentConfig::skip_ws().mixed_no_break().mixed_post_nbsp(),
789 );
790 self.print_ident(ident);
791 pre_init_size += self.estimate_size(ident.span) + 1;
792 }
793 if let Some(init) = initializer {
794 let cache = self.var_init;
795 self.var_init = true;
796
797 pre_init_size += 2;
798 self.print_word(" =");
799 if override_.is_some() {
800 self.end();
801 }
802 self.end();
803 if pre_init_size <= init_space_left {
804 self.neverbreak();
805 pre_init_size += 1;
806 self.call_stack.add_precall(pre_init_size);
807 } else {
808 self.call_stack.add_precall(init_space_left + self.config.tab_width);
809 }
810
811 if let Some(cmnt) = self.peek_comment_before(init.span.lo())
812 && self.inline_config.is_disabled(cmnt.span)
813 {
814 self.print_sep(Separator::Nbsp);
815 }
816 if self
817 .print_comments(
818 init.span.lo(),
819 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(),
820 )
821 .is_some_and(|cmnt| cmnt.is_trailing())
822 {
823 self.break_offset_if_not_bol(SIZE_INFINITY as usize, self.ind, false);
824 }
825
826 if let ast::ExprKind::Lit(lit, ..) = &init.kind
827 && lit.is_str_concatenation()
828 {
829 self.print_sep(Separator::Nbsp);
830 self.neverbreak();
831 self.s.ibox(self.ind);
832 self.print_expr(init);
833 self.end();
834 } else if is_binary_expr(&init.kind) {
835 if !self.is_bol_or_only_ind() {
836 self.print_sep_unhandled(Separator::Space);
837 }
838 if matches!(ty.kind, ast::TypeKind::Elementary(..) | ast::TypeKind::Mapping(..)) {
839 self.s.offset(self.ind);
840 }
841 self.print_expr(init);
842 } else {
843 let callee_doesnt_fit = if let ast::ExprKind::Call(call_expr, ..) = &init.kind {
844 let callee_size = get_callee_head_size(call_expr);
845 callee_size + pre_init_size > init_space_left
846 && callee_size + self.config.tab_width < init_space_left
847 } else {
848 false
849 };
850
851 if (pre_init_size + 1 >= init_space_left && !is_call_chain(&init.kind, false))
852 || callee_doesnt_fit
853 {
854 self.s.ibox(self.ind);
855 } else {
856 self.s.ibox(0);
857 };
858
859 if has_complex_successor(&init.kind, true)
860 && !matches!(&init.kind, ast::ExprKind::Member(..))
861 {
862 if !self.is_bol_or_only_ind() {
864 let init_size = self.estimate_size(init.span);
865 if callee_doesnt_fit {
866 self.print_sep(Separator::Space);
867 } else if init_size + pre_init_size + 1 >= init_space_left
868 && init_size + self.config.tab_width < init_space_left
869 && !self.has_comment_between(init.span.lo(), init.span.hi())
870 {
871 self.print_sep(Separator::Space);
872 self.s.offset(self.ind);
873 } else {
874 self.print_sep(Separator::Nbsp);
875 }
876 }
877 } else {
878 if !self.is_bol_or_only_ind() {
879 self.print_sep_unhandled(Separator::Space);
880 }
881 if matches!(ty.kind, ast::TypeKind::Elementary(..) | ast::TypeKind::Mapping(..))
882 {
883 self.s.offset(self.ind);
884 }
885 }
886 self.print_expr(init);
887 self.end();
888 }
889 self.var_init = cache;
890 self.call_stack.reset_precall();
891 } else {
892 self.end();
893 }
894 self.end();
895 }
896
897 fn print_attribute(
898 &mut self,
899 attribute: Option<&'static str>,
900 is_var_def: bool,
901 size: &mut usize,
902 ) {
903 if let Some(s) = attribute {
904 self.print_sep(Separator::SpaceOrNbsp(is_var_def));
905 self.print_word(s);
906 *size += s.len() + 1;
907 }
908 }
909
910 fn print_parameter_list(
911 &mut self,
912 parameters: &'ast [ast::VariableDefinition<'ast>],
913 span: Span,
914 format: ListFormat,
915 ) {
916 if self.handle_span(span, false) {
917 return;
918 }
919
920 self.print_tuple(
921 parameters,
922 span.lo(),
923 span.hi(),
924 |fmt, var| fmt.print_var(var, false),
925 get_span!(),
926 format,
927 );
928 }
929
930 fn print_ident_or_strlit(&mut self, value: &'ast ast::IdentOrStrLit) {
931 match value {
932 ast::IdentOrStrLit::Ident(ident) => self.print_ident(ident),
933 ast::IdentOrStrLit::StrLit(strlit) => self.print_ast_str_lit(strlit),
934 }
935 }
936
937 fn print_ast_str_lit(&mut self, strlit: &'ast ast::StrLit) {
939 self.print_str_lit(ast::StrKind::Str, strlit.span.lo(), strlit.value.as_str());
940 }
941
942 fn print_ty(&mut self, ty: &'ast ast::Type<'ast>) {
943 if self.handle_span(ty.span, false) {
944 return;
945 }
946
947 match &ty.kind {
948 &ast::TypeKind::Elementary(ty) => 'b: {
949 match ty {
950 ast::ElementaryType::Address(true) => {
952 self.word("address payable");
953 break 'b;
954 }
955 ast::ElementaryType::Int(size) | ast::ElementaryType::UInt(size) => {
957 match (self.config.int_types, size.bits_raw()) {
958 (config::IntTypes::Short, 0 | 256)
959 | (config::IntTypes::Preserve, 0) => {
960 let short = match ty {
961 ast::ElementaryType::Int(_) => "int",
962 ast::ElementaryType::UInt(_) => "uint",
963 _ => unreachable!(),
964 };
965 self.word(short);
966 break 'b;
967 }
968 _ => {}
969 }
970 }
971 _ => {}
972 }
973 self.word(ty.to_abi_str());
974 }
975 ast::TypeKind::Array(ast::TypeArray { element, size }) => {
976 self.print_ty(element);
977 if let Some(size) = size {
978 self.word("[");
979 self.print_expr(size);
980 self.word("]");
981 } else {
982 self.word("[]");
983 }
984 }
985 ast::TypeKind::Function(ast::TypeFunction {
986 parameters,
987 visibility,
988 state_mutability,
989 returns,
990 }) => {
991 self.cbox(0);
992 self.word("function");
993 self.print_parameter_list(parameters, parameters.span, ListFormat::inline());
994
995 if let Some(v) = visibility {
996 self.space();
997 self.word(v.to_str());
998 }
999 if let Some(sm) = state_mutability
1000 && !matches!(**sm, ast::StateMutability::NonPayable)
1001 {
1002 self.space();
1003 self.word(sm.to_str());
1004 }
1005 if let Some(ret) = returns
1006 && !ret.is_empty()
1007 {
1008 self.nbsp();
1009 self.word("returns");
1010 self.nbsp();
1011 self.print_parameter_list(
1012 ret,
1013 ret.span,
1014 ListFormat::consistent(), );
1016 }
1017 self.end();
1018 }
1019 ast::TypeKind::Mapping(ast::TypeMapping { key, key_name, value, value_name }) => {
1020 self.word("mapping(");
1021 self.s.cbox(0);
1022 if let Some(cmnt) = self.peek_comment_before(key.span.lo()) {
1023 if cmnt.style.is_mixed() {
1024 self.print_comments(
1025 key.span.lo(),
1026 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(),
1027 );
1028 self.break_offset_if_not_bol(SIZE_INFINITY as usize, 0, false);
1029 } else {
1030 self.print_comments(key.span.lo(), CommentConfig::skip_ws());
1031 }
1032 }
1033 else if 18
1037 + self.estimate_size(key.span)
1038 + key_name.map(|k| self.estimate_size(k.span)).unwrap_or(0)
1039 + self.estimate_size(value.span)
1040 + value_name.map(|v| self.estimate_size(v.span)).unwrap_or(0)
1041 >= self.space_left()
1042 {
1043 self.hardbreak();
1044 } else {
1045 self.zerobreak();
1046 }
1047 self.s.cbox(0);
1048 self.print_ty(key);
1049 if let Some(ident) = key_name {
1050 if self
1051 .print_comments(
1052 ident.span.lo(),
1053 CommentConfig::skip_ws()
1054 .mixed_no_break()
1055 .mixed_prev_space()
1056 .mixed_post_nbsp(),
1057 )
1058 .is_none()
1059 {
1060 self.nbsp();
1061 }
1062 self.print_ident(ident);
1063 }
1064 self.print_comments(
1067 value.span.lo(),
1068 CommentConfig::skip_ws()
1069 .trailing_no_break()
1070 .mixed_no_break()
1071 .mixed_prev_space(),
1072 );
1073 self.space();
1074 self.s.offset(self.ind);
1075 self.word("=> ");
1076 self.s.ibox(self.ind);
1077 self.print_ty(value);
1078 if let Some(ident) = value_name {
1079 self.neverbreak();
1080 if self
1081 .print_comments(
1082 ident.span.lo(),
1083 CommentConfig::skip_ws()
1084 .mixed_no_break()
1085 .mixed_prev_space()
1086 .mixed_post_nbsp(),
1087 )
1088 .is_none()
1089 {
1090 self.nbsp();
1091 }
1092 self.print_ident(ident);
1093 if self
1094 .peek_comment_before(ty.span.hi())
1095 .is_some_and(|cmnt| cmnt.style.is_mixed())
1096 {
1097 self.neverbreak();
1098 self.print_comments(
1099 value.span.lo(),
1100 CommentConfig::skip_ws().mixed_no_break(),
1101 );
1102 }
1103 }
1104 self.end();
1105 self.end();
1106 if self
1107 .print_comments(
1108 ty.span.hi(),
1109 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(),
1110 )
1111 .is_some_and(|cmnt| !cmnt.is_mixed())
1112 {
1113 self.break_offset_if_not_bol(0, -self.ind, false);
1114 } else {
1115 self.zerobreak();
1116 self.s.offset(-self.ind);
1117 }
1118 self.end();
1119 self.word(")");
1120 }
1121 ast::TypeKind::Custom(path) => self.print_path(path, false),
1122 }
1123 }
1124
1125 fn print_override(&mut self, override_: &'ast ast::Override<'ast>) {
1126 let ast::Override { span, paths } = override_;
1127 if self.handle_span(*span, false) {
1128 return;
1129 }
1130 self.word("override");
1131 if !paths.is_empty() {
1132 if self.config.override_spacing {
1133 self.nbsp();
1134 }
1135 self.print_tuple(
1136 paths,
1137 span.lo(),
1138 span.hi(),
1139 |this, path| this.print_path(path, false),
1140 get_span!(()),
1141 ListFormat::consistent(), );
1143 }
1144 }
1145
1146 fn print_expr(&mut self, expr: &'ast ast::Expr<'ast>) {
1150 let ast::Expr { span, ref kind } = *expr;
1151 if self.handle_span(span, false) {
1152 return;
1153 }
1154
1155 match kind {
1156 ast::ExprKind::Array(exprs) => {
1157 self.print_array(exprs, expr.span, |this, e| this.print_expr(e), get_span!())
1158 }
1159 ast::ExprKind::Assign(lhs, None, rhs) => self.print_assign_expr(lhs, rhs),
1160 ast::ExprKind::Assign(lhs, Some(op), rhs) => self.print_bin_expr(lhs, op, rhs, true),
1161 ast::ExprKind::Binary(lhs, op, rhs) => self.print_bin_expr(lhs, op, rhs, false),
1162 ast::ExprKind::Call(call_expr, call_args) => self.print_member_or_call_chain(
1163 call_expr,
1164 MemberOrCallArgs::CallArgs(
1165 self.estimate_size(call_args.span),
1166 self.has_comments_between_elements(call_args.span, call_args.exprs()),
1167 ),
1168 |s| {
1169 s.print_call_args(
1170 call_args,
1171 ListFormat::compact()
1172 .break_cmnts()
1173 .break_single(true)
1174 .without_ind(s.return_bin_expr),
1175 get_callee_head_size(call_expr),
1176 );
1177 },
1178 ),
1179 ast::ExprKind::CallOptions(expr, named_args) => {
1180 self.print_expr(expr);
1181 self.print_named_args(named_args, span.hi());
1182 }
1183 ast::ExprKind::Delete(expr) => {
1184 self.word("delete ");
1185 self.print_expr(expr);
1186 }
1187 ast::ExprKind::Ident(ident) => self.print_ident(ident),
1188 ast::ExprKind::Index(expr, kind) => self.print_index_expr(span, expr, kind),
1189 ast::ExprKind::Lit(lit, unit) => {
1190 self.print_lit(lit);
1191 if let Some(unit) = unit {
1192 self.nbsp();
1193 self.word(unit.to_str());
1194 }
1195 }
1196 ast::ExprKind::Member(member_expr, ident) => {
1197 self.print_member_or_call_chain(
1198 member_expr,
1199 MemberOrCallArgs::Member(self.estimate_size(ident.span)),
1200 |s| {
1201 s.print_trailing_comment(member_expr.span.hi(), Some(ident.span.lo()));
1202 if !matches!(
1203 member_expr.kind,
1204 ast::ExprKind::Ident(_) | ast::ExprKind::Type(_)
1205 ) {
1206 s.zerobreak();
1207 }
1208 s.word(".");
1209 s.print_ident(ident);
1210 },
1211 );
1212 }
1213 ast::ExprKind::New(ty) => {
1214 self.word("new ");
1215 self.print_ty(ty);
1216 }
1217 ast::ExprKind::Payable(args) => {
1218 self.word("payable");
1219 self.print_call_args(args, ListFormat::compact().break_cmnts(), 7);
1220 }
1221 ast::ExprKind::Ternary(cond, then, els) => self.print_ternary_expr(cond, then, els),
1222 ast::ExprKind::Tuple(exprs) => self.print_tuple(
1223 exprs,
1224 span.lo(),
1225 span.hi(),
1226 |this, expr| match expr.as_ref() {
1227 SpannedOption::Some(expr) => this.print_expr(expr),
1228 SpannedOption::None(span) => {
1229 this.print_comments(span.hi(), CommentConfig::skip_ws().no_breaks());
1230 }
1231 },
1232 |expr| match expr.as_ref() {
1233 SpannedOption::Some(expr) => expr.span,
1234 SpannedOption::None(..) => Span::DUMMY,
1236 },
1237 ListFormat::compact().break_single(is_binary_expr(&expr.kind)),
1238 ),
1239 ast::ExprKind::TypeCall(ty) => {
1240 self.word("type");
1241 self.print_tuple(
1242 std::slice::from_ref(ty),
1243 span.lo(),
1244 span.hi(),
1245 Self::print_ty,
1246 get_span!(),
1247 ListFormat::consistent(),
1248 );
1249 }
1250 ast::ExprKind::Type(ty) => self.print_ty(ty),
1251 ast::ExprKind::Unary(un_op, expr) => {
1252 let prefix = un_op.kind.is_prefix();
1253 let op = un_op.kind.to_str();
1254 if prefix {
1255 self.word(op);
1256 }
1257 self.print_expr(expr);
1258 if !prefix {
1259 debug_assert!(un_op.kind.is_postfix());
1260 self.word(op);
1261 }
1262 }
1263 }
1264 self.cursor.advance_to(span.hi(), true);
1265 }
1266
1267 fn print_assign_expr(&mut self, lhs: &'ast ast::Expr<'ast>, rhs: &'ast ast::Expr<'ast>) {
1269 let prev_var_init = self.var_init;
1270 self.var_init = true;
1271
1272 let space_left = self.space_left();
1274 let lhs_size = self.estimate_size(lhs.span);
1275 let rhs_size = self.estimate_size(rhs.span);
1276
1277 let total_size = lhs_size + rhs_size + 4; let overflows = total_size >= space_left;
1279 let fits_alone = rhs_size + self.config.tab_width < space_left;
1280
1281 self.call_stack.add_precall(lhs_size);
1282
1283 let is_simple_rhs = matches!(rhs.kind, ast::ExprKind::Lit(..) | ast::ExprKind::Ident(..));
1284
1285 if (overflows && fits_alone) || is_simple_rhs {
1286 self.s.ibox(self.ind)
1287 } else {
1288 self.s.ibox(0)
1289 }
1290
1291 self.print_expr(lhs);
1293 self.word(" =");
1294
1295 match &rhs.kind {
1297 ast::ExprKind::Lit(lit, ..) if lit.is_str_concatenation() => {
1298 self.print_sep(Separator::Nbsp);
1299 self.neverbreak();
1300 self.s.ibox(self.ind);
1301 self.print_expr(rhs);
1302 self.end();
1303 }
1304 _ if overflows && (fits_alone || is_simple_rhs) => {
1305 self.print_sep(Separator::Space);
1306 self.print_expr(rhs);
1307 }
1308 _ => {
1309 self.print_sep(Separator::Nbsp);
1310 self.neverbreak();
1311 self.print_expr(rhs);
1312 }
1313 }
1314
1315 self.end();
1316 self.var_init = prev_var_init;
1317 self.call_stack.reset_precall();
1318 }
1319
1320 fn print_bin_expr(
1322 &mut self,
1323 lhs: &'ast ast::Expr<'ast>,
1324 bin_op: &ast::BinOp,
1325 rhs: &'ast ast::Expr<'ast>,
1326 is_assign: bool,
1327 ) {
1328 let prev_chain = self.binary_expr;
1329 let is_chain = prev_chain.is_some_and(|prev| prev == bin_op.kind.group());
1330
1331 if !is_chain {
1333 self.binary_expr = Some(bin_op.kind.group());
1334
1335 let indent = if (is_assign && has_complex_successor(&rhs.kind, true))
1336 || self.call_stack.is_nested()
1337 && is_call_chain(&lhs.kind, false)
1338 && self.estimate_size(lhs.span) >= self.space_left()
1339 {
1340 0
1341 } else {
1342 self.ind
1343 };
1344 self.s.ibox(indent);
1345 }
1346
1347 self.print_expr(lhs);
1349
1350 let no_trailing_comment = !self.print_trailing_comment(lhs.span.hi(), Some(rhs.span.lo()));
1352 if is_assign {
1353 if no_trailing_comment {
1354 self.nbsp();
1355 }
1356 self.word(bin_op.kind.to_str());
1357 self.word("= ");
1358 } else {
1359 if no_trailing_comment
1360 && self
1361 .print_comments(
1362 bin_op.span.lo(),
1363 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(),
1364 )
1365 .is_none_or(|cmnt| cmnt.is_mixed())
1366 {
1367 if !self.config.pow_no_space || !matches!(bin_op.kind, ast::BinOpKind::Pow) {
1368 self.space_if_not_bol();
1369 } else if !self.is_bol_or_only_ind() && !self.last_token_is_break() {
1370 self.zerobreak();
1371 }
1372 }
1373
1374 self.word(bin_op.kind.to_str());
1375
1376 if !self.config.pow_no_space || !matches!(bin_op.kind, ast::BinOpKind::Pow) {
1377 self.nbsp();
1378 }
1379 }
1380
1381 let rhs_has_mixed_comment =
1383 self.peek_comment_before(rhs.span.lo()).is_some_and(|cmnt| cmnt.style.is_mixed());
1384 if rhs_has_mixed_comment {
1385 self.ibox(0);
1386 self.print_expr(rhs);
1387 self.end();
1388 } else {
1389 self.print_expr(rhs);
1390 }
1391
1392 if !is_chain {
1394 self.binary_expr = prev_chain;
1395 self.end();
1396 }
1397 }
1398
1399 fn print_index_expr(
1401 &mut self,
1402 span: Span,
1403 expr: &'ast ast::Expr<'ast>,
1404 kind: &'ast ast::IndexKind<'ast>,
1405 ) {
1406 self.print_expr(expr);
1407 self.word("[");
1408 self.s.cbox(self.ind);
1409
1410 let mut skip_break = false;
1411
1412 match kind {
1413 ast::IndexKind::Index(Some(inner_expr)) => {
1414 self.zerobreak();
1415 self.print_expr(inner_expr);
1416 }
1417 ast::IndexKind::Index(None) => {}
1418 ast::IndexKind::Range(start, end) => {
1419 if let Some(start_expr) = start {
1420 if self
1421 .print_comments(start_expr.span.lo(), CommentConfig::skip_ws())
1422 .is_none_or(|s| s.is_mixed())
1423 {
1424 self.zerobreak();
1425 }
1426 self.print_expr(start_expr);
1427 } else {
1428 self.zerobreak();
1429 }
1430
1431 self.word(":");
1432
1433 if let Some(end_expr) = end {
1434 self.s.ibox(self.ind);
1435 if start.is_some() {
1436 self.zerobreak();
1437 }
1438 self.print_comments(
1439 end_expr.span.lo(),
1440 CommentConfig::skip_ws()
1441 .mixed_prev_space()
1442 .mixed_no_break()
1443 .mixed_post_nbsp(),
1444 );
1445 self.print_expr(end_expr);
1446 }
1447
1448 let mut is_trailing = false;
1450 if let Some(style) = self.print_comments(
1451 span.hi(),
1452 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(),
1453 ) {
1454 skip_break = true;
1455 is_trailing = style.is_trailing();
1456 }
1457
1458 match (skip_break, end.is_some()) {
1460 (true, true) => {
1461 self.break_offset_if_not_bol(0, -2 * self.ind, false);
1462 self.end();
1463 if !is_trailing {
1464 self.break_offset_if_not_bol(0, -self.ind, false);
1465 }
1466 }
1467 (true, false) => {
1468 self.break_offset_if_not_bol(0, -self.ind, false);
1469 }
1470 (false, true) => {
1471 self.end();
1472 }
1473 _ => {}
1474 }
1475 }
1476 }
1477
1478 if !skip_break {
1479 self.zerobreak();
1480 self.s.offset(-self.ind);
1481 }
1482
1483 self.end();
1484 self.word("]");
1485 }
1486
1487 fn print_ternary_expr(
1489 &mut self,
1490 cond: &'ast ast::Expr<'ast>,
1491 then: &'ast ast::Expr<'ast>,
1492 els: &'ast ast::Expr<'ast>,
1493 ) {
1494 self.s.cbox(self.ind);
1495 self.s.ibox(0);
1496
1497 let mut print_ternary_expr =
1498 |span_lo, prefix: Option<&'static str>, expr: &'ast ast::Expr<'ast>| {
1499 match prefix {
1500 Some(prefix) => {
1501 if self.peek_comment_before(span_lo).is_some() {
1502 self.space();
1503 }
1504 self.print_comments(span_lo, CommentConfig::skip_ws());
1505 self.end();
1506 if !self.is_bol_or_only_ind() {
1507 self.space();
1508 }
1509 self.s.ibox(0);
1510 self.word(prefix);
1511 }
1512 None => {
1513 self.print_comments(expr.span.lo(), CommentConfig::skip_ws());
1514 }
1515 };
1516 self.print_expr(expr);
1517 };
1518
1519 print_ternary_expr(then.span.lo(), None, cond);
1521 print_ternary_expr(then.span.lo(), Some("? "), then);
1523 print_ternary_expr(els.span.lo(), Some(": "), els);
1525
1526 self.end();
1527 self.neverbreak();
1528 self.s.offset(-self.ind);
1529 self.end();
1530 }
1531
1532 fn print_modifier_call(
1534 &mut self,
1535 modifier: &'ast ast::Modifier<'ast>,
1536 add_parens_if_empty: bool,
1537 ) {
1538 let ast::Modifier { name, arguments } = modifier;
1539 self.print_path(name, false);
1540 if !arguments.is_empty() || add_parens_if_empty {
1541 self.print_call_args(
1542 arguments,
1543 ListFormat::compact().break_cmnts(),
1544 name.to_string().len(),
1545 );
1546 }
1547 }
1548
1549 fn print_member_or_call_chain<F>(
1550 &mut self,
1551 child_expr: &'ast ast::Expr<'ast>,
1552 member_or_args: MemberOrCallArgs,
1553 print_suffix: F,
1554 ) where
1555 F: FnOnce(&mut Self),
1556 {
1557 fn member_depth(depth: usize, expr: &ast::Expr<'_>) -> usize {
1558 if let ast::ExprKind::Member(child, ..) = &expr.kind {
1559 member_depth(depth + 1, child)
1560 } else {
1561 depth
1562 }
1563 }
1564
1565 let parent_is_chain = self.call_stack.last().copied().is_some_and(|call| call.is_chained());
1566 if !parent_is_chain {
1567 let callee_size = get_callee_head_size(child_expr) + member_or_args.member_size();
1569 let expr_size = self.estimate_size(child_expr.span);
1570
1571 if is_call_chain(&child_expr.kind, false) {
1573 self.call_stack.push(CallContext::chained(callee_size));
1574 }
1575
1576 let callee_fits_line = self.space_left() > callee_size + 1;
1577 let total_fits_line = self.space_left() > expr_size + member_or_args.size() + 2;
1578 let no_mixed_comment =
1579 self.peek_comment_before(child_expr.span.hi()).is_none_or(|c| c.style.is_mixed());
1580
1581 if !is_call_chain(&child_expr.kind, true)
1582 && no_mixed_comment
1583 && callee_fits_line
1584 && (member_depth(0, child_expr) < 2
1585 || (total_fits_line && !member_or_args.has_comments()))
1587 {
1588 self.s.ibox(0);
1589 } else {
1590 self.s.ibox(self.ind);
1591 }
1592 }
1593
1594 self.print_expr(child_expr);
1596
1597 print_suffix(self);
1599
1600 if !parent_is_chain {
1602 if is_call_chain(&child_expr.kind, false) {
1603 self.call_stack.pop();
1604 }
1605 self.end();
1606 }
1607 }
1608
1609 fn print_call_args(
1610 &mut self,
1611 args: &'ast ast::CallArgs<'ast>,
1612 format: ListFormat,
1613 callee_size: usize,
1614 ) {
1615 let ast::CallArgs { span, ref kind } = *args;
1616 if self.handle_span(span, true) {
1617 return;
1618 }
1619
1620 self.call_stack.push(CallContext::nested(callee_size));
1621
1622 let cache = self.binary_expr.take();
1624
1625 match kind {
1626 ast::CallArgsKind::Unnamed(exprs) => {
1627 self.print_tuple(
1628 exprs,
1629 span.lo(),
1630 span.hi(),
1631 |this, e| this.print_expr(e),
1632 get_span!(),
1633 format,
1634 );
1635 }
1636 ast::CallArgsKind::Named(named_args) => {
1637 self.print_inside_parens(|state| state.print_named_args(named_args, span.hi()));
1638 }
1639 }
1640
1641 self.binary_expr = cache;
1643 self.call_stack.pop();
1644 }
1645
1646 fn print_named_args(&mut self, args: &'ast [ast::NamedArg<'ast>], pos_hi: BytePos) {
1647 let cache = self.named_call_expr;
1648 if !cache {
1649 self.named_call_expr = true;
1650 };
1651
1652 let list_format = match (self.config.bracket_spacing, self.config.call_compact_args) {
1653 (false, true) => ListFormat::compact(),
1654 (false, false) => ListFormat::consistent(),
1655 (true, true) => ListFormat::compact().with_space(),
1656 (true, false) => ListFormat::consistent().with_space(),
1657 };
1658
1659 self.word("{");
1660 let list_lo = args.first().map_or(pos_hi, |arg| arg.name.span.lo());
1662
1663 self.commasep(
1664 args,
1665 list_lo,
1666 pos_hi,
1667 |s, arg| {
1669 s.cbox(0);
1670 s.print_ident(&arg.name);
1671 s.word(":");
1672 if s.same_source_line(arg.name.span.hi(), arg.value.span.hi())
1673 || !s.print_trailing_comment(arg.name.span.hi(), None)
1674 {
1675 s.nbsp();
1676 }
1677 s.print_comments(
1678 arg.value.span.lo(),
1679 CommentConfig::skip_ws().mixed_no_break().mixed_post_nbsp(),
1680 );
1681 s.print_expr(arg.value);
1682 s.end();
1683 },
1684 |arg| arg.name.span.until(arg.value.span),
1685 list_format.break_cmnts().break_single(true).without_ind(self.call_stack.is_chain()),
1686 );
1687 self.word("}");
1688
1689 if !cache {
1690 self.named_call_expr = false;
1691 }
1692 }
1693
1694 fn print_stmt(&mut self, stmt: &'ast ast::Stmt<'ast>) {
1698 let ast::Stmt { ref docs, span, ref kind } = *stmt;
1699 self.print_docs(docs);
1700
1701 if self.handle_span(span, false) {
1703 self.print_trailing_comment_no_break(stmt.span.hi(), None);
1704 return;
1705 }
1706
1707 let force_break = matches!(kind, ast::StmtKind::Return(..))
1709 && self.peek_comment_before(span.lo()).is_some_and(|cmnt| cmnt.style.is_mixed());
1710
1711 match kind {
1712 ast::StmtKind::Assembly(ast::StmtAssembly { dialect, flags, block }) => {
1713 self.print_assembly_stmt(span, dialect, flags, block)
1714 }
1715 ast::StmtKind::DeclSingle(var) => self.print_var(var, true),
1716 ast::StmtKind::DeclMulti(vars, init_expr) => {
1717 self.print_multi_decl_stmt(span, vars, init_expr)
1718 }
1719 ast::StmtKind::Block(stmts) => self.print_block(stmts, span),
1720 ast::StmtKind::Break => self.word("break"),
1721 ast::StmtKind::Continue => self.word("continue"),
1722 ast::StmtKind::DoWhile(stmt, cond) => {
1723 self.word("do ");
1724 self.print_stmt_as_block(stmt, cond.span.lo(), false);
1725 self.nbsp();
1726 self.print_if_cond("while", cond, cond.span.hi());
1727 }
1728 ast::StmtKind::Emit(path, args) => self.print_emit_or_revert("emit", path, args),
1729 ast::StmtKind::Expr(expr) => self.print_expr(expr),
1730 ast::StmtKind::For { init, cond, next, body } => {
1731 self.print_for_stmt(span, init, cond, next, body)
1732 }
1733 ast::StmtKind::If(cond, then, els_opt) => self.print_if_stmt(span, cond, then, els_opt),
1734 ast::StmtKind::Return(expr) => self.print_return_stmt(force_break, expr),
1735 ast::StmtKind::Revert(path, args) => self.print_emit_or_revert("revert", path, args),
1736 ast::StmtKind::Try(ast::StmtTry { expr, clauses }) => {
1737 self.print_try_stmt(expr, clauses)
1738 }
1739 ast::StmtKind::UncheckedBlock(block) => {
1740 self.word("unchecked ");
1741 self.print_block(block, stmt.span);
1742 }
1743 ast::StmtKind::While(cond, stmt) => {
1744 let inline = self.is_single_line_block(cond, stmt, None);
1746 if !inline.is_cached && self.single_line_stmt.is_none() {
1747 self.single_line_stmt = Some(inline.outcome);
1748 }
1749
1750 self.print_if_cond("while", cond, stmt.span.lo());
1752 self.nbsp();
1753 self.print_stmt_as_block(stmt, stmt.span.hi(), inline.outcome);
1754
1755 if !inline.is_cached && self.single_line_stmt.is_some() {
1757 self.single_line_stmt = None;
1758 }
1759 }
1760 ast::StmtKind::Placeholder => self.word("_"),
1761 }
1762 if stmt_needs_semi(kind) {
1763 self.neverbreak(); self.word(";");
1765 self.cursor.advance_to(span.hi(), true);
1766 }
1767 self.print_comments(
1769 stmt.span.hi(),
1770 CommentConfig::default().trailing_no_break().mixed_no_break().mixed_prev_space(),
1771 );
1772 self.print_trailing_comment_no_break(stmt.span.hi(), None);
1773 }
1774
1775 fn print_assembly_stmt(
1778 &mut self,
1779 span: Span,
1780 dialect: &'ast Option<ast::StrLit>,
1781 flags: &'ast [ast::StrLit],
1782 block: &'ast ast::yul::Block<'ast>,
1783 ) {
1784 _ = self.handle_span(self.cursor.span(span.lo()), false);
1785 if !self.handle_span(span.until(block.span), false) {
1786 self.cursor.advance_to(span.lo(), true);
1787 self.print_word("assembly ");
1788 if let Some(dialect) = dialect {
1789 self.print_ast_str_lit(dialect);
1790 self.print_sep(Separator::Nbsp);
1791 }
1792 if !flags.is_empty() {
1793 self.print_tuple(
1794 flags,
1795 span.lo(),
1796 block.span.lo(),
1797 Self::print_ast_str_lit,
1798 get_span!(),
1799 ListFormat::consistent(),
1800 );
1801 self.print_sep(Separator::Nbsp);
1802 }
1803 }
1804 self.print_yul_block(block, block.span, false);
1805 }
1806
1807 fn print_multi_decl_stmt(
1810 &mut self,
1811 span: Span,
1812 vars: &'ast BoxSlice<'ast, SpannedOption<ast::VariableDefinition<'ast>>>,
1813 init_expr: &'ast ast::Expr<'ast>,
1814 ) {
1815 let space_left = self.space_left();
1816
1817 self.s.ibox(self.ind);
1818 self.s.ibox(-self.ind);
1819 self.print_tuple(
1820 vars,
1821 span.lo(),
1822 init_expr.span.lo(),
1823 |this, var| match var {
1824 SpannedOption::Some(var) => this.print_var(var, true),
1825 SpannedOption::None(span) => {
1826 this.print_comments(span.hi(), CommentConfig::skip_ws().no_breaks());
1827 }
1828 },
1829 |var| match var {
1830 SpannedOption::Some(var) => var.span,
1831 SpannedOption::None(..) => Span::DUMMY,
1833 },
1834 ListFormat::consistent(),
1835 );
1836 self.end();
1837 self.word(" =");
1838
1839 let vars_size = vars.iter().fold(0, |acc, var| {
1841 acc + var.as_ref().unspan().map_or(0, |v| self.estimate_size(v.span)) + 2
1842 }) + 2;
1843 self.call_stack.add_precall(vars_size);
1844
1845 if self.estimate_size(init_expr.span) + self.config.tab_width
1846 <= std::cmp::max(space_left, self.space_left())
1847 {
1848 self.print_sep(Separator::Space);
1849 self.ibox(0);
1850 } else {
1851 self.print_sep(Separator::Nbsp);
1852 self.neverbreak();
1853 self.s.ibox(-self.ind);
1854 }
1855 self.print_expr(init_expr);
1856 self.end();
1857 self.end();
1858 }
1859
1860 fn print_for_stmt(
1863 &mut self,
1864 span: Span,
1865 init: &'ast Option<&mut ast::Stmt<'ast>>,
1866 cond: &'ast Option<&mut ast::Expr<'ast>>,
1867 next: &'ast Option<&mut ast::Expr<'ast>>,
1868 body: &'ast ast::Stmt<'ast>,
1869 ) {
1870 self.cbox(0);
1871 self.s.ibox(self.ind);
1872 self.print_word("for (");
1873 self.zerobreak();
1874
1875 self.s.cbox(0);
1877 match init {
1878 Some(init_stmt) => self.print_stmt(init_stmt),
1879 None => self.print_word(";"),
1880 }
1881
1882 match cond {
1884 Some(cond_expr) => {
1885 self.print_sep(Separator::Space);
1886 self.print_expr(cond_expr);
1887 }
1888 None => self.zerobreak(),
1889 }
1890 self.print_word(";");
1891
1892 match next {
1894 Some(next_expr) => {
1895 self.space();
1896 self.print_expr(next_expr);
1897 }
1898 None => self.zerobreak(),
1899 }
1900
1901 self.break_offset_if_not_bol(0, -self.ind, false);
1903 self.end();
1904 self.print_word(") ");
1905 self.neverbreak();
1906 self.end();
1907
1908 self.print_comments(body.span.lo(), CommentConfig::skip_ws());
1910 self.print_stmt_as_block(body, span.hi(), false);
1911 self.end();
1912 }
1913
1914 fn print_if_stmt(
1917 &mut self,
1918 span: Span,
1919 cond: &'ast ast::Expr<'ast>,
1920 then: &'ast ast::Stmt<'ast>,
1921 els_opt: &'ast Option<&mut ast::Stmt<'ast>>,
1922 ) {
1923 let inline = self.is_single_line_block(cond, then, els_opt.as_ref());
1925 let set_inline_cache = !inline.is_cached && self.single_line_stmt.is_none();
1926 if set_inline_cache {
1927 self.single_line_stmt = Some(inline.outcome);
1928 }
1929
1930 self.cbox(0);
1931 self.ibox(0);
1932 self.print_if_no_else(cond, then, inline.outcome);
1934
1935 let mut current_else = els_opt.as_deref();
1937 while let Some(els) = current_else {
1938 if self.ends_with('}') {
1939 if self.has_comment_before_with(els.span.lo(), |cmnt| !cmnt.style.is_mixed()) {
1941 if self
1943 .print_comments(els.span.lo(), CommentConfig::skip_ws().mixed_no_break())
1944 .is_some_and(|cmnt| cmnt.is_mixed())
1945 {
1946 self.hardbreak();
1947 }
1948 }
1949 else if self
1951 .print_comments(
1952 els.span.lo(),
1953 CommentConfig::skip_ws()
1954 .mixed_no_break()
1955 .mixed_prev_space()
1956 .mixed_post_nbsp(),
1957 )
1958 .is_none()
1959 {
1960 self.nbsp();
1961 }
1962 } else {
1963 self.hardbreak_if_not_bol();
1964 if self
1965 .print_comments(els.span.lo(), CommentConfig::skip_ws())
1966 .is_some_and(|cmnt| cmnt.is_mixed())
1967 {
1968 self.hardbreak();
1969 };
1970 }
1971
1972 self.ibox(0);
1973 self.print_word("else ");
1974 match &els.kind {
1975 ast::StmtKind::If(cond, then, next_else) => {
1976 self.print_if_no_else(cond, then, inline.outcome);
1977 current_else = next_else.as_deref();
1978 }
1979 _ => {
1980 self.print_stmt_as_block(els, span.hi(), inline.outcome);
1981 self.end(); break;
1983 }
1984 }
1985 }
1986 self.end();
1987
1988 if set_inline_cache {
1990 self.single_line_stmt = None;
1991 }
1992 }
1993
1994 fn print_return_stmt(&mut self, force_break: bool, expr: &'ast Option<&mut ast::Expr<'ast>>) {
1997 if force_break {
1998 self.hardbreak_if_not_bol();
1999 }
2000
2001 let space_left = self.space_left();
2002 let expr_size = expr.as_ref().map_or(0, |expr| self.estimate_size(expr.span));
2003
2004 let overflows = space_left < 8 + expr_size;
2006 let fits_alone = space_left > expr_size;
2007
2008 if let Some(expr) = expr {
2009 let is_simple = matches!(expr.kind, ast::ExprKind::Lit(..) | ast::ExprKind::Ident(..));
2010 let allow_break = overflows && fits_alone;
2011
2012 self.return_bin_expr = matches!(expr.kind, ast::ExprKind::Binary(..));
2013 self.s.ibox(if is_simple || allow_break { self.ind } else { 0 });
2014
2015 self.print_word("return");
2016
2017 match self.print_comments(
2018 expr.span.lo(),
2019 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space().mixed_post_nbsp(),
2020 ) {
2021 Some(cmnt) if cmnt.is_trailing() && !is_simple => self.s.offset(self.ind),
2022 None => self.print_sep(Separator::SpaceOrNbsp(allow_break)),
2023 _ => {}
2024 }
2025
2026 self.print_expr(expr);
2027 self.end();
2028 self.return_bin_expr = false;
2029 } else {
2030 self.print_word("return");
2031 }
2032 }
2033
2034 fn print_try_stmt(
2037 &mut self,
2038 expr: &'ast ast::Expr<'ast>,
2039 clauses: &'ast [ast::TryCatchClause<'ast>],
2040 ) {
2041 self.cbox(0);
2042 if let Some((first, other)) = clauses.split_first() {
2043 let ast::TryCatchClause { args, block, span: try_span, .. } = first;
2045 self.ibox(0);
2046 self.print_word("try ");
2047 self.print_comments(expr.span.lo(), CommentConfig::skip_ws());
2048 self.print_expr(expr);
2049
2050 self.print_comments(
2052 args.first().map(|p| p.span.lo()).unwrap_or_else(|| expr.span.lo()),
2053 CommentConfig::skip_ws(),
2054 );
2055 if !self.is_beginning_of_line() {
2056 self.nbsp();
2057 }
2058
2059 if !args.is_empty() {
2060 self.print_word("returns ");
2061 self.print_parameter_list(
2062 args,
2063 args.span.with_hi(block.span.lo()),
2064 ListFormat::compact(),
2065 );
2066 self.nbsp();
2067 }
2068 if block.is_empty() {
2069 self.print_block(block, *try_span);
2070 self.end();
2071 } else {
2072 self.print_word("{");
2073 self.end();
2074 self.neverbreak();
2075 self.print_trailing_comment_no_break(try_span.lo(), None);
2076 self.print_block_without_braces(block, try_span.hi(), Some(self.ind));
2077 if self.cursor.enabled || self.cursor.pos < try_span.hi() {
2078 self.print_word("}");
2079 self.cursor.advance_to(try_span.hi(), true);
2080 }
2081 }
2082
2083 let mut skip_ind = false;
2084 if self.print_trailing_comment(try_span.hi(), other.first().map(|c| c.span.lo())) {
2085 self.break_offset_if_not_bol(0, self.ind, false);
2088 skip_ind = true;
2089 };
2090
2091 let mut prev_block_multiline = self.is_multiline_block(block, false);
2092
2093 for (pos, ast::TryCatchClause { name, args, block, span: catch_span }) in
2095 other.iter().delimited()
2096 {
2097 let current_block_multiline = self.is_multiline_block(block, false);
2098 if !pos.is_first || !skip_ind {
2099 if prev_block_multiline && (current_block_multiline || pos.is_last) {
2100 self.nbsp();
2101 } else {
2102 self.space();
2103 if !current_block_multiline {
2104 self.s.offset(self.ind);
2105 }
2106 }
2107 }
2108 self.s.ibox(self.ind);
2109 self.print_comments(
2110 catch_span.lo(),
2111 CommentConfig::skip_ws().mixed_no_break().mixed_post_nbsp(),
2112 );
2113
2114 self.print_word("catch ");
2115 if !args.is_empty() {
2116 self.print_comments(
2117 args[0].span.lo(),
2118 CommentConfig::skip_ws().mixed_no_break().mixed_post_nbsp(),
2119 );
2120 if let Some(name) = name {
2121 self.print_ident(name);
2122 }
2123 self.print_parameter_list(
2124 args,
2125 args.span.with_hi(block.span.lo()),
2126 ListFormat::inline(),
2127 );
2128 self.nbsp();
2129 }
2130 self.print_word("{");
2131 self.end();
2132 self.print_trailing_comment_no_break(catch_span.lo(), None);
2133 self.print_block_without_braces(block, catch_span.hi(), Some(self.ind));
2134 if self.cursor.enabled || self.cursor.pos < try_span.hi() {
2135 self.print_word("}");
2136 self.cursor.advance_to(catch_span.hi(), true);
2137 }
2138
2139 prev_block_multiline = current_block_multiline;
2140 }
2141 }
2142 self.end();
2143 }
2144
2145 fn print_if_no_else(
2146 &mut self,
2147 cond: &'ast ast::Expr<'ast>,
2148 then: &'ast ast::Stmt<'ast>,
2149 inline: bool,
2150 ) {
2151 if !self.handle_span(cond.span.until(then.span), true) {
2152 self.print_if_cond("if", cond, then.span.lo());
2153 if let ast::StmtKind::Block(block) = &then.kind
2155 && block.is_empty()
2156 && self.peek_comment_before(then.span.hi()).is_none()
2157 {
2158 self.neverbreak();
2159 self.print_sep(Separator::Nbsp);
2160 } else {
2161 self.print_sep(Separator::Space);
2162 }
2163 }
2164 self.end();
2165 self.print_stmt_as_block(then, then.span.hi(), inline);
2166 self.cursor.advance_to(then.span.hi(), true);
2167 }
2168
2169 fn print_if_cond(&mut self, kw: &'static str, cond: &'ast ast::Expr<'ast>, pos_hi: BytePos) {
2170 self.print_word(kw);
2171 self.print_sep_unhandled(Separator::Nbsp);
2172 self.print_tuple(
2173 std::slice::from_ref(cond),
2174 cond.span.lo(),
2175 pos_hi,
2176 Self::print_expr,
2177 get_span!(),
2178 ListFormat::compact().break_cmnts().break_single(is_binary_expr(&cond.kind)),
2179 );
2180 }
2181
2182 fn print_emit_or_revert(
2183 &mut self,
2184 kw: &'static str,
2185 path: &'ast ast::PathSlice,
2186 args: &'ast ast::CallArgs<'ast>,
2187 ) {
2188 self.word(kw);
2189 if self
2190 .print_comments(
2191 path.span().lo(),
2192 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space().mixed_post_nbsp(),
2193 )
2194 .is_none()
2195 {
2196 self.nbsp();
2197 };
2198 self.s.cbox(0);
2199 self.print_path(path, false);
2200 self.print_call_args(
2201 args,
2202 if args.len() == 1 {
2203 ListFormat::compact().break_cmnts()
2204 } else {
2205 ListFormat::compact().break_cmnts().no_delimiters()
2206 },
2207 path.to_string().len(),
2208 );
2209 self.end();
2210 }
2211
2212 fn print_block(&mut self, block: &'ast [ast::Stmt<'ast>], span: Span) {
2213 self.print_block_inner(
2214 block,
2215 BlockFormat::Regular,
2216 Self::print_stmt,
2217 |b| b.span,
2218 span.hi(),
2219 );
2220 }
2221
2222 fn print_block_without_braces(
2223 &mut self,
2224 block: &'ast [ast::Stmt<'ast>],
2225 pos_hi: BytePos,
2226 offset: Option<isize>,
2227 ) {
2228 self.print_block_inner(
2229 block,
2230 BlockFormat::NoBraces(offset),
2231 Self::print_stmt,
2232 |b| b.span,
2233 pos_hi,
2234 );
2235 }
2236
2237 fn print_stmt_as_block(&mut self, stmt: &'ast ast::Stmt<'ast>, pos_hi: BytePos, inline: bool) {
2239 if self.handle_span(stmt.span, false) {
2240 return;
2241 }
2242
2243 let stmts = if let ast::StmtKind::Block(stmts) = &stmt.kind {
2244 stmts
2245 } else {
2246 std::slice::from_ref(stmt)
2247 };
2248
2249 if inline && !stmts.is_empty() {
2250 self.neverbreak();
2251 self.print_block_without_braces(stmts, pos_hi, None);
2252 } else {
2253 let inline_parent = self.single_line_stmt.take();
2255
2256 self.print_word("{");
2257 self.print_block_without_braces(stmts, pos_hi, Some(self.ind));
2258 self.print_word("}");
2259
2260 self.single_line_stmt = inline_parent;
2262 }
2263 }
2264
2265 fn is_single_line_block(
2274 &mut self,
2275 cond: &'ast ast::Expr<'ast>,
2276 then: &'ast ast::Stmt<'ast>,
2277 els_opt: Option<&'ast &'ast mut ast::Stmt<'ast>>,
2278 ) -> Decision {
2279 if let Some(cached_decision) = self.single_line_stmt {
2281 return Decision { outcome: cached_decision, is_cached: true };
2282 }
2283
2284 if std::slice::from_ref(then).is_empty() {
2286 return Decision { outcome: false, is_cached: false };
2287 }
2288
2289 match self.config.single_line_statement_blocks {
2291 config::SingleLineBlockStyle::Preserve => {
2292 if self.is_stmt_in_new_line(cond, then) || self.is_multiline_block_stmt(then, true)
2293 {
2294 return Decision { outcome: false, is_cached: false };
2295 }
2296 }
2297 config::SingleLineBlockStyle::Single => {
2298 if self.is_multiline_block_stmt(then, true) {
2299 return Decision { outcome: false, is_cached: false };
2300 }
2301 }
2302 config::SingleLineBlockStyle::Multi => {
2303 return Decision { outcome: false, is_cached: false };
2304 }
2305 };
2306
2307 if !self.can_stmts_be_inlined(cond, then, els_opt) {
2310 return Decision { outcome: false, is_cached: false };
2311 }
2312
2313 if let Some(stmt) = els_opt {
2315 if let ast::StmtKind::If(child_cond, child_then, child_els_opt) = &stmt.kind {
2316 return self.is_single_line_block(child_cond, child_then, child_els_opt.as_ref());
2317 } else if self.is_multiline_block_stmt(stmt, true) {
2318 return Decision { outcome: false, is_cached: false };
2319 }
2320 }
2321
2322 Decision { outcome: true, is_cached: false }
2324 }
2325
2326 fn is_inline_stmt(&self, stmt: &'ast ast::Stmt<'ast>, cond_len: usize) -> bool {
2327 if let ast::StmtKind::If(cond, then, els_opt) = &stmt.kind {
2328 let if_span = cond.span.to(then.span);
2329 if self.sm.is_multiline(if_span)
2330 && matches!(
2331 self.config.single_line_statement_blocks,
2332 config::SingleLineBlockStyle::Preserve
2333 )
2334 {
2335 return false;
2336 }
2337 if cond_len + self.estimate_size(if_span) >= self.space_left() {
2338 return false;
2339 }
2340 if let Some(els) = els_opt
2341 && !self.is_inline_stmt(els, 6)
2342 {
2343 return false;
2344 }
2345 } else {
2346 if matches!(
2347 self.config.single_line_statement_blocks,
2348 config::SingleLineBlockStyle::Preserve
2349 ) && self.sm.is_multiline(stmt.span)
2350 {
2351 return false;
2352 }
2353 if cond_len + self.estimate_size(stmt.span) >= self.space_left() {
2354 return false;
2355 }
2356 }
2357 true
2358 }
2359
2360 fn is_stmt_in_new_line(
2362 &self,
2363 cond: &'ast ast::Expr<'ast>,
2364 then: &'ast ast::Stmt<'ast>,
2365 ) -> bool {
2366 let span_between = cond.span.between(then.span);
2367 if let Ok(snip) = self.sm.span_to_snippet(span_between) {
2368 if let Some((_, after_paren)) = snip.split_once(')') {
2370 return after_paren.lines().count() > 1;
2371 }
2372 }
2373 false
2374 }
2375
2376 fn is_multiline_block_stmt(
2378 &self,
2379 stmt: &'ast ast::Stmt<'ast>,
2380 empty_as_multiline: bool,
2381 ) -> bool {
2382 if let ast::StmtKind::Block(block) = &stmt.kind {
2383 return self.is_multiline_block(block, empty_as_multiline);
2384 }
2385 false
2386 }
2387
2388 fn is_multiline_block(&self, block: &'ast ast::Block<'ast>, empty_as_multiline: bool) -> bool {
2390 if block.stmts.is_empty() {
2391 return empty_as_multiline;
2392 }
2393 if self.sm.is_multiline(block.span)
2394 && let Ok(snip) = self.sm.span_to_snippet(block.span)
2395 {
2396 let code_lines = snip.lines().filter(|line| {
2397 let trimmed = line.trim();
2398 if empty_as_multiline {
2400 !trimmed.is_empty() && trimmed != "{" && trimmed != "}"
2401 } else {
2402 !trimmed.is_empty()
2403 }
2404 });
2405 return code_lines.count() > 1;
2406 }
2407 false
2408 }
2409
2410 fn can_stmts_be_inlined(
2412 &mut self,
2413 cond: &'ast ast::Expr<'ast>,
2414 then: &'ast ast::Stmt<'ast>,
2415 els_opt: Option<&'ast &'ast mut ast::Stmt<'ast>>,
2416 ) -> bool {
2417 let cond_len = self.estimate_size(cond.span);
2418
2419 let then_margin = if 6 + cond_len < self.space_left() { 6 + cond_len } else { 2 };
2422
2423 if !self.is_inline_stmt(then, then_margin) {
2424 return false;
2425 }
2426
2427 els_opt.is_none_or(|els| self.is_inline_stmt(els, 6))
2429 }
2430
2431 fn can_header_be_inlined(&mut self, header: &ast::FunctionHeader<'_>) -> bool {
2432 let visibility = header.visibility.map_or(0, |v| self.estimate_size(v.span) + 1);
2434 let mutability = header.state_mutability.map_or(0, |sm| self.estimate_size(sm.span) + 1);
2436 let modifiers =
2438 header.modifiers.iter().fold(0, |len, m| len + self.estimate_size(m.span())) + 1;
2439 let override_ = header.override_.as_ref().map_or(0, |o| self.estimate_size(o.span) + 1);
2441 let returns = header.returns.as_ref().map_or(0, |ret| {
2443 ret.vars
2444 .iter()
2445 .fold(0, |len, p| if len != 0 { len + 2 } else { 8 } + self.estimate_size(p.span))
2446 });
2447
2448 self.estimate_header_params_size(header)
2449 + visibility
2450 + mutability
2451 + modifiers
2452 + override_
2453 + returns
2454 <= self.space_left()
2455 }
2456
2457 fn estimate_header_params_size(&mut self, header: &ast::FunctionHeader<'_>) -> usize {
2458 let params = header
2460 .parameters
2461 .vars
2462 .iter()
2463 .fold(0, |len, p| if len != 0 { len + 2 } else { 2 } + self.estimate_size(p.span));
2464
2465 9 + header.name.map_or(0, |name| self.estimate_size(name.span) + 1) + params
2467 }
2468
2469 fn can_header_params_be_inlined(&mut self, header: &ast::FunctionHeader<'_>) -> bool {
2470 self.estimate_header_params_size(header) <= self.space_left()
2471 }
2472
2473 fn has_comments_between_elements<I>(&self, limits: Span, elements: I) -> bool
2474 where
2475 I: IntoIterator<Item = &'ast ast::Expr<'ast>>,
2476 {
2477 let mut last_span_end = limits.lo();
2478 for expr in elements {
2479 if self.has_comment_between(last_span_end, expr.span.lo()) {
2480 return true;
2481 }
2482 last_span_end = expr.span.hi();
2483 }
2484
2485 if self.has_comment_between(last_span_end, limits.hi()) {
2486 return true;
2487 }
2488
2489 false
2490 }
2491}
2492
2493#[derive(Debug)]
2496enum MemberOrCallArgs {
2497 Member(usize),
2498 CallArgs(usize, bool),
2499}
2500
2501impl MemberOrCallArgs {
2502 fn size(&self) -> usize {
2503 match self {
2504 Self::CallArgs(size, ..) | Self::Member(size) => *size,
2505 }
2506 }
2507
2508 fn member_size(&self) -> usize {
2509 match self {
2510 Self::CallArgs(..) => 0,
2511 Self::Member(size) => *size,
2512 }
2513 }
2514
2515 fn has_comments(&self) -> bool {
2516 matches!(self, Self::CallArgs(.., true))
2517 }
2518}
2519
2520#[derive(Debug, Clone)]
2521#[expect(dead_code)]
2522enum AttributeKind<'ast> {
2523 Visibility(ast::Visibility),
2524 StateMutability(ast::StateMutability),
2525 Virtual,
2526 Override(&'ast ast::Override<'ast>),
2527 Modifier(&'ast ast::Modifier<'ast>),
2528}
2529
2530type AttributeCommentMap = HashMap<BytePos, (Vec<Comment>, Vec<Comment>, Vec<Comment>)>;
2531
2532#[derive(Debug, Clone)]
2533struct AttributeInfo<'ast> {
2534 kind: AttributeKind<'ast>,
2535 span: Span,
2536}
2537
2538struct AttributeCommentMapper<'ast> {
2540 limit_pos: BytePos,
2541 comments: Vec<Comment>,
2542 attributes: Vec<AttributeInfo<'ast>>,
2543}
2544
2545impl<'ast> AttributeCommentMapper<'ast> {
2546 fn new(returns: Option<&'ast ast::ParameterList<'ast>>, body_pos: BytePos) -> Self {
2547 Self {
2548 comments: Vec::new(),
2549 attributes: Vec::new(),
2550 limit_pos: returns.as_ref().map_or(body_pos, |ret| ret.span.lo()),
2551 }
2552 }
2553
2554 #[allow(clippy::type_complexity)]
2555 fn build(
2556 mut self,
2557 state: &mut State<'_, 'ast>,
2558 header: &'ast ast::FunctionHeader<'ast>,
2559 ) -> (AttributeCommentMap, Vec<AttributeInfo<'ast>>, BytePos) {
2560 let first_attr = self.collect_attributes(header);
2561 self.cache_comments(state);
2562 (self.map(), self.attributes, first_attr)
2563 }
2564
2565 fn map(&mut self) -> AttributeCommentMap {
2566 let mut map = HashMap::new();
2567 for a in 0..self.attributes.len() {
2568 let is_last = a == self.attributes.len() - 1;
2569 let (mut before, mut inner, mut after) = (Vec::new(), Vec::new(), Vec::new());
2570
2571 let before_limit = self.attributes[a].span.lo();
2572 let inner_limit = self.attributes[a].span.hi();
2573 let after_limit =
2574 if !is_last { self.attributes[a + 1].span.lo() } else { self.limit_pos };
2575
2576 let mut c = 0;
2577 while c < self.comments.len() {
2578 if self.comments[c].pos() <= before_limit {
2579 before.push(self.comments.remove(c));
2580 } else if self.comments[c].pos() <= inner_limit {
2581 inner.push(self.comments.remove(c));
2582 } else if (after.is_empty() || is_last) && self.comments[c].pos() <= after_limit {
2583 after.push(self.comments.remove(c));
2584 } else {
2585 c += 1;
2586 }
2587 }
2588 map.insert(before_limit, (before, inner, after));
2589 }
2590 map
2591 }
2592
2593 fn collect_attributes(&mut self, header: &'ast ast::FunctionHeader<'ast>) -> BytePos {
2594 let mut first_pos = BytePos(u32::MAX);
2595 if let Some(v) = header.visibility {
2596 if v.span.lo() < first_pos {
2597 first_pos = v.span.lo()
2598 }
2599 self.attributes
2600 .push(AttributeInfo { kind: AttributeKind::Visibility(*v), span: v.span });
2601 }
2602 if let Some(sm) = header.state_mutability {
2603 if sm.span.lo() < first_pos {
2604 first_pos = sm.span.lo()
2605 }
2606 self.attributes
2607 .push(AttributeInfo { kind: AttributeKind::StateMutability(*sm), span: sm.span });
2608 }
2609 if let Some(span) = header.virtual_ {
2610 if span.lo() < first_pos {
2611 first_pos = span.lo()
2612 }
2613 self.attributes.push(AttributeInfo { kind: AttributeKind::Virtual, span });
2614 }
2615 if let Some(ref o) = header.override_ {
2616 if o.span.lo() < first_pos {
2617 first_pos = o.span.lo()
2618 }
2619 self.attributes.push(AttributeInfo { kind: AttributeKind::Override(o), span: o.span });
2620 }
2621 for m in header.modifiers.iter() {
2622 if m.span().lo() < first_pos {
2623 first_pos = m.span().lo()
2624 }
2625 self.attributes
2626 .push(AttributeInfo { kind: AttributeKind::Modifier(m), span: m.span() });
2627 }
2628 self.attributes.sort_by_key(|attr| attr.span.lo());
2629 first_pos
2630 }
2631
2632 fn cache_comments(&mut self, state: &mut State<'_, 'ast>) {
2633 let mut pending = None;
2634 for cmnt in state.comments.iter() {
2635 if cmnt.pos() >= self.limit_pos {
2636 break;
2637 }
2638 match pending {
2639 Some(ref p) => pending = Some(p + 1),
2640 None => pending = Some(0),
2641 }
2642 }
2643 while let Some(p) = pending {
2644 if p == 0 {
2645 pending = None;
2646 } else {
2647 pending = Some(p - 1);
2648 }
2649 let cmnt = state.next_comment().unwrap();
2650 if cmnt.style.is_blank() {
2651 continue;
2652 }
2653 self.comments.push(cmnt);
2654 }
2655 }
2656}
2657
2658fn stmt_needs_semi(stmt: &ast::StmtKind<'_>) -> bool {
2659 match stmt {
2660 ast::StmtKind::Assembly { .. }
2661 | ast::StmtKind::Block { .. }
2662 | ast::StmtKind::For { .. }
2663 | ast::StmtKind::If { .. }
2664 | ast::StmtKind::Try { .. }
2665 | ast::StmtKind::UncheckedBlock { .. }
2666 | ast::StmtKind::While { .. } => false,
2667
2668 ast::StmtKind::DeclSingle { .. }
2669 | ast::StmtKind::DeclMulti { .. }
2670 | ast::StmtKind::Break { .. }
2671 | ast::StmtKind::Continue { .. }
2672 | ast::StmtKind::DoWhile { .. }
2673 | ast::StmtKind::Emit { .. }
2674 | ast::StmtKind::Expr { .. }
2675 | ast::StmtKind::Return { .. }
2676 | ast::StmtKind::Revert { .. }
2677 | ast::StmtKind::Placeholder { .. } => true,
2678 }
2679}
2680
2681fn item_needs_iso(item: &ast::ItemKind<'_>) -> bool {
2683 match item {
2684 ast::ItemKind::Pragma(..)
2685 | ast::ItemKind::Import(..)
2686 | ast::ItemKind::Using(..)
2687 | ast::ItemKind::Variable(..)
2688 | ast::ItemKind::Udvt(..)
2689 | ast::ItemKind::Enum(..)
2690 | ast::ItemKind::Error(..)
2691 | ast::ItemKind::Event(..) => false,
2692
2693 ast::ItemKind::Contract(..) => true,
2694
2695 ast::ItemKind::Struct(strukt) => !strukt.fields.is_empty(),
2696 ast::ItemKind::Function(func) => {
2697 func.body.as_ref().is_some_and(|b| !b.is_empty())
2698 && !matches!(func.kind, ast::FunctionKind::Modifier)
2699 }
2700 }
2701}
2702
2703fn is_binary_expr(expr_kind: &ast::ExprKind<'_>) -> bool {
2704 matches!(expr_kind, ast::ExprKind::Binary(..))
2705}
2706
2707fn has_complex_successor(expr_kind: &ast::ExprKind<'_>, left: bool) -> bool {
2708 match expr_kind {
2709 ast::ExprKind::Binary(lhs, _, rhs) => {
2710 if left {
2711 has_complex_successor(&lhs.kind, left)
2712 } else {
2713 has_complex_successor(&rhs.kind, left)
2714 }
2715 }
2716 ast::ExprKind::Unary(_, expr) => has_complex_successor(&expr.kind, left),
2717 ast::ExprKind::Lit(..) | ast::ExprKind::Ident(_) => false,
2718 _ => true,
2719 }
2720}
2721
2722fn is_call(expr_kind: &ast::ExprKind<'_>) -> bool {
2723 matches!(expr_kind, ast::ExprKind::Call(..))
2724}
2725
2726fn is_call_chain(expr_kind: &ast::ExprKind<'_>, must_have_child: bool) -> bool {
2727 if let ast::ExprKind::Member(child, ..) = expr_kind {
2728 is_call_chain(&child.kind, false)
2729 } else {
2730 !must_have_child && is_call(expr_kind)
2731 }
2732}
2733
2734#[derive(Debug)]
2735struct Decision {
2736 outcome: bool,
2737 is_cached: bool,
2738}
2739
2740#[derive(Clone, Copy, PartialEq, Eq)]
2741pub(crate) enum BinOpGroup {
2742 Arithmetic,
2743 Bitwise,
2744 Comparison,
2745 Logical,
2746}
2747
2748trait BinOpExt {
2749 fn group(&self) -> BinOpGroup;
2750}
2751
2752impl BinOpExt for ast::BinOpKind {
2753 fn group(&self) -> BinOpGroup {
2754 match self {
2755 Self::Or | Self::And => BinOpGroup::Logical,
2756 Self::Eq | Self::Ne | Self::Lt | Self::Le | Self::Gt | Self::Ge => {
2757 BinOpGroup::Comparison
2758 }
2759 Self::BitOr | Self::BitXor | Self::BitAnd | Self::Shl | Self::Shr | Self::Sar => {
2760 BinOpGroup::Bitwise
2761 }
2762 Self::Add | Self::Sub | Self::Mul | Self::Div | Self::Rem | Self::Pow => {
2763 BinOpGroup::Arithmetic
2764 }
2765 }
2766 }
2767}
2768
2769pub(super) fn get_callee_head_size(callee: &ast::Expr<'_>) -> usize {
2778 match &callee.kind {
2779 ast::ExprKind::Ident(id) => id.as_str().len(),
2780 ast::ExprKind::Type(ast::Type { kind: ast::TypeKind::Elementary(ty), .. }) => {
2781 ty.to_abi_str().len()
2782 }
2783 ast::ExprKind::Member(base, member_ident) => {
2784 match &base.kind {
2785 ast::ExprKind::Ident(..) | ast::ExprKind::Type(..) => {
2786 get_callee_head_size(base) + 1 + member_ident.as_str().len()
2787 }
2788
2789 ast::ExprKind::Member(child, ..)
2791 if !matches!(&child.kind, ast::ExprKind::Call(..)) =>
2792 {
2793 get_callee_head_size(base) + 1 + member_ident.as_str().len()
2794 }
2795 _ => member_ident.as_str().len(),
2796 }
2797 }
2798
2799 _ => 0,
2801 }
2802}