Repository: rust-analyzer/rust-analyzer
Pull request #8932 (2021-05-23 01:00:20)

internal: even prettier itemtrees
Extends the ItemTree pretty printer to handle all `Path`s, and to print generic parameters and where-clauses.

bors r+


Patch:
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs
index 5ec02d1be7..4bc87a0e20 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, "<macro>");
             }
-            TypeRef::Error => drop(write!(self, "{{unknown}}")),
+            TypeRef::Error => w!(self, "{{unknown}}"),
             TypeRef::ImplTrait(bounds) => {
                 w!(self, "impl ");
                 self.print_type_bounds(bounds);
@@ -493,15 +528,168 @@ 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());
+        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 {
-            // too complicated, just use `Debug`
-            w!(self, "{:?}", path);
+            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 &params.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
+    }
 }
 
 impl<'a> Write for Printer<'a> {


Test patch:
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs
index 100ae9b97a..6407871b50 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<Self>: 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: <Fully as Qualified>::Syntax,
+    c: <TypeAnchored>::Path::<'a>,
+}
+        "#,
+        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>,
+            }
+        "#]],
+    )
+}
+
+#[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: Copy>(T);
+
+impl<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> S<'a, 'b, T, K> {
+    fn f<G: 'a>(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<T>(
+                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<G, _anon_1>(
+                    _: 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
+            {
+            }
+        "#]],
+    )
+}