1use std::{
4 fmt::{Debug, Display, Write},
5 mem,
6};
7
8use either::Either;
9use hir_def::{expr_store::Body, hir::BindingId};
10use hir_expand::{Lookup, name::Name};
11use la_arena::ArenaMap;
12
13use crate::{
14 db::{HirDatabase, InternedClosureId},
15 display::{ClosureStyle, DisplayTarget, HirDisplay},
16 mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind},
17};
18
19use super::{
20 AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, MutBorrowKind, Operand, OperandKind,
21 Place, Rvalue, UnOp,
22};
23
24macro_rules! w {
25 ($dst:expr, $($arg:tt)*) => {
26 { let _ = write!($dst, $($arg)*); }
27 };
28}
29
30macro_rules! wln {
31 ($dst:expr) => {
32 { let _ = writeln!($dst); }
33 };
34 ($dst:expr, $($arg:tt)*) => {
35 { let _ = writeln!($dst, $($arg)*); }
36 };
37}
38
39impl<'db> MirBody<'db> {
40 pub fn pretty_print(&self, db: &'db dyn HirDatabase, display_target: DisplayTarget) -> String {
41 let hir_body = db.body(self.owner);
42 let mut ctx = MirPrettyCtx::new(self, &hir_body, db, display_target);
43 ctx.for_body(|this| match ctx.body.owner {
44 hir_def::DefWithBodyId::FunctionId(id) => {
45 let data = db.function_signature(id);
46 w!(this, "fn {}() ", data.name.display(db, this.display_target.edition));
47 }
48 hir_def::DefWithBodyId::StaticId(id) => {
49 let data = db.static_signature(id);
50 w!(this, "static {}: _ = ", data.name.display(db, this.display_target.edition));
51 }
52 hir_def::DefWithBodyId::ConstId(id) => {
53 let data = db.const_signature(id);
54 w!(
55 this,
56 "const {}: _ = ",
57 data.name
58 .as_ref()
59 .unwrap_or(&Name::missing())
60 .display(db, this.display_target.edition)
61 );
62 }
63 hir_def::DefWithBodyId::VariantId(id) => {
64 let loc = id.lookup(db);
65 let edition = this.display_target.edition;
66 w!(
67 this,
68 "enum {}::{} = ",
69 db.enum_signature(loc.parent).name.display(db, edition),
70 loc.parent
71 .enum_variants(db)
72 .variant_name_by_id(id)
73 .unwrap()
74 .display(db, edition),
75 )
76 }
77 });
78 ctx.result
79 }
80
81 pub fn dbg(&self, db: &'db dyn HirDatabase, display_target: DisplayTarget) -> impl Debug {
84 struct StringDbg(String);
85 impl Debug for StringDbg {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 f.write_str(&self.0)
88 }
89 }
90 StringDbg(self.pretty_print(db, display_target))
91 }
92}
93
94struct MirPrettyCtx<'a, 'db> {
95 body: &'a MirBody<'db>,
96 hir_body: &'a Body,
97 db: &'db dyn HirDatabase,
98 result: String,
99 indent: String,
100 local_to_binding: ArenaMap<LocalId<'db>, BindingId>,
101 display_target: DisplayTarget,
102}
103
104impl Write for MirPrettyCtx<'_, '_> {
105 fn write_str(&mut self, s: &str) -> std::fmt::Result {
106 let mut it = s.split('\n'); self.write(it.next().unwrap_or_default());
108 for line in it {
109 self.write_line();
110 self.write(line);
111 }
112 Ok(())
113 }
114}
115
116enum LocalName<'db> {
117 Unknown(LocalId<'db>),
118 Binding(Name, LocalId<'db>),
119}
120
121impl<'db> HirDisplay<'db> for LocalName<'db> {
122 fn hir_fmt(
123 &self,
124 f: &mut crate::display::HirFormatter<'_, 'db>,
125 ) -> Result<(), crate::display::HirDisplayError> {
126 match self {
127 LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())),
128 LocalName::Binding(n, l) => {
129 write!(f, "{}_{}", n.display(f.db, f.edition()), u32::from(l.into_raw()))
130 }
131 }
132 }
133}
134
135impl<'a, 'db> MirPrettyCtx<'a, 'db> {
136 fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_, 'db>)) {
137 name(self);
138 self.with_block(|this| {
139 this.locals();
140 wln!(this);
141 this.blocks();
142 });
143 for &closure in &self.body.closures {
144 self.for_closure(closure);
145 }
146 }
147
148 fn for_closure(&mut self, closure: InternedClosureId) {
149 let body = match self.db.mir_body_for_closure(closure) {
150 Ok(it) => it,
151 Err(e) => {
152 wln!(self, "// error in {closure:?}: {e:?}");
153 return;
154 }
155 };
156 let result = mem::take(&mut self.result);
157 let indent = mem::take(&mut self.indent);
158 let mut ctx = MirPrettyCtx {
159 body: &body,
160 local_to_binding: body.local_to_binding_map(),
161 result,
162 indent,
163 ..*self
164 };
165 ctx.for_body(|this| wln!(this, "// Closure: {:?}", closure));
166 self.result = ctx.result;
167 self.indent = ctx.indent;
168 }
169
170 fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_, 'db>)) {
171 self.indent += " ";
172 wln!(self, "{{");
173 f(self);
174 for _ in 0..4 {
175 self.result.pop();
176 self.indent.pop();
177 }
178 wln!(self, "}}");
179 }
180
181 fn new(
182 body: &'a MirBody<'db>,
183 hir_body: &'a Body,
184 db: &'db dyn HirDatabase,
185 display_target: DisplayTarget,
186 ) -> Self {
187 let local_to_binding = body.local_to_binding_map();
188 MirPrettyCtx {
189 body,
190 db,
191 result: String::new(),
192 indent: String::new(),
193 local_to_binding,
194 hir_body,
195 display_target,
196 }
197 }
198
199 fn write_line(&mut self) {
200 self.result.push('\n');
201 self.result += &self.indent;
202 }
203
204 fn write(&mut self, line: &str) {
205 self.result += line;
206 }
207
208 fn locals(&mut self) {
209 for (id, local) in self.body.locals.iter() {
210 wln!(
211 self,
212 "let {}: {};",
213 self.local_name(id).display_test(self.db, self.display_target),
214 self.hir_display(&local.ty)
215 );
216 }
217 }
218
219 fn local_name(&self, local: LocalId<'db>) -> LocalName<'db> {
220 match self.local_to_binding.get(local) {
221 Some(b) => LocalName::Binding(self.hir_body[*b].name.clone(), local),
222 None => LocalName::Unknown(local),
223 }
224 }
225
226 fn basic_block_id(&self, basic_block_id: BasicBlockId<'db>) -> String {
227 format!("'bb{}", u32::from(basic_block_id.into_raw()))
228 }
229
230 fn blocks(&mut self) {
231 for (id, block) in self.body.basic_blocks.iter() {
232 wln!(self);
233 w!(self, "{}: ", self.basic_block_id(id));
234 self.with_block(|this| {
235 for statement in &block.statements {
236 match &statement.kind {
237 StatementKind::Assign(l, r) => {
238 this.place(l);
239 w!(this, " = ");
240 this.rvalue(r);
241 wln!(this, ";");
242 }
243 StatementKind::StorageDead(p) => {
244 wln!(
245 this,
246 "StorageDead({})",
247 this.local_name(*p).display_test(this.db, this.display_target)
248 );
249 }
250 StatementKind::StorageLive(p) => {
251 wln!(
252 this,
253 "StorageLive({})",
254 this.local_name(*p).display_test(this.db, this.display_target)
255 );
256 }
257 StatementKind::Deinit(p) => {
258 w!(this, "Deinit(");
259 this.place(p);
260 wln!(this, ");");
261 }
262 StatementKind::FakeRead(p) => {
263 w!(this, "FakeRead(");
264 this.place(p);
265 wln!(this, ");");
266 }
267 StatementKind::Nop => wln!(this, "Nop;"),
268 }
269 }
270 match &block.terminator {
271 Some(terminator) => match &terminator.kind {
272 TerminatorKind::Goto { target } => {
273 wln!(this, "goto 'bb{};", u32::from(target.into_raw()))
274 }
275 TerminatorKind::SwitchInt { discr, targets } => {
276 w!(this, "switch ");
277 this.operand(discr);
278 w!(this, " ");
279 this.with_block(|this| {
280 for (c, b) in targets.iter() {
281 wln!(this, "{c} => {},", this.basic_block_id(b));
282 }
283 wln!(this, "_ => {},", this.basic_block_id(targets.otherwise()));
284 });
285 }
286 TerminatorKind::Call { func, args, destination, target, .. } => {
287 w!(this, "Call ");
288 this.with_block(|this| {
289 w!(this, "func: ");
290 this.operand(func);
291 wln!(this, ",");
292 w!(this, "args: [");
293 this.operand_list(args);
294 wln!(this, "],");
295 w!(this, "destination: ");
296 this.place(destination);
297 wln!(this, ",");
298 w!(this, "target: ");
299 match target {
300 Some(t) => w!(this, "{}", this.basic_block_id(*t)),
301 None => w!(this, "<unreachable>"),
302 }
303 wln!(this, ",");
304 });
305 }
306 _ => wln!(this, "{:?};", terminator),
307 },
308 None => wln!(this, "<no-terminator>;"),
309 }
310 })
311 }
312 }
313
314 fn place(&mut self, p: &Place<'db>) {
315 fn f<'db>(
316 this: &mut MirPrettyCtx<'_, 'db>,
317 local: LocalId<'db>,
318 projections: &[PlaceElem<'db>],
319 ) {
320 let Some((last, head)) = projections.split_last() else {
321 w!(this, "{}", this.local_name(local).display_test(this.db, this.display_target));
323 return;
324 };
325 match last {
326 ProjectionElem::Deref => {
327 w!(this, "(*");
328 f(this, local, head);
329 w!(this, ")");
330 }
331 ProjectionElem::Field(Either::Left(field)) => {
332 let variant_fields = field.parent.fields(this.db);
333 let name = &variant_fields.fields()[field.local_id].name;
334 match field.parent {
335 hir_def::VariantId::EnumVariantId(e) => {
336 w!(this, "(");
337 f(this, local, head);
338 let loc = e.lookup(this.db);
339 w!(
340 this,
341 " as {}).{}",
342 loc.parent.enum_variants(this.db).variants[loc.index as usize]
343 .1
344 .display(this.db, this.display_target.edition),
345 name.display(this.db, this.display_target.edition)
346 );
347 }
348 hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => {
349 f(this, local, head);
350 w!(this, ".{}", name.display(this.db, this.display_target.edition));
351 }
352 }
353 }
354 ProjectionElem::Field(Either::Right(field)) => {
355 f(this, local, head);
356 w!(this, ".{}", field.index);
357 }
358 ProjectionElem::ClosureField(it) => {
359 f(this, local, head);
360 w!(this, ".{}", it);
361 }
362 ProjectionElem::Index(l) => {
363 f(this, local, head);
364 w!(
365 this,
366 "[{}]",
367 this.local_name(*l).display_test(this.db, this.display_target)
368 );
369 }
370 it => {
371 f(this, local, head);
372 w!(this, ".{:?}", it);
373 }
374 }
375 }
376 f(self, p.local, p.projection.lookup(&self.body.projection_store));
377 }
378
379 fn operand(&mut self, r: &Operand<'db>) {
380 match &r.kind {
381 OperandKind::Copy(p) | OperandKind::Move(p) => {
382 self.place(p);
385 }
386 OperandKind::Constant { konst, .. } => w!(self, "Const({})", self.hir_display(konst)),
387 OperandKind::Static(s) => w!(self, "Static({:?})", s),
388 }
389 }
390
391 fn rvalue(&mut self, r: &Rvalue<'db>) {
392 match r {
393 Rvalue::Use(op) => self.operand(op),
394 Rvalue::Ref(r, p) => {
395 match r {
396 BorrowKind::Shared => w!(self, "&"),
397 BorrowKind::Shallow => w!(self, "&shallow "),
398 BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => w!(self, "&uniq "),
399 BorrowKind::Mut {
400 kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
401 } => w!(self, "&mut "),
402 }
403 self.place(p);
404 }
405 Rvalue::Aggregate(AggregateKind::Tuple(_), it) => {
406 w!(self, "(");
407 self.operand_list(it);
408 w!(self, ")");
409 }
410 Rvalue::Aggregate(AggregateKind::Array(_), it) => {
411 w!(self, "[");
412 self.operand_list(it);
413 w!(self, "]");
414 }
415 Rvalue::Repeat(op, len) => {
416 w!(self, "[");
417 self.operand(op);
418 w!(self, "; {}]", len.display_test(self.db, self.display_target));
419 }
420 Rvalue::Aggregate(AggregateKind::Adt(_, _), it) => {
421 w!(self, "Adt(");
422 self.operand_list(it);
423 w!(self, ")");
424 }
425 Rvalue::Aggregate(AggregateKind::Closure(_), it) => {
426 w!(self, "Closure(");
427 self.operand_list(it);
428 w!(self, ")");
429 }
430 Rvalue::Aggregate(AggregateKind::Union(_, _), it) => {
431 w!(self, "Union(");
432 self.operand_list(it);
433 w!(self, ")");
434 }
435 Rvalue::Len(p) => {
436 w!(self, "Len(");
437 self.place(p);
438 w!(self, ")");
439 }
440 Rvalue::Cast(ck, op, ty) => {
441 w!(self, "Cast({ck:?}, ");
442 self.operand(op);
443 w!(self, ", {})", self.hir_display(ty));
444 }
445 Rvalue::CheckedBinaryOp(b, o1, o2) => {
446 self.operand(o1);
447 w!(self, " {b} ");
448 self.operand(o2);
449 }
450 Rvalue::UnaryOp(u, o) => {
451 let u = match u {
452 UnOp::Not => "!",
453 UnOp::Neg => "-",
454 };
455 w!(self, "{u} ");
456 self.operand(o);
457 }
458 Rvalue::Discriminant(p) => {
459 w!(self, "Discriminant(");
460 self.place(p);
461 w!(self, ")");
462 }
463 Rvalue::ShallowInitBoxWithAlloc(_) => w!(self, "ShallowInitBoxWithAlloc"),
464 Rvalue::ShallowInitBox(op, _) => {
465 w!(self, "ShallowInitBox(");
466 self.operand(op);
467 w!(self, ")");
468 }
469 Rvalue::CopyForDeref(p) => {
470 w!(self, "CopyForDeref(");
471 self.place(p);
472 w!(self, ")");
473 }
474 Rvalue::ThreadLocalRef(n)
475 | Rvalue::AddressOf(n)
476 | Rvalue::BinaryOp(n)
477 | Rvalue::NullaryOp(n) => match *n {},
478 }
479 }
480
481 fn operand_list(&mut self, it: &[Operand<'db>]) {
482 let mut it = it.iter();
483 if let Some(first) = it.next() {
484 self.operand(first);
485 for op in it {
486 w!(self, ", ");
487 self.operand(op);
488 }
489 }
490 }
491
492 fn hir_display<'b, T: HirDisplay<'db>>(&self, ty: &'b T) -> impl Display + use<'a, 'b, 'db, T>
493 where
494 'db: 'b,
495 {
496 ty.display_test(self.db, self.display_target)
497 .with_closure_style(ClosureStyle::ClosureWithSubst)
498 }
499}