1#![allow(clippy::too_many_arguments)]
2
3use super::{
4 CommentConfig, State,
5 common::{BlockFormat, ListFormat},
6};
7use solar::parse::ast::{self, Span, yul};
8
9#[rustfmt::skip]
10macro_rules! get_span {
11 () => { |value| value.span };
12 (()) => { |value| value.span() };
13}
14
15impl<'ast> State<'_, 'ast> {
17 fn print_lit_yul(&mut self, lit: &'ast ast::Lit<'ast>) {
18 self.print_lit_inner(lit, true);
19 }
20
21 pub(crate) fn print_yul_stmt(&mut self, stmt: &'ast yul::Stmt<'ast>) {
22 let yul::Stmt { ref docs, span, ref kind } = *stmt;
23 self.print_docs(docs);
24 if self.handle_span(span, false) {
25 return;
26 }
27
28 match kind {
29 yul::StmtKind::Block(stmts) => self.print_yul_block(stmts, span, false, 0),
30 yul::StmtKind::AssignSingle(path, expr) => {
31 self.print_path(path, false);
32 self.word(" := ");
33 self.neverbreak();
34 self.cursor.advance_to(expr.span.lo(), self.cursor.enabled);
35 self.print_yul_expr(expr);
36 }
37 yul::StmtKind::AssignMulti(paths, expr_call) => {
38 self.ibox(0);
39 self.commasep(
40 paths,
41 stmt.span.lo(),
42 stmt.span.hi(),
43 |this, path| this.print_path(path, false),
44 get_span!(()),
45 ListFormat::consistent(),
46 );
47 self.word(" :=");
48 self.space();
49 self.s.offset(self.ind);
50 self.ibox(0);
51 self.print_yul_expr(expr_call);
52 self.end();
53 self.end();
54 }
55 yul::StmtKind::Expr(expr_call) => self.print_yul_expr(expr_call),
56 yul::StmtKind::If(expr, stmts) => {
57 self.print_word("if "); self.print_yul_expr(expr);
59 self.nbsp(); self.print_yul_block(stmts, span, false, 4 + self.estimate_size(expr.span));
61 }
62 yul::StmtKind::For(yul::StmtFor { init, cond, step, body }) => {
63 self.ibox(0);
64
65 self.print_word("for "); self.print_yul_block(init, init.span, false, 4);
67
68 self.space();
69 self.print_yul_expr(cond);
70
71 self.space();
72 self.print_yul_block(step, step.span, false, 0);
73
74 self.space();
75 self.print_yul_block(body, body.span, false, 0);
76
77 self.end();
78 }
79 yul::StmtKind::Switch(yul::StmtSwitch { selector, cases }) => {
80 self.print_word("switch ");
81 self.print_yul_expr(selector);
82
83 self.print_trailing_comment(selector.span.hi(), None);
84
85 for yul::StmtSwitchCase { constant, body, span } in cases.iter() {
86 self.hardbreak_if_not_bol();
87 if let Some(constant) = constant {
88 self.print_comments(
89 constant.span.lo(),
90 CommentConfig::default().mixed_prev_space(),
91 );
92 self.print_word("case ");
93 self.print_lit_yul(constant);
94 self.nbsp();
95 } else {
96 self.print_comments(
97 body.span.lo(),
98 CommentConfig::default().mixed_prev_space(),
99 );
100 self.print_word("default ");
101 }
102 self.print_yul_block(body, *span, false, 0);
103
104 self.print_trailing_comment(selector.span.hi(), None);
105 }
106 }
107 yul::StmtKind::Leave => self.print_word("leave"),
108 yul::StmtKind::Break => self.print_word("break"),
109 yul::StmtKind::Continue => self.print_word("continue"),
110 yul::StmtKind::FunctionDef(func) => {
111 let yul::Function { name, parameters, returns, body } = func;
112 let params_hi = parameters
113 .last()
114 .map_or(returns.first().map_or(body.span.lo(), |r| r.span.lo()), |p| {
115 p.span.hi()
116 });
117
118 self.cbox(0);
119 self.s.ibox(0);
120 self.print_word("function ");
121 self.print_ident(name);
122 self.print_tuple(
123 parameters,
124 span.lo(),
125 params_hi,
126 Self::print_ident,
127 get_span!(),
128 ListFormat::consistent(),
129 );
130 self.nbsp();
131 let has_returns = !returns.is_empty();
132 let skip_opening_brace = has_returns;
133 if self.can_yul_header_params_be_inlined(func) {
134 self.neverbreak();
135 }
136 if has_returns {
137 self.commasep(
138 returns,
139 returns.first().map_or(params_hi, |ret| ret.span.lo()),
140 returns.last().map_or(body.span.lo(), |ret| ret.span.hi()),
141 Self::print_ident,
142 get_span!(),
143 ListFormat::yul(Some("->"), Some("{")),
144 );
145 }
146 self.end();
147 self.print_yul_block(body, span, skip_opening_brace, 0);
148 self.end();
149 }
150 yul::StmtKind::VarDecl(idents, expr) => {
151 self.s.ibox(self.ind);
152 self.print_word("let ");
153 self.commasep(
154 idents,
155 stmt.span.lo(),
156 idents.last().map_or(stmt.span.lo(), |i| i.span.hi()),
157 Self::print_ident,
158 get_span!(),
159 ListFormat::consistent(),
160 );
161 if let Some(expr) = expr {
162 self.print_word(" :=");
163 self.space();
164 self.print_yul_expr(expr);
165 }
166 self.end();
167 }
168 }
169 }
170
171 fn print_yul_expr(&mut self, expr: &'ast yul::Expr<'ast>) {
172 let yul::Expr { span, ref kind } = *expr;
173 if self.handle_span(span, false) {
174 return;
175 }
176
177 match kind {
178 yul::ExprKind::Path(path) => self.print_path(path, false),
179 yul::ExprKind::Call(yul::ExprCall { name, arguments }) => {
180 self.print_ident(name);
181 self.print_tuple(
182 arguments,
183 span.lo(),
184 span.hi(),
185 |s, arg| s.print_yul_expr(arg),
186 get_span!(),
187 ListFormat::consistent().break_single(true),
188 );
189 }
190 yul::ExprKind::Lit(lit) => {
191 if matches!(&lit.kind, ast::LitKind::Address(_)) {
192 self.print_span_cold(lit.span);
193 } else {
194 self.print_lit_yul(lit);
195 }
196 }
197 }
198 }
199
200 pub(super) fn print_yul_block(
201 &mut self,
202 block: &'ast yul::Block<'ast>,
203 span: Span,
204 skip_opening_brace: bool,
205 prefix_len: usize,
206 ) {
207 if self.handle_span(span, false) {
208 return;
209 }
210
211 if !skip_opening_brace {
212 self.print_word("{");
213 }
214
215 let can_inline_block = if block.len() <= 1 && !self.is_multiline_yul_block(block) {
216 if self.max_space_left(prefix_len) == 0 {
217 self.estimate_size(block.span) + self.config.tab_width < self.space_left()
218 } else {
219 self.estimate_size(block.span) + prefix_len < self.space_left()
220 }
221 } else {
222 false
223 };
224 if can_inline_block {
225 self.neverbreak();
226 self.print_block_inner(
227 block,
228 BlockFormat::NoBraces(None),
229 |s, stmt| {
230 s.nbsp();
231 s.print_yul_stmt(stmt);
232 if s.peek_comment_before(stmt.span.hi()).is_none()
233 && s.peek_trailing_comment(stmt.span.hi(), None).is_none()
234 {
235 s.nbsp();
236 }
237 s.print_comments(
238 stmt.span.hi(),
239 CommentConfig::skip_ws().mixed_no_break().mixed_post_nbsp(),
240 );
241 if !s.last_token_is_space() {
242 s.nbsp();
243 }
244 },
245 |b| b.span,
246 span.hi(),
247 );
248 } else {
249 let (mut i, n_args) = (0, block.len().saturating_sub(1));
250 self.print_block_inner(
251 block,
252 BlockFormat::NoBraces(Some(self.ind)),
253 |s, stmt| {
254 s.print_yul_stmt(stmt);
255 s.print_comments(stmt.span.hi(), CommentConfig::default());
256 if i != n_args {
257 let next_span = block[i + 1].span;
258 s.print_trailing_comment(stmt.span.hi(), Some(next_span.lo()));
259 if !s.is_bol_or_only_ind() && !s.inline_config.is_disabled(stmt.span) {
260 if s.inline_config.is_disabled(next_span)
263 && s.peek_comment_before(next_span.lo())
264 .is_none_or(|cmnt| !cmnt.style.is_isolated())
265 {
266 s.word("\n");
267 } else {
269 s.hardbreak_if_not_bol();
270 }
271 }
272 i += 1;
273 } else {
274 s.print_trailing_comment(stmt.span.hi(), Some(span.hi()));
275 }
276 },
277 |b| b.span,
278 span.hi(),
279 );
280 }
281 self.print_word("}");
282 self.print_trailing_comment(span.hi(), None);
283 }
284
285 fn is_multiline_yul_block(&self, block: &'ast yul::Block<'ast>) -> bool {
287 if block.stmts.is_empty() {
288 return false;
289 }
290 if self.sm.is_multiline(block.span)
291 && let Ok(snip) = self.sm.span_to_snippet(block.span)
292 {
293 let code_lines = snip.lines().filter(|line| {
294 let trimmed = line.trim();
295 !trimmed.is_empty()
297 });
298 return code_lines.count() > 1;
299 }
300 false
301 }
302
303 fn estimate_yul_header_params_size(&mut self, func: &yul::Function<'_>) -> usize {
304 let params = func
306 .parameters
307 .iter()
308 .fold(0, |len, p| if len != 0 { len + 2 } else { 2 } + self.estimate_size(p.span));
309
310 9 + self.estimate_size(func.name.span) + 1 + params + 3
312 }
313
314 fn can_yul_header_params_be_inlined(&mut self, func: &yul::Function<'_>) -> bool {
315 self.estimate_yul_header_params_size(func) <= self.space_left()
316 }
317}