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 fmt_bound_vars(
296        &self,
297        print_infer_mode: bool,
298        left: &str,
299        vars: &[BoundVariableKind],
300        right: &str,
301        f: &mut impl fmt::Write,
302    ) -> fmt::Result {
303        w!(self, f, "{left}")?;
304        for (i, var) in vars.iter().enumerate() {
305            if i > 0 {
306                w!(self, f, ", ")?;
307            }
308            match var {
309                BoundVariableKind::Region(re) => w!(self, f, "{:?}", re)?,
310                BoundVariableKind::Refine(sort, mode, BoundReftKind::Named(name)) => {
311                    if print_infer_mode {
312                        w!(self, f, "{}", ^mode.prefix_str())?;
313                    }
314                    w!(self, f, "{}", ^name)?;
315                    if !self.hide_sorts {
316                        w!(self, f, ": {:?}", sort)?;
317                    }
318                }
319                BoundVariableKind::Refine(sort, mode, BoundReftKind::Anon) => {
320                    if print_infer_mode {
321                        w!(self, f, "{}", ^mode.prefix_str())?;
322                    }
323                    if let Some(name) = self.bvar_env.lookup(INNERMOST, BoundVar::from_usize(i)) {
324                        w!(self, f, "{:?}", ^name)?;
325                    } else {
326                        w!(self, f, "_")?;
327                    }
328                    if !self.hide_sorts {
329                        w!(self, f, ": {:?}", sort)?;
330                    }
331                }
332            }
333        }
334        w!(self, f, "{right}")
335    }
336
337    pub fn fmt_bound_reft(
338        &self,
339        debruijn: DebruijnIndex,
340        breft: BoundReft,
341        f: &mut fmt::Formatter<'_>,
342    ) -> fmt::Result {
343        match breft.kind {
344            BoundReftKind::Anon => {
345                if let Some(name) = self.bvar_env.lookup(debruijn, breft.var) {
346                    w!(self, f, "{name:?}")
347                } else {
348                    w!(self, f, "⭡{}/#{:?}", ^debruijn.as_usize(), ^breft.var)
349                }
350            }
351            BoundReftKind::Named(name) => {
352                w!(self, f, "{name}")
353            }
354        }
355    }
356
357    pub fn with_early_params<R>(&self, f: impl FnOnce() -> R) -> R {
358        assert!(self.earlyparam_env.borrow().is_none(), "Already in an early param env");
359        *self.earlyparam_env.borrow_mut() = Some(FxHashSet::default());
360        let r = f();
361        *self.earlyparam_env.borrow_mut() = None;
362        r
363    }
364
365    pub fn kvar_args(self, kvar_args: KVarArgs) -> Self {
366        Self { kvar_args, ..self }
367    }
368
369    pub fn fully_qualified_paths(self, b: bool) -> Self {
370        Self { fully_qualified_paths: b, ..self }
371    }
372
373    pub fn hide_regions(self, b: bool) -> Self {
374        Self { hide_regions: b, ..self }
375    }
376
377    pub fn hide_sorts(self, b: bool) -> Self {
378        Self { hide_sorts: b, ..self }
379    }
380
381    pub fn hide_refinements(self, b: bool) -> Self {
382        Self { hide_refinements: b, ..self }
383    }
384}
385
386newtype_index! {
387    /// Name used during pretty printing to format anonymous bound variables
388    #[debug_format = "b{}"]
389    pub struct BoundVarName {}
390}
391
392#[derive(Copy, Clone)]
393pub enum FnRootLayerType {
394    FnArgs,
395    FnRet,
396}
397
398#[derive(Clone)]
399pub struct FnRootLayerMap {
400    pub name_map: UnordMap<BoundVar, BoundVarName>,
401    pub seen_vars: FxHashSet<BoundVar>,
402    pub layer_type: FnRootLayerType,
403}
404
405#[derive(Clone)]
406pub struct BoundVarLayer {
407    pub layer_map: BoundVarLayerMap,
408    pub vars_to_remove: FxHashSet<BoundVar>,
409    pub successfully_removed_vars: FxHashSet<BoundVar>,
410}
411
412#[derive(Clone)]
413pub enum BoundVarLayerMap {
414    LayerMap(UnordMap<BoundVar, BoundVarName>),
415    /// We treat vars at the function root differently. The UnordMap
416    /// functions the same as in a regular layer (i.e. giving names to
417    /// anonymous bound vars), but we additionally track a set of
418    /// boundvars that have been seen previously.
419    ///
420    /// This set is used to render a signature like
421    ///
422    ///     fn(usize[@n], usize[n]) -> usize[#m] ensures m > 0
423    ///
424    /// The first time we visit `n`, we'll add the `@`, but the second
425    /// time we'll track that we've seen it and won't.
426    ///
427    /// We do the same thing for `m` but with a different layer.
428    ///
429    /// This is a behavior we _only_ do for bound vars at the fn root level.
430    FnRootLayerMap(FnRootLayerMap),
431}
432
433impl BoundVarLayerMap {
434    fn name_map(&self) -> &UnordMap<BoundVar, BoundVarName> {
435        match self {
436            Self::LayerMap(name_map) => name_map,
437            Self::FnRootLayerMap(root_layer) => &root_layer.name_map,
438        }
439    }
440}
441
442#[derive(Default)]
443pub struct BoundVarEnv {
444    name_gen: IndexGen<BoundVarName>,
445    layers: RefCell<Vec<BoundVarLayer>>,
446}
447
448impl BoundVarEnv {
449    /// Checks if a variable is
450    /// 1. In the function root layer (`Some(..)` if so, `None` otherwise)
451    /// 2. Has been seen before (the `bool` inside of the `Some(..)`)
452    /// 3. At the args or ret layer type (the `FnRootLayerType` inside of the `Some(..)`)
453    ///
454    /// It updates the set of seen variables at the function root layer when it
455    /// does the check.
456    pub fn check_if_seen_fn_root_bvar(
457        &self,
458        debruijn: DebruijnIndex,
459        var: BoundVar,
460    ) -> Option<(bool, FnRootLayerType)> {
461        let num_layers = self.layers.borrow().len();
462        let mut layer = self.layers.borrow_mut();
463        match layer.get_mut(num_layers.checked_sub(debruijn.as_usize() + 1)?)? {
464            BoundVarLayer {
465                layer_map: BoundVarLayerMap::FnRootLayerMap(fn_root_layer), ..
466            } => Some((!fn_root_layer.seen_vars.insert(var), fn_root_layer.layer_type)),
467            _ => None,
468        }
469    }
470
471    pub fn should_remove_var(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<bool> {
472        let layers = self.layers.borrow();
473        Some(
474            layers
475                .get(layers.len().checked_sub(debruijn.as_usize() + 1)?)?
476                .vars_to_remove
477                .contains(&var),
478        )
479    }
480
481    pub fn mark_var_as_removed(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<bool> {
482        let mut layers = self.layers.borrow_mut();
483        let layer_index = layers.len().checked_sub(debruijn.as_usize() + 1)?;
484        Some(
485            layers
486                .get_mut(layer_index)?
487                .successfully_removed_vars
488                .insert(var),
489        )
490    }
491
492    fn lookup(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<BoundVarName> {
493        let layers = self.layers.borrow();
494        layers
495            .get(layers.len().checked_sub(debruijn.as_usize() + 1)?)?
496            .layer_map
497            .name_map()
498            .get(&var)
499            .copied()
500    }
501
502    fn push_layer(
503        &self,
504        vars: &[BoundVariableKind],
505        vars_to_remove: FxHashSet<BoundVar>,
506        is_fn_root_layer: Option<FnRootLayerType>,
507    ) {
508        let mut name_map = UnordMap::default();
509        for (idx, var) in vars.iter().enumerate() {
510            if let BoundVariableKind::Refine(_, _, BoundReftKind::Anon) = var {
511                name_map.insert(BoundVar::from_usize(idx), self.name_gen.fresh());
512            }
513        }
514        let layer_map = if let Some(layer_type) = is_fn_root_layer {
515            BoundVarLayerMap::FnRootLayerMap(FnRootLayerMap {
516                name_map,
517                seen_vars: FxHashSet::default(),
518                layer_type,
519            })
520        } else {
521            BoundVarLayerMap::LayerMap(name_map)
522        };
523        let layer = BoundVarLayer {
524            layer_map,
525            vars_to_remove,
526            successfully_removed_vars: FxHashSet::default(),
527        };
528        self.layers.borrow_mut().push(layer);
529    }
530
531    fn peek_layer(&self) -> Option<BoundVarLayer> {
532        self.layers.borrow().last().cloned()
533    }
534
535    fn pop_layer(&self) -> Option<BoundVarLayer> {
536        self.layers.borrow_mut().pop()
537    }
538}
539
540type EarlyParamEnv = FxHashSet<EarlyReftParam>;
541
542pub struct WithCx<'a, 'genv, 'tcx, T> {
543    data: T,
544    cx: &'a PrettyCx<'genv, 'tcx>,
545}
546
547pub struct Join<'a, I> {
548    sep: &'a str,
549    iter: RefCell<Option<I>>,
550}
551
552pub struct Parens<'a, T> {
553    val: &'a T,
554    parenthesize: bool,
555}
556
557pub trait Pretty {
558    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result;
559
560    fn default_cx(tcx: TyCtxt) -> PrettyCx {
561        PrettyCx::default(tcx)
562    }
563}
564
565impl Pretty for String {
566    fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
567        write!(f, "{self}")
568    }
569}
570
571impl<'a, I> Join<'a, I> {
572    pub fn new<T: IntoIterator<IntoIter = I>>(sep: &'a str, iter: T) -> Self {
573        Self { sep, iter: RefCell::new(Some(iter.into_iter())) }
574    }
575}
576
577impl<'a, T> Parens<'a, T> {
578    pub fn new(val: &'a T, parenthesize: bool) -> Self {
579        Self { val, parenthesize }
580    }
581}
582
583impl<'a, 'genv, 'tcx, T> WithCx<'a, 'genv, 'tcx, T> {
584    pub fn new(cx: &'a PrettyCx<'genv, 'tcx>, data: T) -> Self {
585        Self { data, cx }
586    }
587}
588
589impl<T: Pretty + ?Sized> Pretty for &'_ T {
590    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
591        <T as Pretty>::fmt(self, cx, f)
592    }
593}
594
595impl<T: Pretty + Internable> Pretty for Interned<T> {
596    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
597        <T as Pretty>::fmt(self, cx, f)
598    }
599}
600
601impl<T, I> fmt::Debug for Join<'_, I>
602where
603    T: fmt::Debug,
604    I: Iterator<Item = T>,
605{
606    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607        let Some(iter) = self.iter.borrow_mut().take() else {
608            panic!("Join: was already formatted once")
609        };
610        for (i, item) in iter.enumerate() {
611            if i > 0 {
612                write!(f, "{}", self.sep)?;
613            }
614            <T as fmt::Debug>::fmt(&item, f)?;
615        }
616        Ok(())
617    }
618}
619
620impl<T, I> Pretty for Join<'_, I>
621where
622    T: Pretty,
623    I: Iterator<Item = T>,
624{
625    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
626        let Some(iter) = self.iter.borrow_mut().take() else {
627            panic!("Join: was already formatted once")
628        };
629        for (i, item) in iter.enumerate() {
630            if i > 0 {
631                write!(f, "{}", self.sep)?;
632            }
633            <T as Pretty>::fmt(&item, cx, f)?;
634        }
635        Ok(())
636    }
637}
638impl<T> Pretty for Parens<'_, T>
639where
640    T: Pretty,
641{
642    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
643        if self.parenthesize {
644            write!(f, "(")?;
645        }
646        <T as Pretty>::fmt(self.val, cx, f)?;
647        if self.parenthesize {
648            write!(f, ")")?;
649        }
650        Ok(())
651    }
652}
653
654impl<T: Pretty> fmt::Debug for WithCx<'_, '_, '_, T> {
655    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656        <T as Pretty>::fmt(&self.data, self.cx, f)
657    }
658}
659
660impl Pretty for DefId {
661    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
662        if cx.fully_qualified_paths {
663            w!(cx, f, "{}", ^cx.tcx().def_path_str(self))
664        } else {
665            let path = cx.tcx().def_path(*self);
666            w!(cx, f, "{}", ^path.data.last().unwrap().as_sym(false))
667        }
668    }
669}
670
671impl Pretty for FluxDefId {
672    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
673        if cx.fully_qualified_paths {
674            w!(cx, f, "{:?}::{}", self.parent(), ^self.name())
675        } else {
676            w!(cx, f, "{}", ^self.name())
677        }
678    }
679}
680
681impl Pretty for FluxLocalDefId {
682    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
683        w!(cx, f, "{:?}", self.to_def_id())
684    }
685}
686
687impl Pretty for FieldIdx {
688    fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
689        write!(f, "{}", self.as_u32())
690    }
691}
692
693impl Pretty for Span {
694    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
695        if cx.full_spans {
696            write!(f, "{self:?}")
697        } else {
698            let src_map = cx.tcx().sess.source_map();
699            let lo = src_map.lookup_char_pos(self.lo());
700            let hi = src_map.lookup_char_pos(self.hi());
701            // use rustc_span::FileName;
702            // match lo.file.name {
703            //     FileName::Real(ref name) => {
704            //         write!(
705            //             f,
706            //             "{}",
707            //             name.local_path_if_available()
708            //                 .file_name()
709            //                 .unwrap()
710            //                 .to_string_lossy()
711            //         )
712            //     }
713            //     FileName::QuoteExpansion(_) => write!(f, "<quote expansion>"),
714            //     FileName::MacroExpansion(_) => write!(f, "<macro expansion>"),
715            //     FileName::Anon(_) => write!(f, "<anon>"),
716            //     FileName::ProcMacroSourceCode(_) => write!(f, "<proc-macro source code>"),
717            //     FileName::CfgSpec(_) => write!(f, "<cfgspec>"),
718            //     FileName::CliCrateAttr(_) => write!(f, "<crate attribute>"),
719            //     FileName::Custom(ref s) => write!(f, "<{}>", s),
720            //     FileName::DocTest(ref path, _) => write!(f, "{}", path.display()),
721            //     FileName::InlineAsm(_) => write!(f, "<inline asm>"),
722            // }?;
723            write!(
724                f,
725                "{}:{}: {}:{}",
726                lo.line,
727                lo.col.to_usize() + 1,
728                hi.line,
729                hi.col.to_usize() + 1,
730            )
731        }
732    }
733}
734
735trait FromOpt: Sized {
736    fn from_opt(opt: &config::Value) -> Option<Self>;
737}
738
739impl FromOpt for bool {
740    fn from_opt(opt: &config::Value) -> Option<Self> {
741        opt.as_bool()
742    }
743}
744
745impl FromOpt for KVarArgs {
746    fn from_opt(opt: &config::Value) -> Option<Self> {
747        match opt.as_str() {
748            Some("self") => Some(KVarArgs::SelfOnly),
749            Some("hide") => Some(KVarArgs::Hide),
750            Some("all") => Some(KVarArgs::All),
751            _ => None,
752        }
753    }
754}
755
756// -------------------------------------------------------------------------------------------------------------
757
758#[derive(Serialize, Debug)]
759pub struct NestedString {
760    pub text: String,
761    pub key: Option<String>,
762    pub children: Option<Vec<NestedString>>,
763}
764
765pub fn debug_nested<T: Pretty>(cx: &PrettyCx, t: &T) -> Result<NestedString, fmt::Error> {
766    let t = WithCx::new(cx, t);
767    let text = format!("{t:?}");
768    Ok(NestedString { text, children: None, key: None })
769}
770
771pub fn float_children(children: Vec<Option<Vec<NestedString>>>) -> Option<Vec<NestedString>> {
772    let mut childrens: Vec<_> = children.into_iter().flatten().collect();
773    if childrens.is_empty() {
774        None
775    } else if childrens.len() == 1 {
776        let c = childrens.pop().unwrap();
777        Some(c)
778    } else {
779        let mut res = vec![];
780        for (i, children) in childrens.into_iter().enumerate() {
781            res.push(NestedString { text: format!("arg{i}"), children: Some(children), key: None });
782        }
783        Some(res)
784    }
785}
786
787pub trait PrettyNested {
788    fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error>;
789
790    fn nested_string(&self, cx: &PrettyCx) -> String {
791        let res = self.fmt_nested(cx).unwrap();
792        serde_json::to_string(&res).unwrap()
793    }
794}