foundry_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            Self::Transient(_) => "transient",
1037        }
1038    }
1039}
1040
1041impl Display for pt::Type {
1042    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1043        match self {
1044            Self::Address => f.write_str("address"),
1045            Self::AddressPayable => f.write_str("address payable"),
1046            Self::Payable => f.write_str("payable"),
1047            Self::Bool => f.write_str("bool"),
1048            Self::String => f.write_str("string"),
1049            Self::Rational => f.write_str("fixed"),
1050            Self::DynamicBytes => f.write_str("bytes"),
1051            Self::Bytes(n) => {
1052                f.write_str("bytes")?;
1053                n.fmt(f)
1054            }
1055            Self::Int(n) => {
1056                f.write_str("int")?;
1057                n.fmt(f)
1058            }
1059            Self::Uint(n) => {
1060                f.write_str("uint")?;
1061                n.fmt(f)
1062            }
1063            Self::Mapping {
1064                key,
1065                key_name,
1066                value,
1067                value_name,
1068                ..
1069            } => {
1070                f.write_str("mapping(")?;
1071
1072                key.fmt(f)?;
1073                write_opt!(f, ' ', key_name);
1074
1075                f.write_str(" => ")?;
1076
1077                value.fmt(f)?;
1078                write_opt!(f, ' ', value_name);
1079
1080                f.write_char(')')
1081            }
1082            Self::Function {
1083                params,
1084                attributes,
1085                returns,
1086            } => {
1087                f.write_str("function (")?;
1088                fmt_parameter_list(params, f)?;
1089                f.write_char(')')?;
1090
1091                if !attributes.is_empty() {
1092                    f.write_char(' ')?;
1093                    write_separated(attributes, f, " ")?;
1094                }
1095
1096                if let Some((returns, attrs)) = returns {
1097                    if !attrs.is_empty() {
1098                        f.write_char(' ')?;
1099                        write_separated(attrs, f, " ")?;
1100                    }
1101
1102                    if !returns.is_empty() {
1103                        f.write_str(" returns (")?;
1104                        fmt_parameter_list(returns, f)?;
1105                        f.write_char(')')?;
1106                    }
1107                }
1108                Ok(())
1109            }
1110        }
1111    }
1112}
1113
1114impl Display for pt::UserDefinedOperator {
1115    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1116        f.write_str(self.as_str())
1117    }
1118}
1119
1120impl pt::UserDefinedOperator {
1121    /// Returns the string representation of this type.
1122    pub const fn as_str(&self) -> &'static str {
1123        match self {
1124            Self::BitwiseAnd => "&",
1125            Self::BitwiseNot => "~",
1126            Self::Negate => "-",
1127            Self::BitwiseOr => "|",
1128            Self::BitwiseXor => "^",
1129            Self::Add => "+",
1130            Self::Divide => "/",
1131            Self::Modulo => "%",
1132            Self::Multiply => "*",
1133            Self::Subtract => "-",
1134            Self::Equal => "==",
1135            Self::More => ">",
1136            Self::MoreEqual => ">=",
1137            Self::Less => "<",
1138            Self::LessEqual => "<=",
1139            Self::NotEqual => "!=",
1140        }
1141    }
1142}
1143
1144impl Display for pt::UsingList {
1145    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1146        match self {
1147            Self::Library(ident) => ident.fmt(f),
1148            Self::Functions(list) => {
1149                f.write_char('{')?;
1150                write_separated(list, f, ", ")?;
1151                f.write_char('}')
1152            }
1153            Self::Error => Ok(()),
1154        }
1155    }
1156}
1157
1158impl Display for pt::VariableAttribute {
1159    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1160        match self {
1161            Self::Visibility(vis) => vis.fmt(f),
1162            Self::Constant(_) => f.write_str("constant"),
1163            Self::Immutable(_) => f.write_str("immutable"),
1164            Self::Override(_, idents) => {
1165                f.write_str("override")?;
1166                if !idents.is_empty() {
1167                    f.write_char('(')?;
1168                    write_separated(idents, f, ", ")?;
1169                    f.write_char(')')?;
1170                }
1171                Ok(())
1172            }
1173            Self::StorageType(storage) => match storage {
1174                StorageType::Instance(_) => f.write_str("instance"),
1175                StorageType::Temporary(_) => f.write_str("temporary"),
1176                StorageType::Persistent(_) => f.write_str("persistent"),
1177            },
1178        }
1179    }
1180}
1181
1182impl Display for pt::Visibility {
1183    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1184        f.write_str(self.as_str())
1185    }
1186}
1187impl pt::Visibility {
1188    /// Returns the string representation of this type.
1189    pub const fn as_str(&self) -> &'static str {
1190        match self {
1191            Self::Public(_) => "public",
1192            Self::Internal(_) => "internal",
1193            Self::Private(_) => "private",
1194            Self::External(_) => "external",
1195        }
1196    }
1197}
1198
1199impl Display for pt::YulExpression {
1200    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1201        match self {
1202            Self::BoolLiteral(_, value, ident) => {
1203                let value = if *value { "true" } else { "false" };
1204                f.write_str(value)?;
1205                write_opt!(f, ": ", ident);
1206                Ok(())
1207            }
1208            Self::NumberLiteral(_, value, exponent, ident) => {
1209                f.write_str(value)?;
1210                if !exponent.is_empty() {
1211                    f.write_char('e')?;
1212                    f.write_str(exponent)?;
1213                }
1214                write_opt!(f, ": ", ident);
1215                Ok(())
1216            }
1217            Self::HexNumberLiteral(_, value, ident) => {
1218                f.write_str(value)?;
1219                write_opt!(f, ": ", ident);
1220                Ok(())
1221            }
1222            Self::HexStringLiteral(value, ident) => {
1223                value.fmt(f)?;
1224                write_opt!(f, ": ", ident);
1225                Ok(())
1226            }
1227            Self::StringLiteral(value, ident) => {
1228                value.fmt(f)?;
1229                write_opt!(f, ": ", ident);
1230                Ok(())
1231            }
1232            Self::Variable(ident) => ident.fmt(f),
1233            Self::FunctionCall(call) => call.fmt(f),
1234            Self::SuffixAccess(_, l, r) => {
1235                l.fmt(f)?;
1236                f.write_char('.')?;
1237                r.fmt(f)
1238            }
1239        }
1240    }
1241}
1242
1243impl Display for pt::YulStatement {
1244    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1245        match self {
1246            Self::Block(inner) => inner.fmt(f),
1247            Self::FunctionDefinition(inner) => inner.fmt(f),
1248            Self::FunctionCall(inner) => inner.fmt(f),
1249            Self::For(inner) => inner.fmt(f),
1250            Self::Switch(inner) => inner.fmt(f),
1251
1252            Self::Assign(_, exprs, eq_expr) => {
1253                write_separated(exprs, f, ", ")?;
1254                f.write_str(" := ")?;
1255                eq_expr.fmt(f)
1256            }
1257            Self::VariableDeclaration(_, vars, eq_expr) => {
1258                f.write_str("let")?;
1259                if !vars.is_empty() {
1260                    f.write_char(' ')?;
1261                    write_separated(vars, f, ", ")?;
1262                }
1263                write_opt!(f, " := ", eq_expr);
1264                Ok(())
1265            }
1266
1267            Self::If(_, expr, block) => {
1268                f.write_str("if ")?;
1269                expr.fmt(f)?;
1270                f.write_char(' ')?;
1271                block.fmt(f)
1272            }
1273
1274            Self::Leave(_) => f.write_str("leave"),
1275            Self::Break(_) => f.write_str("break"),
1276            Self::Continue(_) => f.write_str("continue"),
1277
1278            Self::Error(_) => Ok(()),
1279        }
1280    }
1281}
1282
1283impl Display for pt::YulSwitchOptions {
1284    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1285        match self {
1286            Self::Case(_, expr, block) => {
1287                f.write_str("case ")?;
1288                expr.fmt(f)?;
1289                f.write_str(" ")?;
1290                block.fmt(f)
1291            }
1292            Self::Default(_, block) => {
1293                f.write_str("default ")?;
1294                block.fmt(f)
1295            }
1296        }
1297    }
1298}
1299
1300// These functions are private so they should be inlined by the compiler.
1301// We provided these `#[inline]` hints regardless because we don't expect compile time penalties
1302// or other negative impacts from them.
1303// See: <https://github.com/hyperledger-solang/solang/pull/1237#discussion_r1151557453>
1304#[inline]
1305fn fmt_parameter_list(list: &pt::ParameterList, f: &mut Formatter<'_>) -> Result {
1306    let iter = list.iter().flat_map(|(_, param)| param);
1307    write_separated_iter(iter, f, ", ")
1308}
1309
1310#[inline]
1311fn write_separated<T: Display>(slice: &[T], f: &mut Formatter<'_>, sep: &str) -> Result {
1312    write_separated_iter(slice.iter(), f, sep)
1313}
1314
1315fn write_separated_iter<T, I>(mut iter: I, f: &mut Formatter<'_>, sep: &str) -> Result
1316where
1317    I: Iterator<Item = T>,
1318    T: Display,
1319{
1320    if let Some(first) = iter.next() {
1321        first.fmt(f)?;
1322        for item in iter {
1323            f.write_str(sep)?;
1324            item.fmt(f)?;
1325        }
1326    }
1327    Ok(())
1328}
1329
1330fn rm_underscores(s: &str) -> Cow<'_, str> {
1331    if s.is_empty() {
1332        Cow::Borrowed("0")
1333    } else if s.contains('_') {
1334        let mut s = s.to_string();
1335        s.retain(|c| c != '_');
1336        Cow::Owned(s)
1337    } else {
1338        Cow::Borrowed(s)
1339    }
1340}
1341
1342#[cfg(test)]
1343mod tests {
1344    use super::*;
1345    use crate::pt::{Annotation, Loc};
1346
1347    macro_rules! struct_tests {
1348        ($(pt::$t:ident { $( $f:ident: $e:expr ),* $(,)? } => $expected:expr),* $(,)?) => {
1349            $(
1350                assert_eq_display(
1351                    pt::$t {
1352                        loc: loc!(),
1353                        $( $f: $e, )*
1354                    },
1355                    $expected,
1356                );
1357            )*
1358        };
1359    }
1360
1361    macro_rules! enum_tests {
1362        ($(
1363            $t:ty: {
1364                $($p:expr => $expected:expr,)+
1365            }
1366        )+) => {
1367            $(
1368                $(
1369                    assert_eq_display($p, $expected);
1370                )+
1371            )+
1372        };
1373    }
1374
1375    /// Expression
1376    macro_rules! expr {
1377        (this) => {
1378            pt::Expression::This(loc!())
1379        };
1380
1381        ($i:ident) => {
1382            pt::Expression::Variable(id(stringify!($i)))
1383        };
1384
1385        ($l:literal) => {
1386            pt::Expression::Variable(id(stringify!($l)))
1387        };
1388
1389        (++ $($t:tt)+) => {
1390            pt::Expression::PreIncrement(loc!(), Box::new(expr!($($t)+)))
1391        };
1392
1393        ($($t:tt)+ ++) => {
1394            pt::Expression::PostIncrement(loc!(), Box::new(expr!($($t)+)))
1395        };
1396    }
1397    macro_rules! yexpr {
1398        ($i:ident) => {
1399            pt::YulExpression::Variable(id(stringify!($i)))
1400        };
1401        ($l:literal) => {
1402            pt::YulExpression::Variable(id(stringify!($l)))
1403        };
1404    }
1405
1406    /// Type
1407    macro_rules! ty {
1408        (uint256) => {
1409            pt::Type::Uint(256)
1410        };
1411        (string) => {
1412            pt::Type::String
1413        };
1414        (bytes) => {
1415            pt::Type::DynamicBytes
1416        };
1417        (address) => {
1418            pt::Type::Address
1419        };
1420    }
1421    macro_rules! expr_ty {
1422        ($($t:tt)+) => {
1423            pt::Expression::Type(loc!(), ty!($($t)+))
1424        };
1425    }
1426
1427    /// Literals
1428    macro_rules! lit {
1429        // prefixes are not allowed in rust strings
1430        (unicode $($l:literal)+) => {
1431            pt::StringLiteral {
1432                loc: loc!(),
1433                unicode: true,
1434                string: concat!( $($l),+ ).to_string(),
1435            }
1436        };
1437
1438        (hex $($l:literal)+) => {
1439            pt::HexLiteral {
1440                loc: loc!(),
1441                hex: concat!( "hex\"", $($l),+ , "\"" ).to_string(),
1442            }
1443        };
1444
1445        ($($l:literal)+) => {
1446            pt::StringLiteral {
1447                loc: loc!(),
1448                unicode: false,
1449                string: concat!( $($l),+ ).to_string(),
1450            }
1451        };
1452    }
1453
1454    /// VersionComparsion
1455    macro_rules! version {
1456        ($($l:literal),+) => {
1457            <[_]>::into_vec(Box::new([ $( $l.into() ),+ ]))
1458        }
1459    }
1460
1461    macro_rules! plain_version {
1462        ($($l:literal),+) => {
1463            pt::VersionComparator::Plain {
1464                loc: loc!(),
1465                version: <[_]>::into_vec(Box::new([ $( $l.into() ),+ ])),
1466            }
1467        };
1468    }
1469
1470    macro_rules! op_version {
1471        ($op:expr, $($l:literal),+) => {
1472            pt::VersionComparator::Operator {
1473                loc: loc!(),
1474                op: $op,
1475                version: <[_]>::into_vec(Box::new([ $( $l.into() ),+ ])),
1476            }
1477        };
1478    }
1479
1480    macro_rules! range_version {
1481        ($from:expr, $to:expr) => {
1482            pt::VersionComparator::Range {
1483                loc: loc!(),
1484                from: $from,
1485                to: $to,
1486            }
1487        };
1488    }
1489
1490    macro_rules! or_version {
1491        ($left:expr, $right:expr) => {
1492            pt::VersionComparator::Or {
1493                loc: loc!(),
1494                left: $left.into(),
1495                right: $right.into(),
1496            }
1497        };
1498    }
1499
1500    /// Statement
1501    macro_rules! stmt {
1502        ( {} ) => {
1503            pt::Statement::Block {
1504                loc: loc!(),
1505                unchecked: false,
1506                statements: vec![],
1507            }
1508        };
1509
1510        ( unchecked { $($t:tt)* } ) => {
1511            pt::Statement::Block {
1512                loc: loc!(),
1513                unchecked: true,
1514                statements: vec![stmt!($(t)*)],
1515            }
1516        };
1517        ( { $($t:tt)* } ) => {
1518            pt::Statement::Block {
1519                loc: loc!(),
1520                unchecked: false,
1521                statements: vec![stmt!($(t)*)],
1522            }
1523        };
1524    }
1525
1526    /// IdentifierPath
1527    macro_rules! idp {
1528        ($($e:expr),* $(,)?) => {
1529            pt::IdentifierPath {
1530                loc: loc!(),
1531                identifiers: vec![$(id($e)),*],
1532            }
1533        };
1534    }
1535
1536    macro_rules! loc {
1537        () => {
1538            pt::Loc::File(0, 0, 0)
1539        };
1540    }
1541
1542    /// Param
1543    macro_rules! param {
1544        ($i:ident) => {
1545            pt::Parameter {
1546                loc: loc!(),
1547                ty: expr_ty!($i),
1548                storage: None,
1549                name: None,
1550                annotation: None,
1551            }
1552        };
1553
1554        ($i:ident $n:ident) => {
1555            pt::Parameter {
1556                loc: loc!(),
1557                ty: expr_ty!($i),
1558                storage: None,
1559                name: Some(id(stringify!($n))),
1560                annotation: None,
1561            }
1562        };
1563
1564        ($i:ident $s:ident $n:ident) => {
1565            pt::Parameter {
1566                loc: loc!(),
1567                ty: expr_ty!($i),
1568                storage: Some(storage!($s)),
1569                name: Some(id(stringify!($n))),
1570                annotation: None,
1571            }
1572        };
1573    }
1574
1575    macro_rules! storage {
1576        (memory) => {
1577            pt::StorageLocation::Memory(loc!())
1578        };
1579        (storage) => {
1580            pt::StorageLocation::Storage(loc!())
1581        };
1582        (calldata) => {
1583            pt::StorageLocation::Calldata(loc!())
1584        };
1585        (transient) => {
1586            pt::StorageLocation::Transient(loc!())
1587        };
1588    }
1589
1590    /// Identifier
1591    fn id(s: &str) -> pt::Identifier {
1592        pt::Identifier {
1593            loc: loc!(),
1594            name: s.to_string(),
1595        }
1596    }
1597
1598    macro_rules! yid {
1599        ($i:ident) => {
1600            pt::YulTypedIdentifier {
1601                loc: loc!(),
1602                id: id(stringify!($i)),
1603                ty: None,
1604            }
1605        };
1606
1607        ($i:ident : $t:ident) => {
1608            pt::YulTypedIdentifier {
1609                loc: loc!(),
1610                id: id(stringify!($i)),
1611                ty: Some(id(stringify!($t))),
1612            }
1613        };
1614    }
1615
1616    fn var(s: &str) -> Box<pt::Expression> {
1617        Box::new(pt::Expression::Variable(id(s)))
1618    }
1619
1620    fn yul_block() -> pt::YulBlock {
1621        pt::YulBlock {
1622            loc: loc!(),
1623            statements: vec![],
1624        }
1625    }
1626
1627    fn assert_eq_display<T: Display + std::fmt::Debug>(item: T, expected: &str) {
1628        let ty = std::any::type_name::<T>();
1629        let actual = item.to_string();
1630        assert_eq!(actual, expected, "\"{ty}\": {item:?}");
1631        // TODO: Test parsing back into an item
1632        // let parsed = ;
1633        // assert_eq!(parsed, item, "failed to parse display back into an item: {expected}");
1634    }
1635
1636    #[test]
1637    fn display_structs_simple() {
1638        struct_tests![
1639            pt::Annotation {
1640                id: id("name"),
1641                value: Some(expr!(value)),
1642            } => "@name(value)",
1643
1644            pt::Base {
1645                name: idp!("id", "path"),
1646                args: None,
1647            } => "id.path",
1648            pt::Base {
1649                name: idp!("id", "path"),
1650                args: Some(vec![expr!(value)]),
1651            } => "id.path(value)",
1652            pt::Base {
1653                name: idp!("id", "path"),
1654                args: Some(vec![expr!(value1), expr!(value2)]),
1655            } => "id.path(value1, value2)",
1656
1657            pt::ErrorParameter {
1658                ty: expr_ty!(uint256),
1659                name: None,
1660            } => "uint256",
1661            pt::ErrorParameter {
1662                ty: expr_ty!(uint256),
1663                name: Some(id("name")),
1664            } => "uint256 name",
1665
1666            pt::EventParameter {
1667                ty: expr_ty!(uint256),
1668                indexed: false,
1669                name: None,
1670            } => "uint256",
1671            pt::EventParameter {
1672                ty: expr_ty!(uint256),
1673                indexed: true,
1674                name: None,
1675            } => "uint256 indexed",
1676            pt::EventParameter {
1677                ty: expr_ty!(uint256),
1678                indexed: false,
1679                name: Some(id("name")),
1680            } => "uint256 name",
1681            pt::EventParameter {
1682                ty: expr_ty!(uint256),
1683                indexed: true,
1684                name: Some(id("name")),
1685            } => "uint256 indexed name",
1686
1687            pt::HexLiteral {
1688                hex: "hex\"1234\"".into(),
1689            } => "hex\"1234\"",
1690            pt::HexLiteral {
1691                hex: "hex\"455318975130845\"".into(),
1692            } => "hex\"455318975130845\"",
1693
1694            pt::Identifier {
1695                name: "name".to_string(),
1696            } => "name",
1697
1698            pt::IdentifierPath {
1699                identifiers: vec![id("id")],
1700            } => "id",
1701            pt::IdentifierPath {
1702                identifiers: vec![id("id"), id("path")],
1703            } => "id.path",
1704            pt::IdentifierPath {
1705                identifiers: vec![id("long"), id("id"), id("path")],
1706            } => "long.id.path",
1707
1708            pt::NamedArgument {
1709                name: id("name"),
1710                expr: expr!(expr),
1711            } => "name: expr",
1712
1713            pt::Parameter {
1714                ty: expr_ty!(uint256),
1715                storage: None,
1716                name: None,
1717                annotation: None,
1718            } => "uint256",
1719            pt::Parameter {
1720                ty: expr_ty!(uint256),
1721                storage: None,
1722                name: Some(id("name")),
1723                annotation: None,
1724            } => "uint256 name",
1725            pt::Parameter {
1726                ty: expr_ty!(uint256),
1727                storage: Some(pt::StorageLocation::Calldata(Default::default())),
1728                name: Some(id("name")),
1729                annotation: None,
1730            } => "uint256 calldata name",
1731            pt::Parameter {
1732                ty: expr_ty!(uint256),
1733                storage: Some(pt::StorageLocation::Calldata(Default::default())),
1734                name: None,
1735                annotation: None,
1736            } => "uint256 calldata",
1737            pt::Parameter {
1738                ty: expr_ty!(bytes),
1739                storage: None,
1740                name: Some(id("my_seed")),
1741                annotation: Some(Annotation {
1742                    loc: Loc::Builtin,
1743                    id: id("name"),
1744                    value: None,
1745                }),
1746            } => "@name bytes my_seed",
1747
1748            pt::StringLiteral {
1749                unicode: false,
1750                string: "string".into(),
1751            } => "\"string\"",
1752            pt::StringLiteral {
1753                unicode: true,
1754                string: "string".into(),
1755            } => "unicode\"string\"",
1756
1757            pt::UsingFunction {
1758                path: idp!["id", "path"],
1759                oper: None,
1760            } => "id.path",
1761            pt::UsingFunction {
1762                path: idp!["id", "path"],
1763                oper: Some(pt::UserDefinedOperator::Add),
1764            } => "id.path as +",
1765
1766            pt::VariableDeclaration {
1767                ty: expr_ty!(uint256),
1768                storage: None,
1769                name: None,
1770            } => "uint256",
1771            pt::VariableDeclaration {
1772                ty: expr_ty!(uint256),
1773                storage: None,
1774                name: Some(id("name")),
1775            } => "uint256 name",
1776            pt::VariableDeclaration {
1777                ty: expr_ty!(uint256),
1778                storage: Some(pt::StorageLocation::Calldata(Default::default())),
1779                name: Some(id("name")),
1780            } => "uint256 calldata name",
1781            pt::VariableDeclaration {
1782                ty: expr_ty!(uint256),
1783                storage: Some(pt::StorageLocation::Calldata(Default::default())),
1784                name: None,
1785            } => "uint256 calldata",
1786
1787            pt::VariableDefinition {
1788                ty: expr_ty!(uint256),
1789                attrs: vec![],
1790                name: None,
1791                initializer: None,
1792            } => "uint256;",
1793            pt::VariableDefinition {
1794                ty: expr_ty!(uint256),
1795                attrs: vec![],
1796                name: Some(id("name")),
1797                initializer: None,
1798            } => "uint256 name;",
1799            pt::VariableDefinition {
1800                ty: expr_ty!(uint256),
1801                attrs: vec![],
1802                name: Some(id("name")),
1803                initializer: Some(expr!(value)),
1804            } => "uint256 name = value;",
1805            pt::VariableDefinition {
1806                ty: expr_ty!(uint256),
1807                attrs: vec![pt::VariableAttribute::Constant(loc!())],
1808                name: Some(id("name")),
1809                initializer: Some(expr!(value)),
1810            } => "uint256 constant name = value;",
1811            pt::VariableDefinition {
1812                ty: expr_ty!(uint256),
1813                attrs: vec![
1814                    pt::VariableAttribute::Visibility(pt::Visibility::Public(None)),
1815                    pt::VariableAttribute::Constant(loc!())
1816                ],
1817                name: Some(id("name")),
1818                initializer: Some(expr!(value)),
1819            } => "uint256 public constant name = value;",
1820
1821            pt::YulTypedIdentifier {
1822                id: id("name"),
1823                ty: None,
1824            } => "name",
1825            pt::YulTypedIdentifier {
1826                id: id("name"),
1827                ty: Some(id("uint256")),
1828            } => "name: uint256",
1829        ];
1830    }
1831
1832    #[test]
1833    fn display_structs_complex() {
1834        struct_tests![
1835            pt::ContractDefinition {
1836                ty: pt::ContractTy::Contract(loc!()),
1837                name: Some(id("name")),
1838                base: vec![],
1839                layout: None,
1840                parts: vec![],
1841            } => "contract name {}",
1842            pt::ContractDefinition {
1843                ty: pt::ContractTy::Contract(loc!()),
1844                name: Some(id("name")),
1845                base: vec![pt::Base {
1846                    loc: loc!(),
1847                    name: idp!("base"),
1848                    args: None
1849                }],
1850                layout: None,
1851                parts: vec![],
1852            } => "contract name base {}",
1853            pt::ContractDefinition {
1854                ty: pt::ContractTy::Contract(loc!()),
1855                name: Some(id("name")),
1856                base: vec![pt::Base {
1857                    loc: loc!(),
1858                    name: idp!("base"),
1859                    args: Some(vec![])
1860                }],
1861                layout: None,
1862                parts: vec![],
1863            } => "contract name base() {}",
1864            pt::ContractDefinition {
1865                ty: pt::ContractTy::Contract(loc!()),
1866                name: Some(id("name")),
1867                base: vec![pt::Base {
1868                    loc: loc!(),
1869                    name: idp!("base"),
1870                    args: Some(vec![expr!(expr)])
1871                }],
1872                layout: None,
1873                parts: vec![],
1874            } => "contract name base(expr) {}",
1875            pt::ContractDefinition {
1876                ty: pt::ContractTy::Contract(loc!()),
1877                name: Some(id("name")),
1878                base: vec![
1879                    pt::Base {
1880                        loc: loc!(),
1881                        name: idp!("base1"),
1882                        args: None
1883                    },
1884                    pt::Base {
1885                        loc: loc!(),
1886                        name: idp!("base2"),
1887                        args: None
1888                    },
1889                ],
1890                layout: None,
1891                parts: vec![],
1892            } => "contract name base1 base2 {}",
1893
1894            pt::EnumDefinition {
1895                name: Some(id("name")),
1896                values: vec![]
1897            } => "enum name {}",
1898            pt::EnumDefinition {
1899                name: Some(id("name")),
1900                values: vec![Some(id("variant"))]
1901            } => "enum name {variant}",
1902            pt::EnumDefinition {
1903                name: Some(id("name")),
1904                values: vec![
1905                    Some(id("variant1")),
1906                    Some(id("variant2")),
1907                ]
1908            } => "enum name {variant1, variant2}",
1909
1910            pt::ErrorDefinition {
1911                keyword: expr!(error),
1912                name: Some(id("name")),
1913                fields: vec![],
1914            } => "error name();",
1915            pt::ErrorDefinition {
1916                keyword: expr!(error),
1917                name: Some(id("name")),
1918                fields: vec![pt::ErrorParameter {
1919                    loc: loc!(),
1920                    ty: expr_ty!(uint256),
1921                    name: None,
1922                }],
1923            } => "error name(uint256);",
1924
1925            pt::EventDefinition {
1926                name: Some(id("name")),
1927                fields: vec![],
1928                anonymous: false,
1929            } => "event name();",
1930            pt::EventDefinition {
1931                name: Some(id("name")),
1932                fields: vec![pt::EventParameter {
1933                    loc: loc!(),
1934                    ty: expr_ty!(uint256),
1935                    indexed: false,
1936                    name: None,
1937                }],
1938                anonymous: false,
1939            } => "event name(uint256);",
1940            pt::EventDefinition {
1941                name: Some(id("name")),
1942                fields: vec![pt::EventParameter {
1943                    loc: loc!(),
1944                    ty: expr_ty!(uint256),
1945                    indexed: true,
1946                    name: None,
1947                }],
1948                anonymous: false,
1949            } => "event name(uint256 indexed);",
1950            pt::EventDefinition {
1951                name: Some(id("name")),
1952                fields: vec![],
1953                anonymous: true,
1954            } => "event name() anonymous;",
1955
1956            pt::FunctionDefinition {
1957                loc_prototype: loc!(),
1958                ty: pt::FunctionTy::Function,
1959                name: Some(id("name")),
1960                name_loc: loc!(),
1961                params: vec![],
1962                attributes: vec![],
1963                return_not_returns: None,
1964                returns: vec![],
1965                body: None,
1966            } => "function name();",
1967            pt::FunctionDefinition {
1968                loc_prototype: loc!(),
1969                ty: pt::FunctionTy::Function,
1970                name: Some(id("name")),
1971                name_loc: loc!(),
1972                params: vec![],
1973                attributes: vec![],
1974                return_not_returns: None,
1975                returns: vec![],
1976                body: Some(stmt!({})),
1977            } => "function name() {}",
1978            pt::FunctionDefinition {
1979                loc_prototype: loc!(),
1980                ty: pt::FunctionTy::Function,
1981                name: Some(id("name")),
1982                name_loc: loc!(),
1983                params: vec![],
1984                attributes: vec![],
1985                return_not_returns: None,
1986                returns: vec![(loc!(), Some(param!(uint256)))],
1987                body: Some(stmt!({})),
1988            } => "function name() returns (uint256) {}",
1989            pt::FunctionDefinition {
1990                loc_prototype: loc!(),
1991                ty: pt::FunctionTy::Function,
1992                name: Some(id("name")),
1993                name_loc: loc!(),
1994                params: vec![],
1995                attributes: vec![pt::FunctionAttribute::Virtual(loc!())],
1996                return_not_returns: None,
1997                returns: vec![(loc!(), Some(param!(uint256)))],
1998                body: Some(stmt!({})),
1999            } => "function name() virtual returns (uint256) {}",
2000
2001            pt::StructDefinition {
2002                name: Some(id("name")),
2003                fields: vec![],
2004            } => "struct name {}",
2005            pt::StructDefinition {
2006                name: Some(id("name")),
2007                fields: vec![pt::VariableDeclaration {
2008                    loc: loc!(),
2009                    ty: expr_ty!(uint256),
2010                    storage: None,
2011                    name: Some(id("a")),
2012                }],
2013            } => "struct name {uint256 a;}",
2014            pt::StructDefinition {
2015                name: Some(id("name")),
2016                fields: vec![
2017                    pt::VariableDeclaration {
2018                        loc: loc!(),
2019                        ty: expr_ty!(uint256),
2020                        storage: None,
2021                        name: Some(id("a")),
2022                    },
2023                    pt::VariableDeclaration {
2024                        loc: loc!(),
2025                        ty: expr_ty!(uint256),
2026                        storage: None,
2027                        name: Some(id("b")),
2028                    }
2029                ],
2030            } => "struct name {uint256 a; uint256 b;}",
2031
2032            pt::TypeDefinition {
2033                name: id("MyType"),
2034                ty: expr_ty!(uint256),
2035            } => "type MyType is uint256;",
2036
2037            pt::Using {
2038                list: pt::UsingList::Library(idp!["id", "path"]),
2039                ty: None,
2040                global: None,
2041            } => "using id.path for *;",
2042            pt::Using {
2043                list: pt::UsingList::Library(idp!["id", "path"]),
2044                ty: Some(expr_ty!(uint256)),
2045                global: None,
2046            } => "using id.path for uint256;",
2047            pt::Using {
2048                list: pt::UsingList::Library(idp!["id", "path"]),
2049                ty: Some(expr_ty!(uint256)),
2050                global: Some(id("global")),
2051            } => "using id.path for uint256 global;",
2052            pt::Using {
2053                list: pt::UsingList::Functions(vec![]),
2054                ty: None,
2055                global: None,
2056            } => "using {} for *;",
2057            pt::Using {
2058                list: pt::UsingList::Functions(vec![
2059                    pt::UsingFunction {
2060                        loc: loc!(),
2061                        path: idp!("id", "path"),
2062                        oper: None,
2063                    }
2064                ]),
2065                ty: None,
2066                global: None,
2067            } => "using {id.path} for *;",
2068            pt::Using {
2069                list: pt::UsingList::Functions(vec![
2070                    pt::UsingFunction {
2071                        loc: loc!(),
2072                        path: idp!("id", "path"),
2073                        oper: Some(pt::UserDefinedOperator::Add),
2074                    }
2075                ]),
2076                ty: Some(expr_ty!(uint256)),
2077                global: None,
2078            } => "using {id.path as +} for uint256;",
2079            pt::Using {
2080                list: pt::UsingList::Functions(vec![
2081                    pt::UsingFunction {
2082                        loc: loc!(),
2083                        path: idp!("id", "path1"),
2084                        oper: None,
2085                    },
2086                    pt::UsingFunction {
2087                        loc: loc!(),
2088                        path: idp!("id", "path2"),
2089                        oper: None,
2090                    }
2091                ]),
2092                ty: Some(expr_ty!(uint256)),
2093                global: Some(id("global")),
2094            } => "using {id.path1, id.path2} for uint256 global;",
2095
2096            pt::YulBlock {
2097                statements: vec![]
2098            } => "{}",
2099
2100            pt::YulFor {
2101                init_block: yul_block(),
2102                condition: yexpr!(cond),
2103                post_block: yul_block(),
2104                execution_block: yul_block(),
2105            } => "for {} cond {} {}",
2106
2107            pt::YulFunctionCall {
2108                id: id("name"),
2109                arguments: vec![],
2110            } => "name()",
2111            pt::YulFunctionCall {
2112                id: id("name"),
2113                arguments: vec![yexpr!(arg)],
2114            } => "name(arg)",
2115            pt::YulFunctionCall {
2116                id: id("name"),
2117                arguments: vec![yexpr!(arg1), yexpr!(arg2)],
2118            } => "name(arg1, arg2)",
2119
2120            pt::YulFunctionDefinition {
2121                id: id("name"),
2122                params: vec![],
2123                returns: vec![],
2124                body: yul_block(),
2125            } => "function name() {}",
2126            pt::YulFunctionDefinition {
2127                id: id("name"),
2128                params: vec![yid!(param1: a), yid!(param2: b)],
2129                returns: vec![],
2130                body: yul_block(),
2131            } => "function name(param1: a, param2: b) {}",
2132            pt::YulFunctionDefinition {
2133                id: id("name"),
2134                params: vec![yid!(param1: a), yid!(param2: b)],
2135                returns: vec![yid!(ret1: c), yid!(ret2: d)],
2136                body: yul_block(),
2137            } => "function name(param1: a, param2: b) -> (ret1: c, ret2: d) {}",
2138
2139            pt::YulSwitch {
2140                condition: yexpr!(cond),
2141                cases: vec![pt::YulSwitchOptions::Case(loc!(), yexpr!(expr), yul_block())],
2142                default: None,
2143            } => "switch cond case expr {}",
2144            pt::YulSwitch {
2145                condition: yexpr!(cond),
2146                cases: vec![
2147                    pt::YulSwitchOptions::Case(loc!(), yexpr!(0), yul_block()),
2148                    pt::YulSwitchOptions::Case(loc!(), yexpr!(1), yul_block()),
2149                ],
2150                default: None,
2151            } => "switch cond case 0 {} case 1 {}",
2152            pt::YulSwitch {
2153                condition: yexpr!(cond),
2154                cases: vec![pt::YulSwitchOptions::Case(loc!(), yexpr!(0), yul_block())],
2155                default: Some(pt::YulSwitchOptions::Default(loc!(), yul_block())),
2156            } => "switch cond case 0 {} default {}",
2157        ];
2158    }
2159
2160    #[test]
2161    fn display_enums() {
2162        enum_tests![
2163            // https://docs.soliditylang.org/en/latest/control-structures.html#try-catch
2164            pt::CatchClause: {
2165                pt::CatchClause::Named(loc!(), id("Error"), param!(string memory reason), stmt!({}))
2166                    => "catch Error(string memory reason) {}",
2167                pt::CatchClause::Named(loc!(), id("Panic"), param!(uint256 errorCode), stmt!({}))
2168                    => "catch Panic(uint256 errorCode) {}",
2169
2170                pt::CatchClause::Simple(loc!(), None, stmt!({})) => "catch {}",
2171                pt::CatchClause::Simple(loc!(), Some(param!(uint256)), stmt!({}))
2172                    => "catch (uint256) {}",
2173                pt::CatchClause::Simple(loc!(), Some(param!(bytes memory data)), stmt!({}))
2174                    => "catch (bytes memory data) {}",
2175            }
2176
2177            pt::Comment: {
2178                pt::Comment::Line(loc!(), "// line".into()) => "// line",
2179                pt::Comment::Block(loc!(), "/* \nblock\n*/".into()) => "/* \nblock\n*/",
2180                pt::Comment::DocLine(loc!(), "/// doc line".into()) => "/// doc line",
2181                pt::Comment::DocBlock(loc!(), "/**\n * doc block\n */".into()) => "/**\n * doc block\n */",
2182            }
2183
2184            // tested individually
2185            pt::ContractPart: {
2186                pt::ContractPart::StraySemicolon(loc!()) => ";",
2187            }
2188
2189            pt::ContractTy: {
2190                pt::ContractTy::Abstract(loc!()) => "abstract contract",
2191                pt::ContractTy::Contract(loc!()) => "contract",
2192                pt::ContractTy::Interface(loc!()) => "interface",
2193                pt::ContractTy::Library(loc!()) => "library",
2194            }
2195
2196            pt::Expression: {
2197                pt::Expression::New(loc!(), Box::new(expr_ty!(uint256))) => "new uint256",
2198                pt::Expression::Delete(loc!(), Box::new(expr_ty!(uint256))) => "delete uint256",
2199
2200                pt::Expression::Type(loc!(), ty!(uint256)) => "uint256",
2201                pt::Expression::Variable(id("myVar")) => "myVar",
2202
2203                pt::Expression::ArrayLiteral(loc!(), vec![expr!(1), expr!(2)]) => "[1, 2]",
2204
2205                pt::Expression::ArraySubscript(loc!(), Box::new(expr!(arr)), None) => "arr[]",
2206                pt::Expression::ArraySubscript(loc!(), Box::new(expr!(arr)), Some(Box::new(expr!(0)))) => "arr[0]",
2207                pt::Expression::ArraySlice(loc!(), Box::new(expr!(arr)), None, None) => "arr[:]",
2208                pt::Expression::ArraySlice(loc!(), Box::new(expr!(arr)), Some(Box::new(expr!(left))), None)
2209                    => "arr[left:]",
2210                pt::Expression::ArraySlice(loc!(), Box::new(expr!(arr)), None, Some(Box::new(expr!(right))))
2211                    => "arr[:right]",
2212                pt::Expression::ArraySlice(loc!(), Box::new(expr!(arr)), Some(Box::new(expr!(left))), Some(Box::new(expr!(right))))
2213                    => "arr[left:right]",
2214
2215                pt::Expression::MemberAccess(loc!(), Box::new(expr!(struct)), id("access")) => "struct.access",
2216
2217                pt::Expression::Parenthesis(loc!(), Box::new(expr!(var))) => "(var)",
2218                pt::Expression::List(loc!(), vec![]) => "()",
2219                pt::Expression::List(loc!(), vec![(loc!(), Some(param!(address)))])
2220                    => "(address)",
2221                pt::Expression::List(loc!(), vec![(loc!(), Some(param!(address))), (loc!(), Some(param!(uint256)))])
2222                    => "(address, uint256)",
2223
2224                pt::Expression::AddressLiteral(loc!(), "0x1234".into()) => "0x1234",
2225                pt::Expression::StringLiteral(vec![lit!(unicode "¹²³")]) => "unicode\"¹²³\"",
2226                pt::Expression::HexLiteral(vec![lit!(hex "00112233")]) => "hex\"00112233\"",
2227                pt::Expression::BoolLiteral(loc!(), true) => "true",
2228                pt::Expression::BoolLiteral(loc!(), false) => "false",
2229
2230                pt::Expression::HexNumberLiteral(loc!(), "0x1234".into(), None) => "0x1234",
2231                pt::Expression::HexNumberLiteral(loc!(), "0x1234".into(), Some(id("gwei"))) => "0x1234 gwei",
2232                pt::Expression::NumberLiteral(loc!(), "_123_4_".into(), "".into(), None)
2233                    => "1234",
2234                pt::Expression::NumberLiteral(loc!(), "_1_234_".into(), "_2".into(), None)
2235                    => "1234e2",
2236                pt::Expression::NumberLiteral(loc!(), "_1_23_4".into(), "".into(), Some(id("gwei")))
2237                    => "1234 gwei",
2238                pt::Expression::NumberLiteral(loc!(), "1_23_4_".into(), "2_".into(), Some(id("gwei")))
2239                    => "1234e2 gwei",
2240                pt::Expression::RationalNumberLiteral(loc!(), "1_23_4_".into(), "".into(), "".into(), None)
2241                    => "1234.0",
2242                pt::Expression::RationalNumberLiteral(loc!(), "_1_23_4".into(), "0".into(), "_2".into(), None)
2243                    => "1234.0e2",
2244                pt::Expression::RationalNumberLiteral(loc!(), "_1_234_".into(), "09".into(), "".into(), Some(id("gwei")))
2245                    => "1234.09 gwei",
2246                pt::Expression::RationalNumberLiteral(loc!(), "_123_4_".into(), "90".into(), "2_".into(), Some(id("gwei")))
2247                    => "1234.9e2 gwei",
2248
2249                pt::Expression::FunctionCall(loc!(), Box::new(expr!(func)), vec![]) => "func()",
2250                pt::Expression::FunctionCall(loc!(), Box::new(expr!(func)), vec![expr!(arg)])
2251                    => "func(arg)",
2252                pt::Expression::FunctionCall(loc!(), Box::new(expr!(func)), vec![expr!(arg1), expr!(arg2)])
2253                    => "func(arg1, arg2)",
2254                pt::Expression::FunctionCallBlock(loc!(), Box::new(expr!(func)), Box::new(stmt!({})))
2255                    => "func{}",
2256                pt::Expression::NamedFunctionCall(loc!(), Box::new(expr!(func)), vec![])
2257                    => "func({})",
2258                pt::Expression::NamedFunctionCall(loc!(), Box::new(expr!(func)), vec![pt::NamedArgument {
2259                    loc: loc!(),
2260                    name: id("arg"),
2261                    expr: expr!(value),
2262                }]) => "func({arg: value})",
2263                pt::Expression::NamedFunctionCall(loc!(), Box::new(expr!(func)), vec![
2264                    pt::NamedArgument {
2265                        loc: loc!(),
2266                        name: id("arg1"),
2267                        expr: expr!(value1),
2268                    },
2269                    pt::NamedArgument {
2270                        loc: loc!(),
2271                        name: id("arg2"),
2272                        expr: expr!(value2),
2273                    }
2274                ]) => "func({arg1: value1, arg2: value2})",
2275
2276                pt::Expression::PreIncrement(loc!(), var("a")) => "++a",
2277                pt::Expression::PostIncrement(loc!(), var("a")) => "a++",
2278                pt::Expression::PreDecrement(loc!(), var("a")) => "--a",
2279                pt::Expression::PostDecrement(loc!(), var("a")) => "a--",
2280                pt::Expression::Not(loc!(), var("a")) => "!a",
2281                pt::Expression::BitwiseNot(loc!(), var("a")) => "~a",
2282                pt::Expression::UnaryPlus(loc!(), var("a")) => "+a",
2283                pt::Expression::Negate(loc!(), var("a")) => "-a",
2284
2285                pt::Expression::Add(loc!(), var("a"), var("b")) => "a + b",
2286                pt::Expression::Subtract(loc!(), var("a"), var("b")) => "a - b",
2287                pt::Expression::Power(loc!(), var("a"), var("b")) => "a ** b",
2288                pt::Expression::Multiply(loc!(), var("a"), var("b")) => "a * b",
2289                pt::Expression::Divide(loc!(), var("a"), var("b")) => "a / b",
2290                pt::Expression::Modulo(loc!(), var("a"), var("b")) => "a % b",
2291                pt::Expression::ShiftLeft(loc!(), var("a"), var("b")) => "a << b",
2292                pt::Expression::ShiftRight(loc!(), var("a"), var("b")) => "a >> b",
2293                pt::Expression::BitwiseAnd(loc!(), var("a"), var("b")) => "a & b",
2294                pt::Expression::BitwiseXor(loc!(), var("a"), var("b")) => "a ^ b",
2295                pt::Expression::BitwiseOr(loc!(), var("a"), var("b")) => "a | b",
2296                pt::Expression::Less(loc!(), var("a"), var("b")) => "a < b",
2297                pt::Expression::More(loc!(), var("a"), var("b")) => "a > b",
2298                pt::Expression::LessEqual(loc!(), var("a"), var("b")) => "a <= b",
2299                pt::Expression::MoreEqual(loc!(), var("a"), var("b")) => "a >= b",
2300                pt::Expression::And(loc!(), var("a"), var("b")) => "a && b",
2301                pt::Expression::Or(loc!(), var("a"), var("b")) => "a || b",
2302                pt::Expression::Equal(loc!(), var("a"), var("b")) => "a == b",
2303                pt::Expression::NotEqual(loc!(), var("a"), var("b")) => "a != b",
2304
2305                pt::Expression::Assign(loc!(), var("a"), var("b")) => "a = b",
2306                pt::Expression::AssignOr(loc!(), var("a"), var("b")) => "a |= b",
2307                pt::Expression::AssignAnd(loc!(), var("a"), var("b")) => "a &= b",
2308                pt::Expression::AssignXor(loc!(), var("a"), var("b")) => "a ^= b",
2309                pt::Expression::AssignShiftLeft(loc!(), var("a"), var("b")) => "a <<= b",
2310                pt::Expression::AssignShiftRight(loc!(), var("a"), var("b")) => "a >>= b",
2311                pt::Expression::AssignAdd(loc!(), var("a"), var("b")) => "a += b",
2312                pt::Expression::AssignSubtract(loc!(), var("a"), var("b")) => "a -= b",
2313                pt::Expression::AssignMultiply(loc!(), var("a"), var("b")) => "a *= b",
2314                pt::Expression::AssignDivide(loc!(), var("a"), var("b")) => "a /= b",
2315                pt::Expression::AssignModulo(loc!(), var("a"), var("b")) => "a %= b",
2316            }
2317
2318            pt::FunctionAttribute: {
2319                pt::FunctionAttribute::Virtual(loc!()) => "virtual",
2320                pt::FunctionAttribute::Immutable(loc!()) => "immutable",
2321
2322                pt::FunctionAttribute::Override(loc!(), vec![]) => "override",
2323                pt::FunctionAttribute::Override(loc!(), vec![idp!["a", "b"]]) => "override(a.b)",
2324                pt::FunctionAttribute::Override(loc!(), vec![idp!["a", "b"], idp!["c", "d"]])
2325                    => "override(a.b, c.d)",
2326            }
2327
2328            pt::FunctionTy: {
2329                pt::FunctionTy::Constructor => "constructor",
2330                pt::FunctionTy::Function => "function",
2331                pt::FunctionTy::Fallback => "fallback",
2332                pt::FunctionTy::Receive => "receive",
2333                pt::FunctionTy::Modifier => "modifier",
2334            }
2335
2336            pt::Import: {
2337                pt::Import::Plain(pt::ImportPath::Filename(lit!("path/to/import")), loc!()) => "import \"path/to/import\";",
2338
2339                pt::Import::GlobalSymbol(pt::ImportPath::Filename(lit!("path-to-import")), id("ImportedContract"), loc!())
2340                    => "import \"path-to-import\" as ImportedContract;",
2341
2342                pt::Import::Rename(pt::ImportPath::Filename(lit!("import\\to\\path")), vec![], loc!())
2343                    => "import {} from \"import\\to\\path\";",
2344                pt::Import::Rename(pt::ImportPath::Filename(lit!("import\\to\\path")), vec![(id("A"), None), (id("B"), Some(id("C")))], loc!())
2345                    => "import {A, B as C} from \"import\\to\\path\";",
2346
2347                pt::Import::Plain(pt::ImportPath::Path(idp!("std", "stub")), loc!()) => "import std.stub;",
2348
2349                pt::Import::GlobalSymbol(pt::ImportPath::Path(idp!("a", "b", "c")), id("ImportedContract"), loc!())
2350                    => "import a.b.c as ImportedContract;",
2351
2352                pt::Import::Rename(pt::ImportPath::Path(idp!("std", "stub")), vec![], loc!())
2353                    => "import {} from std.stub;",
2354                pt::Import::Rename(pt::ImportPath::Path(idp!("std", "stub")), vec![(id("A"), None), (id("B"), Some(id("C")))], loc!())
2355                    => "import {A, B as C} from std.stub;",
2356            }
2357
2358            pt::Mutability: {
2359                pt::Mutability::Pure(loc!()) => "pure",
2360                pt::Mutability::View(loc!()) => "view",
2361                pt::Mutability::Constant(loc!()) => "view",
2362                pt::Mutability::Payable(loc!()) => "payable",
2363            }
2364
2365            pt::SourceUnitPart: {
2366                // rest tested individually
2367
2368                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Identifier(loc!(), None, None).into()) => "pragma;",
2369                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Identifier(loc!(), Some(id("solidity")), None).into())
2370                    => "pragma solidity;",
2371                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::StringLiteral(loc!(), id("abi"), lit!("v2")).into())
2372                    => "pragma abi \"v2\";",
2373                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Version(loc!(), id("solidity"), vec![plain_version!("0", "8", "0")]).into())
2374                    => "pragma solidity 0.8.0;",
2375                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Version(loc!(), id("solidity"), vec![
2376                    op_version!(pt::VersionOp::Exact, "0", "5", "16"),
2377                    op_version!(pt::VersionOp::GreaterEq, "0", "5"),
2378                    op_version!(pt::VersionOp::Greater, "0"),
2379                    op_version!(pt::VersionOp::Less, "1"),
2380                    op_version!(pt::VersionOp::LessEq, "1"),
2381                    op_version!(pt::VersionOp::Caret, "0", "5", "16"),
2382                    op_version!(pt::VersionOp::Wildcard, "5", "5")]
2383                ).into())
2384                    => "pragma solidity =0.5.16 >=0.5 >0 <1 <=1 ^0.5.16 *5.5;",
2385                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Version(loc!(), id("solidity"), vec![or_version!(plain_version!("0"), op_version!(pt::VersionOp::Caret, "1", "0"))]).into())
2386                    => "pragma solidity 0 || ^1.0;",
2387                pt::SourceUnitPart::PragmaDirective(pt::PragmaDirective::Version(loc!(), id("solidity"), vec![range_version!(version!["0"], version!["1", "0"])]).into())
2388                    => "pragma solidity 0 - 1.0;",
2389                pt::SourceUnitPart::StraySemicolon(loc!()) => ";",
2390            }
2391
2392            pt::Statement: {
2393                pt::Statement::Assembly {
2394                    loc: loc!(),
2395                    dialect: None,
2396                    flags: None,
2397                    block: yul_block(),
2398                } => "assembly {}",
2399                pt::Statement::Assembly {
2400                    loc: loc!(),
2401                    dialect: None,
2402                    flags: Some(vec![lit!("memory-safe")]),
2403                    block: yul_block(),
2404                } => "assembly (\"memory-safe\") {}",
2405                pt::Statement::Assembly {
2406                    loc: loc!(),
2407                    dialect: None,
2408                    flags: Some(vec![lit!("memory-safe"), lit!("second-flag")]),
2409                    block: yul_block(),
2410                } => "assembly (\"memory-safe\", \"second-flag\") {}",
2411
2412                pt::Statement::Args(loc!(), vec![]) => "{}",
2413                pt::Statement::Args(loc!(), vec![
2414                    pt::NamedArgument {
2415                        loc: loc!(),
2416                        name: id("name"),
2417                        expr: expr!(value),
2418                    },
2419                ]) => "{name: value}",
2420                pt::Statement::Args(loc!(), vec![
2421                    pt::NamedArgument {
2422                        loc: loc!(),
2423                        name: id("name1"),
2424                        expr: expr!(value1),
2425                    },
2426                    pt::NamedArgument {
2427                        loc: loc!(),
2428                        name: id("name2"),
2429                        expr: expr!(value2),
2430                    },
2431                ]) => "{name1: value1, name2: value2}",
2432
2433                pt::Statement::If(loc!(), expr!(true), Box::new(stmt!({})), None) => "if (true) {}",
2434                pt::Statement::If(loc!(), expr!(true), Box::new(stmt!({})), Some(Box::new(stmt!({}))))
2435                    => "if (true) {} else {}",
2436
2437                pt::Statement::While(loc!(), expr!(true), Box::new(stmt!({}))) => "while (true) {}",
2438
2439                pt::Statement::Expression(loc!(), expr!(true)) => "true;",
2440
2441                pt::Statement::VariableDefinition(loc!(), pt::VariableDeclaration {
2442                    loc: loc!(),
2443                    ty: expr_ty!(uint256),
2444                    storage: None,
2445                    name: Some(id("a")),
2446                }, None) => "uint256 a;",
2447                pt::Statement::VariableDefinition(loc!(), pt::VariableDeclaration {
2448                    loc: loc!(),
2449                    ty: expr_ty!(uint256),
2450                    storage: None,
2451                    name: Some(id("a")),
2452                }, Some(expr!(0))) => "uint256 a = 0;",
2453
2454                pt::Statement::For(loc!(), None, None, None, Some(Box::new(stmt!({}))))
2455                    => "for (;;) {}",
2456                pt::Statement::For(loc!(), Some(Box::new(pt::Statement::VariableDefinition(
2457                    loc!(),
2458                    pt::VariableDeclaration {
2459                        loc: loc!(),
2460                        ty: expr_ty!(uint256),
2461                        storage: None,
2462                        name: Some(id("a")),
2463                    },
2464                    None
2465                ))), None, None, Some(Box::new(stmt!({}))))
2466                    => "for (uint256 a;;) {}",
2467                pt::Statement::For(loc!(), None, Some(Box::new(expr!(true))), None, Some(Box::new(stmt!({}))))
2468                    => "for (; true;) {}",
2469                pt::Statement::For(
2470                    loc!(),
2471                    None,
2472                    Some(Box::new(expr!(true))),
2473                    Some(Box::new(expr!(++i))),
2474                    Some(Box::new(stmt!({})))
2475                ) => "for (; true; ++i) {}",
2476
2477                pt::Statement::DoWhile(loc!(), Box::new(stmt!({})), expr!(true))
2478                    => "do {} while (true);",
2479
2480                pt::Statement::Continue(loc!()) => "continue;",
2481                pt::Statement::Break(loc!()) => "break;",
2482
2483                pt::Statement::Return(loc!(), None) => "return;",
2484                pt::Statement::Return(loc!(), Some(expr!(true))) => "return true;",
2485
2486                pt::Statement::Revert(loc!(), None, vec![]) => "revert();",
2487                pt::Statement::Revert(loc!(), None, vec![expr!("error")])
2488                    => "revert(\"error\");",
2489                pt::Statement::Revert(loc!(), Some(idp!("my", "error")), vec![expr!("error")])
2490                    => "revert my.error(\"error\");",
2491
2492                pt::Statement::RevertNamedArgs(loc!(), None, vec![]) => "revert();",
2493                pt::Statement::RevertNamedArgs(loc!(), None, vec![pt::NamedArgument {
2494                    loc: loc!(),
2495                    name: id("name"),
2496                    expr: expr!(value),
2497                }]) => "revert({name: value});",
2498                pt::Statement::RevertNamedArgs(loc!(), Some(idp!("my", "error")), vec![pt::NamedArgument {
2499                    loc: loc!(),
2500                    name: id("name"),
2501                    expr: expr!(value),
2502                }]) => "revert my.error({name: value});",
2503
2504                pt::Statement::Emit(loc!(), expr!(true)) => "emit true;",
2505
2506                pt::Statement::Try(loc!(), expr!(true), None, vec![]) => "try true",
2507                pt::Statement::Try(loc!(), expr!(true), None, vec![pt::CatchClause::Simple(loc!(), None, stmt!({}))])
2508                    => "try true catch {}",
2509                pt::Statement::Try(loc!(), expr!(true), Some((vec![], Box::new(stmt!({})))), vec![])
2510                    => "try true returns () {}",
2511                pt::Statement::Try(
2512                    loc!(),
2513                    expr!(true),
2514                    Some((vec![], Box::new(stmt!({})))),
2515                    vec![pt::CatchClause::Simple(loc!(), None, stmt!({}))]
2516                ) => "try true returns () {} catch {}",
2517                pt::Statement::Try(
2518                    loc!(),
2519                    expr!(true),
2520                    Some((vec![(loc!(), Some(param!(uint256 a)))], Box::new(stmt!({})))),
2521                    vec![pt::CatchClause::Simple(loc!(), None, stmt!({}))]
2522                ) => "try true returns (uint256 a) {} catch {}",
2523            }
2524
2525            pt::StorageLocation: {
2526                pt::StorageLocation::Memory(loc!()) => "memory",
2527                pt::StorageLocation::Storage(loc!()) => "storage",
2528                pt::StorageLocation::Calldata(loc!()) => "calldata",
2529                pt::StorageLocation::Transient(loc!()) => "transient",
2530            }
2531
2532            pt::Type: {
2533                pt::Type::Address => "address",
2534                pt::Type::AddressPayable => "address payable",
2535                pt::Type::Payable => "payable",
2536                pt::Type::Bool => "bool",
2537                pt::Type::String => "string",
2538                pt::Type::Int(256) => "int256",
2539                pt::Type::Uint(256) => "uint256",
2540                pt::Type::Bytes(32) => "bytes32",
2541                pt::Type::Rational => "fixed",
2542                pt::Type::DynamicBytes => "bytes",
2543
2544                pt::Type::Mapping {
2545                    loc: loc!(),
2546                    key: Box::new(expr_ty!(uint256)),
2547                    key_name: None,
2548                    value: Box::new(expr_ty!(uint256)),
2549                    value_name: None,
2550                } => "mapping(uint256 => uint256)",
2551                pt::Type::Mapping {
2552                    loc: loc!(),
2553                    key: Box::new(expr_ty!(uint256)),
2554                    key_name: Some(id("key")),
2555                    value: Box::new(expr_ty!(uint256)),
2556                    value_name: None,
2557                } => "mapping(uint256 key => uint256)",
2558                pt::Type::Mapping {
2559                    loc: loc!(),
2560                    key: Box::new(expr_ty!(uint256)),
2561                    key_name: Some(id("key")),
2562                    value: Box::new(expr_ty!(uint256)),
2563                    value_name: Some(id("value")),
2564                } => "mapping(uint256 key => uint256 value)",
2565
2566                pt::Type::Function {
2567                    params: vec![],
2568                    attributes: vec![],
2569                    returns: None
2570                } => "function ()",
2571                pt::Type::Function {
2572                    params: vec![(loc!(), Some(param!(uint256)))],
2573                    attributes: vec![],
2574                    returns: None
2575                } => "function (uint256)",
2576                pt::Type::Function {
2577                    params: vec![(loc!(), Some(param!(uint256))), (loc!(), Some(param!(address)))],
2578                    attributes: vec![],
2579                    returns: None
2580                } => "function (uint256, address)",
2581                pt::Type::Function {
2582                    params: vec![(loc!(), Some(param!(uint256)))],
2583                    attributes: vec![pt::FunctionAttribute::Virtual(loc!())],
2584                    returns: None
2585                } => "function (uint256) virtual",
2586                pt::Type::Function {
2587                    params: vec![(loc!(), Some(param!(uint256)))],
2588                    attributes: vec![pt::FunctionAttribute::Virtual(loc!()), pt::FunctionAttribute::Override(loc!(), vec![])],
2589                    returns: None
2590                } => "function (uint256) virtual override",
2591                pt::Type::Function {
2592                    params: vec![(loc!(), Some(param!(uint256)))],
2593                    attributes: vec![pt::FunctionAttribute::Virtual(loc!()), pt::FunctionAttribute::Override(loc!(), vec![idp!["a", "b"]])],
2594                    returns: None
2595                } => "function (uint256) virtual override(a.b)",
2596                pt::Type::Function {
2597                    params: vec![(loc!(), Some(param!(uint256)))],
2598                    attributes: vec![],
2599                    returns: Some((vec![], vec![])),
2600                } => "function (uint256)",
2601                pt::Type::Function {
2602                    params: vec![(loc!(), Some(param!(uint256)))],
2603                    attributes: vec![],
2604                    returns: Some((vec![(loc!(), Some(param!(uint256)))], vec![])),
2605                } => "function (uint256) returns (uint256)",
2606                pt::Type::Function {
2607                    params: vec![(loc!(), Some(param!(uint256)))],
2608                    attributes: vec![],
2609                    returns: Some((vec![(loc!(), Some(param!(uint256))), (loc!(), Some(param!(address)))], vec![])),
2610                } => "function (uint256) returns (uint256, address)",
2611            }
2612
2613            pt::UserDefinedOperator: {
2614                pt::UserDefinedOperator::BitwiseAnd => "&",
2615                pt::UserDefinedOperator::BitwiseNot => "~",
2616                pt::UserDefinedOperator::Negate => "-",
2617                pt::UserDefinedOperator::BitwiseOr => "|",
2618                pt::UserDefinedOperator::BitwiseXor => "^",
2619                pt::UserDefinedOperator::Add => "+",
2620                pt::UserDefinedOperator::Divide => "/",
2621                pt::UserDefinedOperator::Modulo => "%",
2622                pt::UserDefinedOperator::Multiply => "*",
2623                pt::UserDefinedOperator::Subtract => "-",
2624                pt::UserDefinedOperator::Equal => "==",
2625                pt::UserDefinedOperator::More => ">",
2626                pt::UserDefinedOperator::MoreEqual => ">=",
2627                pt::UserDefinedOperator::Less => "<",
2628                pt::UserDefinedOperator::LessEqual => "<=",
2629                pt::UserDefinedOperator::NotEqual => "!=",
2630            }
2631
2632            pt::UsingList: {
2633                pt::UsingList::Library(idp!("id", "path")) => "id.path",
2634
2635                pt::UsingList::Functions(vec![]) => "{}",
2636                pt::UsingList::Functions(vec![
2637                    pt::UsingFunction {
2638                        loc: loc!(),
2639                        path: idp!["id", "path"],
2640                        oper: None,
2641                    },
2642                    pt::UsingFunction {
2643                        loc: loc!(),
2644                        path: idp!["id", "path"],
2645                        oper: Some(pt::UserDefinedOperator::Add),
2646                }]) => "{id.path, id.path as +}",
2647            }
2648
2649            pt::VariableAttribute: {
2650                pt::VariableAttribute::Constant(loc!()) => "constant",
2651                pt::VariableAttribute::Immutable(loc!()) => "immutable",
2652
2653                pt::VariableAttribute::Override(loc!(), vec![]) => "override",
2654                pt::VariableAttribute::Override(loc!(), vec![idp!["a", "b"]]) => "override(a.b)",
2655                pt::VariableAttribute::Override(loc!(), vec![idp!["a", "b"], idp!["c", "d"]])
2656                    => "override(a.b, c.d)",
2657            }
2658
2659            pt::Visibility: {
2660                pt::Visibility::Public(Some(loc!())) => "public",
2661                pt::Visibility::Internal(Some(loc!())) => "internal",
2662                pt::Visibility::Private(Some(loc!())) => "private",
2663                pt::Visibility::External(Some(loc!())) => "external",
2664            }
2665
2666            pt::YulExpression: {
2667                pt::YulExpression::BoolLiteral(loc!(), false, None) => "false",
2668                pt::YulExpression::BoolLiteral(loc!(), true, None) => "true",
2669                pt::YulExpression::BoolLiteral(loc!(), false, Some(id("name"))) => "false: name",
2670                pt::YulExpression::BoolLiteral(loc!(), true, Some(id("name"))) => "true: name",
2671
2672                pt::YulExpression::NumberLiteral(loc!(), "1234".into(), "".into(), None) => "1234",
2673                pt::YulExpression::NumberLiteral(loc!(), "1234".into(), "9".into(), None) => "1234e9",
2674                pt::YulExpression::NumberLiteral(loc!(), "1234".into(), "".into(), Some(id("name"))) => "1234: name",
2675                pt::YulExpression::NumberLiteral(loc!(), "1234".into(), "9".into(), Some(id("name"))) => "1234e9: name",
2676
2677                pt::YulExpression::HexNumberLiteral(loc!(), "0x1234".into(), None) => "0x1234",
2678                pt::YulExpression::HexNumberLiteral(loc!(), "0x1234".into(), Some(id("name"))) => "0x1234: name",
2679
2680                pt::YulExpression::HexStringLiteral(lit!(hex "1234"), None) => "hex\"1234\"",
2681                pt::YulExpression::HexStringLiteral(lit!(hex "1234"), Some(id("name"))) => "hex\"1234\": name",
2682
2683                pt::YulExpression::StringLiteral(lit!("1234"), None) => "\"1234\"",
2684                pt::YulExpression::StringLiteral(lit!("1234"), Some(id("name"))) => "\"1234\": name",
2685
2686                pt::YulExpression::Variable(id("name")) => "name",
2687
2688                pt::YulExpression::FunctionCall(Box::new(pt::YulFunctionCall {
2689                    loc: loc!(),
2690                    id: id("name"),
2691                    arguments: vec![],
2692                })) => "name()",
2693
2694                pt::YulExpression::SuffixAccess(loc!(), Box::new(yexpr!(struct)), id("access"))
2695                    => "struct.access",
2696            }
2697
2698            pt::YulStatement: {
2699                // rest tested individually
2700
2701                pt::YulStatement::Assign(loc!(), vec![yexpr!(var)], yexpr!(eq))
2702                    => "var := eq",
2703                pt::YulStatement::Assign(loc!(), vec![yexpr!(a), yexpr!(b)], yexpr!(eq))
2704                    => "a, b := eq",
2705
2706                pt::YulStatement::VariableDeclaration(loc!(), vec![yid!(var)], None)
2707                    => "let var",
2708                pt::YulStatement::VariableDeclaration(loc!(), vec![yid!(a), yid!(b)], None)
2709                    => "let a, b",
2710                pt::YulStatement::VariableDeclaration(loc!(), vec![yid!(var)], Some(yexpr!(eq)))
2711                    => "let var := eq",
2712                pt::YulStatement::VariableDeclaration(loc!(), vec![yid!(a), yid!(b)], Some(yexpr!(eq)))
2713                    => "let a, b := eq",
2714
2715                pt::YulStatement::If(loc!(), yexpr!(expr), yul_block()) => "if expr {}",
2716
2717                pt::YulStatement::Leave(loc!()) => "leave",
2718                pt::YulStatement::Break(loc!()) => "break",
2719                pt::YulStatement::Continue(loc!()) => "continue",
2720            }
2721
2722            pt::YulSwitchOptions: {
2723                pt::YulSwitchOptions::Case(loc!(), yexpr!(expr), yul_block()) => "case expr {}",
2724                pt::YulSwitchOptions::Default(loc!(), yul_block()) => "default {}",
2725            }
2726        ];
2727    }
2728}