• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! A pretty-printer for HIR.
2 
3 use std::fmt::{self, Write};
4 
5 use hir_expand::db::ExpandDatabase;
6 use syntax::ast::HasName;
7 
8 use crate::{
9     hir::{
10         Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, LiteralOrConst,
11         Movability, Statement,
12     },
13     pretty::{print_generic_args, print_path, print_type_ref},
14     type_ref::TypeRef,
15 };
16 
17 use super::*;
18 
print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String19 pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
20     let header = match owner {
21         DefWithBodyId::FunctionId(it) => {
22             let item_tree_id = it.lookup(db).id;
23             format!(
24                 "fn {}",
25                 item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
26             )
27         }
28         DefWithBodyId::StaticId(it) => {
29             let item_tree_id = it.lookup(db).id;
30             format!(
31                 "static {} = ",
32                 item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
33             )
34         }
35         DefWithBodyId::ConstId(it) => {
36             let item_tree_id = it.lookup(db).id;
37             let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name {
38                 Some(name) => name.display(db.upcast()).to_string(),
39                 None => "_".to_string(),
40             };
41             format!("const {name} = ")
42         }
43         DefWithBodyId::InTypeConstId(_) => format!("In type const = "),
44         DefWithBodyId::VariantId(it) => {
45             let src = it.parent.child_source(db);
46             let variant = &src.value[it.local_id];
47             match &variant.name() {
48                 Some(name) => name.to_string(),
49                 None => "_".to_string(),
50             }
51         }
52     };
53 
54     let mut p =
55         Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
56     if let DefWithBodyId::FunctionId(it) = owner {
57         p.buf.push('(');
58         body.params.iter().zip(&db.function_data(it).params).for_each(|(&param, ty)| {
59             p.print_pat(param);
60             p.buf.push(':');
61             p.print_type_ref(ty);
62         });
63         p.buf.push(')');
64         p.buf.push(' ');
65     }
66     p.print_expr(body.body_expr);
67     if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
68         p.buf.push(';');
69     }
70     p.buf
71 }
72 
print_expr_hir( db: &dyn DefDatabase, body: &Body, _owner: DefWithBodyId, expr: ExprId, ) -> String73 pub(super) fn print_expr_hir(
74     db: &dyn DefDatabase,
75     body: &Body,
76     _owner: DefWithBodyId,
77     expr: ExprId,
78 ) -> String {
79     let mut p =
80         Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
81     p.print_expr(expr);
82     p.buf
83 }
84 
85 macro_rules! w {
86     ($dst:expr, $($arg:tt)*) => {
87         { let _ = write!($dst, $($arg)*); }
88     };
89 }
90 
91 macro_rules! wln {
92     ($dst:expr) => {
93         { let _ = writeln!($dst); }
94     };
95     ($dst:expr, $($arg:tt)*) => {
96         { let _ = writeln!($dst, $($arg)*); }
97     };
98 }
99 
100 struct Printer<'a> {
101     db: &'a dyn ExpandDatabase,
102     body: &'a Body,
103     buf: String,
104     indent_level: usize,
105     needs_indent: bool,
106 }
107 
108 impl<'a> Write for Printer<'a> {
write_str(&mut self, s: &str) -> fmt::Result109     fn write_str(&mut self, s: &str) -> fmt::Result {
110         for line in s.split_inclusive('\n') {
111             if self.needs_indent {
112                 match self.buf.chars().rev().find(|ch| *ch != ' ') {
113                     Some('\n') | None => {}
114                     _ => self.buf.push('\n'),
115                 }
116                 self.buf.push_str(&"    ".repeat(self.indent_level));
117                 self.needs_indent = false;
118             }
119 
120             self.buf.push_str(line);
121             self.needs_indent = line.ends_with('\n');
122         }
123 
124         Ok(())
125     }
126 }
127 
128 impl<'a> Printer<'a> {
indented(&mut self, f: impl FnOnce(&mut Self))129     fn indented(&mut self, f: impl FnOnce(&mut Self)) {
130         self.indent_level += 1;
131         wln!(self);
132         f(self);
133         self.indent_level -= 1;
134         self.buf = self.buf.trim_end_matches('\n').to_string();
135     }
136 
whitespace(&mut self)137     fn whitespace(&mut self) {
138         match self.buf.chars().next_back() {
139             None | Some('\n' | ' ') => {}
140             _ => self.buf.push(' '),
141         }
142     }
143 
newline(&mut self)144     fn newline(&mut self) {
145         match self.buf.chars().rev().find(|ch| *ch != ' ') {
146             Some('\n') | None => {}
147             _ => writeln!(self).unwrap(),
148         }
149     }
150 
print_expr(&mut self, expr: ExprId)151     fn print_expr(&mut self, expr: ExprId) {
152         let expr = &self.body[expr];
153 
154         match expr {
155             Expr::Missing => w!(self, "�"),
156             Expr::Underscore => w!(self, "_"),
157             Expr::Path(path) => self.print_path(path),
158             Expr::If { condition, then_branch, else_branch } => {
159                 w!(self, "if ");
160                 self.print_expr(*condition);
161                 w!(self, " ");
162                 self.print_expr(*then_branch);
163                 if let Some(els) = *else_branch {
164                     w!(self, " else ");
165                     self.print_expr(els);
166                 }
167             }
168             Expr::Let { pat, expr } => {
169                 w!(self, "let ");
170                 self.print_pat(*pat);
171                 w!(self, " = ");
172                 self.print_expr(*expr);
173             }
174             Expr::Loop { body, label } => {
175                 if let Some(lbl) = label {
176                     w!(self, "{}: ", self.body[*lbl].name.display(self.db));
177                 }
178                 w!(self, "loop ");
179                 self.print_expr(*body);
180             }
181             Expr::While { condition, body, label } => {
182                 if let Some(lbl) = label {
183                     w!(self, "{}: ", self.body[*lbl].name.display(self.db));
184                 }
185                 w!(self, "while ");
186                 self.print_expr(*condition);
187                 self.print_expr(*body);
188             }
189             Expr::Call { callee, args, is_assignee_expr: _ } => {
190                 self.print_expr(*callee);
191                 w!(self, "(");
192                 if !args.is_empty() {
193                     self.indented(|p| {
194                         for arg in &**args {
195                             p.print_expr(*arg);
196                             wln!(p, ",");
197                         }
198                     });
199                 }
200                 w!(self, ")");
201             }
202             Expr::MethodCall { receiver, method_name, args, generic_args } => {
203                 self.print_expr(*receiver);
204                 w!(self, ".{}", method_name.display(self.db));
205                 if let Some(args) = generic_args {
206                     w!(self, "::<");
207                     print_generic_args(self.db, args, self).unwrap();
208                     w!(self, ">");
209                 }
210                 w!(self, "(");
211                 if !args.is_empty() {
212                     self.indented(|p| {
213                         for arg in &**args {
214                             p.print_expr(*arg);
215                             wln!(p, ",");
216                         }
217                     });
218                 }
219                 w!(self, ")");
220             }
221             Expr::Match { expr, arms } => {
222                 w!(self, "match ");
223                 self.print_expr(*expr);
224                 w!(self, " {{");
225                 self.indented(|p| {
226                     for arm in &**arms {
227                         p.print_pat(arm.pat);
228                         if let Some(guard) = arm.guard {
229                             w!(p, " if ");
230                             p.print_expr(guard);
231                         }
232                         w!(p, " => ");
233                         p.print_expr(arm.expr);
234                         wln!(p, ",");
235                     }
236                 });
237                 wln!(self, "}}");
238             }
239             Expr::Continue { label } => {
240                 w!(self, "continue");
241                 if let Some(lbl) = label {
242                     w!(self, " {}", self.body[*lbl].name.display(self.db));
243                 }
244             }
245             Expr::Break { expr, label } => {
246                 w!(self, "break");
247                 if let Some(lbl) = label {
248                     w!(self, " {}", self.body[*lbl].name.display(self.db));
249                 }
250                 if let Some(expr) = expr {
251                     self.whitespace();
252                     self.print_expr(*expr);
253                 }
254             }
255             Expr::Return { expr } => {
256                 w!(self, "return");
257                 if let Some(expr) = expr {
258                     self.whitespace();
259                     self.print_expr(*expr);
260                 }
261             }
262             Expr::Yield { expr } => {
263                 w!(self, "yield");
264                 if let Some(expr) = expr {
265                     self.whitespace();
266                     self.print_expr(*expr);
267                 }
268             }
269             Expr::Yeet { expr } => {
270                 w!(self, "do");
271                 self.whitespace();
272                 w!(self, "yeet");
273                 if let Some(expr) = expr {
274                     self.whitespace();
275                     self.print_expr(*expr);
276                 }
277             }
278             Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr: _ } => {
279                 match path {
280                     Some(path) => self.print_path(path),
281                     None => w!(self, "�"),
282                 }
283 
284                 w!(self, "{{");
285                 self.indented(|p| {
286                     for field in &**fields {
287                         w!(p, "{}: ", field.name.display(self.db));
288                         p.print_expr(field.expr);
289                         wln!(p, ",");
290                     }
291                     if let Some(spread) = spread {
292                         w!(p, "..");
293                         p.print_expr(*spread);
294                         wln!(p);
295                     }
296                     if *ellipsis {
297                         wln!(p, "..");
298                     }
299                 });
300                 w!(self, "}}");
301             }
302             Expr::Field { expr, name } => {
303                 self.print_expr(*expr);
304                 w!(self, ".{}", name.display(self.db));
305             }
306             Expr::Await { expr } => {
307                 self.print_expr(*expr);
308                 w!(self, ".await");
309             }
310             Expr::Cast { expr, type_ref } => {
311                 self.print_expr(*expr);
312                 w!(self, " as ");
313                 self.print_type_ref(type_ref);
314             }
315             Expr::Ref { expr, rawness, mutability } => {
316                 w!(self, "&");
317                 if rawness.is_raw() {
318                     w!(self, "raw ");
319                 }
320                 if mutability.is_mut() {
321                     w!(self, "mut ");
322                 }
323                 self.print_expr(*expr);
324             }
325             Expr::Box { expr } => {
326                 w!(self, "box ");
327                 self.print_expr(*expr);
328             }
329             Expr::UnaryOp { expr, op } => {
330                 let op = match op {
331                     ast::UnaryOp::Deref => "*",
332                     ast::UnaryOp::Not => "!",
333                     ast::UnaryOp::Neg => "-",
334                 };
335                 w!(self, "{}", op);
336                 self.print_expr(*expr);
337             }
338             Expr::BinaryOp { lhs, rhs, op } => {
339                 let (bra, ket) = match op {
340                     None | Some(ast::BinaryOp::Assignment { .. }) => ("", ""),
341                     _ => ("(", ")"),
342                 };
343                 w!(self, "{}", bra);
344                 self.print_expr(*lhs);
345                 w!(self, "{} ", ket);
346                 match op {
347                     Some(op) => w!(self, "{}", op),
348                     None => w!(self, "�"), // :)
349                 }
350                 w!(self, " {}", bra);
351                 self.print_expr(*rhs);
352                 w!(self, "{}", ket);
353             }
354             Expr::Range { lhs, rhs, range_type } => {
355                 if let Some(lhs) = lhs {
356                     w!(self, "(");
357                     self.print_expr(*lhs);
358                     w!(self, ") ");
359                 }
360                 let range = match range_type {
361                     ast::RangeOp::Exclusive => "..",
362                     ast::RangeOp::Inclusive => "..=",
363                 };
364                 w!(self, "{}", range);
365                 if let Some(rhs) = rhs {
366                     w!(self, "(");
367                     self.print_expr(*rhs);
368                     w!(self, ") ");
369                 }
370             }
371             Expr::Index { base, index } => {
372                 self.print_expr(*base);
373                 w!(self, "[");
374                 self.print_expr(*index);
375                 w!(self, "]");
376             }
377             Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
378                 match closure_kind {
379                     ClosureKind::Generator(Movability::Static) => {
380                         w!(self, "static ");
381                     }
382                     ClosureKind::Async => {
383                         w!(self, "async ");
384                     }
385                     _ => (),
386                 }
387                 match capture_by {
388                     CaptureBy::Value => {
389                         w!(self, "move ");
390                     }
391                     CaptureBy::Ref => (),
392                 }
393                 w!(self, "|");
394                 for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
395                     if i != 0 {
396                         w!(self, ", ");
397                     }
398                     self.print_pat(*pat);
399                     if let Some(ty) = ty {
400                         w!(self, ": ");
401                         self.print_type_ref(ty);
402                     }
403                 }
404                 w!(self, "|");
405                 if let Some(ret_ty) = ret_type {
406                     w!(self, " -> ");
407                     self.print_type_ref(ret_ty);
408                 }
409                 self.whitespace();
410                 self.print_expr(*body);
411             }
412             Expr::Tuple { exprs, is_assignee_expr: _ } => {
413                 w!(self, "(");
414                 for expr in exprs.iter() {
415                     self.print_expr(*expr);
416                     w!(self, ", ");
417                 }
418                 w!(self, ")");
419             }
420             Expr::Array(arr) => {
421                 w!(self, "[");
422                 if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) {
423                     self.indented(|p| match arr {
424                         Array::ElementList { elements, is_assignee_expr: _ } => {
425                             for elem in elements.iter() {
426                                 p.print_expr(*elem);
427                                 w!(p, ", ");
428                             }
429                         }
430                         Array::Repeat { initializer, repeat } => {
431                             p.print_expr(*initializer);
432                             w!(p, "; ");
433                             p.print_expr(*repeat);
434                         }
435                     });
436                     self.newline();
437                 }
438                 w!(self, "]");
439             }
440             Expr::Literal(lit) => self.print_literal(lit),
441             Expr::Block { id: _, statements, tail, label } => {
442                 let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db)));
443                 self.print_block(label.as_deref(), statements, tail);
444             }
445             Expr::Unsafe { id: _, statements, tail } => {
446                 self.print_block(Some("unsafe "), statements, tail);
447             }
448             Expr::Async { id: _, statements, tail } => {
449                 self.print_block(Some("async "), statements, tail);
450             }
451             Expr::Const(id) => {
452                 w!(self, "const {{ /* {id:?} */ }}");
453             }
454         }
455     }
456 
print_block( &mut self, label: Option<&str>, statements: &[Statement], tail: &Option<la_arena::Idx<Expr>>, )457     fn print_block(
458         &mut self,
459         label: Option<&str>,
460         statements: &[Statement],
461         tail: &Option<la_arena::Idx<Expr>>,
462     ) {
463         self.whitespace();
464         if let Some(lbl) = label {
465             w!(self, "{}", lbl);
466         }
467         w!(self, "{{");
468         if !statements.is_empty() || tail.is_some() {
469             self.indented(|p| {
470                 for stmt in statements {
471                     p.print_stmt(stmt);
472                 }
473                 if let Some(tail) = tail {
474                     p.print_expr(*tail);
475                 }
476                 p.newline();
477             });
478         }
479         w!(self, "}}");
480     }
481 
print_pat(&mut self, pat: PatId)482     fn print_pat(&mut self, pat: PatId) {
483         let pat = &self.body[pat];
484 
485         match pat {
486             Pat::Missing => w!(self, "�"),
487             Pat::Wild => w!(self, "_"),
488             Pat::Tuple { args, ellipsis } => {
489                 w!(self, "(");
490                 for (i, pat) in args.iter().enumerate() {
491                     if i != 0 {
492                         w!(self, ", ");
493                     }
494                     if *ellipsis == Some(i) {
495                         w!(self, ".., ");
496                     }
497                     self.print_pat(*pat);
498                 }
499                 w!(self, ")");
500             }
501             Pat::Or(pats) => {
502                 for (i, pat) in pats.iter().enumerate() {
503                     if i != 0 {
504                         w!(self, " | ");
505                     }
506                     self.print_pat(*pat);
507                 }
508             }
509             Pat::Record { path, args, ellipsis } => {
510                 match path {
511                     Some(path) => self.print_path(path),
512                     None => w!(self, "�"),
513                 }
514 
515                 w!(self, " {{");
516                 self.indented(|p| {
517                     for arg in args.iter() {
518                         w!(p, "{}: ", arg.name.display(self.db));
519                         p.print_pat(arg.pat);
520                         wln!(p, ",");
521                     }
522                     if *ellipsis {
523                         wln!(p, "..");
524                     }
525                 });
526                 w!(self, "}}");
527             }
528             Pat::Range { start, end } => {
529                 if let Some(start) = start {
530                     self.print_literal_or_const(start);
531                 }
532                 w!(self, "..=");
533                 if let Some(end) = end {
534                     self.print_literal_or_const(end);
535                 }
536             }
537             Pat::Slice { prefix, slice, suffix } => {
538                 w!(self, "[");
539                 for pat in prefix.iter() {
540                     self.print_pat(*pat);
541                     w!(self, ", ");
542                 }
543                 if let Some(pat) = slice {
544                     self.print_pat(*pat);
545                     w!(self, ", ");
546                 }
547                 for pat in suffix.iter() {
548                     self.print_pat(*pat);
549                     w!(self, ", ");
550                 }
551                 w!(self, "]");
552             }
553             Pat::Path(path) => self.print_path(path),
554             Pat::Lit(expr) => self.print_expr(*expr),
555             Pat::Bind { id, subpat } => {
556                 self.print_binding(*id);
557                 if let Some(pat) = subpat {
558                     self.whitespace();
559                     self.print_pat(*pat);
560                 }
561             }
562             Pat::TupleStruct { path, args, ellipsis } => {
563                 match path {
564                     Some(path) => self.print_path(path),
565                     None => w!(self, "�"),
566                 }
567                 w!(self, "(");
568                 for (i, arg) in args.iter().enumerate() {
569                     if i != 0 {
570                         w!(self, ", ");
571                     }
572                     if *ellipsis == Some(i) {
573                         w!(self, ", ..");
574                     }
575                     self.print_pat(*arg);
576                 }
577                 w!(self, ")");
578             }
579             Pat::Ref { pat, mutability } => {
580                 w!(self, "&");
581                 if mutability.is_mut() {
582                     w!(self, "mut ");
583                 }
584                 self.print_pat(*pat);
585             }
586             Pat::Box { inner } => {
587                 w!(self, "box ");
588                 self.print_pat(*inner);
589             }
590             Pat::ConstBlock(c) => {
591                 w!(self, "const ");
592                 self.print_expr(*c);
593             }
594         }
595     }
596 
print_stmt(&mut self, stmt: &Statement)597     fn print_stmt(&mut self, stmt: &Statement) {
598         match stmt {
599             Statement::Let { pat, type_ref, initializer, else_branch } => {
600                 w!(self, "let ");
601                 self.print_pat(*pat);
602                 if let Some(ty) = type_ref {
603                     w!(self, ": ");
604                     self.print_type_ref(ty);
605                 }
606                 if let Some(init) = initializer {
607                     w!(self, " = ");
608                     self.print_expr(*init);
609                 }
610                 if let Some(els) = else_branch {
611                     w!(self, " else ");
612                     self.print_expr(*els);
613                 }
614                 wln!(self, ";");
615             }
616             Statement::Expr { expr, has_semi } => {
617                 self.print_expr(*expr);
618                 if *has_semi {
619                     w!(self, ";");
620                 }
621                 wln!(self);
622             }
623         }
624     }
625 
print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst)626     fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) {
627         match literal_or_const {
628             LiteralOrConst::Literal(l) => self.print_literal(l),
629             LiteralOrConst::Const(c) => self.print_path(c),
630         }
631     }
632 
print_literal(&mut self, literal: &Literal)633     fn print_literal(&mut self, literal: &Literal) {
634         match literal {
635             Literal::String(it) => w!(self, "{:?}", it),
636             Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()),
637             Literal::CString(it) => w!(self, "\"{}\\0\"", it),
638             Literal::Char(it) => w!(self, "'{}'", it.escape_debug()),
639             Literal::Bool(it) => w!(self, "{}", it),
640             Literal::Int(i, suffix) => {
641                 w!(self, "{}", i);
642                 if let Some(suffix) = suffix {
643                     w!(self, "{}", suffix);
644                 }
645             }
646             Literal::Uint(i, suffix) => {
647                 w!(self, "{}", i);
648                 if let Some(suffix) = suffix {
649                     w!(self, "{}", suffix);
650                 }
651             }
652             Literal::Float(f, suffix) => {
653                 w!(self, "{}", f);
654                 if let Some(suffix) = suffix {
655                     w!(self, "{}", suffix);
656                 }
657             }
658         }
659     }
660 
print_type_ref(&mut self, ty: &TypeRef)661     fn print_type_ref(&mut self, ty: &TypeRef) {
662         print_type_ref(self.db, ty, self).unwrap();
663     }
664 
print_path(&mut self, path: &Path)665     fn print_path(&mut self, path: &Path) {
666         print_path(self.db, path, self).unwrap();
667     }
668 
print_binding(&mut self, id: BindingId)669     fn print_binding(&mut self, id: BindingId) {
670         let Binding { name, mode, .. } = &self.body.bindings[id];
671         let mode = match mode {
672             BindingAnnotation::Unannotated => "",
673             BindingAnnotation::Mutable => "mut ",
674             BindingAnnotation::Ref => "ref ",
675             BindingAnnotation::RefMut => "ref mut ",
676         };
677         w!(self, "{}{}", mode, name.display(self.db));
678     }
679 }
680