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 #[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 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#[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}