flux_middle/
pretty.rs

1use std::{cell::RefCell, fmt};
2
3use flux_arc_interner::{Internable, Interned};
4use flux_common::index::IndexGen;
5use flux_config as config;
6use rustc_abi::FieldIdx;
7use rustc_data_structures::unord::UnordMap;
8use rustc_hash::FxHashSet;
9use rustc_hir::def_id::DefId;
10use rustc_index::newtype_index;
11use rustc_middle::ty::TyCtxt;
12use rustc_span::{Pos, Span};
13use rustc_type_ir::{BoundVar, DebruijnIndex, INNERMOST};
14use serde::Serialize;
15
16#[macro_export]
17macro_rules! _with_cx {
18    ($cx:expr, $e:expr) => {
19        $crate::pretty::WithCx::new($cx, $e)
20    };
21}
22pub use crate::_with_cx as with_cx;
23use crate::def_id::{FluxDefId, FluxLocalDefId};
24
25#[macro_export]
26macro_rules! _format_args_cx {
27    ($cx:ident, $fmt:literal, $($args:tt)*) => {
28        $crate::_format_args_cx!(@go ($cx, $fmt; $($args)*) -> ())
29    };
30    ($cx:expr, $fmt:literal) => {
31        format_args!($fmt)
32    };
33    (@go ($cx:ident, $fmt:literal; ^$head:expr, $($tail:tt)*) -> ($($accum:tt)*)) => {
34        $crate::_format_args_cx!(@go ($cx, $fmt; $($tail)*) -> ($($accum)* $head,))
35    };
36    (@go ($cx:ident, $fmt:literal; $head:expr, $($tail:tt)*) -> ($($accum:tt)*)) => {
37        $crate::_format_args_cx!(@go ($cx, $fmt; $($tail)*) -> ($($accum)* $crate::pretty::with_cx!($cx, $head),))
38    };
39    (@go ($cx:ident, $fmt:literal; ^$head:expr) -> ($($accum:tt)*)) => {
40        $crate::_format_args_cx!(@as_expr format_args!($fmt, $($accum)* $head,))
41    };
42    (@go ($cx:ident, $fmt:literal; $head:expr) -> ($($accum:tt)*)) => {
43        $crate::_format_args_cx!(@as_expr format_args!($fmt, $($accum)* $crate::pretty::with_cx!($cx, $head),))
44    };
45    (@as_expr $e:expr) => { $e };
46}
47pub use crate::_format_args_cx as format_args_cx;
48
49#[macro_export]
50macro_rules! _format_cx {
51    ($($arg:tt)*) => {
52        std::fmt::format($crate::_format_args_cx!($($arg)*))
53    }
54}
55pub use crate::_format_cx as format_cx;
56
57#[macro_export]
58macro_rules! _w {
59    ($cx:expr, $f:expr, $fmt:literal, $($args:tt)*) => {{
60        #[allow(unused_variables)]
61        let cx = $cx;
62        $f.write_fmt($crate::_format_args_cx!(cx, $fmt, $($args)*))
63    }};
64    ($cx:expr, $f:expr, $fmt:literal) => {
65        $f.write_fmt($crate::_format_args_cx!($cx, $fmt))
66    };
67}
68pub use crate::_w as w;
69
70#[macro_export]
71macro_rules! _join {
72    ($sep:expr, $iter:expr) => {
73        $crate::pretty::Join::new($sep, $iter)
74    };
75}
76pub use crate::_join as join;
77
78#[macro_export]
79macro_rules! _parens {
80    ($val:expr, $parenthesize:expr) => {
81        $crate::pretty::Parens::new(&$val, $parenthesize)
82    };
83}
84pub use crate::_parens as parens;
85
86#[macro_export]
87macro_rules! _impl_debug_with_default_cx {
88    ($($ty:ty $(=> $key:literal)?),* $(,)?) => {$(
89        impl std::fmt::Debug for $ty  {
90            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91                #[allow(unused_mut, unused_assignments)]
92                let mut key = None;
93                $(
94                    key = Some($key);
95                )?
96                $crate::pretty::pprint_with_default_cx(f, self, key)
97            }
98        }
99    )*};
100}
101
102pub fn pprint_with_default_cx<T: Pretty>(
103    f: &mut std::fmt::Formatter<'_>,
104    t: &T,
105    cfg_key: Option<&'static str>,
106) -> std::fmt::Result {
107    rustc_middle::ty::tls::with(|tcx| {
108        #[allow(unused_mut)]
109        let mut cx = <T>::default_cx(tcx);
110
111        if let Some(pprint) = flux_config::CONFIG_FILE
112            .get("dev")
113            .and_then(|dev| dev.get("pprint"))
114        {
115            if let Some(opts) = pprint.get("default") {
116                cx.merge(opts);
117            }
118
119            if let Some(key) = cfg_key
120                && let Some(opts) = pprint.get(key)
121            {
122                cx.merge(opts);
123            }
124        }
125
126        if let Some(key) = cfg_key
127            && let Some(opts) = flux_config::CONFIG_FILE
128                .get("dev")
129                .and_then(|dev| dev.get("pprint"))
130                .and_then(|pprint| pprint.get(key))
131        {
132            cx.merge(opts);
133        }
134        Pretty::fmt(t, &cx, f)
135    })
136}
137
138pub use crate::_impl_debug_with_default_cx as impl_debug_with_default_cx;
139use crate::{
140    global_env::GlobalEnv,
141    rty::{AdtSortDef, BoundReft, BoundReftKind, BoundVariableKind, EarlyReftParam},
142};
143
144#[derive(Copy, Clone)]
145pub enum KVarArgs {
146    All,
147    SelfOnly,
148    Hide,
149}
150
151#[derive(Clone, Copy)]
152pub enum GenvOrTcx<'genv, 'tcx> {
153    Genv(GlobalEnv<'genv, 'tcx>),
154    Tcx(TyCtxt<'tcx>),
155}
156
157impl<'genv, 'tcx> GenvOrTcx<'genv, 'tcx> {
158    fn tcx(self) -> TyCtxt<'tcx> {
159        match self {
160            GenvOrTcx::Genv(genv) => genv.tcx(),
161            GenvOrTcx::Tcx(tcx) => tcx,
162        }
163    }
164
165    fn genv(self) -> Option<GlobalEnv<'genv, 'tcx>> {
166        match self {
167            GenvOrTcx::Genv(genv) => Some(genv),
168            GenvOrTcx::Tcx(_) => None,
169        }
170    }
171}
172
173impl<'tcx> From<TyCtxt<'tcx>> for GenvOrTcx<'_, 'tcx> {
174    fn from(v: TyCtxt<'tcx>) -> Self {
175        Self::Tcx(v)
176    }
177}
178
179impl<'genv, 'tcx> From<GlobalEnv<'genv, 'tcx>> for GenvOrTcx<'genv, 'tcx> {
180    fn from(v: GlobalEnv<'genv, 'tcx>) -> Self {
181        Self::Genv(v)
182    }
183}
184
185pub struct PrettyCx<'genv, 'tcx> {
186    pub cx: GenvOrTcx<'genv, 'tcx>,
187    pub kvar_args: KVarArgs,
188    pub fully_qualified_paths: bool,
189    pub simplify_exprs: bool,
190    pub tags: bool,
191    pub bindings_chain: bool,
192    pub preds_chain: bool,
193    pub full_spans: bool,
194    pub hide_uninit: bool,
195    pub hide_refinements: bool,
196    pub hide_regions: bool,
197    pub hide_sorts: bool,
198    pub bvar_env: BoundVarEnv,
199    pub earlyparam_env: RefCell<Option<EarlyParamEnv>>,
200}
201
202macro_rules! set_opts {
203    ($cx:expr, $opts:expr, [$($opt:ident),+ $(,)?]) => {
204        $(
205        if let Some(val) = $opts.get(stringify!($opt)).and_then(|v| FromOpt::from_opt(v)) {
206            $cx.$opt = val;
207        }
208        )+
209    };
210}
211
212impl<'genv, 'tcx> PrettyCx<'genv, 'tcx> {
213    pub fn default(cx: impl Into<GenvOrTcx<'genv, 'tcx>>) -> Self {
214        PrettyCx {
215            cx: cx.into(),
216            kvar_args: KVarArgs::SelfOnly,
217            fully_qualified_paths: false,
218            simplify_exprs: true,
219            tags: true,
220            bindings_chain: true,
221            preds_chain: true,
222            full_spans: false,
223            hide_uninit: true,
224            hide_refinements: false,
225            hide_regions: false,
226            hide_sorts: true,
227            bvar_env: BoundVarEnv::default(),
228            earlyparam_env: RefCell::new(None),
229        }
230    }
231
232    pub fn tcx(&self) -> TyCtxt<'tcx> {
233        self.cx.tcx()
234    }
235
236    pub fn genv(&self) -> Option<GlobalEnv<'genv, 'tcx>> {
237        self.cx.genv()
238    }
239
240    pub fn adt_sort_def_of(&self, def_id: DefId) -> Option<AdtSortDef> {
241        self.genv()
242            .and_then(|genv| genv.adt_sort_def_of(def_id).ok())
243    }
244
245    pub fn merge(&mut self, opts: &config::Value) {
246        set_opts!(
247            self,
248            opts,
249            [
250                kvar_args,
251                fully_qualified_paths,
252                simplify_exprs,
253                tags,
254                bindings_chain,
255                preds_chain,
256                full_spans,
257                hide_uninit,
258                hide_refinements,
259                hide_regions,
260                hide_sorts,
261            ]
262        );
263    }
264
265    pub fn with_bound_vars<R>(&self, vars: &[BoundVariableKind], f: impl FnOnce() -> R) -> R {
266        self.bvar_env.push_layer(vars, FxHashSet::default(), None);
267        let r = f();
268        self.bvar_env.pop_layer();
269        r
270    }
271
272    pub fn with_bound_vars_removable<R1, R2>(
273        &self,
274        vars: &[BoundVariableKind],
275        vars_to_remove: FxHashSet<BoundVar>,
276        fn_root_layer_type: Option<FnRootLayerType>,
277        fmt_body: impl FnOnce(&mut String) -> Result<R1, fmt::Error>,
278        fmt_vars_with_body: impl FnOnce(R1, BoundVarLayer, String) -> Result<R2, fmt::Error>,
279    ) -> Result<R2, fmt::Error> {
280        self.bvar_env
281            .push_layer(vars, vars_to_remove, fn_root_layer_type);
282        let mut body = String::new();
283        let r1 = fmt_body(&mut body)?;
284        // We need to be careful when rendering the vars to _not_
285        // refer to the `vars_to_remove` in the context since it'll
286        // still be there. If we remove the layer, then the vars
287        // won't render accurately.
288        //
289        // For now, this should be fine, though.
290        let r2 = fmt_vars_with_body(r1, self.bvar_env.peek_layer().unwrap(), body)?;
291        self.bvar_env.pop_layer();
292        Ok(r2)
293    }
294
295    // pub fn with_fn_root_bound_vars<R1, R2>(
296    //     &self,
297    //     vars: &[BoundVariableKind],
298    //     vars_to_remove: FxHashSet<BoundVar>,
299    //     fn_root_layer_type: FnRootLayerType,
300    //     fmt_body: impl FnOnce(&mut String) -> Result<R1, fmt::Error>,
301    //     fmt_vars_with_body: impl FnOnce(R1, BoundVarLayer, String) -> Result<R2, fmt::Error>,
302    // ) -> Result<R2, fmt::Error> {
303    //     self.bvar_env.push_layer(vars, vars_to_remove, Some(fn_root_layer_type));
304    //     let r = f();
305    //     match self.bvar_env.pop_layer() {
306    //         Some(BoundVarLayer {layer_map: BoundVarLayerMap::FnRootLayerMap(fn_root_layer), ..}) => (r, fn_root_layer.seen_vars),
307    //         _ => unreachable!("The popped layer must exist and be an FnRootLayer"),
308    //     }
309    // }
310
311    pub fn fmt_bound_vars(
312        &self,
313        print_infer_mode: bool,
314        left: &str,
315        vars: &[BoundVariableKind],
316        right: &str,
317        f: &mut impl fmt::Write,
318    ) -> fmt::Result {
319        w!(self, f, "{left}")?;
320        for (i, var) in vars.iter().enumerate() {
321            if i > 0 {
322                w!(self, f, ", ")?;
323            }
324            match var {
325                BoundVariableKind::Region(re) => w!(self, f, "{:?}", re)?,
326                BoundVariableKind::Refine(sort, mode, BoundReftKind::Named(name)) => {
327                    if print_infer_mode {
328                        w!(self, f, "{}", ^mode.prefix_str())?;
329                    }
330                    w!(self, f, "{}", ^name)?;
331                    if !self.hide_sorts {
332                        w!(self, f, ": {:?}", sort)?;
333                    }
334                }
335                BoundVariableKind::Refine(sort, mode, BoundReftKind::Anon) => {
336                    if print_infer_mode {
337                        w!(self, f, "{}", ^mode.prefix_str())?;
338                    }
339                    if let Some(name) = self.bvar_env.lookup(INNERMOST, BoundVar::from_usize(i)) {
340                        w!(self, f, "{:?}", ^name)?;
341                    } else {
342                        w!(self, f, "_")?;
343                    }
344                    if !self.hide_sorts {
345                        w!(self, f, ": {:?}", sort)?;
346                    }
347                }
348            }
349        }
350        w!(self, f, "{right}")
351    }
352
353    pub fn fmt_bound_reft(
354        &self,
355        debruijn: DebruijnIndex,
356        breft: BoundReft,
357        f: &mut fmt::Formatter<'_>,
358    ) -> fmt::Result {
359        match breft.kind {
360            BoundReftKind::Anon => {
361                if let Some(name) = self.bvar_env.lookup(debruijn, breft.var) {
362                    w!(self, f, "{name:?}")
363                } else {
364                    w!(self, f, "⭡{}/#{:?}", ^debruijn.as_usize(), ^breft.var)
365                }
366            }
367            BoundReftKind::Named(name) => {
368                w!(self, f, "{name}")
369            }
370        }
371    }
372
373    pub fn with_early_params<R>(&self, f: impl FnOnce() -> R) -> R {
374        assert!(self.earlyparam_env.borrow().is_none(), "Already in an early param env");
375        *self.earlyparam_env.borrow_mut() = Some(FxHashSet::default());
376        let r = f();
377        *self.earlyparam_env.borrow_mut() = None;
378        r
379    }
380
381    pub fn kvar_args(self, kvar_args: KVarArgs) -> Self {
382        Self { kvar_args, ..self }
383    }
384
385    pub fn fully_qualified_paths(self, b: bool) -> Self {
386        Self { fully_qualified_paths: b, ..self }
387    }
388
389    pub fn hide_regions(self, b: bool) -> Self {
390        Self { hide_regions: b, ..self }
391    }
392
393    pub fn hide_sorts(self, b: bool) -> Self {
394        Self { hide_sorts: b, ..self }
395    }
396
397    pub fn hide_refinements(self, b: bool) -> Self {
398        Self { hide_refinements: b, ..self }
399    }
400}
401
402newtype_index! {
403    /// Name used during pretty printing to format anonymous bound variables
404    #[debug_format = "b{}"]
405    pub struct BoundVarName {}
406}
407
408#[derive(Copy, Clone)]
409pub enum FnRootLayerType {
410    FnArgs,
411    FnRet,
412}
413
414#[derive(Clone)]
415pub struct FnRootLayerMap {
416    pub name_map: UnordMap<BoundVar, BoundVarName>,
417    pub seen_vars: FxHashSet<BoundVar>,
418    pub layer_type: FnRootLayerType,
419}
420
421#[derive(Clone)]
422pub struct BoundVarLayer {
423    pub layer_map: BoundVarLayerMap,
424    pub vars_to_remove: FxHashSet<BoundVar>,
425    pub successfully_removed_vars: FxHashSet<BoundVar>,
426}
427
428#[derive(Clone)]
429pub enum BoundVarLayerMap {
430    LayerMap(UnordMap<BoundVar, BoundVarName>),
431    /// We treat vars at the function root differently. The UnordMap
432    /// functions the same as in a regular layer (i.e. giving names to
433    /// anonymous bound vars), but we additionally track a set of
434    /// boundvars that have been seen previously.
435    ///
436    /// This set is used to render a signature like
437    ///
438    ///     fn(usize[@n], usize[n]) -> usize[#m] ensures m > 0
439    ///
440    /// The first time we visit `n`, we'll add the `@`, but the second
441    /// time we'll track that we've seen it and won't.
442    ///
443    /// We do the same thing for `m` but with a different layer.
444    ///
445    /// This is a behavior we _only_ do for bound vars at the fn root level.
446    FnRootLayerMap(FnRootLayerMap),
447}
448
449impl BoundVarLayerMap {
450    fn name_map(&self) -> &UnordMap<BoundVar, BoundVarName> {
451        match self {
452            Self::LayerMap(name_map) => name_map,
453            Self::FnRootLayerMap(root_layer) => &root_layer.name_map,
454        }
455    }
456}
457
458#[derive(Default)]
459pub struct BoundVarEnv {
460    name_gen: IndexGen<BoundVarName>,
461    layers: RefCell<Vec<BoundVarLayer>>,
462}
463
464impl BoundVarEnv {
465    /// Checks if a variable is
466    /// 1. In the function root layer (`Some(..)` if so, `None` otherwise)
467    /// 2. Has been seen before (the `bool` inside of the `Some(..)`)
468    /// 3. At the args or ret layer type (the `FnRootLayerType` inside of the `Some(..)`)
469    ///
470    /// It updates the set of seen variables at the function root layer when it
471    /// does the check.
472    pub fn check_if_seen_fn_root_bvar(
473        &self,
474        debruijn: DebruijnIndex,
475        var: BoundVar,
476    ) -> Option<(bool, FnRootLayerType)> {
477        let num_layers = self.layers.borrow().len();
478        let mut layer = self.layers.borrow_mut();
479        match layer.get_mut(num_layers.checked_sub(debruijn.as_usize() + 1)?)? {
480            BoundVarLayer {
481                layer_map: BoundVarLayerMap::FnRootLayerMap(fn_root_layer), ..
482            } => Some((!fn_root_layer.seen_vars.insert(var), fn_root_layer.layer_type)),
483            _ => None,
484        }
485    }
486
487    pub fn should_remove_var(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<bool> {
488        let layers = self.layers.borrow();
489        Some(
490            layers
491                .get(layers.len().checked_sub(debruijn.as_usize() + 1)?)?
492                .vars_to_remove
493                .contains(&var),
494        )
495    }
496
497    pub fn mark_var_as_removed(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<bool> {
498        let mut layers = self.layers.borrow_mut();
499        let layer_index = layers.len().checked_sub(debruijn.as_usize() + 1)?;
500        Some(
501            layers
502                .get_mut(layer_index)?
503                .successfully_removed_vars
504                .insert(var),
505        )
506    }
507
508    fn lookup(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<BoundVarName> {
509        let layers = self.layers.borrow();
510        layers
511            .get(layers.len().checked_sub(debruijn.as_usize() + 1)?)?
512            .layer_map
513            .name_map()
514            .get(&var)
515            .copied()
516    }
517
518    fn push_layer(
519        &self,
520        vars: &[BoundVariableKind],
521        vars_to_remove: FxHashSet<BoundVar>,
522        is_fn_root_layer: Option<FnRootLayerType>,
523    ) {
524        let mut name_map = UnordMap::default();
525        for (idx, var) in vars.iter().enumerate() {
526            if let BoundVariableKind::Refine(_, _, BoundReftKind::Anon) = var {
527                name_map.insert(BoundVar::from_usize(idx), self.name_gen.fresh());
528            }
529        }
530        let layer_map = if let Some(layer_type) = is_fn_root_layer {
531            BoundVarLayerMap::FnRootLayerMap(FnRootLayerMap {
532                name_map,
533                seen_vars: FxHashSet::default(),
534                layer_type,
535            })
536        } else {
537            BoundVarLayerMap::LayerMap(name_map)
538        };
539        let layer = BoundVarLayer {
540            layer_map,
541            vars_to_remove,
542            successfully_removed_vars: FxHashSet::default(),
543        };
544        self.layers.borrow_mut().push(layer);
545    }
546
547    fn peek_layer(&self) -> Option<BoundVarLayer> {
548        self.layers.borrow().last().cloned()
549    }
550
551    fn pop_layer(&self) -> Option<BoundVarLayer> {
552        self.layers.borrow_mut().pop()
553    }
554}
555
556type EarlyParamEnv = FxHashSet<EarlyReftParam>;
557
558pub struct WithCx<'a, 'genv, 'tcx, T> {
559    data: T,
560    cx: &'a PrettyCx<'genv, 'tcx>,
561}
562
563pub struct Join<'a, I> {
564    sep: &'a str,
565    iter: RefCell<Option<I>>,
566}
567
568pub struct Parens<'a, T> {
569    val: &'a T,
570    parenthesize: bool,
571}
572
573pub trait Pretty {
574    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result;
575
576    fn default_cx(tcx: TyCtxt) -> PrettyCx {
577        PrettyCx::default(tcx)
578    }
579}
580
581impl Pretty for String {
582    fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583        write!(f, "{self}")
584    }
585}
586
587impl<'a, I> Join<'a, I> {
588    pub fn new<T: IntoIterator<IntoIter = I>>(sep: &'a str, iter: T) -> Self {
589        Self { sep, iter: RefCell::new(Some(iter.into_iter())) }
590    }
591}
592
593impl<'a, T> Parens<'a, T> {
594    pub fn new(val: &'a T, parenthesize: bool) -> Self {
595        Self { val, parenthesize }
596    }
597}
598
599impl<'a, 'genv, 'tcx, T> WithCx<'a, 'genv, 'tcx, T> {
600    pub fn new(cx: &'a PrettyCx<'genv, 'tcx>, data: T) -> Self {
601        Self { data, cx }
602    }
603}
604
605impl<T: Pretty + ?Sized> Pretty for &'_ T {
606    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607        <T as Pretty>::fmt(self, cx, f)
608    }
609}
610
611impl<T: Pretty + Internable> Pretty for Interned<T> {
612    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
613        <T as Pretty>::fmt(self, cx, f)
614    }
615}
616
617impl<T, I> fmt::Debug for Join<'_, I>
618where
619    T: fmt::Debug,
620    I: Iterator<Item = T>,
621{
622    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
623        let Some(iter) = self.iter.borrow_mut().take() else {
624            panic!("Join: was already formatted once")
625        };
626        for (i, item) in iter.enumerate() {
627            if i > 0 {
628                write!(f, "{}", self.sep)?;
629            }
630            <T as fmt::Debug>::fmt(&item, f)?;
631        }
632        Ok(())
633    }
634}
635
636impl<T, I> Pretty for Join<'_, I>
637where
638    T: Pretty,
639    I: Iterator<Item = T>,
640{
641    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
642        let Some(iter) = self.iter.borrow_mut().take() else {
643            panic!("Join: was already formatted once")
644        };
645        for (i, item) in iter.enumerate() {
646            if i > 0 {
647                write!(f, "{}", self.sep)?;
648            }
649            <T as Pretty>::fmt(&item, cx, f)?;
650        }
651        Ok(())
652    }
653}
654impl<T> Pretty for Parens<'_, T>
655where
656    T: Pretty,
657{
658    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
659        if self.parenthesize {
660            write!(f, "(")?;
661        }
662        <T as Pretty>::fmt(self.val, cx, f)?;
663        if self.parenthesize {
664            write!(f, ")")?;
665        }
666        Ok(())
667    }
668}
669
670impl<T: Pretty> fmt::Debug for WithCx<'_, '_, '_, T> {
671    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
672        <T as Pretty>::fmt(&self.data, self.cx, f)
673    }
674}
675
676impl Pretty for DefId {
677    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
678        if cx.fully_qualified_paths {
679            w!(cx, f, "{}", ^cx.tcx().def_path_str(self))
680        } else {
681            let path = cx.tcx().def_path(*self);
682            w!(cx, f, "{}", ^path.data.last().unwrap().as_sym(false))
683        }
684    }
685}
686
687impl Pretty for FluxDefId {
688    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
689        if cx.fully_qualified_paths {
690            w!(cx, f, "{:?}::{}", self.parent(), ^self.name())
691        } else {
692            w!(cx, f, "{}", ^self.name())
693        }
694    }
695}
696
697impl Pretty for FluxLocalDefId {
698    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699        w!(cx, f, "{:?}", self.to_def_id())
700    }
701}
702
703impl Pretty for FieldIdx {
704    fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
705        write!(f, "{}", self.as_u32())
706    }
707}
708
709impl Pretty for Span {
710    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
711        if cx.full_spans {
712            write!(f, "{self:?}")
713        } else {
714            let src_map = cx.tcx().sess.source_map();
715            let lo = src_map.lookup_char_pos(self.lo());
716            let hi = src_map.lookup_char_pos(self.hi());
717            // use rustc_span::FileName;
718            // match lo.file.name {
719            //     FileName::Real(ref name) => {
720            //         write!(
721            //             f,
722            //             "{}",
723            //             name.local_path_if_available()
724            //                 .file_name()
725            //                 .unwrap()
726            //                 .to_string_lossy()
727            //         )
728            //     }
729            //     FileName::QuoteExpansion(_) => write!(f, "<quote expansion>"),
730            //     FileName::MacroExpansion(_) => write!(f, "<macro expansion>"),
731            //     FileName::Anon(_) => write!(f, "<anon>"),
732            //     FileName::ProcMacroSourceCode(_) => write!(f, "<proc-macro source code>"),
733            //     FileName::CfgSpec(_) => write!(f, "<cfgspec>"),
734            //     FileName::CliCrateAttr(_) => write!(f, "<crate attribute>"),
735            //     FileName::Custom(ref s) => write!(f, "<{}>", s),
736            //     FileName::DocTest(ref path, _) => write!(f, "{}", path.display()),
737            //     FileName::InlineAsm(_) => write!(f, "<inline asm>"),
738            // }?;
739            write!(
740                f,
741                "{}:{}: {}:{}",
742                lo.line,
743                lo.col.to_usize() + 1,
744                hi.line,
745                hi.col.to_usize() + 1,
746            )
747        }
748    }
749}
750
751trait FromOpt: Sized {
752    fn from_opt(opt: &config::Value) -> Option<Self>;
753}
754
755impl FromOpt for bool {
756    fn from_opt(opt: &config::Value) -> Option<Self> {
757        opt.as_bool()
758    }
759}
760
761impl FromOpt for KVarArgs {
762    fn from_opt(opt: &config::Value) -> Option<Self> {
763        match opt.as_str() {
764            Some("self") => Some(KVarArgs::SelfOnly),
765            Some("hide") => Some(KVarArgs::Hide),
766            Some("all") => Some(KVarArgs::All),
767            _ => None,
768        }
769    }
770}
771
772// -------------------------------------------------------------------------------------------------------------
773
774#[derive(Serialize, Debug)]
775pub struct NestedString {
776    pub text: String,
777    pub key: Option<String>,
778    pub children: Option<Vec<NestedString>>,
779}
780
781pub fn debug_nested<T: Pretty>(cx: &PrettyCx, t: &T) -> Result<NestedString, fmt::Error> {
782    let t = WithCx::new(cx, t);
783    let text = format!("{t:?}");
784    Ok(NestedString { text, children: None, key: None })
785}
786
787pub fn float_children(children: Vec<Option<Vec<NestedString>>>) -> Option<Vec<NestedString>> {
788    let mut childrens: Vec<_> = children.into_iter().flatten().collect();
789    if childrens.is_empty() {
790        None
791    } else if childrens.len() == 1 {
792        let c = childrens.pop().unwrap();
793        Some(c)
794    } else {
795        let mut res = vec![];
796        for (i, children) in childrens.into_iter().enumerate() {
797            res.push(NestedString { text: format!("arg{i}"), children: Some(children), key: None });
798        }
799        Some(res)
800    }
801}
802
803pub trait PrettyNested {
804    fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error>;
805
806    fn nested_string(&self, cx: &PrettyCx) -> String {
807        let res = self.fmt_nested(cx).unwrap();
808        serde_json::to_string(&res).unwrap()
809    }
810}