aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_db/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_db/src')
-rw-r--r--crates/ra_db/src/fixture.rs188
-rw-r--r--crates/ra_db/src/input.rs161
-rw-r--r--crates/ra_db/src/lib.rs73
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 60use std::{str::FromStr, sync::Arc};
61use std::str::FromStr;
62use std::sync::Arc;
63 61
64use ra_cfg::CfgOptions; 62use ra_cfg::CfgOptions;
65use rustc_hash::FxHashMap; 63use rustc_hash::FxHashMap;
66use test_utils::{extract_offset, parse_fixture, parse_single_fixture, FixtureMeta, CURSOR_MARKER}; 64use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER};
65use vfs::{file_set::FileSet, VfsPath};
67 66
68use crate::{ 67use 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
73pub const WORKSPACE: SourceRootId = SourceRootId(0); 72pub const WORKSPACE: SourceRootId = SourceRootId(0);
@@ -75,21 +74,32 @@ pub const WORKSPACE: SourceRootId = SourceRootId(0);
75pub trait WithFixture: Default + SourceDatabaseExt + 'static { 74pub 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
104impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {} 114impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {}
105 115
106fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId { 116fn 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
157fn 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
247enum ParsedMeta {
248 Root { path: RelativePathBuf },
249 File(FileMeta),
250} 199}
251 200
252struct FileMeta { 201struct 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
261impl From<&FixtureMeta> for ParsedMeta { 210impl 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
9use std::{ 9use std::{fmt, ops, str::FromStr, sync::Arc};
10 fmt, ops,
11 path::{Path, PathBuf},
12 str::FromStr,
13 sync::Arc,
14};
15 10
16use ra_cfg::CfgOptions; 11use ra_cfg::CfgOptions;
17use ra_syntax::SmolStr; 12use ra_syntax::SmolStr;
18use rustc_hash::FxHashMap;
19use rustc_hash::FxHashSet;
20
21use crate::{RelativePath, RelativePathBuf};
22use fmt::Display;
23use ra_tt::TokenExpander; 13use ra_tt::TokenExpander;
14use rustc_hash::{FxHashMap, FxHashSet};
15use vfs::file_set::FileSet;
24 16
25/// `FileId` is an integer which uniquely identifies a file. File paths are 17pub 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)]
31pub 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
53impl SourceRoot { 39impl 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)]
91pub struct CrateId(pub u32); 68pub struct CrateId(pub u32);
92 69
93#[derive(Debug, Clone, PartialEq, Eq)] 70#[derive(Debug, Clone, PartialEq, Eq, Hash)]
94pub struct CrateName(SmolStr); 71pub struct CrateName(SmolStr);
95 72
96impl CrateName { 73impl CrateName {
@@ -111,12 +88,19 @@ impl CrateName {
111 } 88 }
112} 89}
113 90
114impl Display for CrateName { 91impl 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
97impl 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)]
121pub struct ProcMacroId(pub u32); 105pub 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)]
158pub struct ExternSourceId(pub u32);
159
160#[derive(Default, Debug, Clone, PartialEq, Eq)] 140#[derive(Default, Debug, Clone, PartialEq, Eq)]
161pub struct Env { 141pub 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)]
169pub struct ExternSource {
170 extern_paths: FxHashMap<PathBuf, ExternSourceId>,
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)] 145#[derive(Debug, Clone, PartialEq, Eq)]
174pub struct Dependency { 146pub struct Dependency {
175 pub crate_id: CrateId, 147 pub crate_id: CrateId,
176 pub name: SmolStr, 148 pub name: CrateName,
177} 149}
178 150
179impl CrateGraph { 151impl 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
286impl CrateData { 273impl 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
339impl 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)]
358pub struct ParseEditionError { 327pub 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
8use ra_prof::profile; 8use ra_prof::profile;
9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; 9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize};
10use rustc_hash::FxHashSet;
10 11
11pub use crate::{ 12pub 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};
18pub use relative_path::{RelativePath, RelativePathBuf};
19pub use salsa; 19pub use salsa;
20pub use vfs::{file_set::FileSet, VfsPath};
20 21
21#[macro_export] 22#[macro_export]
22macro_rules! impl_intern_key { 23macro_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)]
82pub struct FileRange { 83pub 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;
89pub trait FileLoader { 90pub 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
116fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> { 115fn 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
141fn source_root_crates( 138fn 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}