Skip to main content

hax_rust_engine/backends/
rust.rs

1//! A Rust backend (and printer) for hax.
2
3use super::prelude::*;
4use crate::ast::identifiers::global_id::view::{PathSegment, View};
5use std::cell::RefCell;
6
7mod renamings;
8
9/// The Rust printer.
10#[setup_printer_struct]
11#[derive(Default, Clone)]
12pub struct RustPrinter {
13    current_namespace: RefCell<Option<Vec<String>>>,
14}
15
16impl Printer for RustPrinter {
17    const NAME: &str = "Rust";
18}
19
20impl RenderView for RustPrinter {
21    fn render_path_segment(&self, seg: &PathSegment) -> Vec<String> {
22        if let AnyKind::Constructor(constructor_kind) = seg.kind() {
23            match constructor_kind {
24                global_id::view::ConstructorKind::Constructor { ty } => {
25                    if let global_id::view::TypeDefKind::Struct = ty.kind() {
26                        return vec![
27                            self.render_path_segment_payload(ty.lift().payload())
28                                .to_string(),
29                        ];
30                    }
31                }
32            }
33        };
34        default::render_path_segment(self, seg)
35    }
36    fn render(&self, view: &View) -> Rendered {
37        let (module_path, relative_path) = view.split_at_module();
38        let path_segment = |seg| self.render_path_segment(seg);
39        let mut rendered = Rendered {
40            module: module_path.iter().flat_map(path_segment).collect(),
41            path: relative_path.iter().flat_map(path_segment).collect(),
42        };
43        renamings::rename_rendered(&mut rendered);
44        rendered
45    }
46}
47
48/// The Rust backend.
49pub struct RustBackend;
50
51impl Backend for RustBackend {
52    type Printer = RustPrinter;
53
54    fn resugaring_phases() -> Vec<Box<dyn Resugaring>> {
55        vec![Box::new(FunctionsToConstants), Box::new(Tuples)]
56    }
57
58    fn module_path(&self, module: &Module) -> camino::Utf8PathBuf {
59        let printer = RustPrinter::default();
60        let path = <RustPrinter as RenderView>::module(&printer, &module.ident.view());
61        camino::Utf8PathBuf::from_iter(path).with_extension("rs")
62    }
63}
64
65const INDENT: isize = 4;
66
67#[prepend_associated_functions_with(install_pretty_helpers!(self: Self))]
68// Note: the `const` wrapping makes my IDE and LSP happy. Otherwise, I don't get
69// autocompletion of methods in the impl block below.
70const _: () = {
71    macro_rules! todo {
72        ($($tt:tt)*) => {
73            disambiguated_todo!($($tt)*)
74        };
75    }
76    macro_rules! line {
77        ($($tt:tt)*) => {
78            disambiguated_line!($($tt)*)
79        };
80    }
81    macro_rules! concat {
82        ($($tt:tt)*) => {
83            disambiguated_concat!($($tt)*)
84        };
85    }
86
87    macro_rules! sep {
88        ($l:expr, $it:expr, $r:expr, $sep:expr$(,)?) => {
89            docs![
90                intersperse!($it, docs![$sep, line!()]),
91                docs![","].flat_alt(nil!())
92            ]
93            .enclose(line_!(), line_!())
94            .nest(INDENT)
95            .enclose($l, $r)
96            .group()
97        };
98        ($l:expr, $it:expr, $r:expr$(,)?) => {
99            sep!($l, $it, $r, ",")
100        };
101    }
102
103    macro_rules! print_tuple {
104        ($into_docs:ident) => {{
105            let mut docs: Vec<_> = $into_docs.iter().map(|typ| docs![typ]).collect();
106            if docs.len() == 1 {
107                docs.push(nil![])
108            }
109            sep!("(", docs, ")")
110        }};
111    }
112
113    macro_rules! sep_opt {
114        (@$l:expr, $it:expr, $($rest:tt)*) => {
115            {
116                let mut it = $it.into_iter().peekable();
117                if it.peek().is_some() {
118                    sep!($l, it, $($rest)*)
119                } else {
120                    nil!()
121                }
122            }
123        };
124        ($l:expr, $it:expr, $($rest:tt)*) => {
125            sep_opt!(@$l, $it, $($rest)*)
126        };
127    }
128
129    macro_rules! block {
130        ($body:expr) => {
131            docs![line!(), $body, line!()].group().nest(INDENT).braces()
132        };
133    }
134
135    impl<'a, 'b> RustPrinter {
136        fn generic_params<A: Clone>(&'a self, generic_params: &'b [GenericParam]) -> DocBuilder<A> {
137            let generic_params = generic_params
138                .iter()
139                .filter(|p| !matches!(&p.kind, GenericParamKind::Lifetime if p.ident.0.to_string() == "_"))
140                .collect::<Vec<_>>();
141            sep_opt!("<", generic_params, ">")
142        }
143        fn where_clause<A: Clone>(&'a self, constraints: &'b [GenericConstraint]) -> DocBuilder<A> {
144            if constraints.is_empty() {
145                return nil!();
146            }
147            docs![
148                line!(),
149                "where",
150                line!(),
151                intersperse!(constraints, docs![",", line!()])
152                    .nest(INDENT)
153                    .group(),
154                line!(),
155            ]
156            .nest(INDENT)
157            .group()
158        }
159        fn attributes<A: Clone>(&'a self, attrs: &'b [Attribute]) -> DocBuilder<A> {
160            concat!(
161                attrs
162                    .iter()
163                    .filter(|attr| match &attr.kind {
164                        AttributeKind::Tool { .. } | AttributeKind::Hax(_) => false,
165                        AttributeKind::DocComment { .. } => true,
166                    })
167                    .map(|attr| docs![attr, hardline!()])
168            )
169        }
170
171        fn id_name<A: Clone>(&'a self, id: GlobalId) -> DocBuilder<A> {
172            let view = id.view();
173            let path = <RustPrinter as RenderView>::render_strings(self, &view);
174            let name = path.last().unwrap().clone();
175            docs![if name == "_" {
176                "___empty_name".into()
177            } else {
178                name
179            }]
180        }
181    }
182
183    impl<A: Clone + 'static> PrettyAst<A> for RustPrinter {
184        const NAME: &'static str = "Rust";
185
186        fn module(&self, module: &Module) -> DocBuilder<A> {
187            let previous = self.current_namespace.borrow().clone();
188            let view = module.ident.view();
189            let module_path = <Self as RenderView>::module(self, &view);
190            *self.current_namespace.borrow_mut() = Some(module_path);
191            let doc = intersperse!(&module.items, docs![hardline!(), hardline!()]);
192            *self.current_namespace.borrow_mut() = previous;
193            doc
194        }
195
196        fn safety_kind(&self, safety_kind: &SafetyKind) -> DocBuilder<A> {
197            match safety_kind {
198                SafetyKind::Safe => nil!(),
199                SafetyKind::Unsafe => docs![text!("unsafe"), space!()],
200            }
201        }
202        fn param(&self, param: &Param) -> DocBuilder<A> {
203            docs![&param.pat, ":", space!(), &param.ty]
204        }
205        fn binding_mode(&self, binding_mode: &BindingMode) -> DocBuilder<A> {
206            match binding_mode {
207                BindingMode::ByRef(BorrowKind::Mut) => docs!["ref mut", space!()],
208                BindingMode::ByRef(_) => docs!["ref", space!()],
209                _ => nil!(),
210            }
211        }
212        fn pat(&self, pat: &Pat) -> DocBuilder<A> {
213            match &*pat.kind {
214                PatKind::Wild => docs!["_"],
215                PatKind::Ascription { pat, ty } => docs![pat, ":", space!(), ty],
216                PatKind::Or { sub_pats } => {
217                    intersperse!(sub_pats, docs![line!(), "|", line!()])
218                }
219                PatKind::Array { args } => sep!("[", args, "]", "|"),
220                PatKind::Deref { sub_pat } => docs!["&", sub_pat],
221                PatKind::Constant { lit } => docs![lit],
222                PatKind::Binding {
223                    mutable,
224                    var,
225                    mode,
226                    sub_pat,
227                } => {
228                    docs![
229                        if *mutable {
230                            docs!["mut", space!()]
231                        } else {
232                            nil!()
233                        },
234                        mode,
235                        var,
236                        sub_pat.as_ref().map(|pat| docs!["@", docs![pat]]),
237                    ]
238                }
239                PatKind::Construct { .. } => todo!("resugaring"),
240                PatKind::Resugared(resugared_pat_kind) => docs![resugared_pat_kind],
241                PatKind::Error(_) => todo!("resugaring"),
242            }
243        }
244        fn primitive_ty(&self, primitive_ty: &PrimitiveTy) -> DocBuilder<A> {
245            match primitive_ty {
246                PrimitiveTy::Bool => docs!["bool"],
247                PrimitiveTy::Int(int_kind) => docs![int_kind],
248                PrimitiveTy::Float(float_kind) => docs![float_kind],
249                PrimitiveTy::Char => docs!["char"],
250                PrimitiveTy::Str => docs!["str"],
251            }
252        }
253        fn int_kind(&self, int_kind: &IntKind) -> DocBuilder<A> {
254            docs![match (&int_kind.signedness, &int_kind.size) {
255                (Signedness::Signed, IntSize::S8) => "i8",
256                (Signedness::Signed, IntSize::S16) => "i16",
257                (Signedness::Signed, IntSize::S32) => "i32",
258                (Signedness::Signed, IntSize::S64) => "i64",
259                (Signedness::Signed, IntSize::S128) => "i128",
260                (Signedness::Signed, IntSize::SSize) => "isize",
261                (Signedness::Unsigned, IntSize::S8) => "u8",
262                (Signedness::Unsigned, IntSize::S16) => "u16",
263                (Signedness::Unsigned, IntSize::S32) => "u32",
264                (Signedness::Unsigned, IntSize::S64) => "u64",
265                (Signedness::Unsigned, IntSize::S128) => "u128",
266                (Signedness::Unsigned, IntSize::SSize) => "usize",
267            }]
268        }
269        fn generic_param(&self, generic_param: &GenericParam) -> DocBuilder<A> {
270            docs![
271                match &generic_param.kind {
272                    GenericParamKind::Const { .. } => docs!["const", space!()],
273                    _ => nil!(),
274                },
275                &generic_param.ident,
276                match &generic_param.kind {
277                    GenericParamKind::Const { ty } => docs![":", space!(), ty],
278                    _ => nil!(),
279                }
280            ]
281        }
282        fn generic_constraint(&self, generic_constraint: &GenericConstraint) -> DocBuilder<A> {
283            match generic_constraint {
284                GenericConstraint::Lifetime(s) => docs![s.clone()],
285                GenericConstraint::TypeClass(impl_ident) => docs![impl_ident],
286                GenericConstraint::Equality(projection_predicate) => docs![projection_predicate],
287            }
288        }
289        fn impl_ident(&self, impl_ident: &ImplIdent) -> DocBuilder<A> {
290            let trait_goal = &impl_ident.goal;
291            let [self_ty, args @ ..] = &trait_goal.args[..] else {
292                panic!()
293            };
294            docs![
295                self_ty,
296                space!(),
297                ":",
298                space!(),
299                &trait_goal.trait_,
300                sep_opt!("<", args, ">"),
301            ]
302        }
303
304        fn ty(&self, ty: &Ty) -> DocBuilder<A> {
305            match ty.kind() {
306                TyKind::Primitive(primitive_ty) => docs![primitive_ty],
307                // TyKind::Tuple(items) => intersperse!(items, docs![",", line!()])
308                //     .nest(INDENT)
309                //     .group(),
310                TyKind::App { head, args } => docs![head, sep_opt!("<", args, ">")],
311                TyKind::Arrow { inputs, output } => {
312                    docs!["fn", sep!("(", inputs, ")"), reflow!(" -> "), output]
313                }
314                TyKind::Ref {
315                    inner,
316                    mutable,
317                    region: _,
318                } => docs![
319                    "&",
320                    if *mutable {
321                        docs!["mut", space!()]
322                    } else {
323                        nil!()
324                    },
325                    inner
326                ],
327                TyKind::Param(local_id) => docs![local_id],
328                TyKind::Slice(ty) => docs![ty].brackets(),
329                TyKind::Array { ty, length } => {
330                    docs![ty, ";", space!(), length.as_ref()].brackets()
331                }
332                TyKind::RawPointer => todo!(),
333                TyKind::AssociatedType { impl_, item } => docs![impl_, "::", item],
334                TyKind::Opaque(global_id) => docs![global_id],
335                TyKind::Dyn(dyn_trait_goals) => docs![
336                    "dyn",
337                    docs![
338                        line!(),
339                        intersperse!(dyn_trait_goals, docs![line!(), "+", space!()])
340                    ]
341                    .group()
342                    .hang(0)
343                ],
344                TyKind::Resugared(resugared_ty_kind) => docs![resugared_ty_kind],
345                TyKind::Error(_) => todo!("resugaring"),
346            }
347        }
348        fn resugared_ty_kind(&self, resugared_ty_kind: &ResugaredTyKind) -> DocBuilder<A> {
349            match resugared_ty_kind {
350                ResugaredTyKind::Tuple(types) => print_tuple!(types),
351            }
352        }
353        fn literal(&self, literal: &Literal) -> DocBuilder<A> {
354            match literal {
355                Literal::String(symbol) => docs![symbol],
356                Literal::Char(ch) => text!(format!("{}", ch)),
357                Literal::Bool(b) => text!(format!("{}", b)),
358                Literal::Int {
359                    value,
360                    negative,
361                    kind,
362                } => docs![if *negative { docs!["-"] } else { nil!() }, value, kind],
363                Literal::Float {
364                    value,
365                    negative,
366                    kind,
367                } => docs![if *negative { docs!["-"] } else { nil!() }, value, kind],
368            }
369        }
370        fn trait_goal(&self, trait_goal: &TraitGoal) -> DocBuilder<A> {
371            let [self_ty, args @ ..] = &trait_goal.args[..] else {
372                panic!()
373            };
374            docs![
375                self_ty,
376                space!(),
377                "as",
378                space!(),
379                &trait_goal.trait_,
380                sep_opt!("<", args, ">"),
381            ]
382            .enclose("<", ">")
383        }
384        fn generic_value(&self, generic_value: &GenericValue) -> DocBuilder<A> {
385            match generic_value {
386                GenericValue::Ty(ty) => docs![ty],
387                GenericValue::Expr(expr) => docs![expr],
388                GenericValue::Lifetime => docs!["'_"],
389            }
390        }
391        fn arm(&self, arm: &Arm) -> DocBuilder<A> {
392            docs![
393                &arm.pat,
394                arm.guard.as_ref().map(|guard| docs!["if", space!(), guard]),
395                reflow!(" => "),
396                block![&arm.body],
397            ]
398        }
399        fn expr(&self, expr: &Expr) -> DocBuilder<A> {
400            match &*expr.kind {
401                ExprKind::If {
402                    condition,
403                    then,
404                    else_,
405                } => docs![
406                    "if",
407                    space!(),
408                    docs![condition].parens(),
409                    space!(),
410                    block![then],
411                    else_
412                        .as_ref()
413                        .map(|doc| docs![reflow!(" else "), block![doc]])
414                        .unwrap_or(nil!())
415                ],
416                ExprKind::App {
417                    head,
418                    args,
419                    generic_args,
420                    bounds_impls: _, // this is implicit in Rust
421                    trait_,
422                } => {
423                    mod names {
424                        pub use crate::names::rust_primitives::hax::{
425                            cast_op, deref_op, logical_op_and, logical_op_or,
426                        };
427                    }
428                    use ExprKind::GlobalId;
429                    match (&*head.kind, &args[..]) {
430                        (GlobalId(names::deref_op), [reference]) => {
431                            Some(docs!["*", docs![reference].parens()])
432                        }
433                        (GlobalId(names::cast_op), [value]) => {
434                            Some(docs![docs![value].parens(), reflow!(" as "), &expr.ty])
435                        }
436                        (GlobalId(names::logical_op_and), [lhs, rhs]) => Some(docs![
437                            docs![lhs].parens(),
438                            reflow!(" && "),
439                            docs![rhs].parens()
440                        ]),
441                        (GlobalId(names::logical_op_or), [lhs, rhs]) => Some(docs![
442                            docs![lhs].parens(),
443                            reflow!(" || "),
444                            docs![rhs].parens()
445                        ]),
446                        _ => None,
447                    }
448                    .unwrap_or_else(|| match (trait_, &*head.kind) {
449                        (Some((trait_impl_expr, _trait_args)), GlobalId(head)) => {
450                            docs![
451                                &trait_impl_expr.goal,
452                                "::",
453                                self.id_name(*head),
454                                sep_opt!("::<", generic_args, ">"),
455                                sep!("(", args, ")")
456                            ]
457                        }
458                        _ => docs![
459                            head,
460                            sep_opt!("::<", generic_args, ">"),
461                            sep!("(", args, ")")
462                        ],
463                    })
464                }
465                ExprKind::Literal(literal) => docs![literal],
466                ExprKind::Array(exprs) => sep!("[", exprs, "]"),
467                ExprKind::Construct {
468                    constructor,
469                    is_record,
470                    fields,
471                    // TODO: complete constructors with base
472                    ..
473                } => {
474                    let payload = fields.iter().map(|(id, value)| {
475                        docs![
476                            if *is_record {
477                                docs![id, ":", space!()]
478                            } else {
479                                nil!()
480                            },
481                            value
482                        ]
483                    });
484                    docs![
485                        constructor,
486                        if *is_record {
487                            sep!("{", payload, "}")
488                        } else {
489                            sep!("(", payload, ")")
490                        }
491                    ]
492                }
493                ExprKind::Match { scrutinee, arms } => {
494                    docs![
495                        "match",
496                        space!(),
497                        scrutinee,
498                        space!(),
499                        block!(intersperse!(arms, hardline!())),
500                    ]
501                }
502                ExprKind::Borrow { mutable, inner } => {
503                    docs!["&", if *mutable { reflow!["mut "] } else { nil!() }, inner]
504                }
505                ExprKind::AddressOf { mutable, inner } => docs![
506                    inner,
507                    reflow!(" as *"),
508                    if *mutable { reflow!["mut "] } else { nil!() },
509                    docs![&expr.ty]
510                ]
511                .parens(),
512                ExprKind::Let { lhs, rhs, body } => docs![
513                    "let",
514                    space!(),
515                    lhs,
516                    space!(),
517                    "=",
518                    docs![line!(), rhs].group().nest(INDENT),
519                    ";",
520                    hardline!(),
521                    body
522                ],
523                ExprKind::GlobalId(global_id) => docs![global_id],
524                ExprKind::LocalId(local_id) => docs![local_id],
525                ExprKind::Ascription { e, ty } => docs![e, ":", space!(), ty].parens(),
526                ExprKind::Assign { lhs, value } => docs![lhs, space!(), "=", space!(), value],
527                ExprKind::Loop {
528                    body,
529                    kind,
530                    state: None,
531                    control_flow: None,
532                    label: None,
533                } => match &**kind {
534                    LoopKind::UnconditionalLoop => docs!["loop", space!(), block![body]],
535                    LoopKind::WhileLoop { condition } => {
536                        docs!["while", space!(), condition, space!(), block![body]]
537                    }
538                    LoopKind::ForLoop { pat, iterator } => {
539                        docs![
540                            "for",
541                            space!(),
542                            pat,
543                            reflow!(" in "),
544                            iterator,
545                            space!(),
546                            block![body]
547                        ]
548                    }
549                    LoopKind::ForIndexLoop {
550                        start,
551                        end,
552                        var,
553                        var_ty: _,
554                    } => docs![
555                        "for",
556                        space!(),
557                        var,
558                        reflow!(" in "),
559                        start,
560                        "..",
561                        end,
562                        space!(),
563                        block![body]
564                    ],
565                },
566                ExprKind::Loop { .. } => {
567                    todo!("loop with explicit state or with a label")
568                }
569                ExprKind::Break {
570                    value, label: None, ..
571                } => docs!["break", space!(), value],
572                ExprKind::Break { .. } => todo!("break with a label"),
573                ExprKind::Return { value } => docs!["return", space!(), value],
574                ExprKind::Continue { label: None, .. } => docs!["continue"],
575                ExprKind::Continue { .. } => todo!("continue with a label"),
576                ExprKind::Closure {
577                    params,
578                    body,
579                    captures: _,
580                } => docs![
581                    intersperse!(params, docs![",", space!()]).enclose("|", "|"),
582                    body
583                ],
584                ExprKind::Block { body, safety_mode } => {
585                    docs![safety_mode, block![body]]
586                }
587                ExprKind::Quote { contents } => docs![contents],
588                ExprKind::Resugared(resugared_expr_kind) => docs![resugared_expr_kind],
589                ExprKind::Error { .. } => todo!("resugaring"),
590            }
591        }
592        fn resugared_expr_kind(&self, resugared_expr_kind: &ResugaredExprKind) -> DocBuilder<A> {
593            match resugared_expr_kind {
594                ResugaredExprKind::Tuple(values) => print_tuple!(values),
595                ResugaredExprKind::LetPure { .. } => unreachable!("LetPure resugaring not active"),
596            }
597        }
598
599        fn lhs(&self, lhs: &Lhs) -> DocBuilder<A> {
600            match lhs {
601                Lhs::LocalVar { var, ty: _ } => docs![var],
602                Lhs::VecRef { e, .. } => docs![e],
603                Lhs::ArbitraryExpr(expr) => docs![std::ops::Deref::deref(expr)],
604                Lhs::FieldAccessor { e, ty: _, field } => {
605                    docs![std::ops::Deref::deref(e), ".", field]
606                }
607                Lhs::ArrayAccessor { e, ty: _, index } => {
608                    docs![std::ops::Deref::deref(e), docs!(index).brackets()]
609                }
610            }
611        }
612        fn global_id(&self, global_id: &GlobalId) -> DocBuilder<A> {
613            let view = global_id.view();
614            let module = <Self as RenderView>::module(self, &view);
615            if Some(module) == *self.current_namespace.borrow() {
616                let rendered = self.render(&view);
617                docs![rendered.path.join("::")]
618            } else {
619                docs![self.render_string(&view)]
620            }
621        }
622        fn variant(&self, variant: &Variant) -> DocBuilder<A> {
623            let payload = variant.arguments.iter().map(|(id, ty, attrs)| {
624                docs![
625                    self.attributes(attrs),
626                    if variant.is_record {
627                        docs![id, ":", space!()]
628                    } else {
629                        nil!()
630                    },
631                    ty
632                ]
633            });
634
635            if variant.is_record {
636                sep!("{", payload, "}")
637            } else {
638                sep!("(", payload, ")")
639            }
640        }
641        fn item(&self, item: &Item) -> DocBuilder<A> {
642            docs![&item.meta, item.kind()]
643        }
644        fn resugared_item_kind(&self, resugared_item_kind: &ResugaredItemKind) -> DocBuilder<A> {
645            match resugared_item_kind {
646                ResugaredItemKind::Constant { name, body, .. } => {
647                    docs![
648                        "const",
649                        space!(),
650                        self.id_name(*name),
651                        ":",
652                        space!(),
653                        &body.ty,
654                        reflow!(" = "),
655                        docs![body].braces(),
656                        ";"
657                    ]
658                }
659                ResugaredItemKind::RecursiveFn { .. } => {
660                    unreachable!("The Rust backend does not use the RecursiveFn resugaring")
661                }
662            }
663        }
664        fn item_kind(&self, item_kind: &ItemKind) -> DocBuilder<A> {
665            match item_kind {
666                ItemKind::Fn {
667                    name,
668                    generics,
669                    body,
670                    params,
671                    safety,
672                } => {
673                    docs![
674                        safety,
675                        text!("fn"),
676                        space!(),
677                        self.id_name(*name),
678                        self.generic_params(&generics.params),
679                        sep!("(", params, ")"),
680                        reflow!(" -> "),
681                        &body.ty,
682                        space!(),
683                        self.where_clause(&generics.constraints),
684                        block![body]
685                    ]
686                }
687                ItemKind::TyAlias {
688                    name,
689                    generics: _,
690                    ty,
691                } => docs!["type", space!(), name, space!(), "=", space!(), ty, ";"],
692                ItemKind::Type {
693                    name,
694                    generics,
695                    variants,
696                    is_struct,
697                } => match &variants[..] {
698                    [variant] if *is_struct => {
699                        docs![
700                            "struct",
701                            space!(),
702                            self.id_name(*name),
703                            self.generic_params(&generics.params),
704                            variant,
705                            if variant.is_record {
706                                nil!()
707                            } else {
708                                docs![";"]
709                            }
710                        ]
711                    }
712                    _ => {
713                        docs![
714                            "enum",
715                            space!(),
716                            self.id_name(*name),
717                            self.generic_params(&generics.params),
718                            sep!(
719                                "{",
720                                variants.iter().map(|variant| docs![
721                                    &variant.name,
722                                    space!(),
723                                    variant
724                                ]),
725                                "}",
726                            ),
727                            self.where_clause(&generics.constraints),
728                        ]
729                    }
730                },
731                ItemKind::Trait {
732                    name,
733                    generics,
734                    items,
735                    safety: _,
736                } => docs![
737                    "trait",
738                    space!(),
739                    self.id_name(*name),
740                    self.generic_params(&generics.params),
741                    self.where_clause(&generics.constraints),
742                    sep!("{", items, "}", nil!()),
743                ],
744                ItemKind::Impl {
745                    generics,
746                    self_ty,
747                    of_trait: (trait_, trait_args),
748                    items,
749                    parent_bounds: _,
750                } => docs![
751                    "impl",
752                    self.generic_params(&generics.params),
753                    space!(),
754                    trait_,
755                    sep_opt!("<", trait_args[1..], ">"),
756                    space!(),
757                    "for",
758                    space!(),
759                    self_ty,
760                    self.where_clause(&generics.constraints),
761                    sep!("{", items, "}", nil!()),
762                ],
763                ItemKind::Alias { name, item } => {
764                    docs!["type", self.id_name(*name), reflow!(" = "), item, ";"]
765                }
766                ItemKind::RustModule | ItemKind::Use { .. } => nil!(),
767                ItemKind::Quote { quote, .. } => docs![quote],
768                ItemKind::Error { .. } => todo!("resugaring"),
769                ItemKind::Resugared(resugared_item_kind) => docs![resugared_item_kind],
770                ItemKind::NotImplementedYet => docs!["/* `NotImplementedYet` item */"],
771            }
772        }
773        fn impl_item(&self, impl_item: &ImplItem) -> DocBuilder<A> {
774            match &impl_item.kind {
775                ImplItemKind::Type {
776                    ty,
777                    parent_bounds: _,
778                } => docs![
779                    &impl_item.meta,
780                    reflow!("type "),
781                    self.id_name(impl_item.ident),
782                    reflow!(" = "),
783                    ty,
784                    ";"
785                ],
786                ImplItemKind::Fn { body, params } => docs![
787                    &impl_item.meta,
788                    text!("fn"),
789                    space!(),
790                    self.id_name(impl_item.ident),
791                    self.generic_params(&impl_item.generics.params),
792                    sep!("(", params, ")"),
793                    reflow!(" -> "),
794                    &body.ty,
795                    space!(),
796                    self.where_clause(&impl_item.generics.constraints),
797                    docs![line_!(), body, line_!(),].nest(INDENT).braces()
798                ],
799                ImplItemKind::Resugared(_resugared_impl_item_kind) => todo!(),
800                ImplItemKind::Error(_) => todo!(),
801            }
802        }
803        fn metadata(&self, metadata: &Metadata) -> DocBuilder<A> {
804            self.attributes(&metadata.attributes)
805        }
806        fn attribute(&self, attribute: &Attribute) -> DocBuilder<A> {
807            match &attribute.kind {
808                AttributeKind::Tool { .. } | AttributeKind::Hax(_) => nil!(),
809                AttributeKind::DocComment { kind, body } => match kind {
810                    DocCommentKind::Line => {
811                        intersperse!(
812                            body.lines().map(|line| docs![format!("/// {line}")]),
813                            hardline!()
814                        )
815                    }
816                    DocCommentKind::Block => {
817                        docs![
818                            "/**",
819                            intersperse!(body.lines().map(|line| line.to_string()), hardline!()),
820                            "*/"
821                        ]
822                    }
823                },
824            }
825        }
826    }
827};