Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/expr_store/pretty.rs')
-rw-r--r--crates/hir-def/src/expr_store/pretty.rs815
1 files changed, 815 insertions, 0 deletions
diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs
new file mode 100644
index 0000000000..6a0b1e5197
--- /dev/null
+++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -0,0 +1,815 @@
+//! A pretty-printer for HIR.
+
+use std::fmt::{self, Write};
+
+use itertools::Itertools;
+use span::Edition;
+
+use crate::{
+ hir::{
+ Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, LiteralOrConst, Movability,
+ Statement,
+ },
+ pretty::{print_generic_args, print_path, print_type_ref},
+};
+
+use super::*;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(super) enum LineFormat {
+ Oneline,
+ Newline,
+ Indentation,
+}
+
+pub(super) fn print_body_hir(
+ db: &dyn DefDatabase,
+ body: &Body,
+ owner: DefWithBodyId,
+ edition: Edition,
+) -> String {
+ let header = match owner {
+ DefWithBodyId::FunctionId(it) => it
+ .lookup(db)
+ .id
+ .resolved(db, |it| format!("fn {}", it.name.display(db.upcast(), edition))),
+ DefWithBodyId::StaticId(it) => it
+ .lookup(db)
+ .id
+ .resolved(db, |it| format!("static {} = ", it.name.display(db.upcast(), edition))),
+ DefWithBodyId::ConstId(it) => it.lookup(db).id.resolved(db, |it| {
+ format!(
+ "const {} = ",
+ match &it.name {
+ Some(name) => name.display(db.upcast(), edition).to_string(),
+ None => "_".to_owned(),
+ }
+ )
+ }),
+ DefWithBodyId::InTypeConstId(_) => "In type const = ".to_owned(),
+ DefWithBodyId::VariantId(it) => {
+ let loc = it.lookup(db);
+ let enum_loc = loc.parent.lookup(db);
+ format!(
+ "enum {}::{}",
+ enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast(), edition),
+ loc.id.item_tree(db)[loc.id.value].name.display(db.upcast(), edition),
+ )
+ }
+ };
+
+ let mut p = Printer {
+ db,
+ store: body,
+ buf: header,
+ indent_level: 0,
+ line_format: LineFormat::Newline,
+ edition,
+ };
+ if let DefWithBodyId::FunctionId(it) = owner {
+ p.buf.push('(');
+ let function_data = db.function_data(it);
+ let (mut params, ret_type) = (function_data.params.iter(), &function_data.ret_type);
+ if let Some(self_param) = body.self_param {
+ p.print_binding(self_param);
+ p.buf.push_str(": ");
+ if let Some(ty) = params.next() {
+ p.print_type_ref(*ty, &function_data.types_map);
+ p.buf.push_str(", ");
+ }
+ }
+ body.params.iter().zip(params).for_each(|(&param, ty)| {
+ p.print_pat(param);
+ p.buf.push_str(": ");
+ p.print_type_ref(*ty, &function_data.types_map);
+ p.buf.push_str(", ");
+ });
+ // remove the last ", " in param list
+ if body.params.len() > 0 {
+ p.buf.truncate(p.buf.len() - 2);
+ }
+ p.buf.push(')');
+ // return type
+ p.buf.push_str(" -> ");
+ p.print_type_ref(*ret_type, &function_data.types_map);
+ p.buf.push(' ');
+ }
+ p.print_expr(body.body_expr);
+ if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
+ p.buf.push(';');
+ }
+ p.buf
+}
+
+pub(super) fn print_expr_hir(
+ db: &dyn DefDatabase,
+ store: &ExpressionStore,
+ _owner: DefWithBodyId,
+ expr: ExprId,
+ edition: Edition,
+) -> String {
+ let mut p = Printer {
+ db,
+ store,
+ buf: String::new(),
+ indent_level: 0,
+ line_format: LineFormat::Newline,
+ edition,
+ };
+ p.print_expr(expr);
+ p.buf
+}
+
+pub(super) fn print_pat_hir(
+ db: &dyn DefDatabase,
+ store: &ExpressionStore,
+ _owner: DefWithBodyId,
+ pat: PatId,
+ oneline: bool,
+ edition: Edition,
+) -> String {
+ let mut p = Printer {
+ db,
+ store,
+ buf: String::new(),
+ indent_level: 0,
+ line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline },
+ edition,
+ };
+ p.print_pat(pat);
+ p.buf
+}
+
+macro_rules! w {
+ ($dst:expr, $($arg:tt)*) => {
+ { let _ = write!($dst, $($arg)*); }
+ };
+}
+
+macro_rules! wln {
+ ($dst:expr) => {
+ { $dst.newline(); }
+ };
+ ($dst:expr, $($arg:tt)*) => {
+ { let _ = w!($dst, $($arg)*); $dst.newline(); }
+ };
+}
+
+struct Printer<'a> {
+ db: &'a dyn DefDatabase,
+ store: &'a ExpressionStore,
+ buf: String,
+ indent_level: usize,
+ line_format: LineFormat,
+ edition: Edition,
+}
+
+impl Write for Printer<'_> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ for line in s.split_inclusive('\n') {
+ if matches!(self.line_format, LineFormat::Indentation) {
+ match self.buf.chars().rev().find(|ch| *ch != ' ') {
+ Some('\n') | None => {}
+ _ => self.buf.push('\n'),
+ }
+ self.buf.push_str(&" ".repeat(self.indent_level));
+ }
+
+ self.buf.push_str(line);
+
+ if matches!(self.line_format, LineFormat::Newline | LineFormat::Indentation) {
+ self.line_format = if line.ends_with('\n') {
+ LineFormat::Indentation
+ } else {
+ LineFormat::Newline
+ };
+ }
+ }
+
+ Ok(())
+ }
+}
+
+impl Printer<'_> {
+ fn indented(&mut self, f: impl FnOnce(&mut Self)) {
+ self.indent_level += 1;
+ wln!(self);
+ f(self);
+ self.indent_level -= 1;
+ self.buf = self.buf.trim_end_matches('\n').to_owned();
+ }
+
+ fn whitespace(&mut self) {
+ match self.buf.chars().next_back() {
+ None | Some('\n' | ' ') => {}
+ _ => self.buf.push(' '),
+ }
+ }
+
+ // Add a newline if the current line is not empty.
+ // If the current line is empty, add a space instead.
+ //
+ // Do not use [`writeln!()`] or [`wln!()`] here, which will result in
+ // infinite recursive calls to this function.
+ fn newline(&mut self) {
+ if matches!(self.line_format, LineFormat::Oneline) {
+ match self.buf.chars().last() {
+ Some(' ') | None => {}
+ Some(_) => {
+ w!(self, " ");
+ }
+ }
+ } else {
+ match self.buf.chars().rev().find_position(|ch| *ch != ' ') {
+ Some((_, '\n')) | None => {}
+ Some((idx, _)) => {
+ if idx != 0 {
+ self.buf.drain(self.buf.len() - idx..);
+ }
+ w!(self, "\n");
+ }
+ }
+ }
+ }
+
+ fn print_expr(&mut self, expr: ExprId) {
+ let expr = &self.store[expr];
+
+ match expr {
+ Expr::Missing => w!(self, "�"),
+ Expr::Underscore => w!(self, "_"),
+ Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
+ Expr::OffsetOf(offset_of) => {
+ w!(self, "builtin#offset_of(");
+ self.print_type_ref(offset_of.container, &self.store.types);
+ let edition = self.edition;
+ w!(
+ self,
+ ", {})",
+ offset_of
+ .fields
+ .iter()
+ .format_with(".", |field, f| f(&field.display(self.db.upcast(), edition)))
+ );
+ }
+ Expr::Path(path) => self.print_path(path),
+ Expr::If { condition, then_branch, else_branch } => {
+ w!(self, "if ");
+ self.print_expr(*condition);
+ w!(self, " ");
+ self.print_expr(*then_branch);
+ if let Some(els) = *else_branch {
+ w!(self, " else ");
+ self.print_expr(els);
+ }
+ }
+ Expr::Let { pat, expr } => {
+ w!(self, "let ");
+ self.print_pat(*pat);
+ w!(self, " = ");
+ self.print_expr(*expr);
+ }
+ Expr::Loop { body, label } => {
+ if let Some(lbl) = label {
+ w!(self, "{}: ", self.store[*lbl].name.display(self.db.upcast(), self.edition));
+ }
+ w!(self, "loop ");
+ self.print_expr(*body);
+ }
+ Expr::Call { callee, args } => {
+ self.print_expr(*callee);
+ w!(self, "(");
+ if !args.is_empty() {
+ self.indented(|p| {
+ for arg in &**args {
+ p.print_expr(*arg);
+ wln!(p, ",");
+ }
+ });
+ }
+ w!(self, ")");
+ }
+ Expr::MethodCall { receiver, method_name, args, generic_args } => {
+ self.print_expr(*receiver);
+ w!(self, ".{}", method_name.display(self.db.upcast(), self.edition));
+ if let Some(args) = generic_args {
+ w!(self, "::<");
+ let edition = self.edition;
+ print_generic_args(self.db, args, &self.store.types, self, edition).unwrap();
+ w!(self, ">");
+ }
+ w!(self, "(");
+ if !args.is_empty() {
+ self.indented(|p| {
+ for arg in &**args {
+ p.print_expr(*arg);
+ wln!(p, ",");
+ }
+ });
+ }
+ w!(self, ")");
+ }
+ Expr::Match { expr, arms } => {
+ w!(self, "match ");
+ self.print_expr(*expr);
+ w!(self, " {{");
+ self.indented(|p| {
+ for arm in &**arms {
+ p.print_pat(arm.pat);
+ if let Some(guard) = arm.guard {
+ w!(p, " if ");
+ p.print_expr(guard);
+ }
+ w!(p, " => ");
+ p.print_expr(arm.expr);
+ wln!(p, ",");
+ }
+ });
+ wln!(self, "}}");
+ }
+ Expr::Continue { label } => {
+ w!(self, "continue");
+ if let Some(lbl) = label {
+ w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition));
+ }
+ }
+ Expr::Break { expr, label } => {
+ w!(self, "break");
+ if let Some(lbl) = label {
+ w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition));
+ }
+ if let Some(expr) = expr {
+ self.whitespace();
+ self.print_expr(*expr);
+ }
+ }
+ Expr::Return { expr } => {
+ w!(self, "return");
+ if let Some(expr) = expr {
+ self.whitespace();
+ self.print_expr(*expr);
+ }
+ }
+ Expr::Become { expr } => {
+ w!(self, "become");
+ self.whitespace();
+ self.print_expr(*expr);
+ }
+ Expr::Yield { expr } => {
+ w!(self, "yield");
+ if let Some(expr) = expr {
+ self.whitespace();
+ self.print_expr(*expr);
+ }
+ }
+ Expr::Yeet { expr } => {
+ w!(self, "do");
+ self.whitespace();
+ w!(self, "yeet");
+ if let Some(expr) = expr {
+ self.whitespace();
+ self.print_expr(*expr);
+ }
+ }
+ Expr::RecordLit { path, fields, spread } => {
+ match path {
+ Some(path) => self.print_path(path),
+ None => w!(self, "�"),
+ }
+
+ w!(self, "{{");
+ let edition = self.edition;
+ self.indented(|p| {
+ for field in &**fields {
+ w!(p, "{}: ", field.name.display(self.db.upcast(), edition));
+ p.print_expr(field.expr);
+ wln!(p, ",");
+ }
+ if let Some(spread) = spread {
+ w!(p, "..");
+ p.print_expr(*spread);
+ wln!(p);
+ }
+ });
+ w!(self, "}}");
+ }
+ Expr::Field { expr, name } => {
+ self.print_expr(*expr);
+ w!(self, ".{}", name.display(self.db.upcast(), self.edition));
+ }
+ Expr::Await { expr } => {
+ self.print_expr(*expr);
+ w!(self, ".await");
+ }
+ Expr::Cast { expr, type_ref } => {
+ self.print_expr(*expr);
+ w!(self, " as ");
+ self.print_type_ref(*type_ref, &self.store.types);
+ }
+ Expr::Ref { expr, rawness, mutability } => {
+ w!(self, "&");
+ if rawness.is_raw() {
+ w!(self, "raw ");
+ }
+ if mutability.is_mut() {
+ w!(self, "mut ");
+ }
+ self.print_expr(*expr);
+ }
+ Expr::Box { expr } => {
+ w!(self, "box ");
+ self.print_expr(*expr);
+ }
+ Expr::UnaryOp { expr, op } => {
+ let op = match op {
+ ast::UnaryOp::Deref => "*",
+ ast::UnaryOp::Not => "!",
+ ast::UnaryOp::Neg => "-",
+ };
+ w!(self, "{}", op);
+ self.print_expr(*expr);
+ }
+ Expr::BinaryOp { lhs, rhs, op } => {
+ let (bra, ket) = match op {
+ None | Some(ast::BinaryOp::Assignment { .. }) => ("", ""),
+ _ => ("(", ")"),
+ };
+ w!(self, "{}", bra);
+ self.print_expr(*lhs);
+ w!(self, "{} ", ket);
+ match op {
+ Some(op) => w!(self, "{}", op),
+ None => w!(self, "�"), // :)
+ }
+ w!(self, " {}", bra);
+ self.print_expr(*rhs);
+ w!(self, "{}", ket);
+ }
+ Expr::Range { lhs, rhs, range_type } => {
+ if let Some(lhs) = lhs {
+ w!(self, "(");
+ self.print_expr(*lhs);
+ w!(self, ") ");
+ }
+ let range = match range_type {
+ ast::RangeOp::Exclusive => "..",
+ ast::RangeOp::Inclusive => "..=",
+ };
+ w!(self, "{}", range);
+ if let Some(rhs) = rhs {
+ w!(self, "(");
+ self.print_expr(*rhs);
+ w!(self, ") ");
+ }
+ }
+ Expr::Index { base, index } => {
+ self.print_expr(*base);
+ w!(self, "[");
+ self.print_expr(*index);
+ w!(self, "]");
+ }
+ Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
+ match closure_kind {
+ ClosureKind::Coroutine(Movability::Static) => {
+ w!(self, "static ");
+ }
+ ClosureKind::Async => {
+ w!(self, "async ");
+ }
+ _ => (),
+ }
+ match capture_by {
+ CaptureBy::Value => {
+ w!(self, "move ");
+ }
+ CaptureBy::Ref => (),
+ }
+ w!(self, "|");
+ for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
+ if i != 0 {
+ w!(self, ", ");
+ }
+ self.print_pat(*pat);
+ if let Some(ty) = ty {
+ w!(self, ": ");
+ self.print_type_ref(*ty, &self.store.types);
+ }
+ }
+ w!(self, "|");
+ if let Some(ret_ty) = ret_type {
+ w!(self, " -> ");
+ self.print_type_ref(*ret_ty, &self.store.types);
+ }
+ self.whitespace();
+ self.print_expr(*body);
+ }
+ Expr::Tuple { exprs } => {
+ w!(self, "(");
+ for expr in exprs.iter() {
+ self.print_expr(*expr);
+ w!(self, ", ");
+ }
+ w!(self, ")");
+ }
+ Expr::Array(arr) => {
+ w!(self, "[");
+ if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) {
+ self.indented(|p| match arr {
+ Array::ElementList { elements } => {
+ for elem in elements.iter() {
+ p.print_expr(*elem);
+ w!(p, ", ");
+ }
+ }
+ Array::Repeat { initializer, repeat } => {
+ p.print_expr(*initializer);
+ w!(p, "; ");
+ p.print_expr(*repeat);
+ }
+ });
+ self.newline();
+ }
+ w!(self, "]");
+ }
+ Expr::Literal(lit) => self.print_literal(lit),
+ Expr::Block { id: _, statements, tail, label } => {
+ let label = label.map(|lbl| {
+ format!("{}: ", self.store[lbl].name.display(self.db.upcast(), self.edition))
+ });
+ self.print_block(label.as_deref(), statements, tail);
+ }
+ Expr::Unsafe { id: _, statements, tail } => {
+ self.print_block(Some("unsafe "), statements, tail);
+ }
+ Expr::Async { id: _, statements, tail } => {
+ self.print_block(Some("async "), statements, tail);
+ }
+ Expr::Const(id) => {
+ w!(self, "const {{ /* {id:?} */ }}");
+ }
+ &Expr::Assignment { target, value } => {
+ self.print_pat(target);
+ w!(self, " = ");
+ self.print_expr(value);
+ }
+ }
+ }
+
+ fn print_block(
+ &mut self,
+ label: Option<&str>,
+ statements: &[Statement],
+ tail: &Option<la_arena::Idx<Expr>>,
+ ) {
+ self.whitespace();
+ if let Some(lbl) = label {
+ w!(self, "{}", lbl);
+ }
+ w!(self, "{{");
+ if !statements.is_empty() || tail.is_some() {
+ self.indented(|p| {
+ for stmt in statements {
+ p.print_stmt(stmt);
+ }
+ if let Some(tail) = tail {
+ p.print_expr(*tail);
+ }
+ p.newline();
+ });
+ }
+ w!(self, "}}");
+ }
+
+ fn print_pat(&mut self, pat: PatId) {
+ let pat = &self.store[pat];
+
+ match pat {
+ Pat::Missing => w!(self, "�"),
+ Pat::Wild => w!(self, "_"),
+ Pat::Tuple { args, ellipsis } => {
+ w!(self, "(");
+ for (i, pat) in args.iter().enumerate() {
+ if i != 0 {
+ w!(self, ", ");
+ }
+ if *ellipsis == Some(i as u32) {
+ w!(self, ".., ");
+ }
+ self.print_pat(*pat);
+ }
+ w!(self, ")");
+ }
+ Pat::Or(pats) => {
+ w!(self, "(");
+ for (i, pat) in pats.iter().enumerate() {
+ if i != 0 {
+ w!(self, " | ");
+ }
+ self.print_pat(*pat);
+ }
+ w!(self, ")");
+ }
+ Pat::Record { path, args, ellipsis } => {
+ match path {
+ Some(path) => self.print_path(path),
+ None => w!(self, "�"),
+ }
+
+ w!(self, " {{");
+ let edition = self.edition;
+ let oneline = matches!(self.line_format, LineFormat::Oneline);
+ self.indented(|p| {
+ for (idx, arg) in args.iter().enumerate() {
+ let field_name = arg.name.display(self.db.upcast(), edition).to_string();
+
+ let mut same_name = false;
+ if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] {
+ if let Binding { name, mode: BindingAnnotation::Unannotated, .. } =
+ &self.store.bindings[*id]
+ {
+ if name.as_str() == field_name {
+ same_name = true;
+ }
+ }
+ }
+
+ w!(p, "{}", field_name);
+
+ if !same_name {
+ w!(p, ": ");
+ p.print_pat(arg.pat);
+ }
+
+ // Do not print the extra comma if the line format is oneline
+ if oneline && idx == args.len() - 1 {
+ w!(p, " ");
+ } else {
+ wln!(p, ",");
+ }
+ }
+
+ if *ellipsis {
+ wln!(p, "..");
+ }
+ });
+ w!(self, "}}");
+ }
+ Pat::Range { start, end } => {
+ if let Some(start) = start {
+ self.print_literal_or_const(start);
+ }
+ w!(self, "..=");
+ if let Some(end) = end {
+ self.print_literal_or_const(end);
+ }
+ }
+ Pat::Slice { prefix, slice, suffix } => {
+ w!(self, "[");
+ for pat in prefix.iter() {
+ self.print_pat(*pat);
+ w!(self, ", ");
+ }
+ if let Some(pat) = slice {
+ self.print_pat(*pat);
+ w!(self, ", ");
+ }
+ for pat in suffix.iter() {
+ self.print_pat(*pat);
+ w!(self, ", ");
+ }
+ w!(self, "]");
+ }
+ Pat::Path(path) => self.print_path(path),
+ Pat::Lit(expr) => self.print_expr(*expr),
+ Pat::Bind { id, subpat } => {
+ self.print_binding(*id);
+ if let Some(pat) = subpat {
+ self.whitespace();
+ w!(self, "@ ");
+ self.print_pat(*pat);
+ }
+ }
+ Pat::TupleStruct { path, args, ellipsis } => {
+ match path {
+ Some(path) => self.print_path(path),
+ None => w!(self, "�"),
+ }
+ w!(self, "(");
+ for (i, arg) in args.iter().enumerate() {
+ if i != 0 {
+ w!(self, ", ");
+ }
+ if *ellipsis == Some(i as u32) {
+ w!(self, ", ..");
+ }
+ self.print_pat(*arg);
+ }
+ w!(self, ")");
+ }
+ Pat::Ref { pat, mutability } => {
+ w!(self, "&");
+ if mutability.is_mut() {
+ w!(self, "mut ");
+ }
+ self.print_pat(*pat);
+ }
+ Pat::Box { inner } => {
+ w!(self, "box ");
+ self.print_pat(*inner);
+ }
+ Pat::ConstBlock(c) => {
+ w!(self, "const ");
+ self.print_expr(*c);
+ }
+ Pat::Expr(expr) => {
+ self.print_expr(*expr);
+ }
+ }
+ }
+
+ fn print_stmt(&mut self, stmt: &Statement) {
+ match stmt {
+ Statement::Let { pat, type_ref, initializer, else_branch } => {
+ w!(self, "let ");
+ self.print_pat(*pat);
+ if let Some(ty) = type_ref {
+ w!(self, ": ");
+ self.print_type_ref(*ty, &self.store.types);
+ }
+ if let Some(init) = initializer {
+ w!(self, " = ");
+ self.print_expr(*init);
+ }
+ if let Some(els) = else_branch {
+ w!(self, " else ");
+ self.print_expr(*els);
+ }
+ wln!(self, ";");
+ }
+ Statement::Expr { expr, has_semi } => {
+ self.print_expr(*expr);
+ if *has_semi {
+ w!(self, ";");
+ }
+ wln!(self);
+ }
+ Statement::Item(_) => (),
+ }
+ }
+
+ fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) {
+ match literal_or_const {
+ LiteralOrConst::Literal(l) => self.print_literal(l),
+ LiteralOrConst::Const(c) => self.print_pat(*c),
+ }
+ }
+
+ fn print_literal(&mut self, literal: &Literal) {
+ match literal {
+ Literal::String(it) => w!(self, "{:?}", it),
+ Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()),
+ Literal::CString(it) => w!(self, "\"{}\\0\"", it.escape_ascii()),
+ Literal::Char(it) => w!(self, "'{}'", it.escape_debug()),
+ Literal::Bool(it) => w!(self, "{}", it),
+ Literal::Int(i, suffix) => {
+ w!(self, "{}", i);
+ if let Some(suffix) = suffix {
+ w!(self, "{}", suffix);
+ }
+ }
+ Literal::Uint(i, suffix) => {
+ w!(self, "{}", i);
+ if let Some(suffix) = suffix {
+ w!(self, "{}", suffix);
+ }
+ }
+ Literal::Float(f, suffix) => {
+ w!(self, "{}", f);
+ if let Some(suffix) = suffix {
+ w!(self, "{}", suffix);
+ }
+ }
+ }
+ }
+
+ fn print_type_ref(&mut self, ty: TypeRefId, map: &TypesMap) {
+ let edition = self.edition;
+ print_type_ref(self.db, ty, map, self, edition).unwrap();
+ }
+
+ fn print_path(&mut self, path: &Path) {
+ let edition = self.edition;
+ print_path(self.db, path, &self.store.types, self, edition).unwrap();
+ }
+
+ fn print_binding(&mut self, id: BindingId) {
+ let Binding { name, mode, .. } = &self.store.bindings[id];
+ let mode = match mode {
+ BindingAnnotation::Unannotated => "",
+ BindingAnnotation::Mutable => "mut ",
+ BindingAnnotation::Ref => "ref ",
+ BindingAnnotation::RefMut => "ref mut ",
+ };
+ w!(self, "{}{}", mode, name.display(self.db.upcast(), self.edition));
+ }
+}