diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 8edb178cd74f..ae4aa4ae57a3 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -130,6 +130,7 @@ pub struct HirFormatter<'a, 'db> { pub entity_limit: Option, /// When rendering functions, whether to show the constraint from the container show_container_bounds: bool, + render_private_fields: bool, omit_verbose_types: bool, closure_style: ClosureStyle, display_lifetimes: DisplayLifetime, @@ -263,6 +264,7 @@ pub trait HirDisplay<'db> { display_kind, closure_style, show_container_bounds, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, } } @@ -287,6 +289,7 @@ pub trait HirDisplay<'db> { display_target, display_kind: DisplayKind::Diagnostics, show_container_bounds: false, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, } } @@ -312,6 +315,7 @@ pub trait HirDisplay<'db> { display_target, display_kind: DisplayKind::Diagnostics, show_container_bounds: false, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, } } @@ -337,6 +341,7 @@ pub trait HirDisplay<'db> { display_target, display_kind: DisplayKind::Diagnostics, show_container_bounds: false, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, } } @@ -364,6 +369,7 @@ pub trait HirDisplay<'db> { display_target: DisplayTarget::from_crate(db, module_id.krate(db)), display_kind: DisplayKind::SourceCode { target_module_id: module_id, allow_opaque }, show_container_bounds: false, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, currently_formatting_bounds: Default::default(), trait_bounds_need_parens: false, @@ -394,6 +400,7 @@ pub trait HirDisplay<'db> { display_target, display_kind: DisplayKind::Test, show_container_bounds: false, + render_private_fields: true, display_lifetimes: DisplayLifetime::Always, } } @@ -419,6 +426,7 @@ pub trait HirDisplay<'db> { display_target, display_kind: DisplayKind::Diagnostics, show_container_bounds, + render_private_fields: true, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, } } @@ -495,6 +503,10 @@ impl<'db> HirFormatter<'_, 'db> { pub fn show_container_bounds(&self) -> bool { self.show_container_bounds } + + pub fn render_private_fields(&self) -> bool { + self.render_private_fields + } } #[derive(Debug, Clone, Copy)] @@ -566,6 +578,7 @@ pub struct HirDisplayWrapper<'a, 'db, T> { display_kind: DisplayKind, display_target: DisplayTarget, show_container_bounds: bool, + render_private_fields: bool, display_lifetimes: DisplayLifetime, } @@ -601,6 +614,7 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> { display_target: self.display_target, closure_style: self.closure_style, show_container_bounds: self.show_container_bounds, + render_private_fields: self.render_private_fields, display_lifetimes: self.display_lifetimes, currently_formatting_bounds: Default::default(), trait_bounds_need_parens: false, @@ -616,6 +630,11 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> { self.display_lifetimes = l; self } + + pub fn with_private_fields(mut self, render: bool) -> Self { + self.render_private_fields = render; + self + } } impl<'db, T> fmt::Display for HirDisplayWrapper<'_, 'db, T> diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index ed18482bf380..860be3613e5c 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -14,6 +14,7 @@ use hir_def::{ TraitSignature, TypeAliasSignature, }, type_ref::{TypeBound, TypeRef, TypeRefId}, + visibility::Visibility, }; use hir_expand::name::Name; use hir_ty::{ @@ -363,20 +364,22 @@ impl<'db> HirDisplay<'db> for Struct { let def_id = GenericDefId::AdtId(AdtId::StructId(self.id)); write_generic_params(def_id, f)?; - let variant_data = self.variant_fields(f.db); match self.kind(f.db) { StructKind::Tuple => { f.write_char('(')?; - let mut it = variant_data.fields().iter().peekable(); + let (fields, hidden_fields) = visible_fields(self.fields(f.db), f); + let mut it = fields.iter().peekable(); - while let Some((id, _)) = it.next() { - let field = Field { parent: (*self).into(), id }; + while let Some(field) = it.next() { write_visibility(module_id, field.visibility(f.db), f)?; field.ty(f.db).hir_fmt(f)?; - if it.peek().is_some() { + if it.peek().is_some() || hidden_fields { f.write_str(", ")?; } } + if hidden_fields { + f.write_str("/* … */")?; + } f.write_char(')')?; write_where_clause(def_id, f)?; @@ -384,7 +387,8 @@ impl<'db> HirDisplay<'db> for Struct { StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - write_fields(&self.fields(f.db), has_where_clause, limit, false, f)?; + let (fields, hidden_fields) = visible_fields(self.fields(f.db), f); + write_fields(&fields, hidden_fields, has_where_clause, limit, false, f)?; } } StructKind::Unit => _ = write_where_clause(def_id, f)?, @@ -421,14 +425,33 @@ impl<'db> HirDisplay<'db> for Union { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - write_fields(&self.fields(f.db), has_where_clause, limit, false, f)?; + let (fields, hidden_fields) = visible_fields(self.fields(f.db), f); + write_fields(&fields, hidden_fields, has_where_clause, limit, false, f)?; } Ok(()) } } +fn visible_fields<'db>(fields: Vec, f: &mut HirFormatter<'_, 'db>) -> (Vec, bool) { + if f.render_private_fields() { + return (fields, false); + } + + let mut hidden_fields = false; + let fields = fields + .into_iter() + .filter(|field| { + let is_public = field.visibility(f.db) == Visibility::Public; + hidden_fields |= !is_public; + is_public + }) + .collect(); + (fields, hidden_fields) +} + fn write_fields<'db>( fields: &[Field], + hidden_fields: bool, has_where_clause: bool, limit: usize, in_line: bool, @@ -438,7 +461,7 @@ fn write_fields<'db>( let (indent, separator) = if in_line { ("", ' ') } else { (" ", '\n') }; f.write_char(if !has_where_clause { ' ' } else { separator })?; if count == 0 { - f.write_str(if fields.is_empty() { "{}" } else { "{ /* … */ }" })?; + f.write_str(if fields.is_empty() && !hidden_fields { "{}" } else { "{ /* … */ }" })?; } else { f.write_char('{')?; @@ -450,7 +473,7 @@ fn write_fields<'db>( write!(f, ",{separator}")?; } - if fields.len() > count { + if fields.len() > count || hidden_fields { write!(f, "{indent}/* … */{separator}")?; } } @@ -542,7 +565,8 @@ impl<'db> HirDisplay<'db> for EnumVariant { } FieldsShape::Record => { if let Some(limit) = f.entity_limit { - write_fields(&self.fields(f.db), false, limit, true, f)?; + let (fields, hidden_fields) = visible_fields(self.fields(f.db), f); + write_fields(&fields, hidden_fields, false, limit, true, f)?; } } } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index c3a8e0362fee..28e1dc72bf4c 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -481,6 +481,10 @@ pub(crate) fn hover_for_definition( }; let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default(); let subst_types = subst.map(|subst| subst.types(db)); + let render_private_fields = sema.scope(scope_node).is_some_and(|scope| { + def.krate(db) + .is_some_and(|def_crate| should_render_private_fields(db, def_crate, scope.krate())) + }); let (markup, range_map) = render::definition( sema.db, @@ -489,6 +493,7 @@ pub(crate) fn hover_for_definition( ¬able_traits, macro_arm, render_extras, + render_private_fields, subst_types.as_ref(), config, edition, @@ -508,6 +513,27 @@ pub(crate) fn hover_for_definition( } } +/// | Hover location | Definition location | Render private fields? | +/// |---|---:|---:| +/// | Workspace crate | Workspace crate | Yes | +/// | Workspace crate | External/library crate | No | +/// | External/library crate | Same external/library crate | Yes | +/// | External/library crate | Different external/library crate | No | +/// | Anywhere | Same crate as definition | Yes | +fn should_render_private_fields( + db: &RootDatabase, + def_crate: hir::Crate, + hover_crate: hir::Crate, +) -> bool { + let is_workspace_crate = |db: &RootDatabase, krate: hir::Crate| { + let origin = krate.origin(db); + !origin.is_lib() && !origin.is_lang() + }; + + def_crate == hover_crate + || is_workspace_crate(db, def_crate) && is_workspace_crate(db, hover_crate) +} + fn notable_traits<'db>( db: &'db RootDatabase, ty: &hir::Type<'db>, diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index da4f185d7564..3465375831a1 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -457,6 +457,7 @@ pub(super) fn definition( notable_traits: &[(Trait, Vec<(Option>, Name)>)], macro_arm: Option, render_extras: bool, + render_private_fields: bool, subst_types: Option<&Vec<(Symbol, Type<'_>)>>, config: &HoverConfig<'_>, edition: Edition, @@ -466,22 +467,27 @@ pub(super) fn definition( let label = match def { Definition::Trait(trait_) => trait_ .display_limited(db, config.max_trait_assoc_items_count, display_target) + .with_private_fields(render_private_fields) + .to_string(), + Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => adt + .display_limited(db, config.max_fields_count, display_target) + .with_private_fields(render_private_fields) + .to_string(), + Definition::EnumVariant(variant) => variant + .display_limited(db, config.max_fields_count, display_target) + .with_private_fields(render_private_fields) + .to_string(), + Definition::Adt(adt @ Adt::Enum(_)) => adt + .display_limited(db, config.max_enum_variants_count, display_target) + .with_private_fields(render_private_fields) .to_string(), - Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => { - adt.display_limited(db, config.max_fields_count, display_target).to_string() - } - Definition::EnumVariant(variant) => { - variant.display_limited(db, config.max_fields_count, display_target).to_string() - } - Definition::Adt(adt @ Adt::Enum(_)) => { - adt.display_limited(db, config.max_enum_variants_count, display_target).to_string() - } Definition::SelfType(impl_def) => { let self_ty = &impl_def.self_ty(db); match self_ty.as_adt() { - Some(adt) => { - adt.display_limited(db, config.max_fields_count, display_target).to_string() - } + Some(adt) => adt + .display_limited(db, config.max_fields_count, display_target) + .with_private_fields(render_private_fields) + .to_string(), None => self_ty.display(db, display_target).to_string(), } } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 30644fe2db89..288849c46b44 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -994,6 +994,158 @@ struct Foo$0 where u32: Copy { field: u32 } ); } +#[test] +fn hover_filters_private_struct_fields_by_hover_context() { + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + let _: dep::Foo$0; +} + +//- /dep.rs crate:dep library +pub struct Foo { + pub visible: i32, + hidden: i32, + pub(crate) crate_visible: i32, +} +"#, + expect![[r#" + *Foo* + + ```rust + dep + ``` + + ```rust + pub struct Foo { + pub visible: i32, + /* … */ + } + ``` + "#]], + ); + + check( + r#" +//- /dep.rs crate:dep library +pub struct Foo { + pub visible: i32, + hidden: i32, + pub(crate) crate_visible: i32, +} + +fn main() { + let _: Foo$0; +} +"#, + expect![[r#" + *Foo* + + ```rust + dep + ``` + + ```rust + pub struct Foo { + pub visible: i32, + hidden: i32, + pub(crate) crate_visible: i32, + } + ``` + "#]], + ); + + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + let _: dep::Foo$0; +} + +//- /dep.rs crate:dep +pub struct Foo { + pub visible: i32, + hidden: i32, + pub(crate) crate_visible: i32, +} +"#, + expect![[r#" + *Foo* + + ```rust + dep + ``` + + ```rust + pub struct Foo { + pub visible: i32, + hidden: i32, + pub(crate) crate_visible: i32, + } + ``` + "#]], + ); +} + +#[test] +fn hover_filters_private_tuple_struct_fields_by_hover_context() { + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + let _: dep::Foo$0; +} + +//- /dep.rs crate:dep library +pub struct Foo(pub i32, i32, pub(crate) i32); +"#, + expect![[r#" + *Foo* + + ```rust + dep + ``` + + ```rust + pub struct Foo(pub i32, /* … */) + ``` + "#]], + ); +} + +#[test] +fn hover_filters_private_union_fields_by_hover_context() { + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + let _: dep::U$0; +} + +//- /dep.rs crate:dep library +pub union U { + pub visible: i32, + hidden: u32, +} +"#, + expect![[r#" + *U* + + ```rust + dep + ``` + + ```rust + pub union U { + pub visible: i32, + /* … */ + } + ``` + "#]], + ); +} + #[test] fn hover_record_struct_limit() { check_hover_fields_limit(