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