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