cairo_lang_lowering/
fmt.rs

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
17/// Holds all the information needed for formatting lowered representations.
18/// Acts like a "db" for DebugWithDb.
19pub 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}