solang_parser/helpers/
fmt.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Implements `Display` for all parse tree data types, following the [Solidity style guide][ref].
4//!
5//! [ref]: https://docs.soliditylang.org/en/latest/style-guide.html
6
7use crate::pt::{self, StorageType};
8use std::{
9    borrow::Cow,
10    fmt::{Display, Formatter, Result, Write},
11};
12
13macro_rules! write_opt {
14    // no sep
15    ($f:expr, $opt:expr $(,)?) => {
16        if let Some(t) = $opt {
17            Display::fmt(t, $f)?;
18        }
19    };
20
21    // sep before
22    ($f:expr, $sep:literal, $opt:expr $(,)?) => {
23        if let Some(t) = $opt {
24            Display::fmt(&$sep, $f)?;
25            Display::fmt(t, $f)?;
26        }
27    };
28
29    // sep after
30    ($f:expr, $opt:expr, $sep:literal $(,)?) => {
31        if let Some(t) = $opt {
32            Display::fmt(t, $f)?;
33            Display::fmt(&$sep, $f)?;
34        }
35    };
36
37    // both
38    ($f:expr, $sep1:literal, $opt:expr, $sep2:literal $(,)?) => {
39        if let Some(t) = $opt {
40            Display::fmt(&$sep1, $f)?;
41            Display::fmt(t, $f)?;
42            Display::fmt(&$sep2, $f)?;
43        }
44    };
45}
46
47// structs
48impl Display for pt::Annotation {
49    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
50        f.write_char('@')?;
51        self.id.fmt(f)?;
52        if let Some(value) = &self.value {
53            f.write_char('(')?;
54            value.fmt(f)?;
55            f.write_char(')')?;
56        }
57
58        Ok(())
59    }
60}
61
62impl Display for pt::Base {
63    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
64        self.name.fmt(f)?;
65        if let Some(args) = &self.args {
66            f.write_char('(')?;
67            write_separated(args, f, ", ")?;
68            f.write_char(')')?;
69        }
70        Ok(())
71    }
72}
73
74impl Display for pt::ContractDefinition {
75    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
76        self.ty.fmt(f)?;
77        f.write_char(' ')?;
78
79        write_opt!(f, &self.name, ' ');
80
81        if !self.base.is_empty() {
82            write_separated(&self.base, f, " ")?;
83            f.write_char(' ')?;
84        }
85
86        f.write_char('{')?;
87        write_separated(&self.parts, f, " ")?;
88        f.write_char('}')
89    }
90}
91
92impl Display for pt::EnumDefinition {
93    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
94        f.write_str("enum ")?;
95        write_opt!(f, &self.name, ' ');
96
97        f.write_char('{')?;
98        write_separated_iter(self.values.iter().flatten(), f, ", ")?;
99        f.write_char('}')
100    }
101}
102
103impl Display for pt::ErrorDefinition {
104    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
105        self.keyword.fmt(f)?;
106        write_opt!(f, ' ', &self.name);
107
108        f.write_char('(')?;
109        write_separated(&self.fields, f, ", ")?;
110        f.write_str(");")
111    }
112}
113
114impl Display for pt::ErrorParameter {
115    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
116        self.ty.fmt(f)?;
117        write_opt!(f, ' ', &self.name);
118        Ok(())
119    }
120}
121
122impl Display for pt::EventDefinition {
123    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
124        f.write_str("event")?;
125        write_opt!(f, ' ', &self.name);
126
127        f.write_char('(')?;
128        write_separated(&self.fields, f, ", ")?;
129        f.write_char(')')?;
130
131        if self.anonymous {
132            f.write_str(" anonymous")?;
133        }
134        f.write_char(';')
135    }
136}
137
138impl Display for pt::EventParameter {
139    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
140        self.ty.fmt(f)?;
141        if self.indexed {
142            f.write_str(" indexed")?;
143        }
144        write_opt!(f, ' ', &self.name);
145        Ok(())
146    }
147}
148
149impl Display for pt::FunctionDefinition {
150    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
151        self.ty.fmt(f)?;
152        write_opt!(f, ' ', &self.name);
153
154        f.write_char('(')?;
155        fmt_parameter_list(&self.params, f)?;
156        f.write_char(')')?;
157
158        if !self.attributes.is_empty() {
159            f.write_char(' ')?;
160            write_separated(&self.attributes, f, " ")?;
161        }
162
163        if !self.returns.is_empty() {
164            f.write_str(" returns (")?;
165            fmt_parameter_list(&self.returns, f)?;
166            f.write_char(')')?;
167        }
168
169        if let Some(body) = &self.body {
170            f.write_char(' ')?;
171            body.fmt(f)
172        } else {
173            f.write_char(';')
174        }
175    }
176}
177
178impl Display for pt::HexLiteral {
179    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
180        f.write_str(&self.hex)
181    }
182}
183
184impl Display for pt::Identifier {
185    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
186        f.write_str(&self.name)
187    }
188}
189
190impl Display for pt::IdentifierPath {
191    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
192        write_separated(&self.identifiers, f, ".")
193    }
194}
195
196impl Display for pt::NamedArgument {
197    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
198        self.name.fmt(f)?;
199        f.write_str(": ")?;
200        self.expr.fmt(f)
201    }
202}
203
204impl Display for pt::Parameter {
205    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
206        write_opt!(f, &self.annotation, ' ');
207        self.ty.fmt(f)?;
208        write_opt!(f, ' ', &self.storage);
209        write_opt!(f, ' ', &self.name);
210        Ok(())
211    }
212}
213
214impl Display for pt::SourceUnit {
215    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
216        write_separated(&self.0, f, "\n")
217    }
218}
219
220impl Display for pt::StringLiteral {
221    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
222        if self.unicode {
223            f.write_str("unicode")?;
224        }
225        f.write_char('"')?;
226        f.write_str(&self.string)?;
227        f.write_char('"')
228    }
229}
230
231impl Display for pt::StructDefinition {
232    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
233        f.write_str("struct ")?;
234        write_opt!(f, &self.name, ' ');
235
236        f.write_char('{')?;
237        write_separated(&self.fields, f, "; ")?;
238        if !self.fields.is_empty() {
239            f.write_char(';')?;
240        }
241        f.write_char('}')
242    }
243}
244
245impl Display for pt::TypeDefinition {
246    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
247        f.write_str("type ")?;
248        self.name.fmt(f)?;
249        f.write_str(" is ")?;
250        self.ty.fmt(f)?;
251        f.write_char(';')
252    }
253}
254
255impl Display for pt::Using {
256    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
257        f.write_str("using ")?;
258
259        self.list.fmt(f)?;
260
261        f.write_str(" for ")?;
262
263        match &self.ty {
264            Some(ty) => Display::fmt(ty, f),
265            None => f.write_str("*"),
266        }?;
267
268        write_opt!(f, ' ', &self.global);
269        f.write_char(';')
270    }
271}
272
273impl Display for pt::UsingFunction {
274    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
275        self.path.fmt(f)?;
276        write_opt!(f, " as ", &self.oper);
277        Ok(())
278    }
279}
280
281impl Display for pt::VariableDeclaration {
282    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
283        self.ty.fmt(f)?;
284        write_opt!(f, ' ', &self.storage);
285        write_opt!(f, ' ', &self.name);
286        Ok(())
287    }
288}
289
290impl Display for pt::VariableDefinition {
291    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
292        self.ty.fmt(f)?;
293        if !self.attrs.is_empty() {
294            f.write_char(' ')?;
295            write_separated(&self.attrs, f, " ")?;
296        }
297        write_opt!(f, ' ', &self.name);
298        write_opt!(f, " = ", &self.initializer);
299        f.write_char(';')
300    }
301}
302
303impl Display for pt::YulBlock {
304    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
305        f.write_char('{')?;
306        write_separated(&self.statements, f, " ")?;
307        f.write_char('}')
308    }
309}
310
311impl Display for pt::YulFor {
312    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
313        f.write_str("for ")?;
314        self.init_block.fmt(f)?;
315        f.write_char(' ')?;
316        self.condition.fmt(f)?;
317        f.write_char(' ')?;
318        self.post_block.fmt(f)?;
319        f.write_char(' ')?;
320        self.execution_block.fmt(f)
321    }
322}
323
324impl Display for pt::YulFunctionCall {
325    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
326        self.id.fmt(f)?;
327        f.write_char('(')?;
328        write_separated(&self.arguments, f, ", ")?;
329        f.write_char(')')
330    }
331}
332
333impl Display for pt::YulFunctionDefinition {
334    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
335        f.write_str("function ")?;
336        self.id.fmt(f)?;
337        f.write_char('(')?;
338        write_separated(&self.params, f, ", ")?;
339        f.write_str(") ")?;
340
341        if !self.returns.is_empty() {
342            f.write_str("-> (")?;
343            write_separated(&self.returns, f, ", ")?;
344            f.write_str(") ")?;
345        }
346
347        self.body.fmt(f)
348    }
349}
350
351impl Display for pt::YulSwitch {
352    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
353        f.write_str("switch ")?;
354        self.condition.fmt(f)?;
355        if !self.cases.is_empty() {
356            f.write_char(' ')?;
357            write_separated(&self.cases, f, " ")?;
358        }
359        write_opt!(f, " ", &self.default);
360        Ok(())
361    }
362}
363
364impl Display for pt::YulTypedIdentifier {
365    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
366        self.id.fmt(f)?;
367        write_opt!(f, ": ", &self.ty);
368        Ok(())
369    }
370}
371
372// enums
373impl Display for pt::CatchClause {
374    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
375        match self {
376            Self::Simple(_, param, block) => {
377                f.write_str("catch ")?;
378                write_opt!(f, '(', param, ") ");
379                block.fmt(f)
380            }
381            Self::Named(_, ident, param, block) => {
382                f.write_str("catch ")?;
383                ident.fmt(f)?;
384                f.write_char('(')?;
385                param.fmt(f)?;
386                f.write_str(") ")?;
387                block.fmt(f)
388            }
389        }
390    }
391}
392
393impl Display for pt::Comment {
394    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
395        f.write_str(self.value())
396    }
397}
398
399impl Display for pt::ContractPart {
400    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
401        match self {
402            Self::StructDefinition(inner) => inner.fmt(f),
403            Self::EventDefinition(inner) => inner.fmt(f),
404            Self::EnumDefinition(inner) => inner.fmt(f),
405            Self::ErrorDefinition(inner) => inner.fmt(f),
406            Self::VariableDefinition(inner) => inner.fmt(f),
407            Self::FunctionDefinition(inner) => inner.fmt(f),
408            Self::TypeDefinition(inner) => inner.fmt(f),
409            Self::Annotation(inner) => inner.fmt(f),
410            Self::Using(inner) => inner.fmt(f),
411            Self::StraySemicolon(_) => f.write_char(';'),
412        }
413    }
414}
415
416impl Display for pt::ContractTy {
417    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
418        f.write_str(self.as_str())
419    }
420}
421impl pt::ContractTy {
422    /// Returns the string representation of this type.
423    pub const fn as_str(&self) -> &'static str {
424        match self {
425            Self::Abstract(..) => "abstract contract",
426            Self::Contract(..) => "contract",
427            Self::Interface(..) => "interface",
428            Self::Library(..) => "library",
429        }
430    }
431}
432
433impl Display for pt::Expression {
434    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
435        match self {
436            Self::New(_, expr) => {
437                f.write_str("new ")?;
438                expr.fmt(f)
439            }
440            Self::Delete(_, expr) => {
441                f.write_str("delete ")?;
442                expr.fmt(f)
443            }
444
445            Self::Type(_, ty) => ty.fmt(f),
446
447            Self::Variable(ident) => ident.fmt(f),
448
449            Self::ArrayLiteral(_, exprs) => {
450                f.write_char('[')?;
451                write_separated(exprs, f, ", ")?;
452                f.write_char(']')
453            }
454            Self::ArraySubscript(_, expr1, expr2) => {
455                expr1.fmt(f)?;
456                f.write_char('[')?;
457                write_opt!(f, expr2);
458                f.write_char(']')
459            }
460            Self::ArraySlice(_, arr, l, r) => {
461                arr.fmt(f)?;
462                f.write_char('[')?;
463                write_opt!(f, l);
464                f.write_char(':')?;
465                write_opt!(f, r);
466                f.write_char(']')
467            }
468
469            Self::MemberAccess(_, expr, ident) => {
470                expr.fmt(f)?;
471                f.write_char('.')?;
472                ident.fmt(f)
473            }
474
475            Self::Parenthesis(_, expr) => {
476                f.write_char('(')?;
477                expr.fmt(f)?;
478                f.write_char(')')
479            }
480            Self::List(_, list) => {
481                f.write_char('(')?;
482                fmt_parameter_list(list, f)?;
483                f.write_char(')')
484            }
485
486            Self::AddressLiteral(_, lit) => f.write_str(lit),
487            Self::StringLiteral(vals) => write_separated(vals, f, " "),
488            Self::HexLiteral(vals) => write_separated(vals, f, " "),
489            Self::BoolLiteral(_, bool) => {
490                let s = if *bool { "true" } else { "false" };
491                f.write_str(s)
492            }
493            Self::HexNumberLiteral(_, val, unit) => {
494                // TODO: Check with and write the checksummed address when len == 42
495                // ref: https://docs.soliditylang.org/en/latest/types.html#address-literals
496                f.write_str(val)?;
497                write_opt!(f, ' ', unit);
498                Ok(())
499            }
500            Self::NumberLiteral(_, val, exp, unit) => {
501                let val = rm_underscores(val);
502                f.write_str(&val)?;
503                if !exp.is_empty() {
504                    f.write_char('e')?;
505                    let exp = rm_underscores(exp);
506                    f.write_str(&exp)?;
507                }
508                write_opt!(f, ' ', unit);
509                Ok(())
510            }
511            Self::RationalNumberLiteral(_, val, fraction, exp, unit) => {
512                let val = rm_underscores(val);
513                f.write_str(&val)?;
514
515                let mut fraction = fraction.trim_end_matches('0');
516                if fraction.is_empty() {
517                    fraction = "0"
518                }
519                f.write_char('.')?;
520                f.write_str(fraction)?;
521
522                if !exp.is_empty() {
523                    f.write_char('e')?;
524                    let exp = rm_underscores(exp);
525                    f.write_str(&exp)?;
526                }
527                write_opt!(f, ' ', unit);
528                Ok(())
529            }
530
531            Self::FunctionCall(_, expr, exprs) => {
532                expr.fmt(f)?;
533                f.write_char('(')?;
534                write_separated(exprs, f, ", ")?;
535                f.write_char(')')
536            }
537            Self::FunctionCallBlock(_, expr, block) => {
538                expr.fmt(f)?;
539                block.fmt(f)
540            }
541            Self::NamedFunctionCall(_, expr, args) => {
542                expr.fmt(f)?;
543                f.write_str("({")?;
544                write_separated(args, f, ", ")?;
545                f.write_str("})")
546            }
547
548            Self::ConditionalOperator(_, cond, l, r) => {
549                cond.fmt(f)?;
550                f.write_str(" ? ")?;
551                l.fmt(f)?;
552                f.write_str(" : ")?;
553                r.fmt(f)
554            }
555
556            Self::PreIncrement(..)
557            | Self::PostIncrement(..)
558            | Self::PreDecrement(..)
559            | Self::PostDecrement(..)
560            | Self::Not(..)
561            | Self::BitwiseNot(..)
562            | Self::UnaryPlus(..)
563            | Self::Add(..)
564            | Self::Negate(..)
565            | Self::Subtract(..)
566            | Self::Power(..)
567            | Self::Multiply(..)
568            | Self::Divide(..)
569            | Self::Modulo(..)
570            | Self::ShiftLeft(..)
571            | Self::ShiftRight(..)
572            | Self::BitwiseAnd(..)
573            | Self::BitwiseXor(..)
574            | Self::BitwiseOr(..)
575            | Self::Less(..)
576            | Self::More(..)
577            | Self::LessEqual(..)
578            | Self::MoreEqual(..)
579            | Self::And(..)
580            | Self::Or(..)
581            | Self::Equal(..)
582            | Self::NotEqual(..)
583            | Self::Assign(..)
584            | Self::AssignOr(..)
585            | Self::AssignAnd(..)
586            | Self::AssignXor(..)
587            | Self::AssignShiftLeft(..)
588            | Self::AssignShiftRight(..)
589            | Self::AssignAdd(..)
590            | Self::AssignSubtract(..)
591            | Self::AssignMultiply(..)
592            | Self::AssignDivide(..)
593            | Self::AssignModulo(..) => {
594                let (left, right) = self.components();
595                let has_spaces = self.has_space_around();
596
597                if let Some(left) = left {
598                    left.fmt(f)?;
599                    if has_spaces {
600                        f.write_char(' ')?;
601                    }
602                }
603
604                let operator = self.operator().unwrap();
605                f.write_str(operator)?;
606
607                if let Some(right) = right {
608                    if has_spaces {
609                        f.write_char(' ')?;
610                    }
611                    right.fmt(f)?;
612                }
613
614                Ok(())
615            }
616        }
617    }
618}
619impl pt::Expression {
620    /// Returns the operator string of this expression, if any.
621    #[inline]
622    pub const fn operator(&self) -> Option<&'static str> {
623        use pt::Expression::*;
624        let operator = match self {
625            New(..) => "new",
626            Delete(..) => "delete",
627
628            PreIncrement(..) | PostIncrement(..) => "++",
629            PreDecrement(..) | PostDecrement(..) => "--",
630
631            Not(..) => "!",
632            BitwiseNot(..) => "~",
633            UnaryPlus(..) | Add(..) => "+",
634            Negate(..) | Subtract(..) => "-",
635            Power(..) => "**",
636            Multiply(..) => "*",
637            Divide(..) => "/",
638            Modulo(..) => "%",
639            ShiftLeft(..) => "<<",
640            ShiftRight(..) => ">>",
641            BitwiseAnd(..) => "&",
642            BitwiseXor(..) => "^",
643            BitwiseOr(..) => "|",
644
645            Less(..) => "<",
646            More(..) => ">",
647            LessEqual(..) => "<=",
648            MoreEqual(..) => ">=",
649            And(..) => "&&",
650            Or(..) => "||",
651            Equal(..) => "==",
652            NotEqual(..) => "!=",
653
654            Assign(..) => "=",
655            AssignOr(..) => "|=",
656            AssignAnd(..) => "&=",
657            AssignXor(..) => "^=",
658            AssignShiftLeft(..) => "<<=",
659            AssignShiftRight(..) => ">>=",
660            AssignAdd(..) => "+=",
661            AssignSubtract(..) => "-=",
662            AssignMultiply(..) => "*=",
663            AssignDivide(..) => "/=",
664            AssignModulo(..) => "%=",
665
666            MemberAccess(..)
667            | ArraySubscript(..)
668            | ArraySlice(..)
669            | FunctionCall(..)
670            | FunctionCallBlock(..)
671            | NamedFunctionCall(..)
672            | ConditionalOperator(..)
673            | BoolLiteral(..)
674            | NumberLiteral(..)
675            | RationalNumberLiteral(..)
676            | HexNumberLiteral(..)
677            | StringLiteral(..)
678            | Type(..)
679            | HexLiteral(..)
680            | AddressLiteral(..)
681            | Variable(..)
682            | List(..)
683            | ArrayLiteral(..)
684            | Parenthesis(..) => return None,
685        };
686        Some(operator)
687    }
688}
689
690impl Display for pt::FunctionAttribute {
691    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
692        match self {
693            Self::Mutability(mutability) => mutability.fmt(f),
694            Self::Visibility(visibility) => visibility.fmt(f),
695            Self::Virtual(_) => f.write_str("virtual"),
696            Self::Immutable(_) => f.write_str("immutable"),
697            Self::Override(_, idents) => {
698                f.write_str("override")?;
699                if !idents.is_empty() {
700                    f.write_char('(')?;
701                    write_separated(idents, f, ", ")?;
702                    f.write_char(')')?;
703                }
704                Ok(())
705            }
706            Self::BaseOrModifier(_, base) => base.fmt(f),
707            Self::Error(_) => Ok(()),
708        }
709    }
710}
711
712impl Display for pt::FunctionTy {
713    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
714        f.write_str(self.as_str())
715    }
716}
717impl pt::FunctionTy {
718    /// Returns the string representation of this type.
719    pub const fn as_str(&self) -> &'static str {
720        match self {
721            Self::Constructor => "constructor",
722            Self::Function => "function",
723            Self::Fallback => "fallback",
724            Self::Receive => "receive",
725            Self::Modifier => "modifier",
726        }
727    }
728}
729
730impl Display for pt::Import {
731    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
732        match self {
733            Self::Plain(lit, _) => {
734                f.write_str("import ")?;
735                lit.fmt(f)?;
736                f.write_char(';')
737            }
738            Self::GlobalSymbol(lit, ident, _) => {
739                f.write_str("import ")?;
740                lit.fmt(f)?;
741                f.write_str(" as ")?;
742                ident.fmt(f)?;
743                f.write_char(';')
744            }
745            Self::Rename(lit, idents, _) => {
746                f.write_str("import {")?;
747
748                // same as `write_separated_iter`
749                let mut idents = idents.iter();
750                if let Some((ident, as_ident)) = idents.next() {
751                    ident.fmt(f)?;
752                    write_opt!(f, " as ", as_ident);
753                    for (ident, as_ident) in idents {
754                        f.write_str(", ")?;
755                        ident.fmt(f)?;
756                        write_opt!(f, " as ", as_ident);
757                    }
758                }
759                f.write_str("} from ")?;
760                lit.fmt(f)?;
761                f.write_char(';')
762            }
763        }
764    }
765}
766
767impl Display for pt::ImportPath {
768    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
769        match self {
770            Self::Filename(lit) => lit.fmt(f),
771            Self::Path(path) => path.fmt(f),
772        }
773    }
774}
775
776impl Display for pt::Mutability {
777    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
778        f.write_str(self.as_str())
779    }
780}
781impl pt::Mutability {
782    /// Returns the string representation of this type.
783    pub const fn as_str(&self) -> &'static str {
784        match self {
785            Self::Pure(_) => "pure",
786            Self::Constant(_) | Self::View(_) => "view",
787            Self::Payable(_) => "payable",
788        }
789    }
790}
791
792impl Display for pt::SourceUnitPart {
793    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
794        match self {
795            Self::ImportDirective(inner) => inner.fmt(f),
796            Self::ContractDefinition(inner) => inner.fmt(f),
797            Self::EnumDefinition(inner) => inner.fmt(f),
798            Self::StructDefinition(inner) => inner.fmt(f),
799            Self::EventDefinition(inner) => inner.fmt(f),
800            Self::ErrorDefinition(inner) => inner.fmt(f),
801            Self::FunctionDefinition(inner) => inner.fmt(f),
802            Self::VariableDefinition(inner) => inner.fmt(f),
803            Self::TypeDefinition(inner) => inner.fmt(f),
804            Self::Annotation(inner) => inner.fmt(f),
805            Self::Using(inner) => inner.fmt(f),
806            Self::PragmaDirective(inner) => inner.fmt(f),
807            Self::StraySemicolon(_) => f.write_char(';'),
808        }
809    }
810}
811
812impl Display for pt::PragmaDirective {
813    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
814        match self {
815            Self::Identifier(_, ident, val) => {
816                f.write_str("pragma")?;
817                write_opt!(f, ' ', ident);
818                write_opt!(f, ' ', val);
819                f.write_char(';')
820            }
821            Self::StringLiteral(_, ident, lit) => {
822                f.write_str("pragma ")?;
823                ident.fmt(f)?;
824                f.write_char(' ')?;
825                lit.fmt(f)?;
826                f.write_char(';')
827            }
828            Self::Version(_, ident, versions) => {
829                f.write_str("pragma ")?;
830                ident.fmt(f)?;
831                f.write_char(' ')?;
832                write_separated(versions, f, " ")?;
833                f.write_char(';')
834            }
835        }
836    }
837}
838
839impl Display for pt::VersionComparator {
840    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
841        match self {
842            Self::Plain { version, .. } => write_separated(version, f, "."),
843            Self::Operator { op, version, .. } => {
844                op.fmt(f)?;
845                write_separated(version, f, ".")
846            }
847            Self::Range { from, to, .. } => {
848                write_separated(from, f, ".")?;
849                f.write_str(" - ")?;
850                write_separated(to, f, ".")
851            }
852            Self::Or { left, right, .. } => {
853                left.fmt(f)?;
854                f.write_str(" || ")?;
855                right.fmt(f)
856            }
857        }
858    }
859}
860
861impl Display for pt::VersionOp {
862    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
863        f.write_str(self.as_str())
864    }
865}
866
867impl pt::VersionOp {
868    /// Returns the string representation of this type.
869    pub const fn as_str(&self) -> &'static str {
870        match self {
871            Self::Exact => "=",
872            Self::Greater => ">",
873            Self::GreaterEq => ">=",
874            Self::Less => "<",
875            Self::LessEq => "<=",
876            Self::Tilde => "~",
877            Self::Caret => "^",
878            Self::Wildcard => "*",
879        }
880    }
881}
882
883impl Display for pt::Statement {
884    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
885        match self {
886            Self::Block {
887                unchecked,
888                statements,
889                ..
890            } => {
891                if *unchecked {
892                    f.write_str("unchecked ")?;
893                }
894
895                f.write_char('{')?;
896                write_separated(statements, f, " ")?;
897                f.write_char('}')
898            }
899            Self::Assembly {
900                dialect,
901                flags,
902                block,
903                ..
904            } => {
905                f.write_str("assembly ")?;
906                write_opt!(f, dialect, ' ');
907                if let Some(flags) = flags {
908                    if !flags.is_empty() {
909                        f.write_char('(')?;
910                        write_separated(flags, f, ", ")?;
911                        f.write_str(") ")?;
912                    }
913                }
914                block.fmt(f)
915            }
916            Self::Args(_, args) => {
917                f.write_char('{')?;
918                write_separated(args, f, ", ")?;
919                f.write_char('}')
920            }
921            Self::If(_, cond, block, end_block) => {
922                f.write_str("if (")?;
923                cond.fmt(f)?;
924                f.write_str(") ")?;
925                block.fmt(f)?;
926                write_opt!(f, " else ", end_block);
927                Ok(())
928            }
929            Self::While(_, cond, block) => {
930                f.write_str("while (")?;
931                cond.fmt(f)?;
932                f.write_str(") ")?;
933                block.fmt(f)
934            }
935            Self::Expression(_, expr) => {
936                expr.fmt(f)?;
937                f.write_char(';')
938            }
939            Self::VariableDefinition(_, var, expr) => {
940                var.fmt(f)?;
941                write_opt!(f, " = ", expr);
942                f.write_char(';')
943            }
944            Self::For(_, init, cond, expr, block) => {
945                f.write_str("for (")?;
946                // edge case, don't write semicolon on a variable definition
947                match init.as_deref() {
948                    Some(var @ pt::Statement::VariableDefinition(..)) => var.fmt(f),
949                    Some(stmt) => {
950                        stmt.fmt(f)?;
951                        f.write_char(';')
952                    }
953                    None => f.write_char(';'),
954                }?;
955                write_opt!(f, ' ', cond);
956                f.write_char(';')?;
957                write_opt!(f, ' ', expr);
958                f.write_str(") ")?;
959                if let Some(block) = block {
960                    block.fmt(f)
961                } else {
962                    f.write_char(';')
963                }
964            }
965            Self::DoWhile(_, block, cond) => {
966                f.write_str("do ")?;
967                block.fmt(f)?;
968                f.write_str(" while (")?;
969                cond.fmt(f)?;
970                f.write_str(");")
971            }
972            Self::Continue(_) => f.write_str("continue;"),
973            Self::Break(_) => f.write_str("break;"),
974            Self::Return(_, expr) => {
975                f.write_str("return")?;
976                write_opt!(f, ' ', expr);
977                f.write_char(';')
978            }
979            Self::Revert(_, ident, exprs) => {
980                f.write_str("revert")?;
981                write_opt!(f, ' ', ident);
982                f.write_char('(')?;
983                write_separated(exprs, f, ", ")?;
984                f.write_str(");")
985            }
986            Self::RevertNamedArgs(_, ident, args) => {
987                f.write_str("revert")?;
988                write_opt!(f, ' ', ident);
989                f.write_char('(')?;
990                if !args.is_empty() {
991                    f.write_char('{')?;
992                    write_separated(args, f, ", ")?;
993                    f.write_char('}')?;
994                }
995                f.write_str(");")
996            }
997            Self::Emit(_, expr) => {
998                f.write_str("emit ")?;
999                expr.fmt(f)?;
1000                f.write_char(';')
1001            }
1002            Self::Try(_, expr, returns, catch) => {
1003                f.write_str("try ")?;
1004                expr.fmt(f)?;
1005
1006                if let Some((list, stmt)) = returns {
1007                    f.write_str(" returns (")?;
1008                    fmt_parameter_list(list, f)?;
1009                    f.write_str(") ")?;
1010                    stmt.fmt(f)?;
1011                }
1012
1013                if !catch.is_empty() {
1014                    f.write_char(' ')?;
1015                    write_separated(catch, f, " ")?;
1016                }
1017                Ok(())
1018            }
1019            Self::Error(_) => Ok(()),
1020        }
1021    }
1022}
1023
1024impl Display for pt::StorageLocation {
1025    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1026        f.write_str(self.as_str())
1027    }
1028}
1029impl pt::StorageLocation {
1030    /// Returns the string representation of this type.
1031    pub const fn as_str(&self) -> &'static str {
1032        match self {
1033            Self::Memory(_) => "memory",
1034            Self::Storage(_) => "storage",
1035            Self::Calldata(_) => "calldata",
1036        }
1037    }
1038}
1039
1040impl Display for pt::Type {
1041    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1042        match self {
1043            Self::Address => f.write_str("address"),
1044            Self::AddressPayable => f.write_str("address payable"),
1045            Self::Payable => f.write_str("payable"),
1046            Self::Bool => f.write_str("bool"),
1047            Self::String => f.write_str("string"),
1048            Self::Rational => f.write_str("fixed"),
1049            Self::DynamicBytes => f.write_str("bytes"),
1050            Self::Bytes(n) => {
1051                f.write_str("bytes")?;
1052                n.fmt(f)
1053            }
1054            Self::Int(n) => {
1055                f.write_str("int")?;
1056                n.fmt(f)
1057            }
1058            Self::Uint(n) => {
1059                f.write_str("uint")?;
1060                n.fmt(f)
1061            }
1062            Self::Mapping {
1063                key,
1064                key_name,
1065                value,
1066                value_name,
1067                ..
1068            } => {
1069                f.write_str("mapping(")?;
1070
1071                key.fmt(f)?;
1072                write_opt!(f, ' ', key_name);
1073
1074                f.write_str(" => ")?;
1075
1076                value.fmt(f)?;
1077                write_opt!(f, ' ', value_name);
1078
1079                f.write_char(')')
1080            }
1081            Self::Function {
1082                params,
1083                attributes,
1084                returns,
1085            } => {
1086                f.write_str("function (")?;
1087                fmt_parameter_list(params, f)?;
1088                f.write_char(')')?;
1089
1090                if !attributes.is_empty() {
1091                    f.write_char(' ')?;
1092                    write_separated(attributes, f, " ")?;
1093                }
1094
1095                if let Some((returns, attrs)) = returns {
1096                    if !attrs.is_empty() {
1097                        f.write_char(' ')?;
1098                        write_separated(attrs, f, " ")?;
1099                    }
1100
1101                    if !returns.is_empty() {
1102                        f.write_str(" returns (")?;
1103                        fmt_parameter_list(returns, f)?;
1104                        f.write_char(')')?;
1105                    }
1106                }
1107                Ok(())
1108            }
1109        }
1110    }
1111}
1112
1113impl Display for pt::UserDefinedOperator {
1114    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1115        f.write_str(self.as_str())
1116    }
1117}
1118
1119impl pt::UserDefinedOperator {
1120    /// Returns the string representation of this type.
1121    pub const fn as_str(&self) -> &'static str {
1122        match self {
1123            Self::BitwiseAnd => "&",
1124            Self::BitwiseNot => "~",
1125            Self::Negate => "-",
1126            Self::BitwiseOr => "|",
1127            Self::BitwiseXor => "^",
1128            Self::Add => "+",
1129            Self::Divide => "/",
1130            Self::Modulo => "%",
1131            Self::Multiply => "*",
1132            Self::Subtract => "-",
1133            Self::Equal => "==",
1134            Self::More => ">",
1135            Self::MoreEqual => ">=",
1136            Self::Less => "<",
1137            Self::LessEqual => "<=",
1138            Self::NotEqual => "!=",
1139        }
1140    }
1141}
1142
1143impl Display for pt::UsingList {
1144    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1145        match self {
1146            Self::Library(ident) => ident.fmt(f),
1147            Self::Functions(list) => {
1148                f.write_char('{')?;
1149                write_separated(list, f, ", ")?;
1150                f.write_char('}')
1151            }
1152            Self::Error => Ok(()),
1153        }
1154    }
1155}
1156
1157impl Display for pt::VariableAttribute {
1158    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1159        match self {
1160            Self::Visibility(vis) => vis.fmt(f),
1161            Self::Constant(_) => f.write_str("constant"),
1162            Self::Immutable(_) => f.write_str("immutable"),
1163            Self::Override(_, idents) => {
1164                f.write_str("override")?;
1165                if !idents.is_empty() {
1166                    f.write_char('(')?;
1167                    write_separated(idents, f, ", ")?;
1168                    f.write_char(')')?;
1169                }
1170                Ok(())
1171            }
1172            Self::StorageType(storage) => match storage {
1173                StorageType::Instance(_) => f.write_str("instance"),
1174                StorageType::Temporary(_) => f.write_str("temporary"),
1175                StorageType::Persistent(_) => f.write_str("persistent"),
1176            },
1177        }
1178    }
1179}
1180
1181impl Display for pt::Visibility {
1182    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1183        f.write_str(self.as_str())
1184    }
1185}
1186impl pt::Visibility {
1187    /// Returns the string representation of this type.
1188    pub const fn as_str(&self) -> &'static str {
1189        match self {
1190            Self::Public(_) => "public",
1191            Self::Internal(_) => "internal",
1192            Self::Private(_) => "private",
1193            Self::External(_) => "external",
1194        }
1195    }
1196}
1197
1198impl Display for pt::YulExpression {
1199    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1200        match self {
1201            Self::BoolLiteral(_, value, ident) => {
1202                let value = if *value { "true" } else { "false" };
1203                f.write_str(value)?;
1204                write_opt!(f, ": ", ident);
1205                Ok(())
1206            }
1207            Self::NumberLiteral(_, value, exponent, ident) => {
1208                f.write_str(value)?;
1209                if !exponent.is_empty() {
1210                    f.write_char('e')?;
1211                    f.write_str(exponent)?;
1212                }
1213                write_opt!(f, ": ", ident);
1214                Ok(())
1215            }
1216            Self::HexNumberLiteral(_, value, ident) => {
1217                f.write_str(value)?;
1218                write_opt!(f, ": ", ident);
1219                Ok(())
1220            }
1221            Self::HexStringLiteral(value, ident) => {
1222                value.fmt(f)?;
1223                write_opt!(f, ": ", ident);
1224                Ok(())
1225            }
1226            Self::StringLiteral(value, ident) => {
1227                value.fmt(f)?;
1228                write_opt!(f, ": ", ident);
1229                Ok(())
1230            }
1231            Self::Variable(ident) => ident.fmt(f),
1232            Self::FunctionCall(call) => call.fmt(f),
1233            Self::SuffixAccess(_, l, r) => {
1234                l.fmt(f)?;
1235                f.write_char('.')?;
1236                r.fmt(f)
1237            }
1238        }
1239    }
1240}
1241
1242impl Display for pt::YulStatement {
1243    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1244        match self {
1245            Self::Block(inner) => inner.fmt(f),
1246            Self::FunctionDefinition(inner) => inner.fmt(f),
1247            Self::FunctionCall(inner) => inner.fmt(f),
1248            Self::For(inner) => inner.fmt(f),
1249            Self::Switch(inner) => inner.fmt(f),
1250
1251            Self::Assign(_, exprs, eq_expr) => {
1252                write_separated(exprs, f, ", ")?;
1253                f.write_str(" := ")?;
1254                eq_expr.fmt(f)
1255            }
1256            Self::VariableDeclaration(_, vars, eq_expr) => {
1257                f.write_str("let")?;
1258                if !vars.is_empty() {
1259                    f.write_char(' ')?;
1260                    write_separated(vars, f, ", ")?;
1261                }
1262                write_opt!(f, " := ", eq_expr);
1263                Ok(())
1264            }
1265
1266            Self::If(_, expr, block) => {
1267                f.write_str("if ")?;
1268                expr.fmt(f)?;
1269                f.write_char(' ')?;
1270                block.fmt(f)
1271            }
1272
1273            Self::Leave(_) => f.write_str("leave"),
1274            Self::Break(_) => f.write_str("break"),
1275            Self::Continue(_) => f.write_str("continue"),
1276
1277            Self::Error(_) => Ok(()),
1278        }
1279    }
1280}
1281
1282impl Display for pt::YulSwitchOptions {
1283    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1284        match self {
1285            Self::Case(_, expr, block) => {
1286                f.write_str("case ")?;
1287                expr.fmt(f)?;
1288                f.write_str(" ")?;
1289                block.fmt(f)
1290            }
1291            Self::Default(_, block) => {
1292                f.write_str("default ")?;
1293                block.fmt(f)
1294            }
1295        }
1296    }
1297}
1298
1299// These functions are private so they should be inlined by the compiler.
1300// We provided these `#[inline]` hints regardless because we don't expect compile time penalties
1301// or other negative impacts from them.
1302// See: <https://github.com/hyperledger-solang/solang/pull/1237#discussion_r1151557453>
1303#[inline]
1304fn fmt_parameter_list(list: &pt::ParameterList, f: &mut Formatter<'_>) -> Result {
1305    let iter = list.iter().flat_map(|(_, param)| param);
1306    write_separated_iter(iter, f, ", ")
1307}
1308
1309#[inline]
1310fn write_separated<T: Display>(slice: &[T], f: &mut Formatter<'_>, sep: &str) -> Result {
1311    write_separated_iter(slice.iter(), f, sep)
1312}
1313
1314fn write_separated_iter<T, I>(mut iter: I, f: &mut Formatter<'_>, sep: &str) -> Result
1315where
1316    I: Iterator<Item = T>,
1317    T: Display,
1318{
1319    if let Some(first) = iter.next() {
1320        first.fmt(f)?;
1321        for item in iter {
1322            f.write_str(sep)?;
1323            item.fmt(f)?;
1324        }
1325    }
1326    Ok(())
1327}
1328
1329fn rm_underscores(s: &str) -> Cow<'_, str> {
1330    if s.is_empty() {
1331        Cow::Borrowed("0")
1332    } else if s.contains('_') {
1333        let mut s = s.to_string();
1334        s.retain(|c| c != '_');
1335        Cow::Owned(s)
1336    } else {
1337        Cow::Borrowed(s)
1338    }
1339}
1340
1341#[cfg(test)]
1342mod tests {
1343    use super::*;
1344    use crate::pt::{Annotation, Loc};
1345
1346    macro_rules! struct_tests {
1347        ($(pt::$t:ident { $( $f:ident: $e:expr ),* $(,)? } => $expected:expr),* $(,)?) => {
1348            $(
1349                assert_eq_display(
1350                    pt::$t {
1351                        loc: loc!(),
1352                        $( $f: $e, )*
1353                    },
1354                    $expected,
1355                );
1356            )*
1357        };
1358    }
1359
1360    macro_rules! enum_tests {
1361        ($(
1362            $t:ty: {
1363                $($p:expr => $expected:expr,)+
1364            }
1365        )+) => {
1366            $(
1367                $(
1368                    assert_eq_display($p, $expected);
1369                )+
1370            )+
1371        };
1372    }
1373
1374    /// Expression
1375    macro_rules! expr {
1376        (this) => {
1377            pt::Expression::This(loc!())
1378        };
1379
1380        ($i:ident) => {
1381            pt::Expression::Variable(id(stringify!($i)))
1382        };
1383
1384        ($l:literal) => {
1385            pt::Expression::Variable(id(stringify!($l)))
1386        };
1387
1388        (++ $($t:tt)+) => {
1389            pt::Expression::PreIncrement(loc!(), Box::new(expr!($($t)+)))
1390        };
1391
1392        ($($t:tt)+ ++) => {
1393            pt::Expression::PostIncrement(loc!(), Box::new(expr!($($t)+)))
1394        };
1395    }
1396    macro_rules! yexpr {
1397        ($i:ident) => {
1398            pt::YulExpression::Variable(id(stringify!($i)))
1399        };
1400        ($l:literal) => {
1401            pt::YulExpression::Variable(id(stringify!($l)))
1402        };
1403    }
1404
1405    /// Type
1406    macro_rules! ty {
1407        (uint256) => {
1408            pt::Type::Uint(256)
1409        };
1410        (string) => {
1411            pt::Type::String
1412        };
1413        (bytes) => {
1414            pt::Type::DynamicBytes
1415        };
1416        (address) => {
1417            pt::Type::Address
1418        };
1419    }
1420    macro_rules! expr_ty {
1421        ($($t:tt)+) => {
1422            pt::Expression::Type(loc!(), ty!($($t)+))
1423        };
1424    }
1425
1426    /// Literals
1427    macro_rules! lit {
1428        // prefixes are not allowed in rust strings
1429        (unicode $($l:literal)+) => {
1430            pt::StringLiteral {
1431                loc: loc!(),
1432                unicode: true,
1433                string: concat!( $($l),+ ).to_string(),
1434            }
1435        };
1436
1437        (hex $($l:literal)+) => {
1438            pt::HexLiteral {
1439                loc: loc!(),
1440                hex: concat!( "hex\"", $($l),+ , "\"" ).to_string(),
1441            }
1442        };
1443
1444        ($($l:literal)+) => {
1445            pt::StringLiteral {
1446                loc: loc!(),
1447                unicode: false,
1448                string: concat!( $($l),+ ).to_string(),
1449            }
1450        };
1451    }
1452
1453    /// VersionComparsion
1454    macro_rules! version {
1455        ($($l:literal),+) => {
1456            <[_]>::into_vec(Box::new([ $( $l.into() ),+ ]))
1457        }
1458    }
1459
1460    macro_rules! plain_version {
1461        ($($l:literal),+) => {
1462            pt::VersionComparator::Plain {
1463                loc: loc!(),
1464                version: <[_]>::into_vec(Box::new([ $( $l.into() ),+ ])),
1465            }
1466        };
1467    }
1468
1469    macro_rules! op_version {
1470        ($op:expr, $($l:literal),+) => {
1471            pt::VersionComparator::Operator {
1472                loc: loc!(),
1473                op: $op,
1474                version: <[_]>::into_vec(Box::new([ $( $l.into() ),+ ])),
1475            }
1476        };
1477    }
1478
1479    macro_rules! range_version {
1480        ($from:expr, $to:expr) => {
1481            pt::VersionComparator::Range {
1482                loc: loc!(),
1483                from: $from,
1484                to: $to,
1485            }
1486        };
1487    }
1488
1489    macro_rules! or_version {
1490        ($left:expr, $right:expr) => {
1491            pt::VersionComparator::Or {
1492                loc: loc!(),
1493                left: $left.into(),
1494                right: $right.into(),
1495            }
1496        };
1497    }
1498
1499    /// Statement
1500    macro_rules! stmt {
1501        ( {} ) => {
1502            pt::Statement::Block {
1503                loc: loc!(),
1504                unchecked: false,
1505                statements: vec![],
1506            }
1507        };
1508
1509        ( unchecked { $($t:tt)* } ) => {
1510            pt::Statement::Block {
1511                loc: loc!(),
1512                unchecked: true,
1513                statements: vec![stmt!($(t)*)],
1514            }
1515        };
1516        ( { $($t:tt)* } ) => {
1517            pt::Statement::Block {
1518                loc: loc!(),
1519                unchecked: false,
1520                statements: vec![stmt!($(t)*)],
1521            }
1522        };
1523    }
1524
1525    /// IdentifierPath
1526    macro_rules! idp {
1527        ($($e:expr),* $(,)?) => {
1528            pt::IdentifierPath {
1529                loc: loc!(),
1530                identifiers: vec![$(id($e)),*],
1531            }
1532        };
1533    }
1534
1535    macro_rules! loc {
1536        () => {
1537            pt::Loc::File(0, 0, 0)
1538        };
1539    }
1540
1541    /// Param
1542    macro_rules! param {
1543        ($i:ident) => {
1544            pt::Parameter {
1545                loc: loc!(),
1546                ty: expr_ty!($i),
1547                storage: None,
1548                name: None,
1549                annotation: None,
1550            }
1551        };
1552
1553        ($i:ident $n:ident) => {
1554            pt::Parameter {
1555                loc: loc!(),
1556                ty: expr_ty!($i),
1557                storage: None,
1558                name: Some(id(stringify!($n))),
1559                annotation: None,
1560            }
1561        };
1562
1563        ($i:ident $s:ident $n:ident) => {
1564            pt::Parameter {
1565                loc: loc!(),
1566                ty: expr_ty!($i),
1567                storage: Some(storage!($s)),
1568                name: Some(id(stringify!($n))),
1569                annotation: None,
1570            }
1571        };
1572    }
1573
1574    macro_rules! storage {
1575        (memory) => {
1576            pt::StorageLocation::Memory(loc!())
1577        };
1578        (storage) => {
1579            pt::StorageLocation::Storage(loc!())
1580        };
1581        (calldata) => {
1582            pt::StorageLocation::Calldata(loc!())
1583        };
1584    }
1585
1586    /// Identifier
1587    fn id(s: &str) -> pt::Identifier {
1588        pt::Identifier {
1589            loc: loc!(),
1590            name: s.to_string(),
1591        }
1592    }
1593
1594    macro_rules! yid {
1595        ($i:ident) => {
1596            pt::YulTypedIdentifier {
1597                loc: loc!(),
1598                id: id(stringify!($i)),
1599                ty: None,
1600            }
1601        };
1602
1603        ($i:ident : $t:ident) => {
1604            pt::YulTypedIdentifier {
1605                loc: loc!(),
1606                id: id(stringify!($i)),
1607                ty: Some(id(stringify!($t))),
1608            }
1609        };
1610    }
1611
1612    fn var(s: &str) -> Box<pt::Expression> {
1613        Box::new(pt::Expression::Variable(id(s)))
1614    }
1615
1616    fn yul_block() -> pt::YulBlock {
1617        pt::YulBlock {
1618            loc: loc!(),
1619            statements: vec![],
1620        }
1621    }
1622
1623    fn assert_eq_display<T: Display + std::fmt::Debug>(item: T, expected: &str) {
1624        let ty = std::any::type_name::<T>();
1625        let actual = item.to_string();
1626        assert_eq!(actual, expected, "\"{ty}\": {item:?}");
1627        // TODO: Test parsing back into an item
1628        // let parsed = ;
1629        // assert_eq!(parsed, item, "failed to parse display back into an item: {expected}");
1630    }
1631
1632    #[test]
1633    fn display_structs_simple() {
1634        struct_tests![
1635            pt::Annotation {
1636                id: id("name"),
1637                value: Some(expr!(value)),
1638            } => "@name(value)",
1639
1640            pt::Base {
1641                name: idp!("id", "path"),
1642                args: None,
1643            } => "id.path",
1644            pt::Base {
1645                name: idp!("id", "path"),
1646                args: Some(vec![expr!(value)]),
1647            } => "id.path(value)",
1648            pt::Base {
1649                name: idp!("id", "path"),
1650                args: Some(vec![expr!(value1), expr!(value2)]),
1651            } => "id.path(value1, value2)",
1652
1653            pt::ErrorParameter {
1654                ty: expr_ty!(uint256),
1655                name: None,
1656            } => "uint256",
1657            pt::ErrorParameter {
1658                ty: expr_ty!(uint256),
1659                name: Some(id("name")),
1660            } => "uint256 name",
1661
1662            pt::EventParameter {
1663                ty: expr_ty!(uint256),
1664                indexed: false,
1665                name: None,
1666            } => "uint256",
1667            pt::EventParameter {
1668                ty: expr_ty!(uint256),
1669                indexed: true,
1670                name: None,
1671            } => "uint256 indexed",
1672            pt::EventParameter {
1673                ty: expr_ty!(uint256),
1674                indexed: false,
1675                name: Some(id("name")),
1676            } => "uint256 name",
1677            pt::EventParameter {
1678                ty: expr_ty!(uint256),
1679                indexed: true,
1680                name: Some(id("name")),
1681            } => "uint256 indexed name",
1682
1683            pt::HexLiteral {
1684                hex: "hex\"1234\"".into(),
1685            } => "hex\"1234\"",
1686            pt::HexLiteral {
1687                hex: "hex\"455318975130845\"".into(),
1688            } => "hex\"455318975130845\"",
1689
1690            pt::Identifier {
1691                name: "name".to_string(),
1692            } => "name",
1693
1694            pt::IdentifierPath {
1695                identifiers: vec![id("id")],
1696            } => "id",
1697            pt::IdentifierPath {
1698                identifiers: vec![id("id"), id("path")],
1699            } => "id.path",
1700            pt::IdentifierPath {
1701                identifiers: vec![id("long"), id("id"), id("path")],
1702            } => "long.id.path",
1703
1704            pt::NamedArgument {
1705                name: id("name"),
1706                expr: expr!(expr),
1707            } => "name: expr",
1708
1709            pt::Parameter {
1710                ty: expr_ty!(uint256),
1711                storage: None,
1712                name: None,
1713                annotation: None,
1714            } => "uint256",
1715            pt::Parameter {
1716                ty: expr_ty!(uint256),
1717                storage: None,
1718                name: Some(id("name")),
1719                annotation: None,
1720            } => "uint256 name",
1721            pt::Parameter {
1722                ty: expr_ty!(uint256),
1723                storage: Some(pt::StorageLocation::Calldata(Default::default())),
1724                name: Some(id("name")),
1725                annotation: None,
1726            } => "uint256 calldata name",
1727            pt::Parameter {
1728                ty: expr_ty!(uint256),
1729                storage: Some(pt::StorageLocation::Calldata(Default::default())),
1730                name: None,
1731                annotation: None,
1732            } => "uint256 calldata",
1733            pt::Parameter {
1734                ty: expr_ty!(bytes),
1735                storage: None,
1736                name: Some(id("my_seed")),
1737                annotation: Some(Annotation {
1738                    loc: Loc::Builtin,
1739                    id: id("name"),
1740                    value: None,
1741                }),
1742            } => "@name bytes my_seed",
1743
1744            pt::StringLiteral {
1745                unicode: false,
1746                string: "string".into(),
1747            } => "\"string\"",
1748            pt::StringLiteral {
1749                unicode: true,
1750                string: "string".into(),
1751            } => "unicode\"string\"",
1752
1753            pt::UsingFunction {
1754                path: idp!["id", "path"],
1755                oper: None,
1756            } => "id.path",
1757            pt::UsingFunction {
1758                path: idp!["id", "path"],
1759                oper: Some(pt::UserDefinedOperator::Add),
1760            } => "id.path as +",
1761
1762            pt::VariableDeclaration {
1763                ty: expr_ty!(uint256),
1764                storage: None,
1765                name: None,
1766            } => "uint256",
1767            pt::VariableDeclaration {
1768                ty: expr_ty!(uint256),
1769                storage: None,
1770                name: Some(id("name")),
1771            } => "uint256 name",
1772            pt::VariableDeclaration {
1773                ty: expr_ty!(uint256),
1774                storage: Some(pt::StorageLocation::Calldata(Default::default())),
1775                name: Some(id("name")),
1776            } => "uint256 calldata name",
1777            pt::VariableDeclaration {
1778                ty: expr_ty!(uint256),
1779                storage: Some(pt::StorageLocation::Calldata(Default::default())),
1780                name: None,
1781            } => "uint256 calldata",
1782
1783            pt::VariableDefinition {
1784                ty: expr_ty!(uint256),
1785                attrs: vec![],
1786                name: None,
1787                initializer: None,
1788            } => "uint256;",
1789            pt::VariableDefinition {
1790                ty: expr_ty!(uint256),
1791                attrs: vec![],
1792                name: Some(id("name")),
1793                initializer: None,
1794            } => "uint256 name;",
1795            pt::VariableDefinition {
1796                ty: expr_ty!(uint256),
1797                attrs: vec![],
1798                name: Some(id("name")),
1799                initializer: Some(expr!(value)),
1800            } => "uint256 name = value;",
1801            pt::VariableDefinition {
1802                ty: expr_ty!(uint256),
1803                attrs: vec![pt::VariableAttribute::Constant(loc!())],
1804                name: Some(id("name")),
1805                initializer: Some(expr!(value)),
1806            } => "uint256 constant name = value;",
1807            pt::VariableDefinition {
1808                ty: expr_ty!(uint256),
1809                attrs: vec![
1810                    pt::VariableAttribute::Visibility(pt::Visibility::Public(None)),
1811                    pt::VariableAttribute::Constant(loc!())
1812                ],
1813                name: Some(id("name")),
1814                initializer: Some(expr!(value)),
1815            } => "uint256 public constant name = value;",
1816
1817            pt::YulTypedIdentifier {
1818                id: id("name"),
1819                ty: None,
1820            } => "name",
1821            pt::YulTypedIdentifier {
1822                id: id("name"),
1823                ty: Some(id("uint256")),
1824            } => "name: uint256",
1825        ];
1826    }
1827
1828    #[test]
1829    fn display_structs_complex() {
1830        struct_tests![
1831            pt::ContractDefinition {
1832                ty: pt::ContractTy::Contract(loc!()),
1833                name: Some(id("name")),
1834                base: vec![],
1835                parts: vec![],
1836            } => "contract name {}",
1837            pt::ContractDefinition {
1838                ty: pt::ContractTy::Contract(loc!()),
1839                name: Some(id("name")),
1840                base: vec![pt::Base {
1841                    loc: loc!(),
1842                    name: idp!("base"),
1843                    args: None
1844                }],
1845                parts: vec![],
1846            } => "contract name base {}",
1847            pt::ContractDefinition {
1848                ty: pt::ContractTy::Contract(loc!()),
1849                name: Some(id("name")),
1850                base: vec![pt::Base {
1851                    loc: loc!(),
1852                    name: idp!("base"),
1853                    args: Some(vec![])
1854                }],
1855                parts: vec![],
1856            } => "contract name base() {}",
1857            pt::ContractDefinition {
1858                ty: pt::ContractTy::Contract(loc!()),
1859                name: Some(id("name")),
1860                base: vec![pt::Base {
1861                    loc: loc!(),
1862                    name: idp!("base"),
1863                    args: Some(vec![expr!(expr)])
1864                }],
1865                parts: vec![],
1866            } => "contract name base(expr) {}",
1867            pt::ContractDefinition {
1868                ty: pt::ContractTy::Contract(loc!()),
1869                name: Some(id("name")),
1870                base: vec![
1871                    pt::Base {
1872                        loc: loc!(),
1873                        name: idp!("base1"),
1874                        args: None
1875                    },
1876                    pt::Base {
1877                        loc: loc!(),
1878                        name: idp!("base2"),
1879                        args: None
1880                    },
1881                ],
1882                parts: vec![],
1883            } => "contract name base1 base2 {}",
1884
1885            pt::EnumDefinition {
1886                name: Some(id("name")),
1887                values: vec![]
1888            } => "enum name {}",
1889            pt::EnumDefinition {
1890                name: Some(id("name")),
1891                values: vec![Some(id("variant"))]
1892            } => "enum name {variant}",
1893            pt::EnumDefinition {
1894                name: Some(id("name")),
1895                values: vec![
1896                    Some(id("variant1")),
1897                    Some(id("variant2")),
1898                ]
1899            } => "enum name {variant1, variant2}",
1900
1901            pt::ErrorDefinition {
1902                keyword: expr!(error),
1903                name: Some(id("name")),
1904                fields: vec![],
1905            } => "error name();",
1906            pt::ErrorDefinition {
1907                keyword: expr!(error),
1908                name: Some(id("name")),
1909                fields: vec![pt::ErrorParameter {
1910                    loc: loc!(),
1911                    ty: expr_ty!(uint256),
1912                    name: None,
1913                }],
1914            } => "error name(uint256);",
1915
1916            pt::EventDefinition {
1917                name: Some(id("name")),
1918                fields: vec![],
1919                anonymous: false,
1920            } => "event name();",
1921            pt::EventDefinition {
1922                name: Some(id("name")),
1923                fields: vec![pt::EventParameter {
1924                    loc: loc!(),
1925                    ty: expr_ty!(uint256),
1926                    indexed: false,
1927                    name: None,
1928                }],
1929                anonymous: false,
1930            } => "event name(uint256);",
1931            pt::EventDefinition {
1932                name: Some(id("name")),
1933                fields: vec![pt::EventParameter {
1934                    loc: loc!(),
1935                    ty: expr_ty!(uint256),
1936                    indexed: true,
1937                    name: None,
1938                }],
1939                anonymous: false,
1940            } => "event name(uint256 indexed);",
1941            pt::EventDefinition {
1942                name: Some(id("name")),
1943                fields: vec![],
1944                anonymous: true,
1945            } => "event name() anonymous;",
1946
1947            pt::FunctionDefinition {
1948                loc_prototype: loc!(),
1949                ty: pt::FunctionTy::Function,
1950                name: Some(id("name")),
1951                name_loc: loc!(),
1952                params: vec![],
1953                attributes: vec![],
1954                return_not_returns: None,
1955                returns: vec![],
1956                body: None,
1957            } => "function name();",
1958            pt::FunctionDefinition {
1959                loc_prototype: loc!(),
1960                ty: pt::FunctionTy::Function,
1961                name: Some(id("name")),
1962                name_loc: loc!(),
1963                params: vec![],
1964                attributes: vec![],
1965                return_not_returns: None,
1966                returns: vec![],
1967                body: Some(stmt!({})),
1968            } => "function name() {}",
1969            pt::FunctionDefinition {
1970                loc_prototype: loc!(),
1971                ty: pt::FunctionTy::Function,
1972                name: Some(id("name")),
1973                name_loc: loc!(),
1974                params: vec![],
1975                attributes: vec![],
1976                return_not_returns: None,
1977                returns: vec![(loc!(), Some(param!(uint256)))],
1978                body: Some(stmt!({})),
1979            } => "function name() returns (uint256) {}",
1980            pt::FunctionDefinition {
1981                loc_prototype: loc!(),
1982                ty: pt::FunctionTy::Function,
1983                name: Some(id("name")),
1984                name_loc: loc!(),
1985                params: vec![],
1986                attributes: vec![pt::FunctionAttribute::Virtual(loc!())],
1987                return_not_returns: None,
1988                returns: vec![(loc!(), Some(param!(uint256)))],
1989                body: Some(stmt!({})),
1990            } => "function name() virtual returns (uint256) {}",
1991
1992            pt::StructDefinition {
1993                name: Some(id("name")),
1994                fields: vec![],
1995            } => "struct name {}",
1996            pt::StructDefinition {
1997                name: Some(id("name")),
1998                fields: vec![pt::VariableDeclaration {
1999                    loc: loc!(),
2000                    ty: expr_ty!(uint256),
2001                    storage: None,
2002                    name: Some(id("a")),
2003                }],
2004            } => "struct name {uint256 a;}",
2005            pt::StructDefinition {
2006                name: Some(id("name")),
2007                fields: vec![
2008                    pt::VariableDeclaration {
2009                        loc: loc!(),
2010                        ty: expr_ty!(uint256),
2011                        storage: None,
2012                        name: Some(id("a")),
2013                    },
2014                    pt::VariableDeclaration {
2015                        loc: loc!(),
2016                        ty: expr_ty!(uint256),
2017                        storage: None,
2018                        name: Some(id("b")),
2019                    }
2020                ],
2021            } => "struct name {uint256 a; uint256 b;}",
2022
2023            pt::TypeDefinition {
2024                name: id("MyType"),
2025                ty: expr_ty!(uint256),
2026            } => "type MyType is uint256;",
2027
2028            pt::Using {
2029                list: pt::UsingList::Library(idp!["id", "path"]),
2030                ty: None,
2031                global: None,
2032            } => "using id.path for *;",
2033            pt::Using {
2034                list: pt::UsingList::Library(idp!["id", "path"]),
2035                ty: Some(expr_ty!(uint256)),
2036                global: None,
2037            } => "using id.path for uint256;",
2038            pt::Using {
2039                list: pt::UsingList::Library(idp!["id", "path"]),
2040                ty: Some(expr_ty!(uint256)),
2041                global: Some(id("global")),
2042            } => "using id.path for uint256 global;",
2043            pt::Using {
2044                list: pt::UsingList::Functions(vec![]),
2045                ty: None,
2046                global: None,
2047            } => "using {} for *;",
2048            pt::Using {
2049                list: pt::UsingList::Functions(vec![
2050                    pt::UsingFunction {
2051                        loc: loc!(),
2052                        path: idp!("id", "path"),
2053                        oper: None,
2054                    }
2055                ]),
2056                ty: None,
2057                global: None,
2058            } => "using {id.path} for *;",
2059            pt::Using {
2060                list: pt::UsingList::Functions(vec![
2061                    pt::UsingFunction {
2062                        loc: loc!(),
2063                        path: idp!("id", "path"),
2064                        oper: Some(pt::UserDefinedOperator::Add),
2065                    }
2066                ]),
2067                ty: Some(expr_ty!(uint256)),
2068                global: None,
2069            } => "using {id.path as +} for uint256;",
2070            pt::Using {
2071                list: pt::UsingList::Functions(vec![
2072                    pt::UsingFunction {
2073                        loc: loc!(),
2074                        path: idp!("id", "path1"),
2075                        oper: None,
2076                    },
2077                    pt::UsingFunction {
2078                        loc: loc!(),
2079                        path: idp!("id", "path2"),
2080                        oper: None,
2081                    }
2082                ]),
2083                ty: Some(expr_ty!(uint256)),
2084                global: Some(id("global")),
2085            } => "using {id.path1, id.path2} for uint256 global;",
2086
2087            pt::YulBlock {
2088                statements: vec![]
2089            } => "{}",
2090
2091            pt::YulFor {
2092                init_block: yul_block(),
2093                condition: yexpr!(cond),
2094                post_block: yul_block(),
2095                execution_block: yul_block(),
2096            } => "for {} cond {} {}",
2097
2098            pt::YulFunctionCall {
2099                id: id("name"),
2100                arguments: vec![],
2101            } => "name()",
2102            pt::YulFunctionCall {
2103                id: id("name"),
2104                arguments: vec![yexpr!(arg)],
2105            } => "name(arg)",
2106            pt::YulFunctionCall {
2107                id: id("name"),
2108                arguments: vec![yexpr!(arg1), yexpr!(arg2)],
2109            } => "name(arg1, arg2)",
2110
2111            pt::YulFunctionDefinition {
2112                id: id("name"),
2113                params: vec![],
2114                returns: vec![],
2115                body: yul_block(),
2116            } => "function name() {}",
2117            pt::YulFunctionDefinition {
2118                id: id("name"),
2119                params: vec![yid!(param1: a), yid!(param2: b)],
2120                returns: vec![],
2121                body: yul_block(),
2122            } => "function name(param1: a, param2: b) {}",
2123            pt::YulFunctionDefinition {
2124                id: id("name"),
2125                params: vec![yid!(param1: a), yid!(param2: b)],
2126                returns: vec![yid!(ret1: c), yid!(ret2: d)],
2127                body: yul_block(),
2128            } => "function name(param1: a, param2: b) -> (ret1: c, ret2: d) {}",
2129
2130            pt::YulSwitch {
2131                condition: yexpr!(cond),
2132                cases: vec![pt::YulSwitchOptions::Case(loc!(), yexpr!(expr), yul_block())],
2133                default: None,
2134            } => "switch cond case expr {}",
2135            pt::YulSwitch {
2136                condition: yexpr!(cond),
2137                cases: vec![
2138                    pt::YulSwitchOptions::Case(loc!(), yexpr!(0), yul_block()),
2139                    pt::YulSwitchOptions::Case(loc!(), yexpr!(1), yul_block()),
2140                ],
2141                default: None,
2142            } => "switch cond case 0 {} case 1 {}",
2143            pt::YulSwitch {
2144                condition: yexpr!(cond),
2145                cases: vec![pt::YulSwitchOptions::Case(loc!(), yexpr!(0), yul_block())],
2146                default: Some(pt::YulSwitchOptions::Default(loc!(), yul_block())),
2147            } => "switch cond case 0 {} default {}",
2148        ];
2149    }
2150
2151    #[test]
2152    fn display_enums() {
2153        enum_tests![
2154            // https://docs.soliditylang.org/en/latest/control-structures.html#try-catch
2155            pt::CatchClause: {
2156                pt::CatchClause::Named(loc!(), id("Error"), param!(string memory reason), stmt!({}))
2157                    => "catch Error(string memory reason) {}",
2158                pt::CatchClause::Named(loc!(), id("Panic"), param!(uint256 errorCode), stmt!({}))
2159                    => "catch Panic(uint256 errorCode) {}",
2160
2161                pt::CatchClause::Simple(loc!(), None, stmt!({})) => "catch {}",
2162                pt::CatchClause::Simple(loc!(), Some(param!(uint256)), stmt!({}))
2163                    => "catch (uint256) {}",
2164                pt::CatchClause::Simple(loc!(), Some(param!(bytes memory data)), stmt!({}))
2165                    => "catch (bytes memory data) {}",
2166            }
2167
2168            pt::Comment: {
2169                pt::Comment::Line(loc!(), "// line".into()) => "// line",
2170                pt::Comment::Block(loc!(), "/* \nblock\n*/".into()) => "/* \nblock\n*/",
2171                pt::Comment::DocLine(loc!(), "/// doc line".into()) => "/// doc line",
2172                pt::Comment::DocBlock(loc!(), "/**\n * doc block\n */".into()) => "/**\n * doc block\n */",
2173            }
2174
2175            // tested individually
2176            pt::ContractPart: {
2177                pt::ContractPart::StraySemicolon(loc!()) => ";",
2178            }
2179
2180            pt::ContractTy: {
2181                pt::ContractTy::Abstract(loc!()) => "abstract contract",
2182                pt::ContractTy::Contract(loc!()) => "contract",
2183                pt::ContractTy::Interface(loc!()) => "interface",
2184                pt::ContractTy::Library(loc!()) => "library",
2185            }
2186
2187            pt::Expression: {
2188                pt::Expression::New(loc!(), Box::new(expr_ty!(uint256))) => "new uint256",
2189                pt::Expression::Delete(loc!(), Box::new(expr_ty!(uint256))) => "delete uint256",
2190
2191                pt::Expression::Type(loc!(), ty!(uint256)) => "uint256",
2192                pt::Expression::Variable(id("myVar")) => "myVar",
2193
2194                pt::Expression::ArrayLiteral(loc!(), vec![expr!(1), expr!(2)]) => "[1, 2]",
2195
2196                pt::Expression::ArraySubscript(loc!(), Box::new(expr!(arr)), None) => "arr[]",
2197                pt::Expression::ArraySubscript(loc!(), Box::new(expr!(arr)), Some(Box::new(expr!(0)))) => "arr[0]",
2198                pt::Expression::ArraySlice(loc!(), Box::new(expr!(arr)), None, None) => "arr[:]",
2199                pt::Expression::ArraySlice(loc!(), Box::new(expr!(arr)), Some(Box::new(expr!(left))), None)
2200                    => "arr[left:]",
2201                pt::Expression::ArraySlice(loc!(), Box::new(expr!(arr)), None, Some(Box::new(expr!(right))))
2202                    => "arr[:right]",
2203                pt::Expression::ArraySlice(loc!(), Box::new(expr!(arr)), Some(Box::new(expr!(left))), Some(Box::new(expr!(right))))
2204                    => "arr[left:right]",
2205
2206                pt::Expression::MemberAccess(loc!(), Box::new(expr!(struct)), id("access")) => "struct.access",
2207
2208                pt::Expression::Parenthesis(loc!(), Box::new(expr!(var))) => "(var)",
2209                pt::Expression::List(loc!(), vec![]) => "()",
2210                pt::Expression::List(loc!(), vec![(loc!(), Some(param!(address)))])
2211                    => "(address)",
2212                pt::Expression::List(loc!(), vec![(loc!(), Some(param!(address))), (loc!(), Some(param!(uint256)))])
2213                    => "(address, uint256)",
2214
2215                pt::Expression::AddressLiteral(loc!(), "0x1234".into()) => "0x1234",
2216                pt::Expression::StringLiteral(vec![lit!(unicode "¹²³")]) => "unicode\"¹²³\"",
2217                pt::Expression::HexLiteral(vec![lit!(hex "00112233")]) => "hex\"00112233\"",
2218                pt::Expression::BoolLiteral(loc!(), true) => "true",
2219                pt::Expression::BoolLiteral(loc!(), false) => "false",
2220
2221                pt::Expression::HexNumberLiteral(loc!(), "0x1234".into(), None) => "0x1234",
2222                pt::Expression::HexNumberLiteral(loc!(), "0x1234".into(), Some(id("gwei"))) => "0x1234 gwei",
2223                pt::Expression::NumberLiteral(loc!(), "_123_4_".into(), "".into(), None)
2224                    => "1234",
2225                pt::Expression::NumberLiteral(loc!(), "_1_234_".into(), "_2".into(), None)
2226                    => "1234e2",
2227                pt::Expression::NumberLiteral(loc!(), "_1_23_4".into(), "".into(), Some(id("gwei")))
2228                    => "1234 gwei",
2229                pt::Expression::NumberLiteral(loc!(), "1_23_4_".into(), "2_".into(), Some(id("gwei")))
2230                    => "1234e2 gwei",
2231                pt::Expression::RationalNumberLiteral(loc!(), "1_23_4_".into(), "".into(), "".into(), None)
2232                    => "1234.0",
2233                pt::Expression::RationalNumberLiteral(loc!(), "_1_23_4".into(), "0".into(), "_2".into(), None)
2234                    => "1234.0e2",
2235                pt::Expression::RationalNumberLiteral(loc!(), "_1_234_".into(), "09".into(), "".into(), Some(id("gwei")))
2236                    => "1234.09 gwei",
2237                pt::Expression::RationalNumberLiteral(loc!(), "_123_4_".into(), "90".into(), "2_".into(), Some(id("gwei")))
2238                    => "1234.9e2 gwei",
2239
2240                pt::Expression::FunctionCall(loc!(), Box::new(expr!(func)), vec![]) => "func()",
2241                pt::Expression::FunctionCall(loc!(), Box::new(expr!(func)), vec![expr!(arg)])
2242                    => "func(arg)",
2243                pt::Expression::FunctionCall(loc!(), Box::new(expr!(func)), vec![expr!(arg1), expr!(arg2)])
2244                    => "func(arg1, arg2)",
2245                pt::Expression::FunctionCallBlock(loc!(), Box::new(expr!(func)), Box::new(stmt!({})))
2246                    => "func{}",
2247                pt::Expression::NamedFunctionCall(loc!(), Box::new(expr!(func)), vec![])
2248                    => "func({})",
2249                pt::Expression::NamedFunctionCall(loc!(), Box::new(expr!(func)), vec![pt::NamedArgument {
2250                    loc: loc!(),
2251                    name: id("arg"),
2252                    expr: expr!(value),
2253                }]) => "func({arg: value})",
2254                pt::Expression::NamedFunctionCall(loc!(), Box::new(expr!(func)), vec![
2255                    pt::NamedArgument {
2256                        loc: loc!(),
2257                        name: id("arg1"),
2258                        expr: expr!(value1),
2259                    },
2260                    pt::NamedArgument {
2261                        loc: loc!(),
2262                        name: id("arg2"),
2263                        expr: expr!(value2),
2264                    }
2265                ]) => "func({arg1: value1, arg2: value2})",
2266
2267                pt::Expression::PreIncrement(loc!(), var("a")) => "++a",
2268                pt::Expression::PostIncrement(loc!(), var("a")) => "a++",
2269                pt::Expression::PreDecrement(loc!(), var("a")) => "--a",
2270                pt::Expression::PostDecrement(loc!(), var("a")) => "a--",
2271                pt::Expression::Not(loc!(), var("a")) => "!a",
2272                pt::Expression::BitwiseNot(loc!(), var("a")) => "~a",
2273                pt::Expression::UnaryPlus(loc!(), var("a")) => "+a",
2274                pt::Expression::Negate(loc!(), var("a")) => "-a",
2275
2276                pt::Expression::Add(loc!(), var("a"), var("b")) => "a + b",
2277                pt::Expression::Subtract(loc!(), var("a"), var("b")) => "a - b",
2278                pt::Expression::Power(loc!(), var("a"), var("b")) => "a ** b",
2279                pt::Expression::Multiply(loc!(), var("a"), var("b")) => "a * b",
2280                pt::Expression::Divide(loc!(), var("a"), var("b")) => "a / b",
2281                pt::Expression::Modulo(loc!(), var("a"), var("b")) => "a % b",
2282                pt::Expression::ShiftLeft(loc!(), var("a"), var("b")) => "a << b",
2283                pt::Expression::ShiftRight(loc!(), var("a"), var("b")) => "a >> b",
2284                pt::Expression::BitwiseAnd(loc!(), var("a"), var("b")) => "a & b",
2285                pt::Expression::BitwiseXor(loc!(), var("a"), var("b")) => "a ^ b",
2286                pt::Expression::BitwiseOr(loc!(), var("a"), var("b")) => "a | b",
2287                pt::Expression::Less(loc!(), var("a"), var("b")) => "a < b",
2288                pt::Expression::More(loc!(), var("a"), var("b")) => "a > b",
2289                pt::Expression::LessEqual(loc!(), var("a"), var("b")) => "a <= b",
2290                pt::Expression::MoreEqual(loc!(), var("a"), var("b")) => "a >= b",
2291                pt::Expression::And(loc!(), var("a"), var("b")) => "a && b",
2292                pt::Expression::Or(loc!(), var("a"), var("b")) => "a || b",
2293                pt::Expression::Equal(loc!(), var("a"), var("b")) => "a == b",
2294                pt::Expression::NotEqual(loc!(), var("a"), var("b")) => "a != b",
2295
2296                pt::Expression::Assign(loc!(), var("a"), var("b")) => "a = b",
2297                pt::Expression::AssignOr(loc!(), var("a"), var("b")) => "a |= b",
2298                pt::Expression::AssignAnd(loc!(), var("a"), var("b")) => "a &= b",
2299                pt::Expression::AssignXor(loc!(), var("a"), var("b")) => "a ^= b",
2300                pt::Expression::AssignShiftLeft(loc!(), var("a"), var("b")) => "a <<= b",
2301                pt::Expression::AssignShiftRight(loc!(), var("a"), var("b")) => "a >>= b",
2302                pt::Expression::AssignAdd(loc!(), var("a"), var("b")) => "a += b",
2303                pt::Expression::AssignSubtract(loc!(), var("a"), var("b")) => "a -= b",
2304                pt::Expression::AssignMultiply(loc!(), var("a"), var("b")) => "a *= b",
2305                pt::Expression::AssignDivide(loc!(), var("a"), var("b")) => "a /= b",
2306                pt::Expression::AssignModulo(loc!(), var("a"), var("b")) => "a %= b",
2307            }
2308
2309            pt::FunctionAttribute: {
2310                pt::FunctionAttribute::Virtual(loc!()) => "virtual",
2311                pt::FunctionAttribute::Immutable(loc!()) => "immutable",
2312
2313                pt::FunctionAttribute::Override(loc!(), vec![]) => "override",
2314                pt::FunctionAttribute::Override(loc!(), vec![idp!["a", "b"]]) => "override(a.b)",
2315                pt::FunctionAttribute::Override(loc!(), vec![idp!["a", "b"], idp!["c", "d"]])
2316                    => "override(a.b, c.d)",
2317            }
2318
2319            pt::FunctionTy: {
2320                pt::FunctionTy::Constructor => "constructor",
2321                pt::FunctionTy::Function => "function",
2322                pt::FunctionTy::Fallback => "fallback",
2323                pt::FunctionTy::Receive => "receive",
2324                pt::FunctionTy::Modifier => "modifier",
2325            }
2326
2327            pt::Import: {
2328                pt::Import::Plain(pt::ImportPath::Filename(lit!("path/to/import")), loc!()) => "import \"path/to/import\";",
2329
2330                pt::Import::GlobalSymbol(pt::ImportPath::Filename(lit!("path-to-import")), id("ImportedContract"), loc!())
2331                    => "import \"path-to-import\" as ImportedContract;",
2332
2333                pt::Import::Rename(pt::ImportPath::Filename(lit!("import\\to\\path")), vec![], loc!())
2334                    => "import {} from \"import\\to\\path\";",
2335                pt::Import::Rename(pt::ImportPath::Filename(lit!("import\\to\\path")), vec![(id("A"), None), (id("B"), Some(id("C")))], loc!())
2336                    => "import {A, B as C} from \"import\\to\\path\";",
2337
2338                pt::Import::Plain(pt::ImportPath::Path(idp!("std", "stub")), loc!()) => "import std.stub;",
2339
2340                pt::Import::GlobalSymbol(pt::ImportPath::Path(idp!("a", "b", "c")), id("ImportedContract"), loc!())
2341                    => "import a.b.c as ImportedContract;",
2342
2343                pt::Import::Rename(pt::ImportPath::Path(idp!("std", "stub")), vec![], loc!())
2344                    => "import {} from std.stub;",
2345                pt::Import::Rename(pt::ImportPath::Path(idp!("std", "stub")), vec![(id("A"), None), (id("B"), Some(id("C")))], loc!())
2346                    => "import {A, B as C} from std.stub;",
2347            }
2348
2349            pt::Mutability: {
2350                pt::Mutability::Pure(loc!()) => "pure",
2351                pt::Mutability::View(loc!()) => "view",
2352                pt::Mutability::Constant(loc!()) => "view",
2353                pt::Mutability::Payable(loc!()) => "payable",
2354            }
2355
2356            pt::SourceUnitPart: {
2357                // rest tested individually
2358
2359                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Identifier(loc!(), None, None).into()) => "pragma;",
2360                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Identifier(loc!(), Some(id("solidity")), None).into())
2361                    => "pragma solidity;",
2362                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::StringLiteral(loc!(), id("abi"), lit!("v2")).into())
2363                    => "pragma abi \"v2\";",
2364                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Version(loc!(), id("solidity"), vec![plain_version!("0", "8", "0")]).into())
2365                    => "pragma solidity 0.8.0;",
2366                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Version(loc!(), id("solidity"), vec![
2367                    op_version!(pt::VersionOp::Exact, "0", "5", "16"),
2368                    op_version!(pt::VersionOp::GreaterEq, "0", "5"),
2369                    op_version!(pt::VersionOp::Greater, "0"),
2370                    op_version!(pt::VersionOp::Less, "1"),
2371                    op_version!(pt::VersionOp::LessEq, "1"),
2372                    op_version!(pt::VersionOp::Caret, "0", "5", "16"),
2373                    op_version!(pt::VersionOp::Wildcard, "5", "5")]
2374                ).into())
2375                    => "pragma solidity =0.5.16 >=0.5 >0 <1 <=1 ^0.5.16 *5.5;",
2376                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Version(loc!(), id("solidity"), vec![or_version!(plain_version!("0"), op_version!(pt::VersionOp::Caret, "1", "0"))]).into())
2377                    => "pragma solidity 0 || ^1.0;",
2378                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Version(loc!(), id("solidity"), vec![range_version!(version!["0"], version!["1", "0"])]).into())
2379                    => "pragma solidity 0 - 1.0;",
2380                pt::SourceUnitPart::StraySemicolon(loc!()) => ";",
2381            }
2382
2383            pt::Statement: {
2384                pt::Statement::Assembly {
2385                    loc: loc!(),
2386                    dialect: None,
2387                    flags: None,
2388                    block: yul_block(),
2389                } => "assembly {}",
2390                pt::Statement::Assembly {
2391                    loc: loc!(),
2392                    dialect: None,
2393                    flags: Some(vec![lit!("memory-safe")]),
2394                    block: yul_block(),
2395                } => "assembly (\"memory-safe\") {}",
2396                pt::Statement::Assembly {
2397                    loc: loc!(),
2398                    dialect: None,
2399                    flags: Some(vec![lit!("memory-safe"), lit!("second-flag")]),
2400                    block: yul_block(),
2401                } => "assembly (\"memory-safe\", \"second-flag\") {}",
2402
2403                pt::Statement::Args(loc!(), vec![]) => "{}",
2404                pt::Statement::Args(loc!(), vec![
2405                    pt::NamedArgument {
2406                        loc: loc!(),
2407                        name: id("name"),
2408                        expr: expr!(value),
2409                    },
2410                ]) => "{name: value}",
2411                pt::Statement::Args(loc!(), vec![
2412                    pt::NamedArgument {
2413                        loc: loc!(),
2414                        name: id("name1"),
2415                        expr: expr!(value1),
2416                    },
2417                    pt::NamedArgument {
2418                        loc: loc!(),
2419                        name: id("name2"),
2420                        expr: expr!(value2),
2421                    },
2422                ]) => "{name1: value1, name2: value2}",
2423
2424                pt::Statement::If(loc!(), expr!(true), Box::new(stmt!({})), None) => "if (true) {}",
2425                pt::Statement::If(loc!(), expr!(true), Box::new(stmt!({})), Some(Box::new(stmt!({}))))
2426                    => "if (true) {} else {}",
2427
2428                pt::Statement::While(loc!(), expr!(true), Box::new(stmt!({}))) => "while (true) {}",
2429
2430                pt::Statement::Expression(loc!(), expr!(true)) => "true;",
2431
2432                pt::Statement::VariableDefinition(loc!(), pt::VariableDeclaration {
2433                    loc: loc!(),
2434                    ty: expr_ty!(uint256),
2435                    storage: None,
2436                    name: Some(id("a")),
2437                }, None) => "uint256 a;",
2438                pt::Statement::VariableDefinition(loc!(), pt::VariableDeclaration {
2439                    loc: loc!(),
2440                    ty: expr_ty!(uint256),
2441                    storage: None,
2442                    name: Some(id("a")),
2443                }, Some(expr!(0))) => "uint256 a = 0;",
2444
2445                pt::Statement::For(loc!(), None, None, None, Some(Box::new(stmt!({}))))
2446                    => "for (;;) {}",
2447                pt::Statement::For(loc!(), Some(Box::new(pt::Statement::VariableDefinition(
2448                    loc!(),
2449                    pt::VariableDeclaration {
2450                        loc: loc!(),
2451                        ty: expr_ty!(uint256),
2452                        storage: None,
2453                        name: Some(id("a")),
2454                    },
2455                    None
2456                ))), None, None, Some(Box::new(stmt!({}))))
2457                    => "for (uint256 a;;) {}",
2458                pt::Statement::For(loc!(), None, Some(Box::new(expr!(true))), None, Some(Box::new(stmt!({}))))
2459                    => "for (; true;) {}",
2460                pt::Statement::For(
2461                    loc!(),
2462                    None,
2463                    Some(Box::new(expr!(true))),
2464                    Some(Box::new(expr!(++i))),
2465                    Some(Box::new(stmt!({})))
2466                ) => "for (; true; ++i) {}",
2467
2468                pt::Statement::DoWhile(loc!(), Box::new(stmt!({})), expr!(true))
2469                    => "do {} while (true);",
2470
2471                pt::Statement::Continue(loc!()) => "continue;",
2472                pt::Statement::Break(loc!()) => "break;",
2473
2474                pt::Statement::Return(loc!(), None) => "return;",
2475                pt::Statement::Return(loc!(), Some(expr!(true))) => "return true;",
2476
2477                pt::Statement::Revert(loc!(), None, vec![]) => "revert();",
2478                pt::Statement::Revert(loc!(), None, vec![expr!("error")])
2479                    => "revert(\"error\");",
2480                pt::Statement::Revert(loc!(), Some(idp!("my", "error")), vec![expr!("error")])
2481                    => "revert my.error(\"error\");",
2482
2483                pt::Statement::RevertNamedArgs(loc!(), None, vec![]) => "revert();",
2484                pt::Statement::RevertNamedArgs(loc!(), None, vec![pt::NamedArgument {
2485                    loc: loc!(),
2486                    name: id("name"),
2487                    expr: expr!(value),
2488                }]) => "revert({name: value});",
2489                pt::Statement::RevertNamedArgs(loc!(), Some(idp!("my", "error")), vec![pt::NamedArgument {
2490                    loc: loc!(),
2491                    name: id("name"),
2492                    expr: expr!(value),
2493                }]) => "revert my.error({name: value});",
2494
2495                pt::Statement::Emit(loc!(), expr!(true)) => "emit true;",
2496
2497                pt::Statement::Try(loc!(), expr!(true), None, vec![]) => "try true",
2498                pt::Statement::Try(loc!(), expr!(true), None, vec![pt::CatchClause::Simple(loc!(), None, stmt!({}))])
2499                    => "try true catch {}",
2500                pt::Statement::Try(loc!(), expr!(true), Some((vec![], Box::new(stmt!({})))), vec![])
2501                    => "try true returns () {}",
2502                pt::Statement::Try(
2503                    loc!(),
2504                    expr!(true),
2505                    Some((vec![], Box::new(stmt!({})))),
2506                    vec![pt::CatchClause::Simple(loc!(), None, stmt!({}))]
2507                ) => "try true returns () {} catch {}",
2508                pt::Statement::Try(
2509                    loc!(),
2510                    expr!(true),
2511                    Some((vec![(loc!(), Some(param!(uint256 a)))], Box::new(stmt!({})))),
2512                    vec![pt::CatchClause::Simple(loc!(), None, stmt!({}))]
2513                ) => "try true returns (uint256 a) {} catch {}",
2514            }
2515
2516            pt::StorageLocation: {
2517                pt::StorageLocation::Memory(loc!()) => "memory",
2518                pt::StorageLocation::Storage(loc!()) => "storage",
2519                pt::StorageLocation::Calldata(loc!()) => "calldata",
2520            }
2521
2522            pt::Type: {
2523                pt::Type::Address => "address",
2524                pt::Type::AddressPayable => "address payable",
2525                pt::Type::Payable => "payable",
2526                pt::Type::Bool => "bool",
2527                pt::Type::String => "string",
2528                pt::Type::Int(256) => "int256",
2529                pt::Type::Uint(256) => "uint256",
2530                pt::Type::Bytes(32) => "bytes32",
2531                pt::Type::Rational => "fixed",
2532                pt::Type::DynamicBytes => "bytes",
2533
2534                pt::Type::Mapping {
2535                    loc: loc!(),
2536                    key: Box::new(expr_ty!(uint256)),
2537                    key_name: None,
2538                    value: Box::new(expr_ty!(uint256)),
2539                    value_name: None,
2540                } => "mapping(uint256 => uint256)",
2541                pt::Type::Mapping {
2542                    loc: loc!(),
2543                    key: Box::new(expr_ty!(uint256)),
2544                    key_name: Some(id("key")),
2545                    value: Box::new(expr_ty!(uint256)),
2546                    value_name: None,
2547                } => "mapping(uint256 key => uint256)",
2548                pt::Type::Mapping {
2549                    loc: loc!(),
2550                    key: Box::new(expr_ty!(uint256)),
2551                    key_name: Some(id("key")),
2552                    value: Box::new(expr_ty!(uint256)),
2553                    value_name: Some(id("value")),
2554                } => "mapping(uint256 key => uint256 value)",
2555
2556                pt::Type::Function {
2557                    params: vec![],
2558                    attributes: vec![],
2559                    returns: None
2560                } => "function ()",
2561                pt::Type::Function {
2562                    params: vec![(loc!(), Some(param!(uint256)))],
2563                    attributes: vec![],
2564                    returns: None
2565                } => "function (uint256)",
2566                pt::Type::Function {
2567                    params: vec![(loc!(), Some(param!(uint256))), (loc!(), Some(param!(address)))],
2568                    attributes: vec![],
2569                    returns: None
2570                } => "function (uint256, address)",
2571                pt::Type::Function {
2572                    params: vec![(loc!(), Some(param!(uint256)))],
2573                    attributes: vec![pt::FunctionAttribute::Virtual(loc!())],
2574                    returns: None
2575                } => "function (uint256) virtual",
2576                pt::Type::Function {
2577                    params: vec![(loc!(), Some(param!(uint256)))],
2578                    attributes: vec![pt::FunctionAttribute::Virtual(loc!()), pt::FunctionAttribute::Override(loc!(), vec![])],
2579                    returns: None
2580                } => "function (uint256) virtual override",
2581                pt::Type::Function {
2582                    params: vec![(loc!(), Some(param!(uint256)))],
2583                    attributes: vec![pt::FunctionAttribute::Virtual(loc!()), pt::FunctionAttribute::Override(loc!(), vec![idp!["a", "b"]])],
2584                    returns: None
2585                } => "function (uint256) virtual override(a.b)",
2586                pt::Type::Function {
2587                    params: vec![(loc!(), Some(param!(uint256)))],
2588                    attributes: vec![],
2589                    returns: Some((vec![], vec![])),
2590                } => "function (uint256)",
2591                pt::Type::Function {
2592                    params: vec![(loc!(), Some(param!(uint256)))],
2593                    attributes: vec![],
2594                    returns: Some((vec![(loc!(), Some(param!(uint256)))], vec![])),
2595                } => "function (uint256) returns (uint256)",
2596                pt::Type::Function {
2597                    params: vec![(loc!(), Some(param!(uint256)))],
2598                    attributes: vec![],
2599                    returns: Some((vec![(loc!(), Some(param!(uint256))), (loc!(), Some(param!(address)))], vec![])),
2600                } => "function (uint256) returns (uint256, address)",
2601            }
2602
2603            pt::UserDefinedOperator: {
2604                pt::UserDefinedOperator::BitwiseAnd => "&",
2605                pt::UserDefinedOperator::BitwiseNot => "~",
2606                pt::UserDefinedOperator::Negate => "-",
2607                pt::UserDefinedOperator::BitwiseOr => "|",
2608                pt::UserDefinedOperator::BitwiseXor => "^",
2609                pt::UserDefinedOperator::Add => "+",
2610                pt::UserDefinedOperator::Divide => "/",
2611                pt::UserDefinedOperator::Modulo => "%",
2612                pt::UserDefinedOperator::Multiply => "*",
2613                pt::UserDefinedOperator::Subtract => "-",
2614                pt::UserDefinedOperator::Equal => "==",
2615                pt::UserDefinedOperator::More => ">",
2616                pt::UserDefinedOperator::MoreEqual => ">=",
2617                pt::UserDefinedOperator::Less => "<",
2618                pt::UserDefinedOperator::LessEqual => "<=",
2619                pt::UserDefinedOperator::NotEqual => "!=",
2620            }
2621
2622            pt::UsingList: {
2623                pt::UsingList::Library(idp!("id", "path")) => "id.path",
2624
2625                pt::UsingList::Functions(vec![]) => "{}",
2626                pt::UsingList::Functions(vec![
2627                    pt::UsingFunction {
2628                        loc: loc!(),
2629                        path: idp!["id", "path"],
2630                        oper: None,
2631                    },
2632                    pt::UsingFunction {
2633                        loc: loc!(),
2634                        path: idp!["id", "path"],
2635                        oper: Some(pt::UserDefinedOperator::Add),
2636                }]) => "{id.path, id.path as +}",
2637            }
2638
2639            pt::VariableAttribute: {
2640                pt::VariableAttribute::Constant(loc!()) => "constant",
2641                pt::VariableAttribute::Immutable(loc!()) => "immutable",
2642
2643                pt::VariableAttribute::Override(loc!(), vec![]) => "override",
2644                pt::VariableAttribute::Override(loc!(), vec![idp!["a", "b"]]) => "override(a.b)",
2645                pt::VariableAttribute::Override(loc!(), vec![idp!["a", "b"], idp!["c", "d"]])
2646                    => "override(a.b, c.d)",
2647            }
2648
2649            pt::Visibility: {
2650                pt::Visibility::Public(Some(loc!())) => "public",
2651                pt::Visibility::Internal(Some(loc!())) => "internal",
2652                pt::Visibility::Private(Some(loc!())) => "private",
2653                pt::Visibility::External(Some(loc!())) => "external",
2654            }
2655
2656            pt::YulExpression: {
2657                pt::YulExpression::BoolLiteral(loc!(), false, None) => "false",
2658                pt::YulExpression::BoolLiteral(loc!(), true, None) => "true",
2659                pt::YulExpression::BoolLiteral(loc!(), false, Some(id("name"))) => "false: name",
2660                pt::YulExpression::BoolLiteral(loc!(), true, Some(id("name"))) => "true: name",
2661
2662                pt::YulExpression::NumberLiteral(loc!(), "1234".into(), "".into(), None) => "1234",
2663                pt::YulExpression::NumberLiteral(loc!(), "1234".into(), "9".into(), None) => "1234e9",
2664                pt::YulExpression::NumberLiteral(loc!(), "1234".into(), "".into(), Some(id("name"))) => "1234: name",
2665                pt::YulExpression::NumberLiteral(loc!(), "1234".into(), "9".into(), Some(id("name"))) => "1234e9: name",
2666
2667                pt::YulExpression::HexNumberLiteral(loc!(), "0x1234".into(), None) => "0x1234",
2668                pt::YulExpression::HexNumberLiteral(loc!(), "0x1234".into(), Some(id("name"))) => "0x1234: name",
2669
2670                pt::YulExpression::HexStringLiteral(lit!(hex "1234"), None) => "hex\"1234\"",
2671                pt::YulExpression::HexStringLiteral(lit!(hex "1234"), Some(id("name"))) => "hex\"1234\": name",
2672
2673                pt::YulExpression::StringLiteral(lit!("1234"), None) => "\"1234\"",
2674                pt::YulExpression::StringLiteral(lit!("1234"), Some(id("name"))) => "\"1234\": name",
2675
2676                pt::YulExpression::Variable(id("name")) => "name",
2677
2678                pt::YulExpression::FunctionCall(Box::new(pt::YulFunctionCall {
2679                    loc: loc!(),
2680                    id: id("name"),
2681                    arguments: vec![],
2682                })) => "name()",
2683
2684                pt::YulExpression::SuffixAccess(loc!(), Box::new(yexpr!(struct)), id("access"))
2685                    => "struct.access",
2686            }
2687
2688            pt::YulStatement: {
2689                // rest tested individually
2690
2691                pt::YulStatement::Assign(loc!(), vec![yexpr!(var)], yexpr!(eq))
2692                    => "var := eq",
2693                pt::YulStatement::Assign(loc!(), vec![yexpr!(a), yexpr!(b)], yexpr!(eq))
2694                    => "a, b := eq",
2695
2696                pt::YulStatement::VariableDeclaration(loc!(), vec![yid!(var)], None)
2697                    => "let var",
2698                pt::YulStatement::VariableDeclaration(loc!(), vec![yid!(a), yid!(b)], None)
2699                    => "let a, b",
2700                pt::YulStatement::VariableDeclaration(loc!(), vec![yid!(var)], Some(yexpr!(eq)))
2701                    => "let var := eq",
2702                pt::YulStatement::VariableDeclaration(loc!(), vec![yid!(a), yid!(b)], Some(yexpr!(eq)))
2703                    => "let a, b := eq",
2704
2705                pt::YulStatement::If(loc!(), yexpr!(expr), yul_block()) => "if expr {}",
2706
2707                pt::YulStatement::Leave(loc!()) => "leave",
2708                pt::YulStatement::Break(loc!()) => "break",
2709                pt::YulStatement::Continue(loc!()) => "continue",
2710            }
2711
2712            pt::YulSwitchOptions: {
2713                pt::YulSwitchOptions::Case(loc!(), yexpr!(expr), yul_block()) => "case expr {}",
2714                pt::YulSwitchOptions::Default(loc!(), yul_block()) => "default {}",
2715            }
2716        ];
2717    }
2718}