mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 12:29:21 +00:00
⬆️ rust-analyzer
This commit is contained in:
parent
134701885d
commit
31519bb394
83 changed files with 2092 additions and 626 deletions
|
@ -4,6 +4,7 @@ mod lower;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod scope;
|
||||
mod pretty;
|
||||
|
||||
use std::{ops::Index, sync::Arc};
|
||||
|
||||
|
@ -352,6 +353,10 @@ impl Body {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
|
||||
pretty::print_body_hir(db, self, owner)
|
||||
}
|
||||
|
||||
fn new(
|
||||
db: &dyn DefDatabase,
|
||||
expander: Expander,
|
||||
|
|
|
@ -551,9 +551,17 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
}
|
||||
ast::Expr::MacroStmts(e) => {
|
||||
let statements = e.statements().filter_map(|s| self.collect_stmt(s)).collect();
|
||||
let statements: Box<[_]> =
|
||||
e.statements().filter_map(|s| self.collect_stmt(s)).collect();
|
||||
let tail = e.expr().map(|e| self.collect_expr(e));
|
||||
|
||||
if e.syntax().children().next().is_none() {
|
||||
// HACK: make sure that macros that expand to nothing aren't treated as a `()`
|
||||
// expression when used in block tail position.
|
||||
cov_mark::hit!(empty_macro_in_trailing_position_is_removed);
|
||||
return None;
|
||||
}
|
||||
|
||||
self.alloc_expr(Expr::MacroStmts { tail, statements }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
|
||||
|
|
621
crates/hir-def/src/body/pretty.rs
Normal file
621
crates/hir-def/src/body/pretty.rs
Normal file
|
@ -0,0 +1,621 @@
|
|||
//! A pretty-printer for HIR.
|
||||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use crate::{
|
||||
expr::{Array, BindingAnnotation, Literal, Statement},
|
||||
pretty::{print_generic_args, print_path, print_type_ref},
|
||||
type_ref::TypeRef,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
|
||||
let needs_semi;
|
||||
let header = match owner {
|
||||
DefWithBodyId::FunctionId(it) => {
|
||||
needs_semi = false;
|
||||
let item_tree_id = it.lookup(db).id;
|
||||
format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name)
|
||||
}
|
||||
DefWithBodyId::StaticId(it) => {
|
||||
needs_semi = true;
|
||||
let item_tree_id = it.lookup(db).id;
|
||||
format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name)
|
||||
}
|
||||
DefWithBodyId::ConstId(it) => {
|
||||
needs_semi = true;
|
||||
let item_tree_id = it.lookup(db).id;
|
||||
let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name {
|
||||
Some(name) => name.to_string(),
|
||||
None => "_".to_string(),
|
||||
};
|
||||
format!("const {} = ", name)
|
||||
}
|
||||
};
|
||||
|
||||
let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };
|
||||
p.print_expr(body.body_expr);
|
||||
if needs_semi {
|
||||
p.buf.push(';');
|
||||
}
|
||||
p.buf
|
||||
}
|
||||
|
||||
macro_rules! w {
|
||||
($dst:expr, $($arg:tt)*) => {
|
||||
{ let _ = write!($dst, $($arg)*); }
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! wln {
|
||||
($dst:expr) => {
|
||||
{ let _ = writeln!($dst); }
|
||||
};
|
||||
($dst:expr, $($arg:tt)*) => {
|
||||
{ let _ = writeln!($dst, $($arg)*); }
|
||||
};
|
||||
}
|
||||
|
||||
struct Printer<'a> {
|
||||
body: &'a Body,
|
||||
buf: String,
|
||||
indent_level: usize,
|
||||
needs_indent: bool,
|
||||
}
|
||||
|
||||
impl<'a> Write for Printer<'a> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for line in s.split_inclusive('\n') {
|
||||
if self.needs_indent {
|
||||
match self.buf.chars().rev().skip_while(|ch| *ch == ' ').next() {
|
||||
Some('\n') | None => {}
|
||||
_ => self.buf.push('\n'),
|
||||
}
|
||||
self.buf.push_str(&" ".repeat(self.indent_level));
|
||||
self.needs_indent = false;
|
||||
}
|
||||
|
||||
self.buf.push_str(line);
|
||||
self.needs_indent = line.ends_with('\n');
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Printer<'a> {
|
||||
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_string();
|
||||
}
|
||||
|
||||
fn whitespace(&mut self) {
|
||||
match self.buf.chars().next_back() {
|
||||
None | Some('\n' | ' ') => {}
|
||||
_ => self.buf.push(' '),
|
||||
}
|
||||
}
|
||||
|
||||
fn newline(&mut self) {
|
||||
match self.buf.chars().rev().skip_while(|ch| *ch == ' ').next() {
|
||||
Some('\n') | None => {}
|
||||
_ => writeln!(self).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_expr(&mut self, expr: ExprId) {
|
||||
let expr = &self.body[expr];
|
||||
|
||||
match expr {
|
||||
Expr::Missing => w!(self, "<EFBFBD>"),
|
||||
Expr::Underscore => w!(self, "_"),
|
||||
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.body[*lbl].name);
|
||||
}
|
||||
w!(self, "loop ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::While { condition, body, label } => {
|
||||
if let Some(lbl) = label {
|
||||
w!(self, "{}: ", self.body[*lbl].name);
|
||||
}
|
||||
w!(self, "while ");
|
||||
self.print_expr(*condition);
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::For { iterable, pat, body, label } => {
|
||||
if let Some(lbl) = label {
|
||||
w!(self, "{}: ", self.body[*lbl].name);
|
||||
}
|
||||
w!(self, "for ");
|
||||
self.print_pat(*pat);
|
||||
w!(self, " in ");
|
||||
self.print_expr(*iterable);
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Call { callee, args, is_assignee_expr: _ } => {
|
||||
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);
|
||||
if let Some(args) = generic_args {
|
||||
w!(self, "::<");
|
||||
print_generic_args(args, self).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(label) = label {
|
||||
w!(self, " {}", label);
|
||||
}
|
||||
}
|
||||
Expr::Break { expr, label } => {
|
||||
w!(self, "break");
|
||||
if let Some(label) = label {
|
||||
w!(self, " {}", label);
|
||||
}
|
||||
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::Yield { expr } => {
|
||||
w!(self, "yield");
|
||||
if let Some(expr) = expr {
|
||||
self.whitespace();
|
||||
self.print_expr(*expr);
|
||||
}
|
||||
}
|
||||
Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr: _ } => {
|
||||
match path {
|
||||
Some(path) => self.print_path(path),
|
||||
None => w!(self, "<EFBFBD>"),
|
||||
}
|
||||
|
||||
w!(self, "{{");
|
||||
self.indented(|p| {
|
||||
for field in &**fields {
|
||||
w!(p, "{}: ", field.name);
|
||||
p.print_expr(field.expr);
|
||||
wln!(p, ",");
|
||||
}
|
||||
if let Some(spread) = spread {
|
||||
w!(p, "..");
|
||||
p.print_expr(*spread);
|
||||
wln!(p);
|
||||
}
|
||||
if *ellipsis {
|
||||
wln!(p, "..");
|
||||
}
|
||||
});
|
||||
w!(self, "}}");
|
||||
}
|
||||
Expr::Field { expr, name } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, ".{}", name);
|
||||
}
|
||||
Expr::Await { expr } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, ".await");
|
||||
}
|
||||
Expr::Try { expr } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, "?");
|
||||
}
|
||||
Expr::TryBlock { body } => {
|
||||
w!(self, "try ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Async { body } => {
|
||||
w!(self, "async ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Const { body } => {
|
||||
w!(self, "const ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Cast { expr, type_ref } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, " as ");
|
||||
self.print_type_ref(type_ref);
|
||||
}
|
||||
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, "<EFBFBD>"), // :)
|
||||
}
|
||||
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 } => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
w!(self, "|");
|
||||
if let Some(ret_ty) = ret_type {
|
||||
w!(self, " -> ");
|
||||
self.print_type_ref(ret_ty);
|
||||
}
|
||||
self.whitespace();
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
w!(self, "(");
|
||||
for expr in exprs.iter() {
|
||||
self.print_expr(*expr);
|
||||
w!(self, ", ");
|
||||
}
|
||||
w!(self, ")");
|
||||
}
|
||||
Expr::Unsafe { body } => {
|
||||
w!(self, "unsafe ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Array(arr) => {
|
||||
w!(self, "[");
|
||||
if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) {
|
||||
self.indented(|p| match arr {
|
||||
Array::ElementList { elements, is_assignee_expr: _ } => {
|
||||
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 } => {
|
||||
self.whitespace();
|
||||
if let Some(lbl) = label {
|
||||
w!(self, "{}: ", self.body[*lbl].name);
|
||||
}
|
||||
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, "}}");
|
||||
}
|
||||
Expr::MacroStmts { statements, tail } => {
|
||||
w!(self, "{{ // macro statements");
|
||||
self.indented(|p| {
|
||||
for stmt in statements.iter() {
|
||||
p.print_stmt(stmt);
|
||||
}
|
||||
if let Some(tail) = tail {
|
||||
p.print_expr(*tail);
|
||||
}
|
||||
});
|
||||
self.newline();
|
||||
w!(self, "}}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_pat(&mut self, pat: PatId) {
|
||||
let pat = &self.body[pat];
|
||||
|
||||
match pat {
|
||||
Pat::Missing => w!(self, "<EFBFBD>"),
|
||||
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) {
|
||||
w!(self, ".., ");
|
||||
}
|
||||
self.print_pat(*pat);
|
||||
}
|
||||
w!(self, ")");
|
||||
}
|
||||
Pat::Or(pats) => {
|
||||
for (i, pat) in pats.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(self, " | ");
|
||||
}
|
||||
self.print_pat(*pat);
|
||||
}
|
||||
}
|
||||
Pat::Record { path, args, ellipsis } => {
|
||||
match path {
|
||||
Some(path) => self.print_path(path),
|
||||
None => w!(self, "<EFBFBD>"),
|
||||
}
|
||||
|
||||
w!(self, " {{");
|
||||
self.indented(|p| {
|
||||
for arg in args.iter() {
|
||||
w!(p, "{}: ", arg.name);
|
||||
p.print_pat(arg.pat);
|
||||
wln!(p, ",");
|
||||
}
|
||||
if *ellipsis {
|
||||
wln!(p, "..");
|
||||
}
|
||||
});
|
||||
w!(self, "}}");
|
||||
}
|
||||
Pat::Range { start, end } => {
|
||||
self.print_expr(*start);
|
||||
w!(self, "...");
|
||||
self.print_expr(*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 { mode, name, subpat } => {
|
||||
let mode = match mode {
|
||||
BindingAnnotation::Unannotated => "",
|
||||
BindingAnnotation::Mutable => "mut ",
|
||||
BindingAnnotation::Ref => "ref ",
|
||||
BindingAnnotation::RefMut => "ref mut ",
|
||||
};
|
||||
w!(self, "{}{}", mode, name);
|
||||
if let Some(pat) = subpat {
|
||||
self.whitespace();
|
||||
self.print_pat(*pat);
|
||||
}
|
||||
}
|
||||
Pat::TupleStruct { path, args, ellipsis } => {
|
||||
match path {
|
||||
Some(path) => self.print_path(path),
|
||||
None => w!(self, "<EFBFBD>"),
|
||||
}
|
||||
w!(self, "(");
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(self, ", ");
|
||||
}
|
||||
if *ellipsis == Some(i) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_literal(&mut self, literal: &Literal) {
|
||||
match literal {
|
||||
Literal::String(it) => w!(self, "{:?}", it),
|
||||
Literal::ByteString(it) => w!(self, "\"{}\"", 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: &TypeRef) {
|
||||
print_type_ref(ty, self).unwrap();
|
||||
}
|
||||
|
||||
fn print_path(&mut self, path: &Path) {
|
||||
print_path(path, self).unwrap();
|
||||
}
|
||||
}
|
|
@ -156,3 +156,38 @@ impl BuiltinFloat {
|
|||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BuiltinInt {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
BuiltinInt::Isize => "isize",
|
||||
BuiltinInt::I8 => "i8",
|
||||
BuiltinInt::I16 => "i16",
|
||||
BuiltinInt::I32 => "i32",
|
||||
BuiltinInt::I64 => "i64",
|
||||
BuiltinInt::I128 => "i128",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BuiltinUint {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
BuiltinUint::Usize => "usize",
|
||||
BuiltinUint::U8 => "u8",
|
||||
BuiltinUint::U16 => "u16",
|
||||
BuiltinUint::U32 => "u32",
|
||||
BuiltinUint::U64 => "u64",
|
||||
BuiltinUint::U128 => "u128",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BuiltinFloat {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
BuiltinFloat::F32 => "f32",
|
||||
BuiltinFloat::F64 => "f64",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
//!
|
||||
//! See also a neighboring `body` module.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use la_arena::{Idx, RawIdx};
|
||||
|
||||
|
@ -52,8 +54,8 @@ impl FloatTypeWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FloatTypeWrapper {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
impl fmt::Display for FloatTypeWrapper {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", f64::from_bits(self.0))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,10 @@
|
|||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
attr::RawAttrs,
|
||||
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
|
||||
path::GenericArg,
|
||||
type_ref::TraitBoundModifier,
|
||||
pretty::{print_path, print_type_bounds, print_type_ref},
|
||||
visibility::RawVisibility,
|
||||
};
|
||||
|
||||
|
@ -464,183 +461,15 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
|
||||
fn print_type_ref(&mut self, type_ref: &TypeRef) {
|
||||
// FIXME: deduplicate with `HirDisplay` impl
|
||||
match type_ref {
|
||||
TypeRef::Never => w!(self, "!"),
|
||||
TypeRef::Placeholder => w!(self, "_"),
|
||||
TypeRef::Tuple(fields) => {
|
||||
w!(self, "(");
|
||||
for (i, field) in fields.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(self, ", ");
|
||||
}
|
||||
self.print_type_ref(field);
|
||||
}
|
||||
w!(self, ")");
|
||||
}
|
||||
TypeRef::Path(path) => self.print_path(path),
|
||||
TypeRef::RawPtr(pointee, mtbl) => {
|
||||
let mtbl = match mtbl {
|
||||
Mutability::Shared => "*const",
|
||||
Mutability::Mut => "*mut",
|
||||
};
|
||||
w!(self, "{} ", mtbl);
|
||||
self.print_type_ref(pointee);
|
||||
}
|
||||
TypeRef::Reference(pointee, lt, mtbl) => {
|
||||
let mtbl = match mtbl {
|
||||
Mutability::Shared => "",
|
||||
Mutability::Mut => "mut ",
|
||||
};
|
||||
w!(self, "&");
|
||||
if let Some(lt) = lt {
|
||||
w!(self, "{} ", lt.name);
|
||||
}
|
||||
w!(self, "{}", mtbl);
|
||||
self.print_type_ref(pointee);
|
||||
}
|
||||
TypeRef::Array(elem, len) => {
|
||||
w!(self, "[");
|
||||
self.print_type_ref(elem);
|
||||
w!(self, "; {}]", len);
|
||||
}
|
||||
TypeRef::Slice(elem) => {
|
||||
w!(self, "[");
|
||||
self.print_type_ref(elem);
|
||||
w!(self, "]");
|
||||
}
|
||||
TypeRef::Fn(args_and_ret, varargs) => {
|
||||
let ((_, return_type), args) =
|
||||
args_and_ret.split_last().expect("TypeRef::Fn is missing return type");
|
||||
w!(self, "fn(");
|
||||
for (i, (_, typeref)) in args.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(self, ", ");
|
||||
}
|
||||
self.print_type_ref(typeref);
|
||||
}
|
||||
if *varargs {
|
||||
if !args.is_empty() {
|
||||
w!(self, ", ");
|
||||
}
|
||||
w!(self, "...");
|
||||
}
|
||||
w!(self, ") -> ");
|
||||
self.print_type_ref(return_type);
|
||||
}
|
||||
TypeRef::Macro(_ast_id) => {
|
||||
w!(self, "<macro>");
|
||||
}
|
||||
TypeRef::Error => w!(self, "{{unknown}}"),
|
||||
TypeRef::ImplTrait(bounds) => {
|
||||
w!(self, "impl ");
|
||||
self.print_type_bounds(bounds);
|
||||
}
|
||||
TypeRef::DynTrait(bounds) => {
|
||||
w!(self, "dyn ");
|
||||
self.print_type_bounds(bounds);
|
||||
}
|
||||
}
|
||||
print_type_ref(type_ref, self).unwrap();
|
||||
}
|
||||
|
||||
fn print_type_bounds(&mut self, bounds: &[Interned<TypeBound>]) {
|
||||
for (i, bound) in bounds.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(self, " + ");
|
||||
}
|
||||
|
||||
match bound.as_ref() {
|
||||
TypeBound::Path(path, modifier) => {
|
||||
match modifier {
|
||||
TraitBoundModifier::None => (),
|
||||
TraitBoundModifier::Maybe => w!(self, "?"),
|
||||
}
|
||||
self.print_path(path)
|
||||
}
|
||||
TypeBound::ForLifetime(lifetimes, path) => {
|
||||
w!(self, "for<{}> ", lifetimes.iter().format(", "));
|
||||
self.print_path(path);
|
||||
}
|
||||
TypeBound::Lifetime(lt) => w!(self, "{}", lt.name),
|
||||
TypeBound::Error => w!(self, "{{unknown}}"),
|
||||
}
|
||||
}
|
||||
print_type_bounds(bounds, self).unwrap();
|
||||
}
|
||||
|
||||
fn print_path(&mut self, path: &Path) {
|
||||
match path.type_anchor() {
|
||||
Some(anchor) => {
|
||||
w!(self, "<");
|
||||
self.print_type_ref(anchor);
|
||||
w!(self, ">::");
|
||||
}
|
||||
None => match path.kind() {
|
||||
PathKind::Plain => {}
|
||||
PathKind::Super(0) => w!(self, "self::"),
|
||||
PathKind::Super(n) => {
|
||||
for _ in 0..*n {
|
||||
w!(self, "super::");
|
||||
}
|
||||
}
|
||||
PathKind::Crate => w!(self, "crate::"),
|
||||
PathKind::Abs => w!(self, "::"),
|
||||
PathKind::DollarCrate(_) => w!(self, "$crate::"),
|
||||
},
|
||||
}
|
||||
|
||||
for (i, segment) in path.segments().iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(self, "::");
|
||||
}
|
||||
|
||||
w!(self, "{}", segment.name);
|
||||
if let Some(generics) = segment.args_and_bindings {
|
||||
// NB: these are all in type position, so `::<` turbofish syntax is not necessary
|
||||
w!(self, "<");
|
||||
let mut first = true;
|
||||
let args = if generics.has_self_type {
|
||||
let (self_ty, args) = generics.args.split_first().unwrap();
|
||||
w!(self, "Self=");
|
||||
self.print_generic_arg(self_ty);
|
||||
first = false;
|
||||
args
|
||||
} else {
|
||||
&generics.args
|
||||
};
|
||||
for arg in args {
|
||||
if !first {
|
||||
w!(self, ", ");
|
||||
}
|
||||
first = false;
|
||||
self.print_generic_arg(arg);
|
||||
}
|
||||
for binding in &generics.bindings {
|
||||
if !first {
|
||||
w!(self, ", ");
|
||||
}
|
||||
first = false;
|
||||
w!(self, "{}", binding.name);
|
||||
if !binding.bounds.is_empty() {
|
||||
w!(self, ": ");
|
||||
self.print_type_bounds(&binding.bounds);
|
||||
}
|
||||
if let Some(ty) = &binding.type_ref {
|
||||
w!(self, " = ");
|
||||
self.print_type_ref(ty);
|
||||
}
|
||||
}
|
||||
|
||||
w!(self, ">");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_generic_arg(&mut self, arg: &GenericArg) {
|
||||
match arg {
|
||||
GenericArg::Type(ty) => self.print_type_ref(ty),
|
||||
GenericArg::Const(c) => w!(self, "{}", c),
|
||||
GenericArg::Lifetime(lt) => w!(self, "{}", lt.name),
|
||||
}
|
||||
print_path(path, self).unwrap();
|
||||
}
|
||||
|
||||
fn print_generic_params(&mut self, params: &GenericParams) {
|
||||
|
|
|
@ -283,10 +283,10 @@ struct S {
|
|||
"#,
|
||||
expect![[r#"
|
||||
pub(self) struct S {
|
||||
pub(self) a: Mixed<'a, T, Item = (), OtherItem = u8>,
|
||||
pub(self) b: Qualified<Self=Fully>::Syntax,
|
||||
pub(self) c: <TypeAnchored>::Path<'a>,
|
||||
pub(self) d: dyn for<'a> Trait<'a>,
|
||||
pub(self) a: Mixed::<'a, T, Item = (), OtherItem = u8>,
|
||||
pub(self) b: Qualified::<Self=Fully>::Syntax,
|
||||
pub(self) c: <TypeAnchored>::Path::<'a>,
|
||||
pub(self) d: dyn for<'a> Trait::<'a>,
|
||||
}
|
||||
"#]],
|
||||
)
|
||||
|
@ -329,7 +329,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
|
|||
T: Copy,
|
||||
U: ?Sized;
|
||||
|
||||
impl<'a, 'b, T, const K: u8> S<'a, 'b, T, K>
|
||||
impl<'a, 'b, T, const K: u8> S::<'a, 'b, T, K>
|
||||
where
|
||||
T: Copy,
|
||||
T: 'a,
|
||||
|
@ -352,7 +352,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
|
|||
where
|
||||
Self: Super,
|
||||
T: 'a,
|
||||
Self: for<'a> Tr<'a, T>
|
||||
Self: for<'a> Tr::<'a, T>
|
||||
{
|
||||
}
|
||||
"#]],
|
||||
|
|
|
@ -53,6 +53,7 @@ pub mod import_map;
|
|||
mod test_db;
|
||||
#[cfg(test)]
|
||||
mod macro_expansion_tests;
|
||||
mod pretty;
|
||||
|
||||
use std::{
|
||||
hash::{Hash, Hasher},
|
||||
|
|
|
@ -399,14 +399,15 @@ impl DefMap {
|
|||
Some(_) | None => from_scope.or(from_builtin),
|
||||
},
|
||||
};
|
||||
let from_extern_prelude = self
|
||||
.extern_prelude
|
||||
.get(name)
|
||||
.map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public));
|
||||
|
||||
let from_prelude = self.resolve_in_prelude(db, name);
|
||||
let extern_prelude = || {
|
||||
self.extern_prelude
|
||||
.get(name)
|
||||
.map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
|
||||
};
|
||||
let prelude = || self.resolve_in_prelude(db, name);
|
||||
|
||||
from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude)
|
||||
from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude)
|
||||
}
|
||||
|
||||
fn resolve_name_in_crate_root_or_extern_prelude(
|
||||
|
@ -414,20 +415,19 @@ impl DefMap {
|
|||
db: &dyn DefDatabase,
|
||||
name: &Name,
|
||||
) -> PerNs {
|
||||
let arc;
|
||||
let crate_def_map = match self.block {
|
||||
let from_crate_root = match self.block {
|
||||
Some(_) => {
|
||||
arc = self.crate_root(db).def_map(db);
|
||||
&arc
|
||||
let def_map = self.crate_root(db).def_map(db);
|
||||
def_map[def_map.root].scope.get(name)
|
||||
}
|
||||
None => self,
|
||||
None => self[self.root].scope.get(name),
|
||||
};
|
||||
let from_extern_prelude = || {
|
||||
self.resolve_name_in_extern_prelude(db, name)
|
||||
.map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public))
|
||||
};
|
||||
let from_crate_root = crate_def_map[crate_def_map.root].scope.get(name);
|
||||
let from_extern_prelude = self
|
||||
.resolve_name_in_extern_prelude(db, name)
|
||||
.map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public));
|
||||
|
||||
from_crate_root.or(from_extern_prelude)
|
||||
from_crate_root.or_else(from_extern_prelude)
|
||||
}
|
||||
|
||||
fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
|
||||
|
|
|
@ -45,7 +45,7 @@ impl Attrs {
|
|||
kind: ProcMacroKind::CustomDerive { helpers: Box::new([]) },
|
||||
}),
|
||||
|
||||
// `#[proc_macro_derive(Trait, attibutes(helper1, helper2, ...))]`
|
||||
// `#[proc_macro_derive(Trait, attributes(helper1, helper2, ...))]`
|
||||
[
|
||||
TokenTree::Leaf(Leaf::Ident(trait_name)),
|
||||
TokenTree::Leaf(Leaf::Punct(comma)),
|
||||
|
|
|
@ -43,6 +43,10 @@ impl PerNs {
|
|||
self.types.is_none() && self.values.is_none() && self.macros.is_none()
|
||||
}
|
||||
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.types.is_some() && self.values.is_some() && self.macros.is_some()
|
||||
}
|
||||
|
||||
pub fn take_types(self) -> Option<ModuleDefId> {
|
||||
self.types.map(|it| it.0)
|
||||
}
|
||||
|
@ -84,6 +88,14 @@ impl PerNs {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn or_else(self, f: impl FnOnce() -> PerNs) -> PerNs {
|
||||
if self.is_full() {
|
||||
self
|
||||
} else {
|
||||
self.or(f())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> {
|
||||
let _p = profile::span("PerNs::iter_items");
|
||||
self.types
|
||||
|
|
209
crates/hir-def/src/pretty.rs
Normal file
209
crates/hir-def/src/pretty.rs
Normal file
|
@ -0,0 +1,209 @@
|
|||
//! Display and pretty printing routines.
|
||||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use hir_expand::mod_path::PathKind;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
intern::Interned,
|
||||
path::{GenericArg, GenericArgs, Path},
|
||||
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
|
||||
};
|
||||
|
||||
pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
|
||||
match path.type_anchor() {
|
||||
Some(anchor) => {
|
||||
write!(buf, "<")?;
|
||||
print_type_ref(anchor, buf)?;
|
||||
write!(buf, ">::")?;
|
||||
}
|
||||
None => match path.kind() {
|
||||
PathKind::Plain => {}
|
||||
PathKind::Super(0) => write!(buf, "self")?,
|
||||
PathKind::Super(n) => {
|
||||
for i in 0..*n {
|
||||
if i == 0 {
|
||||
buf.write_str("super")?;
|
||||
} else {
|
||||
buf.write_str("::super")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
PathKind::Crate => write!(buf, "crate")?,
|
||||
PathKind::Abs => {}
|
||||
PathKind::DollarCrate(_) => write!(buf, "$crate")?,
|
||||
},
|
||||
}
|
||||
|
||||
for (i, segment) in path.segments().iter().enumerate() {
|
||||
if i != 0 || !matches!(path.kind(), PathKind::Plain) {
|
||||
write!(buf, "::")?;
|
||||
}
|
||||
|
||||
write!(buf, "{}", segment.name)?;
|
||||
if let Some(generics) = segment.args_and_bindings {
|
||||
write!(buf, "::<")?;
|
||||
print_generic_args(generics, buf)?;
|
||||
|
||||
write!(buf, ">")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result {
|
||||
let mut first = true;
|
||||
let args = if generics.has_self_type {
|
||||
let (self_ty, args) = generics.args.split_first().unwrap();
|
||||
write!(buf, "Self=")?;
|
||||
print_generic_arg(self_ty, buf)?;
|
||||
first = false;
|
||||
args
|
||||
} else {
|
||||
&generics.args
|
||||
};
|
||||
for arg in args {
|
||||
if !first {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
first = false;
|
||||
print_generic_arg(arg, buf)?;
|
||||
}
|
||||
for binding in &generics.bindings {
|
||||
if !first {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
first = false;
|
||||
write!(buf, "{}", binding.name)?;
|
||||
if !binding.bounds.is_empty() {
|
||||
write!(buf, ": ")?;
|
||||
print_type_bounds(&binding.bounds, buf)?;
|
||||
}
|
||||
if let Some(ty) = &binding.type_ref {
|
||||
write!(buf, " = ")?;
|
||||
print_type_ref(ty, buf)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result {
|
||||
match arg {
|
||||
GenericArg::Type(ty) => print_type_ref(ty, buf),
|
||||
GenericArg::Const(c) => write!(buf, "{}", c),
|
||||
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result {
|
||||
// FIXME: deduplicate with `HirDisplay` impl
|
||||
match type_ref {
|
||||
TypeRef::Never => write!(buf, "!")?,
|
||||
TypeRef::Placeholder => write!(buf, "_")?,
|
||||
TypeRef::Tuple(fields) => {
|
||||
write!(buf, "(")?;
|
||||
for (i, field) in fields.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
print_type_ref(field, buf)?;
|
||||
}
|
||||
write!(buf, ")")?;
|
||||
}
|
||||
TypeRef::Path(path) => print_path(path, buf)?,
|
||||
TypeRef::RawPtr(pointee, mtbl) => {
|
||||
let mtbl = match mtbl {
|
||||
Mutability::Shared => "*const",
|
||||
Mutability::Mut => "*mut",
|
||||
};
|
||||
write!(buf, "{} ", mtbl)?;
|
||||
print_type_ref(pointee, buf)?;
|
||||
}
|
||||
TypeRef::Reference(pointee, lt, mtbl) => {
|
||||
let mtbl = match mtbl {
|
||||
Mutability::Shared => "",
|
||||
Mutability::Mut => "mut ",
|
||||
};
|
||||
write!(buf, "&")?;
|
||||
if let Some(lt) = lt {
|
||||
write!(buf, "{} ", lt.name)?;
|
||||
}
|
||||
write!(buf, "{}", mtbl)?;
|
||||
print_type_ref(pointee, buf)?;
|
||||
}
|
||||
TypeRef::Array(elem, len) => {
|
||||
write!(buf, "[")?;
|
||||
print_type_ref(elem, buf)?;
|
||||
write!(buf, "; {}]", len)?;
|
||||
}
|
||||
TypeRef::Slice(elem) => {
|
||||
write!(buf, "[")?;
|
||||
print_type_ref(elem, buf)?;
|
||||
write!(buf, "]")?;
|
||||
}
|
||||
TypeRef::Fn(args_and_ret, varargs) => {
|
||||
let ((_, return_type), args) =
|
||||
args_and_ret.split_last().expect("TypeRef::Fn is missing return type");
|
||||
write!(buf, "fn(")?;
|
||||
for (i, (_, typeref)) in args.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
print_type_ref(typeref, buf)?;
|
||||
}
|
||||
if *varargs {
|
||||
if !args.is_empty() {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
write!(buf, "...")?;
|
||||
}
|
||||
write!(buf, ") -> ")?;
|
||||
print_type_ref(return_type, buf)?;
|
||||
}
|
||||
TypeRef::Macro(_ast_id) => {
|
||||
write!(buf, "<macro>")?;
|
||||
}
|
||||
TypeRef::Error => write!(buf, "{{unknown}}")?,
|
||||
TypeRef::ImplTrait(bounds) => {
|
||||
write!(buf, "impl ")?;
|
||||
print_type_bounds(bounds, buf)?;
|
||||
}
|
||||
TypeRef::DynTrait(bounds) => {
|
||||
write!(buf, "dyn ")?;
|
||||
print_type_bounds(bounds, buf)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn print_type_bounds(
|
||||
bounds: &[Interned<TypeBound>],
|
||||
buf: &mut dyn Write,
|
||||
) -> fmt::Result {
|
||||
for (i, bound) in bounds.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(buf, " + ")?;
|
||||
}
|
||||
|
||||
match bound.as_ref() {
|
||||
TypeBound::Path(path, modifier) => {
|
||||
match modifier {
|
||||
TraitBoundModifier::None => (),
|
||||
TraitBoundModifier::Maybe => write!(buf, "?")?,
|
||||
}
|
||||
print_path(path, buf)?;
|
||||
}
|
||||
TypeBound::ForLifetime(lifetimes, path) => {
|
||||
write!(buf, "for<{}> ", lifetimes.iter().format(", "))?;
|
||||
print_path(path, buf)?;
|
||||
}
|
||||
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?,
|
||||
TypeBound::Error => write!(buf, "{{unknown}}")?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -77,6 +77,10 @@ impl Rawness {
|
|||
Rawness::Ref
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_raw(&self) -> bool {
|
||||
matches!(self, Self::RawPtr)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue