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