mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
Merge #2667
2667: Visibility r=matklad a=flodiebold This adds the infrastructure for handling visibility (for fields and methods, not in name resolution) in the HIR and code model, and as a first application hides struct fields from completions if they're not visible from the current module. (We might want to relax this again later, but I think it's ok for now?) Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
cdcb3d3833
21 changed files with 607 additions and 112 deletions
|
@ -118,7 +118,7 @@ impl_froms!(
|
||||||
BuiltinType
|
BuiltinType
|
||||||
);
|
);
|
||||||
|
|
||||||
pub use hir_def::attr::Attrs;
|
pub use hir_def::{attr::Attrs, visibility::Visibility};
|
||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module {
|
pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module {
|
||||||
|
@ -255,6 +255,15 @@ impl StructField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasVisibility for StructField {
|
||||||
|
fn visibility(&self, db: &impl HirDatabase) -> Visibility {
|
||||||
|
let variant_data = self.parent.variant_data(db);
|
||||||
|
let visibility = &variant_data.fields()[self.id].visibility;
|
||||||
|
let parent_id: hir_def::VariantId = self.parent.into();
|
||||||
|
visibility.resolve(db, &parent_id.resolver(db))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Struct {
|
pub struct Struct {
|
||||||
pub(crate) id: StructId,
|
pub(crate) id: StructId,
|
||||||
|
@ -1041,3 +1050,11 @@ impl<T: Into<AttrDef> + Copy> Docs for T {
|
||||||
db.documentation(def.into())
|
db.documentation(def.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait HasVisibility {
|
||||||
|
fn visibility(&self, db: &impl HirDatabase) -> Visibility;
|
||||||
|
fn is_visible_from(&self, db: &impl HirDatabase, module: Module) -> bool {
|
||||||
|
let vis = self.visibility(db);
|
||||||
|
vis.is_visible_from(db, module.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -40,8 +40,8 @@ mod from_source;
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
code_model::{
|
code_model::{
|
||||||
Adt, AssocItem, AttrDef, Const, Crate, CrateDependency, DefWithBody, Docs, Enum,
|
Adt, AssocItem, AttrDef, Const, Crate, CrateDependency, DefWithBody, Docs, Enum,
|
||||||
EnumVariant, FieldSource, Function, GenericDef, HasAttrs, ImplBlock, Local, MacroDef,
|
EnumVariant, FieldSource, Function, GenericDef, HasAttrs, HasVisibility, ImplBlock, Local,
|
||||||
Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias,
|
MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias,
|
||||||
TypeParam, Union, VariantDef,
|
TypeParam, Union, VariantDef,
|
||||||
},
|
},
|
||||||
from_source::FromSource,
|
from_source::FromSource,
|
||||||
|
|
|
@ -9,11 +9,12 @@ use hir_expand::{
|
||||||
};
|
};
|
||||||
use ra_arena::{map::ArenaMap, Arena};
|
use ra_arena::{map::ArenaMap, Arena};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
|
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace, type_ref::TypeRef, EnumId,
|
db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace, type_ref::TypeRef,
|
||||||
LocalEnumVariantId, LocalStructFieldId, Lookup, StructId, UnionId, VariantId,
|
visibility::RawVisibility, EnumId, LocalEnumVariantId, LocalStructFieldId, Lookup, StructId,
|
||||||
|
UnionId, VariantId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Note that we use `StructData` for unions as well!
|
/// Note that we use `StructData` for unions as well!
|
||||||
|
@ -47,13 +48,14 @@ pub enum VariantData {
|
||||||
pub struct StructFieldData {
|
pub struct StructFieldData {
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub type_ref: TypeRef,
|
pub type_ref: TypeRef,
|
||||||
|
pub visibility: RawVisibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructData {
|
impl StructData {
|
||||||
pub(crate) fn struct_data_query(db: &impl DefDatabase, id: StructId) -> Arc<StructData> {
|
pub(crate) fn struct_data_query(db: &impl DefDatabase, id: StructId) -> Arc<StructData> {
|
||||||
let src = id.lookup(db).source(db);
|
let src = id.lookup(db).source(db);
|
||||||
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
||||||
let variant_data = VariantData::new(src.value.kind());
|
let variant_data = VariantData::new(db, src.map(|s| s.kind()));
|
||||||
let variant_data = Arc::new(variant_data);
|
let variant_data = Arc::new(variant_data);
|
||||||
Arc::new(StructData { name, variant_data })
|
Arc::new(StructData { name, variant_data })
|
||||||
}
|
}
|
||||||
|
@ -61,10 +63,12 @@ impl StructData {
|
||||||
let src = id.lookup(db).source(db);
|
let src = id.lookup(db).source(db);
|
||||||
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
||||||
let variant_data = VariantData::new(
|
let variant_data = VariantData::new(
|
||||||
src.value
|
db,
|
||||||
.record_field_def_list()
|
src.map(|s| {
|
||||||
.map(ast::StructKind::Record)
|
s.record_field_def_list()
|
||||||
.unwrap_or(ast::StructKind::Unit),
|
.map(ast::StructKind::Record)
|
||||||
|
.unwrap_or(ast::StructKind::Unit)
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
let variant_data = Arc::new(variant_data);
|
let variant_data = Arc::new(variant_data);
|
||||||
Arc::new(StructData { name, variant_data })
|
Arc::new(StructData { name, variant_data })
|
||||||
|
@ -77,7 +81,7 @@ impl EnumData {
|
||||||
let src = e.lookup(db).source(db);
|
let src = e.lookup(db).source(db);
|
||||||
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
||||||
let mut trace = Trace::new_for_arena();
|
let mut trace = Trace::new_for_arena();
|
||||||
lower_enum(&mut trace, &src.value);
|
lower_enum(db, &mut trace, &src);
|
||||||
Arc::new(EnumData { name, variants: trace.into_arena() })
|
Arc::new(EnumData { name, variants: trace.into_arena() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,30 +97,31 @@ impl HasChildSource for EnumId {
|
||||||
fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
|
fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
|
||||||
let src = self.lookup(db).source(db);
|
let src = self.lookup(db).source(db);
|
||||||
let mut trace = Trace::new_for_map();
|
let mut trace = Trace::new_for_map();
|
||||||
lower_enum(&mut trace, &src.value);
|
lower_enum(db, &mut trace, &src);
|
||||||
src.with_value(trace.into_map())
|
src.with_value(trace.into_map())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_enum(
|
fn lower_enum(
|
||||||
|
db: &impl DefDatabase,
|
||||||
trace: &mut Trace<LocalEnumVariantId, EnumVariantData, ast::EnumVariant>,
|
trace: &mut Trace<LocalEnumVariantId, EnumVariantData, ast::EnumVariant>,
|
||||||
ast: &ast::EnumDef,
|
ast: &InFile<ast::EnumDef>,
|
||||||
) {
|
) {
|
||||||
for var in ast.variant_list().into_iter().flat_map(|it| it.variants()) {
|
for var in ast.value.variant_list().into_iter().flat_map(|it| it.variants()) {
|
||||||
trace.alloc(
|
trace.alloc(
|
||||||
|| var.clone(),
|
|| var.clone(),
|
||||||
|| EnumVariantData {
|
|| EnumVariantData {
|
||||||
name: var.name().map_or_else(Name::missing, |it| it.as_name()),
|
name: var.name().map_or_else(Name::missing, |it| it.as_name()),
|
||||||
variant_data: Arc::new(VariantData::new(var.kind())),
|
variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()))),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VariantData {
|
impl VariantData {
|
||||||
fn new(flavor: ast::StructKind) -> Self {
|
fn new(db: &impl DefDatabase, flavor: InFile<ast::StructKind>) -> Self {
|
||||||
let mut trace = Trace::new_for_arena();
|
let mut trace = Trace::new_for_arena();
|
||||||
match lower_struct(&mut trace, &flavor) {
|
match lower_struct(db, &mut trace, &flavor) {
|
||||||
StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
|
StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
|
||||||
StructKind::Record => VariantData::Record(trace.into_arena()),
|
StructKind::Record => VariantData::Record(trace.into_arena()),
|
||||||
StructKind::Unit => VariantData::Unit,
|
StructKind::Unit => VariantData::Unit,
|
||||||
|
@ -163,7 +168,7 @@ impl HasChildSource for VariantId {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
let mut trace = Trace::new_for_map();
|
let mut trace = Trace::new_for_map();
|
||||||
lower_struct(&mut trace, &src.value);
|
lower_struct(db, &mut trace, &src);
|
||||||
src.with_value(trace.into_map())
|
src.with_value(trace.into_map())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,14 +180,15 @@ enum StructKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_struct(
|
fn lower_struct(
|
||||||
|
db: &impl DefDatabase,
|
||||||
trace: &mut Trace<
|
trace: &mut Trace<
|
||||||
LocalStructFieldId,
|
LocalStructFieldId,
|
||||||
StructFieldData,
|
StructFieldData,
|
||||||
Either<ast::TupleFieldDef, ast::RecordFieldDef>,
|
Either<ast::TupleFieldDef, ast::RecordFieldDef>,
|
||||||
>,
|
>,
|
||||||
ast: &ast::StructKind,
|
ast: &InFile<ast::StructKind>,
|
||||||
) -> StructKind {
|
) -> StructKind {
|
||||||
match ast {
|
match &ast.value {
|
||||||
ast::StructKind::Tuple(fl) => {
|
ast::StructKind::Tuple(fl) => {
|
||||||
for (i, fd) in fl.fields().enumerate() {
|
for (i, fd) in fl.fields().enumerate() {
|
||||||
trace.alloc(
|
trace.alloc(
|
||||||
|
@ -190,6 +196,7 @@ fn lower_struct(
|
||||||
|| StructFieldData {
|
|| StructFieldData {
|
||||||
name: Name::new_tuple_field(i),
|
name: Name::new_tuple_field(i),
|
||||||
type_ref: TypeRef::from_ast_opt(fd.type_ref()),
|
type_ref: TypeRef::from_ast_opt(fd.type_ref()),
|
||||||
|
visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -202,6 +209,7 @@ fn lower_struct(
|
||||||
|| StructFieldData {
|
|| StructFieldData {
|
||||||
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
|
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
|
||||||
type_ref: TypeRef::from_ast_opt(fd.ascribed_type()),
|
type_ref: TypeRef::from_ast_opt(fd.ascribed_type()),
|
||||||
|
visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -543,7 +543,10 @@ where
|
||||||
};
|
};
|
||||||
self.body.item_scope.define_def(def);
|
self.body.item_scope.define_def(def);
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
self.body.item_scope.push_res(name.as_name(), def.into());
|
let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
|
||||||
|
self.body
|
||||||
|
.item_scope
|
||||||
|
.push_res(name.as_name(), crate::per_ns::PerNs::from_def(def, vis));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,10 @@ use hir_expand::name::Name;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{per_ns::PerNs, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, TraitId};
|
use crate::{
|
||||||
|
per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId,
|
||||||
|
TraitId,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq)]
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
pub struct ItemScope {
|
pub struct ItemScope {
|
||||||
|
@ -30,7 +33,7 @@ pub struct ItemScope {
|
||||||
static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
|
static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
|
||||||
BuiltinType::ALL
|
BuiltinType::ALL
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into())))
|
.map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public)))
|
||||||
.collect()
|
.collect()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -144,8 +147,8 @@ impl ItemScope {
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_resolutions(&self) -> Vec<(Name, PerNs)> {
|
pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a {
|
||||||
self.visible.iter().map(|(name, res)| (name.clone(), res.clone())).collect()
|
self.visible.iter().map(|(name, res)| (name.clone(), res.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {
|
pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {
|
||||||
|
@ -153,20 +156,20 @@ impl ItemScope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ModuleDefId> for PerNs {
|
impl PerNs {
|
||||||
fn from(def: ModuleDefId) -> PerNs {
|
pub(crate) fn from_def(def: ModuleDefId, v: Visibility) -> PerNs {
|
||||||
match def {
|
match def {
|
||||||
ModuleDefId::ModuleId(_) => PerNs::types(def),
|
ModuleDefId::ModuleId(_) => PerNs::types(def, v),
|
||||||
ModuleDefId::FunctionId(_) => PerNs::values(def),
|
ModuleDefId::FunctionId(_) => PerNs::values(def, v),
|
||||||
ModuleDefId::AdtId(adt) => match adt {
|
ModuleDefId::AdtId(adt) => match adt {
|
||||||
AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def),
|
AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def, v),
|
||||||
AdtId::EnumId(_) => PerNs::types(def),
|
AdtId::EnumId(_) => PerNs::types(def, v),
|
||||||
},
|
},
|
||||||
ModuleDefId::EnumVariantId(_) => PerNs::both(def, def),
|
ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v),
|
||||||
ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def),
|
ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v),
|
||||||
ModuleDefId::TraitId(_) => PerNs::types(def),
|
ModuleDefId::TraitId(_) => PerNs::types(def, v),
|
||||||
ModuleDefId::TypeAliasId(_) => PerNs::types(def),
|
ModuleDefId::TypeAliasId(_) => PerNs::types(def, v),
|
||||||
ModuleDefId::BuiltinType(_) => PerNs::types(def),
|
ModuleDefId::BuiltinType(_) => PerNs::types(def, v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@ pub mod nameres;
|
||||||
pub mod src;
|
pub mod src;
|
||||||
pub mod child_by_source;
|
pub mod child_by_source;
|
||||||
|
|
||||||
|
pub mod visibility;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_db;
|
mod test_db;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -24,6 +24,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
path::{ModPath, PathKind},
|
path::{ModPath, PathKind},
|
||||||
per_ns::PerNs,
|
per_ns::PerNs,
|
||||||
|
visibility::Visibility,
|
||||||
AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern,
|
AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern,
|
||||||
LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
|
LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
|
||||||
};
|
};
|
||||||
|
@ -108,7 +109,7 @@ struct MacroDirective {
|
||||||
struct DefCollector<'a, DB> {
|
struct DefCollector<'a, DB> {
|
||||||
db: &'a DB,
|
db: &'a DB,
|
||||||
def_map: CrateDefMap,
|
def_map: CrateDefMap,
|
||||||
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, raw::Import)>>,
|
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
|
||||||
unresolved_imports: Vec<ImportDirective>,
|
unresolved_imports: Vec<ImportDirective>,
|
||||||
resolved_imports: Vec<ImportDirective>,
|
resolved_imports: Vec<ImportDirective>,
|
||||||
unexpanded_macros: Vec<MacroDirective>,
|
unexpanded_macros: Vec<MacroDirective>,
|
||||||
|
@ -214,7 +215,11 @@ where
|
||||||
// In Rust, `#[macro_export]` macros are unconditionally visible at the
|
// In Rust, `#[macro_export]` macros are unconditionally visible at the
|
||||||
// crate root, even if the parent modules is **not** visible.
|
// crate root, even if the parent modules is **not** visible.
|
||||||
if export {
|
if export {
|
||||||
self.update(self.def_map.root, &[(name, PerNs::macros(macro_))]);
|
self.update(
|
||||||
|
self.def_map.root,
|
||||||
|
&[(name, PerNs::macros(macro_, Visibility::Public))],
|
||||||
|
Visibility::Public,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,9 +353,12 @@ where
|
||||||
|
|
||||||
fn record_resolved_import(&mut self, directive: &ImportDirective) {
|
fn record_resolved_import(&mut self, directive: &ImportDirective) {
|
||||||
let module_id = directive.module_id;
|
let module_id = directive.module_id;
|
||||||
let import_id = directive.import_id;
|
|
||||||
let import = &directive.import;
|
let import = &directive.import;
|
||||||
let def = directive.status.namespaces();
|
let def = directive.status.namespaces();
|
||||||
|
let vis = self
|
||||||
|
.def_map
|
||||||
|
.resolve_visibility(self.db, module_id, &directive.import.visibility)
|
||||||
|
.unwrap_or(Visibility::Public);
|
||||||
|
|
||||||
if import.is_glob {
|
if import.is_glob {
|
||||||
log::debug!("glob import: {:?}", import);
|
log::debug!("glob import: {:?}", import);
|
||||||
|
@ -366,9 +374,16 @@ where
|
||||||
let scope = &item_map[m.local_id].scope;
|
let scope = &item_map[m.local_id].scope;
|
||||||
|
|
||||||
// Module scoped macros is included
|
// Module scoped macros is included
|
||||||
let items = scope.collect_resolutions();
|
let items = scope
|
||||||
|
.resolutions()
|
||||||
|
// only keep visible names...
|
||||||
|
.map(|(n, res)| {
|
||||||
|
(n, res.filter_visibility(|v| v.is_visible_from_other_crate()))
|
||||||
|
})
|
||||||
|
.filter(|(_, res)| !res.is_none())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
self.update(module_id, &items);
|
self.update(module_id, &items, vis);
|
||||||
} else {
|
} else {
|
||||||
// glob import from same crate => we do an initial
|
// glob import from same crate => we do an initial
|
||||||
// import, and then need to propagate any further
|
// import, and then need to propagate any further
|
||||||
|
@ -376,13 +391,25 @@ where
|
||||||
let scope = &self.def_map[m.local_id].scope;
|
let scope = &self.def_map[m.local_id].scope;
|
||||||
|
|
||||||
// Module scoped macros is included
|
// Module scoped macros is included
|
||||||
let items = scope.collect_resolutions();
|
let items = scope
|
||||||
|
.resolutions()
|
||||||
|
// only keep visible names...
|
||||||
|
.map(|(n, res)| {
|
||||||
|
(
|
||||||
|
n,
|
||||||
|
res.filter_visibility(|v| {
|
||||||
|
v.is_visible_from_def_map(&self.def_map, module_id)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.filter(|(_, res)| !res.is_none())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
self.update(module_id, &items);
|
self.update(module_id, &items, vis);
|
||||||
// record the glob import in case we add further items
|
// record the glob import in case we add further items
|
||||||
let glob = self.glob_imports.entry(m.local_id).or_default();
|
let glob = self.glob_imports.entry(m.local_id).or_default();
|
||||||
if !glob.iter().any(|it| *it == (module_id, import_id)) {
|
if !glob.iter().any(|(mid, _)| *mid == module_id) {
|
||||||
glob.push((module_id, import_id));
|
glob.push((module_id, vis));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -396,11 +423,11 @@ where
|
||||||
.map(|(local_id, variant_data)| {
|
.map(|(local_id, variant_data)| {
|
||||||
let name = variant_data.name.clone();
|
let name = variant_data.name.clone();
|
||||||
let variant = EnumVariantId { parent: e, local_id };
|
let variant = EnumVariantId { parent: e, local_id };
|
||||||
let res = PerNs::both(variant.into(), variant.into());
|
let res = PerNs::both(variant.into(), variant.into(), vis);
|
||||||
(name, res)
|
(name, res)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
self.update(module_id, &resolutions);
|
self.update(module_id, &resolutions, vis);
|
||||||
}
|
}
|
||||||
Some(d) => {
|
Some(d) => {
|
||||||
log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
|
log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
|
||||||
|
@ -422,21 +449,24 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update(module_id, &[(name, def)]);
|
self.update(module_id, &[(name, def)], vis);
|
||||||
}
|
}
|
||||||
None => tested_by!(bogus_paths),
|
None => tested_by!(bogus_paths),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)]) {
|
fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)], vis: Visibility) {
|
||||||
self.update_recursive(module_id, resolutions, 0)
|
self.update_recursive(module_id, resolutions, vis, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_recursive(
|
fn update_recursive(
|
||||||
&mut self,
|
&mut self,
|
||||||
module_id: LocalModuleId,
|
module_id: LocalModuleId,
|
||||||
resolutions: &[(Name, PerNs)],
|
resolutions: &[(Name, PerNs)],
|
||||||
|
// All resolutions are imported with this visibility; the visibilies in
|
||||||
|
// the `PerNs` values are ignored and overwritten
|
||||||
|
vis: Visibility,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
) {
|
) {
|
||||||
if depth > 100 {
|
if depth > 100 {
|
||||||
|
@ -446,7 +476,7 @@ where
|
||||||
let scope = &mut self.def_map.modules[module_id].scope;
|
let scope = &mut self.def_map.modules[module_id].scope;
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
for (name, res) in resolutions {
|
for (name, res) in resolutions {
|
||||||
changed |= scope.push_res(name.clone(), *res);
|
changed |= scope.push_res(name.clone(), res.with_visibility(vis));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !changed {
|
if !changed {
|
||||||
|
@ -459,9 +489,13 @@ where
|
||||||
.flat_map(|v| v.iter())
|
.flat_map(|v| v.iter())
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
for (glob_importing_module, _glob_import) in glob_imports {
|
for (glob_importing_module, glob_import_vis) in glob_imports {
|
||||||
// We pass the glob import so that the tracked import in those modules is that glob import
|
// we know all resolutions have the same visibility (`vis`), so we
|
||||||
self.update_recursive(glob_importing_module, resolutions, depth + 1);
|
// just need to check that once
|
||||||
|
if !vis.is_visible_from_def_map(&self.def_map, glob_importing_module) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.update_recursive(glob_importing_module, resolutions, glob_import_vis, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,9 +667,13 @@ where
|
||||||
let is_macro_use = attrs.by_key("macro_use").exists();
|
let is_macro_use = attrs.by_key("macro_use").exists();
|
||||||
match module {
|
match module {
|
||||||
// inline module, just recurse
|
// inline module, just recurse
|
||||||
raw::ModuleData::Definition { name, items, ast_id } => {
|
raw::ModuleData::Definition { name, visibility, items, ast_id } => {
|
||||||
let module_id =
|
let module_id = self.push_child_module(
|
||||||
self.push_child_module(name.clone(), AstId::new(self.file_id, *ast_id), None);
|
name.clone(),
|
||||||
|
AstId::new(self.file_id, *ast_id),
|
||||||
|
None,
|
||||||
|
&visibility,
|
||||||
|
);
|
||||||
|
|
||||||
ModCollector {
|
ModCollector {
|
||||||
def_collector: &mut *self.def_collector,
|
def_collector: &mut *self.def_collector,
|
||||||
|
@ -650,7 +688,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// out of line module, resolve, parse and recurse
|
// out of line module, resolve, parse and recurse
|
||||||
raw::ModuleData::Declaration { name, ast_id } => {
|
raw::ModuleData::Declaration { name, visibility, ast_id } => {
|
||||||
let ast_id = AstId::new(self.file_id, *ast_id);
|
let ast_id = AstId::new(self.file_id, *ast_id);
|
||||||
match self.mod_dir.resolve_declaration(
|
match self.mod_dir.resolve_declaration(
|
||||||
self.def_collector.db,
|
self.def_collector.db,
|
||||||
|
@ -659,7 +697,12 @@ where
|
||||||
path_attr,
|
path_attr,
|
||||||
) {
|
) {
|
||||||
Ok((file_id, mod_dir)) => {
|
Ok((file_id, mod_dir)) => {
|
||||||
let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id));
|
let module_id = self.push_child_module(
|
||||||
|
name.clone(),
|
||||||
|
ast_id,
|
||||||
|
Some(file_id),
|
||||||
|
&visibility,
|
||||||
|
);
|
||||||
let raw_items = self.def_collector.db.raw_items(file_id.into());
|
let raw_items = self.def_collector.db.raw_items(file_id.into());
|
||||||
ModCollector {
|
ModCollector {
|
||||||
def_collector: &mut *self.def_collector,
|
def_collector: &mut *self.def_collector,
|
||||||
|
@ -690,7 +733,13 @@ where
|
||||||
name: Name,
|
name: Name,
|
||||||
declaration: AstId<ast::Module>,
|
declaration: AstId<ast::Module>,
|
||||||
definition: Option<FileId>,
|
definition: Option<FileId>,
|
||||||
|
visibility: &crate::visibility::RawVisibility,
|
||||||
) -> LocalModuleId {
|
) -> LocalModuleId {
|
||||||
|
let vis = self
|
||||||
|
.def_collector
|
||||||
|
.def_map
|
||||||
|
.resolve_visibility(self.def_collector.db, self.module_id, visibility)
|
||||||
|
.unwrap_or(Visibility::Public);
|
||||||
let modules = &mut self.def_collector.def_map.modules;
|
let modules = &mut self.def_collector.def_map.modules;
|
||||||
let res = modules.alloc(ModuleData::default());
|
let res = modules.alloc(ModuleData::default());
|
||||||
modules[res].parent = Some(self.module_id);
|
modules[res].parent = Some(self.module_id);
|
||||||
|
@ -702,7 +751,7 @@ where
|
||||||
let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res };
|
let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res };
|
||||||
let def: ModuleDefId = module.into();
|
let def: ModuleDefId = module.into();
|
||||||
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
|
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
|
||||||
self.def_collector.update(self.module_id, &[(name, def.into())]);
|
self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,6 +765,7 @@ where
|
||||||
|
|
||||||
let name = def.name.clone();
|
let name = def.name.clone();
|
||||||
let container = ContainerId::ModuleId(module);
|
let container = ContainerId::ModuleId(module);
|
||||||
|
let vis = &def.visibility;
|
||||||
let def: ModuleDefId = match def.kind {
|
let def: ModuleDefId = match def.kind {
|
||||||
raw::DefKind::Function(ast_id) => FunctionLoc {
|
raw::DefKind::Function(ast_id) => FunctionLoc {
|
||||||
container: container.into(),
|
container: container.into(),
|
||||||
|
@ -761,7 +811,12 @@ where
|
||||||
.into(),
|
.into(),
|
||||||
};
|
};
|
||||||
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
|
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
|
||||||
self.def_collector.update(self.module_id, &[(name, def.into())])
|
let vis = self
|
||||||
|
.def_collector
|
||||||
|
.def_map
|
||||||
|
.resolve_visibility(self.def_collector.db, self.module_id, vis)
|
||||||
|
.unwrap_or(Visibility::Public);
|
||||||
|
self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
|
fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ use crate::{
|
||||||
nameres::{BuiltinShadowMode, CrateDefMap},
|
nameres::{BuiltinShadowMode, CrateDefMap},
|
||||||
path::{ModPath, PathKind},
|
path::{ModPath, PathKind},
|
||||||
per_ns::PerNs,
|
per_ns::PerNs,
|
||||||
|
visibility::{RawVisibility, Visibility},
|
||||||
AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
|
AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,7 +62,35 @@ impl ResolvePathResult {
|
||||||
|
|
||||||
impl CrateDefMap {
|
impl CrateDefMap {
|
||||||
pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
|
pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
|
||||||
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
|
self.extern_prelude
|
||||||
|
.get(name)
|
||||||
|
.map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_visibility(
|
||||||
|
&self,
|
||||||
|
db: &impl DefDatabase,
|
||||||
|
original_module: LocalModuleId,
|
||||||
|
visibility: &RawVisibility,
|
||||||
|
) -> Option<Visibility> {
|
||||||
|
match visibility {
|
||||||
|
RawVisibility::Module(path) => {
|
||||||
|
let (result, remaining) =
|
||||||
|
self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module);
|
||||||
|
if remaining.is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let types = result.take_types()?;
|
||||||
|
match types {
|
||||||
|
ModuleDefId::ModuleId(m) => Some(Visibility::Module(m)),
|
||||||
|
_ => {
|
||||||
|
// error: visibility needs to refer to module
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RawVisibility::Public => Some(Visibility::Public),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
|
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
|
||||||
|
@ -88,17 +117,21 @@ impl CrateDefMap {
|
||||||
PathKind::DollarCrate(krate) => {
|
PathKind::DollarCrate(krate) => {
|
||||||
if krate == self.krate {
|
if krate == self.krate {
|
||||||
tested_by!(macro_dollar_crate_self);
|
tested_by!(macro_dollar_crate_self);
|
||||||
PerNs::types(ModuleId { krate: self.krate, local_id: self.root }.into())
|
PerNs::types(
|
||||||
|
ModuleId { krate: self.krate, local_id: self.root }.into(),
|
||||||
|
Visibility::Public,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let def_map = db.crate_def_map(krate);
|
let def_map = db.crate_def_map(krate);
|
||||||
let module = ModuleId { krate, local_id: def_map.root };
|
let module = ModuleId { krate, local_id: def_map.root };
|
||||||
tested_by!(macro_dollar_crate_other);
|
tested_by!(macro_dollar_crate_other);
|
||||||
PerNs::types(module.into())
|
PerNs::types(module.into(), Visibility::Public)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PathKind::Crate => {
|
PathKind::Crate => PerNs::types(
|
||||||
PerNs::types(ModuleId { krate: self.krate, local_id: self.root }.into())
|
ModuleId { krate: self.krate, local_id: self.root }.into(),
|
||||||
}
|
Visibility::Public,
|
||||||
|
),
|
||||||
// plain import or absolute path in 2015: crate-relative with
|
// plain import or absolute path in 2015: crate-relative with
|
||||||
// fallback to extern prelude (with the simplification in
|
// fallback to extern prelude (with the simplification in
|
||||||
// rust-lang/rust#57745)
|
// rust-lang/rust#57745)
|
||||||
|
@ -126,7 +159,10 @@ impl CrateDefMap {
|
||||||
let m = successors(Some(original_module), |m| self.modules[*m].parent)
|
let m = successors(Some(original_module), |m| self.modules[*m].parent)
|
||||||
.nth(lvl as usize);
|
.nth(lvl as usize);
|
||||||
if let Some(local_id) = m {
|
if let Some(local_id) = m {
|
||||||
PerNs::types(ModuleId { krate: self.krate, local_id }.into())
|
PerNs::types(
|
||||||
|
ModuleId { krate: self.krate, local_id }.into(),
|
||||||
|
Visibility::Public,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
log::debug!("super path in root module");
|
log::debug!("super path in root module");
|
||||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||||
|
@ -140,7 +176,7 @@ impl CrateDefMap {
|
||||||
};
|
};
|
||||||
if let Some(def) = self.extern_prelude.get(&segment) {
|
if let Some(def) = self.extern_prelude.get(&segment) {
|
||||||
log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
|
log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
|
||||||
PerNs::types(*def)
|
PerNs::types(*def, Visibility::Public)
|
||||||
} else {
|
} else {
|
||||||
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
|
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
|
||||||
}
|
}
|
||||||
|
@ -148,7 +184,7 @@ impl CrateDefMap {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i, segment) in segments {
|
for (i, segment) in segments {
|
||||||
let curr = match curr_per_ns.take_types() {
|
let (curr, vis) = match curr_per_ns.take_types_vis() {
|
||||||
Some(r) => r,
|
Some(r) => r,
|
||||||
None => {
|
None => {
|
||||||
// we still have path segments left, but the path so far
|
// we still have path segments left, but the path so far
|
||||||
|
@ -189,11 +225,11 @@ impl CrateDefMap {
|
||||||
match enum_data.variant(&segment) {
|
match enum_data.variant(&segment) {
|
||||||
Some(local_id) => {
|
Some(local_id) => {
|
||||||
let variant = EnumVariantId { parent: e, local_id };
|
let variant = EnumVariantId { parent: e, local_id };
|
||||||
PerNs::both(variant.into(), variant.into())
|
PerNs::both(variant.into(), variant.into(), Visibility::Public)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return ResolvePathResult::with(
|
return ResolvePathResult::with(
|
||||||
PerNs::types(e.into()),
|
PerNs::types(e.into(), vis),
|
||||||
ReachedFixedPoint::Yes,
|
ReachedFixedPoint::Yes,
|
||||||
Some(i),
|
Some(i),
|
||||||
Some(self.krate),
|
Some(self.krate),
|
||||||
|
@ -211,7 +247,7 @@ impl CrateDefMap {
|
||||||
);
|
);
|
||||||
|
|
||||||
return ResolvePathResult::with(
|
return ResolvePathResult::with(
|
||||||
PerNs::types(s),
|
PerNs::types(s, vis),
|
||||||
ReachedFixedPoint::Yes,
|
ReachedFixedPoint::Yes,
|
||||||
Some(i),
|
Some(i),
|
||||||
Some(self.krate),
|
Some(self.krate),
|
||||||
|
@ -235,11 +271,15 @@ impl CrateDefMap {
|
||||||
// - current module / scope
|
// - current module / scope
|
||||||
// - extern prelude
|
// - extern prelude
|
||||||
// - std prelude
|
// - std prelude
|
||||||
let from_legacy_macro =
|
let from_legacy_macro = self[module]
|
||||||
self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
|
.scope
|
||||||
|
.get_legacy_macro(name)
|
||||||
|
.map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
|
||||||
let from_scope = self[module].scope.get(name, shadow);
|
let from_scope = self[module].scope.get(name, shadow);
|
||||||
let from_extern_prelude =
|
let from_extern_prelude = self
|
||||||
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
|
.extern_prelude
|
||||||
|
.get(name)
|
||||||
|
.map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public));
|
||||||
let from_prelude = self.resolve_in_prelude(db, name, shadow);
|
let from_prelude = self.resolve_in_prelude(db, name, shadow);
|
||||||
|
|
||||||
from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
|
from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
|
||||||
|
|
|
@ -16,12 +16,15 @@ use hir_expand::{
|
||||||
use ra_arena::{impl_arena_id, Arena, RawId};
|
use ra_arena::{impl_arena_id, Arena, RawId};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AttrsOwner, NameOwner},
|
ast::{self, AttrsOwner, NameOwner, VisibilityOwner},
|
||||||
AstNode,
|
AstNode,
|
||||||
};
|
};
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{attr::Attrs, db::DefDatabase, path::ModPath, FileAstId, HirFileId, InFile};
|
use crate::{
|
||||||
|
attr::Attrs, db::DefDatabase, path::ModPath, visibility::RawVisibility, FileAstId, HirFileId,
|
||||||
|
InFile,
|
||||||
|
};
|
||||||
|
|
||||||
/// `RawItems` is a set of top-level items in a file (except for impls).
|
/// `RawItems` is a set of top-level items in a file (except for impls).
|
||||||
///
|
///
|
||||||
|
@ -122,8 +125,17 @@ impl_arena_id!(Module);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub(super) enum ModuleData {
|
pub(super) enum ModuleData {
|
||||||
Declaration { name: Name, ast_id: FileAstId<ast::Module> },
|
Declaration {
|
||||||
Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> },
|
name: Name,
|
||||||
|
visibility: RawVisibility,
|
||||||
|
ast_id: FileAstId<ast::Module>,
|
||||||
|
},
|
||||||
|
Definition {
|
||||||
|
name: Name,
|
||||||
|
visibility: RawVisibility,
|
||||||
|
ast_id: FileAstId<ast::Module>,
|
||||||
|
items: Vec<RawItem>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -138,6 +150,7 @@ pub struct ImportData {
|
||||||
pub(super) is_prelude: bool,
|
pub(super) is_prelude: bool,
|
||||||
pub(super) is_extern_crate: bool,
|
pub(super) is_extern_crate: bool,
|
||||||
pub(super) is_macro_use: bool,
|
pub(super) is_macro_use: bool,
|
||||||
|
pub(super) visibility: RawVisibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -148,6 +161,7 @@ impl_arena_id!(Def);
|
||||||
pub(super) struct DefData {
|
pub(super) struct DefData {
|
||||||
pub(super) name: Name,
|
pub(super) name: Name,
|
||||||
pub(super) kind: DefKind,
|
pub(super) kind: DefKind,
|
||||||
|
pub(super) visibility: RawVisibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
@ -218,6 +232,7 @@ impl RawItemsCollector {
|
||||||
|
|
||||||
fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) {
|
fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) {
|
||||||
let attrs = self.parse_attrs(&item);
|
let attrs = self.parse_attrs(&item);
|
||||||
|
let visibility = RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
|
||||||
let (kind, name) = match item {
|
let (kind, name) = match item {
|
||||||
ast::ModuleItem::Module(module) => {
|
ast::ModuleItem::Module(module) => {
|
||||||
self.add_module(current_module, module);
|
self.add_module(current_module, module);
|
||||||
|
@ -266,7 +281,7 @@ impl RawItemsCollector {
|
||||||
};
|
};
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
let name = name.as_name();
|
let name = name.as_name();
|
||||||
let def = self.raw_items.defs.alloc(DefData { name, kind });
|
let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
|
||||||
self.push_item(current_module, attrs, RawItemKind::Def(def));
|
self.push_item(current_module, attrs, RawItemKind::Def(def));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,10 +292,12 @@ impl RawItemsCollector {
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
let attrs = self.parse_attrs(&module);
|
let attrs = self.parse_attrs(&module);
|
||||||
|
let visibility = RawVisibility::from_ast_with_hygiene(module.visibility(), &self.hygiene);
|
||||||
|
|
||||||
let ast_id = self.source_ast_id_map.ast_id(&module);
|
let ast_id = self.source_ast_id_map.ast_id(&module);
|
||||||
if module.has_semi() {
|
if module.has_semi() {
|
||||||
let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id });
|
let item =
|
||||||
|
self.raw_items.modules.alloc(ModuleData::Declaration { name, visibility, ast_id });
|
||||||
self.push_item(current_module, attrs, RawItemKind::Module(item));
|
self.push_item(current_module, attrs, RawItemKind::Module(item));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -288,6 +305,7 @@ impl RawItemsCollector {
|
||||||
if let Some(item_list) = module.item_list() {
|
if let Some(item_list) = module.item_list() {
|
||||||
let item = self.raw_items.modules.alloc(ModuleData::Definition {
|
let item = self.raw_items.modules.alloc(ModuleData::Definition {
|
||||||
name,
|
name,
|
||||||
|
visibility,
|
||||||
ast_id,
|
ast_id,
|
||||||
items: Vec::new(),
|
items: Vec::new(),
|
||||||
});
|
});
|
||||||
|
@ -302,6 +320,7 @@ impl RawItemsCollector {
|
||||||
// FIXME: cfg_attr
|
// FIXME: cfg_attr
|
||||||
let is_prelude = use_item.has_atom_attr("prelude_import");
|
let is_prelude = use_item.has_atom_attr("prelude_import");
|
||||||
let attrs = self.parse_attrs(&use_item);
|
let attrs = self.parse_attrs(&use_item);
|
||||||
|
let visibility = RawVisibility::from_ast_with_hygiene(use_item.visibility(), &self.hygiene);
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
ModPath::expand_use_item(
|
ModPath::expand_use_item(
|
||||||
|
@ -315,6 +334,7 @@ impl RawItemsCollector {
|
||||||
is_prelude,
|
is_prelude,
|
||||||
is_extern_crate: false,
|
is_extern_crate: false,
|
||||||
is_macro_use: false,
|
is_macro_use: false,
|
||||||
|
visibility: visibility.clone(),
|
||||||
};
|
};
|
||||||
buf.push(import_data);
|
buf.push(import_data);
|
||||||
},
|
},
|
||||||
|
@ -331,6 +351,8 @@ impl RawItemsCollector {
|
||||||
) {
|
) {
|
||||||
if let Some(name_ref) = extern_crate.name_ref() {
|
if let Some(name_ref) = extern_crate.name_ref() {
|
||||||
let path = ModPath::from_name_ref(&name_ref);
|
let path = ModPath::from_name_ref(&name_ref);
|
||||||
|
let visibility =
|
||||||
|
RawVisibility::from_ast_with_hygiene(extern_crate.visibility(), &self.hygiene);
|
||||||
let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
|
let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
|
||||||
let attrs = self.parse_attrs(&extern_crate);
|
let attrs = self.parse_attrs(&extern_crate);
|
||||||
// FIXME: cfg_attr
|
// FIXME: cfg_attr
|
||||||
|
@ -342,6 +364,7 @@ impl RawItemsCollector {
|
||||||
is_prelude: false,
|
is_prelude: false,
|
||||||
is_extern_crate: true,
|
is_extern_crate: true,
|
||||||
is_macro_use,
|
is_macro_use,
|
||||||
|
visibility,
|
||||||
};
|
};
|
||||||
self.push_import(current_module, attrs, import_data);
|
self.push_import(current_module, attrs, import_data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@ use test_utils::covers;
|
||||||
|
|
||||||
use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId};
|
use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId};
|
||||||
|
|
||||||
fn def_map(fixtute: &str) -> String {
|
fn def_map(fixture: &str) -> String {
|
||||||
let dm = compute_crate_def_map(fixtute);
|
let dm = compute_crate_def_map(fixture);
|
||||||
render_crate_def_map(&dm)
|
render_crate_def_map(&dm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ fn render_crate_def_map(map: &CrateDefMap) -> String {
|
||||||
*buf += path;
|
*buf += path;
|
||||||
*buf += "\n";
|
*buf += "\n";
|
||||||
|
|
||||||
let mut entries = map.modules[module].scope.collect_resolutions();
|
let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect();
|
||||||
entries.sort_by_key(|(name, _)| name.clone());
|
entries.sort_by_key(|(name, _)| name.clone());
|
||||||
|
|
||||||
for (name, def) in entries {
|
for (name, def) in entries {
|
||||||
|
|
|
@ -73,6 +73,83 @@ fn glob_2() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn glob_privacy_1() {
|
||||||
|
let map = def_map(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
mod foo;
|
||||||
|
use foo::*;
|
||||||
|
|
||||||
|
//- /foo/mod.rs
|
||||||
|
pub mod bar;
|
||||||
|
pub use self::bar::*;
|
||||||
|
struct PrivateStructFoo;
|
||||||
|
|
||||||
|
//- /foo/bar.rs
|
||||||
|
pub struct Baz;
|
||||||
|
struct PrivateStructBar;
|
||||||
|
pub use super::*;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
assert_snapshot!(map, @r###"
|
||||||
|
crate
|
||||||
|
Baz: t v
|
||||||
|
bar: t
|
||||||
|
foo: t
|
||||||
|
|
||||||
|
crate::foo
|
||||||
|
Baz: t v
|
||||||
|
PrivateStructFoo: t v
|
||||||
|
bar: t
|
||||||
|
|
||||||
|
crate::foo::bar
|
||||||
|
Baz: t v
|
||||||
|
PrivateStructBar: t v
|
||||||
|
PrivateStructFoo: t v
|
||||||
|
bar: t
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn glob_privacy_2() {
|
||||||
|
let map = def_map(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
mod foo;
|
||||||
|
use foo::*;
|
||||||
|
use foo::bar::*;
|
||||||
|
|
||||||
|
//- /foo/mod.rs
|
||||||
|
mod bar;
|
||||||
|
fn Foo() {};
|
||||||
|
pub struct Foo {};
|
||||||
|
|
||||||
|
//- /foo/bar.rs
|
||||||
|
pub(super) struct PrivateBaz;
|
||||||
|
struct PrivateBar;
|
||||||
|
pub(crate) struct PubCrateStruct;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
assert_snapshot!(map, @r###"
|
||||||
|
crate
|
||||||
|
Foo: t
|
||||||
|
PubCrateStruct: t v
|
||||||
|
foo: t
|
||||||
|
|
||||||
|
crate::foo
|
||||||
|
Foo: t v
|
||||||
|
bar: t
|
||||||
|
|
||||||
|
crate::foo::bar
|
||||||
|
PrivateBar: t v
|
||||||
|
PrivateBaz: t v
|
||||||
|
PubCrateStruct: t v
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn glob_across_crates() {
|
fn glob_across_crates() {
|
||||||
covers!(glob_across_crates);
|
covers!(glob_across_crates);
|
||||||
|
@ -92,6 +169,26 @@ fn glob_across_crates() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn glob_privacy_across_crates() {
|
||||||
|
covers!(glob_across_crates);
|
||||||
|
let map = def_map(
|
||||||
|
"
|
||||||
|
//- /main.rs crate:main deps:test_crate
|
||||||
|
use test_crate::*;
|
||||||
|
|
||||||
|
//- /lib.rs crate:test_crate
|
||||||
|
pub struct Baz;
|
||||||
|
struct Foo;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
assert_snapshot!(map, @r###"
|
||||||
|
⋮crate
|
||||||
|
⋮Baz: t v
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn glob_enum() {
|
fn glob_enum() {
|
||||||
covers!(glob_enum);
|
covers!(glob_enum);
|
||||||
|
|
|
@ -116,7 +116,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
||||||
let events = db.log_executed(|| {
|
let events = db.log_executed(|| {
|
||||||
let crate_def_map = db.crate_def_map(krate);
|
let crate_def_map = db.crate_def_map(krate);
|
||||||
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
||||||
assert_eq!(module_data.scope.collect_resolutions().len(), 1);
|
assert_eq!(module_data.scope.resolutions().collect::<Vec<_>>().len(), 1);
|
||||||
});
|
});
|
||||||
assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
||||||
let events = db.log_executed(|| {
|
let events = db.log_executed(|| {
|
||||||
let crate_def_map = db.crate_def_map(krate);
|
let crate_def_map = db.crate_def_map(krate);
|
||||||
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
||||||
assert_eq!(module_data.scope.collect_resolutions().len(), 1);
|
assert_eq!(module_data.scope.resolutions().collect::<Vec<_>>().len(), 1);
|
||||||
});
|
});
|
||||||
assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
|
|
||||||
use hir_expand::MacroDefId;
|
use hir_expand::MacroDefId;
|
||||||
|
|
||||||
use crate::ModuleDefId;
|
use crate::{visibility::Visibility, ModuleDefId};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct PerNs {
|
pub struct PerNs {
|
||||||
pub types: Option<ModuleDefId>,
|
pub types: Option<(ModuleDefId, Visibility)>,
|
||||||
pub values: Option<ModuleDefId>,
|
pub values: Option<(ModuleDefId, Visibility)>,
|
||||||
pub macros: Option<MacroDefId>,
|
pub macros: Option<(MacroDefId, Visibility)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PerNs {
|
impl Default for PerNs {
|
||||||
|
@ -25,20 +25,20 @@ impl PerNs {
|
||||||
PerNs { types: None, values: None, macros: None }
|
PerNs { types: None, values: None, macros: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn values(t: ModuleDefId) -> PerNs {
|
pub fn values(t: ModuleDefId, v: Visibility) -> PerNs {
|
||||||
PerNs { types: None, values: Some(t), macros: None }
|
PerNs { types: None, values: Some((t, v)), macros: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn types(t: ModuleDefId) -> PerNs {
|
pub fn types(t: ModuleDefId, v: Visibility) -> PerNs {
|
||||||
PerNs { types: Some(t), values: None, macros: None }
|
PerNs { types: Some((t, v)), values: None, macros: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn both(types: ModuleDefId, values: ModuleDefId) -> PerNs {
|
pub fn both(types: ModuleDefId, values: ModuleDefId, v: Visibility) -> PerNs {
|
||||||
PerNs { types: Some(types), values: Some(values), macros: None }
|
PerNs { types: Some((types, v)), values: Some((values, v)), macros: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn macros(macro_: MacroDefId) -> PerNs {
|
pub fn macros(macro_: MacroDefId, v: Visibility) -> PerNs {
|
||||||
PerNs { types: None, values: None, macros: Some(macro_) }
|
PerNs { types: None, values: None, macros: Some((macro_, v)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_none(&self) -> bool {
|
pub fn is_none(&self) -> bool {
|
||||||
|
@ -46,15 +46,35 @@ impl PerNs {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_types(self) -> Option<ModuleDefId> {
|
pub fn take_types(self) -> Option<ModuleDefId> {
|
||||||
|
self.types.map(|it| it.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_types_vis(self) -> Option<(ModuleDefId, Visibility)> {
|
||||||
self.types
|
self.types
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_values(self) -> Option<ModuleDefId> {
|
pub fn take_values(self) -> Option<ModuleDefId> {
|
||||||
self.values
|
self.values.map(|it| it.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_macros(self) -> Option<MacroDefId> {
|
pub fn take_macros(self) -> Option<MacroDefId> {
|
||||||
self.macros
|
self.macros.map(|it| it.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs {
|
||||||
|
PerNs {
|
||||||
|
types: self.types.filter(|(_, v)| f(*v)),
|
||||||
|
values: self.values.filter(|(_, v)| f(*v)),
|
||||||
|
macros: self.macros.filter(|(_, v)| f(*v)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_visibility(self, vis: Visibility) -> PerNs {
|
||||||
|
PerNs {
|
||||||
|
types: self.types.map(|(it, _)| (it, vis)),
|
||||||
|
values: self.values.map(|(it, _)| (it, vis)),
|
||||||
|
macros: self.macros.map(|(it, _)| (it, vis)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn or(self, other: PerNs) -> PerNs {
|
pub fn or(self, other: PerNs) -> PerNs {
|
||||||
|
|
|
@ -19,6 +19,7 @@ use crate::{
|
||||||
nameres::CrateDefMap,
|
nameres::CrateDefMap,
|
||||||
path::{ModPath, PathKind},
|
path::{ModPath, PathKind},
|
||||||
per_ns::PerNs,
|
per_ns::PerNs,
|
||||||
|
visibility::{RawVisibility, Visibility},
|
||||||
AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId,
|
AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId,
|
||||||
FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId,
|
FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId,
|
||||||
StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId,
|
StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId,
|
||||||
|
@ -231,6 +232,23 @@ impl Resolver {
|
||||||
Some(res)
|
Some(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_visibility(
|
||||||
|
&self,
|
||||||
|
db: &impl DefDatabase,
|
||||||
|
visibility: &RawVisibility,
|
||||||
|
) -> Option<Visibility> {
|
||||||
|
match visibility {
|
||||||
|
RawVisibility::Module(_) => {
|
||||||
|
let (item_map, module) = match self.module() {
|
||||||
|
Some(it) => it,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
item_map.resolve_visibility(db, module, visibility)
|
||||||
|
}
|
||||||
|
RawVisibility::Public => Some(Visibility::Public),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolve_path_in_value_ns(
|
pub fn resolve_path_in_value_ns(
|
||||||
&self,
|
&self,
|
||||||
db: &impl DefDatabase,
|
db: &impl DefDatabase,
|
||||||
|
@ -448,10 +466,10 @@ impl Scope {
|
||||||
f(name.clone(), ScopeDef::PerNs(def));
|
f(name.clone(), ScopeDef::PerNs(def));
|
||||||
});
|
});
|
||||||
m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
|
m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
|
||||||
f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_)));
|
f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_, Visibility::Public)));
|
||||||
});
|
});
|
||||||
m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| {
|
m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| {
|
||||||
f(name.clone(), ScopeDef::PerNs(PerNs::types(def.into())));
|
f(name.clone(), ScopeDef::PerNs(PerNs::types(def.into(), Visibility::Public)));
|
||||||
});
|
});
|
||||||
if let Some(prelude) = m.crate_def_map.prelude {
|
if let Some(prelude) = m.crate_def_map.prelude {
|
||||||
let prelude_def_map = db.crate_def_map(prelude.krate);
|
let prelude_def_map = db.crate_def_map(prelude.krate);
|
||||||
|
|
120
crates/ra_hir_def/src/visibility.rs
Normal file
120
crates/ra_hir_def/src/visibility.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
//! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
|
||||||
|
|
||||||
|
use hir_expand::{hygiene::Hygiene, InFile};
|
||||||
|
use ra_syntax::ast;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
db::DefDatabase,
|
||||||
|
path::{ModPath, PathKind},
|
||||||
|
ModuleId,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Visibility of an item, not yet resolved.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum RawVisibility {
|
||||||
|
/// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is
|
||||||
|
/// equivalent to `pub(self)`.
|
||||||
|
Module(ModPath),
|
||||||
|
/// `pub`.
|
||||||
|
Public,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawVisibility {
|
||||||
|
const fn private() -> RawVisibility {
|
||||||
|
let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() };
|
||||||
|
RawVisibility::Module(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_ast(
|
||||||
|
db: &impl DefDatabase,
|
||||||
|
node: InFile<Option<ast::Visibility>>,
|
||||||
|
) -> RawVisibility {
|
||||||
|
Self::from_ast_with_hygiene(node.value, &Hygiene::new(db, node.file_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_ast_with_hygiene(
|
||||||
|
node: Option<ast::Visibility>,
|
||||||
|
hygiene: &Hygiene,
|
||||||
|
) -> RawVisibility {
|
||||||
|
let node = match node {
|
||||||
|
None => return RawVisibility::private(),
|
||||||
|
Some(node) => node,
|
||||||
|
};
|
||||||
|
match node.kind() {
|
||||||
|
ast::VisibilityKind::In(path) => {
|
||||||
|
let path = ModPath::from_src(path, hygiene);
|
||||||
|
let path = match path {
|
||||||
|
None => return RawVisibility::private(),
|
||||||
|
Some(path) => path,
|
||||||
|
};
|
||||||
|
RawVisibility::Module(path)
|
||||||
|
}
|
||||||
|
ast::VisibilityKind::PubCrate => {
|
||||||
|
let path = ModPath { kind: PathKind::Crate, segments: Vec::new() };
|
||||||
|
RawVisibility::Module(path)
|
||||||
|
}
|
||||||
|
ast::VisibilityKind::PubSuper => {
|
||||||
|
let path = ModPath { kind: PathKind::Super(1), segments: Vec::new() };
|
||||||
|
RawVisibility::Module(path)
|
||||||
|
}
|
||||||
|
ast::VisibilityKind::Pub => RawVisibility::Public,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve(
|
||||||
|
&self,
|
||||||
|
db: &impl DefDatabase,
|
||||||
|
resolver: &crate::resolver::Resolver,
|
||||||
|
) -> Visibility {
|
||||||
|
// we fall back to public visibility (i.e. fail open) if the path can't be resolved
|
||||||
|
resolver.resolve_visibility(db, self).unwrap_or(Visibility::Public)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visibility of an item, with the path resolved.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Visibility {
|
||||||
|
/// Visibility is restricted to a certain module.
|
||||||
|
Module(ModuleId),
|
||||||
|
/// Visibility is unrestricted.
|
||||||
|
Public,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visibility {
|
||||||
|
pub fn is_visible_from(self, db: &impl DefDatabase, from_module: ModuleId) -> bool {
|
||||||
|
let to_module = match self {
|
||||||
|
Visibility::Module(m) => m,
|
||||||
|
Visibility::Public => return true,
|
||||||
|
};
|
||||||
|
// if they're not in the same crate, it can't be visible
|
||||||
|
if from_module.krate != to_module.krate {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let def_map = db.crate_def_map(from_module.krate);
|
||||||
|
self.is_visible_from_def_map(&def_map, from_module.local_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_visible_from_other_crate(self) -> bool {
|
||||||
|
match self {
|
||||||
|
Visibility::Module(_) => false,
|
||||||
|
Visibility::Public => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_visible_from_def_map(
|
||||||
|
self,
|
||||||
|
def_map: &crate::nameres::CrateDefMap,
|
||||||
|
from_module: crate::LocalModuleId,
|
||||||
|
) -> bool {
|
||||||
|
let to_module = match self {
|
||||||
|
Visibility::Module(m) => m,
|
||||||
|
Visibility::Public => return true,
|
||||||
|
};
|
||||||
|
// from_module needs to be a descendant of to_module
|
||||||
|
let mut ancestors = std::iter::successors(Some(from_module), |m| {
|
||||||
|
let parent_id = def_map[*m].parent?;
|
||||||
|
Some(parent_id)
|
||||||
|
});
|
||||||
|
ancestors.any(|m| m == to_module.local_id)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
use hir::Type;
|
use hir::{HasVisibility, Type};
|
||||||
|
|
||||||
use crate::completion::completion_item::CompletionKind;
|
use crate::completion::completion_item::CompletionKind;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -38,9 +38,15 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
|
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
|
||||||
for receiver in receiver.autoderef(ctx.db) {
|
for receiver in receiver.autoderef(ctx.db) {
|
||||||
for (field, ty) in receiver.fields(ctx.db) {
|
for (field, ty) in receiver.fields(ctx.db) {
|
||||||
|
if ctx.module.map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
|
||||||
|
// Skip private field. FIXME: If the definition location of the
|
||||||
|
// field is editable, we should show the completion
|
||||||
|
continue;
|
||||||
|
}
|
||||||
acc.add_field(ctx, field, &ty);
|
acc.add_field(ctx, field, &ty);
|
||||||
}
|
}
|
||||||
for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
|
for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
|
||||||
|
// FIXME: Handle visibility
|
||||||
acc.add_tuple_field(ctx, i, &ty);
|
acc.add_tuple_field(ctx, i, &ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,6 +192,55 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_struct_field_visibility_private() {
|
||||||
|
assert_debug_snapshot!(
|
||||||
|
do_ref_completion(
|
||||||
|
r"
|
||||||
|
mod inner {
|
||||||
|
struct A {
|
||||||
|
private_field: u32,
|
||||||
|
pub pub_field: u32,
|
||||||
|
pub(crate) crate_field: u32,
|
||||||
|
pub(super) super_field: u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn foo(a: inner::A) {
|
||||||
|
a.<|>
|
||||||
|
}
|
||||||
|
",
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
label: "crate_field",
|
||||||
|
source_range: [313; 313),
|
||||||
|
delete: [313; 313),
|
||||||
|
insert: "crate_field",
|
||||||
|
kind: Field,
|
||||||
|
detail: "u32",
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "pub_field",
|
||||||
|
source_range: [313; 313),
|
||||||
|
delete: [313; 313),
|
||||||
|
insert: "pub_field",
|
||||||
|
kind: Field,
|
||||||
|
detail: "u32",
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "super_field",
|
||||||
|
source_range: [313; 313),
|
||||||
|
delete: [313; 313),
|
||||||
|
insert: "super_field",
|
||||||
|
kind: Field,
|
||||||
|
detail: "u32",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_method_completion() {
|
fn test_method_completion() {
|
||||||
assert_debug_snapshot!(
|
assert_debug_snapshot!(
|
||||||
|
|
|
@ -17,7 +17,9 @@ use crate::{
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
|
expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
|
||||||
extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind},
|
extensions::{
|
||||||
|
FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind, VisibilityKind,
|
||||||
|
},
|
||||||
generated::*,
|
generated::*,
|
||||||
tokens::*,
|
tokens::*,
|
||||||
traits::*,
|
traits::*,
|
||||||
|
|
|
@ -413,3 +413,32 @@ impl ast::TraitDef {
|
||||||
self.syntax().children_with_tokens().any(|t| t.kind() == T![auto])
|
self.syntax().children_with_tokens().any(|t| t.kind() == T![auto])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum VisibilityKind {
|
||||||
|
In(ast::Path),
|
||||||
|
PubCrate,
|
||||||
|
PubSuper,
|
||||||
|
Pub,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ast::Visibility {
|
||||||
|
pub fn kind(&self) -> VisibilityKind {
|
||||||
|
if let Some(path) = children(self).next() {
|
||||||
|
VisibilityKind::In(path)
|
||||||
|
} else if self.is_pub_crate() {
|
||||||
|
VisibilityKind::PubCrate
|
||||||
|
} else if self.is_pub_super() {
|
||||||
|
VisibilityKind::PubSuper
|
||||||
|
} else {
|
||||||
|
VisibilityKind::Pub
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_pub_crate(&self) -> bool {
|
||||||
|
self.syntax().children_with_tokens().any(|it| it.kind() == T![crate])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_pub_super(&self) -> bool {
|
||||||
|
self.syntax().children_with_tokens().any(|it| it.kind() == T![super])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1064,6 +1064,7 @@ impl AstNode for ExternCrateItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ast::AttrsOwner for ExternCrateItem {}
|
impl ast::AttrsOwner for ExternCrateItem {}
|
||||||
|
impl ast::VisibilityOwner for ExternCrateItem {}
|
||||||
impl ExternCrateItem {
|
impl ExternCrateItem {
|
||||||
pub fn name_ref(&self) -> Option<NameRef> {
|
pub fn name_ref(&self) -> Option<NameRef> {
|
||||||
AstChildren::new(&self.syntax).next()
|
AstChildren::new(&self.syntax).next()
|
||||||
|
@ -2006,6 +2007,7 @@ impl AstNode for ModuleItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ast::AttrsOwner for ModuleItem {}
|
impl ast::AttrsOwner for ModuleItem {}
|
||||||
|
impl ast::VisibilityOwner for ModuleItem {}
|
||||||
impl ModuleItem {}
|
impl ModuleItem {}
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Name {
|
pub struct Name {
|
||||||
|
@ -3893,6 +3895,7 @@ impl AstNode for UseItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ast::AttrsOwner for UseItem {}
|
impl ast::AttrsOwner for UseItem {}
|
||||||
|
impl ast::VisibilityOwner for UseItem {}
|
||||||
impl UseItem {
|
impl UseItem {
|
||||||
pub fn use_tree(&self) -> Option<UseTree> {
|
pub fn use_tree(&self) -> Option<UseTree> {
|
||||||
AstChildren::new(&self.syntax).next()
|
AstChildren::new(&self.syntax).next()
|
||||||
|
|
|
@ -412,7 +412,7 @@ Grammar(
|
||||||
"ModuleItem": (
|
"ModuleItem": (
|
||||||
enum: ["StructDef", "UnionDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock",
|
enum: ["StructDef", "UnionDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock",
|
||||||
"UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ],
|
"UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ],
|
||||||
traits: ["AttrsOwner"],
|
traits: ["AttrsOwner", "VisibilityOwner"],
|
||||||
),
|
),
|
||||||
"ImplItem": (
|
"ImplItem": (
|
||||||
enum: ["FnDef", "TypeAliasDef", "ConstDef"],
|
enum: ["FnDef", "TypeAliasDef", "ConstDef"],
|
||||||
|
@ -683,7 +683,7 @@ Grammar(
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
"UseItem": (
|
"UseItem": (
|
||||||
traits: ["AttrsOwner"],
|
traits: ["AttrsOwner", "VisibilityOwner"],
|
||||||
options: [ "UseTree" ],
|
options: [ "UseTree" ],
|
||||||
),
|
),
|
||||||
"UseTree": (
|
"UseTree": (
|
||||||
|
@ -696,7 +696,7 @@ Grammar(
|
||||||
collections: [("use_trees", "UseTree")]
|
collections: [("use_trees", "UseTree")]
|
||||||
),
|
),
|
||||||
"ExternCrateItem": (
|
"ExternCrateItem": (
|
||||||
traits: ["AttrsOwner"],
|
traits: ["AttrsOwner", "VisibilityOwner"],
|
||||||
options: ["NameRef", "Alias"],
|
options: ["NameRef", "Alias"],
|
||||||
),
|
),
|
||||||
"ArgList": (
|
"ArgList": (
|
||||||
|
|
|
@ -43,7 +43,7 @@ fn no_todo() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let text = std::fs::read_to_string(e.path()).unwrap();
|
let text = std::fs::read_to_string(e.path()).unwrap();
|
||||||
if text.contains("TODO") || text.contains("TOOD") {
|
if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
|
||||||
panic!(
|
panic!(
|
||||||
"\nTODO markers should not be committed to the master branch,\n\
|
"\nTODO markers should not be committed to the master branch,\n\
|
||||||
use FIXME instead\n\
|
use FIXME instead\n\
|
||||||
|
|
Loading…
Reference in a new issue