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