1use std::{
3 fmt, fs,
4 io::{self, Write},
5};
6
7use flux_config as config;
8use flux_macros::DebugAsJson;
9use rustc_hir::def_id::DefId;
10use rustc_middle::ty::TyCtxt;
11use rustc_span::Span;
12use serde::Serialize;
13
14#[derive(Serialize, DebugAsJson)]
15pub struct SpanTrace {
16 file: Option<String>,
17 start_line: usize,
18 start_col: usize,
19 end_line: usize,
20 end_col: usize,
21}
22
23impl SpanTrace {
24 fn span_file(tcx: TyCtxt, span: Span) -> Option<String> {
25 let sm = tcx.sess.source_map();
26 let current_dir = &tcx.sess.opts.working_dir;
27 let current_dir = current_dir.local_path()?;
28 if let rustc_span::FileName::Real(file_name) = sm.span_to_filename(span) {
29 let file_path = file_name.local_path()?;
30 let full_path = current_dir.join(file_path);
31 Some(full_path.display().to_string())
32 } else {
33 None
34 }
35 }
36 pub fn new(tcx: TyCtxt, span: Span) -> Self {
37 let sm = tcx.sess.source_map();
38 let (_, start_line, start_col, end_line, end_col) = sm.span_to_location_info(span);
39 let file = SpanTrace::span_file(tcx, span);
40 SpanTrace { file, start_line, start_col, end_line, end_col }
41 }
42}
43
44pub fn writer_for_item(
45 tcx: TyCtxt,
46 def_id: DefId,
47 ext: impl AsRef<str>,
48) -> io::Result<impl io::Write> {
49 fs::create_dir_all(config::log_dir())?;
50 let path = config::log_dir().join(dump_base_name(tcx, def_id, ext));
51 let file = fs::File::create(path)?;
52 let buf = std::io::BufWriter::new(file);
53 Ok(buf)
54}
55
56pub fn dump_item_info<T: fmt::Debug>(
57 tcx: TyCtxt,
58 def_id: impl Into<DefId>,
59 ext: impl AsRef<str>,
60 val: T,
61) -> io::Result<()> {
62 let mut writer = writer_for_item(tcx, def_id.into(), ext)?;
63 writeln!(writer, "{val:#?}")
64}
65
66#[macro_export]
67macro_rules! _shape_mode_span {
68 ($tcx:expr, $def_id:expr) => {{
69 let path = $tcx.def_path_str(rustc_hir::def_id::DefId::from($def_id));
70 tracing::info_span!("shape", def_id = path.as_str())
71 }};
72}
73pub use crate::_shape_mode_span as shape_mode_span;
74
75#[macro_export]
76macro_rules! _refine_mode_span {
77 ($tcx:expr, $def_id:expr, $bb_envs:expr) => {{
78 let path = $tcx.def_path_str(rustc_hir::def_id::DefId::from($def_id));
79 tracing::info_span!("refine", def_id = path.as_str(), bb_envs = ?$bb_envs)
80 }};
81}
82pub use crate::_refine_mode_span as refine_mode_span;
83
84#[macro_export]
85macro_rules! _check_fn_span {
86 ($tcx:expr, $def_id:expr) => {{
87 let path = $tcx.def_path_str(rustc_hir::def_id::DefId::from($def_id));
88 tracing::info_span!("check_fn", def_id = path.as_str())
89 }};
90}
91pub use crate::_check_fn_span as check_fn_span;
92
93#[macro_export]
94macro_rules! _basic_block_start {
95 ($bb:expr, $rcx:expr, $env:expr) => {{
96 tracing::debug!(event = "basic_block_start", bb = ?$bb, rcx = ?$rcx, env = ?$env)
97 }};
98}
99pub use crate::_basic_block_start as basic_block_start;
100
101#[macro_export]
102macro_rules! _statement{
103 ($pos:literal, $stmt:expr, $infcx:expr, $env:expr, $span:expr, $checker:expr) => {{
104 if let Some(level) = config::dump_checker_trace() && level <= tracing::Level::INFO {
105 let rcx = $infcx.cursor();
106 let ck = $checker;
107 let genv = ck.genv;
108 let local_names = &ck.body.local_names;
109 let local_decls = &ck.body.local_decls;
110 let rcx_json = RefineCtxtTrace::new(genv, rcx);
111 let env_json = TypeEnvTrace::new(genv, local_names, local_decls, $env);
112 let span_json = SpanTrace::new(genv.tcx(), $span);
113 tracing::info!(event = concat!("statement_", $pos), stmt = ?$stmt, stmt_span = ?$span, rcx = ?rcx, env = ?$env, rcx_json = ?rcx_json, env_json = ?env_json, stmt_span_json = ?span_json)
114 }
115 }};
116}
117pub use crate::_statement as statement;
118
119#[macro_export]
120macro_rules! _terminator{
121 ($pos:literal, $terminator:expr, $rcx:expr, $env:expr) => {{
122 tracing::debug!(event = concat!("terminator_", $pos), terminator = ?$terminator, rcx = ?$rcx, env = ?$env)
123 }};
124}
125pub use crate::_terminator as terminator;
126
127#[macro_export]
128macro_rules! _refine_goto {
129 ($target:expr, $rcx:expr, $env:expr, $bb_env:expr) => {{
130 tracing::debug!(event = "refine_goto", target = ?$target, rcx = ?$rcx, env = ?$env, bb_env = ?$bb_env)
131 }};
132}
133pub use crate::_refine_goto as refine_goto;
134
135#[macro_export]
136macro_rules! _shape_goto_enter {
137 ($target:expr, $env:expr, $bb_env:expr) => {{
138 if let Some(bb_env) = &$bb_env {
139 tracing::debug!(event = "shape_goto_enter", target = ?$target, env = ?&$env, ?bb_env)
140 } else {
141 tracing::debug!(event = "shape_goto_enter", target = ?$target, env = ?&$env, bb_env = "empty")
142 }
143 }};
144}
145pub use crate::_shape_goto_enter as shape_goto_enter;
146
147#[macro_export]
148macro_rules! _shape_goto_exit {
149 ($target:expr, $bb_env:expr) => {{
150 tracing::debug!(event = "shape_goto_exit", target = ?$target, bb_env = ?&$bb_env)
151 }};
152}
153pub use crate::_shape_goto_exit as shape_goto_exit;
154
155#[macro_export]
156macro_rules! _hyperlink {
157 ($tcx:expr, $src_span:expr, $dst_span:expr) => {{
158 let src_json = SpanTrace::new($tcx, $src_span);
159 let dst_json = SpanTrace::new($tcx, $dst_span);
160 tracing::warn!(event = "hyperlink", src_span = ?src_json, dst_span = ?dst_json)
161 }};
162}
163pub use crate::_hyperlink as hyperlink;
164
165fn dump_base_name(tcx: TyCtxt, def_id: DefId, ext: impl AsRef<str>) -> String {
166 let crate_name = tcx.crate_name(def_id.krate);
167 let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate();
168 format!("{crate_name}.{item_name}.{}", ext.as_ref())
169}
170
171#[macro_export]
172macro_rules! _debug_assert_eq3 {
173 ($e1:expr, $e2:expr, $e3:expr) => {{
174 debug_assert!($e1 == $e2 && $e2 == $e3, "{:?} != {:?} != {:?}", $e1, $e2, $e3);
175 }};
176}
177pub use crate::_debug_assert_eq3 as debug_assert_eq3;