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