diff options
Diffstat (limited to 'crates/ra_db/src')
-rw-r--r-- | crates/ra_db/src/fixture.rs | 188 | ||||
-rw-r--r-- | crates/ra_db/src/input.rs | 161 | ||||
-rw-r--r-- | crates/ra_db/src/lib.rs | 73 |
3 files changed, 168 insertions, 254 deletions
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index 482a2f3e6..209713987 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs | |||
@@ -57,17 +57,16 @@ | |||
57 | //! fn insert_source_code_here() {} | 57 | //! fn insert_source_code_here() {} |
58 | //! " | 58 | //! " |
59 | //! ``` | 59 | //! ``` |
60 | 60 | use std::{str::FromStr, sync::Arc}; | |
61 | use std::str::FromStr; | ||
62 | use std::sync::Arc; | ||
63 | 61 | ||
64 | use ra_cfg::CfgOptions; | 62 | use ra_cfg::CfgOptions; |
65 | use rustc_hash::FxHashMap; | 63 | use rustc_hash::FxHashMap; |
66 | use test_utils::{extract_offset, parse_fixture, parse_single_fixture, FixtureMeta, CURSOR_MARKER}; | 64 | use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER}; |
65 | use vfs::{file_set::FileSet, VfsPath}; | ||
67 | 66 | ||
68 | use crate::{ | 67 | use crate::{ |
69 | input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf, | 68 | input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, SourceDatabaseExt, |
70 | SourceDatabaseExt, SourceRoot, SourceRootId, | 69 | SourceRoot, SourceRootId, |
71 | }; | 70 | }; |
72 | 71 | ||
73 | pub const WORKSPACE: SourceRootId = SourceRootId(0); | 72 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
@@ -75,21 +74,32 @@ pub const WORKSPACE: SourceRootId = SourceRootId(0); | |||
75 | pub trait WithFixture: Default + SourceDatabaseExt + 'static { | 74 | pub trait WithFixture: Default + SourceDatabaseExt + 'static { |
76 | fn with_single_file(text: &str) -> (Self, FileId) { | 75 | fn with_single_file(text: &str) -> (Self, FileId) { |
77 | let mut db = Self::default(); | 76 | let mut db = Self::default(); |
78 | let file_id = with_single_file(&mut db, text); | 77 | let (_, files) = with_files(&mut db, text); |
79 | (db, file_id) | 78 | assert_eq!(files.len(), 1); |
79 | (db, files[0]) | ||
80 | } | 80 | } |
81 | 81 | ||
82 | fn with_files(ra_fixture: &str) -> Self { | 82 | fn with_files(ra_fixture: &str) -> Self { |
83 | let mut db = Self::default(); | 83 | let mut db = Self::default(); |
84 | let pos = with_files(&mut db, ra_fixture); | 84 | let (pos, _) = with_files(&mut db, ra_fixture); |
85 | assert!(pos.is_none()); | 85 | assert!(pos.is_none()); |
86 | db | 86 | db |
87 | } | 87 | } |
88 | 88 | ||
89 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { | 89 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { |
90 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | ||
91 | let offset = match range_or_offset { | ||
92 | RangeOrOffset::Range(_) => panic!(), | ||
93 | RangeOrOffset::Offset(it) => it, | ||
94 | }; | ||
95 | (db, FilePosition { file_id, offset }) | ||
96 | } | ||
97 | |||
98 | fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) { | ||
90 | let mut db = Self::default(); | 99 | let mut db = Self::default(); |
91 | let pos = with_files(&mut db, ra_fixture); | 100 | let (pos, _) = with_files(&mut db, ra_fixture); |
92 | (db, pos.unwrap()) | 101 | let (file_id, range_or_offset) = pos.unwrap(); |
102 | (db, file_id, range_or_offset) | ||
93 | } | 103 | } |
94 | 104 | ||
95 | fn test_crate(&self) -> CrateId { | 105 | fn test_crate(&self) -> CrateId { |
@@ -103,119 +113,64 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
103 | 113 | ||
104 | impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {} | 114 | impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {} |
105 | 115 | ||
106 | fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId { | 116 | fn with_files( |
107 | let file_id = FileId(0); | 117 | db: &mut dyn SourceDatabaseExt, |
108 | let rel_path: RelativePathBuf = "/main.rs".into(); | 118 | fixture: &str, |
109 | 119 | ) -> (Option<(FileId, RangeOrOffset)>, Vec<FileId>) { | |
110 | let mut source_root = SourceRoot::new_local(); | 120 | let fixture = Fixture::parse(fixture); |
111 | source_root.insert_file(rel_path.clone(), file_id); | ||
112 | |||
113 | let fixture = parse_single_fixture(ra_fixture); | ||
114 | |||
115 | let crate_graph = if let Some(entry) = fixture { | ||
116 | let meta = match ParsedMeta::from(&entry.meta) { | ||
117 | ParsedMeta::File(it) => it, | ||
118 | _ => panic!("with_single_file only support file meta"), | ||
119 | }; | ||
120 | |||
121 | let mut crate_graph = CrateGraph::default(); | ||
122 | crate_graph.add_crate_root( | ||
123 | file_id, | ||
124 | meta.edition, | ||
125 | meta.krate.map(|name| { | ||
126 | CrateName::new(&name).expect("Fixture crate name should not contain dashes") | ||
127 | }), | ||
128 | meta.cfg, | ||
129 | meta.env, | ||
130 | Default::default(), | ||
131 | Default::default(), | ||
132 | ); | ||
133 | crate_graph | ||
134 | } else { | ||
135 | let mut crate_graph = CrateGraph::default(); | ||
136 | crate_graph.add_crate_root( | ||
137 | file_id, | ||
138 | Edition::Edition2018, | ||
139 | None, | ||
140 | CfgOptions::default(), | ||
141 | Env::default(), | ||
142 | Default::default(), | ||
143 | Default::default(), | ||
144 | ); | ||
145 | crate_graph | ||
146 | }; | ||
147 | |||
148 | db.set_file_text(file_id, Arc::new(ra_fixture.to_string())); | ||
149 | db.set_file_relative_path(file_id, rel_path); | ||
150 | db.set_file_source_root(file_id, WORKSPACE); | ||
151 | db.set_source_root(WORKSPACE, Arc::new(source_root)); | ||
152 | db.set_crate_graph(Arc::new(crate_graph)); | ||
153 | |||
154 | file_id | ||
155 | } | ||
156 | |||
157 | fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosition> { | ||
158 | let fixture = parse_fixture(fixture); | ||
159 | 121 | ||
122 | let mut files = Vec::new(); | ||
160 | let mut crate_graph = CrateGraph::default(); | 123 | let mut crate_graph = CrateGraph::default(); |
161 | let mut crates = FxHashMap::default(); | 124 | let mut crates = FxHashMap::default(); |
162 | let mut crate_deps = Vec::new(); | 125 | let mut crate_deps = Vec::new(); |
163 | let mut default_crate_root: Option<FileId> = None; | 126 | let mut default_crate_root: Option<FileId> = None; |
164 | 127 | ||
165 | let mut source_root = SourceRoot::new_local(); | 128 | let mut file_set = FileSet::default(); |
166 | let mut source_root_id = WORKSPACE; | 129 | let source_root_id = WORKSPACE; |
167 | let mut source_root_prefix: RelativePathBuf = "/".into(); | 130 | let source_root_prefix = "/".to_string(); |
168 | let mut file_id = FileId(0); | 131 | let mut file_id = FileId(0); |
169 | 132 | ||
170 | let mut file_position = None; | 133 | let mut file_position = None; |
171 | 134 | ||
172 | for entry in fixture.iter() { | 135 | for entry in fixture { |
173 | let meta = match ParsedMeta::from(&entry.meta) { | 136 | let text = if entry.text.contains(CURSOR_MARKER) { |
174 | ParsedMeta::Root { path } => { | 137 | let (range_or_offset, text) = extract_range_or_offset(&entry.text); |
175 | let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local()); | 138 | assert!(file_position.is_none()); |
176 | db.set_source_root(source_root_id, Arc::new(source_root)); | 139 | file_position = Some((file_id, range_or_offset)); |
177 | source_root_id.0 += 1; | 140 | text.to_string() |
178 | source_root_prefix = path; | 141 | } else { |
179 | continue; | 142 | entry.text.clone() |
180 | } | ||
181 | ParsedMeta::File(it) => it, | ||
182 | }; | 143 | }; |
144 | |||
145 | let meta = FileMeta::from(entry); | ||
183 | assert!(meta.path.starts_with(&source_root_prefix)); | 146 | assert!(meta.path.starts_with(&source_root_prefix)); |
184 | 147 | ||
185 | if let Some(krate) = meta.krate { | 148 | if let Some(krate) = meta.krate { |
186 | let crate_id = crate_graph.add_crate_root( | 149 | let crate_id = crate_graph.add_crate_root( |
187 | file_id, | 150 | file_id, |
188 | meta.edition, | 151 | meta.edition, |
189 | Some(CrateName::new(&krate).unwrap()), | 152 | Some(krate.clone()), |
190 | meta.cfg, | 153 | meta.cfg, |
191 | meta.env, | 154 | meta.env, |
192 | Default::default(), | 155 | Default::default(), |
193 | Default::default(), | ||
194 | ); | 156 | ); |
195 | let prev = crates.insert(krate.clone(), crate_id); | 157 | let crate_name = CrateName::new(&krate).unwrap(); |
158 | let prev = crates.insert(crate_name.clone(), crate_id); | ||
196 | assert!(prev.is_none()); | 159 | assert!(prev.is_none()); |
197 | for dep in meta.deps { | 160 | for dep in meta.deps { |
198 | crate_deps.push((krate.clone(), dep)) | 161 | let dep = CrateName::new(&dep).unwrap(); |
162 | crate_deps.push((crate_name.clone(), dep)) | ||
199 | } | 163 | } |
200 | } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { | 164 | } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { |
201 | assert!(default_crate_root.is_none()); | 165 | assert!(default_crate_root.is_none()); |
202 | default_crate_root = Some(file_id); | 166 | default_crate_root = Some(file_id); |
203 | } | 167 | } |
204 | 168 | ||
205 | let text = if entry.text.contains(CURSOR_MARKER) { | ||
206 | let (offset, text) = extract_offset(&entry.text); | ||
207 | assert!(file_position.is_none()); | ||
208 | file_position = Some(FilePosition { file_id, offset }); | ||
209 | text.to_string() | ||
210 | } else { | ||
211 | entry.text.to_string() | ||
212 | }; | ||
213 | |||
214 | db.set_file_text(file_id, Arc::new(text)); | 169 | db.set_file_text(file_id, Arc::new(text)); |
215 | db.set_file_relative_path(file_id, meta.path.clone()); | ||
216 | db.set_file_source_root(file_id, source_root_id); | 170 | db.set_file_source_root(file_id, source_root_id); |
217 | source_root.insert_file(meta.path, file_id); | 171 | let path = VfsPath::new_virtual_path(meta.path); |
218 | 172 | file_set.insert(file_id, path.into()); | |
173 | files.push(file_id); | ||
219 | file_id.0 += 1; | 174 | file_id.0 += 1; |
220 | } | 175 | } |
221 | 176 | ||
@@ -228,7 +183,6 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit | |||
228 | CfgOptions::default(), | 183 | CfgOptions::default(), |
229 | Env::default(), | 184 | Env::default(), |
230 | Default::default(), | 185 | Default::default(), |
231 | Default::default(), | ||
232 | ); | 186 | ); |
233 | } else { | 187 | } else { |
234 | for (from, to) in crate_deps { | 188 | for (from, to) in crate_deps { |
@@ -238,19 +192,14 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit | |||
238 | } | 192 | } |
239 | } | 193 | } |
240 | 194 | ||
241 | db.set_source_root(source_root_id, Arc::new(source_root)); | 195 | db.set_source_root(source_root_id, Arc::new(SourceRoot::new_local(file_set))); |
242 | db.set_crate_graph(Arc::new(crate_graph)); | 196 | db.set_crate_graph(Arc::new(crate_graph)); |
243 | 197 | ||
244 | file_position | 198 | (file_position, files) |
245 | } | ||
246 | |||
247 | enum ParsedMeta { | ||
248 | Root { path: RelativePathBuf }, | ||
249 | File(FileMeta), | ||
250 | } | 199 | } |
251 | 200 | ||
252 | struct FileMeta { | 201 | struct FileMeta { |
253 | path: RelativePathBuf, | 202 | path: String, |
254 | krate: Option<String>, | 203 | krate: Option<String>, |
255 | deps: Vec<String>, | 204 | deps: Vec<String>, |
256 | cfg: CfgOptions, | 205 | cfg: CfgOptions, |
@@ -258,25 +207,22 @@ struct FileMeta { | |||
258 | env: Env, | 207 | env: Env, |
259 | } | 208 | } |
260 | 209 | ||
261 | impl From<&FixtureMeta> for ParsedMeta { | 210 | impl From<Fixture> for FileMeta { |
262 | fn from(meta: &FixtureMeta) -> Self { | 211 | fn from(f: Fixture) -> FileMeta { |
263 | match meta { | 212 | let mut cfg = CfgOptions::default(); |
264 | FixtureMeta::Root { path } => { | 213 | f.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into())); |
265 | // `Self::Root` causes a false warning: 'variant is never constructed: `Root` ' | 214 | f.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into())); |
266 | // see https://github.com/rust-lang/rust/issues/69018 | 215 | |
267 | ParsedMeta::Root { path: path.to_owned() } | 216 | FileMeta { |
268 | } | 217 | path: f.path, |
269 | FixtureMeta::File(f) => Self::File(FileMeta { | 218 | krate: f.krate, |
270 | path: f.path.to_owned(), | 219 | deps: f.deps, |
271 | krate: f.crate_name.to_owned(), | 220 | cfg, |
272 | deps: f.deps.to_owned(), | 221 | edition: f |
273 | cfg: f.cfg.to_owned(), | 222 | .edition |
274 | edition: f | 223 | .as_ref() |
275 | .edition | 224 | .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), |
276 | .as_ref() | 225 | env: Env::from(f.env.iter()), |
277 | .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), | ||
278 | env: Env::from(f.env.iter()), | ||
279 | }), | ||
280 | } | 226 | } |
281 | } | 227 | } |
282 | } | 228 | } |
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 4d2d3b48a..aaa492759 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -6,29 +6,15 @@ | |||
6 | //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how | 6 | //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how |
7 | //! actual IO is done and lowered to input. | 7 | //! actual IO is done and lowered to input. |
8 | 8 | ||
9 | use std::{ | 9 | use std::{fmt, ops, str::FromStr, sync::Arc}; |
10 | fmt, ops, | ||
11 | path::{Path, PathBuf}, | ||
12 | str::FromStr, | ||
13 | sync::Arc, | ||
14 | }; | ||
15 | 10 | ||
16 | use ra_cfg::CfgOptions; | 11 | use ra_cfg::CfgOptions; |
17 | use ra_syntax::SmolStr; | 12 | use ra_syntax::SmolStr; |
18 | use rustc_hash::FxHashMap; | ||
19 | use rustc_hash::FxHashSet; | ||
20 | |||
21 | use crate::{RelativePath, RelativePathBuf}; | ||
22 | use fmt::Display; | ||
23 | use ra_tt::TokenExpander; | 13 | use ra_tt::TokenExpander; |
14 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
15 | use vfs::file_set::FileSet; | ||
24 | 16 | ||
25 | /// `FileId` is an integer which uniquely identifies a file. File paths are | 17 | pub use vfs::FileId; |
26 | /// messy and system-dependent, so most of the code should work directly with | ||
27 | /// `FileId`, without inspecting the path. The mapping between `FileId` and path | ||
28 | /// and `SourceRoot` is constant. A file rename is represented as a pair of | ||
29 | /// deletion/creation. | ||
30 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
31 | pub struct FileId(pub u32); | ||
32 | 18 | ||
33 | /// Files are grouped into source roots. A source root is a directory on the | 19 | /// Files are grouped into source roots. A source root is a directory on the |
34 | /// file systems which is watched for changes. Typically it corresponds to a | 20 | /// file systems which is watched for changes. Typically it corresponds to a |
@@ -47,27 +33,18 @@ pub struct SourceRoot { | |||
47 | /// Libraries are considered mostly immutable, this assumption is used to | 33 | /// Libraries are considered mostly immutable, this assumption is used to |
48 | /// optimize salsa's query structure | 34 | /// optimize salsa's query structure |
49 | pub is_library: bool, | 35 | pub is_library: bool, |
50 | files: FxHashMap<RelativePathBuf, FileId>, | 36 | pub(crate) file_set: FileSet, |
51 | } | 37 | } |
52 | 38 | ||
53 | impl SourceRoot { | 39 | impl SourceRoot { |
54 | pub fn new_local() -> SourceRoot { | 40 | pub fn new_local(file_set: FileSet) -> SourceRoot { |
55 | SourceRoot { is_library: false, files: Default::default() } | 41 | SourceRoot { is_library: false, file_set } |
56 | } | ||
57 | pub fn new_library() -> SourceRoot { | ||
58 | SourceRoot { is_library: true, files: Default::default() } | ||
59 | } | ||
60 | pub fn insert_file(&mut self, path: RelativePathBuf, file_id: FileId) { | ||
61 | self.files.insert(path, file_id); | ||
62 | } | 42 | } |
63 | pub fn remove_file(&mut self, path: &RelativePath) { | 43 | pub fn new_library(file_set: FileSet) -> SourceRoot { |
64 | self.files.remove(path); | 44 | SourceRoot { is_library: true, file_set } |
65 | } | 45 | } |
66 | pub fn walk(&self) -> impl Iterator<Item = FileId> + '_ { | 46 | pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ { |
67 | self.files.values().copied() | 47 | self.file_set.iter() |
68 | } | ||
69 | pub fn file_by_relative_path(&self, path: &RelativePath) -> Option<FileId> { | ||
70 | self.files.get(path).copied() | ||
71 | } | 48 | } |
72 | } | 49 | } |
73 | 50 | ||
@@ -90,7 +67,7 @@ pub struct CrateGraph { | |||
90 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 67 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
91 | pub struct CrateId(pub u32); | 68 | pub struct CrateId(pub u32); |
92 | 69 | ||
93 | #[derive(Debug, Clone, PartialEq, Eq)] | 70 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
94 | pub struct CrateName(SmolStr); | 71 | pub struct CrateName(SmolStr); |
95 | 72 | ||
96 | impl CrateName { | 73 | impl CrateName { |
@@ -111,12 +88,19 @@ impl CrateName { | |||
111 | } | 88 | } |
112 | } | 89 | } |
113 | 90 | ||
114 | impl Display for CrateName { | 91 | impl fmt::Display for CrateName { |
115 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 92 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
116 | write!(f, "{}", self.0) | 93 | write!(f, "{}", self.0) |
117 | } | 94 | } |
118 | } | 95 | } |
119 | 96 | ||
97 | impl ops::Deref for CrateName { | ||
98 | type Target = str; | ||
99 | fn deref(&self) -> &Self::Target { | ||
100 | &*self.0 | ||
101 | } | ||
102 | } | ||
103 | |||
120 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 104 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
121 | pub struct ProcMacroId(pub u32); | 105 | pub struct ProcMacroId(pub u32); |
122 | 106 | ||
@@ -140,10 +124,9 @@ pub struct CrateData { | |||
140 | /// The name to display to the end user. | 124 | /// The name to display to the end user. |
141 | /// This actual crate name can be different in a particular dependent crate | 125 | /// This actual crate name can be different in a particular dependent crate |
142 | /// or may even be missing for some cases, such as a dummy crate for the code snippet. | 126 | /// or may even be missing for some cases, such as a dummy crate for the code snippet. |
143 | pub display_name: Option<CrateName>, | 127 | pub display_name: Option<String>, |
144 | pub cfg_options: CfgOptions, | 128 | pub cfg_options: CfgOptions, |
145 | pub env: Env, | 129 | pub env: Env, |
146 | pub extern_source: ExternSource, | ||
147 | pub dependencies: Vec<Dependency>, | 130 | pub dependencies: Vec<Dependency>, |
148 | pub proc_macro: Vec<ProcMacro>, | 131 | pub proc_macro: Vec<ProcMacro>, |
149 | } | 132 | } |
@@ -154,26 +137,15 @@ pub enum Edition { | |||
154 | Edition2015, | 137 | Edition2015, |
155 | } | 138 | } |
156 | 139 | ||
157 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
158 | pub struct ExternSourceId(pub u32); | ||
159 | |||
160 | #[derive(Default, Debug, Clone, PartialEq, Eq)] | 140 | #[derive(Default, Debug, Clone, PartialEq, Eq)] |
161 | pub struct Env { | 141 | pub struct Env { |
162 | entries: FxHashMap<String, String>, | 142 | entries: FxHashMap<String, String>, |
163 | } | 143 | } |
164 | 144 | ||
165 | // FIXME: Redesign vfs for solve the following limitation ? | ||
166 | // Note: Some env variables (e.g. OUT_DIR) are located outside of the | ||
167 | // crate. We store a map to allow remap it to ExternSourceId | ||
168 | #[derive(Default, Debug, Clone, PartialEq, Eq)] | ||
169 | pub struct ExternSource { | ||
170 | extern_paths: FxHashMap<PathBuf, ExternSourceId>, | ||
171 | } | ||
172 | |||
173 | #[derive(Debug, Clone, PartialEq, Eq)] | 145 | #[derive(Debug, Clone, PartialEq, Eq)] |
174 | pub struct Dependency { | 146 | pub struct Dependency { |
175 | pub crate_id: CrateId, | 147 | pub crate_id: CrateId, |
176 | pub name: SmolStr, | 148 | pub name: CrateName, |
177 | } | 149 | } |
178 | 150 | ||
179 | impl CrateGraph { | 151 | impl CrateGraph { |
@@ -181,10 +153,9 @@ impl CrateGraph { | |||
181 | &mut self, | 153 | &mut self, |
182 | file_id: FileId, | 154 | file_id: FileId, |
183 | edition: Edition, | 155 | edition: Edition, |
184 | display_name: Option<CrateName>, | 156 | display_name: Option<String>, |
185 | cfg_options: CfgOptions, | 157 | cfg_options: CfgOptions, |
186 | env: Env, | 158 | env: Env, |
187 | extern_source: ExternSource, | ||
188 | proc_macro: Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)>, | 159 | proc_macro: Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)>, |
189 | ) -> CrateId { | 160 | ) -> CrateId { |
190 | let proc_macro = | 161 | let proc_macro = |
@@ -196,7 +167,6 @@ impl CrateGraph { | |||
196 | display_name, | 167 | display_name, |
197 | cfg_options, | 168 | cfg_options, |
198 | env, | 169 | env, |
199 | extern_source, | ||
200 | proc_macro, | 170 | proc_macro, |
201 | dependencies: Vec::new(), | 171 | dependencies: Vec::new(), |
202 | }; | 172 | }; |
@@ -215,7 +185,7 @@ impl CrateGraph { | |||
215 | if self.dfs_find(from, to, &mut FxHashSet::default()) { | 185 | if self.dfs_find(from, to, &mut FxHashSet::default()) { |
216 | return Err(CyclicDependenciesError); | 186 | return Err(CyclicDependenciesError); |
217 | } | 187 | } |
218 | self.arena.get_mut(&from).unwrap().add_dep(name.0, to); | 188 | self.arena.get_mut(&from).unwrap().add_dep(name, to); |
219 | Ok(()) | 189 | Ok(()) |
220 | } | 190 | } |
221 | 191 | ||
@@ -227,6 +197,23 @@ impl CrateGraph { | |||
227 | self.arena.keys().copied() | 197 | self.arena.keys().copied() |
228 | } | 198 | } |
229 | 199 | ||
200 | /// Returns an iterator over all transitive dependencies of the given crate. | ||
201 | pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> + '_ { | ||
202 | let mut worklist = vec![of]; | ||
203 | let mut deps = FxHashSet::default(); | ||
204 | |||
205 | while let Some(krate) = worklist.pop() { | ||
206 | if !deps.insert(krate) { | ||
207 | continue; | ||
208 | } | ||
209 | |||
210 | worklist.extend(self[krate].dependencies.iter().map(|dep| dep.crate_id)); | ||
211 | } | ||
212 | |||
213 | deps.remove(&of); | ||
214 | deps.into_iter() | ||
215 | } | ||
216 | |||
230 | // FIXME: this only finds one crate with the given root; we could have multiple | 217 | // FIXME: this only finds one crate with the given root; we could have multiple |
231 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { | 218 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { |
232 | let (&crate_id, _) = | 219 | let (&crate_id, _) = |
@@ -256,12 +243,12 @@ impl CrateGraph { | |||
256 | return false; | 243 | return false; |
257 | } | 244 | } |
258 | 245 | ||
246 | if target == from { | ||
247 | return true; | ||
248 | } | ||
249 | |||
259 | for dep in &self[from].dependencies { | 250 | for dep in &self[from].dependencies { |
260 | let crate_id = dep.crate_id; | 251 | let crate_id = dep.crate_id; |
261 | if crate_id == target { | ||
262 | return true; | ||
263 | } | ||
264 | |||
265 | if self.dfs_find(target, crate_id, visited) { | 252 | if self.dfs_find(target, crate_id, visited) { |
266 | return true; | 253 | return true; |
267 | } | 254 | } |
@@ -284,7 +271,7 @@ impl CrateId { | |||
284 | } | 271 | } |
285 | 272 | ||
286 | impl CrateData { | 273 | impl CrateData { |
287 | fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { | 274 | fn add_dep(&mut self, name: CrateName, crate_id: CrateId) { |
288 | self.dependencies.push(Dependency { name, crate_id }) | 275 | self.dependencies.push(Dependency { name, crate_id }) |
289 | } | 276 | } |
290 | } | 277 | } |
@@ -336,24 +323,6 @@ impl Env { | |||
336 | } | 323 | } |
337 | } | 324 | } |
338 | 325 | ||
339 | impl ExternSource { | ||
340 | pub fn extern_path(&self, path: impl AsRef<Path>) -> Option<(ExternSourceId, RelativePathBuf)> { | ||
341 | let path = path.as_ref(); | ||
342 | self.extern_paths.iter().find_map(|(root_path, id)| { | ||
343 | if let Ok(rel_path) = path.strip_prefix(root_path) { | ||
344 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
345 | Some((*id, rel_path)) | ||
346 | } else { | ||
347 | None | ||
348 | } | ||
349 | }) | ||
350 | } | ||
351 | |||
352 | pub fn set_extern_path(&mut self, root_path: &Path, root: ExternSourceId) { | ||
353 | self.extern_paths.insert(root_path.to_path_buf(), root); | ||
354 | } | ||
355 | } | ||
356 | |||
357 | #[derive(Debug)] | 326 | #[derive(Debug)] |
358 | pub struct ParseEditionError { | 327 | pub struct ParseEditionError { |
359 | invalid_input: String, | 328 | invalid_input: String, |
@@ -375,7 +344,7 @@ mod tests { | |||
375 | use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId}; | 344 | use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId}; |
376 | 345 | ||
377 | #[test] | 346 | #[test] |
378 | fn it_should_panic_because_of_cycle_dependencies() { | 347 | fn detect_cyclic_dependency_indirect() { |
379 | let mut graph = CrateGraph::default(); | 348 | let mut graph = CrateGraph::default(); |
380 | let crate1 = graph.add_crate_root( | 349 | let crate1 = graph.add_crate_root( |
381 | FileId(1u32), | 350 | FileId(1u32), |
@@ -384,7 +353,6 @@ mod tests { | |||
384 | CfgOptions::default(), | 353 | CfgOptions::default(), |
385 | Env::default(), | 354 | Env::default(), |
386 | Default::default(), | 355 | Default::default(), |
387 | Default::default(), | ||
388 | ); | 356 | ); |
389 | let crate2 = graph.add_crate_root( | 357 | let crate2 = graph.add_crate_root( |
390 | FileId(2u32), | 358 | FileId(2u32), |
@@ -393,7 +361,6 @@ mod tests { | |||
393 | CfgOptions::default(), | 361 | CfgOptions::default(), |
394 | Env::default(), | 362 | Env::default(), |
395 | Default::default(), | 363 | Default::default(), |
396 | Default::default(), | ||
397 | ); | 364 | ); |
398 | let crate3 = graph.add_crate_root( | 365 | let crate3 = graph.add_crate_root( |
399 | FileId(3u32), | 366 | FileId(3u32), |
@@ -402,7 +369,6 @@ mod tests { | |||
402 | CfgOptions::default(), | 369 | CfgOptions::default(), |
403 | Env::default(), | 370 | Env::default(), |
404 | Default::default(), | 371 | Default::default(), |
405 | Default::default(), | ||
406 | ); | 372 | ); |
407 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); | 373 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); |
408 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); | 374 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); |
@@ -410,7 +376,7 @@ mod tests { | |||
410 | } | 376 | } |
411 | 377 | ||
412 | #[test] | 378 | #[test] |
413 | fn it_works() { | 379 | fn detect_cyclic_dependency_direct() { |
414 | let mut graph = CrateGraph::default(); | 380 | let mut graph = CrateGraph::default(); |
415 | let crate1 = graph.add_crate_root( | 381 | let crate1 = graph.add_crate_root( |
416 | FileId(1u32), | 382 | FileId(1u32), |
@@ -419,7 +385,6 @@ mod tests { | |||
419 | CfgOptions::default(), | 385 | CfgOptions::default(), |
420 | Env::default(), | 386 | Env::default(), |
421 | Default::default(), | 387 | Default::default(), |
422 | Default::default(), | ||
423 | ); | 388 | ); |
424 | let crate2 = graph.add_crate_root( | 389 | let crate2 = graph.add_crate_root( |
425 | FileId(2u32), | 390 | FileId(2u32), |
@@ -428,6 +393,28 @@ mod tests { | |||
428 | CfgOptions::default(), | 393 | CfgOptions::default(), |
429 | Env::default(), | 394 | Env::default(), |
430 | Default::default(), | 395 | Default::default(), |
396 | ); | ||
397 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); | ||
398 | assert!(graph.add_dep(crate2, CrateName::new("crate2").unwrap(), crate2).is_err()); | ||
399 | } | ||
400 | |||
401 | #[test] | ||
402 | fn it_works() { | ||
403 | let mut graph = CrateGraph::default(); | ||
404 | let crate1 = graph.add_crate_root( | ||
405 | FileId(1u32), | ||
406 | Edition2018, | ||
407 | None, | ||
408 | CfgOptions::default(), | ||
409 | Env::default(), | ||
410 | Default::default(), | ||
411 | ); | ||
412 | let crate2 = graph.add_crate_root( | ||
413 | FileId(2u32), | ||
414 | Edition2018, | ||
415 | None, | ||
416 | CfgOptions::default(), | ||
417 | Env::default(), | ||
431 | Default::default(), | 418 | Default::default(), |
432 | ); | 419 | ); |
433 | let crate3 = graph.add_crate_root( | 420 | let crate3 = graph.add_crate_root( |
@@ -437,7 +424,6 @@ mod tests { | |||
437 | CfgOptions::default(), | 424 | CfgOptions::default(), |
438 | Env::default(), | 425 | Env::default(), |
439 | Default::default(), | 426 | Default::default(), |
440 | Default::default(), | ||
441 | ); | 427 | ); |
442 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); | 428 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); |
443 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); | 429 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); |
@@ -453,7 +439,6 @@ mod tests { | |||
453 | CfgOptions::default(), | 439 | CfgOptions::default(), |
454 | Env::default(), | 440 | Env::default(), |
455 | Default::default(), | 441 | Default::default(), |
456 | Default::default(), | ||
457 | ); | 442 | ); |
458 | let crate2 = graph.add_crate_root( | 443 | let crate2 = graph.add_crate_root( |
459 | FileId(2u32), | 444 | FileId(2u32), |
@@ -462,14 +447,16 @@ mod tests { | |||
462 | CfgOptions::default(), | 447 | CfgOptions::default(), |
463 | Env::default(), | 448 | Env::default(), |
464 | Default::default(), | 449 | Default::default(), |
465 | Default::default(), | ||
466 | ); | 450 | ); |
467 | assert!(graph | 451 | assert!(graph |
468 | .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) | 452 | .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) |
469 | .is_ok()); | 453 | .is_ok()); |
470 | assert_eq!( | 454 | assert_eq!( |
471 | graph[crate1].dependencies, | 455 | graph[crate1].dependencies, |
472 | vec![Dependency { crate_id: crate2, name: "crate_name_with_dashes".into() }] | 456 | vec![Dependency { |
457 | crate_id: crate2, | ||
458 | name: CrateName::new("crate_name_with_dashes").unwrap() | ||
459 | }] | ||
473 | ); | 460 | ); |
474 | } | 461 | } |
475 | } | 462 | } |
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index fd4280de2..f25be24fe 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs | |||
@@ -7,16 +7,17 @@ use std::{panic, sync::Arc}; | |||
7 | 7 | ||
8 | use ra_prof::profile; | 8 | use ra_prof::profile; |
9 | use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; | 9 | use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; |
10 | use rustc_hash::FxHashSet; | ||
10 | 11 | ||
11 | pub use crate::{ | 12 | pub use crate::{ |
12 | cancellation::Canceled, | 13 | cancellation::Canceled, |
13 | input::{ | 14 | input::{ |
14 | CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId, | 15 | CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacroId, |
15 | FileId, ProcMacroId, SourceRoot, SourceRootId, | 16 | SourceRoot, SourceRootId, |
16 | }, | 17 | }, |
17 | }; | 18 | }; |
18 | pub use relative_path::{RelativePath, RelativePathBuf}; | ||
19 | pub use salsa; | 19 | pub use salsa; |
20 | pub use vfs::{file_set::FileSet, VfsPath}; | ||
20 | 21 | ||
21 | #[macro_export] | 22 | #[macro_export] |
22 | macro_rules! impl_intern_key { | 23 | macro_rules! impl_intern_key { |
@@ -78,7 +79,7 @@ pub struct FilePosition { | |||
78 | pub offset: TextSize, | 79 | pub offset: TextSize, |
79 | } | 80 | } |
80 | 81 | ||
81 | #[derive(Clone, Copy, Debug)] | 82 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
82 | pub struct FileRange { | 83 | pub struct FileRange { |
83 | pub file_id: FileId, | 84 | pub file_id: FileId, |
84 | pub range: TextRange, | 85 | pub range: TextRange, |
@@ -89,15 +90,13 @@ pub const DEFAULT_LRU_CAP: usize = 128; | |||
89 | pub trait FileLoader { | 90 | pub trait FileLoader { |
90 | /// Text of the file. | 91 | /// Text of the file. |
91 | fn file_text(&self, file_id: FileId) -> Arc<String>; | 92 | fn file_text(&self, file_id: FileId) -> Arc<String>; |
92 | fn resolve_relative_path(&self, anchor: FileId, relative_path: &RelativePath) | 93 | /// Note that we intentionally accept a `&str` and not a `&Path` here. This |
93 | -> Option<FileId>; | 94 | /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such, |
94 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; | 95 | /// so the input is guaranteed to be utf-8 string. One might be tempted to |
95 | 96 | /// introduce some kind of "utf-8 path with / separators", but that's a bad idea. Behold | |
96 | fn resolve_extern_path( | 97 | /// `#[path = "C://no/way"]` |
97 | &self, | 98 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>; |
98 | extern_id: ExternSourceId, | 99 | fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>; |
99 | relative_path: &RelativePath, | ||
100 | ) -> Option<FileId>; | ||
101 | } | 100 | } |
102 | 101 | ||
103 | /// Database which stores all significant input facts: source code and project | 102 | /// Database which stores all significant input facts: source code and project |
@@ -113,8 +112,8 @@ pub trait SourceDatabase: CheckCanceled + FileLoader + std::fmt::Debug { | |||
113 | fn crate_graph(&self) -> Arc<CrateGraph>; | 112 | fn crate_graph(&self) -> Arc<CrateGraph>; |
114 | } | 113 | } |
115 | 114 | ||
116 | fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> { | 115 | fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> { |
117 | let _p = profile("parse_query"); | 116 | let _p = profile("parse_query").detail(|| format!("{:?}", file_id)); |
118 | let text = db.file_text(file_id); | 117 | let text = db.file_text(file_id); |
119 | SourceFile::parse(&*text) | 118 | SourceFile::parse(&*text) |
120 | } | 119 | } |
@@ -126,8 +125,6 @@ pub trait SourceDatabaseExt: SourceDatabase { | |||
126 | #[salsa::input] | 125 | #[salsa::input] |
127 | fn file_text(&self, file_id: FileId) -> Arc<String>; | 126 | fn file_text(&self, file_id: FileId) -> Arc<String>; |
128 | /// Path to a file, relative to the root of its source root. | 127 | /// Path to a file, relative to the root of its source root. |
129 | #[salsa::input] | ||
130 | fn file_relative_path(&self, file_id: FileId) -> RelativePathBuf; | ||
131 | /// Source root of the file. | 128 | /// Source root of the file. |
132 | #[salsa::input] | 129 | #[salsa::input] |
133 | fn file_source_root(&self, file_id: FileId) -> SourceRootId; | 130 | fn file_source_root(&self, file_id: FileId) -> SourceRootId; |
@@ -135,16 +132,18 @@ pub trait SourceDatabaseExt: SourceDatabase { | |||
135 | #[salsa::input] | 132 | #[salsa::input] |
136 | fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; | 133 | fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; |
137 | 134 | ||
138 | fn source_root_crates(&self, id: SourceRootId) -> Arc<Vec<CrateId>>; | 135 | fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>; |
139 | } | 136 | } |
140 | 137 | ||
141 | fn source_root_crates( | 138 | fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<FxHashSet<CrateId>> { |
142 | db: &(impl SourceDatabaseExt + SourceDatabase), | ||
143 | id: SourceRootId, | ||
144 | ) -> Arc<Vec<CrateId>> { | ||
145 | let root = db.source_root(id); | ||
146 | let graph = db.crate_graph(); | 139 | let graph = db.crate_graph(); |
147 | let res = root.walk().filter_map(|it| graph.crate_id_for_crate_root(it)).collect::<Vec<_>>(); | 140 | let res = graph |
141 | .iter() | ||
142 | .filter(|&krate| { | ||
143 | let root_file = graph[krate].root_file_id; | ||
144 | db.file_source_root(root_file) == id | ||
145 | }) | ||
146 | .collect::<FxHashSet<_>>(); | ||
148 | Arc::new(res) | 147 | Arc::new(res) |
149 | } | 148 | } |
150 | 149 | ||
@@ -155,33 +154,15 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> { | |||
155 | fn file_text(&self, file_id: FileId) -> Arc<String> { | 154 | fn file_text(&self, file_id: FileId) -> Arc<String> { |
156 | SourceDatabaseExt::file_text(self.0, file_id) | 155 | SourceDatabaseExt::file_text(self.0, file_id) |
157 | } | 156 | } |
158 | fn resolve_relative_path( | 157 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
159 | &self, | 158 | // FIXME: this *somehow* should be platform agnostic... |
160 | anchor: FileId, | ||
161 | relative_path: &RelativePath, | ||
162 | ) -> Option<FileId> { | ||
163 | let path = { | ||
164 | let mut path = self.0.file_relative_path(anchor); | ||
165 | assert!(path.pop()); | ||
166 | path.push(relative_path); | ||
167 | path.normalize() | ||
168 | }; | ||
169 | let source_root = self.0.file_source_root(anchor); | 159 | let source_root = self.0.file_source_root(anchor); |
170 | let source_root = self.0.source_root(source_root); | 160 | let source_root = self.0.source_root(source_root); |
171 | source_root.file_by_relative_path(&path) | 161 | source_root.file_set.resolve_path(anchor, path) |
172 | } | 162 | } |
173 | 163 | ||
174 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 164 | fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { |
175 | let source_root = self.0.file_source_root(file_id); | 165 | let source_root = self.0.file_source_root(file_id); |
176 | self.0.source_root_crates(source_root) | 166 | self.0.source_root_crates(source_root) |
177 | } | 167 | } |
178 | |||
179 | fn resolve_extern_path( | ||
180 | &self, | ||
181 | extern_id: ExternSourceId, | ||
182 | relative_path: &RelativePath, | ||
183 | ) -> Option<FileId> { | ||
184 | let source_root = self.0.source_root(SourceRootId(extern_id.0)); | ||
185 | source_root.file_by_relative_path(&relative_path) | ||
186 | } | ||
187 | } | 168 | } |