1use cairo_lang_debug::DebugWithDb;
2use cairo_lang_debug::debug::DebugWithDbOverride;
3use cairo_lang_defs::ids::NamedLanguageElementId;
4use itertools::Itertools;
5use salsa::Database;
6
7use crate::objects::{
8 MatchExternInfo, Statement, StatementCall, StatementConst, StatementIntoBox,
9 StatementStructDestructure, StatementUnbox, VariableId,
10};
11use crate::{
12 Block, BlockEnd, Lowered, MatchArm, MatchEnumInfo, MatchEnumValue, MatchInfo, StatementDesnap,
13 StatementEnumConstruct, StatementSnapshot, StatementStructConstruct, VarRemapping, VarUsage,
14 VariableArena,
15};
16
17pub struct LoweredFormatter<'db> {
20 pub db: &'db dyn Database,
21 pub variables: &'db VariableArena<'db>,
22 pub include_usage_location: bool,
23}
24impl<'db> LoweredFormatter<'db> {
25 pub fn new(db: &'db dyn Database, variables: &'db VariableArena<'db>) -> Self {
26 Self { db, variables, include_usage_location: false }
27 }
28}
29
30impl<'db> DebugWithDb<'db> for VarRemapping<'db> {
31 type Db = LoweredFormatter<'db>;
32
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, _ctx: &Self::Db) -> std::fmt::Result {
34 let mut remapping = self.iter().peekable();
35 write!(f, "{{")?;
36 while let Some((dst, src)) = remapping.next() {
37 write!(f, "v{:?} -> v{:?}", src.var_id.index(), dst.index())?;
38 if remapping.peek().is_some() {
39 write!(f, ", ")?;
40 }
41 }
42 write!(f, "}}")?;
43 Ok(())
44 }
45}
46impl<'db> DebugWithDb<'db> for Lowered<'db> {
47 type Db = LoweredFormatter<'db>;
48
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
50 write!(f, "Parameters:")?;
51 let mut inputs = self.parameters.iter().peekable();
52 while let Some(var) = inputs.next() {
53 write!(f, " ")?;
54 format_var_with_ty(*var, f, ctx)?;
55 if inputs.peek().is_some() {
56 write!(f, ",")?;
57 }
58 }
59 writeln!(f)?;
60 let mut blocks = self.blocks.iter();
61 if let Some((root_block_id, root_block)) = blocks.next() {
62 write!(f, "{root_block_id:?}")?;
63 writeln!(f, " (root):")?;
64 root_block.fmt(f, ctx)?;
65 writeln!(f)?;
66 }
67 for (block_id, block) in blocks {
68 write!(f, "{block_id:?}")?;
69 writeln!(f, ":")?;
70 block.fmt(f, ctx)?;
71 writeln!(f)?;
72 }
73 Ok(())
74 }
75}
76
77impl<'db> DebugWithDb<'db> for Block<'db> {
78 type Db = LoweredFormatter<'db>;
79
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
81 writeln!(f, "Statements:")?;
82 for stmt in &self.statements {
83 write!(f, " ")?;
84 stmt.fmt(f, ctx)?;
85 writeln!(f)?;
86 }
87
88 writeln!(f, "End:")?;
89 self.end.fmt(f, ctx)?;
90 writeln!(f)
91 }
92}
93
94impl<'db> DebugWithDb<'db> for BlockEnd<'db> {
95 type Db = LoweredFormatter<'db>;
96
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
98 match &self {
99 BlockEnd::Return(returns, _location) => {
100 write!(f, " Return(")?;
101 let mut it = returns.iter().peekable();
102 while let Some(var_usage) = it.next() {
103 write!(f, "v{:?}", var_usage.var_id.index())?;
104 if it.peek().is_some() {
105 write!(f, ", ")?;
106 }
107 }
108 write!(f, ")")
109 }
110 BlockEnd::Panic(data) => {
111 write!(f, " Panic(v{:?})", data.var_id.index())
112 }
113 BlockEnd::Goto(block_id, remapping) => {
114 write!(f, " Goto({block_id:?}, {:?})", remapping.debug(ctx))
115 }
116 BlockEnd::NotSet => write!(f, " Not set"),
117 BlockEnd::Match { info } => write!(f, " Match({:?})", info.debug(ctx)),
118 }
119 }
120}
121
122fn format_var_with_ty(
123 var_id: VariableId,
124 f: &mut std::fmt::Formatter<'_>,
125 ctx: &LoweredFormatter<'_>,
126) -> std::fmt::Result {
127 write!(f, "v{:?}", var_id.index())?;
128 let var = &ctx.variables[var_id];
129 write!(f, ": {}", var.ty.format(ctx.db))
130}
131
132impl<'db> DebugWithDb<'db> for VarUsage<'db> {
133 type Db = LoweredFormatter<'db>;
134
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
136 write!(f, "v{:?}", self.var_id.index(),)?;
137 if ctx.include_usage_location {
138 write!(
139 f,
140 "{{`{}`}}",
141 self.location
142 .long(ctx.db)
143 .stable_location
144 .syntax_node(ctx.db)
145 .get_text_without_trivia(ctx.db)
146 .long(ctx.db)
147 .lines()
148 .map(|s| s.trim())
149 .join(" ")
150 )?;
151 }
152 Ok(())
153 }
154}
155
156impl<'db> DebugWithDbOverride<'db, LoweredFormatter<'db>> for VariableId {
157 fn fmt_override(
158 &self,
159 f: &mut std::fmt::Formatter<'_>,
160 _db: &'db LoweredFormatter<'db>,
161 ) -> std::fmt::Result {
162 write!(f, "v{:?}", self.index())
163 }
164}
165
166impl<'db> DebugWithDb<'db> for Statement<'db> {
167 type Db = LoweredFormatter<'db>;
168
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
170 write!(f, "(")?;
171 let mut outputs = self.outputs().iter().peekable();
172 while let Some(var) = outputs.next() {
173 format_var_with_ty(*var, f, ctx)?;
174 if outputs.peek().is_some() {
175 write!(f, ", ")?;
176 }
177 }
178 write!(f, ") <- ")?;
179 match self {
180 Statement::Const(stmt) => stmt.fmt(f, ctx),
181 Statement::Call(stmt) => stmt.fmt(f, ctx),
182 Statement::StructConstruct(stmt) => stmt.fmt(f, ctx),
183 Statement::StructDestructure(stmt) => stmt.fmt(f, ctx),
184 Statement::EnumConstruct(stmt) => stmt.fmt(f, ctx),
185 Statement::Snapshot(stmt) => stmt.fmt(f, ctx),
186 Statement::Desnap(stmt) => stmt.fmt(f, ctx),
187 Statement::IntoBox(stmt) => stmt.fmt(f, ctx),
188 Statement::Unbox(stmt) => stmt.fmt(f, ctx),
189 }
190 }
191}
192
193impl<'db> DebugWithDb<'db> for MatchInfo<'db> {
194 type Db = LoweredFormatter<'db>;
195
196 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
197 match self {
198 MatchInfo::Extern(s) => s.fmt(f, ctx),
199 MatchInfo::Enum(s) => s.fmt(f, ctx),
200 MatchInfo::Value(s) => s.fmt(f, ctx),
201 }
202 }
203}
204
205impl<'db> DebugWithDb<'db> for StatementConst<'db> {
206 type Db = LoweredFormatter<'db>;
207
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
209 self.value.fmt(f, ctx.db)?;
210 if self.boxed {
211 write!(f, ".into_box()")?
212 }
213 Ok(())
214 }
215}
216
217impl<'db> DebugWithDb<'db> for StatementCall<'db> {
218 type Db = LoweredFormatter<'db>;
219
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
221 write!(f, "{:?}(", self.function.long(ctx.db).debug(ctx.db))?;
222 for (i, var) in self.inputs.iter().enumerate() {
223 let is_last = i == self.inputs.len() - 1;
224 if is_last && self.with_coupon {
225 write!(f, "__coupon__: ")?;
226 }
227 var.fmt(f, ctx)?;
228 if !is_last {
229 write!(f, ", ")?;
230 }
231 }
232 write!(f, ")")
233 }
234}
235
236impl<'db> DebugWithDb<'db> for MatchExternInfo<'db> {
237 type Db = LoweredFormatter<'db>;
238
239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
240 write!(f, "match {:?}(", self.function.long(ctx.db).debug(ctx.db))?;
241 let mut inputs = self.inputs.iter().peekable();
242 while let Some(var) = inputs.next() {
243 var.fmt(f, ctx)?;
244 if inputs.peek().is_some() {
245 write!(f, ", ")?;
246 }
247 }
248 writeln!(f, ") {{")?;
249 for arm in &self.arms {
250 arm.fmt(f, ctx)?;
251 writeln!(f)?;
252 }
253 write!(f, " }}")
254 }
255}
256
257impl<'db> DebugWithDb<'db> for MatchArm<'db> {
258 type Db = LoweredFormatter<'db>;
259
260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
261 write!(f, " {:?}", self.arm_selector.debug(ctx.db))?;
262
263 if !self.var_ids.is_empty() {
264 write!(f, "(")?;
265 let mut var_ids = self.var_ids.iter().peekable();
266 while let Some(var_id) = var_ids.next() {
267 write!(f, "v{:?}", var_id.index())?;
268 if var_ids.peek().is_some() {
269 write!(f, ", ")?;
270 }
271 }
272 write!(f, ")")?;
273 }
274
275 write!(f, " => {:?},", self.block_id)
276 }
277}
278
279impl<'db> DebugWithDb<'db> for MatchEnumInfo<'db> {
280 type Db = LoweredFormatter<'db>;
281
282 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
283 write!(f, "match_enum(")?;
284 self.input.fmt(f, ctx)?;
285 writeln!(f, ") {{")?;
286 for arm in &self.arms {
287 arm.fmt(f, ctx)?;
288 writeln!(f)?;
289 }
290 write!(f, " }}")
291 }
292}
293
294impl<'db> DebugWithDb<'db> for MatchEnumValue<'db> {
295 type Db = LoweredFormatter<'db>;
296
297 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
298 write!(f, "match_enum.(")?;
299 self.input.fmt(f, ctx)?;
300 writeln!(f, ") {{")?;
301 for arm in &self.arms {
302 arm.fmt(f, ctx)?;
303 writeln!(f)?;
304 }
305 write!(f, " }}")
306 }
307}
308
309impl<'db> DebugWithDb<'db> for StatementEnumConstruct<'db> {
310 type Db = LoweredFormatter<'db>;
311
312 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
313 let enum_name = self.variant.concrete_enum_id.enum_id(ctx.db).name(ctx.db).long(ctx.db);
314 let variant_name = self.variant.id.name(ctx.db).long(ctx.db);
315 write!(f, "{enum_name}::{variant_name}(",)?;
316 self.input.fmt(f, ctx)?;
317 write!(f, ")")
318 }
319}
320
321impl<'db> DebugWithDb<'db> for StatementStructConstruct<'db> {
322 type Db = LoweredFormatter<'db>;
323
324 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
325 write!(f, "struct_construct(")?;
326 let mut inputs = self.inputs.iter().peekable();
327 while let Some(var) = inputs.next() {
328 var.fmt(f, ctx)?;
329 if inputs.peek().is_some() {
330 write!(f, ", ")?;
331 }
332 }
333 write!(f, ")")
334 }
335}
336
337impl<'db> DebugWithDb<'db> for StatementStructDestructure<'db> {
338 type Db = LoweredFormatter<'db>;
339
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
341 write!(f, "struct_destructure(")?;
342 self.input.fmt(f, ctx)?;
343 write!(f, ")")
344 }
345}
346
347impl<'db> DebugWithDb<'db> for StatementSnapshot<'db> {
348 type Db = LoweredFormatter<'db>;
349
350 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
351 write!(f, "snapshot(")?;
352 self.input.fmt(f, ctx)?;
353 write!(f, ")")
354 }
355}
356
357impl<'db> DebugWithDb<'db> for StatementDesnap<'db> {
358 type Db = LoweredFormatter<'db>;
359
360 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
361 write!(f, "desnap(")?;
362 self.input.fmt(f, ctx)?;
363 write!(f, ")")
364 }
365}
366
367impl<'db> DebugWithDb<'db> for StatementIntoBox<'db> {
368 type Db = LoweredFormatter<'db>;
369
370 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
371 write!(f, "into_box(")?;
372 self.input.fmt(f, ctx)?;
373 write!(f, ")")
374 }
375}
376
377impl<'db> DebugWithDb<'db> for StatementUnbox<'db> {
378 type Db = LoweredFormatter<'db>;
379
380 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
381 write!(f, "unbox(")?;
382 self.input.fmt(f, ctx)?;
383 write!(f, ")")
384 }
385}