]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/visibility.rs
Rename `CrateDefMap` to `DefMap`
[rust.git] / crates / hir_def / src / visibility.rs
1 //! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
2
3 use hir_expand::{hygiene::Hygiene, InFile};
4 use syntax::ast;
5
6 use crate::{
7     db::DefDatabase,
8     nameres::DefMap,
9     path::{ModPath, PathKind},
10     ModuleId,
11 };
12
13 /// Visibility of an item, not yet resolved.
14 #[derive(Debug, Clone, PartialEq, Eq)]
15 pub enum RawVisibility {
16     /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is
17     /// equivalent to `pub(self)`.
18     Module(ModPath),
19     /// `pub`.
20     Public,
21 }
22
23 impl RawVisibility {
24     pub(crate) const fn private() -> RawVisibility {
25         let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() };
26         RawVisibility::Module(path)
27     }
28
29     pub(crate) fn from_ast(
30         db: &dyn DefDatabase,
31         node: InFile<Option<ast::Visibility>>,
32     ) -> RawVisibility {
33         Self::from_ast_with_hygiene(node.value, &Hygiene::new(db.upcast(), node.file_id))
34     }
35
36     pub(crate) fn from_ast_with_hygiene(
37         node: Option<ast::Visibility>,
38         hygiene: &Hygiene,
39     ) -> RawVisibility {
40         Self::from_ast_with_hygiene_and_default(node, RawVisibility::private(), hygiene)
41     }
42
43     pub(crate) fn from_ast_with_hygiene_and_default(
44         node: Option<ast::Visibility>,
45         default: RawVisibility,
46         hygiene: &Hygiene,
47     ) -> RawVisibility {
48         let node = match node {
49             None => return default,
50             Some(node) => node,
51         };
52         match node.kind() {
53             ast::VisibilityKind::In(path) => {
54                 let path = ModPath::from_src(path, hygiene);
55                 let path = match path {
56                     None => return RawVisibility::private(),
57                     Some(path) => path,
58                 };
59                 RawVisibility::Module(path)
60             }
61             ast::VisibilityKind::PubCrate => {
62                 let path = ModPath { kind: PathKind::Crate, segments: Vec::new() };
63                 RawVisibility::Module(path)
64             }
65             ast::VisibilityKind::PubSuper => {
66                 let path = ModPath { kind: PathKind::Super(1), segments: Vec::new() };
67                 RawVisibility::Module(path)
68             }
69             ast::VisibilityKind::PubSelf => {
70                 let path = ModPath { kind: PathKind::Plain, segments: Vec::new() };
71                 RawVisibility::Module(path)
72             }
73             ast::VisibilityKind::Pub => RawVisibility::Public,
74         }
75     }
76
77     pub fn resolve(
78         &self,
79         db: &dyn DefDatabase,
80         resolver: &crate::resolver::Resolver,
81     ) -> Visibility {
82         // we fall back to public visibility (i.e. fail open) if the path can't be resolved
83         resolver.resolve_visibility(db, self).unwrap_or(Visibility::Public)
84     }
85 }
86
87 /// Visibility of an item, with the path resolved.
88 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
89 pub enum Visibility {
90     /// Visibility is restricted to a certain module.
91     Module(ModuleId),
92     /// Visibility is unrestricted.
93     Public,
94 }
95
96 impl Visibility {
97     pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool {
98         let to_module = match self {
99             Visibility::Module(m) => m,
100             Visibility::Public => return true,
101         };
102         // if they're not in the same crate, it can't be visible
103         if from_module.krate != to_module.krate {
104             return false;
105         }
106         let def_map = db.crate_def_map(from_module.krate);
107         self.is_visible_from_def_map(&def_map, from_module.local_id)
108     }
109
110     pub(crate) fn is_visible_from_other_crate(self) -> bool {
111         match self {
112             Visibility::Module(_) => false,
113             Visibility::Public => true,
114         }
115     }
116
117     pub(crate) fn is_visible_from_def_map(
118         self,
119         def_map: &DefMap,
120         from_module: crate::LocalModuleId,
121     ) -> bool {
122         let to_module = match self {
123             Visibility::Module(m) => m,
124             Visibility::Public => return true,
125         };
126         // from_module needs to be a descendant of to_module
127         let mut ancestors = std::iter::successors(Some(from_module), |m| {
128             let parent_id = def_map[*m].parent?;
129             Some(parent_id)
130         });
131         ancestors.any(|m| m == to_module.local_id)
132     }
133
134     /// Returns the most permissive visibility of `self` and `other`.
135     ///
136     /// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only
137     /// visible in unrelated modules).
138     pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
139         match (self, other) {
140             (Visibility::Module(_), Visibility::Public)
141             | (Visibility::Public, Visibility::Module(_))
142             | (Visibility::Public, Visibility::Public) => Some(Visibility::Public),
143             (Visibility::Module(mod_a), Visibility::Module(mod_b)) => {
144                 if mod_a.krate != mod_b.krate {
145                     return None;
146                 }
147
148                 let mut a_ancestors = std::iter::successors(Some(mod_a.local_id), |m| {
149                     let parent_id = def_map[*m].parent?;
150                     Some(parent_id)
151                 });
152                 let mut b_ancestors = std::iter::successors(Some(mod_b.local_id), |m| {
153                     let parent_id = def_map[*m].parent?;
154                     Some(parent_id)
155                 });
156
157                 if a_ancestors.any(|m| m == mod_b.local_id) {
158                     // B is above A
159                     return Some(Visibility::Module(mod_b));
160                 }
161
162                 if b_ancestors.any(|m| m == mod_a.local_id) {
163                     // A is above B
164                     return Some(Visibility::Module(mod_a));
165                 }
166
167                 None
168             }
169         }
170     }
171 }