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