diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index eb7865c842..6fa676c4d2 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1999,6 +1999,7 @@ impl Type { env, krate, traits_in_scope, + None, name, method_resolution::LookupMode::MethodCall, |ty, it| match it { @@ -2031,6 +2032,7 @@ impl Type { env, krate, traits_in_scope, + None, name, method_resolution::LookupMode::Path, |ty, it| callback(ty, it.into()), diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index 53df85089a..068b2ee385 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs @@ -16,6 +16,7 @@ use crate::{ item_tree::ItemTree, lang_item::{LangItemTarget, LangItems}, nameres::DefMap, + visibility::{self, Visibility}, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, @@ -131,6 +132,12 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast { #[salsa::invoke(ImportMap::import_map_query)] fn import_map(&self, krate: CrateId) -> Arc; + + #[salsa::invoke(visibility::field_visibilities_query)] + fn field_visibilities(&self, var: VariantId) -> Arc>; + + #[salsa::invoke(visibility::function_visibility_query)] + fn function_visibility(&self, def: FunctionId) -> Visibility; } fn crate_def_map_wait(db: &dyn DefDatabase, krate: CrateId) -> Arc { diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs index 0e39519100..7d00a37c40 100644 --- a/crates/hir_def/src/visibility.rs +++ b/crates/hir_def/src/visibility.rs @@ -1,13 +1,17 @@ //! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`). +use std::sync::Arc; + use hir_expand::{hygiene::Hygiene, InFile}; +use la_arena::ArenaMap; use syntax::ast; use crate::{ db::DefDatabase, nameres::DefMap, path::{ModPath, PathKind}, - ModuleId, + resolver::HasResolver, + FunctionId, HasModule, LocalFieldId, ModuleDefId, ModuleId, VariantId, }; /// Visibility of an item, not yet resolved. @@ -190,3 +194,29 @@ impl Visibility { } } } + +/// Resolve visibility of all specific fields of a struct or union variant. +pub(crate) fn field_visibilities_query( + db: &dyn DefDatabase, + variant_id: VariantId, +) -> Arc> { + let var_data = match variant_id { + VariantId::StructId(it) => db.struct_data(it).variant_data.clone(), + VariantId::UnionId(it) => db.union_data(it).variant_data.clone(), + VariantId::EnumVariantId(it) => { + db.enum_data(it.parent).variants[it.local_id].variant_data.clone() + } + }; + let resolver = variant_id.module(db).resolver(db); + let mut res = ArenaMap::default(); + for (field_id, field_data) in var_data.fields().iter() { + res.insert(field_id, field_data.visibility.resolve(db, &resolver)) + } + Arc::new(res) +} + +/// Resolve visibility of a function. +pub(crate) fn function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -> Visibility { + let resolver = ModuleDefId::from(def).module(db).unwrap().resolver(db); + db.function_data(def).visibility.resolve(db, &resolver) +} diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 19249973c9..3f3187ea27 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -443,27 +443,47 @@ impl<'a> InferenceContext<'a> { }, ) .find_map(|derefed_ty| { + let def_db = self.db.upcast(); + let module = self.resolver.module(); + let is_visible = |field_id: &FieldId| { + module + .map(|mod_id| { + self.db.field_visibilities(field_id.parent)[field_id.local_id] + .is_visible_from(def_db, mod_id) + }) + .unwrap_or(true) + }; match canonicalized.decanonicalize_ty(derefed_ty.value).interned(&Interner) { TyKind::Tuple(_, substs) => { name.as_tuple_index().and_then(|idx| substs.0.get(idx).cloned()) } TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => { - self.db.struct_data(*s).variant_data.field(name).map(|local_id| { - let field = FieldId { parent: (*s).into(), local_id }; + let local_id = self.db.struct_data(*s).variant_data.field(name)?; + let field = FieldId { parent: (*s).into(), local_id }; + if is_visible(&field) { self.write_field_resolution(tgt_expr, field); - self.db.field_types((*s).into())[field.local_id] - .clone() - .subst(¶meters) - }) + Some( + self.db.field_types((*s).into())[field.local_id] + .clone() + .subst(¶meters), + ) + } else { + None + } } TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => { - self.db.union_data(*u).variant_data.field(name).map(|local_id| { - let field = FieldId { parent: (*u).into(), local_id }; + let local_id = self.db.union_data(*u).variant_data.field(name)?; + let field = FieldId { parent: (*u).into(), local_id }; + if is_visible(&field) { self.write_field_resolution(tgt_expr, field); - self.db.field_types((*u).into())[field.local_id] - .clone() - .subst(¶meters) - }) + Some( + self.db.field_types((*u).into())[field.local_id] + .clone() + .subst(¶meters), + ) + } else { + None + } } _ => None, } @@ -828,6 +848,7 @@ impl<'a> InferenceContext<'a> { self.trait_env.clone(), krate, &traits_in_scope, + self.resolver.module(), method_name, ) }); diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index 58cce56abb..cefa385094 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs @@ -230,6 +230,7 @@ impl<'a> InferenceContext<'a> { self.trait_env.clone(), krate, &traits_in_scope, + None, Some(name), method_resolution::LookupMode::Path, move |_ty, item| { diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 8e986ddde2..84d9a1e186 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -296,6 +296,7 @@ pub(crate) fn lookup_method( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, + visible_from_module: Option, name: &Name, ) -> Option<(Ty, FunctionId)> { iterate_method_candidates( @@ -304,6 +305,7 @@ pub(crate) fn lookup_method( env, krate, &traits_in_scope, + visible_from_module, Some(name), LookupMode::MethodCall, |ty, f| match f { @@ -334,6 +336,7 @@ pub fn iterate_method_candidates( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, + visible_from_module: Option, name: Option<&Name>, mode: LookupMode, mut callback: impl FnMut(&Ty, AssocItemId) -> Option, @@ -345,6 +348,7 @@ pub fn iterate_method_candidates( env, krate, traits_in_scope, + visible_from_module, name, mode, &mut |ty, item| { @@ -362,6 +366,7 @@ fn iterate_method_candidates_impl( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, + visible_from_module: Option, name: Option<&Name>, mode: LookupMode, callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, @@ -399,6 +404,7 @@ fn iterate_method_candidates_impl( env.clone(), krate, traits_in_scope, + visible_from_module, name, callback, ) { @@ -415,6 +421,7 @@ fn iterate_method_candidates_impl( env, krate, traits_in_scope, + visible_from_module, name, callback, ) @@ -428,6 +435,7 @@ fn iterate_method_candidates_with_autoref( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, + visible_from_module: Option, name: Option<&Name>, mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, ) -> bool { @@ -438,6 +446,7 @@ fn iterate_method_candidates_with_autoref( env.clone(), krate, &traits_in_scope, + visible_from_module, name, &mut callback, ) { @@ -454,6 +463,7 @@ fn iterate_method_candidates_with_autoref( env.clone(), krate, &traits_in_scope, + visible_from_module, name, &mut callback, ) { @@ -470,6 +480,7 @@ fn iterate_method_candidates_with_autoref( env, krate, &traits_in_scope, + visible_from_module, name, &mut callback, ) { @@ -485,6 +496,7 @@ fn iterate_method_candidates_by_receiver( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, + visible_from_module: Option, name: Option<&Name>, mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, ) -> bool { @@ -492,7 +504,15 @@ fn iterate_method_candidates_by_receiver( // be found in any of the derefs of receiver_ty, so we have to go through // that. for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) { - if iterate_inherent_methods(self_ty, db, name, Some(receiver_ty), krate, &mut callback) { + if iterate_inherent_methods( + self_ty, + db, + name, + Some(receiver_ty), + krate, + visible_from_module, + &mut callback, + ) { return true; } } @@ -519,10 +539,12 @@ fn iterate_method_candidates_for_self_ty( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, + visible_from_module: Option, name: Option<&Name>, mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, ) -> bool { - if iterate_inherent_methods(self_ty, db, name, None, krate, &mut callback) { + if iterate_inherent_methods(self_ty, db, name, None, krate, visible_from_module, &mut callback) + { return true; } iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback) @@ -559,7 +581,9 @@ fn iterate_trait_method_candidates( // iteration let mut known_implemented = false; for (_name, item) in data.items.iter() { - if !is_valid_candidate(db, name, receiver_ty, *item, self_ty) { + // Don't pass a `visible_from_module` down to `is_valid_candidate`, + // since only inherent methods should be included into visibility checking. + if !is_valid_candidate(db, name, receiver_ty, *item, self_ty, None) { continue; } if !known_implemented { @@ -583,6 +607,7 @@ fn iterate_inherent_methods( name: Option<&Name>, receiver_ty: Option<&Canonical>, krate: CrateId, + visible_from_module: Option, callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, ) -> bool { let def_crates = match self_ty.value.def_crates(db, krate) { @@ -594,7 +619,7 @@ fn iterate_inherent_methods( for &impl_def in impls.for_self_ty(&self_ty.value) { for &item in db.impl_data(impl_def).items.iter() { - if !is_valid_candidate(db, name, receiver_ty, item, self_ty) { + if !is_valid_candidate(db, name, receiver_ty, item, self_ty, visible_from_module) { continue; } // we have to check whether the self type unifies with the type @@ -639,6 +664,7 @@ fn is_valid_candidate( receiver_ty: Option<&Canonical>, item: AssocItemId, self_ty: &Canonical, + visible_from_module: Option, ) -> bool { match item { AssocItemId::FunctionId(m) => { @@ -660,6 +686,13 @@ fn is_valid_candidate( return false; } } + if let Some(from_module) = visible_from_module { + if !db.function_visibility(m).is_visible_from(db.upcast(), from_module) { + cov_mark::hit!(autoderef_candidate_not_visible); + return false; + } + } + true } AssocItemId::ConstId(c) => { diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index 12951fb16c..7eda518663 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs @@ -31,12 +31,12 @@ struct S; #[cfg(not(test))] impl S { - fn foo3(&self) -> i32 { 0 } + pub fn foo3(&self) -> i32 { 0 } } #[cfg(test)] impl S { - fn foo4(&self) -> i32 { 0 } + pub fn foo4(&self) -> i32 { 0 } } "#, ); diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 4e3f9a9b62..61f18b0d20 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs @@ -1173,3 +1173,122 @@ fn main() { "#, ); } + +#[test] +fn autoderef_visibility_field() { + check_infer( + r#" +#[lang = "deref"] +pub trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} +mod a { + pub struct Foo(pub char); + pub struct Bar(i32); + impl Bar { + pub fn new() -> Self { + Self(0) + } + } + impl super::Deref for Bar { + type Target = Foo; + fn deref(&self) -> &Foo { + &Foo('z') + } + } +} +mod b { + fn foo() { + let x = super::a::Bar::new().0; + } +} + "#, + expect![[r#" + 67..71 'self': &Self + 200..231 '{ ... }': Bar + 214..218 'Self': Bar(i32) -> Bar + 214..221 'Self(0)': Bar + 219..220 '0': i32 + 315..319 'self': &Bar + 329..362 '{ ... }': &Foo + 343..352 '&Foo('z')': &Foo + 344..347 'Foo': Foo(char) -> Foo + 344..352 'Foo('z')': Foo + 348..351 ''z'': char + 392..439 '{ ... }': () + 406..407 'x': char + 410..428 'super:...r::new': fn new() -> Bar + 410..430 'super:...:new()': Bar + 410..432 'super:...ew().0': char + "#]], + ) +} + +#[test] +fn autoderef_visibility_method() { + cov_mark::check!(autoderef_candidate_not_visible); + check_infer( + r#" +#[lang = "deref"] +pub trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} +mod a { + pub struct Foo(pub char); + impl Foo { + pub fn mango(&self) -> char { + self.0 + } + } + pub struct Bar(i32); + impl Bar { + pub fn new() -> Self { + Self(0) + } + fn mango(&self) -> i32 { + self.0 + } + } + impl super::Deref for Bar { + type Target = Foo; + fn deref(&self) -> &Foo { + &Foo('z') + } + } +} +mod b { + fn foo() { + let x = super::a::Bar::new().mango(); + } +} + "#, + expect![[r#" + 67..71 'self': &Self + 168..172 'self': &Foo + 182..212 '{ ... }': char + 196..200 'self': &Foo + 196..202 'self.0': char + 288..319 '{ ... }': Bar + 302..306 'Self': Bar(i32) -> Bar + 302..309 'Self(0)': Bar + 307..308 '0': i32 + 338..342 'self': &Bar + 351..381 '{ ... }': i32 + 365..369 'self': &Bar + 365..371 'self.0': i32 + 465..469 'self': &Bar + 479..512 '{ ... }': &Foo + 493..502 '&Foo('z')': &Foo + 494..497 'Foo': Foo(char) -> Foo + 494..502 'Foo('z')': Foo + 498..501 ''z'': char + 542..595 '{ ... }': () + 556..557 'x': char + 560..578 'super:...r::new': fn new() -> Bar + 560..580 'super:...:new()': Bar + 560..588 'super:...ango()': char + "#]], + ) +} diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index bcc43ed700..361cd63026 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs @@ -1103,7 +1103,7 @@ fn infer_inherent_method() { mod b { impl super::A { - fn bar(&self, x: u64) -> i64 {} + pub fn bar(&self, x: u64) -> i64 {} } } @@ -1117,21 +1117,21 @@ fn infer_inherent_method() { 31..35 'self': A 37..38 'x': u32 52..54 '{}': () - 102..106 'self': &A - 108..109 'x': u64 - 123..125 '{}': () - 143..144 'a': A - 149..197 '{ ...(1); }': () - 155..156 'a': A - 155..163 'a.foo(1)': i32 - 161..162 '1': u32 - 169..180 '(&a).bar(1)': i64 - 170..172 '&a': &A - 171..172 'a': A - 178..179 '1': u64 - 186..187 'a': A - 186..194 'a.bar(1)': i64 - 192..193 '1': u64 + 106..110 'self': &A + 112..113 'x': u64 + 127..129 '{}': () + 147..148 'a': A + 153..201 '{ ...(1); }': () + 159..160 'a': A + 159..167 'a.foo(1)': i32 + 165..166 '1': u32 + 173..184 '(&a).bar(1)': i64 + 174..176 '&a': &A + 175..176 'a': A + 182..183 '1': u64 + 190..191 'a': A + 190..198 'a.bar(1)': i64 + 196..197 '1': u64 "#]], ); } diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 45a1958e30..2ba97f814d 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs @@ -187,8 +187,8 @@ mod iter { mod collections { struct Vec {} impl Vec { - fn new() -> Self { Vec {} } - fn push(&mut self, t: T) { } + pub fn new() -> Self { Vec {} } + pub fn push(&mut self, t: T) { } } impl IntoIterator for Vec {