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