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_hir::def_id::DefId;
9use rustc_index::newtype_index;
10use rustc_middle::ty::TyCtxt;
11use rustc_span::{Pos, Span};
12use rustc_type_ir::{BoundVar, DebruijnIndex, INNERMOST};
13use serde::Serialize;
14
15#[macro_export]
16macro_rules! _with_cx {
17    ($cx:expr, $e:expr) => {
18        $crate::pretty::WithCx::new($cx, $e)
19    };
20}
21pub use crate::_with_cx as with_cx;
22use crate::def_id::{FluxDefId, FluxLocalDefId};
23
24#[macro_export]
25macro_rules! _format_args_cx {
26    ($cx:ident, $fmt:literal, $($args:tt)*) => {
27        $crate::_format_args_cx!(@go ($cx, $fmt; $($args)*) -> ())
28    };
29    ($cx:expr, $fmt:literal) => {
30        format_args!($fmt)
31    };
32    (@go ($cx:ident, $fmt:literal; ^$head:expr, $($tail:tt)*) -> ($($accum:tt)*)) => {
33        $crate::_format_args_cx!(@go ($cx, $fmt; $($tail)*) -> ($($accum)* $head,))
34    };
35    (@go ($cx:ident, $fmt:literal; $head:expr, $($tail:tt)*) -> ($($accum:tt)*)) => {
36        $crate::_format_args_cx!(@go ($cx, $fmt; $($tail)*) -> ($($accum)* $crate::pretty::with_cx!($cx, $head),))
37    };
38    (@go ($cx:ident, $fmt:literal; ^$head:expr) -> ($($accum:tt)*)) => {
39        $crate::_format_args_cx!(@as_expr format_args!($fmt, $($accum)* $head,))
40    };
41    (@go ($cx:ident, $fmt:literal; $head:expr) -> ($($accum:tt)*)) => {
42        $crate::_format_args_cx!(@as_expr format_args!($fmt, $($accum)* $crate::pretty::with_cx!($cx, $head),))
43    };
44    (@as_expr $e:expr) => { $e };
45}
46pub use crate::_format_args_cx as format_args_cx;
47
48#[macro_export]
49macro_rules! _format_cx {
50    ($($arg:tt)*) => {
51        std::fmt::format($crate::_format_args_cx!($($arg)*))
52    }
53}
54pub use crate::_format_cx as format_cx;
55
56#[macro_export]
57macro_rules! _w {
58    ($cx:expr, $f:expr, $fmt:literal, $($args:tt)*) => {{
59        #[allow(unused_variables)]
60        let cx = $cx;
61        $f.write_fmt($crate::_format_args_cx!(cx, $fmt, $($args)*))
62    }};
63    ($cx:expr, $f:expr, $fmt:literal) => {
64        $f.write_fmt($crate::_format_args_cx!($cx, $fmt))
65    };
66}
67pub use crate::_w as w;
68
69#[macro_export]
70macro_rules! _join {
71    ($sep:expr, $iter:expr) => {
72        $crate::pretty::Join::new($sep, $iter)
73    };
74}
75pub use crate::_join as join;
76
77#[macro_export]
78macro_rules! _parens {
79    ($val:expr, $parenthesize:expr) => {
80        $crate::pretty::Parens::new(&$val, $parenthesize)
81    };
82}
83pub use crate::_parens as parens;
84
85#[macro_export]
86macro_rules! _impl_debug_with_default_cx {
87    ($($ty:ty $(=> $key:literal)?),* $(,)?) => {$(
88        impl std::fmt::Debug for $ty  {
89            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90                #[allow(unused_mut, unused_assignments)]
91                let mut key = None;
92                $(
93                    key = Some($key);
94                )?
95                $crate::pretty::pprint_with_default_cx(f, self, key)
96            }
97        }
98    )*};
99}
100
101pub fn pprint_with_default_cx<T: Pretty>(
102    f: &mut std::fmt::Formatter<'_>,
103    t: &T,
104    cfg_key: Option<&'static str>,
105) -> std::fmt::Result {
106    rustc_middle::ty::tls::with(|tcx| {
107        #[allow(unused_mut)]
108        let mut cx = <T>::default_cx(tcx);
109
110        if let Some(pprint) = flux_config::CONFIG_FILE
111            .get("dev")
112            .and_then(|dev| dev.get("pprint"))
113        {
114            if let Some(opts) = pprint.get("default") {
115                cx.merge(opts);
116            }
117
118            if let Some(key) = cfg_key
119                && let Some(opts) = pprint.get(key)
120            {
121                cx.merge(opts);
122            }
123        }
124
125        if let Some(key) = cfg_key
126            && let Some(opts) = flux_config::CONFIG_FILE
127                .get("dev")
128                .and_then(|dev| dev.get("pprint"))
129                .and_then(|pprint| pprint.get(key))
130        {
131            cx.merge(opts);
132        }
133        Pretty::fmt(t, &cx, f)
134    })
135}
136
137pub use crate::_impl_debug_with_default_cx as impl_debug_with_default_cx;
138use crate::{
139    global_env::GlobalEnv,
140    rty::{AdtSortDef, BoundReft, BoundReftKind, BoundVariableKind},
141};
142
143#[derive(Copy, Clone)]
144pub enum KVarArgs {
145    All,
146    SelfOnly,
147    Hide,
148}
149
150#[derive(Clone, Copy)]
151pub enum GenvOrTcx<'genv, 'tcx> {
152    Genv(GlobalEnv<'genv, 'tcx>),
153    Tcx(TyCtxt<'tcx>),
154}
155
156impl<'genv, 'tcx> GenvOrTcx<'genv, 'tcx> {
157    fn tcx(self) -> TyCtxt<'tcx> {
158        match self {
159            GenvOrTcx::Genv(genv) => genv.tcx(),
160            GenvOrTcx::Tcx(tcx) => tcx,
161        }
162    }
163
164    fn genv(self) -> Option<GlobalEnv<'genv, 'tcx>> {
165        match self {
166            GenvOrTcx::Genv(genv) => Some(genv),
167            GenvOrTcx::Tcx(_) => None,
168        }
169    }
170}
171
172impl<'tcx> From<TyCtxt<'tcx>> for GenvOrTcx<'_, 'tcx> {
173    fn from(v: TyCtxt<'tcx>) -> Self {
174        Self::Tcx(v)
175    }
176}
177
178impl<'genv, 'tcx> From<GlobalEnv<'genv, 'tcx>> for GenvOrTcx<'genv, 'tcx> {
179    fn from(v: GlobalEnv<'genv, 'tcx>) -> Self {
180        Self::Genv(v)
181    }
182}
183
184pub struct PrettyCx<'genv, 'tcx> {
185    pub cx: GenvOrTcx<'genv, 'tcx>,
186    pub kvar_args: KVarArgs,
187    pub fully_qualified_paths: bool,
188    pub simplify_exprs: bool,
189    pub tags: bool,
190    pub bindings_chain: bool,
191    pub preds_chain: bool,
192    pub full_spans: bool,
193    pub hide_uninit: bool,
194    pub hide_refinements: bool,
195    pub hide_regions: bool,
196    pub hide_sorts: bool,
197    env: BoundVarEnv,
198}
199
200macro_rules! set_opts {
201    ($cx:expr, $opts:expr, [$($opt:ident),+ $(,)?]) => {
202        $(
203        if let Some(val) = $opts.get(stringify!($opt)).and_then(|v| FromOpt::from_opt(v)) {
204            $cx.$opt = val;
205        }
206        )+
207    };
208}
209
210impl<'genv, 'tcx> PrettyCx<'genv, 'tcx> {
211    pub fn default(cx: impl Into<GenvOrTcx<'genv, 'tcx>>) -> Self {
212        PrettyCx {
213            cx: cx.into(),
214            kvar_args: KVarArgs::SelfOnly,
215            fully_qualified_paths: false,
216            simplify_exprs: true,
217            tags: true,
218            bindings_chain: true,
219            preds_chain: true,
220            full_spans: false,
221            hide_uninit: true,
222            hide_refinements: false,
223            hide_regions: false,
224            hide_sorts: true,
225            env: BoundVarEnv::default(),
226        }
227    }
228
229    pub fn tcx(&self) -> TyCtxt<'tcx> {
230        self.cx.tcx()
231    }
232
233    pub fn genv(&self) -> Option<GlobalEnv<'genv, 'tcx>> {
234        self.cx.genv()
235    }
236
237    pub fn adt_sort_def_of(&self, def_id: DefId) -> Option<AdtSortDef> {
238        self.genv()
239            .and_then(|genv| genv.adt_sort_def_of(def_id).ok())
240    }
241
242    pub fn merge(&mut self, opts: &config::Value) {
243        set_opts!(
244            self,
245            opts,
246            [
247                kvar_args,
248                fully_qualified_paths,
249                simplify_exprs,
250                tags,
251                bindings_chain,
252                preds_chain,
253                full_spans,
254                hide_uninit,
255                hide_refinements,
256                hide_regions,
257                hide_sorts,
258            ]
259        );
260    }
261
262    pub fn with_bound_vars<R>(&self, vars: &[BoundVariableKind], f: impl FnOnce() -> R) -> R {
263        self.env.push_layer(vars);
264        let r = f();
265        self.env.pop_layer();
266        r
267    }
268
269    pub fn fmt_bound_vars(
270        &self,
271        print_infer_mode: bool,
272        left: &str,
273        vars: &[BoundVariableKind],
274        right: &str,
275        f: &mut impl fmt::Write,
276    ) -> fmt::Result {
277        w!(self, f, "{left}")?;
278        for (i, var) in vars.iter().enumerate() {
279            if i > 0 {
280                w!(self, f, ", ")?;
281            }
282            match var {
283                BoundVariableKind::Region(re) => w!(self, f, "{:?}", re)?,
284                BoundVariableKind::Refine(sort, mode, BoundReftKind::Named(name)) => {
285                    if print_infer_mode {
286                        w!(self, f, "{}", ^mode.prefix_str())?;
287                    }
288                    w!(self, f, "{}", ^name)?;
289                    if !self.hide_sorts {
290                        w!(self, f, ": {:?}", sort)?;
291                    }
292                }
293                BoundVariableKind::Refine(sort, mode, BoundReftKind::Annon) => {
294                    if print_infer_mode {
295                        w!(self, f, "{}", ^mode.prefix_str())?;
296                    }
297                    if let Some(name) = self.env.lookup(INNERMOST, BoundVar::from_usize(i)) {
298                        w!(self, f, "{:?}", ^name)?;
299                    } else {
300                        w!(self, f, "_")?;
301                    }
302                    if !self.hide_sorts {
303                        w!(self, f, ": {:?}", sort)?;
304                    }
305                }
306            }
307        }
308        w!(self, f, "{right}")
309    }
310
311    pub fn fmt_bound_reft(
312        &self,
313        debruijn: DebruijnIndex,
314        breft: BoundReft,
315        f: &mut fmt::Formatter<'_>,
316    ) -> fmt::Result {
317        match breft.kind {
318            BoundReftKind::Annon => {
319                if let Some(name) = self.env.lookup(debruijn, breft.var) {
320                    w!(self, f, "{name:?}")
321                } else {
322                    w!(self, f, "⭡{}/#{:?}", ^debruijn.as_usize(), ^breft.var)
323                }
324            }
325            BoundReftKind::Named(name) => {
326                w!(self, f, "{name}<<⭡{}/#{:?}>>", ^debruijn.as_usize(), ^breft.var)
327            }
328        }
329    }
330
331    pub fn kvar_args(self, kvar_args: KVarArgs) -> Self {
332        Self { kvar_args, ..self }
333    }
334
335    pub fn fully_qualified_paths(self, b: bool) -> Self {
336        Self { fully_qualified_paths: b, ..self }
337    }
338
339    pub fn hide_regions(self, b: bool) -> Self {
340        Self { hide_regions: b, ..self }
341    }
342
343    pub fn hide_sorts(self, b: bool) -> Self {
344        Self { hide_sorts: b, ..self }
345    }
346
347    pub fn hide_refinements(self, b: bool) -> Self {
348        Self { hide_refinements: b, ..self }
349    }
350}
351
352newtype_index! {
353    /// Name used during pretty printing to format anonymous bound variables
354    #[debug_format = "b{}"]
355    struct BoundVarName {}
356}
357
358#[derive(Default)]
359struct BoundVarEnv {
360    name_gen: IndexGen<BoundVarName>,
361    layers: RefCell<Vec<UnordMap<BoundVar, BoundVarName>>>,
362}
363
364impl BoundVarEnv {
365    fn lookup(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<BoundVarName> {
366        let layers = self.layers.borrow();
367        layers
368            .get(layers.len().checked_sub(debruijn.as_usize() + 1)?)?
369            .get(&var)
370            .copied()
371    }
372
373    fn push_layer(&self, vars: &[BoundVariableKind]) {
374        let mut layer = UnordMap::default();
375        for (idx, var) in vars.iter().enumerate() {
376            if let BoundVariableKind::Refine(_, _, BoundReftKind::Annon) = var {
377                layer.insert(BoundVar::from_usize(idx), self.name_gen.fresh());
378            }
379        }
380        self.layers.borrow_mut().push(layer);
381    }
382
383    fn pop_layer(&self) {
384        self.layers.borrow_mut().pop();
385    }
386}
387
388pub struct WithCx<'a, 'genv, 'tcx, T> {
389    data: T,
390    cx: &'a PrettyCx<'genv, 'tcx>,
391}
392
393pub struct Join<'a, I> {
394    sep: &'a str,
395    iter: RefCell<Option<I>>,
396}
397
398pub struct Parens<'a, T> {
399    val: &'a T,
400    parenthesize: bool,
401}
402
403pub trait Pretty {
404    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result;
405
406    fn default_cx(tcx: TyCtxt) -> PrettyCx {
407        PrettyCx::default(tcx)
408    }
409}
410
411impl Pretty for String {
412    fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413        write!(f, "{}", self)
414    }
415}
416
417impl<'a, I> Join<'a, I> {
418    pub fn new<T: IntoIterator<IntoIter = I>>(sep: &'a str, iter: T) -> Self {
419        Self { sep, iter: RefCell::new(Some(iter.into_iter())) }
420    }
421}
422
423impl<'a, T> Parens<'a, T> {
424    pub fn new(val: &'a T, parenthesize: bool) -> Self {
425        Self { val, parenthesize }
426    }
427}
428
429impl<'a, 'genv, 'tcx, T> WithCx<'a, 'genv, 'tcx, T> {
430    pub fn new(cx: &'a PrettyCx<'genv, 'tcx>, data: T) -> Self {
431        Self { data, cx }
432    }
433}
434
435impl<T: Pretty + ?Sized> Pretty for &'_ T {
436    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437        <T as Pretty>::fmt(self, cx, f)
438    }
439}
440
441impl<T: Pretty + Internable> Pretty for Interned<T> {
442    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443        <T as Pretty>::fmt(self, cx, f)
444    }
445}
446
447impl<T, I> fmt::Debug for Join<'_, I>
448where
449    T: fmt::Debug,
450    I: Iterator<Item = T>,
451{
452    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453        let Some(iter) = self.iter.borrow_mut().take() else {
454            panic!("Join: was already formatted once")
455        };
456        for (i, item) in iter.enumerate() {
457            if i > 0 {
458                write!(f, "{}", self.sep)?;
459            }
460            <T as fmt::Debug>::fmt(&item, f)?;
461        }
462        Ok(())
463    }
464}
465
466impl<T, I> Pretty for Join<'_, I>
467where
468    T: Pretty,
469    I: Iterator<Item = T>,
470{
471    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472        let Some(iter) = self.iter.borrow_mut().take() else {
473            panic!("Join: was already formatted once")
474        };
475        for (i, item) in iter.enumerate() {
476            if i > 0 {
477                write!(f, "{}", self.sep)?;
478            }
479            <T as Pretty>::fmt(&item, cx, f)?;
480        }
481        Ok(())
482    }
483}
484impl<T> Pretty for Parens<'_, T>
485where
486    T: Pretty,
487{
488    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489        if self.parenthesize {
490            write!(f, "(")?;
491        }
492        <T as Pretty>::fmt(self.val, cx, f)?;
493        if self.parenthesize {
494            write!(f, ")")?;
495        }
496        Ok(())
497    }
498}
499
500impl<T: Pretty> fmt::Debug for WithCx<'_, '_, '_, T> {
501    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
502        <T as Pretty>::fmt(&self.data, self.cx, f)
503    }
504}
505
506impl Pretty for DefId {
507    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508        let path = cx.tcx().def_path(*self);
509        if cx.fully_qualified_paths {
510            let krate = cx.tcx().crate_name(self.krate);
511            w!(cx, f, "{}{}", ^krate, ^path.to_string_no_crate_verbose())
512        } else {
513            w!(cx, f, "{}", ^path.data.last().unwrap())
514        }
515    }
516}
517
518impl Pretty for FluxDefId {
519    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520        if cx.fully_qualified_paths {
521            w!(cx, f, "{:?}::{}", self.parent(), ^self.name())
522        } else {
523            w!(cx, f, "{}", ^self.name())
524        }
525    }
526}
527
528impl Pretty for FluxLocalDefId {
529    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530        w!(cx, f, "{:?}", self.to_def_id())
531    }
532}
533
534impl Pretty for FieldIdx {
535    fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
536        write!(f, "{}", self.as_u32())
537    }
538}
539
540impl Pretty for Span {
541    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542        if cx.full_spans {
543            write!(f, "{self:?}")
544        } else {
545            let src_map = cx.tcx().sess.source_map();
546            let lo = src_map.lookup_char_pos(self.lo());
547            let hi = src_map.lookup_char_pos(self.hi());
548            // use rustc_span::FileName;
549            // match lo.file.name {
550            //     FileName::Real(ref name) => {
551            //         write!(
552            //             f,
553            //             "{}",
554            //             name.local_path_if_available()
555            //                 .file_name()
556            //                 .unwrap()
557            //                 .to_string_lossy()
558            //         )
559            //     }
560            //     FileName::QuoteExpansion(_) => write!(f, "<quote expansion>"),
561            //     FileName::MacroExpansion(_) => write!(f, "<macro expansion>"),
562            //     FileName::Anon(_) => write!(f, "<anon>"),
563            //     FileName::ProcMacroSourceCode(_) => write!(f, "<proc-macro source code>"),
564            //     FileName::CfgSpec(_) => write!(f, "<cfgspec>"),
565            //     FileName::CliCrateAttr(_) => write!(f, "<crate attribute>"),
566            //     FileName::Custom(ref s) => write!(f, "<{}>", s),
567            //     FileName::DocTest(ref path, _) => write!(f, "{}", path.display()),
568            //     FileName::InlineAsm(_) => write!(f, "<inline asm>"),
569            // }?;
570            write!(
571                f,
572                "{}:{}: {}:{}",
573                lo.line,
574                lo.col.to_usize() + 1,
575                hi.line,
576                hi.col.to_usize() + 1,
577            )
578        }
579    }
580}
581
582trait FromOpt: Sized {
583    fn from_opt(opt: &config::Value) -> Option<Self>;
584}
585
586impl FromOpt for bool {
587    fn from_opt(opt: &config::Value) -> Option<Self> {
588        opt.as_bool()
589    }
590}
591
592impl FromOpt for KVarArgs {
593    fn from_opt(opt: &config::Value) -> Option<Self> {
594        match opt.as_str() {
595            Some("self") => Some(KVarArgs::SelfOnly),
596            Some("hide") => Some(KVarArgs::Hide),
597            Some("all") => Some(KVarArgs::All),
598            _ => None,
599        }
600    }
601}
602
603// -------------------------------------------------------------------------------------------------------------
604
605#[derive(Serialize)]
606pub struct NestedString {
607    pub text: String,
608    pub key: Option<String>,
609    pub children: Option<Vec<NestedString>>,
610}
611
612pub fn debug_nested<T: Pretty>(cx: &PrettyCx, t: &T) -> Result<NestedString, fmt::Error> {
613    let t = WithCx::new(cx, t);
614    let text = format!("{:?}", t);
615    Ok(NestedString { text, children: None, key: None })
616}
617
618pub fn float_children(children: Vec<Option<Vec<NestedString>>>) -> Option<Vec<NestedString>> {
619    let mut childrens: Vec<_> = children.into_iter().flatten().collect();
620    if childrens.is_empty() {
621        None
622    } else if childrens.len() == 1 {
623        let c = childrens.pop().unwrap();
624        Some(c)
625    } else {
626        let mut res = vec![];
627        for (i, children) in childrens.into_iter().enumerate() {
628            res.push(NestedString {
629                text: format!("arg{}", i),
630                children: Some(children),
631                key: None,
632            });
633        }
634        Some(res)
635    }
636}
637
638pub trait PrettyNested {
639    fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error>;
640
641    fn nested_string(&self, cx: &PrettyCx) -> String {
642        let res = self.fmt_nested(cx).unwrap();
643        serde_json::to_string(&res).unwrap()
644    }
645}