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, StatementStructDestructure,
9 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 let outputs = match &self {
99 BlockEnd::Return(returns, _location) => {
100 write!(f, " Return(")?;
101 returns.iter().map(|var_usage| var_usage.var_id).collect()
102 }
103 BlockEnd::Panic(data) => {
104 write!(f, " Panic(")?;
105 vec![data.var_id]
106 }
107 BlockEnd::Goto(block_id, remapping) => {
108 return write!(f, " Goto({block_id:?}, {:?})", remapping.debug(ctx));
109 }
110 BlockEnd::NotSet => return write!(f, " Not set"),
111 BlockEnd::Match { info } => {
112 return write!(f, " Match({:?})", info.debug(ctx));
113 }
114 };
115 let mut outputs = outputs.iter().peekable();
116 while let Some(var) = outputs.next() {
117 write!(f, "v{:?}", var.index())?;
118 if outputs.peek().is_some() {
119 write!(f, ", ")?;
120 }
121 }
122 write!(f, ")")
123 }
124}
125
126fn format_var_with_ty(
127 var_id: VariableId,
128 f: &mut std::fmt::Formatter<'_>,
129 ctx: &LoweredFormatter<'_>,
130) -> std::fmt::Result {
131 write!(f, "v{:?}", var_id.index())?;
132 let var = &ctx.variables[var_id];
133 write!(f, ": {}", var.ty.format(ctx.db))
134}
135
136impl<'db> DebugWithDb<'db> for VarUsage<'db> {
137 type Db = LoweredFormatter<'db>;
138
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
140 write!(f, "v{:?}", self.var_id.index(),)?;
141 if ctx.include_usage_location {
142 write!(
143 f,
144 "{{`{}`}}",
145 self.location
146 .long(ctx.db)
147 .stable_location
148 .syntax_node(ctx.db)
149 .get_text_without_trivia(ctx.db)
150 .long(ctx.db)
151 .lines()
152 .map(|s| s.trim())
153 .join(" ")
154 )?;
155 }
156 Ok(())
157 }
158}
159
160impl<'db> DebugWithDbOverride<'db, LoweredFormatter<'db>> for VariableId {
161 fn fmt_override(
162 &self,
163 f: &mut std::fmt::Formatter<'_>,
164 _db: &'db LoweredFormatter<'db>,
165 ) -> std::fmt::Result {
166 write!(f, "v{:?}", self.index())
167 }
168}
169
170impl<'db> DebugWithDb<'db> for Statement<'db> {
171 type Db = LoweredFormatter<'db>;
172
173 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
174 write!(f, "(")?;
175 let mut outputs = self.outputs().iter().peekable();
176 while let Some(var) = outputs.next() {
177 format_var_with_ty(*var, f, ctx)?;
178 if outputs.peek().is_some() {
179 write!(f, ", ")?;
180 }
181 }
182 write!(f, ") <- ")?;
183 match self {
184 Statement::Const(stmt) => stmt.fmt(f, ctx),
185 Statement::Call(stmt) => stmt.fmt(f, ctx),
186 Statement::StructConstruct(stmt) => stmt.fmt(f, ctx),
187 Statement::StructDestructure(stmt) => stmt.fmt(f, ctx),
188 Statement::EnumConstruct(stmt) => stmt.fmt(f, ctx),
189 Statement::Snapshot(stmt) => stmt.fmt(f, ctx),
190 Statement::Desnap(stmt) => stmt.fmt(f, ctx),
191 }
192 }
193}
194
195impl<'db> DebugWithDb<'db> for MatchInfo<'db> {
196 type Db = LoweredFormatter<'db>;
197
198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
199 match self {
200 MatchInfo::Extern(s) => s.fmt(f, ctx),
201 MatchInfo::Enum(s) => s.fmt(f, ctx),
202 MatchInfo::Value(s) => s.fmt(f, ctx),
203 }
204 }
205}
206
207impl<'db> DebugWithDb<'db> for StatementConst<'db> {
208 type Db = LoweredFormatter<'db>;
209
210 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
211 self.value.fmt(f, ctx.db)?;
212 if self.boxed {
213 write!(f, ".into_box()")?
214 }
215 Ok(())
216 }
217}
218
219impl<'db> DebugWithDb<'db> for StatementCall<'db> {
220 type Db = LoweredFormatter<'db>;
221
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
223 write!(f, "{:?}(", self.function.long(ctx.db).debug(ctx.db))?;
224 for (i, var) in self.inputs.iter().enumerate() {
225 let is_last = i == self.inputs.len() - 1;
226 if is_last && self.with_coupon {
227 write!(f, "__coupon__: ")?;
228 }
229 var.fmt(f, ctx)?;
230 if !is_last {
231 write!(f, ", ")?;
232 }
233 }
234 write!(f, ")")
235 }
236}
237
238impl<'db> DebugWithDb<'db> for MatchExternInfo<'db> {
239 type Db = LoweredFormatter<'db>;
240
241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
242 write!(f, "match {:?}(", self.function.long(ctx.db).debug(ctx.db))?;
243 let mut inputs = self.inputs.iter().peekable();
244 while let Some(var) = inputs.next() {
245 var.fmt(f, ctx)?;
246 if inputs.peek().is_some() {
247 write!(f, ", ")?;
248 }
249 }
250 writeln!(f, ") {{")?;
251 for arm in &self.arms {
252 arm.fmt(f, ctx)?;
253 writeln!(f)?;
254 }
255 write!(f, " }}")
256 }
257}
258
259impl<'db> DebugWithDb<'db> for MatchArm<'db> {
260 type Db = LoweredFormatter<'db>;
261
262 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
263 write!(f, " {:?}", self.arm_selector.debug(ctx.db))?;
264
265 if !self.var_ids.is_empty() {
266 write!(f, "(")?;
267 let mut var_ids = self.var_ids.iter().peekable();
268 while let Some(var_id) = var_ids.next() {
269 write!(f, "v{:?}", var_id.index())?;
270 if var_ids.peek().is_some() {
271 write!(f, ", ")?;
272 }
273 }
274 write!(f, ")")?;
275 }
276
277 write!(f, " => {:?},", self.block_id)
278 }
279}
280
281impl<'db> DebugWithDb<'db> for MatchEnumInfo<'db> {
282 type Db = LoweredFormatter<'db>;
283
284 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
285 write!(f, "match_enum(")?;
286 self.input.fmt(f, ctx)?;
287 writeln!(f, ") {{")?;
288 for arm in &self.arms {
289 arm.fmt(f, ctx)?;
290 writeln!(f)?;
291 }
292 write!(f, " }}")
293 }
294}
295
296impl<'db> DebugWithDb<'db> for MatchEnumValue<'db> {
297 type Db = LoweredFormatter<'db>;
298
299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
300 write!(f, "match_enum.(")?;
301 self.input.fmt(f, ctx)?;
302 writeln!(f, ") {{")?;
303 for arm in &self.arms {
304 arm.fmt(f, ctx)?;
305 writeln!(f)?;
306 }
307 write!(f, " }}")
308 }
309}
310
311impl<'db> DebugWithDb<'db> for StatementEnumConstruct<'db> {
312 type Db = LoweredFormatter<'db>;
313
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
315 let enum_name = self.variant.concrete_enum_id.enum_id(ctx.db).name(ctx.db).long(ctx.db);
316 let variant_name = self.variant.id.name(ctx.db).long(ctx.db);
317 write!(f, "{enum_name}::{variant_name}(",)?;
318 self.input.fmt(f, ctx)?;
319 write!(f, ")")
320 }
321}
322
323impl<'db> DebugWithDb<'db> for StatementStructConstruct<'db> {
324 type Db = LoweredFormatter<'db>;
325
326 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
327 write!(f, "struct_construct(")?;
328 let mut inputs = self.inputs.iter().peekable();
329 while let Some(var) = inputs.next() {
330 var.fmt(f, ctx)?;
331 if inputs.peek().is_some() {
332 write!(f, ", ")?;
333 }
334 }
335 write!(f, ")")
336 }
337}
338
339impl<'db> DebugWithDb<'db> for StatementStructDestructure<'db> {
340 type Db = LoweredFormatter<'db>;
341
342 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
343 write!(f, "struct_destructure(")?;
344 self.input.fmt(f, ctx)?;
345 write!(f, ")")
346 }
347}
348
349impl<'db> DebugWithDb<'db> for StatementSnapshot<'db> {
350 type Db = LoweredFormatter<'db>;
351
352 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
353 write!(f, "snapshot(")?;
354 self.input.fmt(f, ctx)?;
355 write!(f, ")")
356 }
357}
358
359impl<'db> DebugWithDb<'db> for StatementDesnap<'db> {
360 type Db = LoweredFormatter<'db>;
361
362 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
363 write!(f, "desnap(")?;
364 self.input.fmt(f, ctx)?;
365 write!(f, ")")
366 }
367}