diff options
Diffstat (limited to 'crates')
48 files changed, 1349 insertions, 260 deletions
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml index 731d50371..741345a21 100644 --- a/crates/ra_cargo_watch/Cargo.toml +++ b/crates/ra_cargo_watch/Cargo.toml | |||
@@ -6,10 +6,10 @@ authors = ["rust-analyzer developers"] | |||
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | crossbeam-channel = "0.4.0" | 8 | crossbeam-channel = "0.4.0" |
9 | lsp-types = { version = "0.72.0", features = ["proposed"] } | 9 | lsp-types = { version = "0.73.0", features = ["proposed"] } |
10 | log = "0.4.8" | 10 | log = "0.4.8" |
11 | cargo_metadata = "0.9.1" | 11 | cargo_metadata = "0.9.1" |
12 | serde_json = "1.0.48" | 12 | serde_json = "1.0.48" |
13 | 13 | ||
14 | [dev-dependencies] | 14 | [dev-dependencies] |
15 | insta = "0.13.1" | 15 | insta = "0.15.0" |
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index da7af110c..947d6ad56 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs | |||
@@ -56,6 +56,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, text: &str) -> FileId { | |||
56 | crate_graph.add_crate_root( | 56 | crate_graph.add_crate_root( |
57 | file_id, | 57 | file_id, |
58 | Edition::Edition2018, | 58 | Edition::Edition2018, |
59 | None, | ||
59 | CfgOptions::default(), | 60 | CfgOptions::default(), |
60 | Env::default(), | 61 | Env::default(), |
61 | ); | 62 | ); |
@@ -98,8 +99,13 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit | |||
98 | assert!(meta.path.starts_with(&source_root_prefix)); | 99 | assert!(meta.path.starts_with(&source_root_prefix)); |
99 | 100 | ||
100 | if let Some(krate) = meta.krate { | 101 | if let Some(krate) = meta.krate { |
101 | let crate_id = | 102 | let crate_id = crate_graph.add_crate_root( |
102 | crate_graph.add_crate_root(file_id, meta.edition, meta.cfg, Env::default()); | 103 | file_id, |
104 | meta.edition, | ||
105 | Some(krate.clone()), | ||
106 | meta.cfg, | ||
107 | Env::default(), | ||
108 | ); | ||
103 | let prev = crates.insert(krate.clone(), crate_id); | 109 | let prev = crates.insert(krate.clone(), crate_id); |
104 | assert!(prev.is_none()); | 110 | assert!(prev.is_none()); |
105 | for dep in meta.deps { | 111 | for dep in meta.deps { |
@@ -132,6 +138,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit | |||
132 | crate_graph.add_crate_root( | 138 | crate_graph.add_crate_root( |
133 | crate_root, | 139 | crate_root, |
134 | Edition::Edition2018, | 140 | Edition::Edition2018, |
141 | None, | ||
135 | CfgOptions::default(), | 142 | CfgOptions::default(), |
136 | Env::default(), | 143 | Env::default(), |
137 | ); | 144 | ); |
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index eaff99fd3..b77640b2b 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -6,7 +6,7 @@ | |||
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::{fmt, str::FromStr}; | 9 | use std::{fmt, ops, str::FromStr}; |
10 | 10 | ||
11 | use ra_cfg::CfgOptions; | 11 | use ra_cfg::CfgOptions; |
12 | use ra_syntax::SmolStr; | 12 | use ra_syntax::SmolStr; |
@@ -86,7 +86,7 @@ pub struct CrateId(pub u32); | |||
86 | pub struct CrateName(SmolStr); | 86 | pub struct CrateName(SmolStr); |
87 | 87 | ||
88 | impl CrateName { | 88 | impl CrateName { |
89 | /// Crates a crate name, checking for dashes in the string provided. | 89 | /// Creates a crate name, checking for dashes in the string provided. |
90 | /// Dashes are not allowed in the crate names, | 90 | /// Dashes are not allowed in the crate names, |
91 | /// hence the input string is returned as `Err` for those cases. | 91 | /// hence the input string is returned as `Err` for those cases. |
92 | pub fn new(name: &str) -> Result<CrateName, &str> { | 92 | pub fn new(name: &str) -> Result<CrateName, &str> { |
@@ -97,19 +97,23 @@ impl CrateName { | |||
97 | } | 97 | } |
98 | } | 98 | } |
99 | 99 | ||
100 | /// Crates a crate name, unconditionally replacing the dashes with underscores. | 100 | /// Creates a crate name, unconditionally replacing the dashes with underscores. |
101 | pub fn normalize_dashes(name: &str) -> CrateName { | 101 | pub fn normalize_dashes(name: &str) -> CrateName { |
102 | Self(SmolStr::new(name.replace('-', "_"))) | 102 | Self(SmolStr::new(name.replace('-', "_"))) |
103 | } | 103 | } |
104 | } | 104 | } |
105 | 105 | ||
106 | #[derive(Debug, Clone, PartialEq, Eq)] | 106 | #[derive(Debug, Clone, PartialEq, Eq)] |
107 | struct CrateData { | 107 | pub struct CrateData { |
108 | file_id: FileId, | 108 | pub root_file_id: FileId, |
109 | edition: Edition, | 109 | pub edition: Edition, |
110 | cfg_options: CfgOptions, | 110 | /// The name to display to the end user. |
111 | env: Env, | 111 | /// This actual crate name can be different in a particular dependent crate |
112 | dependencies: Vec<Dependency>, | 112 | /// or may even be missing for some cases, such as a dummy crate for the code snippet. |
113 | pub display_name: Option<String>, | ||
114 | pub cfg_options: CfgOptions, | ||
115 | pub env: Env, | ||
116 | pub dependencies: Vec<Dependency>, | ||
113 | } | 117 | } |
114 | 118 | ||
115 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 119 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -134,20 +138,24 @@ impl CrateGraph { | |||
134 | &mut self, | 138 | &mut self, |
135 | file_id: FileId, | 139 | file_id: FileId, |
136 | edition: Edition, | 140 | edition: Edition, |
141 | display_name: Option<String>, | ||
137 | cfg_options: CfgOptions, | 142 | cfg_options: CfgOptions, |
138 | env: Env, | 143 | env: Env, |
139 | ) -> CrateId { | 144 | ) -> CrateId { |
140 | let data = CrateData::new(file_id, edition, cfg_options, env); | 145 | let data = CrateData { |
146 | root_file_id: file_id, | ||
147 | edition, | ||
148 | display_name, | ||
149 | cfg_options, | ||
150 | env, | ||
151 | dependencies: Vec::new(), | ||
152 | }; | ||
141 | let crate_id = CrateId(self.arena.len() as u32); | 153 | let crate_id = CrateId(self.arena.len() as u32); |
142 | let prev = self.arena.insert(crate_id, data); | 154 | let prev = self.arena.insert(crate_id, data); |
143 | assert!(prev.is_none()); | 155 | assert!(prev.is_none()); |
144 | crate_id | 156 | crate_id |
145 | } | 157 | } |
146 | 158 | ||
147 | pub fn cfg_options(&self, crate_id: CrateId) -> &CfgOptions { | ||
148 | &self.arena[&crate_id].cfg_options | ||
149 | } | ||
150 | |||
151 | pub fn add_dep( | 159 | pub fn add_dep( |
152 | &mut self, | 160 | &mut self, |
153 | from: CrateId, | 161 | from: CrateId, |
@@ -169,24 +177,13 @@ impl CrateGraph { | |||
169 | self.arena.keys().copied() | 177 | self.arena.keys().copied() |
170 | } | 178 | } |
171 | 179 | ||
172 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { | ||
173 | self.arena[&crate_id].file_id | ||
174 | } | ||
175 | |||
176 | pub fn edition(&self, crate_id: CrateId) -> Edition { | ||
177 | self.arena[&crate_id].edition | ||
178 | } | ||
179 | |||
180 | // FIXME: this only finds one crate with the given root; we could have multiple | 180 | // FIXME: this only finds one crate with the given root; we could have multiple |
181 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { | 181 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { |
182 | let (&crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.file_id == file_id)?; | 182 | let (&crate_id, _) = |
183 | self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?; | ||
183 | Some(crate_id) | 184 | Some(crate_id) |
184 | } | 185 | } |
185 | 186 | ||
186 | pub fn dependencies(&self, crate_id: CrateId) -> impl Iterator<Item = &Dependency> { | ||
187 | self.arena[&crate_id].dependencies.iter() | ||
188 | } | ||
189 | |||
190 | /// Extends this crate graph by adding a complete disjoint second crate | 187 | /// Extends this crate graph by adding a complete disjoint second crate |
191 | /// graph. | 188 | /// graph. |
192 | /// | 189 | /// |
@@ -209,8 +206,8 @@ impl CrateGraph { | |||
209 | return false; | 206 | return false; |
210 | } | 207 | } |
211 | 208 | ||
212 | for dep in self.dependencies(from) { | 209 | for dep in &self[from].dependencies { |
213 | let crate_id = dep.crate_id(); | 210 | let crate_id = dep.crate_id; |
214 | if crate_id == target { | 211 | if crate_id == target { |
215 | return true; | 212 | return true; |
216 | } | 213 | } |
@@ -223,6 +220,13 @@ impl CrateGraph { | |||
223 | } | 220 | } |
224 | } | 221 | } |
225 | 222 | ||
223 | impl ops::Index<CrateId> for CrateGraph { | ||
224 | type Output = CrateData; | ||
225 | fn index(&self, crate_id: CrateId) -> &CrateData { | ||
226 | &self.arena[&crate_id] | ||
227 | } | ||
228 | } | ||
229 | |||
226 | impl CrateId { | 230 | impl CrateId { |
227 | pub fn shift(self, amount: u32) -> CrateId { | 231 | pub fn shift(self, amount: u32) -> CrateId { |
228 | CrateId(self.0 + amount) | 232 | CrateId(self.0 + amount) |
@@ -230,10 +234,6 @@ impl CrateId { | |||
230 | } | 234 | } |
231 | 235 | ||
232 | impl CrateData { | 236 | impl CrateData { |
233 | fn new(file_id: FileId, edition: Edition, cfg_options: CfgOptions, env: Env) -> CrateData { | ||
234 | CrateData { file_id, edition, dependencies: Vec::new(), cfg_options, env } | ||
235 | } | ||
236 | |||
237 | fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { | 237 | fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { |
238 | self.dependencies.push(Dependency { name, crate_id }) | 238 | self.dependencies.push(Dependency { name, crate_id }) |
239 | } | 239 | } |
@@ -261,12 +261,6 @@ impl fmt::Display for Edition { | |||
261 | } | 261 | } |
262 | } | 262 | } |
263 | 263 | ||
264 | impl Dependency { | ||
265 | pub fn crate_id(&self) -> CrateId { | ||
266 | self.crate_id | ||
267 | } | ||
268 | } | ||
269 | |||
270 | #[derive(Debug)] | 264 | #[derive(Debug)] |
271 | pub struct ParseEditionError { | 265 | pub struct ParseEditionError { |
272 | invalid_input: String, | 266 | invalid_input: String, |
@@ -290,12 +284,27 @@ mod tests { | |||
290 | #[test] | 284 | #[test] |
291 | fn it_should_panic_because_of_cycle_dependencies() { | 285 | fn it_should_panic_because_of_cycle_dependencies() { |
292 | let mut graph = CrateGraph::default(); | 286 | let mut graph = CrateGraph::default(); |
293 | let crate1 = | 287 | let crate1 = graph.add_crate_root( |
294 | graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default(), Env::default()); | 288 | FileId(1u32), |
295 | let crate2 = | 289 | Edition2018, |
296 | graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default(), Env::default()); | 290 | None, |
297 | let crate3 = | 291 | CfgOptions::default(), |
298 | graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default(), Env::default()); | 292 | Env::default(), |
293 | ); | ||
294 | let crate2 = graph.add_crate_root( | ||
295 | FileId(2u32), | ||
296 | Edition2018, | ||
297 | None, | ||
298 | CfgOptions::default(), | ||
299 | Env::default(), | ||
300 | ); | ||
301 | let crate3 = graph.add_crate_root( | ||
302 | FileId(3u32), | ||
303 | Edition2018, | ||
304 | None, | ||
305 | CfgOptions::default(), | ||
306 | Env::default(), | ||
307 | ); | ||
299 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); | 308 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); |
300 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); | 309 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); |
301 | assert!(graph.add_dep(crate3, CrateName::new("crate1").unwrap(), crate1).is_err()); | 310 | assert!(graph.add_dep(crate3, CrateName::new("crate1").unwrap(), crate1).is_err()); |
@@ -304,12 +313,27 @@ mod tests { | |||
304 | #[test] | 313 | #[test] |
305 | fn it_works() { | 314 | fn it_works() { |
306 | let mut graph = CrateGraph::default(); | 315 | let mut graph = CrateGraph::default(); |
307 | let crate1 = | 316 | let crate1 = graph.add_crate_root( |
308 | graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default(), Env::default()); | 317 | FileId(1u32), |
309 | let crate2 = | 318 | Edition2018, |
310 | graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default(), Env::default()); | 319 | None, |
311 | let crate3 = | 320 | CfgOptions::default(), |
312 | graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default(), Env::default()); | 321 | Env::default(), |
322 | ); | ||
323 | let crate2 = graph.add_crate_root( | ||
324 | FileId(2u32), | ||
325 | Edition2018, | ||
326 | None, | ||
327 | CfgOptions::default(), | ||
328 | Env::default(), | ||
329 | ); | ||
330 | let crate3 = graph.add_crate_root( | ||
331 | FileId(3u32), | ||
332 | Edition2018, | ||
333 | None, | ||
334 | CfgOptions::default(), | ||
335 | Env::default(), | ||
336 | ); | ||
313 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); | 337 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); |
314 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); | 338 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); |
315 | } | 339 | } |
@@ -317,16 +341,26 @@ mod tests { | |||
317 | #[test] | 341 | #[test] |
318 | fn dashes_are_normalized() { | 342 | fn dashes_are_normalized() { |
319 | let mut graph = CrateGraph::default(); | 343 | let mut graph = CrateGraph::default(); |
320 | let crate1 = | 344 | let crate1 = graph.add_crate_root( |
321 | graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default(), Env::default()); | 345 | FileId(1u32), |
322 | let crate2 = | 346 | Edition2018, |
323 | graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default(), Env::default()); | 347 | None, |
348 | CfgOptions::default(), | ||
349 | Env::default(), | ||
350 | ); | ||
351 | let crate2 = graph.add_crate_root( | ||
352 | FileId(2u32), | ||
353 | Edition2018, | ||
354 | None, | ||
355 | CfgOptions::default(), | ||
356 | Env::default(), | ||
357 | ); | ||
324 | assert!(graph | 358 | assert!(graph |
325 | .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) | 359 | .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) |
326 | .is_ok()); | 360 | .is_ok()); |
327 | assert_eq!( | 361 | assert_eq!( |
328 | graph.dependencies(crate1).collect::<Vec<_>>(), | 362 | graph[crate1].dependencies, |
329 | vec![&Dependency { crate_id: crate2, name: "crate_name_with_dashes".into() }] | 363 | vec![Dependency { crate_id: crate2, name: "crate_name_with_dashes".into() }] |
330 | ); | 364 | ); |
331 | } | 365 | } |
332 | } | 366 | } |
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index 0555a0de7..266c4cff3 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml | |||
@@ -12,6 +12,8 @@ log = "0.4.8" | |||
12 | rustc-hash = "1.1.0" | 12 | rustc-hash = "1.1.0" |
13 | either = "1.5.3" | 13 | either = "1.5.3" |
14 | 14 | ||
15 | itertools = "0.8.2" | ||
16 | |||
15 | ra_syntax = { path = "../ra_syntax" } | 17 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_db = { path = "../ra_db" } | 18 | ra_db = { path = "../ra_db" } |
17 | ra_prof = { path = "../ra_prof" } | 19 | ra_prof = { path = "../ra_prof" } |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 2944926e6..41d4e2ed3 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -54,10 +54,11 @@ pub struct CrateDependency { | |||
54 | 54 | ||
55 | impl Crate { | 55 | impl Crate { |
56 | pub fn dependencies(self, db: &impl DefDatabase) -> Vec<CrateDependency> { | 56 | pub fn dependencies(self, db: &impl DefDatabase) -> Vec<CrateDependency> { |
57 | db.crate_graph() | 57 | db.crate_graph()[self.id] |
58 | .dependencies(self.id) | 58 | .dependencies |
59 | .iter() | ||
59 | .map(|dep| { | 60 | .map(|dep| { |
60 | let krate = Crate { id: dep.crate_id() }; | 61 | let krate = Crate { id: dep.crate_id }; |
61 | let name = dep.as_name(); | 62 | let name = dep.as_name(); |
62 | CrateDependency { krate, name } | 63 | CrateDependency { krate, name } |
63 | }) | 64 | }) |
@@ -69,7 +70,9 @@ impl Crate { | |||
69 | let crate_graph = db.crate_graph(); | 70 | let crate_graph = db.crate_graph(); |
70 | crate_graph | 71 | crate_graph |
71 | .iter() | 72 | .iter() |
72 | .filter(|&krate| crate_graph.dependencies(krate).any(|it| it.crate_id == self.id)) | 73 | .filter(|&krate| { |
74 | crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id) | ||
75 | }) | ||
73 | .map(|id| Crate { id }) | 76 | .map(|id| Crate { id }) |
74 | .collect() | 77 | .collect() |
75 | } | 78 | } |
@@ -80,12 +83,11 @@ impl Crate { | |||
80 | } | 83 | } |
81 | 84 | ||
82 | pub fn root_file(self, db: &impl DefDatabase) -> FileId { | 85 | pub fn root_file(self, db: &impl DefDatabase) -> FileId { |
83 | db.crate_graph().crate_root(self.id) | 86 | db.crate_graph()[self.id].root_file_id |
84 | } | 87 | } |
85 | 88 | ||
86 | pub fn edition(self, db: &impl DefDatabase) -> Edition { | 89 | pub fn edition(self, db: &impl DefDatabase) -> Edition { |
87 | let crate_graph = db.crate_graph(); | 90 | db.crate_graph()[self.id].edition |
88 | crate_graph.edition(self.id) | ||
89 | } | 91 | } |
90 | 92 | ||
91 | pub fn all(db: &impl DefDatabase) -> Vec<Crate> { | 93 | pub fn all(db: &impl DefDatabase) -> Vec<Crate> { |
@@ -204,10 +206,26 @@ impl Module { | |||
204 | } | 206 | } |
205 | 207 | ||
206 | /// Returns a `ModuleScope`: a set of items, visible in this module. | 208 | /// Returns a `ModuleScope`: a set of items, visible in this module. |
207 | pub fn scope(self, db: &impl HirDatabase) -> Vec<(Name, ScopeDef)> { | 209 | pub fn scope( |
210 | self, | ||
211 | db: &impl HirDatabase, | ||
212 | visible_from: Option<Module>, | ||
213 | ) -> Vec<(Name, ScopeDef)> { | ||
208 | db.crate_def_map(self.id.krate)[self.id.local_id] | 214 | db.crate_def_map(self.id.krate)[self.id.local_id] |
209 | .scope | 215 | .scope |
210 | .entries() | 216 | .entries() |
217 | .filter_map(|(name, def)| { | ||
218 | if let Some(m) = visible_from { | ||
219 | let filtered = def.filter_visibility(|vis| vis.is_visible_from(db, m.id)); | ||
220 | if filtered.is_none() && !def.is_none() { | ||
221 | None | ||
222 | } else { | ||
223 | Some((name, filtered)) | ||
224 | } | ||
225 | } else { | ||
226 | Some((name, def)) | ||
227 | } | ||
228 | }) | ||
211 | .map(|(name, def)| (name.clone(), def.into())) | 229 | .map(|(name, def)| (name.clone(), def.into())) |
212 | .collect() | 230 | .collect() |
213 | } | 231 | } |
@@ -480,6 +498,14 @@ impl Adt { | |||
480 | pub fn krate(self, db: &impl HirDatabase) -> Option<Crate> { | 498 | pub fn krate(self, db: &impl HirDatabase) -> Option<Crate> { |
481 | Some(self.module(db).krate()) | 499 | Some(self.module(db).krate()) |
482 | } | 500 | } |
501 | |||
502 | pub fn name(&self, db: &impl HirDatabase) -> Name { | ||
503 | match self { | ||
504 | Adt::Struct(s) => s.name(db), | ||
505 | Adt::Union(u) => u.name(db), | ||
506 | Adt::Enum(e) => e.name(db), | ||
507 | } | ||
508 | } | ||
483 | } | 509 | } |
484 | 510 | ||
485 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 511 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
@@ -507,6 +533,14 @@ impl VariantDef { | |||
507 | } | 533 | } |
508 | } | 534 | } |
509 | 535 | ||
536 | pub fn name(&self, db: &impl HirDatabase) -> Name { | ||
537 | match self { | ||
538 | VariantDef::Struct(s) => s.name(db), | ||
539 | VariantDef::Union(u) => u.name(db), | ||
540 | VariantDef::EnumVariant(e) => e.name(db), | ||
541 | } | ||
542 | } | ||
543 | |||
510 | pub(crate) fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> { | 544 | pub(crate) fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> { |
511 | match self { | 545 | match self { |
512 | VariantDef::Struct(it) => it.variant_data(db), | 546 | VariantDef::Struct(it) => it.variant_data(db), |
@@ -534,6 +568,14 @@ impl DefWithBody { | |||
534 | DefWithBody::Static(s) => s.module(db), | 568 | DefWithBody::Static(s) => s.module(db), |
535 | } | 569 | } |
536 | } | 570 | } |
571 | |||
572 | pub fn name(self, db: &impl HirDatabase) -> Option<Name> { | ||
573 | match self { | ||
574 | DefWithBody::Function(f) => Some(f.name(db)), | ||
575 | DefWithBody::Static(s) => s.name(db), | ||
576 | DefWithBody::Const(c) => c.name(db), | ||
577 | } | ||
578 | } | ||
537 | } | 579 | } |
538 | 580 | ||
539 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 581 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -571,6 +613,14 @@ impl Function { | |||
571 | } | 613 | } |
572 | } | 614 | } |
573 | 615 | ||
616 | impl HasVisibility for Function { | ||
617 | fn visibility(&self, db: &impl HirDatabase) -> Visibility { | ||
618 | let function_data = db.function_data(self.id); | ||
619 | let visibility = &function_data.visibility; | ||
620 | visibility.resolve(db, &self.id.resolver(db)) | ||
621 | } | ||
622 | } | ||
623 | |||
574 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 624 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
575 | pub struct Const { | 625 | pub struct Const { |
576 | pub(crate) id: ConstId, | 626 | pub(crate) id: ConstId, |
@@ -590,6 +640,14 @@ impl Const { | |||
590 | } | 640 | } |
591 | } | 641 | } |
592 | 642 | ||
643 | impl HasVisibility for Const { | ||
644 | fn visibility(&self, db: &impl HirDatabase) -> Visibility { | ||
645 | let function_data = db.const_data(self.id); | ||
646 | let visibility = &function_data.visibility; | ||
647 | visibility.resolve(db, &self.id.resolver(db)) | ||
648 | } | ||
649 | } | ||
650 | |||
593 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 651 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
594 | pub struct Static { | 652 | pub struct Static { |
595 | pub(crate) id: StaticId, | 653 | pub(crate) id: StaticId, |
@@ -664,6 +722,14 @@ impl TypeAlias { | |||
664 | } | 722 | } |
665 | } | 723 | } |
666 | 724 | ||
725 | impl HasVisibility for TypeAlias { | ||
726 | fn visibility(&self, db: &impl HirDatabase) -> Visibility { | ||
727 | let function_data = db.type_alias_data(self.id); | ||
728 | let visibility = &function_data.visibility; | ||
729 | visibility.resolve(db, &self.id.resolver(db)) | ||
730 | } | ||
731 | } | ||
732 | |||
667 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 733 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
668 | pub struct MacroDef { | 734 | pub struct MacroDef { |
669 | pub(crate) id: MacroDefId, | 735 | pub(crate) id: MacroDefId, |
@@ -751,6 +817,16 @@ impl AssocItem { | |||
751 | } | 817 | } |
752 | } | 818 | } |
753 | 819 | ||
820 | impl HasVisibility for AssocItem { | ||
821 | fn visibility(&self, db: &impl HirDatabase) -> Visibility { | ||
822 | match self { | ||
823 | AssocItem::Function(f) => f.visibility(db), | ||
824 | AssocItem::Const(c) => c.visibility(db), | ||
825 | AssocItem::TypeAlias(t) => t.visibility(db), | ||
826 | } | ||
827 | } | ||
828 | } | ||
829 | |||
754 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | 830 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] |
755 | pub enum GenericDef { | 831 | pub enum GenericDef { |
756 | Function(Function), | 832 | Function(Function), |
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 965d185a4..3782a9984 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -6,7 +6,7 @@ use std::{cell::RefCell, fmt, iter::successors}; | |||
6 | 6 | ||
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | resolver::{self, HasResolver, Resolver}, | 8 | resolver::{self, HasResolver, Resolver}, |
9 | TraitId, | 9 | AsMacroCall, TraitId, |
10 | }; | 10 | }; |
11 | use hir_expand::ExpansionInfo; | 11 | use hir_expand::ExpansionInfo; |
12 | use ra_db::{FileId, FileRange}; | 12 | use ra_db::{FileId, FileRange}; |
@@ -70,6 +70,20 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
70 | Some(node) | 70 | Some(node) |
71 | } | 71 | } |
72 | 72 | ||
73 | pub fn expand_hypothetical( | ||
74 | &self, | ||
75 | actual_macro_call: &ast::MacroCall, | ||
76 | hypothetical_args: &ast::TokenTree, | ||
77 | token_to_map: SyntaxToken, | ||
78 | ) -> Option<(SyntaxNode, SyntaxToken)> { | ||
79 | let macro_call = | ||
80 | self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); | ||
81 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); | ||
82 | let macro_call_id = macro_call | ||
83 | .as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; | ||
84 | hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) | ||
85 | } | ||
86 | |||
73 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { | 87 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { |
74 | let parent = token.parent(); | 88 | let parent = token.parent(); |
75 | let parent = self.find_file(parent); | 89 | let parent = self.find_file(parent); |
@@ -104,6 +118,25 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
104 | node.ancestors_with_macros(self.db).map(|it| it.value) | 118 | node.ancestors_with_macros(self.db).map(|it| it.value) |
105 | } | 119 | } |
106 | 120 | ||
121 | pub fn ancestors_at_offset_with_macros( | ||
122 | &self, | ||
123 | node: &SyntaxNode, | ||
124 | offset: TextUnit, | ||
125 | ) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
126 | use itertools::Itertools; | ||
127 | node.token_at_offset(offset) | ||
128 | .map(|token| self.ancestors_with_macros(token.parent())) | ||
129 | .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) | ||
130 | } | ||
131 | |||
132 | pub fn find_node_at_offset_with_macros<N: AstNode>( | ||
133 | &self, | ||
134 | node: &SyntaxNode, | ||
135 | offset: TextUnit, | ||
136 | ) -> Option<N> { | ||
137 | self.ancestors_at_offset_with_macros(node, offset).find_map(N::cast) | ||
138 | } | ||
139 | |||
107 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | 140 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { |
108 | self.analyze(expr.syntax()).type_of(self.db, &expr) | 141 | self.analyze(expr.syntax()).type_of(self.db, &expr) |
109 | } | 142 | } |
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index fa25cc4fb..30a12337e 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml | |||
@@ -26,4 +26,4 @@ ra_cfg = { path = "../ra_cfg" } | |||
26 | tt = { path = "../ra_tt", package = "ra_tt" } | 26 | tt = { path = "../ra_tt", package = "ra_tt" } |
27 | 27 | ||
28 | [dev-dependencies] | 28 | [dev-dependencies] |
29 | insta = "0.13.1" | 29 | insta = "0.15.0" |
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index 9fc43f3fb..a72eb5369 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs | |||
@@ -7,13 +7,16 @@ use hir_expand::{ | |||
7 | AstId, InFile, | 7 | AstId, InFile, |
8 | }; | 8 | }; |
9 | use ra_prof::profile; | 9 | use ra_prof::profile; |
10 | use ra_syntax::ast::{self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner}; | 10 | use ra_syntax::ast::{ |
11 | self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner, | ||
12 | }; | ||
11 | 13 | ||
12 | use crate::{ | 14 | use crate::{ |
13 | db::DefDatabase, | 15 | db::DefDatabase, |
14 | path::{path, GenericArgs, Path}, | 16 | path::{path, GenericArgs, Path}, |
15 | src::HasSource, | 17 | src::HasSource, |
16 | type_ref::{Mutability, TypeBound, TypeRef}, | 18 | type_ref::{Mutability, TypeBound, TypeRef}, |
19 | visibility::RawVisibility, | ||
17 | AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule, | 20 | AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule, |
18 | ImplId, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc, | 21 | ImplId, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc, |
19 | }; | 22 | }; |
@@ -26,6 +29,7 @@ pub struct FunctionData { | |||
26 | /// True if the first param is `self`. This is relevant to decide whether this | 29 | /// True if the first param is `self`. This is relevant to decide whether this |
27 | /// can be called as a method. | 30 | /// can be called as a method. |
28 | pub has_self_param: bool, | 31 | pub has_self_param: bool, |
32 | pub visibility: RawVisibility, | ||
29 | } | 33 | } |
30 | 34 | ||
31 | impl FunctionData { | 35 | impl FunctionData { |
@@ -72,7 +76,9 @@ impl FunctionData { | |||
72 | ret_type | 76 | ret_type |
73 | }; | 77 | }; |
74 | 78 | ||
75 | let sig = FunctionData { name, params, ret_type, has_self_param }; | 79 | let visibility = RawVisibility::from_ast(db, src.map(|s| s.visibility())); |
80 | |||
81 | let sig = FunctionData { name, params, ret_type, has_self_param, visibility }; | ||
76 | Arc::new(sig) | 82 | Arc::new(sig) |
77 | } | 83 | } |
78 | } | 84 | } |
@@ -91,6 +97,7 @@ fn desugar_future_path(orig: TypeRef) -> Path { | |||
91 | pub struct TypeAliasData { | 97 | pub struct TypeAliasData { |
92 | pub name: Name, | 98 | pub name: Name, |
93 | pub type_ref: Option<TypeRef>, | 99 | pub type_ref: Option<TypeRef>, |
100 | pub visibility: RawVisibility, | ||
94 | } | 101 | } |
95 | 102 | ||
96 | impl TypeAliasData { | 103 | impl TypeAliasData { |
@@ -98,10 +105,11 @@ impl TypeAliasData { | |||
98 | db: &impl DefDatabase, | 105 | db: &impl DefDatabase, |
99 | typ: TypeAliasId, | 106 | typ: TypeAliasId, |
100 | ) -> Arc<TypeAliasData> { | 107 | ) -> Arc<TypeAliasData> { |
101 | let node = typ.lookup(db).source(db).value; | 108 | let node = typ.lookup(db).source(db); |
102 | let name = node.name().map_or_else(Name::missing, |n| n.as_name()); | 109 | let name = node.value.name().map_or_else(Name::missing, |n| n.as_name()); |
103 | let type_ref = node.type_ref().map(TypeRef::from_ast); | 110 | let type_ref = node.value.type_ref().map(TypeRef::from_ast); |
104 | Arc::new(TypeAliasData { name, type_ref }) | 111 | let visibility = RawVisibility::from_ast(db, node.map(|n| n.visibility())); |
112 | Arc::new(TypeAliasData { name, type_ref, visibility }) | ||
105 | } | 113 | } |
106 | } | 114 | } |
107 | 115 | ||
@@ -217,23 +225,28 @@ pub struct ConstData { | |||
217 | /// const _: () = (); | 225 | /// const _: () = (); |
218 | pub name: Option<Name>, | 226 | pub name: Option<Name>, |
219 | pub type_ref: TypeRef, | 227 | pub type_ref: TypeRef, |
228 | pub visibility: RawVisibility, | ||
220 | } | 229 | } |
221 | 230 | ||
222 | impl ConstData { | 231 | impl ConstData { |
223 | pub(crate) fn const_data_query(db: &impl DefDatabase, konst: ConstId) -> Arc<ConstData> { | 232 | pub(crate) fn const_data_query(db: &impl DefDatabase, konst: ConstId) -> Arc<ConstData> { |
224 | let node = konst.lookup(db).source(db).value; | 233 | let node = konst.lookup(db).source(db); |
225 | Arc::new(ConstData::new(&node)) | 234 | Arc::new(ConstData::new(db, node)) |
226 | } | 235 | } |
227 | 236 | ||
228 | pub(crate) fn static_data_query(db: &impl DefDatabase, konst: StaticId) -> Arc<ConstData> { | 237 | pub(crate) fn static_data_query(db: &impl DefDatabase, konst: StaticId) -> Arc<ConstData> { |
229 | let node = konst.lookup(db).source(db).value; | 238 | let node = konst.lookup(db).source(db); |
230 | Arc::new(ConstData::new(&node)) | 239 | Arc::new(ConstData::new(db, node)) |
231 | } | 240 | } |
232 | 241 | ||
233 | fn new<N: NameOwner + TypeAscriptionOwner>(node: &N) -> ConstData { | 242 | fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>( |
234 | let name = node.name().map(|n| n.as_name()); | 243 | db: &impl DefDatabase, |
235 | let type_ref = TypeRef::from_ast_opt(node.ascribed_type()); | 244 | node: InFile<N>, |
236 | ConstData { name, type_ref } | 245 | ) -> ConstData { |
246 | let name = node.value.name().map(|n| n.as_name()); | ||
247 | let type_ref = TypeRef::from_ast_opt(node.value.ascribed_type()); | ||
248 | let visibility = RawVisibility::from_ast(db, node.map(|n| n.visibility())); | ||
249 | ConstData { name, type_ref, visibility } | ||
237 | } | 250 | } |
238 | } | 251 | } |
239 | 252 | ||
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 43b9b124a..07ca74ec3 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs | |||
@@ -176,7 +176,7 @@ fn find_importable_locations( | |||
176 | // directly (only through reexports in direct dependencies). | 176 | // directly (only through reexports in direct dependencies). |
177 | for krate in Some(from.krate) | 177 | for krate in Some(from.krate) |
178 | .into_iter() | 178 | .into_iter() |
179 | .chain(crate_graph.dependencies(from.krate).map(|dep| dep.crate_id)) | 179 | .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) |
180 | { | 180 | { |
181 | result.extend( | 181 | result.extend( |
182 | importable_locations_in_crate(db, item, krate) | 182 | importable_locations_in_crate(db, item, krate) |
diff --git a/crates/ra_hir_def/src/lang_item.rs b/crates/ra_hir_def/src/lang_item.rs index 5a336ea1f..6de49730e 100644 --- a/crates/ra_hir_def/src/lang_item.rs +++ b/crates/ra_hir_def/src/lang_item.rs | |||
@@ -116,8 +116,9 @@ impl LangItems { | |||
116 | if let Some(target) = start_crate_target { | 116 | if let Some(target) = start_crate_target { |
117 | return Some(*target); | 117 | return Some(*target); |
118 | } | 118 | } |
119 | db.crate_graph() | 119 | db.crate_graph()[start_crate] |
120 | .dependencies(start_crate) | 120 | .dependencies |
121 | .iter() | ||
121 | .find_map(|dep| db.lang_item(dep.crate_id, item.clone())) | 122 | .find_map(|dep| db.lang_item(dep.crate_id, item.clone())) |
122 | } | 123 | } |
123 | 124 | ||
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index 628c44c99..81eac52ad 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs | |||
@@ -177,10 +177,10 @@ pub struct ModuleData { | |||
177 | 177 | ||
178 | impl CrateDefMap { | 178 | impl CrateDefMap { |
179 | pub(crate) fn crate_def_map_query(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { | 179 | pub(crate) fn crate_def_map_query(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { |
180 | let _p = profile("crate_def_map_query"); | 180 | let _p = profile("crate_def_map_query") |
181 | .detail(|| db.crate_graph()[krate].display_name.clone().unwrap_or_default()); | ||
181 | let def_map = { | 182 | let def_map = { |
182 | let crate_graph = db.crate_graph(); | 183 | let edition = db.crate_graph()[krate].edition; |
183 | let edition = crate_graph.edition(krate); | ||
184 | let mut modules: Arena<LocalModuleId, ModuleData> = Arena::default(); | 184 | let mut modules: Arena<LocalModuleId, ModuleData> = Arena::default(); |
185 | let root = modules.alloc(ModuleData::default()); | 185 | let root = modules.alloc(ModuleData::default()); |
186 | CrateDefMap { | 186 | CrateDefMap { |
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 51c65a5d7..d0459d9b0 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -34,7 +34,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C | |||
34 | let crate_graph = db.crate_graph(); | 34 | let crate_graph = db.crate_graph(); |
35 | 35 | ||
36 | // populate external prelude | 36 | // populate external prelude |
37 | for dep in crate_graph.dependencies(def_map.krate) { | 37 | for dep in &crate_graph[def_map.krate].dependencies { |
38 | let dep_def_map = db.crate_def_map(dep.crate_id); | 38 | let dep_def_map = db.crate_def_map(dep.crate_id); |
39 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); | 39 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); |
40 | def_map.extern_prelude.insert( | 40 | def_map.extern_prelude.insert( |
@@ -51,7 +51,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C | |||
51 | } | 51 | } |
52 | } | 52 | } |
53 | 53 | ||
54 | let cfg_options = crate_graph.cfg_options(def_map.krate); | 54 | let cfg_options = &crate_graph[def_map.krate].cfg_options; |
55 | 55 | ||
56 | let mut collector = DefCollector { | 56 | let mut collector = DefCollector { |
57 | db, | 57 | db, |
@@ -128,8 +128,7 @@ where | |||
128 | DB: DefDatabase, | 128 | DB: DefDatabase, |
129 | { | 129 | { |
130 | fn collect(&mut self) { | 130 | fn collect(&mut self) { |
131 | let crate_graph = self.db.crate_graph(); | 131 | let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; |
132 | let file_id = crate_graph.crate_root(self.def_map.krate); | ||
133 | let raw_items = self.db.raw_items(file_id.into()); | 132 | let raw_items = self.db.raw_items(file_id.into()); |
134 | let module_id = self.def_map.root; | 133 | let module_id = self.def_map.root; |
135 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; | 134 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; |
@@ -955,7 +954,7 @@ mod tests { | |||
955 | let krate = db.test_crate(); | 954 | let krate = db.test_crate(); |
956 | 955 | ||
957 | let def_map = { | 956 | let def_map = { |
958 | let edition = db.crate_graph().edition(krate); | 957 | let edition = db.crate_graph()[krate].edition; |
959 | let mut modules: Arena<LocalModuleId, ModuleData> = Arena::default(); | 958 | let mut modules: Arena<LocalModuleId, ModuleData> = Arena::default(); |
960 | let root = modules.alloc(ModuleData::default()); | 959 | let root = modules.alloc(ModuleData::default()); |
961 | CrateDefMap { | 960 | CrateDefMap { |
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 9fc33e4b1..3f60b1cca 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -7,6 +7,7 @@ use crate::{ | |||
7 | 7 | ||
8 | use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId}; | 8 | use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId}; |
9 | use either::Either; | 9 | use either::Either; |
10 | use mbe::parse_to_token_tree; | ||
10 | use ra_db::{FileId, RelativePath}; | 11 | use ra_db::{FileId, RelativePath}; |
11 | use ra_parser::FragmentKind; | 12 | use ra_parser::FragmentKind; |
12 | 13 | ||
@@ -142,7 +143,10 @@ fn env_expand( | |||
142 | _tt: &tt::Subtree, | 143 | _tt: &tt::Subtree, |
143 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 144 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
144 | // dummy implementation for type-checking purposes | 145 | // dummy implementation for type-checking purposes |
145 | let expanded = quote! { "" }; | 146 | // we cannot use an empty string here, because for |
147 | // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become | ||
148 | // `include!("foo.rs"), which maybe infinite loop | ||
149 | let expanded = quote! { "__RA_UNIMPLEMENTATED__" }; | ||
146 | 150 | ||
147 | Ok(expanded) | 151 | Ok(expanded) |
148 | } | 152 | } |
@@ -276,7 +280,12 @@ fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Opti | |||
276 | let call_site = call_id.as_file().original_file(db); | 280 | let call_site = call_id.as_file().original_file(db); |
277 | let path = RelativePath::new(&path); | 281 | let path = RelativePath::new(&path); |
278 | 282 | ||
279 | db.resolve_relative_path(call_site, &path) | 283 | let res = db.resolve_relative_path(call_site, &path)?; |
284 | // Prevent include itself | ||
285 | if res == call_site { | ||
286 | return None; | ||
287 | } | ||
288 | Some(res) | ||
280 | } | 289 | } |
281 | 290 | ||
282 | fn include_expand( | 291 | fn include_expand( |
@@ -298,10 +307,9 @@ fn include_expand( | |||
298 | 307 | ||
299 | // FIXME: | 308 | // FIXME: |
300 | // Handle include as expression | 309 | // Handle include as expression |
301 | let node = | 310 | let res = parse_to_token_tree(&db.file_text(file_id.into())) |
302 | db.parse_or_expand(file_id.into()).ok_or_else(|| mbe::ExpandError::ConversionError)?; | 311 | .ok_or_else(|| mbe::ExpandError::ConversionError)? |
303 | let res = | 312 | .0; |
304 | mbe::syntax_node_to_token_tree(&node).ok_or_else(|| mbe::ExpandError::ConversionError)?.0; | ||
305 | 313 | ||
306 | Ok((res, FragmentKind::Items)) | 314 | Ok((res, FragmentKind::Items)) |
307 | } | 315 | } |
@@ -394,7 +402,7 @@ mod tests { | |||
394 | "#, | 402 | "#, |
395 | ); | 403 | ); |
396 | 404 | ||
397 | assert_eq!(expanded, "\"\""); | 405 | assert_eq!(expanded, "\"__RA_UNIMPLEMENTATED__\""); |
398 | } | 406 | } |
399 | 407 | ||
400 | #[test] | 408 | #[test] |
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index f3a84cacc..29dde3d80 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs | |||
@@ -72,6 +72,30 @@ pub trait AstDatabase: SourceDatabase { | |||
72 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | 72 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; |
73 | } | 73 | } |
74 | 74 | ||
75 | /// This expands the given macro call, but with different arguments. This is | ||
76 | /// used for completion, where we want to see what 'would happen' if we insert a | ||
77 | /// token. The `token_to_map` mapped down into the expansion, with the mapped | ||
78 | /// token returned. | ||
79 | pub fn expand_hypothetical( | ||
80 | db: &impl AstDatabase, | ||
81 | actual_macro_call: MacroCallId, | ||
82 | hypothetical_args: &ra_syntax::ast::TokenTree, | ||
83 | token_to_map: ra_syntax::SyntaxToken, | ||
84 | ) -> Option<(SyntaxNode, ra_syntax::SyntaxToken)> { | ||
85 | let macro_file = MacroFile { macro_call_id: actual_macro_call }; | ||
86 | let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax()).unwrap(); | ||
87 | let range = | ||
88 | token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?; | ||
89 | let token_id = tmap_1.token_by_range(range)?; | ||
90 | let macro_def = expander(db, actual_macro_call)?; | ||
91 | let (node, tmap_2) = | ||
92 | parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1))))?; | ||
93 | let token_id = macro_def.0.map_id_down(token_id); | ||
94 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; | ||
95 | let token = ra_syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; | ||
96 | Some((node.syntax_node(), token)) | ||
97 | } | ||
98 | |||
75 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | 99 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { |
76 | let map = | 100 | let map = |
77 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); | 101 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); |
@@ -130,15 +154,42 @@ pub(crate) fn macro_expand( | |||
130 | db: &dyn AstDatabase, | 154 | db: &dyn AstDatabase, |
131 | id: MacroCallId, | 155 | id: MacroCallId, |
132 | ) -> Result<Arc<tt::Subtree>, String> { | 156 | ) -> Result<Arc<tt::Subtree>, String> { |
157 | macro_expand_with_arg(db, id, None) | ||
158 | } | ||
159 | |||
160 | fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | ||
161 | let lazy_id = match id { | ||
162 | MacroCallId::LazyMacro(id) => id, | ||
163 | MacroCallId::EagerMacro(_id) => { | ||
164 | return None; | ||
165 | } | ||
166 | }; | ||
167 | |||
168 | let loc = db.lookup_intern_macro(lazy_id); | ||
169 | let macro_rules = db.macro_def(loc.def)?; | ||
170 | Some(macro_rules) | ||
171 | } | ||
172 | |||
173 | fn macro_expand_with_arg( | ||
174 | db: &dyn AstDatabase, | ||
175 | id: MacroCallId, | ||
176 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | ||
177 | ) -> Result<Arc<tt::Subtree>, String> { | ||
133 | let lazy_id = match id { | 178 | let lazy_id = match id { |
134 | MacroCallId::LazyMacro(id) => id, | 179 | MacroCallId::LazyMacro(id) => id, |
135 | MacroCallId::EagerMacro(id) => { | 180 | MacroCallId::EagerMacro(id) => { |
136 | return Ok(db.lookup_intern_eager_expansion(id).subtree); | 181 | if arg.is_some() { |
182 | return Err( | ||
183 | "hypothetical macro expansion not implemented for eager macro".to_owned() | ||
184 | ); | ||
185 | } else { | ||
186 | return Ok(db.lookup_intern_eager_expansion(id).subtree); | ||
187 | } | ||
137 | } | 188 | } |
138 | }; | 189 | }; |
139 | 190 | ||
140 | let loc = db.lookup_intern_macro(lazy_id); | 191 | let loc = db.lookup_intern_macro(lazy_id); |
141 | let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; | 192 | let macro_arg = arg.or_else(|| db.macro_arg(id)).ok_or("Fail to args in to tt::TokenTree")?; |
142 | 193 | ||
143 | let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; | 194 | let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; |
144 | let tt = macro_rules.0.expand(db, lazy_id, ¯o_arg.0).map_err(|err| format!("{:?}", err))?; | 195 | let tt = macro_rules.0.expand(db, lazy_id, ¯o_arg.0).map_err(|err| format!("{:?}", err))?; |
@@ -163,11 +214,23 @@ pub(crate) fn parse_macro( | |||
163 | db: &dyn AstDatabase, | 214 | db: &dyn AstDatabase, |
164 | macro_file: MacroFile, | 215 | macro_file: MacroFile, |
165 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | 216 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { |
217 | parse_macro_with_arg(db, macro_file, None) | ||
218 | } | ||
219 | |||
220 | pub fn parse_macro_with_arg( | ||
221 | db: &dyn AstDatabase, | ||
222 | macro_file: MacroFile, | ||
223 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | ||
224 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | ||
166 | let _p = profile("parse_macro_query"); | 225 | let _p = profile("parse_macro_query"); |
167 | 226 | ||
168 | let macro_call_id = macro_file.macro_call_id; | 227 | let macro_call_id = macro_file.macro_call_id; |
169 | let tt = db | 228 | let expansion = if let Some(arg) = arg { |
170 | .macro_expand(macro_call_id) | 229 | macro_expand_with_arg(db, macro_call_id, Some(arg)) |
230 | } else { | ||
231 | db.macro_expand(macro_call_id) | ||
232 | }; | ||
233 | let tt = expansion | ||
171 | .map_err(|err| { | 234 | .map_err(|err| { |
172 | // Note: | 235 | // Note: |
173 | // The final goal we would like to make all parse_macro success, | 236 | // The final goal we would like to make all parse_macro success, |
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index f2558b579..9962112db 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -26,4 +26,4 @@ chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "177d713 | |||
26 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "177d71340acc7a7204a33115fc63075d86452179" } | 26 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "177d71340acc7a7204a33115fc63075d86452179" } |
27 | 27 | ||
28 | [dev-dependencies] | 28 | [dev-dependencies] |
29 | insta = "0.13.1" | 29 | insta = "0.15.0" |
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 42814941f..ffa78b046 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs | |||
@@ -484,6 +484,51 @@ fn bar() -> u32 {0} | |||
484 | } | 484 | } |
485 | 485 | ||
486 | #[test] | 486 | #[test] |
487 | fn infer_builtin_macros_include_concat_with_bad_env_should_failed() { | ||
488 | let (db, pos) = TestDB::with_position( | ||
489 | r#" | ||
490 | //- /main.rs | ||
491 | #[rustc_builtin_macro] | ||
492 | macro_rules! include {() => {}} | ||
493 | |||
494 | #[rustc_builtin_macro] | ||
495 | macro_rules! concat {() => {}} | ||
496 | |||
497 | #[rustc_builtin_macro] | ||
498 | macro_rules! env {() => {}} | ||
499 | |||
500 | include!(concat!(env!("OUT_DIR"), "/foo.rs")); | ||
501 | |||
502 | fn main() { | ||
503 | bar()<|>; | ||
504 | } | ||
505 | |||
506 | //- /foo.rs | ||
507 | fn bar() -> u32 {0} | ||
508 | "#, | ||
509 | ); | ||
510 | assert_eq!("{unknown}", type_at_pos(&db, pos)); | ||
511 | } | ||
512 | |||
513 | #[test] | ||
514 | fn infer_builtin_macros_include_itself_should_failed() { | ||
515 | let (db, pos) = TestDB::with_position( | ||
516 | r#" | ||
517 | //- /main.rs | ||
518 | #[rustc_builtin_macro] | ||
519 | macro_rules! include {() => {}} | ||
520 | |||
521 | include!("main.rs"); | ||
522 | |||
523 | fn main() { | ||
524 | 0<|> | ||
525 | } | ||
526 | "#, | ||
527 | ); | ||
528 | assert_eq!("i32", type_at_pos(&db, pos)); | ||
529 | } | ||
530 | |||
531 | #[test] | ||
487 | fn infer_builtin_macros_concat_with_lazy() { | 532 | fn infer_builtin_macros_concat_with_lazy() { |
488 | assert_snapshot!( | 533 | assert_snapshot!( |
489 | infer(r#" | 534 | infer(r#" |
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 8de588790..6e1c8e42a 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs | |||
@@ -47,7 +47,7 @@ pub(crate) fn impls_for_trait_query( | |||
47 | // will only ever get called for a few crates near the root of the tree (the | 47 | // will only ever get called for a few crates near the root of the tree (the |
48 | // ones the user is editing), so this may actually be a waste of memory. I'm | 48 | // ones the user is editing), so this may actually be a waste of memory. I'm |
49 | // doing it like this mainly for simplicity for now. | 49 | // doing it like this mainly for simplicity for now. |
50 | for dep in db.crate_graph().dependencies(krate) { | 50 | for dep in &db.crate_graph()[krate].dependencies { |
51 | impls.extend(db.impls_for_trait(dep.crate_id, trait_).iter()); | 51 | impls.extend(db.impls_for_trait(dep.crate_id, trait_).iter()); |
52 | } | 52 | } |
53 | let crate_impl_defs = db.impls_in_crate(krate); | 53 | let crate_impl_defs = db.impls_in_crate(krate); |
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index 410d8de62..7235c944c 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml | |||
@@ -35,4 +35,4 @@ ra_assists = { path = "../ra_assists" } | |||
35 | hir = { path = "../ra_hir", package = "ra_hir" } | 35 | hir = { path = "../ra_hir", package = "ra_hir" } |
36 | 36 | ||
37 | [dev-dependencies] | 37 | [dev-dependencies] |
38 | insta = "0.13.1" | 38 | insta = "0.15.0" |
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 9145aa183..f275305e2 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -38,7 +38,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
38 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 38 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { |
39 | for receiver in receiver.autoderef(ctx.db) { | 39 | for receiver in receiver.autoderef(ctx.db) { |
40 | for (field, ty) in receiver.fields(ctx.db) { | 40 | for (field, ty) in receiver.fields(ctx.db) { |
41 | if ctx.module.map_or(false, |m| !field.is_visible_from(ctx.db, m)) { | 41 | if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { |
42 | // Skip private field. FIXME: If the definition location of the | 42 | // Skip private field. FIXME: If the definition location of the |
43 | // field is editable, we should show the completion | 43 | // field is editable, we should show the completion |
44 | continue; | 44 | continue; |
@@ -53,11 +53,14 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty | |||
53 | } | 53 | } |
54 | 54 | ||
55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { |
56 | if let Some(krate) = ctx.module.map(|it| it.krate()) { | 56 | if let Some(krate) = ctx.krate { |
57 | let mut seen_methods = FxHashSet::default(); | 57 | let mut seen_methods = FxHashSet::default(); |
58 | let traits_in_scope = ctx.scope().traits_in_scope(); | 58 | let traits_in_scope = ctx.scope().traits_in_scope(); |
59 | receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { | 59 | receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { |
60 | if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { | 60 | if func.has_self_param(ctx.db) |
61 | && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m)) | ||
62 | && seen_methods.insert(func.name(ctx.db)) | ||
63 | { | ||
61 | acc.add_function(ctx, func); | 64 | acc.add_function(ctx, func); |
62 | } | 65 | } |
63 | None::<()> | 66 | None::<()> |
@@ -308,6 +311,39 @@ mod tests { | |||
308 | } | 311 | } |
309 | 312 | ||
310 | #[test] | 313 | #[test] |
314 | fn test_method_completion_private() { | ||
315 | assert_debug_snapshot!( | ||
316 | do_ref_completion( | ||
317 | r" | ||
318 | struct A {} | ||
319 | mod m { | ||
320 | impl super::A { | ||
321 | fn private_method(&self) {} | ||
322 | pub(super) fn the_method(&self) {} | ||
323 | } | ||
324 | } | ||
325 | fn foo(a: A) { | ||
326 | a.<|> | ||
327 | } | ||
328 | ", | ||
329 | ), | ||
330 | @r###" | ||
331 | [ | ||
332 | CompletionItem { | ||
333 | label: "the_method()", | ||
334 | source_range: [256; 256), | ||
335 | delete: [256; 256), | ||
336 | insert: "the_method()$0", | ||
337 | kind: Method, | ||
338 | lookup: "the_method", | ||
339 | detail: "pub(super) fn the_method(&self)", | ||
340 | }, | ||
341 | ] | ||
342 | "### | ||
343 | ); | ||
344 | } | ||
345 | |||
346 | #[test] | ||
311 | fn test_trait_method_completion() { | 347 | fn test_trait_method_completion() { |
312 | assert_debug_snapshot!( | 348 | assert_debug_snapshot!( |
313 | do_ref_completion( | 349 | do_ref_completion( |
@@ -584,4 +620,102 @@ mod tests { | |||
584 | "### | 620 | "### |
585 | ); | 621 | ); |
586 | } | 622 | } |
623 | |||
624 | #[test] | ||
625 | fn works_in_simple_macro_1() { | ||
626 | assert_debug_snapshot!( | ||
627 | do_ref_completion( | ||
628 | r" | ||
629 | macro_rules! m { ($e:expr) => { $e } } | ||
630 | struct A { the_field: u32 } | ||
631 | fn foo(a: A) { | ||
632 | m!(a.x<|>) | ||
633 | } | ||
634 | ", | ||
635 | ), | ||
636 | @r###" | ||
637 | [ | ||
638 | CompletionItem { | ||
639 | label: "the_field", | ||
640 | source_range: [156; 157), | ||
641 | delete: [156; 157), | ||
642 | insert: "the_field", | ||
643 | kind: Field, | ||
644 | detail: "u32", | ||
645 | }, | ||
646 | ] | ||
647 | "### | ||
648 | ); | ||
649 | } | ||
650 | |||
651 | #[test] | ||
652 | fn works_in_simple_macro_recursive() { | ||
653 | assert_debug_snapshot!( | ||
654 | do_ref_completion( | ||
655 | r" | ||
656 | macro_rules! m { ($e:expr) => { $e } } | ||
657 | struct A { the_field: u32 } | ||
658 | fn foo(a: A) { | ||
659 | m!(a.x<|>) | ||
660 | } | ||
661 | ", | ||
662 | ), | ||
663 | @r###" | ||
664 | [ | ||
665 | CompletionItem { | ||
666 | label: "the_field", | ||
667 | source_range: [156; 157), | ||
668 | delete: [156; 157), | ||
669 | insert: "the_field", | ||
670 | kind: Field, | ||
671 | detail: "u32", | ||
672 | }, | ||
673 | ] | ||
674 | "### | ||
675 | ); | ||
676 | } | ||
677 | |||
678 | #[test] | ||
679 | fn works_in_simple_macro_2() { | ||
680 | // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery | ||
681 | assert_debug_snapshot!( | ||
682 | do_ref_completion( | ||
683 | r" | ||
684 | macro_rules! m { ($e:expr) => { $e } } | ||
685 | struct A { the_field: u32 } | ||
686 | fn foo(a: A) { | ||
687 | m!(a.<|>) | ||
688 | } | ||
689 | ", | ||
690 | ), | ||
691 | @r###"[]"### | ||
692 | ); | ||
693 | } | ||
694 | |||
695 | #[test] | ||
696 | fn works_in_simple_macro_recursive_1() { | ||
697 | assert_debug_snapshot!( | ||
698 | do_ref_completion( | ||
699 | r" | ||
700 | macro_rules! m { ($e:expr) => { $e } } | ||
701 | struct A { the_field: u32 } | ||
702 | fn foo(a: A) { | ||
703 | m!(m!(m!(a.x<|>))) | ||
704 | } | ||
705 | ", | ||
706 | ), | ||
707 | @r###" | ||
708 | [ | ||
709 | CompletionItem { | ||
710 | label: "the_field", | ||
711 | source_range: [162; 163), | ||
712 | delete: [162; 163), | ||
713 | insert: "the_field", | ||
714 | kind: Field, | ||
715 | detail: "u32", | ||
716 | }, | ||
717 | ] | ||
718 | "### | ||
719 | ); | ||
720 | } | ||
587 | } | 721 | } |
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index eb7cd9ac2..e1c0ffb1f 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs | |||
@@ -79,6 +79,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
79 | } | 79 | } |
80 | 80 | ||
81 | fn is_in_loop_body(leaf: &SyntaxToken) -> bool { | 81 | fn is_in_loop_body(leaf: &SyntaxToken) -> bool { |
82 | // FIXME move this to CompletionContext and make it handle macros | ||
82 | for node in leaf.parent().ancestors() { | 83 | for node in leaf.parent().ancestors() { |
83 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { | 84 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { |
84 | break; | 85 | break; |
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index 1a9699466..3c4a70561 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! Completion of paths, including when writing a single name. | 1 | //! Completion of paths, i.e. `some::prefix::<|>`. |
2 | 2 | ||
3 | use hir::{Adt, PathResolution, ScopeDef}; | 3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::AstNode; |
5 | use test_utils::tested_by; | 5 | use test_utils::tested_by; |
6 | 6 | ||
@@ -15,9 +15,10 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
15 | Some(PathResolution::Def(def)) => def, | 15 | Some(PathResolution::Def(def)) => def, |
16 | _ => return, | 16 | _ => return, |
17 | }; | 17 | }; |
18 | let context_module = ctx.scope().module(); | ||
18 | match def { | 19 | match def { |
19 | hir::ModuleDef::Module(module) => { | 20 | hir::ModuleDef::Module(module) => { |
20 | let module_scope = module.scope(ctx.db); | 21 | let module_scope = module.scope(ctx.db, context_module); |
21 | for (name, def) in module_scope { | 22 | for (name, def) in module_scope { |
22 | if ctx.use_item_syntax.is_some() { | 23 | if ctx.use_item_syntax.is_some() { |
23 | if let ScopeDef::Unknown = def { | 24 | if let ScopeDef::Unknown = def { |
@@ -47,10 +48,13 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
47 | }; | 48 | }; |
48 | // Iterate assoc types separately | 49 | // Iterate assoc types separately |
49 | // FIXME: complete T::AssocType | 50 | // FIXME: complete T::AssocType |
50 | let krate = ctx.module.map(|m| m.krate()); | 51 | let krate = ctx.krate; |
51 | if let Some(krate) = krate { | 52 | if let Some(krate) = krate { |
52 | let traits_in_scope = ctx.scope().traits_in_scope(); | 53 | let traits_in_scope = ctx.scope().traits_in_scope(); |
53 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | 54 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { |
55 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
56 | return None; | ||
57 | } | ||
54 | match item { | 58 | match item { |
55 | hir::AssocItem::Function(func) => { | 59 | hir::AssocItem::Function(func) => { |
56 | if !func.has_self_param(ctx.db) { | 60 | if !func.has_self_param(ctx.db) { |
@@ -64,6 +68,9 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
64 | }); | 68 | }); |
65 | 69 | ||
66 | ty.iterate_impl_items(ctx.db, krate, |item| { | 70 | ty.iterate_impl_items(ctx.db, krate, |item| { |
71 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
72 | return None; | ||
73 | } | ||
67 | match item { | 74 | match item { |
68 | hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} | 75 | hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} |
69 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | 76 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), |
@@ -74,6 +81,9 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
74 | } | 81 | } |
75 | hir::ModuleDef::Trait(t) => { | 82 | hir::ModuleDef::Trait(t) => { |
76 | for item in t.items(ctx.db) { | 83 | for item in t.items(ctx.db) { |
84 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
85 | continue; | ||
86 | } | ||
77 | match item { | 87 | match item { |
78 | hir::AssocItem::Function(func) => { | 88 | hir::AssocItem::Function(func) => { |
79 | if !func.has_self_param(ctx.db) { | 89 | if !func.has_self_param(ctx.db) { |
@@ -170,6 +180,41 @@ mod tests { | |||
170 | } | 180 | } |
171 | 181 | ||
172 | #[test] | 182 | #[test] |
183 | fn path_visibility() { | ||
184 | assert_debug_snapshot!( | ||
185 | do_reference_completion( | ||
186 | r" | ||
187 | use self::my::<|>; | ||
188 | |||
189 | mod my { | ||
190 | struct Bar; | ||
191 | pub struct Foo; | ||
192 | pub use Bar as PublicBar; | ||
193 | } | ||
194 | " | ||
195 | ), | ||
196 | @r###" | ||
197 | [ | ||
198 | CompletionItem { | ||
199 | label: "Foo", | ||
200 | source_range: [31; 31), | ||
201 | delete: [31; 31), | ||
202 | insert: "Foo", | ||
203 | kind: Struct, | ||
204 | }, | ||
205 | CompletionItem { | ||
206 | label: "PublicBar", | ||
207 | source_range: [31; 31), | ||
208 | delete: [31; 31), | ||
209 | insert: "PublicBar", | ||
210 | kind: Struct, | ||
211 | }, | ||
212 | ] | ||
213 | "### | ||
214 | ); | ||
215 | } | ||
216 | |||
217 | #[test] | ||
173 | fn completes_use_item_starting_with_self() { | 218 | fn completes_use_item_starting_with_self() { |
174 | assert_debug_snapshot!( | 219 | assert_debug_snapshot!( |
175 | do_reference_completion( | 220 | do_reference_completion( |
@@ -177,7 +222,7 @@ mod tests { | |||
177 | use self::m::<|>; | 222 | use self::m::<|>; |
178 | 223 | ||
179 | mod m { | 224 | mod m { |
180 | struct Bar; | 225 | pub struct Bar; |
181 | } | 226 | } |
182 | " | 227 | " |
183 | ), | 228 | ), |
@@ -502,6 +547,60 @@ mod tests { | |||
502 | } | 547 | } |
503 | 548 | ||
504 | #[test] | 549 | #[test] |
550 | fn associated_item_visibility() { | ||
551 | assert_debug_snapshot!( | ||
552 | do_reference_completion( | ||
553 | " | ||
554 | //- /lib.rs | ||
555 | struct S; | ||
556 | |||
557 | mod m { | ||
558 | impl super::S { | ||
559 | pub(super) fn public_method() { } | ||
560 | fn private_method() { } | ||
561 | pub(super) type PublicType = u32; | ||
562 | type PrivateType = u32; | ||
563 | pub(super) const PUBLIC_CONST: u32 = 1; | ||
564 | const PRIVATE_CONST: u32 = 1; | ||
565 | } | ||
566 | } | ||
567 | |||
568 | fn foo() { let _ = S::<|> } | ||
569 | " | ||
570 | ), | ||
571 | @r###" | ||
572 | [ | ||
573 | CompletionItem { | ||
574 | label: "PUBLIC_CONST", | ||
575 | source_range: [302; 302), | ||
576 | delete: [302; 302), | ||
577 | insert: "PUBLIC_CONST", | ||
578 | kind: Const, | ||
579 | detail: "pub(super) const PUBLIC_CONST: u32 = 1;", | ||
580 | }, | ||
581 | CompletionItem { | ||
582 | label: "PublicType", | ||
583 | source_range: [302; 302), | ||
584 | delete: [302; 302), | ||
585 | insert: "PublicType", | ||
586 | kind: TypeAlias, | ||
587 | detail: "pub(super) type PublicType = u32;", | ||
588 | }, | ||
589 | CompletionItem { | ||
590 | label: "public_method()", | ||
591 | source_range: [302; 302), | ||
592 | delete: [302; 302), | ||
593 | insert: "public_method()$0", | ||
594 | kind: Function, | ||
595 | lookup: "public_method", | ||
596 | detail: "pub(super) fn public_method()", | ||
597 | }, | ||
598 | ] | ||
599 | "### | ||
600 | ); | ||
601 | } | ||
602 | |||
603 | #[test] | ||
505 | fn completes_enum_associated_method() { | 604 | fn completes_enum_associated_method() { |
506 | assert_debug_snapshot!( | 605 | assert_debug_snapshot!( |
507 | do_reference_completion( | 606 | do_reference_completion( |
@@ -835,4 +934,37 @@ mod tests { | |||
835 | "### | 934 | "### |
836 | ); | 935 | ); |
837 | } | 936 | } |
937 | |||
938 | #[test] | ||
939 | fn completes_in_simple_macro_call() { | ||
940 | let completions = do_reference_completion( | ||
941 | r#" | ||
942 | macro_rules! m { ($e:expr) => { $e } } | ||
943 | fn main() { m!(self::f<|>); } | ||
944 | fn foo() {} | ||
945 | "#, | ||
946 | ); | ||
947 | assert_debug_snapshot!(completions, @r###" | ||
948 | [ | ||
949 | CompletionItem { | ||
950 | label: "foo()", | ||
951 | source_range: [93; 94), | ||
952 | delete: [93; 94), | ||
953 | insert: "foo()$0", | ||
954 | kind: Function, | ||
955 | lookup: "foo", | ||
956 | detail: "fn foo()", | ||
957 | }, | ||
958 | CompletionItem { | ||
959 | label: "main()", | ||
960 | source_range: [93; 94), | ||
961 | delete: [93; 94), | ||
962 | insert: "main()$0", | ||
963 | kind: Function, | ||
964 | lookup: "main", | ||
965 | detail: "fn main()", | ||
966 | }, | ||
967 | ] | ||
968 | "###); | ||
969 | } | ||
838 | } | 970 | } |
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs index c2c6ca002..fa8aeceda 100644 --- a/crates/ra_ide/src/completion/complete_pattern.rs +++ b/crates/ra_ide/src/completion/complete_pattern.rs | |||
@@ -86,4 +86,22 @@ mod tests { | |||
86 | ] | 86 | ] |
87 | "###); | 87 | "###); |
88 | } | 88 | } |
89 | |||
90 | #[test] | ||
91 | fn completes_in_simple_macro_call() { | ||
92 | // FIXME: doesn't work yet because of missing error recovery in macro expansion | ||
93 | let completions = complete( | ||
94 | r" | ||
95 | macro_rules! m { ($e:expr) => { $e } } | ||
96 | enum E { X } | ||
97 | |||
98 | fn foo() { | ||
99 | m!(match E::X { | ||
100 | <|> | ||
101 | }) | ||
102 | } | ||
103 | ", | ||
104 | ); | ||
105 | assert_debug_snapshot!(completions, @r###"[]"###); | ||
106 | } | ||
89 | } | 107 | } |
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 8a74f993a..65ecea125 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs | |||
@@ -67,8 +67,8 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
67 | 67 | ||
68 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { | 68 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { |
69 | let edit = { | 69 | let edit = { |
70 | let receiver_range = | 70 | let receiver_syntax = ctx.dot_receiver.as_ref().expect("no receiver available").syntax(); |
71 | ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range(); | 71 | let receiver_range = ctx.sema.original_range(receiver_syntax).range; |
72 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); | 72 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); |
73 | TextEdit::replace(delete_range, snippet.to_string()) | 73 | TextEdit::replace(delete_range, snippet.to_string()) |
74 | }; | 74 | }; |
@@ -279,4 +279,65 @@ mod tests { | |||
279 | "### | 279 | "### |
280 | ); | 280 | ); |
281 | } | 281 | } |
282 | |||
283 | #[test] | ||
284 | fn works_in_simple_macro() { | ||
285 | assert_debug_snapshot!( | ||
286 | do_postfix_completion( | ||
287 | r#" | ||
288 | macro_rules! m { ($e:expr) => { $e } } | ||
289 | fn main() { | ||
290 | let bar: u8 = 12; | ||
291 | m!(bar.b<|>) | ||
292 | } | ||
293 | "#, | ||
294 | ), | ||
295 | @r###" | ||
296 | [ | ||
297 | CompletionItem { | ||
298 | label: "box", | ||
299 | source_range: [149; 150), | ||
300 | delete: [145; 150), | ||
301 | insert: "Box::new(bar)", | ||
302 | detail: "Box::new(expr)", | ||
303 | }, | ||
304 | CompletionItem { | ||
305 | label: "dbg", | ||
306 | source_range: [149; 150), | ||
307 | delete: [145; 150), | ||
308 | insert: "dbg!(bar)", | ||
309 | detail: "dbg!(expr)", | ||
310 | }, | ||
311 | CompletionItem { | ||
312 | label: "match", | ||
313 | source_range: [149; 150), | ||
314 | delete: [145; 150), | ||
315 | insert: "match bar {\n ${1:_} => {$0\\},\n}", | ||
316 | detail: "match expr {}", | ||
317 | }, | ||
318 | CompletionItem { | ||
319 | label: "not", | ||
320 | source_range: [149; 150), | ||
321 | delete: [145; 150), | ||
322 | insert: "!bar", | ||
323 | detail: "!expr", | ||
324 | }, | ||
325 | CompletionItem { | ||
326 | label: "ref", | ||
327 | source_range: [149; 150), | ||
328 | delete: [145; 150), | ||
329 | insert: "&bar", | ||
330 | detail: "&expr", | ||
331 | }, | ||
332 | CompletionItem { | ||
333 | label: "refm", | ||
334 | source_range: [149; 150), | ||
335 | delete: [145; 150), | ||
336 | insert: "&mut bar", | ||
337 | detail: "&mut expr", | ||
338 | }, | ||
339 | ] | ||
340 | "### | ||
341 | ); | ||
342 | } | ||
282 | } | 343 | } |
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs index f98353d76..be6e4194f 100644 --- a/crates/ra_ide/src/completion/complete_record_literal.rs +++ b/crates/ra_ide/src/completion/complete_record_literal.rs | |||
@@ -153,4 +153,29 @@ mod tests { | |||
153 | ] | 153 | ] |
154 | "###); | 154 | "###); |
155 | } | 155 | } |
156 | |||
157 | #[test] | ||
158 | fn test_record_literal_field_in_simple_macro() { | ||
159 | let completions = complete( | ||
160 | r" | ||
161 | macro_rules! m { ($e:expr) => { $e } } | ||
162 | struct A { the_field: u32 } | ||
163 | fn foo() { | ||
164 | m!(A { the<|> }) | ||
165 | } | ||
166 | ", | ||
167 | ); | ||
168 | assert_debug_snapshot!(completions, @r###" | ||
169 | [ | ||
170 | CompletionItem { | ||
171 | label: "the_field", | ||
172 | source_range: [137; 140), | ||
173 | delete: [137; 140), | ||
174 | insert: "the_field", | ||
175 | kind: Field, | ||
176 | detail: "u32", | ||
177 | }, | ||
178 | ] | ||
179 | "###); | ||
180 | } | ||
156 | } | 181 | } |
diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs index 9bdeae49f..687c57d3e 100644 --- a/crates/ra_ide/src/completion/complete_record_pattern.rs +++ b/crates/ra_ide/src/completion/complete_record_pattern.rs | |||
@@ -87,4 +87,32 @@ mod tests { | |||
87 | ] | 87 | ] |
88 | "###); | 88 | "###); |
89 | } | 89 | } |
90 | |||
91 | #[test] | ||
92 | fn test_record_pattern_field_in_simple_macro() { | ||
93 | let completions = complete( | ||
94 | r" | ||
95 | macro_rules! m { ($e:expr) => { $e } } | ||
96 | struct S { foo: u32 } | ||
97 | |||
98 | fn process(f: S) { | ||
99 | m!(match f { | ||
100 | S { f<|>: 92 } => (), | ||
101 | }) | ||
102 | } | ||
103 | ", | ||
104 | ); | ||
105 | assert_debug_snapshot!(completions, @r###" | ||
106 | [ | ||
107 | CompletionItem { | ||
108 | label: "foo", | ||
109 | source_range: [171; 172), | ||
110 | delete: [171; 172), | ||
111 | insert: "foo", | ||
112 | kind: Field, | ||
113 | detail: "u32", | ||
114 | }, | ||
115 | ] | ||
116 | "###); | ||
117 | } | ||
90 | } | 118 | } |
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 2b9a0e556..eb3c8cf1b 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | 4 | ||
@@ -797,4 +797,72 @@ mod tests { | |||
797 | "### | 797 | "### |
798 | ) | 798 | ) |
799 | } | 799 | } |
800 | |||
801 | #[test] | ||
802 | fn completes_in_simple_macro_1() { | ||
803 | assert_debug_snapshot!( | ||
804 | do_reference_completion( | ||
805 | r" | ||
806 | macro_rules! m { ($e:expr) => { $e } } | ||
807 | fn quux(x: i32) { | ||
808 | let y = 92; | ||
809 | m!(<|>); | ||
810 | } | ||
811 | " | ||
812 | ), | ||
813 | @"[]" | ||
814 | ); | ||
815 | } | ||
816 | |||
817 | #[test] | ||
818 | fn completes_in_simple_macro_2() { | ||
819 | assert_debug_snapshot!( | ||
820 | do_reference_completion( | ||
821 | r" | ||
822 | macro_rules! m { ($e:expr) => { $e } } | ||
823 | fn quux(x: i32) { | ||
824 | let y = 92; | ||
825 | m!(x<|>); | ||
826 | } | ||
827 | " | ||
828 | ), | ||
829 | @r###" | ||
830 | [ | ||
831 | CompletionItem { | ||
832 | label: "m!", | ||
833 | source_range: [145; 146), | ||
834 | delete: [145; 146), | ||
835 | insert: "m!($0)", | ||
836 | kind: Macro, | ||
837 | detail: "macro_rules! m", | ||
838 | }, | ||
839 | CompletionItem { | ||
840 | label: "quux(…)", | ||
841 | source_range: [145; 146), | ||
842 | delete: [145; 146), | ||
843 | insert: "quux(${1:x})$0", | ||
844 | kind: Function, | ||
845 | lookup: "quux", | ||
846 | detail: "fn quux(x: i32)", | ||
847 | }, | ||
848 | CompletionItem { | ||
849 | label: "x", | ||
850 | source_range: [145; 146), | ||
851 | delete: [145; 146), | ||
852 | insert: "x", | ||
853 | kind: Binding, | ||
854 | detail: "i32", | ||
855 | }, | ||
856 | CompletionItem { | ||
857 | label: "y", | ||
858 | source_range: [145; 146), | ||
859 | delete: [145; 146), | ||
860 | insert: "y", | ||
861 | kind: Binding, | ||
862 | detail: "i32", | ||
863 | }, | ||
864 | ] | ||
865 | "### | ||
866 | ); | ||
867 | } | ||
800 | } | 868 | } |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 9aa5a705d..40535c09e 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -5,7 +5,7 @@ use ra_db::SourceDatabase; | |||
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{find_covering_element, find_node_at_offset}, |
8 | ast, AstNode, SourceFile, | 8 | ast, AstNode, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNode, SyntaxToken, TextRange, TextUnit, | 10 | SyntaxNode, SyntaxToken, TextRange, TextUnit, |
11 | }; | 11 | }; |
@@ -20,8 +20,11 @@ pub(crate) struct CompletionContext<'a> { | |||
20 | pub(super) sema: Semantics<'a, RootDatabase>, | 20 | pub(super) sema: Semantics<'a, RootDatabase>, |
21 | pub(super) db: &'a RootDatabase, | 21 | pub(super) db: &'a RootDatabase, |
22 | pub(super) offset: TextUnit, | 22 | pub(super) offset: TextUnit, |
23 | /// The token before the cursor, in the original file. | ||
24 | pub(super) original_token: SyntaxToken, | ||
25 | /// The token before the cursor, in the macro-expanded file. | ||
23 | pub(super) token: SyntaxToken, | 26 | pub(super) token: SyntaxToken, |
24 | pub(super) module: Option<hir::Module>, | 27 | pub(super) krate: Option<hir::Crate>, |
25 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 28 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
26 | pub(super) function_syntax: Option<ast::FnDef>, | 29 | pub(super) function_syntax: Option<ast::FnDef>, |
27 | pub(super) use_item_syntax: Option<ast::UseItem>, | 30 | pub(super) use_item_syntax: Option<ast::UseItem>, |
@@ -67,15 +70,20 @@ impl<'a> CompletionContext<'a> { | |||
67 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); | 70 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); |
68 | parse.reparse(&edit).tree() | 71 | parse.reparse(&edit).tree() |
69 | }; | 72 | }; |
73 | let fake_ident_token = | ||
74 | file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap(); | ||
70 | 75 | ||
71 | let module = sema.to_module_def(position.file_id); | 76 | let krate = sema.to_module_def(position.file_id).map(|m| m.krate()); |
72 | let token = original_file.syntax().token_at_offset(position.offset).left_biased()?; | 77 | let original_token = |
78 | original_file.syntax().token_at_offset(position.offset).left_biased()?; | ||
79 | let token = sema.descend_into_macros(original_token.clone()); | ||
73 | let mut ctx = CompletionContext { | 80 | let mut ctx = CompletionContext { |
74 | sema, | 81 | sema, |
75 | db, | 82 | db, |
83 | original_token, | ||
76 | token, | 84 | token, |
77 | offset: position.offset, | 85 | offset: position.offset, |
78 | module, | 86 | krate, |
79 | name_ref_syntax: None, | 87 | name_ref_syntax: None, |
80 | function_syntax: None, | 88 | function_syntax: None, |
81 | use_item_syntax: None, | 89 | use_item_syntax: None, |
@@ -95,15 +103,57 @@ impl<'a> CompletionContext<'a> { | |||
95 | has_type_args: false, | 103 | has_type_args: false, |
96 | dot_receiver_is_ambiguous_float_literal: false, | 104 | dot_receiver_is_ambiguous_float_literal: false, |
97 | }; | 105 | }; |
98 | ctx.fill(&original_file, file_with_fake_ident, position.offset); | 106 | |
107 | let mut original_file = original_file.syntax().clone(); | ||
108 | let mut hypothetical_file = file_with_fake_ident.syntax().clone(); | ||
109 | let mut offset = position.offset; | ||
110 | let mut fake_ident_token = fake_ident_token; | ||
111 | |||
112 | // Are we inside a macro call? | ||
113 | while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( | ||
114 | find_node_at_offset::<ast::MacroCall>(&original_file, offset), | ||
115 | find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset), | ||
116 | ) { | ||
117 | if actual_macro_call.path().as_ref().map(|s| s.syntax().text()) | ||
118 | != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text()) | ||
119 | { | ||
120 | break; | ||
121 | } | ||
122 | let hypothetical_args = match macro_call_with_fake_ident.token_tree() { | ||
123 | Some(tt) => tt, | ||
124 | None => break, | ||
125 | }; | ||
126 | if let (Some(actual_expansion), Some(hypothetical_expansion)) = ( | ||
127 | ctx.sema.expand(&actual_macro_call), | ||
128 | ctx.sema.expand_hypothetical( | ||
129 | &actual_macro_call, | ||
130 | &hypothetical_args, | ||
131 | fake_ident_token, | ||
132 | ), | ||
133 | ) { | ||
134 | let new_offset = hypothetical_expansion.1.text_range().start(); | ||
135 | if new_offset >= actual_expansion.text_range().end() { | ||
136 | break; | ||
137 | } | ||
138 | original_file = actual_expansion; | ||
139 | hypothetical_file = hypothetical_expansion.0; | ||
140 | fake_ident_token = hypothetical_expansion.1; | ||
141 | offset = new_offset; | ||
142 | } else { | ||
143 | break; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | ctx.fill(&original_file, hypothetical_file, offset); | ||
99 | Some(ctx) | 148 | Some(ctx) |
100 | } | 149 | } |
101 | 150 | ||
102 | // The range of the identifier that is being completed. | 151 | // The range of the identifier that is being completed. |
103 | pub(crate) fn source_range(&self) -> TextRange { | 152 | pub(crate) fn source_range(&self) -> TextRange { |
153 | // check kind of macro-expanded token, but use range of original token | ||
104 | match self.token.kind() { | 154 | match self.token.kind() { |
105 | // workaroud when completion is triggered by trigger characters. | 155 | // workaroud when completion is triggered by trigger characters. |
106 | IDENT => self.token.text_range(), | 156 | IDENT => self.original_token.text_range(), |
107 | _ => TextRange::offset_len(self.offset, 0.into()), | 157 | _ => TextRange::offset_len(self.offset, 0.into()), |
108 | } | 158 | } |
109 | } | 159 | } |
@@ -114,27 +164,24 @@ impl<'a> CompletionContext<'a> { | |||
114 | 164 | ||
115 | fn fill( | 165 | fn fill( |
116 | &mut self, | 166 | &mut self, |
117 | original_file: &ast::SourceFile, | 167 | original_file: &SyntaxNode, |
118 | file_with_fake_ident: ast::SourceFile, | 168 | file_with_fake_ident: SyntaxNode, |
119 | offset: TextUnit, | 169 | offset: TextUnit, |
120 | ) { | 170 | ) { |
121 | // First, let's try to complete a reference to some declaration. | 171 | // First, let's try to complete a reference to some declaration. |
122 | if let Some(name_ref) = | 172 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { |
123 | find_node_at_offset::<ast::NameRef>(file_with_fake_ident.syntax(), offset) | ||
124 | { | ||
125 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | 173 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. |
126 | // See RFC#1685. | 174 | // See RFC#1685. |
127 | if is_node::<ast::Param>(name_ref.syntax()) { | 175 | if is_node::<ast::Param>(name_ref.syntax()) { |
128 | self.is_param = true; | 176 | self.is_param = true; |
129 | return; | 177 | return; |
130 | } | 178 | } |
131 | self.classify_name_ref(original_file, name_ref); | 179 | self.classify_name_ref(original_file, name_ref, offset); |
132 | } | 180 | } |
133 | 181 | ||
134 | // Otherwise, see if this is a declaration. We can use heuristics to | 182 | // Otherwise, see if this is a declaration. We can use heuristics to |
135 | // suggest declaration names, see `CompletionKind::Magic`. | 183 | // suggest declaration names, see `CompletionKind::Magic`. |
136 | if let Some(name) = find_node_at_offset::<ast::Name>(file_with_fake_ident.syntax(), offset) | 184 | if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) { |
137 | { | ||
138 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { | 185 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { |
139 | let parent = bind_pat.syntax().parent(); | 186 | let parent = bind_pat.syntax().parent(); |
140 | if parent.clone().and_then(ast::MatchArm::cast).is_some() | 187 | if parent.clone().and_then(ast::MatchArm::cast).is_some() |
@@ -148,23 +195,29 @@ impl<'a> CompletionContext<'a> { | |||
148 | return; | 195 | return; |
149 | } | 196 | } |
150 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { | 197 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { |
151 | self.record_lit_pat = find_node_at_offset(original_file.syntax(), self.offset); | 198 | self.record_lit_pat = |
199 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
152 | } | 200 | } |
153 | } | 201 | } |
154 | } | 202 | } |
155 | 203 | ||
156 | fn classify_name_ref(&mut self, original_file: &SourceFile, name_ref: ast::NameRef) { | 204 | fn classify_name_ref( |
205 | &mut self, | ||
206 | original_file: &SyntaxNode, | ||
207 | name_ref: ast::NameRef, | ||
208 | offset: TextUnit, | ||
209 | ) { | ||
157 | self.name_ref_syntax = | 210 | self.name_ref_syntax = |
158 | find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); | 211 | find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); |
159 | let name_range = name_ref.syntax().text_range(); | 212 | let name_range = name_ref.syntax().text_range(); |
160 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { | 213 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { |
161 | self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); | 214 | self.record_lit_syntax = |
215 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
162 | } | 216 | } |
163 | 217 | ||
164 | self.impl_def = self | 218 | self.impl_def = self |
165 | .token | 219 | .sema |
166 | .parent() | 220 | .ancestors_with_macros(self.token.parent()) |
167 | .ancestors() | ||
168 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 221 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
169 | .find_map(ast::ImplDef::cast); | 222 | .find_map(ast::ImplDef::cast); |
170 | 223 | ||
@@ -183,12 +236,12 @@ impl<'a> CompletionContext<'a> { | |||
183 | _ => (), | 236 | _ => (), |
184 | } | 237 | } |
185 | 238 | ||
186 | self.use_item_syntax = self.token.parent().ancestors().find_map(ast::UseItem::cast); | 239 | self.use_item_syntax = |
240 | self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast); | ||
187 | 241 | ||
188 | self.function_syntax = self | 242 | self.function_syntax = self |
189 | .token | 243 | .sema |
190 | .parent() | 244 | .ancestors_with_macros(self.token.parent()) |
191 | .ancestors() | ||
192 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 245 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
193 | .find_map(ast::FnDef::cast); | 246 | .find_map(ast::FnDef::cast); |
194 | 247 | ||
@@ -242,7 +295,7 @@ impl<'a> CompletionContext<'a> { | |||
242 | 295 | ||
243 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { | 296 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { |
244 | if let Some(if_expr) = | 297 | if let Some(if_expr) = |
245 | find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off) | 298 | self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off) |
246 | { | 299 | { |
247 | if if_expr.syntax().text_range().end() | 300 | if if_expr.syntax().text_range().end() |
248 | < name_ref.syntax().text_range().start() | 301 | < name_ref.syntax().text_range().start() |
@@ -259,7 +312,7 @@ impl<'a> CompletionContext<'a> { | |||
259 | self.dot_receiver = field_expr | 312 | self.dot_receiver = field_expr |
260 | .expr() | 313 | .expr() |
261 | .map(|e| e.syntax().text_range()) | 314 | .map(|e| e.syntax().text_range()) |
262 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 315 | .and_then(|r| find_node_with_range(original_file, r)); |
263 | self.dot_receiver_is_ambiguous_float_literal = | 316 | self.dot_receiver_is_ambiguous_float_literal = |
264 | if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { | 317 | if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { |
265 | match l.kind() { | 318 | match l.kind() { |
@@ -275,7 +328,7 @@ impl<'a> CompletionContext<'a> { | |||
275 | self.dot_receiver = method_call_expr | 328 | self.dot_receiver = method_call_expr |
276 | .expr() | 329 | .expr() |
277 | .map(|e| e.syntax().text_range()) | 330 | .map(|e| e.syntax().text_range()) |
278 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 331 | .and_then(|r| find_node_with_range(original_file, r)); |
279 | self.is_call = true; | 332 | self.is_call = true; |
280 | } | 333 | } |
281 | } | 334 | } |
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs index 1c26a8697..eaeaaa2b4 100644 --- a/crates/ra_ide/src/display.rs +++ b/crates/ra_ide/src/display.rs | |||
@@ -68,17 +68,23 @@ pub(crate) fn macro_label(node: &ast::MacroCall) -> String { | |||
68 | } | 68 | } |
69 | 69 | ||
70 | pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { | 70 | pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { |
71 | rust_code_markup_with_doc::<_, &str>(val, None) | 71 | rust_code_markup_with_doc::<_, &str>(val, None, None) |
72 | } | 72 | } |
73 | 73 | ||
74 | pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String | 74 | pub(crate) fn rust_code_markup_with_doc<CODE, DOC>( |
75 | val: CODE, | ||
76 | doc: Option<DOC>, | ||
77 | mod_path: Option<String>, | ||
78 | ) -> String | ||
75 | where | 79 | where |
76 | CODE: AsRef<str>, | 80 | CODE: AsRef<str>, |
77 | DOC: AsRef<str>, | 81 | DOC: AsRef<str>, |
78 | { | 82 | { |
83 | let mod_path = | ||
84 | mod_path.filter(|path| !path.is_empty()).map(|path| path + "\n").unwrap_or_default(); | ||
79 | if let Some(doc) = doc { | 85 | if let Some(doc) = doc { |
80 | format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref()) | 86 | format!("```rust\n{}{}\n```\n\n{}", mod_path, val.as_ref(), doc.as_ref()) |
81 | } else { | 87 | } else { |
82 | format!("```rust\n{}\n```", val.as_ref()) | 88 | format!("```rust\n{}{}\n```", mod_path, val.as_ref()) |
83 | } | 89 | } |
84 | } | 90 | } |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index e9c682557..25e038a55 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -1,6 +1,10 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Adt, HasSource, HirDisplay, Semantics}; | 3 | use hir::{ |
4 | Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, | ||
5 | ModuleSource, Semantics, | ||
6 | }; | ||
7 | use ra_db::SourceDatabase; | ||
4 | use ra_ide_db::{ | 8 | use ra_ide_db::{ |
5 | defs::{classify_name, classify_name_ref, Definition}, | 9 | defs::{classify_name, classify_name_ref, Definition}, |
6 | RootDatabase, | 10 | RootDatabase, |
@@ -16,6 +20,8 @@ use crate::{ | |||
16 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, | 20 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, |
17 | FilePosition, RangeInfo, | 21 | FilePosition, RangeInfo, |
18 | }; | 22 | }; |
23 | use itertools::Itertools; | ||
24 | use std::iter::once; | ||
19 | 25 | ||
20 | /// Contains the results when hovering over an item | 26 | /// Contains the results when hovering over an item |
21 | #[derive(Debug, Clone)] | 27 | #[derive(Debug, Clone)] |
@@ -83,44 +89,86 @@ impl HoverResult { | |||
83 | } | 89 | } |
84 | } | 90 | } |
85 | 91 | ||
86 | fn hover_text(docs: Option<String>, desc: Option<String>) -> Option<String> { | 92 | fn hover_text( |
87 | match (desc, docs) { | 93 | docs: Option<String>, |
88 | (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)), | 94 | desc: Option<String>, |
89 | (None, Some(docs)) => Some(docs), | 95 | mod_path: Option<String>, |
96 | ) -> Option<String> { | ||
97 | match (desc, docs, mod_path) { | ||
98 | (Some(desc), docs, mod_path) => Some(rust_code_markup_with_doc(desc, docs, mod_path)), | ||
99 | (None, Some(docs), _) => Some(docs), | ||
100 | _ => None, | ||
101 | } | ||
102 | } | ||
103 | |||
104 | fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> { | ||
105 | match def { | ||
106 | Definition::StructField(f) => Some(f.parent_def(db).name(db)), | ||
107 | Definition::Local(l) => l.parent(db).name(db), | ||
108 | Definition::ModuleDef(md) => match md { | ||
109 | ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) { | ||
110 | AssocItemContainer::Trait(t) => Some(t.name(db)), | ||
111 | AssocItemContainer::ImplDef(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)), | ||
112 | }, | ||
113 | ModuleDef::EnumVariant(e) => Some(e.parent_enum(db).name(db)), | ||
114 | _ => None, | ||
115 | }, | ||
116 | Definition::SelfType(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)), | ||
90 | _ => None, | 117 | _ => None, |
91 | } | 118 | } |
119 | .map(|name| name.to_string()) | ||
120 | } | ||
121 | |||
122 | fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { | ||
123 | let mod_path = def.module(db).map(|module| { | ||
124 | once(db.crate_graph()[module.krate().into()].display_name.clone()) | ||
125 | .chain( | ||
126 | module | ||
127 | .path_to_root(db) | ||
128 | .into_iter() | ||
129 | .rev() | ||
130 | .map(|it| it.name(db).map(|name| name.to_string())), | ||
131 | ) | ||
132 | .chain(once(definition_owner_name(db, def))) | ||
133 | .flatten() | ||
134 | .join("::") | ||
135 | }); | ||
136 | mod_path | ||
92 | } | 137 | } |
93 | 138 | ||
94 | fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { | 139 | fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { |
140 | let mod_path = determine_mod_path(db, &def); | ||
95 | return match def { | 141 | return match def { |
96 | Definition::Macro(it) => { | 142 | Definition::Macro(it) => { |
97 | let src = it.source(db); | 143 | let src = it.source(db); |
98 | hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value))) | 144 | hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) |
99 | } | 145 | } |
100 | Definition::StructField(it) => { | 146 | Definition::StructField(it) => { |
101 | let src = it.source(db); | 147 | let src = it.source(db); |
102 | match src.value { | 148 | match src.value { |
103 | hir::FieldSource::Named(it) => hover_text(it.doc_comment_text(), it.short_label()), | 149 | FieldSource::Named(it) => { |
150 | hover_text(it.doc_comment_text(), it.short_label(), mod_path) | ||
151 | } | ||
104 | _ => None, | 152 | _ => None, |
105 | } | 153 | } |
106 | } | 154 | } |
107 | Definition::ModuleDef(it) => match it { | 155 | Definition::ModuleDef(it) => match it { |
108 | hir::ModuleDef::Module(it) => match it.definition_source(db).value { | 156 | ModuleDef::Module(it) => match it.definition_source(db).value { |
109 | hir::ModuleSource::Module(it) => { | 157 | ModuleSource::Module(it) => { |
110 | hover_text(it.doc_comment_text(), it.short_label()) | 158 | hover_text(it.doc_comment_text(), it.short_label(), mod_path) |
111 | } | 159 | } |
112 | _ => None, | 160 | _ => None, |
113 | }, | 161 | }, |
114 | hir::ModuleDef::Function(it) => from_def_source(db, it), | 162 | ModuleDef::Function(it) => from_def_source(db, it, mod_path), |
115 | hir::ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it), | 163 | ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it, mod_path), |
116 | hir::ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it), | 164 | ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it, mod_path), |
117 | hir::ModuleDef::Adt(Adt::Enum(it)) => from_def_source(db, it), | 165 | ModuleDef::Adt(Adt::Enum(it)) => from_def_source(db, it, mod_path), |
118 | hir::ModuleDef::EnumVariant(it) => from_def_source(db, it), | 166 | ModuleDef::EnumVariant(it) => from_def_source(db, it, mod_path), |
119 | hir::ModuleDef::Const(it) => from_def_source(db, it), | 167 | ModuleDef::Const(it) => from_def_source(db, it, mod_path), |
120 | hir::ModuleDef::Static(it) => from_def_source(db, it), | 168 | ModuleDef::Static(it) => from_def_source(db, it, mod_path), |
121 | hir::ModuleDef::Trait(it) => from_def_source(db, it), | 169 | ModuleDef::Trait(it) => from_def_source(db, it, mod_path), |
122 | hir::ModuleDef::TypeAlias(it) => from_def_source(db, it), | 170 | ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), |
123 | hir::ModuleDef::BuiltinType(it) => Some(it.to_string()), | 171 | ModuleDef::BuiltinType(it) => Some(it.to_string()), |
124 | }, | 172 | }, |
125 | Definition::Local(it) => { | 173 | Definition::Local(it) => { |
126 | Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string())) | 174 | Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string())) |
@@ -131,13 +179,13 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin | |||
131 | } | 179 | } |
132 | }; | 180 | }; |
133 | 181 | ||
134 | fn from_def_source<A, D>(db: &RootDatabase, def: D) -> Option<String> | 182 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> |
135 | where | 183 | where |
136 | D: HasSource<Ast = A>, | 184 | D: HasSource<Ast = A>, |
137 | A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, | 185 | A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, |
138 | { | 186 | { |
139 | let src = def.source(db); | 187 | let src = def.source(db); |
140 | hover_text(src.value.doc_comment_text(), src.value.short_label()) | 188 | hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) |
141 | } | 189 | } |
142 | } | 190 | } |
143 | 191 | ||
@@ -345,7 +393,7 @@ mod tests { | |||
345 | }; | 393 | }; |
346 | } | 394 | } |
347 | "#, | 395 | "#, |
348 | &["field_a: u32"], | 396 | &["Foo\nfield_a: u32"], |
349 | ); | 397 | ); |
350 | 398 | ||
351 | // Hovering over the field in the definition | 399 | // Hovering over the field in the definition |
@@ -362,7 +410,7 @@ mod tests { | |||
362 | }; | 410 | }; |
363 | } | 411 | } |
364 | "#, | 412 | "#, |
365 | &["field_a: u32"], | 413 | &["Foo\nfield_a: u32"], |
366 | ); | 414 | ); |
367 | } | 415 | } |
368 | 416 | ||
@@ -415,7 +463,7 @@ fn main() { | |||
415 | ", | 463 | ", |
416 | ); | 464 | ); |
417 | let hover = analysis.hover(position).unwrap().unwrap(); | 465 | let hover = analysis.hover(position).unwrap().unwrap(); |
418 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Some")); | 466 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\nSome")); |
419 | 467 | ||
420 | let (analysis, position) = single_file_with_position( | 468 | let (analysis, position) = single_file_with_position( |
421 | " | 469 | " |
@@ -442,6 +490,7 @@ fn main() { | |||
442 | } | 490 | } |
443 | "#, | 491 | "#, |
444 | &[" | 492 | &[" |
493 | Option | ||
445 | None | 494 | None |
446 | ``` | 495 | ``` |
447 | 496 | ||
@@ -462,6 +511,7 @@ The None variant | |||
462 | } | 511 | } |
463 | "#, | 512 | "#, |
464 | &[" | 513 | &[" |
514 | Option | ||
465 | Some | 515 | Some |
466 | ``` | 516 | ``` |
467 | 517 | ||
@@ -528,21 +578,23 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
528 | fn test_hover_infer_associated_method_exact() { | 578 | fn test_hover_infer_associated_method_exact() { |
529 | let (analysis, position) = single_file_with_position( | 579 | let (analysis, position) = single_file_with_position( |
530 | " | 580 | " |
531 | struct Thing { x: u32 } | 581 | mod wrapper { |
582 | struct Thing { x: u32 } | ||
532 | 583 | ||
533 | impl Thing { | 584 | impl Thing { |
534 | fn new() -> Thing { | 585 | fn new() -> Thing { |
535 | Thing { x: 0 } | 586 | Thing { x: 0 } |
587 | } | ||
536 | } | 588 | } |
537 | } | 589 | } |
538 | 590 | ||
539 | fn main() { | 591 | fn main() { |
540 | let foo_test = Thing::new<|>(); | 592 | let foo_test = wrapper::Thing::new<|>(); |
541 | } | 593 | } |
542 | ", | 594 | ", |
543 | ); | 595 | ); |
544 | let hover = analysis.hover(position).unwrap().unwrap(); | 596 | let hover = analysis.hover(position).unwrap().unwrap(); |
545 | assert_eq!(trim_markup_opt(hover.info.first()), Some("fn new() -> Thing")); | 597 | assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing")); |
546 | assert_eq!(hover.info.is_exact(), true); | 598 | assert_eq!(hover.info.is_exact(), true); |
547 | } | 599 | } |
548 | 600 | ||
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 69098a630..cf0cbdbd0 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -119,6 +119,12 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ | |||
119 | return true; | 119 | return true; |
120 | } | 120 | } |
121 | 121 | ||
122 | if let Some(Adt::Struct(s)) = pat_ty.as_adt() { | ||
123 | if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.syntax().to_string() { | ||
124 | return true; | ||
125 | } | ||
126 | } | ||
127 | |||
122 | for node in bind_pat.syntax().ancestors() { | 128 | for node in bind_pat.syntax().ancestors() { |
123 | match_ast! { | 129 | match_ast! { |
124 | match node { | 130 | match node { |
@@ -943,4 +949,30 @@ fn main() { | |||
943 | "### | 949 | "### |
944 | ); | 950 | ); |
945 | } | 951 | } |
952 | |||
953 | #[test] | ||
954 | fn unit_structs_have_no_type_hints() { | ||
955 | let (analysis, file_id) = single_file( | ||
956 | r#" | ||
957 | enum CustomResult<T, E> { | ||
958 | Ok(T), | ||
959 | Err(E), | ||
960 | } | ||
961 | use CustomResult::*; | ||
962 | |||
963 | struct SyntheticSyntax; | ||
964 | |||
965 | fn main() { | ||
966 | match Ok(()) { | ||
967 | Ok(_) => (), | ||
968 | Err(SyntheticSyntax) => (), | ||
969 | } | ||
970 | }"#, | ||
971 | ); | ||
972 | |||
973 | assert_debug_snapshot!(analysis.inlay_hints(file_id, Some(8)).unwrap(), @r###" | ||
974 | [] | ||
975 | "### | ||
976 | ); | ||
977 | } | ||
946 | } | 978 | } |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 4dfe0553e..c60e86aea 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -211,7 +211,13 @@ impl Analysis { | |||
211 | // Default to enable test for single file. | 211 | // Default to enable test for single file. |
212 | let mut cfg_options = CfgOptions::default(); | 212 | let mut cfg_options = CfgOptions::default(); |
213 | cfg_options.insert_atom("test".into()); | 213 | cfg_options.insert_atom("test".into()); |
214 | crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options, Env::default()); | 214 | crate_graph.add_crate_root( |
215 | file_id, | ||
216 | Edition::Edition2018, | ||
217 | None, | ||
218 | cfg_options, | ||
219 | Env::default(), | ||
220 | ); | ||
215 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); | 221 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); |
216 | change.set_crate_graph(crate_graph); | 222 | change.set_crate_graph(crate_graph); |
217 | host.apply_change(change); | 223 | host.apply_change(change); |
@@ -415,12 +421,12 @@ impl Analysis { | |||
415 | 421 | ||
416 | /// Returns the edition of the given crate. | 422 | /// Returns the edition of the given crate. |
417 | pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable<Edition> { | 423 | pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable<Edition> { |
418 | self.with_db(|db| db.crate_graph().edition(crate_id)) | 424 | self.with_db(|db| db.crate_graph()[crate_id].edition) |
419 | } | 425 | } |
420 | 426 | ||
421 | /// Returns the root file of the given crate. | 427 | /// Returns the root file of the given crate. |
422 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { | 428 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { |
423 | self.with_db(|db| db.crate_graph().crate_root(crate_id)) | 429 | self.with_db(|db| db.crate_graph()[crate_id].root_file_id) |
424 | } | 430 | } |
425 | 431 | ||
426 | /// Returns the set of possible targets to run for the current file. | 432 | /// Returns the set of possible targets to run for the current file. |
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index f4cd6deb7..90f84b052 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs | |||
@@ -99,13 +99,19 @@ impl MockAnalysis { | |||
99 | root_crate = Some(crate_graph.add_crate_root( | 99 | root_crate = Some(crate_graph.add_crate_root( |
100 | file_id, | 100 | file_id, |
101 | Edition2018, | 101 | Edition2018, |
102 | None, | ||
102 | cfg_options, | 103 | cfg_options, |
103 | Env::default(), | 104 | Env::default(), |
104 | )); | 105 | )); |
105 | } else if path.ends_with("/lib.rs") { | 106 | } else if path.ends_with("/lib.rs") { |
106 | let other_crate = | ||
107 | crate_graph.add_crate_root(file_id, Edition2018, cfg_options, Env::default()); | ||
108 | let crate_name = path.parent().unwrap().file_name().unwrap(); | 107 | let crate_name = path.parent().unwrap().file_name().unwrap(); |
108 | let other_crate = crate_graph.add_crate_root( | ||
109 | file_id, | ||
110 | Edition2018, | ||
111 | Some(crate_name.to_owned()), | ||
112 | cfg_options, | ||
113 | Env::default(), | ||
114 | ); | ||
109 | if let Some(root_crate) = root_crate { | 115 | if let Some(root_crate) = root_crate { |
110 | crate_graph | 116 | crate_graph |
111 | .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate) | 117 | .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate) |
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index 2c4bdb039..b73cefd97 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs | |||
@@ -133,6 +133,7 @@ mod tests { | |||
133 | let crate_id = crate_graph.add_crate_root( | 133 | let crate_id = crate_graph.add_crate_root( |
134 | root_file, | 134 | root_file, |
135 | Edition2018, | 135 | Edition2018, |
136 | None, | ||
136 | CfgOptions::default(), | 137 | CfgOptions::default(), |
137 | Env::default(), | 138 | Env::default(), |
138 | ); | 139 | ); |
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml index 52f0f23df..de4f5bce0 100644 --- a/crates/ra_ide_db/Cargo.toml +++ b/crates/ra_ide_db/Cargo.toml | |||
@@ -13,7 +13,7 @@ wasm = [] | |||
13 | [dependencies] | 13 | [dependencies] |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | rayon = "1.3.0" | 15 | rayon = "1.3.0" |
16 | fst = { version = "0.3.5", default-features = false } | 16 | fst = { version = "0.4", default-features = false } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | superslice = "1.0.0" | 18 | superslice = "1.0.0" |
19 | once_cell = "1.3.1" | 19 | once_cell = "1.3.1" |
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs index 8b5be9d21..628cf6416 100644 --- a/crates/ra_ide_db/src/change.rs +++ b/crates/ra_ide_db/src/change.rs | |||
@@ -5,7 +5,7 @@ use std::{fmt, sync::Arc, time}; | |||
5 | 5 | ||
6 | use ra_db::{ | 6 | use ra_db::{ |
7 | salsa::{Database, Durability, SweepStrategy}, | 7 | salsa::{Database, Durability, SweepStrategy}, |
8 | CrateGraph, CrateId, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, | 8 | CrateGraph, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, |
9 | SourceRootId, | 9 | SourceRootId, |
10 | }; | 10 | }; |
11 | use ra_prof::{memory_usage, profile, Bytes}; | 11 | use ra_prof::{memory_usage, profile, Bytes}; |
@@ -88,10 +88,6 @@ impl AnalysisChange { | |||
88 | self.crate_graph = Some(graph); | 88 | self.crate_graph = Some(graph); |
89 | } | 89 | } |
90 | 90 | ||
91 | pub fn set_debug_crate_name(&mut self, crate_id: CrateId, name: String) { | ||
92 | self.debug_data.crate_names.insert(crate_id, name); | ||
93 | } | ||
94 | |||
95 | pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) { | 91 | pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) { |
96 | self.debug_data.root_paths.insert(source_root_id, path); | 92 | self.debug_data.root_paths.insert(source_root_id, path); |
97 | } | 93 | } |
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 79f48c9e3..a105c7556 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs | |||
@@ -131,12 +131,10 @@ fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> { | |||
131 | #[derive(Debug, Default, Clone)] | 131 | #[derive(Debug, Default, Clone)] |
132 | pub(crate) struct DebugData { | 132 | pub(crate) struct DebugData { |
133 | pub(crate) root_paths: FxHashMap<SourceRootId, String>, | 133 | pub(crate) root_paths: FxHashMap<SourceRootId, String>, |
134 | pub(crate) crate_names: FxHashMap<CrateId, String>, | ||
135 | } | 134 | } |
136 | 135 | ||
137 | impl DebugData { | 136 | impl DebugData { |
138 | pub(crate) fn merge(&mut self, other: DebugData) { | 137 | pub(crate) fn merge(&mut self, other: DebugData) { |
139 | self.root_paths.extend(other.root_paths.into_iter()); | 138 | self.root_paths.extend(other.root_paths.into_iter()); |
140 | self.crate_names.extend(other.crate_names.into_iter()); | ||
141 | } | 139 | } |
142 | } | 140 | } |
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs index e6b3126b6..884359ee3 100644 --- a/crates/ra_ide_db/src/symbol_index.rs +++ b/crates/ra_ide_db/src/symbol_index.rs | |||
@@ -163,7 +163,7 @@ pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymb | |||
163 | #[derive(Default)] | 163 | #[derive(Default)] |
164 | pub struct SymbolIndex { | 164 | pub struct SymbolIndex { |
165 | symbols: Vec<FileSymbol>, | 165 | symbols: Vec<FileSymbol>, |
166 | map: fst::Map, | 166 | map: fst::Map<Vec<u8>>, |
167 | } | 167 | } |
168 | 168 | ||
169 | impl fmt::Debug for SymbolIndex { | 169 | impl fmt::Debug for SymbolIndex { |
@@ -221,7 +221,7 @@ impl SymbolIndex { | |||
221 | builder.insert(key, value).unwrap(); | 221 | builder.insert(key, value).unwrap(); |
222 | } | 222 | } |
223 | 223 | ||
224 | let map = fst::Map::from_bytes(builder.into_inner().unwrap()).unwrap(); | 224 | let map = fst::Map::new(builder.into_inner().unwrap()).unwrap(); |
225 | SymbolIndex { symbols, map } | 225 | SymbolIndex { symbols, map } |
226 | } | 226 | } |
227 | 227 | ||
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index 2c6ae5658..43afe24cc 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -31,7 +31,8 @@ pub enum ExpandError { | |||
31 | } | 31 | } |
32 | 32 | ||
33 | pub use crate::syntax_bridge::{ | 33 | pub use crate::syntax_bridge::{ |
34 | ast_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node, TokenMap, | 34 | ast_to_token_tree, parse_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node, |
35 | TokenMap, | ||
35 | }; | 36 | }; |
36 | 37 | ||
37 | /// This struct contains AST for a single `macro_rules` definition. What might | 38 | /// This struct contains AST for a single `macro_rules` definition. What might |
diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs index ffba03898..49c53183a 100644 --- a/crates/ra_mbe/src/mbe_expander/matcher.rs +++ b/crates/ra_mbe/src/mbe_expander/matcher.rs | |||
@@ -247,6 +247,7 @@ impl<'a> TtIter<'a> { | |||
247 | ra_parser::parse_fragment(&mut src, &mut sink, fragment_kind); | 247 | ra_parser::parse_fragment(&mut src, &mut sink, fragment_kind); |
248 | 248 | ||
249 | if !sink.cursor.is_root() || sink.error { | 249 | if !sink.cursor.is_root() || sink.error { |
250 | // FIXME better recovery in this case would help completion inside macros immensely | ||
250 | return Err(()); | 251 | return Err(()); |
251 | } | 252 | } |
252 | 253 | ||
@@ -375,7 +376,8 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> Result<Option<Fragment>, Ex | |||
375 | return Ok(Some(Fragment::Tokens(tt))); | 376 | return Ok(Some(Fragment::Tokens(tt))); |
376 | } | 377 | } |
377 | }; | 378 | }; |
378 | let tt = input.expect_fragment(fragment).map_err(|()| err!())?; | 379 | let tt = |
380 | input.expect_fragment(fragment).map_err(|()| err!("fragment did not parse as {}", kind))?; | ||
379 | let fragment = if kind == "expr" { Fragment::Ast(tt) } else { Fragment::Tokens(tt) }; | 381 | let fragment = if kind == "expr" { Fragment::Ast(tt) } else { Fragment::Tokens(tt) }; |
380 | Ok(Some(fragment)) | 382 | Ok(Some(fragment)) |
381 | } | 383 | } |
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index fb9fa5314..fcb73fbc7 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs | |||
@@ -2,8 +2,10 @@ | |||
2 | 2 | ||
3 | use ra_parser::{FragmentKind, ParseError, TreeSink}; | 3 | use ra_parser::{FragmentKind, ParseError, TreeSink}; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxNode, | 5 | ast::{self, make::tokens::doc_comment}, |
6 | SyntaxTreeBuilder, TextRange, TextUnit, T, | 6 | tokenize, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, |
7 | SyntaxKind::*, | ||
8 | SyntaxNode, SyntaxTreeBuilder, TextRange, TextUnit, Token, T, | ||
7 | }; | 9 | }; |
8 | use rustc_hash::FxHashMap; | 10 | use rustc_hash::FxHashMap; |
9 | use std::iter::successors; | 11 | use std::iter::successors; |
@@ -48,9 +50,11 @@ pub fn ast_to_token_tree(ast: &impl ast::AstNode) -> Option<(tt::Subtree, TokenM | |||
48 | /// will consume). | 50 | /// will consume). |
49 | pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> Option<(tt::Subtree, TokenMap)> { | 51 | pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> Option<(tt::Subtree, TokenMap)> { |
50 | let global_offset = node.text_range().start(); | 52 | let global_offset = node.text_range().start(); |
51 | let mut c = Convertor { map: TokenMap::default(), global_offset, next_id: 0 }; | 53 | let mut c = Convertor { |
54 | id_alloc: { TokenIdAlloc { map: TokenMap::default(), global_offset, next_id: 0 } }, | ||
55 | }; | ||
52 | let subtree = c.go(node)?; | 56 | let subtree = c.go(node)?; |
53 | Some((subtree, c.map)) | 57 | Some((subtree, c.id_alloc.map)) |
54 | } | 58 | } |
55 | 59 | ||
56 | // The following items are what `rustc` macro can be parsed into : | 60 | // The following items are what `rustc` macro can be parsed into : |
@@ -89,6 +93,28 @@ pub fn token_tree_to_syntax_node( | |||
89 | Ok((parse, range_map)) | 93 | Ok((parse, range_map)) |
90 | } | 94 | } |
91 | 95 | ||
96 | /// Convert a string to a `TokenTree` | ||
97 | pub fn parse_to_token_tree(text: &str) -> Option<(tt::Subtree, TokenMap)> { | ||
98 | let (tokens, errors) = tokenize(text); | ||
99 | if !errors.is_empty() { | ||
100 | return None; | ||
101 | } | ||
102 | |||
103 | let mut conv = RawConvertor { | ||
104 | text, | ||
105 | offset: TextUnit::default(), | ||
106 | inner: tokens.iter(), | ||
107 | id_alloc: TokenIdAlloc { | ||
108 | map: Default::default(), | ||
109 | global_offset: TextUnit::default(), | ||
110 | next_id: 0, | ||
111 | }, | ||
112 | }; | ||
113 | |||
114 | let subtree = conv.go()?; | ||
115 | Some((subtree, conv.id_alloc.map)) | ||
116 | } | ||
117 | |||
92 | impl TokenMap { | 118 | impl TokenMap { |
93 | pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> { | 119 | pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> { |
94 | let &(token_id, _) = self.entries.iter().find(|(_, range)| match range { | 120 | let &(token_id, _) = self.entries.iter().find(|(_, range)| match range { |
@@ -118,6 +144,14 @@ impl TokenMap { | |||
118 | self.entries | 144 | self.entries |
119 | .push((token_id, TokenTextRange::Delimiter(open_relative_range, close_relative_range))); | 145 | .push((token_id, TokenTextRange::Delimiter(open_relative_range, close_relative_range))); |
120 | } | 146 | } |
147 | |||
148 | fn update_close_delim(&mut self, token_id: tt::TokenId, close_relative_range: TextRange) { | ||
149 | if let Some(entry) = self.entries.iter_mut().find(|(tid, _)| *tid == token_id) { | ||
150 | if let TokenTextRange::Delimiter(dim, _) = entry.1 { | ||
151 | entry.1 = TokenTextRange::Delimiter(dim, close_relative_range); | ||
152 | } | ||
153 | } | ||
154 | } | ||
121 | } | 155 | } |
122 | 156 | ||
123 | /// Returns the textual content of a doc comment block as a quoted string | 157 | /// Returns the textual content of a doc comment block as a quoted string |
@@ -188,12 +222,161 @@ fn convert_doc_comment(token: &ra_syntax::SyntaxToken) -> Option<Vec<tt::TokenTr | |||
188 | } | 222 | } |
189 | } | 223 | } |
190 | 224 | ||
191 | struct Convertor { | 225 | struct TokenIdAlloc { |
192 | map: TokenMap, | 226 | map: TokenMap, |
193 | global_offset: TextUnit, | 227 | global_offset: TextUnit, |
194 | next_id: u32, | 228 | next_id: u32, |
195 | } | 229 | } |
196 | 230 | ||
231 | impl TokenIdAlloc { | ||
232 | fn alloc(&mut self, absolute_range: TextRange) -> tt::TokenId { | ||
233 | let relative_range = absolute_range - self.global_offset; | ||
234 | let token_id = tt::TokenId(self.next_id); | ||
235 | self.next_id += 1; | ||
236 | self.map.insert(token_id, relative_range); | ||
237 | token_id | ||
238 | } | ||
239 | |||
240 | fn delim(&mut self, open_abs_range: TextRange, close_abs_range: TextRange) -> tt::TokenId { | ||
241 | let open_relative_range = open_abs_range - self.global_offset; | ||
242 | let close_relative_range = close_abs_range - self.global_offset; | ||
243 | let token_id = tt::TokenId(self.next_id); | ||
244 | self.next_id += 1; | ||
245 | |||
246 | self.map.insert_delim(token_id, open_relative_range, close_relative_range); | ||
247 | token_id | ||
248 | } | ||
249 | |||
250 | fn open_delim(&mut self, open_abs_range: TextRange) -> tt::TokenId { | ||
251 | let token_id = tt::TokenId(self.next_id); | ||
252 | self.next_id += 1; | ||
253 | self.map.insert_delim(token_id, open_abs_range, open_abs_range); | ||
254 | token_id | ||
255 | } | ||
256 | |||
257 | fn close_delim(&mut self, id: tt::TokenId, close_abs_range: TextRange) { | ||
258 | self.map.update_close_delim(id, close_abs_range); | ||
259 | } | ||
260 | } | ||
261 | |||
262 | /// A Raw Token (straightly from lexer) convertor | ||
263 | struct RawConvertor<'a> { | ||
264 | text: &'a str, | ||
265 | offset: TextUnit, | ||
266 | id_alloc: TokenIdAlloc, | ||
267 | inner: std::slice::Iter<'a, Token>, | ||
268 | } | ||
269 | |||
270 | impl RawConvertor<'_> { | ||
271 | fn go(&mut self) -> Option<tt::Subtree> { | ||
272 | let mut subtree = tt::Subtree::default(); | ||
273 | subtree.delimiter = None; | ||
274 | while self.peek().is_some() { | ||
275 | self.collect_leaf(&mut subtree.token_trees); | ||
276 | } | ||
277 | if subtree.token_trees.is_empty() { | ||
278 | return None; | ||
279 | } | ||
280 | if subtree.token_trees.len() == 1 { | ||
281 | if let tt::TokenTree::Subtree(first) = &subtree.token_trees[0] { | ||
282 | return Some(first.clone()); | ||
283 | } | ||
284 | } | ||
285 | Some(subtree) | ||
286 | } | ||
287 | |||
288 | fn bump(&mut self) -> Option<(Token, TextRange)> { | ||
289 | let token = self.inner.next()?; | ||
290 | let range = TextRange::offset_len(self.offset, token.len); | ||
291 | self.offset += token.len; | ||
292 | Some((*token, range)) | ||
293 | } | ||
294 | |||
295 | fn peek(&self) -> Option<Token> { | ||
296 | self.inner.as_slice().get(0).cloned() | ||
297 | } | ||
298 | |||
299 | fn collect_leaf(&mut self, result: &mut Vec<tt::TokenTree>) { | ||
300 | let (token, range) = match self.bump() { | ||
301 | None => return, | ||
302 | Some(it) => it, | ||
303 | }; | ||
304 | |||
305 | let k: SyntaxKind = token.kind; | ||
306 | if k == COMMENT { | ||
307 | let node = doc_comment(&self.text[range]); | ||
308 | if let Some(tokens) = convert_doc_comment(&node) { | ||
309 | result.extend(tokens); | ||
310 | } | ||
311 | return; | ||
312 | } | ||
313 | |||
314 | result.push(if k.is_punct() { | ||
315 | let delim = match k { | ||
316 | T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])), | ||
317 | T!['{'] => Some((tt::DelimiterKind::Brace, T!['}'])), | ||
318 | T!['['] => Some((tt::DelimiterKind::Bracket, T![']'])), | ||
319 | _ => None, | ||
320 | }; | ||
321 | |||
322 | if let Some((kind, closed)) = delim { | ||
323 | let mut subtree = tt::Subtree::default(); | ||
324 | let id = self.id_alloc.open_delim(range); | ||
325 | subtree.delimiter = Some(tt::Delimiter { kind, id }); | ||
326 | |||
327 | while self.peek().map(|it| it.kind != closed).unwrap_or(false) { | ||
328 | self.collect_leaf(&mut subtree.token_trees); | ||
329 | } | ||
330 | let last_range = match self.bump() { | ||
331 | None => return, | ||
332 | Some(it) => it.1, | ||
333 | }; | ||
334 | self.id_alloc.close_delim(id, last_range); | ||
335 | subtree.into() | ||
336 | } else { | ||
337 | let spacing = match self.peek() { | ||
338 | Some(next) | ||
339 | if next.kind.is_trivia() | ||
340 | || next.kind == T!['['] | ||
341 | || next.kind == T!['{'] | ||
342 | || next.kind == T!['('] => | ||
343 | { | ||
344 | tt::Spacing::Alone | ||
345 | } | ||
346 | Some(next) if next.kind.is_punct() => tt::Spacing::Joint, | ||
347 | _ => tt::Spacing::Alone, | ||
348 | }; | ||
349 | let char = | ||
350 | self.text[range].chars().next().expect("Token from lexer must be single char"); | ||
351 | |||
352 | tt::Leaf::from(tt::Punct { char, spacing, id: self.id_alloc.alloc(range) }).into() | ||
353 | } | ||
354 | } else { | ||
355 | macro_rules! make_leaf { | ||
356 | ($i:ident) => { | ||
357 | tt::$i { id: self.id_alloc.alloc(range), text: self.text[range].into() }.into() | ||
358 | }; | ||
359 | } | ||
360 | let leaf: tt::Leaf = match k { | ||
361 | T![true] | T![false] => make_leaf!(Literal), | ||
362 | IDENT | LIFETIME => make_leaf!(Ident), | ||
363 | k if k.is_keyword() => make_leaf!(Ident), | ||
364 | k if k.is_literal() => make_leaf!(Literal), | ||
365 | _ => return, | ||
366 | }; | ||
367 | |||
368 | leaf.into() | ||
369 | }); | ||
370 | } | ||
371 | } | ||
372 | |||
373 | // FIXME: There are some duplicate logic between RawConvertor and Convertor | ||
374 | // It would be nice to refactor to converting SyntaxNode to ra_parser::Token and thus | ||
375 | // use RawConvertor directly. But performance-wise it may not be a good idea ? | ||
376 | struct Convertor { | ||
377 | id_alloc: TokenIdAlloc, | ||
378 | } | ||
379 | |||
197 | impl Convertor { | 380 | impl Convertor { |
198 | fn go(&mut self, tt: &SyntaxNode) -> Option<tt::Subtree> { | 381 | fn go(&mut self, tt: &SyntaxNode) -> Option<tt::Subtree> { |
199 | // This tree is empty | 382 | // This tree is empty |
@@ -236,7 +419,7 @@ impl Convertor { | |||
236 | }; | 419 | }; |
237 | let delimiter = delimiter_kind.map(|kind| tt::Delimiter { | 420 | let delimiter = delimiter_kind.map(|kind| tt::Delimiter { |
238 | kind, | 421 | kind, |
239 | id: self.alloc_delim(first_child.text_range(), last_child.text_range()), | 422 | id: self.id_alloc.delim(first_child.text_range(), last_child.text_range()), |
240 | }); | 423 | }); |
241 | 424 | ||
242 | let mut token_trees = Vec::new(); | 425 | let mut token_trees = Vec::new(); |
@@ -273,7 +456,7 @@ impl Convertor { | |||
273 | tt::Leaf::from(tt::Punct { | 456 | tt::Leaf::from(tt::Punct { |
274 | char, | 457 | char, |
275 | spacing, | 458 | spacing, |
276 | id: self.alloc(token.text_range()), | 459 | id: self.id_alloc.alloc(token.text_range()), |
277 | }) | 460 | }) |
278 | .into(), | 461 | .into(), |
279 | ); | 462 | ); |
@@ -282,7 +465,7 @@ impl Convertor { | |||
282 | macro_rules! make_leaf { | 465 | macro_rules! make_leaf { |
283 | ($i:ident) => { | 466 | ($i:ident) => { |
284 | tt::$i { | 467 | tt::$i { |
285 | id: self.alloc(token.text_range()), | 468 | id: self.id_alloc.alloc(token.text_range()), |
286 | text: token.text().clone(), | 469 | text: token.text().clone(), |
287 | } | 470 | } |
288 | .into() | 471 | .into() |
@@ -313,28 +496,6 @@ impl Convertor { | |||
313 | let res = tt::Subtree { delimiter, token_trees }; | 496 | let res = tt::Subtree { delimiter, token_trees }; |
314 | Some(res) | 497 | Some(res) |
315 | } | 498 | } |
316 | |||
317 | fn alloc(&mut self, absolute_range: TextRange) -> tt::TokenId { | ||
318 | let relative_range = absolute_range - self.global_offset; | ||
319 | let token_id = tt::TokenId(self.next_id); | ||
320 | self.next_id += 1; | ||
321 | self.map.insert(token_id, relative_range); | ||
322 | token_id | ||
323 | } | ||
324 | |||
325 | fn alloc_delim( | ||
326 | &mut self, | ||
327 | open_abs_range: TextRange, | ||
328 | close_abs_range: TextRange, | ||
329 | ) -> tt::TokenId { | ||
330 | let open_relative_range = open_abs_range - self.global_offset; | ||
331 | let close_relative_range = close_abs_range - self.global_offset; | ||
332 | let token_id = tt::TokenId(self.next_id); | ||
333 | self.next_id += 1; | ||
334 | |||
335 | self.map.insert_delim(token_id, open_relative_range, close_relative_range); | ||
336 | token_id | ||
337 | } | ||
338 | } | 499 | } |
339 | 500 | ||
340 | struct TtTreeSink<'a> { | 501 | struct TtTreeSink<'a> { |
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index 066ce150b..6d5d1e9e6 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs | |||
@@ -1499,12 +1499,20 @@ impl MacroFixture { | |||
1499 | } | 1499 | } |
1500 | } | 1500 | } |
1501 | 1501 | ||
1502 | pub(crate) fn parse_macro(macro_definition: &str) -> MacroFixture { | 1502 | pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { |
1503 | let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap(); | 1503 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); |
1504 | let macro_definition = | 1504 | let macro_definition = |
1505 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | 1505 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); |
1506 | 1506 | ||
1507 | let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); | 1507 | let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); |
1508 | |||
1509 | let parsed = parse_to_token_tree( | ||
1510 | &ra_fixture[macro_definition.token_tree().unwrap().syntax().text_range()], | ||
1511 | ) | ||
1512 | .unwrap() | ||
1513 | .0; | ||
1514 | assert_eq!(definition_tt, parsed); | ||
1515 | |||
1508 | let rules = MacroRules::parse(&definition_tt).unwrap(); | 1516 | let rules = MacroRules::parse(&definition_tt).unwrap(); |
1509 | MacroFixture { rules } | 1517 | MacroFixture { rules } |
1510 | } | 1518 | } |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index bcf12460d..37845ca56 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -14,7 +14,7 @@ use std::{ | |||
14 | 14 | ||
15 | use anyhow::{bail, Context, Result}; | 15 | use anyhow::{bail, Context, Result}; |
16 | use ra_cfg::CfgOptions; | 16 | use ra_cfg::CfgOptions; |
17 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; | 17 | use ra_db::{CrateGraph, CrateName, Edition, Env, FileId}; |
18 | use rustc_hash::FxHashMap; | 18 | use rustc_hash::FxHashMap; |
19 | use serde_json::from_reader; | 19 | use serde_json::from_reader; |
20 | 20 | ||
@@ -163,9 +163,8 @@ impl ProjectWorkspace { | |||
163 | &self, | 163 | &self, |
164 | default_cfg_options: &CfgOptions, | 164 | default_cfg_options: &CfgOptions, |
165 | load: &mut dyn FnMut(&Path) -> Option<FileId>, | 165 | load: &mut dyn FnMut(&Path) -> Option<FileId>, |
166 | ) -> (CrateGraph, FxHashMap<CrateId, String>) { | 166 | ) -> CrateGraph { |
167 | let mut crate_graph = CrateGraph::default(); | 167 | let mut crate_graph = CrateGraph::default(); |
168 | let mut names = FxHashMap::default(); | ||
169 | match self { | 168 | match self { |
170 | ProjectWorkspace::Json { project } => { | 169 | ProjectWorkspace::Json { project } => { |
171 | let mut crates = FxHashMap::default(); | 170 | let mut crates = FxHashMap::default(); |
@@ -191,6 +190,8 @@ impl ProjectWorkspace { | |||
191 | crate_graph.add_crate_root( | 190 | crate_graph.add_crate_root( |
192 | file_id, | 191 | file_id, |
193 | edition, | 192 | edition, |
193 | // FIXME json definitions can store the crate name | ||
194 | None, | ||
194 | cfg_options, | 195 | cfg_options, |
195 | Env::default(), | 196 | Env::default(), |
196 | ), | 197 | ), |
@@ -233,11 +234,11 @@ impl ProjectWorkspace { | |||
233 | let crate_id = crate_graph.add_crate_root( | 234 | let crate_id = crate_graph.add_crate_root( |
234 | file_id, | 235 | file_id, |
235 | Edition::Edition2018, | 236 | Edition::Edition2018, |
237 | Some(krate.name(&sysroot).to_string()), | ||
236 | cfg_options, | 238 | cfg_options, |
237 | Env::default(), | 239 | Env::default(), |
238 | ); | 240 | ); |
239 | sysroot_crates.insert(krate, crate_id); | 241 | sysroot_crates.insert(krate, crate_id); |
240 | names.insert(crate_id, krate.name(&sysroot).to_string()); | ||
241 | } | 242 | } |
242 | } | 243 | } |
243 | for from in sysroot.crates() { | 244 | for from in sysroot.crates() { |
@@ -277,10 +278,10 @@ impl ProjectWorkspace { | |||
277 | let crate_id = crate_graph.add_crate_root( | 278 | let crate_id = crate_graph.add_crate_root( |
278 | file_id, | 279 | file_id, |
279 | edition, | 280 | edition, |
281 | Some(pkg.name(&cargo).to_string()), | ||
280 | cfg_options, | 282 | cfg_options, |
281 | Env::default(), | 283 | Env::default(), |
282 | ); | 284 | ); |
283 | names.insert(crate_id, pkg.name(&cargo).to_string()); | ||
284 | if tgt.kind(&cargo) == TargetKind::Lib { | 285 | if tgt.kind(&cargo) == TargetKind::Lib { |
285 | lib_tgt = Some(crate_id); | 286 | lib_tgt = Some(crate_id); |
286 | pkg_to_lib_crate.insert(pkg, crate_id); | 287 | pkg_to_lib_crate.insert(pkg, crate_id); |
@@ -381,7 +382,7 @@ impl ProjectWorkspace { | |||
381 | } | 382 | } |
382 | } | 383 | } |
383 | } | 384 | } |
384 | (crate_graph, names) | 385 | crate_graph |
385 | } | 386 | } |
386 | 387 | ||
387 | pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> { | 388 | pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> { |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 53d6fa562..ae8829807 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -267,6 +267,12 @@ pub mod tokens { | |||
267 | sf.syntax().first_child_or_token().unwrap().into_token().unwrap() | 267 | sf.syntax().first_child_or_token().unwrap().into_token().unwrap() |
268 | } | 268 | } |
269 | 269 | ||
270 | pub fn doc_comment(text: &str) -> SyntaxToken { | ||
271 | assert!(!text.trim().is_empty()); | ||
272 | let sf = SourceFile::parse(text).ok().unwrap(); | ||
273 | sf.syntax().first_child_or_token().unwrap().into_token().unwrap() | ||
274 | } | ||
275 | |||
270 | pub fn literal(text: &str) -> SyntaxToken { | 276 | pub fn literal(text: &str) -> SyntaxToken { |
271 | assert_eq!(text.trim(), text); | 277 | assert_eq!(text.trim(), text); |
272 | let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {}; }}", text)); | 278 | let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {}; }}", text)); |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index b14ebb268..d44f0ef1d 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -20,7 +20,7 @@ globset = "0.4.4" | |||
20 | itertools = "0.8.2" | 20 | itertools = "0.8.2" |
21 | jod-thread = "0.1.0" | 21 | jod-thread = "0.1.0" |
22 | log = "0.4.8" | 22 | log = "0.4.8" |
23 | lsp-types = { version = "0.72.0", features = ["proposed"] } | 23 | lsp-types = { version = "0.73.0", features = ["proposed"] } |
24 | parking_lot = "0.10.0" | 24 | parking_lot = "0.10.0" |
25 | pico-args = "0.3.1" | 25 | pico-args = "0.3.1" |
26 | rand = { version = "0.7.3", features = ["small_rng"] } | 26 | rand = { version = "0.7.3", features = ["small_rng"] } |
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 8cd08ecb6..4be987860 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -52,12 +52,11 @@ pub(crate) fn load_cargo( | |||
52 | opts | 52 | opts |
53 | }; | 53 | }; |
54 | 54 | ||
55 | let (crate_graph, _crate_names) = | 55 | let crate_graph = ws.to_crate_graph(&default_cfg_options, &mut |path: &Path| { |
56 | ws.to_crate_graph(&default_cfg_options, &mut |path: &Path| { | 56 | let vfs_file = vfs.load(path); |
57 | let vfs_file = vfs.load(path); | 57 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); |
58 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); | 58 | vfs_file.map(vfs_file_to_id) |
59 | vfs_file.map(vfs_file_to_id) | 59 | }); |
60 | }); | ||
61 | log::debug!("crate graph: {:?}", crate_graph); | 60 | log::debug!("crate graph: {:?}", crate_graph); |
62 | 61 | ||
63 | let source_roots = roots | 62 | let source_roots = roots |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 580ad1f2c..221f464b6 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -44,6 +44,8 @@ pub struct LspError { | |||
44 | } | 44 | } |
45 | 45 | ||
46 | impl LspError { | 46 | impl LspError { |
47 | pub const UNKNOWN_FILE: i32 = -32900; | ||
48 | |||
47 | pub fn new(code: i32, message: String) -> LspError { | 49 | pub fn new(code: i32, message: String) -> LspError { |
48 | LspError { code, message } | 50 | LspError { code, message } |
49 | } | 51 | } |
@@ -631,6 +633,9 @@ fn on_notification( | |||
631 | } | 633 | } |
632 | Err(not) => not, | 634 | Err(not) => not, |
633 | }; | 635 | }; |
636 | if not.method.starts_with("$/") { | ||
637 | return Ok(()); | ||
638 | } | ||
634 | log::error!("unhandled notification: {:?}", not); | 639 | log::error!("unhandled notification: {:?}", not); |
635 | Ok(()) | 640 | Ok(()) |
636 | } | 641 | } |
@@ -802,7 +807,14 @@ where | |||
802 | let response = match result { | 807 | let response = match result { |
803 | Ok(resp) => Response::new_ok(id, &resp), | 808 | Ok(resp) => Response::new_ok(id, &resp), |
804 | Err(e) => match e.downcast::<LspError>() { | 809 | Err(e) => match e.downcast::<LspError>() { |
805 | Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message), | 810 | Ok(lsp_error) => { |
811 | if lsp_error.code == LspError::UNKNOWN_FILE { | ||
812 | // Work-around for https://github.com/rust-analyzer/rust-analyzer/issues/1521 | ||
813 | Response::new_ok(id, ()) | ||
814 | } else { | ||
815 | Response::new_err(id, lsp_error.code, lsp_error.message) | ||
816 | } | ||
817 | } | ||
806 | Err(e) => { | 818 | Err(e) => { |
807 | if is_canceled(&e) { | 819 | if is_canceled(&e) { |
808 | Response::new_err( | 820 | Response::new_err( |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index b498c90c9..8dc6e8dc0 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -85,11 +85,11 @@ pub fn handle_expand_macro( | |||
85 | pub fn handle_selection_range( | 85 | pub fn handle_selection_range( |
86 | world: WorldSnapshot, | 86 | world: WorldSnapshot, |
87 | params: req::SelectionRangeParams, | 87 | params: req::SelectionRangeParams, |
88 | ) -> Result<Vec<req::SelectionRange>> { | 88 | ) -> Result<Option<Vec<req::SelectionRange>>> { |
89 | let _p = profile("handle_selection_range"); | 89 | let _p = profile("handle_selection_range"); |
90 | let file_id = params.text_document.try_conv_with(&world)?; | 90 | let file_id = params.text_document.try_conv_with(&world)?; |
91 | let line_index = world.analysis().file_line_index(file_id)?; | 91 | let line_index = world.analysis().file_line_index(file_id)?; |
92 | params | 92 | let res: Result<Vec<req::SelectionRange>> = params |
93 | .positions | 93 | .positions |
94 | .into_iter() | 94 | .into_iter() |
95 | .map_conv_with(&line_index) | 95 | .map_conv_with(&line_index) |
@@ -120,7 +120,9 @@ pub fn handle_selection_range( | |||
120 | } | 120 | } |
121 | Ok(range) | 121 | Ok(range) |
122 | }) | 122 | }) |
123 | .collect() | 123 | .collect(); |
124 | |||
125 | Ok(Some(res?)) | ||
124 | } | 126 | } |
125 | 127 | ||
126 | pub fn handle_find_matching_brace( | 128 | pub fn handle_find_matching_brace( |
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index 96efab844..6f394055a 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs | |||
@@ -9,7 +9,6 @@ use std::{ | |||
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crossbeam_channel::{unbounded, Receiver}; | 11 | use crossbeam_channel::{unbounded, Receiver}; |
12 | use lsp_server::ErrorCode; | ||
13 | use lsp_types::Url; | 12 | use lsp_types::Url; |
14 | use parking_lot::RwLock; | 13 | use parking_lot::RwLock; |
15 | use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher}; | 14 | use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher}; |
@@ -123,13 +122,12 @@ impl WorldState { | |||
123 | let vfs_file = vfs.load(path); | 122 | let vfs_file = vfs.load(path); |
124 | vfs_file.map(|f| FileId(f.0)) | 123 | vfs_file.map(|f| FileId(f.0)) |
125 | }; | 124 | }; |
126 | for ws in workspaces.iter() { | 125 | |
127 | let (graph, crate_names) = ws.to_crate_graph(&default_cfg_options, &mut load); | 126 | workspaces.iter().map(|ws| ws.to_crate_graph(&default_cfg_options, &mut load)).for_each( |
128 | let shift = crate_graph.extend(graph); | 127 | |graph| { |
129 | for (crate_id, name) in crate_names { | 128 | crate_graph.extend(graph); |
130 | change.set_debug_crate_name(crate_id.shift(shift), name) | 129 | }, |
131 | } | 130 | ); |
132 | } | ||
133 | change.set_crate_graph(crate_graph); | 131 | change.set_crate_graph(crate_graph); |
134 | 132 | ||
135 | // FIXME: Figure out the multi-workspace situation | 133 | // FIXME: Figure out the multi-workspace situation |
@@ -252,8 +250,9 @@ impl WorldSnapshot { | |||
252 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | 250 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; |
253 | let file = self.vfs.read().path2file(&path).ok_or_else(|| { | 251 | let file = self.vfs.read().path2file(&path).ok_or_else(|| { |
254 | // Show warning as this file is outside current workspace | 252 | // Show warning as this file is outside current workspace |
253 | // FIXME: just handle such files, and remove `LspError::UNKNOWN_FILE`. | ||
255 | LspError { | 254 | LspError { |
256 | code: ErrorCode::InvalidRequest as i32, | 255 | code: LspError::UNKNOWN_FILE, |
257 | message: "Rust file outside current workspace is not supported yet.".to_string(), | 256 | message: "Rust file outside current workspace is not supported yet.".to_string(), |
258 | } | 257 | } |
259 | })?; | 258 | })?; |