diff options
Diffstat (limited to 'crates/libanalysis/src/descriptors.rs')
-rw-r--r-- | crates/libanalysis/src/descriptors.rs | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/crates/libanalysis/src/descriptors.rs b/crates/libanalysis/src/descriptors.rs new file mode 100644 index 000000000..e21ee728f --- /dev/null +++ b/crates/libanalysis/src/descriptors.rs | |||
@@ -0,0 +1,217 @@ | |||
1 | use std::{ | ||
2 | collections::BTreeMap, | ||
3 | }; | ||
4 | use relative_path::RelativePathBuf; | ||
5 | use libsyntax2::{ | ||
6 | SmolStr, | ||
7 | ast::{self, NameOwner}, | ||
8 | }; | ||
9 | use { | ||
10 | FileId, | ||
11 | imp::FileResolverImp, | ||
12 | }; | ||
13 | |||
14 | #[derive(Debug, Hash)] | ||
15 | pub struct ModuleDescriptor { | ||
16 | pub submodules: Vec<Submodule> | ||
17 | } | ||
18 | |||
19 | impl ModuleDescriptor { | ||
20 | pub fn new(root: ast::Root) -> ModuleDescriptor { | ||
21 | let submodules = modules(root) | ||
22 | .map(|(name, _)| Submodule { name }) | ||
23 | .collect(); | ||
24 | |||
25 | ModuleDescriptor { submodules } } | ||
26 | } | ||
27 | |||
28 | fn modules<'a>(root: ast::Root<'a>) -> impl Iterator<Item=(SmolStr, ast::Module<'a>)> { | ||
29 | root | ||
30 | .modules() | ||
31 | .filter_map(|module| { | ||
32 | let name = module.name()?.text(); | ||
33 | if !module.has_semi() { | ||
34 | return None; | ||
35 | } | ||
36 | Some((name, module)) | ||
37 | }) | ||
38 | } | ||
39 | |||
40 | #[derive(Clone, Hash, PartialEq, Eq, Debug)] | ||
41 | pub struct Submodule { | ||
42 | pub name: SmolStr, | ||
43 | } | ||
44 | |||
45 | #[derive(Hash)] | ||
46 | pub(crate) struct ModuleTreeDescriptor { | ||
47 | nodes: Vec<NodeData>, | ||
48 | links: Vec<LinkData>, | ||
49 | file_id2node: BTreeMap<FileId, Node>, | ||
50 | } | ||
51 | |||
52 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] | ||
53 | struct Node(usize); | ||
54 | #[derive(Hash)] | ||
55 | struct NodeData { | ||
56 | file_id: FileId, | ||
57 | links: Vec<Link>, | ||
58 | parents: Vec<Link> | ||
59 | } | ||
60 | |||
61 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] | ||
62 | pub(crate) struct Link(usize); | ||
63 | #[derive(Hash)] | ||
64 | struct LinkData { | ||
65 | owner: Node, | ||
66 | name: SmolStr, | ||
67 | points_to: Vec<Node>, | ||
68 | problem: Option<Problem>, | ||
69 | } | ||
70 | |||
71 | |||
72 | #[derive(Clone, Debug, Hash)] | ||
73 | pub enum Problem { | ||
74 | UnresolvedModule { | ||
75 | candidate: RelativePathBuf, | ||
76 | }, | ||
77 | NotDirOwner { | ||
78 | move_to: RelativePathBuf, | ||
79 | candidate: RelativePathBuf, | ||
80 | } | ||
81 | } | ||
82 | |||
83 | impl ModuleTreeDescriptor { | ||
84 | pub(crate) fn new<'a>( | ||
85 | files: impl Iterator<Item=(FileId, &'a ModuleDescriptor)> + Clone, | ||
86 | file_resolver: &FileResolverImp, | ||
87 | ) -> ModuleTreeDescriptor { | ||
88 | let mut file_id2node = BTreeMap::new(); | ||
89 | let mut nodes: Vec<NodeData> = files.clone().enumerate() | ||
90 | .map(|(idx, (file_id, _))| { | ||
91 | file_id2node.insert(file_id, Node(idx)); | ||
92 | NodeData { | ||
93 | file_id, | ||
94 | links: Vec::new(), | ||
95 | parents: Vec::new(), | ||
96 | } | ||
97 | }) | ||
98 | .collect(); | ||
99 | let mut links = Vec::new(); | ||
100 | |||
101 | for (idx, (file_id, descr)) in files.enumerate() { | ||
102 | let owner = Node(idx); | ||
103 | for sub in descr.submodules.iter() { | ||
104 | let link = Link(links.len()); | ||
105 | nodes[owner.0].links.push(link); | ||
106 | let (points_to, problem) = resolve_submodule(file_id, &sub.name, file_resolver); | ||
107 | let points_to = points_to | ||
108 | .into_iter() | ||
109 | .map(|file_id| { | ||
110 | let node = file_id2node[&file_id]; | ||
111 | nodes[node.0].parents.push(link); | ||
112 | node | ||
113 | }) | ||
114 | .collect(); | ||
115 | |||
116 | links.push(LinkData { | ||
117 | owner, | ||
118 | name: sub.name.clone(), | ||
119 | points_to, | ||
120 | problem, | ||
121 | }) | ||
122 | |||
123 | } | ||
124 | } | ||
125 | |||
126 | ModuleTreeDescriptor { | ||
127 | nodes, links, file_id2node | ||
128 | } | ||
129 | } | ||
130 | |||
131 | pub(crate) fn parent_modules(&self, file_id: FileId) -> Vec<Link> { | ||
132 | let node = self.file_id2node[&file_id]; | ||
133 | self.node(node) | ||
134 | .parents | ||
135 | .clone() | ||
136 | } | ||
137 | pub(crate) fn child_module_by_name(&self, file_id: FileId, name: &str) -> Vec<FileId> { | ||
138 | let node = self.file_id2node[&file_id]; | ||
139 | self.node(node) | ||
140 | .links | ||
141 | .iter() | ||
142 | .filter(|it| it.name(self) == name) | ||
143 | .map(|link| link.owner(self)) | ||
144 | .collect() | ||
145 | } | ||
146 | pub(crate) fn problems<'a, 'b>(&'b self, file_id: FileId, root: ast::Root<'a>) -> Vec<(ast::Name<'a>, &'b Problem)> { | ||
147 | let node = self.file_id2node[&file_id]; | ||
148 | self.node(node) | ||
149 | .links | ||
150 | .iter() | ||
151 | .filter_map(|&link| { | ||
152 | let problem = self.link(link).problem.as_ref()?; | ||
153 | let name = link.bind_source(self, root).name()?; | ||
154 | Some((name, problem)) | ||
155 | }) | ||
156 | .collect() | ||
157 | } | ||
158 | |||
159 | fn node(&self, node: Node) -> &NodeData { | ||
160 | &self.nodes[node.0] | ||
161 | } | ||
162 | fn link(&self, link: Link) -> &LinkData { | ||
163 | &self.links[link.0] | ||
164 | } | ||
165 | } | ||
166 | |||
167 | impl Link { | ||
168 | pub(crate) fn name(self, tree: &ModuleTreeDescriptor) -> SmolStr { | ||
169 | tree.link(self).name.clone() | ||
170 | } | ||
171 | pub(crate) fn owner(self, tree: &ModuleTreeDescriptor) -> FileId { | ||
172 | let owner = tree.link(self).owner; | ||
173 | tree.node(owner).file_id | ||
174 | } | ||
175 | pub(crate) fn bind_source<'a>(self, tree: &ModuleTreeDescriptor, root: ast::Root<'a>) -> ast::Module<'a> { | ||
176 | modules(root) | ||
177 | .filter(|(name, _)| name == &tree.link(self).name) | ||
178 | .next() | ||
179 | .unwrap() | ||
180 | .1 | ||
181 | } | ||
182 | } | ||
183 | |||
184 | |||
185 | fn resolve_submodule( | ||
186 | file_id: FileId, | ||
187 | name: &SmolStr, | ||
188 | file_resolver: &FileResolverImp | ||
189 | ) -> (Vec<FileId>, Option<Problem>) { | ||
190 | let mod_name = file_resolver.file_stem(file_id); | ||
191 | let is_dir_owner = | ||
192 | mod_name == "mod" || mod_name == "lib" || mod_name == "main"; | ||
193 | |||
194 | let file_mod = RelativePathBuf::from(format!("../{}.rs", name)); | ||
195 | let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", name)); | ||
196 | let points_to: Vec<FileId>; | ||
197 | let problem: Option<Problem>; | ||
198 | if is_dir_owner { | ||
199 | points_to = [&file_mod, &dir_mod].iter() | ||
200 | .filter_map(|path| file_resolver.resolve(file_id, path)) | ||
201 | .collect(); | ||
202 | problem = if points_to.is_empty() { | ||
203 | Some(Problem::UnresolvedModule { | ||
204 | candidate: file_mod, | ||
205 | }) | ||
206 | } else { | ||
207 | None | ||
208 | } | ||
209 | } else { | ||
210 | points_to = Vec::new(); | ||
211 | problem = Some(Problem::NotDirOwner { | ||
212 | move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)), | ||
213 | candidate: file_mod, | ||
214 | }); | ||
215 | } | ||
216 | (points_to, problem) | ||
217 | } | ||