diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs index 5ec02d1be74..4bc87a0e20b 100644 --- a/crates/hir_def/src/item_tree/pretty.rs +++ b/crates/hir_def/src/item_tree/pretty.rs @@ -2,7 +2,12 @@ use std::fmt::{self, Write}; -use crate::{attr::RawAttrs, visibility::RawVisibility}; +use crate::{ + attr::RawAttrs, + generics::{WherePredicate, WherePredicateTypeTarget}, + path::GenericArg, + visibility::RawVisibility, +}; use super::*; @@ -70,6 +75,13 @@ impl<'a> Printer<'a> { } } + fn whitespace(&mut self) { + match self.buf.chars().next_back() { + None | Some('\n') | Some(' ') => {} + _ => self.buf.push(' '), + } + } + fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool) { let inner = if inner { "!" } else { "" }; for attr in &**attrs { @@ -100,7 +112,8 @@ impl<'a> Printer<'a> { fn print_fields(&mut self, fields: &Fields) { match fields { Fields::Record(fields) => { - w!(self, " {{"); + self.whitespace(); + w!(self, "{{"); self.indented(|this| { for field in fields.clone() { let Field { visibility, name, type_ref } = &this.tree[field]; @@ -131,6 +144,25 @@ impl<'a> Printer<'a> { } } + fn print_fields_and_where_clause(&mut self, fields: &Fields, params: &GenericParams) { + match fields { + Fields::Record(_) => { + if self.print_where_clause(params) { + wln!(self); + } + self.print_fields(fields); + } + Fields::Unit => { + self.print_where_clause(params); + self.print_fields(fields); + } + Fields::Tuple(_) => { + self.print_fields(fields); + self.print_where_clause(params); + } + } + } + fn print_mod_item(&mut self, item: ModItem) { self.print_attrs_of(item); @@ -174,7 +206,7 @@ impl<'a> Printer<'a> { let Function { name, visibility, - generic_params: _, // FIXME print these somehow + generic_params, abi, params, ret_type, @@ -188,7 +220,9 @@ impl<'a> Printer<'a> { if let Some(abi) = abi { w!(self, "extern \"{}\" ", abi); } - w!(self, "fn {}(", name); + w!(self, "fn {}", name); + self.print_generic_params(generic_params); + w!(self, "("); if !params.is_empty() { self.indented(|this| { for param in params.clone() { @@ -208,14 +242,15 @@ impl<'a> Printer<'a> { } w!(self, ") -> "); self.print_type_ref(ret_type); + self.print_where_clause(generic_params); wln!(self, ";"); } ModItem::Struct(it) => { - let Struct { visibility, name, fields, generic_params: _, ast_id: _ } = - &self.tree[it]; + let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "struct {}", name); - self.print_fields(fields); + self.print_generic_params(generic_params); + self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { wln!(self); } else { @@ -223,11 +258,11 @@ impl<'a> Printer<'a> { } } ModItem::Union(it) => { - let Union { name, visibility, fields, generic_params: _, ast_id: _ } = - &self.tree[it]; + let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "union {}", name); - self.print_fields(fields); + self.print_generic_params(generic_params); + self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { wln!(self); } else { @@ -235,10 +270,11 @@ impl<'a> Printer<'a> { } } ModItem::Enum(it) => { - let Enum { name, visibility, variants, generic_params: _, ast_id: _ } = - &self.tree[it]; + let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "enum {} {{", name); + w!(self, "enum {}", name); + self.print_generic_params(generic_params); + self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { for variant in variants.clone() { let Variant { name, fields } = &this.tree[variant]; @@ -286,7 +322,7 @@ impl<'a> Printer<'a> { is_unsafe, bounds, items, - generic_params: _, + generic_params, ast_id: _, } = &self.tree[it]; self.print_visibility(*visibility); @@ -297,11 +333,12 @@ impl<'a> Printer<'a> { w!(self, "auto "); } w!(self, "trait {}", name); + self.print_generic_params(generic_params); if !bounds.is_empty() { w!(self, ": "); self.print_type_bounds(bounds); } - w!(self, " {{"); + self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { for item in &**items { this.print_mod_item((*item).into()); @@ -310,15 +347,11 @@ impl<'a> Printer<'a> { wln!(self, "}}"); } ModItem::Impl(it) => { - let Impl { - target_trait, - self_ty, - is_negative, - items, - generic_params: _, - ast_id: _, - } = &self.tree[it]; - w!(self, "impl "); + let Impl { target_trait, self_ty, is_negative, items, generic_params, ast_id: _ } = + &self.tree[it]; + w!(self, "impl"); + self.print_generic_params(generic_params); + w!(self, " "); if *is_negative { w!(self, "!"); } @@ -327,7 +360,7 @@ impl<'a> Printer<'a> { w!(self, " for "); } self.print_type_ref(self_ty); - w!(self, " {{"); + self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { for item in &**items { this.print_mod_item((*item).into()); @@ -342,11 +375,12 @@ impl<'a> Printer<'a> { bounds, type_ref, is_extern, - generic_params: _, + generic_params, ast_id: _, } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "type {}", name); + self.print_generic_params(generic_params); if !bounds.is_empty() { w!(self, ": "); self.print_type_bounds(bounds); @@ -355,6 +389,7 @@ impl<'a> Printer<'a> { w!(self, " = "); self.print_type_ref(ty); } + self.print_where_clause(generic_params); w!(self, ";"); if *is_extern { w!(self, " // extern"); @@ -466,7 +501,7 @@ impl<'a> Printer<'a> { TypeRef::Macro(_ast_id) => { w!(self, ""); } - TypeRef::Error => drop(write!(self, "{{unknown}}")), + TypeRef::Error => w!(self, "{{unknown}}"), TypeRef::ImplTrait(bounds) => { w!(self, "impl "); self.print_type_bounds(bounds); @@ -493,14 +528,167 @@ impl<'a> Printer<'a> { } fn print_path(&mut self, path: &Path) { - if path.type_anchor().is_none() - && path.segments().iter().all(|seg| seg.args_and_bindings.is_none()) - { - w!(self, "{}", path.mod_path()); - } else { - // too complicated, just use `Debug` - w!(self, "{:?}", 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::Lifetime(lt) => w!(self, "{}", lt.name), + } + } + + fn print_generic_params(&mut self, params: &GenericParams) { + if params.types.is_empty() && params.lifetimes.is_empty() && params.consts.is_empty() { + return; + } + + w!(self, "<"); + let mut first = true; + for (_, lt) in params.lifetimes.iter() { + if !first { + w!(self, ", "); + } + first = false; + w!(self, "{}", lt.name); + } + for (idx, ty) in params.types.iter() { + if !first { + w!(self, ", "); + } + first = false; + match &ty.name { + Some(name) => w!(self, "{}", name), + None => w!(self, "_anon_{}", idx.into_raw()), + } + } + for (_, konst) in params.consts.iter() { + if !first { + w!(self, ", "); + } + first = false; + w!(self, "const {}: ", konst.name); + self.print_type_ref(&konst.ty); + } + w!(self, ">"); + } + + fn print_where_clause_and_opening_brace(&mut self, params: &GenericParams) { + if self.print_where_clause(params) { + w!(self, "\n{{"); + } else { + self.whitespace(); + w!(self, "{{"); + } + } + + fn print_where_clause(&mut self, params: &GenericParams) -> bool { + if params.where_predicates.is_empty() { + return false; + } + + w!(self, "\nwhere"); + self.indented(|this| { + for (i, pred) in params.where_predicates.iter().enumerate() { + if i != 0 { + wln!(this, ","); + } + + let (target, bound) = match pred { + WherePredicate::TypeBound { target, bound } => (target, bound), + WherePredicate::Lifetime { target, bound } => { + wln!(this, "{}: {},", target.name, bound.name); + continue; + } + WherePredicate::ForLifetime { lifetimes, target, bound } => { + w!(this, "for<"); + for (i, lt) in lifetimes.iter().enumerate() { + if i != 0 { + w!(this, ", "); + } + w!(this, "{}", lt); + } + w!(this, "> "); + (target, bound) + } + }; + + match target { + WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty), + WherePredicateTypeTarget::TypeParam(id) => match ¶ms.types[*id].name { + Some(name) => w!(this, "{}", name), + None => w!(this, "_anon_{}", id.into_raw()), + }, + } + w!(this, ": "); + this.print_type_bounds(std::slice::from_ref(bound)); + } + }); + true } } diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs index 100ae9b97a2..6407871b504 100644 --- a/crates/hir_def/src/item_tree/tests.rs +++ b/crates/hir_def/src/item_tree/tests.rs @@ -183,7 +183,11 @@ trait Tr: SuperTrait + 'lifetime { _: (), ) -> (); - pub(self) trait Tr: SuperTrait + 'lifetime { + pub(self) trait Tr: SuperTrait + 'lifetime + where + Self: SuperTrait, + Self: 'lifetime + { pub(self) type Assoc: AssocBound = Default; // flags = 0x1 @@ -207,6 +211,8 @@ mod inline { fn fn_in_module() {} } + +mod outline; "#, expect![[r##" #[doc = " outer"] // AttrId { is_doc_comment: true, ast_index: 0 } @@ -217,6 +223,8 @@ mod inline { // flags = 0x2 pub(self) fn fn_in_module() -> (); } + + pub(self) mod outline; "##]], ); } @@ -242,3 +250,115 @@ m!(); "#]], ); } + +#[test] +fn mod_paths() { + check( + r#" +struct S { + a: self::Ty, + b: super::SuperTy, + c: super::super::SuperSuperTy, + d: ::abs::Path, + e: crate::Crate, + f: plain::path::Ty, +} + "#, + expect![[r#" + pub(self) struct S { + pub(self) a: self::Ty, + pub(self) b: super::SuperTy, + pub(self) c: super::super::SuperSuperTy, + pub(self) d: ::abs::Path, + pub(self) e: crate::Crate, + pub(self) f: plain::path::Ty, + } + "#]], + ) +} + +#[test] +fn types() { + check( + r#" +struct S { + a: Mixed<'a, T, Item=(), OtherItem=u8>, + b: ::Syntax, + c: ::Path::<'a>, +} + "#, + expect![[r#" + pub(self) struct S { + pub(self) a: Mixed<'a, T, Item = (), OtherItem = u8>, + pub(self) b: Qualified::Syntax, + pub(self) c: ::Path<'a>, + } + "#]], + ) +} + +#[test] +fn generics() { + check( + r#" +struct S<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> { + field: &'a &'b T, +} + +struct Tuple(T); + +impl<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> S<'a, 'b, T, K> { + fn f(arg: impl Copy) -> impl Copy {} +} + +enum Enum<'a, T, const U: u8> {} +union Union<'a, T, const U: u8> {} + +trait Tr<'a, T: 'a>: Super {} + "#, + expect![[r#" + pub(self) struct S<'a, 'b, T, const K: u8> + where + T: Copy, + T: 'a, + T: 'b + { + pub(self) field: &'a &'b T, + } + + pub(self) struct Tuple( + pub(self) 0: T, + ) + where + T: Copy; + + impl<'a, 'b, T, const K: u8> S<'a, 'b, T, K> + where + T: Copy, + T: 'a, + T: 'b + { + // flags = 0x2 + pub(self) fn f( + _: impl Copy, + ) -> impl Copy + where + G: 'a, + _anon_1: Copy; + } + + pub(self) enum Enum<'a, T, const U: u8> { + } + + pub(self) union Union<'a, T, const U: u8> { + } + + pub(self) trait Tr<'a, Self, T>: Super + where + Self: Super, + T: 'a + { + } + "#]], + ) +}