diff options
Diffstat (limited to 'crates')
99 files changed, 3958 insertions, 3286 deletions
diff --git a/crates/expect/Cargo.toml b/crates/expect/Cargo.toml new file mode 100644 index 000000000..caee43106 --- /dev/null +++ b/crates/expect/Cargo.toml | |||
@@ -0,0 +1,10 @@ | |||
1 | [package] | ||
2 | name = "expect" | ||
3 | version = "0.1.0" | ||
4 | authors = ["rust-analyzer developers"] | ||
5 | edition = "2018" | ||
6 | |||
7 | [dependencies] | ||
8 | once_cell = "1" | ||
9 | difference = "2" | ||
10 | stdx = { path = "../stdx" } | ||
diff --git a/crates/expect/src/lib.rs b/crates/expect/src/lib.rs new file mode 100644 index 000000000..a5e26fade --- /dev/null +++ b/crates/expect/src/lib.rs | |||
@@ -0,0 +1,348 @@ | |||
1 | //! Snapshot testing library, see | ||
2 | //! https://github.com/rust-analyzer/rust-analyzer/pull/5101 | ||
3 | use std::{ | ||
4 | collections::HashMap, | ||
5 | env, fmt, fs, mem, | ||
6 | ops::Range, | ||
7 | panic, | ||
8 | path::{Path, PathBuf}, | ||
9 | sync::Mutex, | ||
10 | }; | ||
11 | |||
12 | use difference::Changeset; | ||
13 | use once_cell::sync::Lazy; | ||
14 | use stdx::{lines_with_ends, trim_indent}; | ||
15 | |||
16 | const HELP: &str = " | ||
17 | You can update all `expect![[]]` tests by running: | ||
18 | |||
19 | env UPDATE_EXPECT=1 cargo test | ||
20 | |||
21 | To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer. | ||
22 | "; | ||
23 | |||
24 | fn update_expect() -> bool { | ||
25 | env::var("UPDATE_EXPECT").is_ok() | ||
26 | } | ||
27 | |||
28 | /// expect![[r#"inline snapshot"#]] | ||
29 | #[macro_export] | ||
30 | macro_rules! expect { | ||
31 | [[$data:literal]] => {$crate::Expect { | ||
32 | position: $crate::Position { | ||
33 | file: file!(), | ||
34 | line: line!(), | ||
35 | column: column!(), | ||
36 | }, | ||
37 | data: $data, | ||
38 | }}; | ||
39 | [[]] => { $crate::expect![[""]] }; | ||
40 | } | ||
41 | |||
42 | /// expect_file!["/crates/foo/test_data/bar.html"] | ||
43 | #[macro_export] | ||
44 | macro_rules! expect_file { | ||
45 | [$path:literal] => {$crate::ExpectFile { path: $path }}; | ||
46 | } | ||
47 | |||
48 | #[derive(Debug)] | ||
49 | pub struct Expect { | ||
50 | pub position: Position, | ||
51 | pub data: &'static str, | ||
52 | } | ||
53 | |||
54 | #[derive(Debug)] | ||
55 | pub struct ExpectFile { | ||
56 | pub path: &'static str, | ||
57 | } | ||
58 | |||
59 | #[derive(Debug)] | ||
60 | pub struct Position { | ||
61 | pub file: &'static str, | ||
62 | pub line: u32, | ||
63 | pub column: u32, | ||
64 | } | ||
65 | |||
66 | impl fmt::Display for Position { | ||
67 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
68 | write!(f, "{}:{}:{}", self.file, self.line, self.column) | ||
69 | } | ||
70 | } | ||
71 | |||
72 | impl Expect { | ||
73 | pub fn assert_eq(&self, actual: &str) { | ||
74 | let trimmed = self.trimmed(); | ||
75 | if &trimmed == actual { | ||
76 | return; | ||
77 | } | ||
78 | Runtime::fail_expect(self, &trimmed, actual); | ||
79 | } | ||
80 | pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) { | ||
81 | let actual = format!("{:#?}\n", actual); | ||
82 | self.assert_eq(&actual) | ||
83 | } | ||
84 | |||
85 | fn trimmed(&self) -> String { | ||
86 | if !self.data.contains('\n') { | ||
87 | return self.data.to_string(); | ||
88 | } | ||
89 | trim_indent(self.data) | ||
90 | } | ||
91 | |||
92 | fn locate(&self, file: &str) -> Location { | ||
93 | let mut target_line = None; | ||
94 | let mut line_start = 0; | ||
95 | for (i, line) in lines_with_ends(file).enumerate() { | ||
96 | if i == self.position.line as usize - 1 { | ||
97 | let pat = "expect![["; | ||
98 | let offset = line.find(pat).unwrap(); | ||
99 | let literal_start = line_start + offset + pat.len(); | ||
100 | let indent = line.chars().take_while(|&it| it == ' ').count(); | ||
101 | target_line = Some((literal_start, indent)); | ||
102 | break; | ||
103 | } | ||
104 | line_start += line.len(); | ||
105 | } | ||
106 | let (literal_start, line_indent) = target_line.unwrap(); | ||
107 | let literal_length = | ||
108 | file[literal_start..].find("]]").expect("Couldn't find matching `]]` for `expect![[`."); | ||
109 | let literal_range = literal_start..literal_start + literal_length; | ||
110 | Location { line_indent, literal_range } | ||
111 | } | ||
112 | } | ||
113 | |||
114 | impl ExpectFile { | ||
115 | pub fn assert_eq(&self, actual: &str) { | ||
116 | let expected = self.read(); | ||
117 | if actual == expected { | ||
118 | return; | ||
119 | } | ||
120 | Runtime::fail_file(self, &expected, actual); | ||
121 | } | ||
122 | fn read(&self) -> String { | ||
123 | fs::read_to_string(self.abs_path()).unwrap_or_default().replace("\r\n", "\n") | ||
124 | } | ||
125 | fn write(&self, contents: &str) { | ||
126 | fs::write(self.abs_path(), contents).unwrap() | ||
127 | } | ||
128 | fn abs_path(&self) -> PathBuf { | ||
129 | workspace_root().join(self.path) | ||
130 | } | ||
131 | } | ||
132 | |||
133 | #[derive(Default)] | ||
134 | struct Runtime { | ||
135 | help_printed: bool, | ||
136 | per_file: HashMap<&'static str, FileRuntime>, | ||
137 | } | ||
138 | static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default); | ||
139 | |||
140 | impl Runtime { | ||
141 | fn fail_expect(expect: &Expect, expected: &str, actual: &str) { | ||
142 | let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); | ||
143 | if update_expect() { | ||
144 | println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.position); | ||
145 | rt.per_file | ||
146 | .entry(expect.position.file) | ||
147 | .or_insert_with(|| FileRuntime::new(expect)) | ||
148 | .update(expect, actual); | ||
149 | return; | ||
150 | } | ||
151 | rt.panic(expect.position.to_string(), expected, actual); | ||
152 | } | ||
153 | |||
154 | fn fail_file(expect: &ExpectFile, expected: &str, actual: &str) { | ||
155 | let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); | ||
156 | if update_expect() { | ||
157 | println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.path); | ||
158 | expect.write(actual); | ||
159 | return; | ||
160 | } | ||
161 | rt.panic(expect.path.to_string(), expected, actual); | ||
162 | } | ||
163 | |||
164 | fn panic(&mut self, position: String, expected: &str, actual: &str) { | ||
165 | let print_help = !mem::replace(&mut self.help_printed, true); | ||
166 | let help = if print_help { HELP } else { "" }; | ||
167 | |||
168 | let diff = Changeset::new(actual, expected, "\n"); | ||
169 | |||
170 | println!( | ||
171 | "\n | ||
172 | \x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m | ||
173 | \x1b[1m\x1b[34m-->\x1b[0m {} | ||
174 | {} | ||
175 | \x1b[1mExpect\x1b[0m: | ||
176 | ---- | ||
177 | {} | ||
178 | ---- | ||
179 | |||
180 | \x1b[1mActual\x1b[0m: | ||
181 | ---- | ||
182 | {} | ||
183 | ---- | ||
184 | |||
185 | \x1b[1mDiff\x1b[0m: | ||
186 | ---- | ||
187 | {} | ||
188 | ---- | ||
189 | ", | ||
190 | position, help, expected, actual, diff | ||
191 | ); | ||
192 | // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise. | ||
193 | panic::resume_unwind(Box::new(())); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | struct FileRuntime { | ||
198 | path: PathBuf, | ||
199 | original_text: String, | ||
200 | patchwork: Patchwork, | ||
201 | } | ||
202 | |||
203 | impl FileRuntime { | ||
204 | fn new(expect: &Expect) -> FileRuntime { | ||
205 | let path = workspace_root().join(expect.position.file); | ||
206 | let original_text = fs::read_to_string(&path).unwrap(); | ||
207 | let patchwork = Patchwork::new(original_text.clone()); | ||
208 | FileRuntime { path, original_text, patchwork } | ||
209 | } | ||
210 | fn update(&mut self, expect: &Expect, actual: &str) { | ||
211 | let loc = expect.locate(&self.original_text); | ||
212 | let patch = format_patch(loc.line_indent.clone(), actual); | ||
213 | self.patchwork.patch(loc.literal_range, &patch); | ||
214 | fs::write(&self.path, &self.patchwork.text).unwrap() | ||
215 | } | ||
216 | } | ||
217 | |||
218 | #[derive(Debug)] | ||
219 | struct Location { | ||
220 | line_indent: usize, | ||
221 | literal_range: Range<usize>, | ||
222 | } | ||
223 | |||
224 | #[derive(Debug)] | ||
225 | struct Patchwork { | ||
226 | text: String, | ||
227 | indels: Vec<(Range<usize>, usize)>, | ||
228 | } | ||
229 | |||
230 | impl Patchwork { | ||
231 | fn new(text: String) -> Patchwork { | ||
232 | Patchwork { text, indels: Vec::new() } | ||
233 | } | ||
234 | fn patch(&mut self, mut range: Range<usize>, patch: &str) { | ||
235 | self.indels.push((range.clone(), patch.len())); | ||
236 | self.indels.sort_by_key(|(delete, _insert)| delete.start); | ||
237 | |||
238 | let (delete, insert) = self | ||
239 | .indels | ||
240 | .iter() | ||
241 | .take_while(|(delete, _)| delete.start < range.start) | ||
242 | .map(|(delete, insert)| (delete.end - delete.start, insert)) | ||
243 | .fold((0usize, 0usize), |(x1, y1), (x2, y2)| (x1 + x2, y1 + y2)); | ||
244 | |||
245 | for pos in &mut [&mut range.start, &mut range.end] { | ||
246 | **pos -= delete; | ||
247 | **pos += insert; | ||
248 | } | ||
249 | |||
250 | self.text.replace_range(range, &patch); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | fn format_patch(line_indent: usize, patch: &str) -> String { | ||
255 | let mut max_hashes = 0; | ||
256 | let mut cur_hashes = 0; | ||
257 | for byte in patch.bytes() { | ||
258 | if byte != b'#' { | ||
259 | cur_hashes = 0; | ||
260 | continue; | ||
261 | } | ||
262 | cur_hashes += 1; | ||
263 | max_hashes = max_hashes.max(cur_hashes); | ||
264 | } | ||
265 | let hashes = &"#".repeat(max_hashes + 1); | ||
266 | let indent = &" ".repeat(line_indent); | ||
267 | let is_multiline = patch.contains('\n'); | ||
268 | |||
269 | let mut buf = String::new(); | ||
270 | buf.push('r'); | ||
271 | buf.push_str(hashes); | ||
272 | buf.push('"'); | ||
273 | if is_multiline { | ||
274 | buf.push('\n'); | ||
275 | } | ||
276 | let mut final_newline = false; | ||
277 | for line in lines_with_ends(patch) { | ||
278 | if is_multiline { | ||
279 | buf.push_str(indent); | ||
280 | buf.push_str(" "); | ||
281 | } | ||
282 | buf.push_str(line); | ||
283 | final_newline = line.ends_with('\n'); | ||
284 | } | ||
285 | if final_newline { | ||
286 | buf.push_str(indent); | ||
287 | } | ||
288 | buf.push('"'); | ||
289 | buf.push_str(hashes); | ||
290 | buf | ||
291 | } | ||
292 | |||
293 | fn workspace_root() -> PathBuf { | ||
294 | Path::new( | ||
295 | &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()), | ||
296 | ) | ||
297 | .ancestors() | ||
298 | .nth(2) | ||
299 | .unwrap() | ||
300 | .to_path_buf() | ||
301 | } | ||
302 | |||
303 | #[cfg(test)] | ||
304 | mod tests { | ||
305 | use super::*; | ||
306 | |||
307 | #[test] | ||
308 | fn test_format_patch() { | ||
309 | let patch = format_patch(0, "hello\nworld\n"); | ||
310 | expect![[r##" | ||
311 | r#" | ||
312 | hello | ||
313 | world | ||
314 | "#"##]] | ||
315 | .assert_eq(&patch); | ||
316 | |||
317 | let patch = format_patch(4, "single line"); | ||
318 | expect![[r##"r#"single line"#"##]].assert_eq(&patch); | ||
319 | } | ||
320 | |||
321 | #[test] | ||
322 | fn test_patchwork() { | ||
323 | let mut patchwork = Patchwork::new("one two three".to_string()); | ||
324 | patchwork.patch(4..7, "zwei"); | ||
325 | patchwork.patch(0..3, "один"); | ||
326 | patchwork.patch(8..13, "3"); | ||
327 | expect![[r#" | ||
328 | Patchwork { | ||
329 | text: "один zwei 3", | ||
330 | indels: [ | ||
331 | ( | ||
332 | 0..3, | ||
333 | 8, | ||
334 | ), | ||
335 | ( | ||
336 | 4..7, | ||
337 | 4, | ||
338 | ), | ||
339 | ( | ||
340 | 8..13, | ||
341 | 1, | ||
342 | ), | ||
343 | ], | ||
344 | } | ||
345 | "#]] | ||
346 | .assert_debug_eq(&patchwork); | ||
347 | } | ||
348 | } | ||
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 1023d3040..844b093d4 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs | |||
@@ -132,6 +132,7 @@ impl FlycheckActor { | |||
132 | self.cancel_check_process(); | 132 | self.cancel_check_process(); |
133 | 133 | ||
134 | let mut command = self.check_command(); | 134 | let mut command = self.check_command(); |
135 | log::info!("restart flycheck {:?}", command); | ||
135 | command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); | 136 | command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); |
136 | if let Ok(child) = command.spawn().map(JodChild) { | 137 | if let Ok(child) = command.spawn().map(JodChild) { |
137 | self.cargo_handle = Some(CargoHandle::spawn(child)); | 138 | self.cargo_handle = Some(CargoHandle::spawn(child)); |
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index ee614de72..3640bb4d2 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -55,7 +55,6 @@ use crate::{ | |||
55 | pub(crate) struct AssistContext<'a> { | 55 | pub(crate) struct AssistContext<'a> { |
56 | pub(crate) config: &'a AssistConfig, | 56 | pub(crate) config: &'a AssistConfig, |
57 | pub(crate) sema: Semantics<'a, RootDatabase>, | 57 | pub(crate) sema: Semantics<'a, RootDatabase>, |
58 | pub(crate) db: &'a RootDatabase, | ||
59 | pub(crate) frange: FileRange, | 58 | pub(crate) frange: FileRange, |
60 | source_file: SourceFile, | 59 | source_file: SourceFile, |
61 | } | 60 | } |
@@ -67,8 +66,11 @@ impl<'a> AssistContext<'a> { | |||
67 | frange: FileRange, | 66 | frange: FileRange, |
68 | ) -> AssistContext<'a> { | 67 | ) -> AssistContext<'a> { |
69 | let source_file = sema.parse(frange.file_id); | 68 | let source_file = sema.parse(frange.file_id); |
70 | let db = sema.db; | 69 | AssistContext { config, sema, frange, source_file } |
71 | AssistContext { config, sema, db, frange, source_file } | 70 | } |
71 | |||
72 | pub(crate) fn db(&self) -> &RootDatabase { | ||
73 | self.sema.db | ||
72 | } | 74 | } |
73 | 75 | ||
74 | // NB, this ignores active selection. | 76 | // NB, this ignores active selection. |
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs index 00fa95b6c..01adb834c 100644 --- a/crates/ra_assists/src/ast_transform.rs +++ b/crates/ra_assists/src/ast_transform.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
3 | 3 | ||
4 | use hir::{HirDisplay, PathResolution, SemanticsScope}; | 4 | use hir::{HirDisplay, PathResolution, SemanticsScope}; |
5 | use ra_ide_db::RootDatabase; | ||
6 | use ra_syntax::{ | 5 | use ra_syntax::{ |
7 | algo::SyntaxRewriter, | 6 | algo::SyntaxRewriter, |
8 | ast::{self, AstNode}, | 7 | ast::{self, AstNode}, |
@@ -32,14 +31,14 @@ impl<'a> AstTransform<'a> for NullTransformer { | |||
32 | } | 31 | } |
33 | 32 | ||
34 | pub struct SubstituteTypeParams<'a> { | 33 | pub struct SubstituteTypeParams<'a> { |
35 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | 34 | source_scope: &'a SemanticsScope<'a>, |
36 | substs: FxHashMap<hir::TypeParam, ast::TypeRef>, | 35 | substs: FxHashMap<hir::TypeParam, ast::TypeRef>, |
37 | previous: Box<dyn AstTransform<'a> + 'a>, | 36 | previous: Box<dyn AstTransform<'a> + 'a>, |
38 | } | 37 | } |
39 | 38 | ||
40 | impl<'a> SubstituteTypeParams<'a> { | 39 | impl<'a> SubstituteTypeParams<'a> { |
41 | pub fn for_trait_impl( | 40 | pub fn for_trait_impl( |
42 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | 41 | source_scope: &'a SemanticsScope<'a>, |
43 | // FIXME: there's implicit invariant that `trait_` and `source_scope` match... | 42 | // FIXME: there's implicit invariant that `trait_` and `source_scope` match... |
44 | trait_: hir::Trait, | 43 | trait_: hir::Trait, |
45 | impl_def: ast::ImplDef, | 44 | impl_def: ast::ImplDef, |
@@ -126,16 +125,13 @@ impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { | |||
126 | } | 125 | } |
127 | 126 | ||
128 | pub struct QualifyPaths<'a> { | 127 | pub struct QualifyPaths<'a> { |
129 | target_scope: &'a SemanticsScope<'a, RootDatabase>, | 128 | target_scope: &'a SemanticsScope<'a>, |
130 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | 129 | source_scope: &'a SemanticsScope<'a>, |
131 | previous: Box<dyn AstTransform<'a> + 'a>, | 130 | previous: Box<dyn AstTransform<'a> + 'a>, |
132 | } | 131 | } |
133 | 132 | ||
134 | impl<'a> QualifyPaths<'a> { | 133 | impl<'a> QualifyPaths<'a> { |
135 | pub fn new( | 134 | pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self { |
136 | target_scope: &'a SemanticsScope<'a, RootDatabase>, | ||
137 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | ||
138 | ) -> Self { | ||
139 | Self { target_scope, source_scope, previous: Box::new(NullTransformer) } | 135 | Self { target_scope, source_scope, previous: Box::new(NullTransformer) } |
140 | } | 136 | } |
141 | 137 | ||
@@ -156,7 +152,7 @@ impl<'a> QualifyPaths<'a> { | |||
156 | let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; | 152 | let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; |
157 | match resolution { | 153 | match resolution { |
158 | PathResolution::Def(def) => { | 154 | PathResolution::Def(def) => { |
159 | let found_path = from.find_use_path(self.source_scope.db, def)?; | 155 | let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?; |
160 | let mut path = path_to_ast(found_path); | 156 | let mut path = path_to_ast(found_path); |
161 | 157 | ||
162 | let type_args = p | 158 | let type_args = p |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 90b06a625..11df922a2 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -57,7 +57,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
57 | return None; | 57 | return None; |
58 | } | 58 | } |
59 | 59 | ||
60 | let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; | 60 | let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?; |
61 | acc.add( | 61 | acc.add( |
62 | AssistId("add_explicit_type"), | 62 | AssistId("add_explicit_type"), |
63 | format!("Insert explicit type `{}`", inferred_type), | 63 | format!("Insert explicit type `{}`", inferred_type), |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 1cfbd75aa..fc4e82309 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -117,7 +117,7 @@ impl FunctionBuilder { | |||
117 | let mut file = ctx.frange.file_id; | 117 | let mut file = ctx.frange.file_id; |
118 | let target = match &target_module { | 118 | let target = match &target_module { |
119 | Some(target_module) => { | 119 | Some(target_module) => { |
120 | let module_source = target_module.definition_source(ctx.db); | 120 | let module_source = target_module.definition_source(ctx.db()); |
121 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; | 121 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; |
122 | file = in_file; | 122 | file = in_file; |
123 | target | 123 | target |
@@ -269,7 +269,7 @@ fn fn_arg_type( | |||
269 | return None; | 269 | return None; |
270 | } | 270 | } |
271 | 271 | ||
272 | if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) { | 272 | if let Ok(rendered) = ty.display_source_code(ctx.db(), target_module.into()) { |
273 | Some(rendered) | 273 | Some(rendered) |
274 | } else { | 274 | } else { |
275 | None | 275 | None |
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index abacd4065..77e092f62 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -128,9 +128,9 @@ fn add_missing_impl_members_inner( | |||
128 | let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def) | 128 | let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def) |
129 | .iter() | 129 | .iter() |
130 | .map(|i| match i { | 130 | .map(|i| match i { |
131 | hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db).value), | 131 | hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db()).value), |
132 | hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db).value), | 132 | hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db()).value), |
133 | hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db).value), | 133 | hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db()).value), |
134 | }) | 134 | }) |
135 | .filter(|t| def_name(&t).is_some()) | 135 | .filter(|t| def_name(&t).is_some()) |
136 | .filter(|t| match t { | 136 | .filter(|t| match t { |
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs index 837aa8377..e41b2aa06 100644 --- a/crates/ra_assists/src/handlers/add_new.rs +++ b/crates/ra_assists/src/handlers/add_new.rs | |||
@@ -122,7 +122,7 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String { | |||
122 | // FIXME: change the new fn checking to a more semantic approach when that's more | 122 | // FIXME: change the new fn checking to a more semantic approach when that's more |
123 | // viable (e.g. we process proc macros, etc) | 123 | // viable (e.g. we process proc macros, etc) |
124 | fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> { | 124 | fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> { |
125 | let db = ctx.db; | 125 | let db = ctx.db(); |
126 | let module = strukt.syntax().ancestors().find(|node| { | 126 | let module = strukt.syntax().ancestors().find(|node| { |
127 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) | 127 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) |
128 | })?; | 128 | })?; |
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index d1cafa7d9..7b6499a08 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -5,7 +5,7 @@ use hir::{ | |||
5 | AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, | 5 | AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, |
6 | Type, | 6 | Type, |
7 | }; | 7 | }; |
8 | use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; | 8 | use ra_ide_db::{imports_locator, RootDatabase}; |
9 | use ra_prof::profile; | 9 | use ra_prof::profile; |
10 | use ra_syntax::{ | 10 | use ra_syntax::{ |
11 | ast::{self, AstNode}, | 11 | ast::{self, AstNode}, |
@@ -35,8 +35,8 @@ use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, Group | |||
35 | // # pub mod std { pub mod collections { pub struct HashMap { } } } | 35 | // # pub mod std { pub mod collections { pub struct HashMap { } } } |
36 | // ``` | 36 | // ``` |
37 | pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 37 | pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
38 | let auto_import_assets = AutoImportAssets::new(&ctx)?; | 38 | let auto_import_assets = AutoImportAssets::new(ctx)?; |
39 | let proposed_imports = auto_import_assets.search_for_imports(ctx.db); | 39 | let proposed_imports = auto_import_assets.search_for_imports(ctx); |
40 | if proposed_imports.is_empty() { | 40 | if proposed_imports.is_empty() { |
41 | return None; | 41 | return None; |
42 | } | 42 | } |
@@ -127,11 +127,11 @@ impl AutoImportAssets { | |||
127 | GroupLabel(name) | 127 | GroupLabel(name) |
128 | } | 128 | } |
129 | 129 | ||
130 | fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { | 130 | fn search_for_imports(&self, ctx: &AssistContext) -> BTreeSet<ModPath> { |
131 | let _p = profile("auto_import::search_for_imports"); | 131 | let _p = profile("auto_import::search_for_imports"); |
132 | let db = ctx.db(); | ||
132 | let current_crate = self.module_with_name_to_import.krate(); | 133 | let current_crate = self.module_with_name_to_import.krate(); |
133 | ImportsLocator::new(db, current_crate) | 134 | imports_locator::find_imports(&ctx.sema, current_crate, &self.get_search_query()) |
134 | .find_imports(&self.get_search_query()) | ||
135 | .into_iter() | 135 | .into_iter() |
136 | .filter_map(|candidate| match &self.import_candidate { | 136 | .filter_map(|candidate| match &self.import_candidate { |
137 | ImportCandidate::TraitAssocItem(assoc_item_type, _) => { | 137 | ImportCandidate::TraitAssocItem(assoc_item_type, _) => { |
diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs index 43b4584b4..ca19cf198 100644 --- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -37,15 +37,15 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
37 | }; | 37 | }; |
38 | let variant_name = variant.name()?.to_string(); | 38 | let variant_name = variant.name()?.to_string(); |
39 | let variant_hir = ctx.sema.to_def(&variant)?; | 39 | let variant_hir = ctx.sema.to_def(&variant)?; |
40 | if existing_struct_def(ctx.db, &variant_name, &variant_hir) { | 40 | if existing_struct_def(ctx.db(), &variant_name, &variant_hir) { |
41 | return None; | 41 | return None; |
42 | } | 42 | } |
43 | let enum_ast = variant.parent_enum(); | 43 | let enum_ast = variant.parent_enum(); |
44 | let visibility = enum_ast.visibility(); | 44 | let visibility = enum_ast.visibility(); |
45 | let enum_hir = ctx.sema.to_def(&enum_ast)?; | 45 | let enum_hir = ctx.sema.to_def(&enum_ast)?; |
46 | let variant_hir_name = variant_hir.name(ctx.db); | 46 | let variant_hir_name = variant_hir.name(ctx.db()); |
47 | let enum_module_def = ModuleDef::from(enum_hir); | 47 | let enum_module_def = ModuleDef::from(enum_hir); |
48 | let current_module = enum_hir.module(ctx.db); | 48 | let current_module = enum_hir.module(ctx.db()); |
49 | let target = variant.syntax().text_range(); | 49 | let target = variant.syntax().text_range(); |
50 | acc.add( | 50 | acc.add( |
51 | AssistId("extract_struct_from_enum_variant"), | 51 | AssistId("extract_struct_from_enum_variant"), |
@@ -53,7 +53,7 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
53 | target, | 53 | target, |
54 | |builder| { | 54 | |builder| { |
55 | let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); | 55 | let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); |
56 | let res = definition.find_usages(&ctx.db, None); | 56 | let res = definition.find_usages(&ctx.sema, None); |
57 | let start_offset = variant.parent_enum().syntax().text_range().start(); | 57 | let start_offset = variant.parent_enum().syntax().text_range().start(); |
58 | let mut visited_modules_set = FxHashSet::default(); | 58 | let mut visited_modules_set = FxHashSet::default(); |
59 | visited_modules_set.insert(current_module); | 59 | visited_modules_set.insert(current_module); |
@@ -101,7 +101,7 @@ fn insert_import( | |||
101 | enum_module_def: &ModuleDef, | 101 | enum_module_def: &ModuleDef, |
102 | variant_hir_name: &Name, | 102 | variant_hir_name: &Name, |
103 | ) -> Option<()> { | 103 | ) -> Option<()> { |
104 | let db = ctx.db; | 104 | let db = ctx.db(); |
105 | let mod_path = module.find_use_path(db, enum_module_def.clone()); | 105 | let mod_path = module.find_use_path(db, enum_module_def.clone()); |
106 | if let Some(mut mod_path) = mod_path { | 106 | if let Some(mut mod_path) = mod_path { |
107 | mod_path.segments.pop(); | 107 | mod_path.segments.pop(); |
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index 64270c86f..5b1235682 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs | |||
@@ -51,11 +51,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
51 | let module = ctx.sema.scope(expr.syntax()).module()?; | 51 | let module = ctx.sema.scope(expr.syntax()).module()?; |
52 | 52 | ||
53 | let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { | 53 | let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { |
54 | let variants = enum_def.variants(ctx.db); | 54 | let variants = enum_def.variants(ctx.db()); |
55 | 55 | ||
56 | let mut variants = variants | 56 | let mut variants = variants |
57 | .into_iter() | 57 | .into_iter() |
58 | .filter_map(|variant| build_pat(ctx.db, module, variant)) | 58 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) |
59 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | 59 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) |
60 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 60 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
61 | .collect::<Vec<_>>(); | 61 | .collect::<Vec<_>>(); |
@@ -84,11 +84,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
84 | // where each tuple represents a proposed match arm. | 84 | // where each tuple represents a proposed match arm. |
85 | enum_defs | 85 | enum_defs |
86 | .into_iter() | 86 | .into_iter() |
87 | .map(|enum_def| enum_def.variants(ctx.db)) | 87 | .map(|enum_def| enum_def.variants(ctx.db())) |
88 | .multi_cartesian_product() | 88 | .multi_cartesian_product() |
89 | .map(|variants| { | 89 | .map(|variants| { |
90 | let patterns = | 90 | let patterns = |
91 | variants.into_iter().filter_map(|variant| build_pat(ctx.db, module, variant)); | 91 | variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant)); |
92 | ast::Pat::from(make::tuple_pat(patterns)) | 92 | ast::Pat::from(make::tuple_pat(patterns)) |
93 | }) | 93 | }) |
94 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | 94 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) |
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs index 19d4dac5e..c0f57c329 100644 --- a/crates/ra_assists/src/handlers/fix_visibility.rs +++ b/crates/ra_assists/src/handlers/fix_visibility.rs | |||
@@ -41,14 +41,14 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O | |||
41 | }; | 41 | }; |
42 | 42 | ||
43 | let current_module = ctx.sema.scope(&path.syntax()).module()?; | 43 | let current_module = ctx.sema.scope(&path.syntax()).module()?; |
44 | let target_module = def.module(ctx.db)?; | 44 | let target_module = def.module(ctx.db())?; |
45 | 45 | ||
46 | let vis = target_module.visibility_of(ctx.db, &def)?; | 46 | let vis = target_module.visibility_of(ctx.db(), &def)?; |
47 | if vis.is_visible_from(ctx.db, current_module.into()) { | 47 | if vis.is_visible_from(ctx.db(), current_module.into()) { |
48 | return None; | 48 | return None; |
49 | }; | 49 | }; |
50 | 50 | ||
51 | let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?; | 51 | let (offset, target, target_file, target_name) = target_data_for_def(ctx.db(), def)?; |
52 | 52 | ||
53 | let missing_visibility = | 53 | let missing_visibility = |
54 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; | 54 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; |
@@ -72,16 +72,16 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> | |||
72 | let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; | 72 | let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; |
73 | 73 | ||
74 | let current_module = ctx.sema.scope(record_field.syntax()).module()?; | 74 | let current_module = ctx.sema.scope(record_field.syntax()).module()?; |
75 | let visibility = record_field_def.visibility(ctx.db); | 75 | let visibility = record_field_def.visibility(ctx.db()); |
76 | if visibility.is_visible_from(ctx.db, current_module.into()) { | 76 | if visibility.is_visible_from(ctx.db(), current_module.into()) { |
77 | return None; | 77 | return None; |
78 | } | 78 | } |
79 | 79 | ||
80 | let parent = record_field_def.parent_def(ctx.db); | 80 | let parent = record_field_def.parent_def(ctx.db()); |
81 | let parent_name = parent.name(ctx.db); | 81 | let parent_name = parent.name(ctx.db()); |
82 | let target_module = parent.module(ctx.db); | 82 | let target_module = parent.module(ctx.db()); |
83 | 83 | ||
84 | let in_file_source = record_field_def.source(ctx.db); | 84 | let in_file_source = record_field_def.source(ctx.db()); |
85 | let (offset, target) = match in_file_source.value { | 85 | let (offset, target) = match in_file_source.value { |
86 | hir::FieldSource::Named(it) => { | 86 | hir::FieldSource::Named(it) => { |
87 | let s = it.syntax(); | 87 | let s = it.syntax(); |
@@ -95,9 +95,9 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> | |||
95 | 95 | ||
96 | let missing_visibility = | 96 | let missing_visibility = |
97 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; | 97 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; |
98 | let target_file = in_file_source.file_id.original_file(ctx.db); | 98 | let target_file = in_file_source.file_id.original_file(ctx.db()); |
99 | 99 | ||
100 | let target_name = record_field_def.name(ctx.db); | 100 | let target_name = record_field_def.name(ctx.db()); |
101 | let assist_label = | 101 | let assist_label = |
102 | format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); | 102 | format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); |
103 | 103 | ||
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs index d26e68847..259839535 100644 --- a/crates/ra_assists/src/handlers/inline_local_variable.rs +++ b/crates/ra_assists/src/handlers/inline_local_variable.rs | |||
@@ -44,7 +44,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
44 | 44 | ||
45 | let def = ctx.sema.to_def(&bind_pat)?; | 45 | let def = ctx.sema.to_def(&bind_pat)?; |
46 | let def = Definition::Local(def); | 46 | let def = Definition::Local(def); |
47 | let refs = def.find_usages(ctx.db, None); | 47 | let refs = def.find_usages(&ctx.sema, None); |
48 | if refs.is_empty() { | 48 | if refs.is_empty() { |
49 | mark::hit!(test_not_applicable_if_variable_unused); | 49 | mark::hit!(test_not_applicable_if_variable_unused); |
50 | return None; | 50 | return None; |
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs index bc58ce5fe..b8cf30e7f 100644 --- a/crates/ra_assists/src/handlers/reorder_fields.rs +++ b/crates/ra_assists/src/handlers/reorder_fields.rs | |||
@@ -90,10 +90,10 @@ fn struct_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option | |||
90 | fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> { | 90 | fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> { |
91 | Some( | 91 | Some( |
92 | struct_definition(path, &ctx.sema)? | 92 | struct_definition(path, &ctx.sema)? |
93 | .fields(ctx.db) | 93 | .fields(ctx.db()) |
94 | .iter() | 94 | .iter() |
95 | .enumerate() | 95 | .enumerate() |
96 | .map(|(idx, field)| (field.name(ctx.db).to_string(), idx)) | 96 | .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx)) |
97 | .collect(), | 97 | .collect(), |
98 | ) | 98 | ) |
99 | } | 99 | } |
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index 4f4fb4494..209713987 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs | |||
@@ -149,15 +149,17 @@ fn with_files( | |||
149 | let crate_id = crate_graph.add_crate_root( | 149 | let crate_id = crate_graph.add_crate_root( |
150 | file_id, | 150 | file_id, |
151 | meta.edition, | 151 | meta.edition, |
152 | Some(CrateName::new(&krate).unwrap()), | 152 | Some(krate.clone()), |
153 | meta.cfg, | 153 | meta.cfg, |
154 | meta.env, | 154 | meta.env, |
155 | Default::default(), | 155 | Default::default(), |
156 | ); | 156 | ); |
157 | let prev = crates.insert(krate.clone(), crate_id); | 157 | let crate_name = CrateName::new(&krate).unwrap(); |
158 | let prev = crates.insert(crate_name.clone(), crate_id); | ||
158 | assert!(prev.is_none()); | 159 | assert!(prev.is_none()); |
159 | for dep in meta.deps { | 160 | for dep in meta.deps { |
160 | crate_deps.push((krate.clone(), dep)) | 161 | let dep = CrateName::new(&dep).unwrap(); |
162 | crate_deps.push((crate_name.clone(), dep)) | ||
161 | } | 163 | } |
162 | } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { | 164 | } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { |
163 | assert!(default_crate_root.is_none()); | 165 | assert!(default_crate_root.is_none()); |
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 7f3660118..aaa492759 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -67,7 +67,7 @@ pub struct CrateGraph { | |||
67 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 67 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
68 | pub struct CrateId(pub u32); | 68 | pub struct CrateId(pub u32); |
69 | 69 | ||
70 | #[derive(Debug, Clone, PartialEq, Eq)] | 70 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
71 | pub struct CrateName(SmolStr); | 71 | pub struct CrateName(SmolStr); |
72 | 72 | ||
73 | impl CrateName { | 73 | impl CrateName { |
@@ -94,6 +94,13 @@ impl fmt::Display for CrateName { | |||
94 | } | 94 | } |
95 | } | 95 | } |
96 | 96 | ||
97 | impl ops::Deref for CrateName { | ||
98 | type Target = str; | ||
99 | fn deref(&self) -> &Self::Target { | ||
100 | &*self.0 | ||
101 | } | ||
102 | } | ||
103 | |||
97 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 104 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
98 | pub struct ProcMacroId(pub u32); | 105 | pub struct ProcMacroId(pub u32); |
99 | 106 | ||
@@ -117,7 +124,7 @@ pub struct CrateData { | |||
117 | /// The name to display to the end user. | 124 | /// The name to display to the end user. |
118 | /// This actual crate name can be different in a particular dependent crate | 125 | /// This actual crate name can be different in a particular dependent crate |
119 | /// or may even be missing for some cases, such as a dummy crate for the code snippet. | 126 | /// or may even be missing for some cases, such as a dummy crate for the code snippet. |
120 | pub display_name: Option<CrateName>, | 127 | pub display_name: Option<String>, |
121 | pub cfg_options: CfgOptions, | 128 | pub cfg_options: CfgOptions, |
122 | pub env: Env, | 129 | pub env: Env, |
123 | pub dependencies: Vec<Dependency>, | 130 | pub dependencies: Vec<Dependency>, |
@@ -138,7 +145,7 @@ pub struct Env { | |||
138 | #[derive(Debug, Clone, PartialEq, Eq)] | 145 | #[derive(Debug, Clone, PartialEq, Eq)] |
139 | pub struct Dependency { | 146 | pub struct Dependency { |
140 | pub crate_id: CrateId, | 147 | pub crate_id: CrateId, |
141 | pub name: SmolStr, | 148 | pub name: CrateName, |
142 | } | 149 | } |
143 | 150 | ||
144 | impl CrateGraph { | 151 | impl CrateGraph { |
@@ -146,7 +153,7 @@ impl CrateGraph { | |||
146 | &mut self, | 153 | &mut self, |
147 | file_id: FileId, | 154 | file_id: FileId, |
148 | edition: Edition, | 155 | edition: Edition, |
149 | display_name: Option<CrateName>, | 156 | display_name: Option<String>, |
150 | cfg_options: CfgOptions, | 157 | cfg_options: CfgOptions, |
151 | env: Env, | 158 | env: Env, |
152 | proc_macro: Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)>, | 159 | proc_macro: Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)>, |
@@ -178,7 +185,7 @@ impl CrateGraph { | |||
178 | if self.dfs_find(from, to, &mut FxHashSet::default()) { | 185 | if self.dfs_find(from, to, &mut FxHashSet::default()) { |
179 | return Err(CyclicDependenciesError); | 186 | return Err(CyclicDependenciesError); |
180 | } | 187 | } |
181 | self.arena.get_mut(&from).unwrap().add_dep(name.0, to); | 188 | self.arena.get_mut(&from).unwrap().add_dep(name, to); |
182 | Ok(()) | 189 | Ok(()) |
183 | } | 190 | } |
184 | 191 | ||
@@ -190,6 +197,23 @@ impl CrateGraph { | |||
190 | self.arena.keys().copied() | 197 | self.arena.keys().copied() |
191 | } | 198 | } |
192 | 199 | ||
200 | /// Returns an iterator over all transitive dependencies of the given crate. | ||
201 | pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> + '_ { | ||
202 | let mut worklist = vec![of]; | ||
203 | let mut deps = FxHashSet::default(); | ||
204 | |||
205 | while let Some(krate) = worklist.pop() { | ||
206 | if !deps.insert(krate) { | ||
207 | continue; | ||
208 | } | ||
209 | |||
210 | worklist.extend(self[krate].dependencies.iter().map(|dep| dep.crate_id)); | ||
211 | } | ||
212 | |||
213 | deps.remove(&of); | ||
214 | deps.into_iter() | ||
215 | } | ||
216 | |||
193 | // FIXME: this only finds one crate with the given root; we could have multiple | 217 | // FIXME: this only finds one crate with the given root; we could have multiple |
194 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { | 218 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { |
195 | let (&crate_id, _) = | 219 | let (&crate_id, _) = |
@@ -247,7 +271,7 @@ impl CrateId { | |||
247 | } | 271 | } |
248 | 272 | ||
249 | impl CrateData { | 273 | impl CrateData { |
250 | fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { | 274 | fn add_dep(&mut self, name: CrateName, crate_id: CrateId) { |
251 | self.dependencies.push(Dependency { name, crate_id }) | 275 | self.dependencies.push(Dependency { name, crate_id }) |
252 | } | 276 | } |
253 | } | 277 | } |
@@ -429,7 +453,10 @@ mod tests { | |||
429 | .is_ok()); | 453 | .is_ok()); |
430 | assert_eq!( | 454 | assert_eq!( |
431 | graph[crate1].dependencies, | 455 | graph[crate1].dependencies, |
432 | vec![Dependency { crate_id: crate2, name: "crate_name_with_dashes".into() }] | 456 | vec![Dependency { |
457 | crate_id: crate2, | ||
458 | name: CrateName::new("crate_name_with_dashes").unwrap() | ||
459 | }] | ||
433 | ); | 460 | ); |
434 | } | 461 | } |
435 | } | 462 | } |
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 4a3ba57da..1ddacc1f6 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs | |||
@@ -80,7 +80,7 @@ pub struct FilePosition { | |||
80 | pub offset: TextSize, | 80 | pub offset: TextSize, |
81 | } | 81 | } |
82 | 82 | ||
83 | #[derive(Clone, Copy, Debug)] | 83 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
84 | pub struct FileRange { | 84 | pub struct FileRange { |
85 | pub file_id: FileId, | 85 | pub file_id: FileId, |
86 | pub range: TextRange, | 86 | pub range: TextRange, |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index e86077dd6..1b3525011 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -31,7 +31,7 @@ use hir_ty::{ | |||
31 | ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, TraitEnvironment, Ty, | 31 | ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, TraitEnvironment, Ty, |
32 | TyDefId, TypeCtor, | 32 | TyDefId, TypeCtor, |
33 | }; | 33 | }; |
34 | use ra_db::{CrateId, CrateName, Edition, FileId}; | 34 | use ra_db::{CrateId, Edition, FileId}; |
35 | use ra_prof::profile; | 35 | use ra_prof::profile; |
36 | use ra_syntax::ast::{self, AttrsOwner, NameOwner}; | 36 | use ra_syntax::ast::{self, AttrsOwner, NameOwner}; |
37 | use rustc_hash::FxHashSet; | 37 | use rustc_hash::FxHashSet; |
@@ -94,8 +94,8 @@ impl Crate { | |||
94 | db.crate_graph()[self.id].edition | 94 | db.crate_graph()[self.id].edition |
95 | } | 95 | } |
96 | 96 | ||
97 | pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateName> { | 97 | pub fn display_name(self, db: &dyn HirDatabase) -> Option<String> { |
98 | db.crate_graph()[self.id].display_name.as_ref().cloned() | 98 | db.crate_graph()[self.id].display_name.clone() |
99 | } | 99 | } |
100 | 100 | ||
101 | pub fn query_external_importables( | 101 | pub fn query_external_importables( |
@@ -1053,12 +1053,14 @@ pub struct ImplDef { | |||
1053 | 1053 | ||
1054 | impl ImplDef { | 1054 | impl ImplDef { |
1055 | pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<ImplDef> { | 1055 | pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<ImplDef> { |
1056 | let impls = db.impls_in_crate(krate.id); | 1056 | let inherent = db.inherent_impls_in_crate(krate.id); |
1057 | impls.all_impls().map(Self::from).collect() | 1057 | let trait_ = db.trait_impls_in_crate(krate.id); |
1058 | |||
1059 | inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect() | ||
1058 | } | 1060 | } |
1059 | pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<ImplDef> { | 1061 | pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<ImplDef> { |
1060 | let impls = db.impls_in_crate(krate.id); | 1062 | let impls = db.trait_impls_in_crate(krate.id); |
1061 | impls.lookup_impl_defs_for_trait(trait_.id).map(Self::from).collect() | 1063 | impls.for_trait(trait_.id).map(Self::from).collect() |
1062 | } | 1064 | } |
1063 | 1065 | ||
1064 | pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> { | 1066 | pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> { |
@@ -1187,7 +1189,7 @@ impl Type { | |||
1187 | None => return false, | 1189 | None => return false, |
1188 | }; | 1190 | }; |
1189 | 1191 | ||
1190 | let canonical_ty = Canonical { value: self.ty.value.clone(), num_vars: 0 }; | 1192 | let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; |
1191 | method_resolution::implements_trait( | 1193 | method_resolution::implements_trait( |
1192 | &canonical_ty, | 1194 | &canonical_ty, |
1193 | db, | 1195 | db, |
@@ -1211,7 +1213,7 @@ impl Type { | |||
1211 | self.ty.environment.clone(), | 1213 | self.ty.environment.clone(), |
1212 | hir_ty::Obligation::Trait(trait_ref), | 1214 | hir_ty::Obligation::Trait(trait_ref), |
1213 | ), | 1215 | ), |
1214 | num_vars: 0, | 1216 | kinds: Arc::new([]), |
1215 | }; | 1217 | }; |
1216 | 1218 | ||
1217 | db.trait_solve(self.krate, goal).is_some() | 1219 | db.trait_solve(self.krate, goal).is_some() |
@@ -1286,7 +1288,7 @@ impl Type { | |||
1286 | pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a { | 1288 | pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a { |
1287 | // There should be no inference vars in types passed here | 1289 | // There should be no inference vars in types passed here |
1288 | // FIXME check that? | 1290 | // FIXME check that? |
1289 | let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 }; | 1291 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; |
1290 | let environment = self.ty.environment.clone(); | 1292 | let environment = self.ty.environment.clone(); |
1291 | let ty = InEnvironment { value: canonical, environment }; | 1293 | let ty = InEnvironment { value: canonical, environment }; |
1292 | autoderef(db, Some(self.krate), ty) | 1294 | autoderef(db, Some(self.krate), ty) |
@@ -1303,10 +1305,10 @@ impl Type { | |||
1303 | mut callback: impl FnMut(AssocItem) -> Option<T>, | 1305 | mut callback: impl FnMut(AssocItem) -> Option<T>, |
1304 | ) -> Option<T> { | 1306 | ) -> Option<T> { |
1305 | for krate in self.ty.value.def_crates(db, krate.id)? { | 1307 | for krate in self.ty.value.def_crates(db, krate.id)? { |
1306 | let impls = db.impls_in_crate(krate); | 1308 | let impls = db.inherent_impls_in_crate(krate); |
1307 | 1309 | ||
1308 | for impl_def in impls.lookup_impl_defs(&self.ty.value) { | 1310 | for impl_def in impls.for_self_ty(&self.ty.value) { |
1309 | for &item in db.impl_data(impl_def).items.iter() { | 1311 | for &item in db.impl_data(*impl_def).items.iter() { |
1310 | if let Some(result) = callback(item.into()) { | 1312 | if let Some(result) = callback(item.into()) { |
1311 | return Some(result); | 1313 | return Some(result); |
1312 | } | 1314 | } |
@@ -1327,7 +1329,7 @@ impl Type { | |||
1327 | // There should be no inference vars in types passed here | 1329 | // There should be no inference vars in types passed here |
1328 | // FIXME check that? | 1330 | // FIXME check that? |
1329 | // FIXME replace Unknown by bound vars here | 1331 | // FIXME replace Unknown by bound vars here |
1330 | let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 }; | 1332 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; |
1331 | 1333 | ||
1332 | let env = self.ty.environment.clone(); | 1334 | let env = self.ty.environment.clone(); |
1333 | let krate = krate.id; | 1335 | let krate = krate.id; |
@@ -1358,7 +1360,7 @@ impl Type { | |||
1358 | // There should be no inference vars in types passed here | 1360 | // There should be no inference vars in types passed here |
1359 | // FIXME check that? | 1361 | // FIXME check that? |
1360 | // FIXME replace Unknown by bound vars here | 1362 | // FIXME replace Unknown by bound vars here |
1361 | let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 }; | 1363 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; |
1362 | 1364 | ||
1363 | let env = self.ty.environment.clone(); | 1365 | let env = self.ty.environment.clone(); |
1364 | let krate = krate.id; | 1366 | let krate = krate.id; |
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index bb67952de..cb48ca065 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -16,10 +16,10 @@ pub use hir_expand::db::{ | |||
16 | pub use hir_ty::db::{ | 16 | pub use hir_ty::db::{ |
17 | AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery, | 17 | AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery, |
18 | GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, | 18 | GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, |
19 | HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsFromDepsQuery, | 19 | HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, InferQueryQuery, |
20 | ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, | 20 | InherentImplsInCrateQuery, InternAssocTyValueQuery, InternChalkImplQuery, InternTypeCtorQuery, |
21 | InternTypeCtorQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery, | 21 | InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery, TraitDatumQuery, |
22 | TraitDatumQuery, TraitSolveQuery, TyQuery, ValueTyQuery, | 22 | TraitImplsInCrateQuery, TraitImplsInDepsQuery, TraitSolveQuery, TyQuery, ValueTyQuery, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | #[test] | 25 | #[test] |
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 6a49c424a..3d78f71c1 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -83,6 +83,11 @@ impl PathResolution { | |||
83 | /// Primary API to get semantic information, like types, from syntax trees. | 83 | /// Primary API to get semantic information, like types, from syntax trees. |
84 | pub struct Semantics<'db, DB> { | 84 | pub struct Semantics<'db, DB> { |
85 | pub db: &'db DB, | 85 | pub db: &'db DB, |
86 | imp: SemanticsImpl<'db>, | ||
87 | } | ||
88 | |||
89 | pub struct SemanticsImpl<'db> { | ||
90 | pub db: &'db dyn HirDatabase, | ||
86 | s2d_cache: RefCell<SourceToDefCache>, | 91 | s2d_cache: RefCell<SourceToDefCache>, |
87 | cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>, | 92 | cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>, |
88 | } | 93 | } |
@@ -95,20 +100,180 @@ impl<DB> fmt::Debug for Semantics<'_, DB> { | |||
95 | 100 | ||
96 | impl<'db, DB: HirDatabase> Semantics<'db, DB> { | 101 | impl<'db, DB: HirDatabase> Semantics<'db, DB> { |
97 | pub fn new(db: &DB) -> Semantics<DB> { | 102 | pub fn new(db: &DB) -> Semantics<DB> { |
98 | Semantics { db, s2d_cache: Default::default(), cache: Default::default() } | 103 | let impl_ = SemanticsImpl::new(db); |
104 | Semantics { db, imp: impl_ } | ||
99 | } | 105 | } |
100 | 106 | ||
101 | pub fn parse(&self, file_id: FileId) -> ast::SourceFile { | 107 | pub fn parse(&self, file_id: FileId) -> ast::SourceFile { |
102 | let tree = self.db.parse(file_id).tree(); | 108 | self.imp.parse(file_id) |
103 | self.cache(tree.syntax().clone(), file_id.into()); | ||
104 | tree | ||
105 | } | 109 | } |
106 | 110 | ||
107 | pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST { | 111 | pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST { |
108 | let file_id = d.source().file_id; | 112 | let file_id = d.source().file_id; |
109 | let root = self.db.parse_or_expand(file_id).unwrap(); | 113 | let root = self.db.parse_or_expand(file_id).unwrap(); |
110 | self.cache(root, file_id); | 114 | self.imp.cache(root, file_id); |
111 | d.ast(self.db) | 115 | d.ast(self.db.upcast()) |
116 | } | ||
117 | |||
118 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { | ||
119 | self.imp.expand(macro_call) | ||
120 | } | ||
121 | |||
122 | pub fn expand_hypothetical( | ||
123 | &self, | ||
124 | actual_macro_call: &ast::MacroCall, | ||
125 | hypothetical_args: &ast::TokenTree, | ||
126 | token_to_map: SyntaxToken, | ||
127 | ) -> Option<(SyntaxNode, SyntaxToken)> { | ||
128 | self.imp.expand_hypothetical(actual_macro_call, hypothetical_args, token_to_map) | ||
129 | } | ||
130 | |||
131 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { | ||
132 | self.imp.descend_into_macros(token) | ||
133 | } | ||
134 | |||
135 | pub fn descend_node_at_offset<N: ast::AstNode>( | ||
136 | &self, | ||
137 | node: &SyntaxNode, | ||
138 | offset: TextSize, | ||
139 | ) -> Option<N> { | ||
140 | self.imp.descend_node_at_offset(node, offset).find_map(N::cast) | ||
141 | } | ||
142 | |||
143 | pub fn original_range(&self, node: &SyntaxNode) -> FileRange { | ||
144 | self.imp.original_range(node) | ||
145 | } | ||
146 | |||
147 | pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { | ||
148 | self.imp.diagnostics_range(diagnostics) | ||
149 | } | ||
150 | |||
151 | pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
152 | self.imp.ancestors_with_macros(node) | ||
153 | } | ||
154 | |||
155 | pub fn ancestors_at_offset_with_macros( | ||
156 | &self, | ||
157 | node: &SyntaxNode, | ||
158 | offset: TextSize, | ||
159 | ) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
160 | self.imp.ancestors_at_offset_with_macros(node, offset) | ||
161 | } | ||
162 | |||
163 | /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*, | ||
164 | /// search up until it is of the target AstNode type | ||
165 | pub fn find_node_at_offset_with_macros<N: AstNode>( | ||
166 | &self, | ||
167 | node: &SyntaxNode, | ||
168 | offset: TextSize, | ||
169 | ) -> Option<N> { | ||
170 | self.imp.ancestors_at_offset_with_macros(node, offset).find_map(N::cast) | ||
171 | } | ||
172 | |||
173 | /// Find a AstNode by offset inside SyntaxNode, if it is inside *MacroCall*, | ||
174 | /// descend it and find again | ||
175 | pub fn find_node_at_offset_with_descend<N: AstNode>( | ||
176 | &self, | ||
177 | node: &SyntaxNode, | ||
178 | offset: TextSize, | ||
179 | ) -> Option<N> { | ||
180 | if let Some(it) = find_node_at_offset(&node, offset) { | ||
181 | return Some(it); | ||
182 | } | ||
183 | |||
184 | self.imp.descend_node_at_offset(node, offset).find_map(N::cast) | ||
185 | } | ||
186 | |||
187 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | ||
188 | self.imp.type_of_expr(expr) | ||
189 | } | ||
190 | |||
191 | pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> { | ||
192 | self.imp.type_of_pat(pat) | ||
193 | } | ||
194 | |||
195 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | ||
196 | self.imp.resolve_method_call(call) | ||
197 | } | ||
198 | |||
199 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> { | ||
200 | self.imp.resolve_field(field) | ||
201 | } | ||
202 | |||
203 | pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<(Field, Option<Local>)> { | ||
204 | self.imp.resolve_record_field(field) | ||
205 | } | ||
206 | |||
207 | pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<Field> { | ||
208 | self.imp.resolve_record_field_pat(field) | ||
209 | } | ||
210 | |||
211 | pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { | ||
212 | self.imp.resolve_macro_call(macro_call) | ||
213 | } | ||
214 | |||
215 | pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> { | ||
216 | self.imp.resolve_path(path) | ||
217 | } | ||
218 | |||
219 | pub fn resolve_variant(&self, record_lit: ast::RecordLit) -> Option<VariantId> { | ||
220 | self.imp.resolve_variant(record_lit) | ||
221 | } | ||
222 | |||
223 | pub fn lower_path(&self, path: &ast::Path) -> Option<Path> { | ||
224 | self.imp.lower_path(path) | ||
225 | } | ||
226 | |||
227 | pub fn resolve_bind_pat_to_const(&self, pat: &ast::BindPat) -> Option<ModuleDef> { | ||
228 | self.imp.resolve_bind_pat_to_const(pat) | ||
229 | } | ||
230 | |||
231 | // FIXME: use this instead? | ||
232 | // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>; | ||
233 | |||
234 | pub fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> { | ||
235 | self.imp.record_literal_missing_fields(literal) | ||
236 | } | ||
237 | |||
238 | pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> { | ||
239 | self.imp.record_pattern_missing_fields(pattern) | ||
240 | } | ||
241 | |||
242 | pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> { | ||
243 | let src = self.imp.find_file(src.syntax().clone()).with_value(src).cloned(); | ||
244 | T::to_def(&self.imp, src) | ||
245 | } | ||
246 | |||
247 | pub fn to_module_def(&self, file: FileId) -> Option<Module> { | ||
248 | self.imp.to_module_def(file) | ||
249 | } | ||
250 | |||
251 | pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> { | ||
252 | self.imp.scope(node) | ||
253 | } | ||
254 | |||
255 | pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> { | ||
256 | self.imp.scope_at_offset(node, offset) | ||
257 | } | ||
258 | |||
259 | pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> { | ||
260 | self.imp.scope_for_def(def) | ||
261 | } | ||
262 | |||
263 | pub fn assert_contains_node(&self, node: &SyntaxNode) { | ||
264 | self.imp.assert_contains_node(node) | ||
265 | } | ||
266 | } | ||
267 | |||
268 | impl<'db> SemanticsImpl<'db> { | ||
269 | pub fn new(db: &'db dyn HirDatabase) -> Self { | ||
270 | Self { db, s2d_cache: Default::default(), cache: Default::default() } | ||
271 | } | ||
272 | |||
273 | pub fn parse(&self, file_id: FileId) -> ast::SourceFile { | ||
274 | let tree = self.db.parse(file_id).tree(); | ||
275 | self.cache(tree.syntax().clone(), file_id.into()); | ||
276 | tree | ||
112 | } | 277 | } |
113 | 278 | ||
114 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { | 279 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { |
@@ -130,9 +295,15 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
130 | self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); | 295 | self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); |
131 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); | 296 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); |
132 | let krate = sa.resolver.krate()?; | 297 | let krate = sa.resolver.krate()?; |
133 | let macro_call_id = macro_call | 298 | let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| { |
134 | .as_call_id(self.db, krate, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; | 299 | sa.resolver.resolve_path_as_macro(self.db.upcast(), &path) |
135 | hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) | 300 | })?; |
301 | hir_expand::db::expand_hypothetical( | ||
302 | self.db.upcast(), | ||
303 | macro_call_id, | ||
304 | hypothetical_args, | ||
305 | token_to_map, | ||
306 | ) | ||
136 | } | 307 | } |
137 | 308 | ||
138 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { | 309 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { |
@@ -147,7 +318,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
147 | return None; | 318 | return None; |
148 | } | 319 | } |
149 | let file_id = sa.expand(self.db, token.with_value(¯o_call))?; | 320 | let file_id = sa.expand(self.db, token.with_value(¯o_call))?; |
150 | let token = file_id.expansion_info(self.db)?.map_token_down(token.as_ref())?; | 321 | let token = file_id.expansion_info(self.db.upcast())?.map_token_down(token.as_ref())?; |
151 | 322 | ||
152 | self.cache(find_root(&token.value.parent()), token.file_id); | 323 | self.cache(find_root(&token.value.parent()), token.file_id); |
153 | 324 | ||
@@ -159,15 +330,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
159 | token.value | 330 | token.value |
160 | } | 331 | } |
161 | 332 | ||
162 | pub fn descend_node_at_offset<N: ast::AstNode>( | 333 | pub fn descend_node_at_offset( |
163 | &self, | 334 | &self, |
164 | node: &SyntaxNode, | 335 | node: &SyntaxNode, |
165 | offset: TextSize, | 336 | offset: TextSize, |
166 | ) -> Option<N> { | 337 | ) -> impl Iterator<Item = SyntaxNode> + '_ { |
167 | // Handle macro token cases | 338 | // Handle macro token cases |
168 | node.token_at_offset(offset) | 339 | node.token_at_offset(offset) |
169 | .map(|token| self.descend_into_macros(token)) | 340 | .map(|token| self.descend_into_macros(token)) |
170 | .find_map(|it| self.ancestors_with_macros(it.parent()).find_map(N::cast)) | 341 | .map(|it| self.ancestors_with_macros(it.parent())) |
342 | .flatten() | ||
171 | } | 343 | } |
172 | 344 | ||
173 | pub fn original_range(&self, node: &SyntaxNode) -> FileRange { | 345 | pub fn original_range(&self, node: &SyntaxNode) -> FileRange { |
@@ -184,7 +356,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
184 | 356 | ||
185 | pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { | 357 | pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { |
186 | let node = self.find_file(node); | 358 | let node = self.find_file(node); |
187 | node.ancestors_with_macros(self.db).map(|it| it.value) | 359 | node.ancestors_with_macros(self.db.upcast()).map(|it| it.value) |
188 | } | 360 | } |
189 | 361 | ||
190 | pub fn ancestors_at_offset_with_macros( | 362 | pub fn ancestors_at_offset_with_macros( |
@@ -197,29 +369,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
197 | .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) | 369 | .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) |
198 | } | 370 | } |
199 | 371 | ||
200 | /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*, | ||
201 | /// search up until it is of the target AstNode type | ||
202 | pub fn find_node_at_offset_with_macros<N: AstNode>( | ||
203 | &self, | ||
204 | node: &SyntaxNode, | ||
205 | offset: TextSize, | ||
206 | ) -> Option<N> { | ||
207 | self.ancestors_at_offset_with_macros(node, offset).find_map(N::cast) | ||
208 | } | ||
209 | |||
210 | /// Find a AstNode by offset inside SyntaxNode, if it is inside *MacroCall*, | ||
211 | /// descend it and find again | ||
212 | pub fn find_node_at_offset_with_descend<N: AstNode>( | ||
213 | &self, | ||
214 | node: &SyntaxNode, | ||
215 | offset: TextSize, | ||
216 | ) -> Option<N> { | ||
217 | if let Some(it) = find_node_at_offset(&node, offset) { | ||
218 | return Some(it); | ||
219 | } | ||
220 | self.descend_node_at_offset(&node, offset) | ||
221 | } | ||
222 | |||
223 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | 372 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { |
224 | self.analyze(expr.syntax()).type_of(self.db, &expr) | 373 | self.analyze(expr.syntax()).type_of(self.db, &expr) |
225 | } | 374 | } |
@@ -267,9 +416,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
267 | self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat) | 416 | self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat) |
268 | } | 417 | } |
269 | 418 | ||
270 | // FIXME: use this instead? | ||
271 | // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>; | ||
272 | |||
273 | pub fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> { | 419 | pub fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> { |
274 | self.analyze(literal.syntax()) | 420 | self.analyze(literal.syntax()) |
275 | .record_literal_missing_fields(self.db, literal) | 421 | .record_literal_missing_fields(self.db, literal) |
@@ -282,11 +428,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
282 | .unwrap_or_default() | 428 | .unwrap_or_default() |
283 | } | 429 | } |
284 | 430 | ||
285 | pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> { | ||
286 | let src = self.find_file(src.syntax().clone()).with_value(src).cloned(); | ||
287 | T::to_def(self, src) | ||
288 | } | ||
289 | |||
290 | fn with_ctx<F: FnOnce(&mut SourceToDefCtx) -> T, T>(&self, f: F) -> T { | 431 | fn with_ctx<F: FnOnce(&mut SourceToDefCtx) -> T, T>(&self, f: F) -> T { |
291 | let mut cache = self.s2d_cache.borrow_mut(); | 432 | let mut cache = self.s2d_cache.borrow_mut(); |
292 | let mut ctx = SourceToDefCtx { db: self.db, cache: &mut *cache }; | 433 | let mut ctx = SourceToDefCtx { db: self.db, cache: &mut *cache }; |
@@ -297,20 +438,20 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
297 | self.with_ctx(|ctx| ctx.file_to_def(file)).map(Module::from) | 438 | self.with_ctx(|ctx| ctx.file_to_def(file)).map(Module::from) |
298 | } | 439 | } |
299 | 440 | ||
300 | pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db, DB> { | 441 | pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> { |
301 | let node = self.find_file(node.clone()); | 442 | let node = self.find_file(node.clone()); |
302 | let resolver = self.analyze2(node.as_ref(), None).resolver; | 443 | let resolver = self.analyze2(node.as_ref(), None).resolver; |
303 | SemanticsScope { db: self.db, resolver } | 444 | SemanticsScope { db: self.db, resolver } |
304 | } | 445 | } |
305 | 446 | ||
306 | pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db, DB> { | 447 | pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> { |
307 | let node = self.find_file(node.clone()); | 448 | let node = self.find_file(node.clone()); |
308 | let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver; | 449 | let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver; |
309 | SemanticsScope { db: self.db, resolver } | 450 | SemanticsScope { db: self.db, resolver } |
310 | } | 451 | } |
311 | 452 | ||
312 | pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db, DB> { | 453 | pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> { |
313 | let resolver = def.id.resolver(self.db); | 454 | let resolver = def.id.resolver(self.db.upcast()); |
314 | SemanticsScope { db: self.db, resolver } | 455 | SemanticsScope { db: self.db, resolver } |
315 | } | 456 | } |
316 | 457 | ||
@@ -331,17 +472,17 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
331 | ChildContainer::DefWithBodyId(def) => { | 472 | ChildContainer::DefWithBodyId(def) => { |
332 | return SourceAnalyzer::new_for_body(self.db, def, src, offset) | 473 | return SourceAnalyzer::new_for_body(self.db, def, src, offset) |
333 | } | 474 | } |
334 | ChildContainer::TraitId(it) => it.resolver(self.db), | 475 | ChildContainer::TraitId(it) => it.resolver(self.db.upcast()), |
335 | ChildContainer::ImplId(it) => it.resolver(self.db), | 476 | ChildContainer::ImplId(it) => it.resolver(self.db.upcast()), |
336 | ChildContainer::ModuleId(it) => it.resolver(self.db), | 477 | ChildContainer::ModuleId(it) => it.resolver(self.db.upcast()), |
337 | ChildContainer::EnumId(it) => it.resolver(self.db), | 478 | ChildContainer::EnumId(it) => it.resolver(self.db.upcast()), |
338 | ChildContainer::VariantId(it) => it.resolver(self.db), | 479 | ChildContainer::VariantId(it) => it.resolver(self.db.upcast()), |
339 | ChildContainer::GenericDefId(it) => it.resolver(self.db), | 480 | ChildContainer::GenericDefId(it) => it.resolver(self.db.upcast()), |
340 | }; | 481 | }; |
341 | SourceAnalyzer::new_for_resolver(resolver, src) | 482 | SourceAnalyzer::new_for_resolver(resolver, src) |
342 | } | 483 | } |
343 | 484 | ||
344 | fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) { | 485 | pub fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) { |
345 | assert!(root_node.parent().is_none()); | 486 | assert!(root_node.parent().is_none()); |
346 | let mut cache = self.cache.borrow_mut(); | 487 | let mut cache = self.cache.borrow_mut(); |
347 | let prev = cache.insert(root_node, file_id); | 488 | let prev = cache.insert(root_node, file_id); |
@@ -357,7 +498,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
357 | cache.get(root_node).copied() | 498 | cache.get(root_node).copied() |
358 | } | 499 | } |
359 | 500 | ||
360 | fn find_file(&self, node: SyntaxNode) -> InFile<SyntaxNode> { | 501 | pub fn find_file(&self, node: SyntaxNode) -> InFile<SyntaxNode> { |
361 | let root_node = find_root(&node); | 502 | let root_node = find_root(&node); |
362 | let file_id = self.lookup(&root_node).unwrap_or_else(|| { | 503 | let file_id = self.lookup(&root_node).unwrap_or_else(|| { |
363 | panic!( | 504 | panic!( |
@@ -382,14 +523,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
382 | pub trait ToDef: AstNode + Clone { | 523 | pub trait ToDef: AstNode + Clone { |
383 | type Def; | 524 | type Def; |
384 | 525 | ||
385 | fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: InFile<Self>) -> Option<Self::Def>; | 526 | fn to_def(sema: &SemanticsImpl, src: InFile<Self>) -> Option<Self::Def>; |
386 | } | 527 | } |
387 | 528 | ||
388 | macro_rules! to_def_impls { | 529 | macro_rules! to_def_impls { |
389 | ($(($def:path, $ast:path, $meth:ident)),* ,) => {$( | 530 | ($(($def:path, $ast:path, $meth:ident)),* ,) => {$( |
390 | impl ToDef for $ast { | 531 | impl ToDef for $ast { |
391 | type Def = $def; | 532 | type Def = $def; |
392 | fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: InFile<Self>) -> Option<Self::Def> { | 533 | fn to_def(sema: &SemanticsImpl, src: InFile<Self>) -> Option<Self::Def> { |
393 | sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from) | 534 | sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from) |
394 | } | 535 | } |
395 | } | 536 | } |
@@ -419,12 +560,12 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode { | |||
419 | node.ancestors().last().unwrap() | 560 | node.ancestors().last().unwrap() |
420 | } | 561 | } |
421 | 562 | ||
422 | pub struct SemanticsScope<'a, DB> { | 563 | pub struct SemanticsScope<'a> { |
423 | pub db: &'a DB, | 564 | pub db: &'a dyn HirDatabase, |
424 | resolver: Resolver, | 565 | resolver: Resolver, |
425 | } | 566 | } |
426 | 567 | ||
427 | impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> { | 568 | impl<'a> SemanticsScope<'a> { |
428 | pub fn module(&self) -> Option<Module> { | 569 | pub fn module(&self) -> Option<Module> { |
429 | Some(Module { id: self.resolver.module()? }) | 570 | Some(Module { id: self.resolver.module()? }) |
430 | } | 571 | } |
@@ -433,13 +574,13 @@ impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> { | |||
433 | // FIXME: rename to visible_traits to not repeat scope? | 574 | // FIXME: rename to visible_traits to not repeat scope? |
434 | pub fn traits_in_scope(&self) -> FxHashSet<TraitId> { | 575 | pub fn traits_in_scope(&self) -> FxHashSet<TraitId> { |
435 | let resolver = &self.resolver; | 576 | let resolver = &self.resolver; |
436 | resolver.traits_in_scope(self.db) | 577 | resolver.traits_in_scope(self.db.upcast()) |
437 | } | 578 | } |
438 | 579 | ||
439 | pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { | 580 | pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { |
440 | let resolver = &self.resolver; | 581 | let resolver = &self.resolver; |
441 | 582 | ||
442 | resolver.process_all_names(self.db, &mut |name, def| { | 583 | resolver.process_all_names(self.db.upcast(), &mut |name, def| { |
443 | let def = match def { | 584 | let def = match def { |
444 | resolver::ScopeDef::PerNs(it) => { | 585 | resolver::ScopeDef::PerNs(it) => { |
445 | let items = ScopeDef::all_items(it); | 586 | let items = ScopeDef::all_items(it); |
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index 4d446c707..beeb98559 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | //! Describes items defined or visible (ie, imported) in a certain scope. | 1 | //! Describes items defined or visible (ie, imported) in a certain scope. |
2 | //! This is shared between modules and blocks. | 2 | //! This is shared between modules and blocks. |
3 | 3 | ||
4 | use std::collections::hash_map::Entry; | ||
5 | |||
4 | use hir_expand::name::Name; | 6 | use hir_expand::name::Name; |
5 | use once_cell::sync::Lazy; | 7 | use once_cell::sync::Lazy; |
6 | use ra_db::CrateId; | 8 | use ra_db::CrateId; |
@@ -27,7 +29,11 @@ pub struct PerNsGlobImports { | |||
27 | 29 | ||
28 | #[derive(Debug, Default, PartialEq, Eq)] | 30 | #[derive(Debug, Default, PartialEq, Eq)] |
29 | pub struct ItemScope { | 31 | pub struct ItemScope { |
30 | visible: FxHashMap<Name, PerNs>, | 32 | types: FxHashMap<Name, (ModuleDefId, Visibility)>, |
33 | values: FxHashMap<Name, (ModuleDefId, Visibility)>, | ||
34 | macros: FxHashMap<Name, (MacroDefId, Visibility)>, | ||
35 | unresolved: FxHashSet<Name>, | ||
36 | |||
31 | defs: Vec<ModuleDefId>, | 37 | defs: Vec<ModuleDefId>, |
32 | impls: Vec<ImplId>, | 38 | impls: Vec<ImplId>, |
33 | /// Macros visible in current module in legacy textual scope | 39 | /// Macros visible in current module in legacy textual scope |
@@ -65,14 +71,16 @@ pub(crate) enum BuiltinShadowMode { | |||
65 | /// Other methods will only resolve values, types and module scoped macros only. | 71 | /// Other methods will only resolve values, types and module scoped macros only. |
66 | impl ItemScope { | 72 | impl ItemScope { |
67 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { | 73 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { |
68 | //FIXME: shadowing | 74 | // FIXME: shadowing |
69 | self.visible.iter().map(|(n, def)| (n, *def)) | 75 | let keys: FxHashSet<_> = self |
70 | } | 76 | .types |
71 | 77 | .keys() | |
72 | pub fn entries_without_primitives<'a>( | 78 | .chain(self.values.keys()) |
73 | &'a self, | 79 | .chain(self.macros.keys()) |
74 | ) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { | 80 | .chain(self.unresolved.iter()) |
75 | self.visible.iter().map(|(n, def)| (n, *def)) | 81 | .collect(); |
82 | |||
83 | keys.into_iter().map(move |name| (name, self.get(name))) | ||
76 | } | 84 | } |
77 | 85 | ||
78 | pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { | 86 | pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { |
@@ -91,7 +99,7 @@ impl ItemScope { | |||
91 | 99 | ||
92 | /// Iterate over all module scoped macros | 100 | /// Iterate over all module scoped macros |
93 | pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { | 101 | pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { |
94 | self.visible.iter().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) | 102 | self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) |
95 | } | 103 | } |
96 | 104 | ||
97 | /// Iterate over all legacy textual scoped macros visible at the end of the module | 105 | /// Iterate over all legacy textual scoped macros visible at the end of the module |
@@ -101,12 +109,16 @@ impl ItemScope { | |||
101 | 109 | ||
102 | /// Get a name from current module scope, legacy macros are not included | 110 | /// Get a name from current module scope, legacy macros are not included |
103 | pub(crate) fn get(&self, name: &Name) -> PerNs { | 111 | pub(crate) fn get(&self, name: &Name) -> PerNs { |
104 | self.visible.get(name).copied().unwrap_or_else(PerNs::none) | 112 | PerNs { |
113 | types: self.types.get(name).copied(), | ||
114 | values: self.values.get(name).copied(), | ||
115 | macros: self.macros.get(name).copied(), | ||
116 | } | ||
105 | } | 117 | } |
106 | 118 | ||
107 | pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { | 119 | pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { |
108 | for (name, per_ns) in &self.visible { | 120 | for (name, per_ns) in self.entries() { |
109 | if let Some(vis) = item.match_with(*per_ns) { | 121 | if let Some(vis) = item.match_with(per_ns) { |
110 | return Some((name, vis)); | 122 | return Some((name, vis)); |
111 | } | 123 | } |
112 | } | 124 | } |
@@ -114,8 +126,8 @@ impl ItemScope { | |||
114 | } | 126 | } |
115 | 127 | ||
116 | pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { | 128 | pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { |
117 | self.visible.values().filter_map(|def| match def.take_types() { | 129 | self.types.values().filter_map(|(def, _)| match def { |
118 | Some(ModuleDefId::TraitId(t)) => Some(t), | 130 | ModuleDefId::TraitId(t) => Some(*t), |
119 | _ => None, | 131 | _ => None, |
120 | }) | 132 | }) |
121 | } | 133 | } |
@@ -138,21 +150,30 @@ impl ItemScope { | |||
138 | 150 | ||
139 | pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool { | 151 | pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool { |
140 | let mut changed = false; | 152 | let mut changed = false; |
141 | let existing = self.visible.entry(name).or_default(); | ||
142 | 153 | ||
143 | if existing.types.is_none() && def.types.is_some() { | 154 | if let Some(types) = def.types { |
144 | existing.types = def.types; | 155 | self.types.entry(name.clone()).or_insert_with(|| { |
145 | changed = true; | 156 | changed = true; |
157 | types | ||
158 | }); | ||
146 | } | 159 | } |
147 | 160 | if let Some(values) = def.values { | |
148 | if existing.values.is_none() && def.values.is_some() { | 161 | self.values.entry(name.clone()).or_insert_with(|| { |
149 | existing.values = def.values; | 162 | changed = true; |
150 | changed = true; | 163 | values |
164 | }); | ||
165 | } | ||
166 | if let Some(macros) = def.macros { | ||
167 | self.macros.entry(name.clone()).or_insert_with(|| { | ||
168 | changed = true; | ||
169 | macros | ||
170 | }); | ||
151 | } | 171 | } |
152 | 172 | ||
153 | if existing.macros.is_none() && def.macros.is_some() { | 173 | if def.is_none() { |
154 | existing.macros = def.macros; | 174 | if self.unresolved.insert(name) { |
155 | changed = true; | 175 | changed = true; |
176 | } | ||
156 | } | 177 | } |
157 | 178 | ||
158 | changed | 179 | changed |
@@ -166,17 +187,17 @@ impl ItemScope { | |||
166 | def_import_type: ImportType, | 187 | def_import_type: ImportType, |
167 | ) -> bool { | 188 | ) -> bool { |
168 | let mut changed = false; | 189 | let mut changed = false; |
169 | let existing = self.visible.entry(lookup.1.clone()).or_default(); | ||
170 | 190 | ||
171 | macro_rules! check_changed { | 191 | macro_rules! check_changed { |
172 | ( | 192 | ( |
173 | $changed:ident, | 193 | $changed:ident, |
174 | ( $existing:ident / $def:ident ) . $field:ident, | 194 | ( $this:ident / $def:ident ) . $field:ident, |
175 | $glob_imports:ident [ $lookup:ident ], | 195 | $glob_imports:ident [ $lookup:ident ], |
176 | $def_import_type:ident | 196 | $def_import_type:ident |
177 | ) => { | 197 | ) => {{ |
178 | match ($existing.$field, $def.$field) { | 198 | let existing = $this.$field.entry($lookup.1.clone()); |
179 | (None, Some(_)) => { | 199 | match (existing, $def.$field) { |
200 | (Entry::Vacant(entry), Some(_)) => { | ||
180 | match $def_import_type { | 201 | match $def_import_type { |
181 | ImportType::Glob => { | 202 | ImportType::Glob => { |
182 | $glob_imports.$field.insert($lookup.clone()); | 203 | $glob_imports.$field.insert($lookup.clone()); |
@@ -186,32 +207,42 @@ impl ItemScope { | |||
186 | } | 207 | } |
187 | } | 208 | } |
188 | 209 | ||
189 | $existing.$field = $def.$field; | 210 | if let Some(fld) = $def.$field { |
211 | entry.insert(fld); | ||
212 | } | ||
190 | $changed = true; | 213 | $changed = true; |
191 | } | 214 | } |
192 | (Some(_), Some(_)) | 215 | (Entry::Occupied(mut entry), Some(_)) |
193 | if $glob_imports.$field.contains(&$lookup) | 216 | if $glob_imports.$field.contains(&$lookup) |
194 | && matches!($def_import_type, ImportType::Named) => | 217 | && matches!($def_import_type, ImportType::Named) => |
195 | { | 218 | { |
196 | mark::hit!(import_shadowed); | 219 | mark::hit!(import_shadowed); |
197 | $glob_imports.$field.remove(&$lookup); | 220 | $glob_imports.$field.remove(&$lookup); |
198 | $existing.$field = $def.$field; | 221 | if let Some(fld) = $def.$field { |
222 | entry.insert(fld); | ||
223 | } | ||
199 | $changed = true; | 224 | $changed = true; |
200 | } | 225 | } |
201 | _ => {} | 226 | _ => {} |
202 | } | 227 | } |
203 | }; | 228 | }}; |
204 | } | 229 | } |
205 | 230 | ||
206 | check_changed!(changed, (existing / def).types, glob_imports[lookup], def_import_type); | 231 | check_changed!(changed, (self / def).types, glob_imports[lookup], def_import_type); |
207 | check_changed!(changed, (existing / def).values, glob_imports[lookup], def_import_type); | 232 | check_changed!(changed, (self / def).values, glob_imports[lookup], def_import_type); |
208 | check_changed!(changed, (existing / def).macros, glob_imports[lookup], def_import_type); | 233 | check_changed!(changed, (self / def).macros, glob_imports[lookup], def_import_type); |
234 | |||
235 | if def.is_none() { | ||
236 | if self.unresolved.insert(lookup.1) { | ||
237 | changed = true; | ||
238 | } | ||
239 | } | ||
209 | 240 | ||
210 | changed | 241 | changed |
211 | } | 242 | } |
212 | 243 | ||
213 | pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a { | 244 | pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a { |
214 | self.visible.iter().map(|(name, res)| (name.clone(), *res)) | 245 | self.entries().map(|(name, res)| (name.clone(), res)) |
215 | } | 246 | } |
216 | 247 | ||
217 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { | 248 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { |
diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs index 5149dd141..06743d7fc 100644 --- a/crates/ra_hir_def/src/item_tree/lower.rs +++ b/crates/ra_hir_def/src/item_tree/lower.rs | |||
@@ -211,7 +211,7 @@ impl Ctx { | |||
211 | fn lower_record_field(&mut self, field: &ast::RecordFieldDef) -> Option<Field> { | 211 | fn lower_record_field(&mut self, field: &ast::RecordFieldDef) -> Option<Field> { |
212 | let name = field.name()?.as_name(); | 212 | let name = field.name()?.as_name(); |
213 | let visibility = self.lower_visibility(field); | 213 | let visibility = self.lower_visibility(field); |
214 | let type_ref = self.lower_type_ref(&field.ascribed_type()?); | 214 | let type_ref = self.lower_type_ref_opt(field.ascribed_type()); |
215 | let res = Field { name, type_ref, visibility }; | 215 | let res = Field { name, type_ref, visibility }; |
216 | Some(res) | 216 | Some(res) |
217 | } | 217 | } |
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index 15fdd9019..0bf51eb7b 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs | |||
@@ -511,11 +511,9 @@ impl Scope { | |||
511 | }); | 511 | }); |
512 | } | 512 | } |
513 | } | 513 | } |
514 | Scope::LocalItemsScope(body) => { | 514 | Scope::LocalItemsScope(body) => body.item_scope.entries().for_each(|(name, def)| { |
515 | body.item_scope.entries_without_primitives().for_each(|(name, def)| { | 515 | f(name.clone(), ScopeDef::PerNs(def)); |
516 | f(name.clone(), ScopeDef::PerNs(def)); | 516 | }), |
517 | }) | ||
518 | } | ||
519 | Scope::GenericParams { params, def } => { | 517 | Scope::GenericParams { params, def } => { |
520 | for (local_id, param) in params.types.iter() { | 518 | for (local_id, param) in params.types.iter() { |
521 | if let Some(name) = ¶m.name { | 519 | if let Some(name) = ¶m.name { |
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs index 26b667b55..f2d664863 100644 --- a/crates/ra_hir_expand/src/builtin_derive.rs +++ b/crates/ra_hir_expand/src/builtin_derive.rs | |||
@@ -161,7 +161,7 @@ fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree { | |||
161 | // XXX | 161 | // XXX |
162 | // All crates except core itself should have a dependency on core, | 162 | // All crates except core itself should have a dependency on core, |
163 | // We detect `core` by seeing whether it doesn't have such a dependency. | 163 | // We detect `core` by seeing whether it doesn't have such a dependency. |
164 | let tt = if cg[krate].dependencies.iter().any(|dep| dep.name == "core") { | 164 | let tt = if cg[krate].dependencies.iter().any(|dep| &*dep.name == "core") { |
165 | quote! { core } | 165 | quote! { core } |
166 | } else { | 166 | } else { |
167 | quote! { crate } | 167 | quote! { crate } |
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs index 99209c6e8..545cff9bd 100644 --- a/crates/ra_hir_expand/src/diagnostics.rs +++ b/crates/ra_hir_expand/src/diagnostics.rs | |||
@@ -28,7 +28,7 @@ pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | |||
28 | 28 | ||
29 | pub trait AstDiagnostic { | 29 | pub trait AstDiagnostic { |
30 | type AST; | 30 | type AST; |
31 | fn ast(&self, db: &impl AstDatabase) -> Self::AST; | 31 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST; |
32 | } | 32 | } |
33 | 33 | ||
34 | impl dyn Diagnostic { | 34 | impl dyn Diagnostic { |
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index 1b0303685..969a2e5b8 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs | |||
@@ -117,7 +117,7 @@ impl AsName for ast::FieldKind { | |||
117 | 117 | ||
118 | impl AsName for ra_db::Dependency { | 118 | impl AsName for ra_db::Dependency { |
119 | fn as_name(&self) -> Name { | 119 | fn as_name(&self) -> Name { |
120 | Name::new_text(self.name.clone()) | 120 | Name::new_text(SmolStr::new(&*self.name)) |
121 | } | 121 | } |
122 | } | 122 | } |
123 | 123 | ||
diff --git a/crates/ra_hir_ty/src/autoderef.rs b/crates/ra_hir_ty/src/autoderef.rs index 1b0f84c5c..c727012c6 100644 --- a/crates/ra_hir_ty/src/autoderef.rs +++ b/crates/ra_hir_ty/src/autoderef.rs | |||
@@ -37,7 +37,7 @@ pub(crate) fn deref( | |||
37 | ty: InEnvironment<&Canonical<Ty>>, | 37 | ty: InEnvironment<&Canonical<Ty>>, |
38 | ) -> Option<Canonical<Ty>> { | 38 | ) -> Option<Canonical<Ty>> { |
39 | if let Some(derefed) = ty.value.value.builtin_deref() { | 39 | if let Some(derefed) = ty.value.value.builtin_deref() { |
40 | Some(Canonical { value: derefed, num_vars: ty.value.num_vars }) | 40 | Some(Canonical { value: derefed, kinds: ty.value.kinds.clone() }) |
41 | } else { | 41 | } else { |
42 | deref_by_trait(db, krate, ty) | 42 | deref_by_trait(db, krate, ty) |
43 | } | 43 | } |
@@ -68,8 +68,8 @@ fn deref_by_trait( | |||
68 | 68 | ||
69 | // Check that the type implements Deref at all | 69 | // Check that the type implements Deref at all |
70 | let trait_ref = TraitRef { trait_: deref_trait, substs: parameters.clone() }; | 70 | let trait_ref = TraitRef { trait_: deref_trait, substs: parameters.clone() }; |
71 | let implements_goal = super::Canonical { | 71 | let implements_goal = Canonical { |
72 | num_vars: ty.value.num_vars, | 72 | kinds: ty.value.kinds.clone(), |
73 | value: InEnvironment { | 73 | value: InEnvironment { |
74 | value: Obligation::Trait(trait_ref), | 74 | value: Obligation::Trait(trait_ref), |
75 | environment: ty.environment.clone(), | 75 | environment: ty.environment.clone(), |
@@ -81,7 +81,7 @@ fn deref_by_trait( | |||
81 | 81 | ||
82 | // Now do the assoc type projection | 82 | // Now do the assoc type projection |
83 | let projection = super::traits::ProjectionPredicate { | 83 | let projection = super::traits::ProjectionPredicate { |
84 | ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.num_vars)), | 84 | ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.kinds.len())), |
85 | projection_ty: super::ProjectionTy { associated_ty: target, parameters }, | 85 | projection_ty: super::ProjectionTy { associated_ty: target, parameters }, |
86 | }; | 86 | }; |
87 | 87 | ||
@@ -89,7 +89,8 @@ fn deref_by_trait( | |||
89 | 89 | ||
90 | let in_env = InEnvironment { value: obligation, environment: ty.environment }; | 90 | let in_env = InEnvironment { value: obligation, environment: ty.environment }; |
91 | 91 | ||
92 | let canonical = super::Canonical { num_vars: 1 + ty.value.num_vars, value: in_env }; | 92 | let canonical = |
93 | Canonical::new(in_env, ty.value.kinds.iter().copied().chain(Some(super::TyKind::General))); | ||
93 | 94 | ||
94 | let solution = db.trait_solve(krate, canonical)?; | 95 | let solution = db.trait_solve(krate, canonical)?; |
95 | 96 | ||
@@ -110,7 +111,7 @@ fn deref_by_trait( | |||
110 | // assumptions will be broken. We would need to properly introduce | 111 | // assumptions will be broken. We would need to properly introduce |
111 | // new variables in that case | 112 | // new variables in that case |
112 | 113 | ||
113 | for i in 1..vars.0.num_vars { | 114 | for i in 1..vars.0.kinds.len() { |
114 | if vars.0.value[i - 1] != Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) | 115 | if vars.0.value[i - 1] != Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) |
115 | { | 116 | { |
116 | warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.value, solution); | 117 | warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.value, solution); |
@@ -119,7 +120,7 @@ fn deref_by_trait( | |||
119 | } | 120 | } |
120 | Some(Canonical { | 121 | Some(Canonical { |
121 | value: vars.0.value[vars.0.value.len() - 1].clone(), | 122 | value: vars.0.value[vars.0.value.len() - 1].clone(), |
122 | num_vars: vars.0.num_vars, | 123 | kinds: vars.0.kinds.clone(), |
123 | }) | 124 | }) |
124 | } | 125 | } |
125 | Solution::Ambig(_) => { | 126 | Solution::Ambig(_) => { |
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs index cad553273..dc06c0ee7 100644 --- a/crates/ra_hir_ty/src/db.rs +++ b/crates/ra_hir_ty/src/db.rs | |||
@@ -11,7 +11,7 @@ use ra_db::{impl_intern_key, salsa, CrateId, Upcast}; | |||
11 | use ra_prof::profile; | 11 | use ra_prof::profile; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | method_resolution::CrateImplDefs, | 14 | method_resolution::{InherentImpls, TraitImpls}, |
15 | traits::{chalk, AssocTyValue, Impl}, | 15 | traits::{chalk, AssocTyValue, Impl}, |
16 | Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig, | 16 | Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig, |
17 | ReturnTypeImplTraits, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId, | 17 | ReturnTypeImplTraits, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId, |
@@ -67,11 +67,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
67 | #[salsa::invoke(crate::lower::generic_defaults_query)] | 67 | #[salsa::invoke(crate::lower::generic_defaults_query)] |
68 | fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders<Ty>]>; | 68 | fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders<Ty>]>; |
69 | 69 | ||
70 | #[salsa::invoke(crate::method_resolution::CrateImplDefs::impls_in_crate_query)] | 70 | #[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)] |
71 | fn impls_in_crate(&self, krate: CrateId) -> Arc<CrateImplDefs>; | 71 | fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>; |
72 | 72 | ||
73 | #[salsa::invoke(crate::method_resolution::CrateImplDefs::impls_from_deps_query)] | 73 | #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] |
74 | fn impls_from_deps(&self, krate: CrateId) -> Arc<CrateImplDefs>; | 74 | fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; |
75 | |||
76 | #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] | ||
77 | fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>; | ||
75 | 78 | ||
76 | // Interned IDs for Chalk integration | 79 | // Interned IDs for Chalk integration |
77 | #[salsa::interned] | 80 | #[salsa::interned] |
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index a59efb347..0289911de 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -32,7 +32,7 @@ impl Diagnostic for NoSuchField { | |||
32 | impl AstDiagnostic for NoSuchField { | 32 | impl AstDiagnostic for NoSuchField { |
33 | type AST = ast::RecordField; | 33 | type AST = ast::RecordField; |
34 | 34 | ||
35 | fn ast(&self, db: &impl AstDatabase) -> Self::AST { | 35 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { |
36 | let root = db.parse_or_expand(self.source().file_id).unwrap(); | 36 | let root = db.parse_or_expand(self.source().file_id).unwrap(); |
37 | let node = self.source().value.to_node(&root); | 37 | let node = self.source().value.to_node(&root); |
38 | ast::RecordField::cast(node).unwrap() | 38 | ast::RecordField::cast(node).unwrap() |
@@ -65,7 +65,7 @@ impl Diagnostic for MissingFields { | |||
65 | impl AstDiagnostic for MissingFields { | 65 | impl AstDiagnostic for MissingFields { |
66 | type AST = ast::RecordFieldList; | 66 | type AST = ast::RecordFieldList; |
67 | 67 | ||
68 | fn ast(&self, db: &impl AstDatabase) -> Self::AST { | 68 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { |
69 | let root = db.parse_or_expand(self.source().file_id).unwrap(); | 69 | let root = db.parse_or_expand(self.source().file_id).unwrap(); |
70 | let node = self.source().value.to_node(&root); | 70 | let node = self.source().value.to_node(&root); |
71 | ast::RecordFieldList::cast(node).unwrap() | 71 | ast::RecordFieldList::cast(node).unwrap() |
@@ -135,7 +135,7 @@ impl Diagnostic for MissingOkInTailExpr { | |||
135 | impl AstDiagnostic for MissingOkInTailExpr { | 135 | impl AstDiagnostic for MissingOkInTailExpr { |
136 | type AST = ast::Expr; | 136 | type AST = ast::Expr; |
137 | 137 | ||
138 | fn ast(&self, db: &impl AstDatabase) -> Self::AST { | 138 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { |
139 | let root = db.parse_or_expand(self.file).unwrap(); | 139 | let root = db.parse_or_expand(self.file).unwrap(); |
140 | let node = self.source().value.to_node(&root); | 140 | let node = self.source().value.to_node(&root); |
141 | ast::Expr::cast(node).unwrap() | 141 | ast::Expr::cast(node).unwrap() |
@@ -163,7 +163,7 @@ impl Diagnostic for BreakOutsideOfLoop { | |||
163 | impl AstDiagnostic for BreakOutsideOfLoop { | 163 | impl AstDiagnostic for BreakOutsideOfLoop { |
164 | type AST = ast::Expr; | 164 | type AST = ast::Expr; |
165 | 165 | ||
166 | fn ast(&self, db: &impl AstDatabase) -> Self::AST { | 166 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { |
167 | let root = db.parse_or_expand(self.file).unwrap(); | 167 | let root = db.parse_or_expand(self.file).unwrap(); |
168 | let node = self.source().value.to_node(&root); | 168 | let node = self.source().value.to_node(&root); |
169 | ast::Expr::cast(node).unwrap() | 169 | ast::Expr::cast(node).unwrap() |
@@ -191,7 +191,7 @@ impl Diagnostic for MissingUnsafe { | |||
191 | impl AstDiagnostic for MissingUnsafe { | 191 | impl AstDiagnostic for MissingUnsafe { |
192 | type AST = ast::Expr; | 192 | type AST = ast::Expr; |
193 | 193 | ||
194 | fn ast(&self, db: &impl AstDatabase) -> Self::AST { | 194 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { |
195 | let root = db.parse_or_expand(self.source().file_id).unwrap(); | 195 | let root = db.parse_or_expand(self.source().file_id).unwrap(); |
196 | let node = self.source().value.to_node(&root); | 196 | let node = self.source().value.to_node(&root); |
197 | ast::Expr::cast(node).unwrap() | 197 | ast::Expr::cast(node).unwrap() |
diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index 269495ca0..2e895d911 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs | |||
@@ -9,7 +9,7 @@ use test_utils::mark; | |||
9 | use super::{InferenceContext, Obligation}; | 9 | use super::{InferenceContext, Obligation}; |
10 | use crate::{ | 10 | use crate::{ |
11 | BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty, | 11 | BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty, |
12 | TypeCtor, TypeWalk, | 12 | TyKind, TypeCtor, TypeWalk, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | impl<'a> InferenceContext<'a> { | 15 | impl<'a> InferenceContext<'a> { |
@@ -86,10 +86,20 @@ where | |||
86 | } | 86 | } |
87 | 87 | ||
88 | fn into_canonicalized<T>(self, result: T) -> Canonicalized<T> { | 88 | fn into_canonicalized<T>(self, result: T) -> Canonicalized<T> { |
89 | Canonicalized { | 89 | let kinds = self |
90 | value: Canonical { value: result, num_vars: self.free_vars.len() }, | 90 | .free_vars |
91 | free_vars: self.free_vars, | 91 | .iter() |
92 | } | 92 | .map(|v| match v { |
93 | // mapping MaybeNeverTypeVar to the same kind as general ones | ||
94 | // should be fine, because as opposed to int or float type vars, | ||
95 | // they don't restrict what kind of type can go into them, they | ||
96 | // just affect fallback. | ||
97 | InferTy::TypeVar(_) | InferTy::MaybeNeverTypeVar(_) => TyKind::General, | ||
98 | InferTy::IntVar(_) => TyKind::Integer, | ||
99 | InferTy::FloatVar(_) => TyKind::Float, | ||
100 | }) | ||
101 | .collect(); | ||
102 | Canonicalized { value: Canonical { value: result, kinds }, free_vars: self.free_vars } | ||
93 | } | 103 | } |
94 | 104 | ||
95 | pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> { | 105 | pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> { |
@@ -131,26 +141,41 @@ impl<T> Canonicalized<T> { | |||
131 | ty | 141 | ty |
132 | } | 142 | } |
133 | 143 | ||
134 | pub fn apply_solution(&self, ctx: &mut InferenceContext<'_>, solution: Canonical<Vec<Ty>>) { | 144 | pub fn apply_solution(&self, ctx: &mut InferenceContext<'_>, solution: Canonical<Substs>) { |
135 | // the solution may contain new variables, which we need to convert to new inference vars | 145 | // the solution may contain new variables, which we need to convert to new inference vars |
136 | let new_vars = Substs((0..solution.num_vars).map(|_| ctx.table.new_type_var()).collect()); | 146 | let new_vars = Substs( |
147 | solution | ||
148 | .kinds | ||
149 | .iter() | ||
150 | .map(|k| match k { | ||
151 | TyKind::General => ctx.table.new_type_var(), | ||
152 | TyKind::Integer => ctx.table.new_integer_var(), | ||
153 | TyKind::Float => ctx.table.new_float_var(), | ||
154 | }) | ||
155 | .collect(), | ||
156 | ); | ||
137 | for (i, ty) in solution.value.into_iter().enumerate() { | 157 | for (i, ty) in solution.value.into_iter().enumerate() { |
138 | let var = self.free_vars[i]; | 158 | let var = self.free_vars[i]; |
139 | // eagerly replace projections in the type; we may be getting types | 159 | // eagerly replace projections in the type; we may be getting types |
140 | // e.g. from where clauses where this hasn't happened yet | 160 | // e.g. from where clauses where this hasn't happened yet |
141 | let ty = ctx.normalize_associated_types_in(ty.subst_bound_vars(&new_vars)); | 161 | let ty = ctx.normalize_associated_types_in(ty.clone().subst_bound_vars(&new_vars)); |
142 | ctx.table.unify(&Ty::Infer(var), &ty); | 162 | ctx.table.unify(&Ty::Infer(var), &ty); |
143 | } | 163 | } |
144 | } | 164 | } |
145 | } | 165 | } |
146 | 166 | ||
147 | pub fn unify(ty1: &Canonical<Ty>, ty2: &Canonical<Ty>) -> Option<Substs> { | 167 | pub fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substs> { |
148 | let mut table = InferenceTable::new(); | 168 | let mut table = InferenceTable::new(); |
149 | let num_vars = ty1.num_vars.max(ty2.num_vars); | 169 | let vars = Substs( |
150 | let vars = | 170 | tys.kinds |
151 | Substs::builder(num_vars).fill(std::iter::repeat_with(|| table.new_type_var())).build(); | 171 | .iter() |
152 | let ty1_with_vars = ty1.value.clone().subst_bound_vars(&vars); | 172 | // we always use type vars here because we want everything to |
153 | let ty2_with_vars = ty2.value.clone().subst_bound_vars(&vars); | 173 | // fallback to Unknown in the end (kind of hacky, as below) |
174 | .map(|_| table.new_type_var()) | ||
175 | .collect(), | ||
176 | ); | ||
177 | let ty1_with_vars = tys.value.0.clone().subst_bound_vars(&vars); | ||
178 | let ty2_with_vars = tys.value.1.clone().subst_bound_vars(&vars); | ||
154 | if !table.unify(&ty1_with_vars, &ty2_with_vars) { | 179 | if !table.unify(&ty1_with_vars, &ty2_with_vars) { |
155 | return None; | 180 | return None; |
156 | } | 181 | } |
@@ -162,7 +187,7 @@ pub fn unify(ty1: &Canonical<Ty>, ty2: &Canonical<Ty>) -> Option<Substs> { | |||
162 | } | 187 | } |
163 | } | 188 | } |
164 | Some( | 189 | Some( |
165 | Substs::builder(ty1.num_vars) | 190 | Substs::builder(tys.kinds.len()) |
166 | .fill(vars.iter().map(|v| table.resolve_ty_completely(v.clone()))) | 191 | .fill(vars.iter().map(|v| table.resolve_ty_completely(v.clone()))) |
167 | .build(), | 192 | .build(), |
168 | ) | 193 | ) |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index c9513b752..7f3f5e771 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -662,13 +662,27 @@ impl TypeWalk for GenericPredicate { | |||
662 | 662 | ||
663 | /// Basically a claim (currently not validated / checked) that the contained | 663 | /// Basically a claim (currently not validated / checked) that the contained |
664 | /// type / trait ref contains no inference variables; any inference variables it | 664 | /// type / trait ref contains no inference variables; any inference variables it |
665 | /// contained have been replaced by bound variables, and `num_vars` tells us how | 665 | /// contained have been replaced by bound variables, and `kinds` tells us how |
666 | /// many there are. This is used to erase irrelevant differences between types | 666 | /// many there are and whether they were normal or float/int variables. This is |
667 | /// before using them in queries. | 667 | /// used to erase irrelevant differences between types before using them in |
668 | /// queries. | ||
668 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 669 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
669 | pub struct Canonical<T> { | 670 | pub struct Canonical<T> { |
670 | pub value: T, | 671 | pub value: T, |
671 | pub num_vars: usize, | 672 | pub kinds: Arc<[TyKind]>, |
673 | } | ||
674 | |||
675 | impl<T> Canonical<T> { | ||
676 | pub fn new(value: T, kinds: impl IntoIterator<Item = TyKind>) -> Self { | ||
677 | Self { value, kinds: kinds.into_iter().collect() } | ||
678 | } | ||
679 | } | ||
680 | |||
681 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
682 | pub enum TyKind { | ||
683 | General, | ||
684 | Integer, | ||
685 | Float, | ||
672 | } | 686 | } |
673 | 687 | ||
674 | /// A function signature as seen by type inference: Several parameter types and | 688 | /// A function signature as seen by type inference: Several parameter types and |
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index c19519cf1..a45febbf7 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | //! For details about how this works in rustc, see the method lookup page in the | 2 | //! For details about how this works in rustc, see the method lookup page in the |
3 | //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html) | 3 | //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html) |
4 | //! and the corresponding code mostly in librustc_typeck/check/method/probe.rs. | 4 | //! and the corresponding code mostly in librustc_typeck/check/method/probe.rs. |
5 | use std::sync::Arc; | 5 | use std::{iter, sync::Arc}; |
6 | 6 | ||
7 | use arrayvec::ArrayVec; | 7 | use arrayvec::ArrayVec; |
8 | use hir_def::{ | 8 | use hir_def::{ |
@@ -17,7 +17,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
17 | use super::Substs; | 17 | use super::Substs; |
18 | use crate::{ | 18 | use crate::{ |
19 | autoderef, db::HirDatabase, primitive::FloatBitness, utils::all_super_traits, ApplicationTy, | 19 | autoderef, db::HirDatabase, primitive::FloatBitness, utils::all_super_traits, ApplicationTy, |
20 | Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, | 20 | Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TyKind, TypeCtor, |
21 | TypeWalk, | ||
21 | }; | 22 | }; |
22 | 23 | ||
23 | /// This is used as a key for indexing impls. | 24 | /// This is used as a key for indexing impls. |
@@ -38,136 +39,131 @@ impl TyFingerprint { | |||
38 | } | 39 | } |
39 | } | 40 | } |
40 | 41 | ||
41 | /// A queryable and mergeable collection of impls. | 42 | /// Trait impls defined or available in some crate. |
42 | #[derive(Debug, PartialEq, Eq)] | 43 | #[derive(Debug, Eq, PartialEq)] |
43 | pub struct CrateImplDefs { | 44 | pub struct TraitImpls { |
44 | inherent_impls: FxHashMap<TyFingerprint, Vec<ImplId>>, | 45 | // If the `Option<TyFingerprint>` is `None`, the impl may apply to any self type. |
45 | impls_by_trait: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>, | 46 | map: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>, |
46 | } | 47 | } |
47 | 48 | ||
48 | impl CrateImplDefs { | 49 | impl TraitImpls { |
49 | pub(crate) fn impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<CrateImplDefs> { | 50 | pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { |
50 | let _p = profile("impls_in_crate_query"); | 51 | let _p = profile("trait_impls_in_crate_query"); |
51 | let mut res = CrateImplDefs { | 52 | let mut impls = Self { map: FxHashMap::default() }; |
52 | inherent_impls: FxHashMap::default(), | ||
53 | impls_by_trait: FxHashMap::default(), | ||
54 | }; | ||
55 | res.fill(db, krate); | ||
56 | 53 | ||
57 | Arc::new(res) | 54 | let crate_def_map = db.crate_def_map(krate); |
55 | for (_module_id, module_data) in crate_def_map.modules.iter() { | ||
56 | for impl_id in module_data.scope.impls() { | ||
57 | let target_trait = match db.impl_trait(impl_id) { | ||
58 | Some(tr) => tr.value.trait_, | ||
59 | None => continue, | ||
60 | }; | ||
61 | let self_ty = db.impl_self_ty(impl_id); | ||
62 | let self_ty_fp = TyFingerprint::for_impl(&self_ty.value); | ||
63 | impls | ||
64 | .map | ||
65 | .entry(target_trait) | ||
66 | .or_default() | ||
67 | .entry(self_ty_fp) | ||
68 | .or_default() | ||
69 | .push(impl_id); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | Arc::new(impls) | ||
58 | } | 74 | } |
59 | 75 | ||
60 | /// Collects all impls from transitive dependencies of `krate` that may be used by `krate`. | 76 | pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { |
61 | /// | 77 | let _p = profile("trait_impls_in_deps_query"); |
62 | /// The full set of impls that can be used by `krate` is the returned map plus all the impls | ||
63 | /// from `krate` itself. | ||
64 | pub(crate) fn impls_from_deps_query( | ||
65 | db: &dyn HirDatabase, | ||
66 | krate: CrateId, | ||
67 | ) -> Arc<CrateImplDefs> { | ||
68 | let _p = profile("impls_from_deps_query"); | ||
69 | let crate_graph = db.crate_graph(); | 78 | let crate_graph = db.crate_graph(); |
70 | let mut res = CrateImplDefs { | 79 | let mut res = Self { map: FxHashMap::default() }; |
71 | inherent_impls: FxHashMap::default(), | ||
72 | impls_by_trait: FxHashMap::default(), | ||
73 | }; | ||
74 | 80 | ||
75 | // For each dependency, calculate `impls_from_deps` recursively, then add its own | 81 | for krate in crate_graph.transitive_deps(krate) { |
76 | // `impls_in_crate`. | 82 | res.merge(&db.trait_impls_in_crate(krate)); |
77 | // As we might visit crates multiple times, `merge` has to deduplicate impls to avoid | ||
78 | // wasting memory. | ||
79 | for dep in &crate_graph[krate].dependencies { | ||
80 | res.merge(&db.impls_from_deps(dep.crate_id)); | ||
81 | res.merge(&db.impls_in_crate(dep.crate_id)); | ||
82 | } | 83 | } |
83 | 84 | ||
84 | Arc::new(res) | 85 | Arc::new(res) |
85 | } | 86 | } |
86 | 87 | ||
87 | fn fill(&mut self, db: &dyn HirDatabase, krate: CrateId) { | ||
88 | let crate_def_map = db.crate_def_map(krate); | ||
89 | for (_module_id, module_data) in crate_def_map.modules.iter() { | ||
90 | for impl_id in module_data.scope.impls() { | ||
91 | match db.impl_trait(impl_id) { | ||
92 | Some(tr) => { | ||
93 | let self_ty = db.impl_self_ty(impl_id); | ||
94 | let self_ty_fp = TyFingerprint::for_impl(&self_ty.value); | ||
95 | self.impls_by_trait | ||
96 | .entry(tr.value.trait_) | ||
97 | .or_default() | ||
98 | .entry(self_ty_fp) | ||
99 | .or_default() | ||
100 | .push(impl_id); | ||
101 | } | ||
102 | None => { | ||
103 | let self_ty = db.impl_self_ty(impl_id); | ||
104 | if let Some(self_ty_fp) = TyFingerprint::for_impl(&self_ty.value) { | ||
105 | self.inherent_impls.entry(self_ty_fp).or_default().push(impl_id); | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | fn merge(&mut self, other: &Self) { | 88 | fn merge(&mut self, other: &Self) { |
114 | for (fp, impls) in &other.inherent_impls { | 89 | for (trait_, other_map) in &other.map { |
115 | let vec = self.inherent_impls.entry(*fp).or_default(); | 90 | let map = self.map.entry(*trait_).or_default(); |
116 | vec.extend(impls); | ||
117 | vec.sort(); | ||
118 | vec.dedup(); | ||
119 | } | ||
120 | |||
121 | for (trait_, other_map) in &other.impls_by_trait { | ||
122 | let map = self.impls_by_trait.entry(*trait_).or_default(); | ||
123 | for (fp, impls) in other_map { | 91 | for (fp, impls) in other_map { |
124 | let vec = map.entry(*fp).or_default(); | 92 | let vec = map.entry(*fp).or_default(); |
125 | vec.extend(impls); | 93 | vec.extend(impls); |
126 | vec.sort(); | ||
127 | vec.dedup(); | ||
128 | } | 94 | } |
129 | } | 95 | } |
130 | } | 96 | } |
131 | 97 | ||
132 | pub fn lookup_impl_defs(&self, ty: &Ty) -> impl Iterator<Item = ImplId> + '_ { | 98 | /// Queries all impls of the given trait. |
133 | let fingerprint = TyFingerprint::for_impl(ty); | 99 | pub fn for_trait(&self, trait_: TraitId) -> impl Iterator<Item = ImplId> + '_ { |
134 | fingerprint.and_then(|f| self.inherent_impls.get(&f)).into_iter().flatten().copied() | 100 | self.map |
135 | } | 101 | .get(&trait_) |
136 | |||
137 | pub fn lookup_impl_defs_for_trait(&self, tr: TraitId) -> impl Iterator<Item = ImplId> + '_ { | ||
138 | self.impls_by_trait | ||
139 | .get(&tr) | ||
140 | .into_iter() | 102 | .into_iter() |
141 | .flat_map(|m| m.values().flat_map(|v| v.iter().copied())) | 103 | .flat_map(|map| map.values().flat_map(|v| v.iter().copied())) |
142 | } | 104 | } |
143 | 105 | ||
144 | pub fn lookup_impl_defs_for_trait_and_ty( | 106 | /// Queries all impls of `trait_` that may apply to `self_ty`. |
107 | pub fn for_trait_and_self_ty( | ||
145 | &self, | 108 | &self, |
146 | tr: TraitId, | 109 | trait_: TraitId, |
147 | fp: TyFingerprint, | 110 | self_ty: TyFingerprint, |
148 | ) -> impl Iterator<Item = ImplId> + '_ { | 111 | ) -> impl Iterator<Item = ImplId> + '_ { |
149 | self.impls_by_trait | 112 | self.map |
150 | .get(&tr) | 113 | .get(&trait_) |
151 | .and_then(|m| m.get(&Some(fp))) | ||
152 | .into_iter() | 114 | .into_iter() |
153 | .flatten() | 115 | .flat_map(move |map| map.get(&None).into_iter().chain(map.get(&Some(self_ty)))) |
154 | .copied() | 116 | .flat_map(|v| v.iter().copied()) |
155 | .chain( | 117 | } |
156 | self.impls_by_trait | 118 | |
157 | .get(&tr) | 119 | pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ { |
158 | .and_then(|m| m.get(&None)) | 120 | self.map.values().flat_map(|map| map.values().flat_map(|v| v.iter().copied())) |
159 | .into_iter() | 121 | } |
160 | .flatten() | 122 | } |
161 | .copied(), | 123 | |
162 | ) | 124 | /// Inherent impls defined in some crate. |
125 | /// | ||
126 | /// Inherent impls can only be defined in the crate that also defines the self type of the impl | ||
127 | /// (note that some primitives are considered to be defined by both libcore and liballoc). | ||
128 | /// | ||
129 | /// This makes inherent impl lookup easier than trait impl lookup since we only have to consider a | ||
130 | /// single crate. | ||
131 | #[derive(Debug, Eq, PartialEq)] | ||
132 | pub struct InherentImpls { | ||
133 | map: FxHashMap<TyFingerprint, Vec<ImplId>>, | ||
134 | } | ||
135 | |||
136 | impl InherentImpls { | ||
137 | pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { | ||
138 | let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default(); | ||
139 | |||
140 | let crate_def_map = db.crate_def_map(krate); | ||
141 | for (_module_id, module_data) in crate_def_map.modules.iter() { | ||
142 | for impl_id in module_data.scope.impls() { | ||
143 | let data = db.impl_data(impl_id); | ||
144 | if data.target_trait.is_some() { | ||
145 | continue; | ||
146 | } | ||
147 | |||
148 | let self_ty = db.impl_self_ty(impl_id); | ||
149 | if let Some(fp) = TyFingerprint::for_impl(&self_ty.value) { | ||
150 | map.entry(fp).or_default().push(impl_id); | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | Arc::new(Self { map }) | ||
156 | } | ||
157 | |||
158 | pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] { | ||
159 | match TyFingerprint::for_impl(self_ty) { | ||
160 | Some(fp) => self.map.get(&fp).map(|vec| vec.as_ref()).unwrap_or(&[]), | ||
161 | None => &[], | ||
162 | } | ||
163 | } | 163 | } |
164 | 164 | ||
165 | pub fn all_impls<'a>(&'a self) -> impl Iterator<Item = ImplId> + 'a { | 165 | pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ { |
166 | self.inherent_impls | 166 | self.map.values().flat_map(|v| v.iter().copied()) |
167 | .values() | ||
168 | .chain(self.impls_by_trait.values().flat_map(|m| m.values())) | ||
169 | .flatten() | ||
170 | .copied() | ||
171 | } | 167 | } |
172 | } | 168 | } |
173 | 169 | ||
@@ -377,7 +373,7 @@ fn iterate_method_candidates_with_autoref( | |||
377 | return true; | 373 | return true; |
378 | } | 374 | } |
379 | let refed = Canonical { | 375 | let refed = Canonical { |
380 | num_vars: deref_chain[0].num_vars, | 376 | kinds: deref_chain[0].kinds.clone(), |
381 | value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()), | 377 | value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()), |
382 | }; | 378 | }; |
383 | if iterate_method_candidates_by_receiver( | 379 | if iterate_method_candidates_by_receiver( |
@@ -393,7 +389,7 @@ fn iterate_method_candidates_with_autoref( | |||
393 | return true; | 389 | return true; |
394 | } | 390 | } |
395 | let ref_muted = Canonical { | 391 | let ref_muted = Canonical { |
396 | num_vars: deref_chain[0].num_vars, | 392 | kinds: deref_chain[0].kinds.clone(), |
397 | value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()), | 393 | value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()), |
398 | }; | 394 | }; |
399 | if iterate_method_candidates_by_receiver( | 395 | if iterate_method_candidates_by_receiver( |
@@ -524,9 +520,9 @@ fn iterate_inherent_methods( | |||
524 | None => return false, | 520 | None => return false, |
525 | }; | 521 | }; |
526 | for krate in def_crates { | 522 | for krate in def_crates { |
527 | let impls = db.impls_in_crate(krate); | 523 | let impls = db.inherent_impls_in_crate(krate); |
528 | 524 | ||
529 | for impl_def in impls.lookup_impl_defs(&self_ty.value) { | 525 | for &impl_def in impls.for_self_ty(&self_ty.value) { |
530 | for &item in db.impl_data(impl_def).items.iter() { | 526 | for &item in db.impl_data(impl_def).items.iter() { |
531 | if !is_valid_candidate(db, name, receiver_ty, item, self_ty) { | 527 | if !is_valid_candidate(db, name, receiver_ty, item, self_ty) { |
532 | continue; | 528 | continue; |
@@ -612,18 +608,19 @@ pub(crate) fn inherent_impl_substs( | |||
612 | // we create a var for each type parameter of the impl; we need to keep in | 608 | // we create a var for each type parameter of the impl; we need to keep in |
613 | // mind here that `self_ty` might have vars of its own | 609 | // mind here that `self_ty` might have vars of its own |
614 | let vars = Substs::build_for_def(db, impl_id) | 610 | let vars = Substs::build_for_def(db, impl_id) |
615 | .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.num_vars) | 611 | .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.kinds.len()) |
616 | .build(); | 612 | .build(); |
617 | let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars); | 613 | let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars); |
618 | let self_ty_with_vars = | 614 | let mut kinds = self_ty.kinds.to_vec(); |
619 | Canonical { num_vars: vars.len() + self_ty.num_vars, value: self_ty_with_vars }; | 615 | kinds.extend(iter::repeat(TyKind::General).take(vars.len())); |
620 | let substs = super::infer::unify(&self_ty_with_vars, self_ty); | 616 | let tys = Canonical { kinds: kinds.into(), value: (self_ty_with_vars, self_ty.value.clone()) }; |
617 | let substs = super::infer::unify(&tys); | ||
621 | // We only want the substs for the vars we added, not the ones from self_ty. | 618 | // We only want the substs for the vars we added, not the ones from self_ty. |
622 | // Also, if any of the vars we added are still in there, we replace them by | 619 | // Also, if any of the vars we added are still in there, we replace them by |
623 | // Unknown. I think this can only really happen if self_ty contained | 620 | // Unknown. I think this can only really happen if self_ty contained |
624 | // Unknown, and in that case we want the result to contain Unknown in those | 621 | // Unknown, and in that case we want the result to contain Unknown in those |
625 | // places again. | 622 | // places again. |
626 | substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.num_vars)) | 623 | substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.kinds.len())) |
627 | } | 624 | } |
628 | 625 | ||
629 | /// This replaces any 'free' Bound vars in `s` (i.e. those with indices past | 626 | /// This replaces any 'free' Bound vars in `s` (i.e. those with indices past |
@@ -683,15 +680,15 @@ fn generic_implements_goal( | |||
683 | trait_: TraitId, | 680 | trait_: TraitId, |
684 | self_ty: Canonical<Ty>, | 681 | self_ty: Canonical<Ty>, |
685 | ) -> Canonical<InEnvironment<super::Obligation>> { | 682 | ) -> Canonical<InEnvironment<super::Obligation>> { |
686 | let num_vars = self_ty.num_vars; | 683 | let mut kinds = self_ty.kinds.to_vec(); |
687 | let substs = super::Substs::build_for_def(db, trait_) | 684 | let substs = super::Substs::build_for_def(db, trait_) |
688 | .push(self_ty.value) | 685 | .push(self_ty.value) |
689 | .fill_with_bound_vars(DebruijnIndex::INNERMOST, num_vars) | 686 | .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len()) |
690 | .build(); | 687 | .build(); |
691 | let num_vars = substs.len() - 1 + self_ty.num_vars; | 688 | kinds.extend(iter::repeat(TyKind::General).take(substs.len() - 1)); |
692 | let trait_ref = TraitRef { trait_, substs }; | 689 | let trait_ref = TraitRef { trait_, substs }; |
693 | let obligation = super::Obligation::Trait(trait_ref); | 690 | let obligation = super::Obligation::Trait(trait_ref); |
694 | Canonical { num_vars, value: InEnvironment::new(env, obligation) } | 691 | Canonical { kinds: kinds.into(), value: InEnvironment::new(env, obligation) } |
695 | } | 692 | } |
696 | 693 | ||
697 | fn autoderef_method_receiver( | 694 | fn autoderef_method_receiver( |
@@ -704,9 +701,9 @@ fn autoderef_method_receiver( | |||
704 | if let Some(Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, parameters })) = | 701 | if let Some(Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, parameters })) = |
705 | deref_chain.last().map(|ty| &ty.value) | 702 | deref_chain.last().map(|ty| &ty.value) |
706 | { | 703 | { |
707 | let num_vars = deref_chain.last().unwrap().num_vars; | 704 | let kinds = deref_chain.last().unwrap().kinds.clone(); |
708 | let unsized_ty = Ty::apply(TypeCtor::Slice, parameters.clone()); | 705 | let unsized_ty = Ty::apply(TypeCtor::Slice, parameters.clone()); |
709 | deref_chain.push(Canonical { value: unsized_ty, num_vars }) | 706 | deref_chain.push(Canonical { value: unsized_ty, kinds }) |
710 | } | 707 | } |
711 | deref_chain | 708 | deref_chain |
712 | } | 709 | } |
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index 0481a7b12..fddf0604d 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs | |||
@@ -8,8 +8,10 @@ use std::{ | |||
8 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; | 8 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; |
9 | use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; | 9 | use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; |
10 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; | 10 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; |
11 | use rustc_hash::FxHashSet; | 11 | use ra_syntax::TextRange; |
12 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
12 | use stdx::format_to; | 13 | use stdx::format_to; |
14 | use test_utils::extract_annotations; | ||
13 | 15 | ||
14 | use crate::{ | 16 | use crate::{ |
15 | db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator, | 17 | db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator, |
@@ -155,17 +157,27 @@ impl TestDB { | |||
155 | (buf, count) | 157 | (buf, count) |
156 | } | 158 | } |
157 | 159 | ||
158 | pub fn all_files(&self) -> Vec<FileId> { | 160 | pub fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { |
159 | let mut res = Vec::new(); | 161 | let mut files = Vec::new(); |
160 | let crate_graph = self.crate_graph(); | 162 | let crate_graph = self.crate_graph(); |
161 | for krate in crate_graph.iter() { | 163 | for krate in crate_graph.iter() { |
162 | let crate_def_map = self.crate_def_map(krate); | 164 | let crate_def_map = self.crate_def_map(krate); |
163 | for (module_id, _) in crate_def_map.modules.iter() { | 165 | for (module_id, _) in crate_def_map.modules.iter() { |
164 | let file_id = crate_def_map[module_id].origin.file_id(); | 166 | let file_id = crate_def_map[module_id].origin.file_id(); |
165 | res.extend(file_id) | 167 | files.extend(file_id) |
166 | } | 168 | } |
167 | } | 169 | } |
168 | res | 170 | files |
171 | .into_iter() | ||
172 | .filter_map(|file_id| { | ||
173 | let text = self.file_text(file_id); | ||
174 | let annotations = extract_annotations(&text); | ||
175 | if annotations.is_empty() { | ||
176 | return None; | ||
177 | } | ||
178 | Some((file_id, annotations)) | ||
179 | }) | ||
180 | .collect() | ||
169 | } | 181 | } |
170 | } | 182 | } |
171 | 183 | ||
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index 5424e6bb1..eeac34d14 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs | |||
@@ -28,7 +28,6 @@ use ra_syntax::{ | |||
28 | SyntaxNode, | 28 | SyntaxNode, |
29 | }; | 29 | }; |
30 | use stdx::format_to; | 30 | use stdx::format_to; |
31 | use test_utils::extract_annotations; | ||
32 | 31 | ||
33 | use crate::{ | 32 | use crate::{ |
34 | db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty, | 33 | db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty, |
@@ -49,9 +48,7 @@ fn check_types_source_code(ra_fixture: &str) { | |||
49 | fn check_types_impl(ra_fixture: &str, display_source: bool) { | 48 | fn check_types_impl(ra_fixture: &str, display_source: bool) { |
50 | let db = TestDB::with_files(ra_fixture); | 49 | let db = TestDB::with_files(ra_fixture); |
51 | let mut checked_one = false; | 50 | let mut checked_one = false; |
52 | for file_id in db.all_files() { | 51 | for (file_id, annotations) in db.extract_annotations() { |
53 | let text = db.parse(file_id).syntax_node().to_string(); | ||
54 | let annotations = extract_annotations(&text); | ||
55 | for (range, expected) in annotations { | 52 | for (range, expected) in annotations { |
56 | let ty = type_at_range(&db, FileRange { file_id, range }); | 53 | let ty = type_at_range(&db, FileRange { file_id, range }); |
57 | let actual = if display_source { | 54 | let actual = if display_source { |
@@ -511,6 +508,30 @@ fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() { | |||
511 | } | 508 | } |
512 | 509 | ||
513 | #[test] | 510 | #[test] |
511 | fn no_such_field_with_type_macro() { | ||
512 | let diagnostics = TestDB::with_files( | ||
513 | r" | ||
514 | macro_rules! Type { | ||
515 | () => { u32 }; | ||
516 | } | ||
517 | |||
518 | struct Foo { | ||
519 | bar: Type![], | ||
520 | } | ||
521 | impl Foo { | ||
522 | fn new() -> Self { | ||
523 | Foo { bar: 0 } | ||
524 | } | ||
525 | } | ||
526 | ", | ||
527 | ) | ||
528 | .diagnostics() | ||
529 | .0; | ||
530 | |||
531 | assert_snapshot!(diagnostics, @r###""###); | ||
532 | } | ||
533 | |||
534 | #[test] | ||
514 | fn missing_record_pat_field_diagnostic() { | 535 | fn missing_record_pat_field_diagnostic() { |
515 | let diagnostics = TestDB::with_files( | 536 | let diagnostics = TestDB::with_files( |
516 | r" | 537 | r" |
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 01c919a7e..766790576 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs | |||
@@ -3029,3 +3029,21 @@ fn infer_dyn_fn_output() { | |||
3029 | "### | 3029 | "### |
3030 | ); | 3030 | ); |
3031 | } | 3031 | } |
3032 | |||
3033 | #[test] | ||
3034 | fn variable_kinds() { | ||
3035 | check_types( | ||
3036 | r#" | ||
3037 | trait Trait<T> { fn get(self, t: T) -> T; } | ||
3038 | struct S; | ||
3039 | impl Trait<u128> for S {} | ||
3040 | impl Trait<f32> for S {} | ||
3041 | fn test() { | ||
3042 | S.get(1); | ||
3043 | //^^^^^^^^ u128 | ||
3044 | S.get(1.); | ||
3045 | //^^^^^^^^ f32 | ||
3046 | } | ||
3047 | "#, | ||
3048 | ); | ||
3049 | } | ||
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 6f43c3a22..2a6d7faef 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! Trait solving using Chalk. | 1 | //! Trait solving using Chalk. |
2 | use std::{panic, sync::Arc}; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use chalk_ir::cast::Cast; | 4 | use chalk_ir::cast::Cast; |
5 | use hir_def::{ | 5 | use hir_def::{ |
@@ -8,7 +8,7 @@ use hir_def::{ | |||
8 | use ra_db::{impl_intern_key, salsa, CrateId}; | 8 | use ra_db::{impl_intern_key, salsa, CrateId}; |
9 | use ra_prof::profile; | 9 | use ra_prof::profile; |
10 | 10 | ||
11 | use crate::{db::HirDatabase, DebruijnIndex}; | 11 | use crate::{db::HirDatabase, DebruijnIndex, Substs}; |
12 | 12 | ||
13 | use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; | 13 | use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; |
14 | 14 | ||
@@ -190,15 +190,7 @@ fn solution_from_chalk( | |||
190 | solution: chalk_solve::Solution<Interner>, | 190 | solution: chalk_solve::Solution<Interner>, |
191 | ) -> Solution { | 191 | ) -> Solution { |
192 | let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| { | 192 | let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| { |
193 | let value = subst | 193 | let result = from_chalk(db, subst); |
194 | .value | ||
195 | .iter(&Interner) | ||
196 | .map(|p| match p.ty(&Interner) { | ||
197 | Some(ty) => from_chalk(db, ty.clone()), | ||
198 | None => unimplemented!(), | ||
199 | }) | ||
200 | .collect(); | ||
201 | let result = Canonical { value, num_vars: subst.binders.len(&Interner) }; | ||
202 | SolutionVariables(result) | 194 | SolutionVariables(result) |
203 | }; | 195 | }; |
204 | match solution { | 196 | match solution { |
@@ -222,7 +214,7 @@ fn solution_from_chalk( | |||
222 | } | 214 | } |
223 | 215 | ||
224 | #[derive(Clone, Debug, PartialEq, Eq)] | 216 | #[derive(Clone, Debug, PartialEq, Eq)] |
225 | pub struct SolutionVariables(pub Canonical<Vec<Ty>>); | 217 | pub struct SolutionVariables(pub Canonical<Substs>); |
226 | 218 | ||
227 | #[derive(Clone, Debug, PartialEq, Eq)] | 219 | #[derive(Clone, Debug, PartialEq, Eq)] |
228 | /// A (possible) solution for a proposed goal. | 220 | /// A (possible) solution for a proposed goal. |
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 8ef4941c0..c97b81d57 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs | |||
@@ -77,8 +77,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
77 | // Note: Since we're using impls_for_trait, only impls where the trait | 77 | // Note: Since we're using impls_for_trait, only impls where the trait |
78 | // can be resolved should ever reach Chalk. `impl_datum` relies on that | 78 | // can be resolved should ever reach Chalk. `impl_datum` relies on that |
79 | // and will panic if the trait can't be resolved. | 79 | // and will panic if the trait can't be resolved. |
80 | let in_deps = self.db.impls_from_deps(self.krate); | 80 | let in_deps = self.db.trait_impls_in_deps(self.krate); |
81 | let in_self = self.db.impls_in_crate(self.krate); | 81 | let in_self = self.db.trait_impls_in_crate(self.krate); |
82 | let impl_maps = [in_deps, in_self]; | 82 | let impl_maps = [in_deps, in_self]; |
83 | 83 | ||
84 | let id_to_chalk = |id: hir_def::ImplId| Impl::ImplDef(id).to_chalk(self.db); | 84 | let id_to_chalk = |id: hir_def::ImplId| Impl::ImplDef(id).to_chalk(self.db); |
@@ -87,14 +87,12 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
87 | Some(fp) => impl_maps | 87 | Some(fp) => impl_maps |
88 | .iter() | 88 | .iter() |
89 | .flat_map(|crate_impl_defs| { | 89 | .flat_map(|crate_impl_defs| { |
90 | crate_impl_defs.lookup_impl_defs_for_trait_and_ty(trait_, fp).map(id_to_chalk) | 90 | crate_impl_defs.for_trait_and_self_ty(trait_, fp).map(id_to_chalk) |
91 | }) | 91 | }) |
92 | .collect(), | 92 | .collect(), |
93 | None => impl_maps | 93 | None => impl_maps |
94 | .iter() | 94 | .iter() |
95 | .flat_map(|crate_impl_defs| { | 95 | .flat_map(|crate_impl_defs| crate_impl_defs.for_trait(trait_).map(id_to_chalk)) |
96 | crate_impl_defs.lookup_impl_defs_for_trait(trait_).map(id_to_chalk) | ||
97 | }) | ||
98 | .collect(), | 96 | .collect(), |
99 | }; | 97 | }; |
100 | 98 | ||
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs index ac82ea831..433d6aa03 100644 --- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs +++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs | |||
@@ -17,7 +17,7 @@ use crate::{ | |||
17 | primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness}, | 17 | primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness}, |
18 | traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, | 18 | traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, |
19 | ApplicationTy, CallableDef, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId, | 19 | ApplicationTy, CallableDef, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId, |
20 | ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, | 20 | ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TyKind, TypeCtor, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | use super::interner::*; | 23 | use super::interner::*; |
@@ -555,22 +555,39 @@ where | |||
555 | type Chalk = chalk_ir::Canonical<T::Chalk>; | 555 | type Chalk = chalk_ir::Canonical<T::Chalk>; |
556 | 556 | ||
557 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> { | 557 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> { |
558 | let parameter = chalk_ir::CanonicalVarKind::new( | 558 | let kinds = self |
559 | chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General), | 559 | .kinds |
560 | chalk_ir::UniverseIndex::ROOT, | 560 | .iter() |
561 | ); | 561 | .map(|k| match k { |
562 | TyKind::General => chalk_ir::TyKind::General, | ||
563 | TyKind::Integer => chalk_ir::TyKind::Integer, | ||
564 | TyKind::Float => chalk_ir::TyKind::Float, | ||
565 | }) | ||
566 | .map(|tk| { | ||
567 | chalk_ir::CanonicalVarKind::new( | ||
568 | chalk_ir::VariableKind::Ty(tk), | ||
569 | chalk_ir::UniverseIndex::ROOT, | ||
570 | ) | ||
571 | }); | ||
562 | let value = self.value.to_chalk(db); | 572 | let value = self.value.to_chalk(db); |
563 | chalk_ir::Canonical { | 573 | chalk_ir::Canonical { value, binders: chalk_ir::CanonicalVarKinds::from(&Interner, kinds) } |
564 | value, | ||
565 | binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]), | ||
566 | } | ||
567 | } | 574 | } |
568 | 575 | ||
569 | fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> { | 576 | fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> { |
570 | Canonical { | 577 | let kinds = canonical |
571 | num_vars: canonical.binders.len(&Interner), | 578 | .binders |
572 | value: from_chalk(db, canonical.value), | 579 | .iter(&Interner) |
573 | } | 580 | .map(|k| match k.kind { |
581 | chalk_ir::VariableKind::Ty(tk) => match tk { | ||
582 | chalk_ir::TyKind::General => TyKind::General, | ||
583 | chalk_ir::TyKind::Integer => TyKind::Integer, | ||
584 | chalk_ir::TyKind::Float => TyKind::Float, | ||
585 | }, | ||
586 | chalk_ir::VariableKind::Lifetime => panic!("unexpected lifetime from Chalk"), | ||
587 | chalk_ir::VariableKind::Const(_) => panic!("unexpected const from Chalk"), | ||
588 | }) | ||
589 | .collect(); | ||
590 | Canonical { kinds, value: from_chalk(db, canonical.value) } | ||
574 | } | 591 | } |
575 | } | 592 | } |
576 | 593 | ||
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index bbc6a5c9b..8e8892309 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml | |||
@@ -28,6 +28,7 @@ ra_cfg = { path = "../ra_cfg" } | |||
28 | ra_fmt = { path = "../ra_fmt" } | 28 | ra_fmt = { path = "../ra_fmt" } |
29 | ra_prof = { path = "../ra_prof" } | 29 | ra_prof = { path = "../ra_prof" } |
30 | test_utils = { path = "../test_utils" } | 30 | test_utils = { path = "../test_utils" } |
31 | expect = { path = "../expect" } | ||
31 | ra_assists = { path = "../ra_assists" } | 32 | ra_assists = { path = "../ra_assists" } |
32 | ra_ssr = { path = "../ra_ssr" } | 33 | ra_ssr = { path = "../ra_ssr" } |
33 | 34 | ||
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs index 1e3a31602..884353808 100644 --- a/crates/ra_ide/src/call_hierarchy.rs +++ b/crates/ra_ide/src/call_hierarchy.rs | |||
@@ -39,10 +39,11 @@ pub(crate) fn call_hierarchy( | |||
39 | 39 | ||
40 | pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { | 40 | pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { |
41 | let sema = Semantics::new(db); | 41 | let sema = Semantics::new(db); |
42 | |||
42 | // 1. Find all refs | 43 | // 1. Find all refs |
43 | // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. | 44 | // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. |
44 | // 3. Add ranges relative to the start of the fndef. | 45 | // 3. Add ranges relative to the start of the fndef. |
45 | let refs = references::find_all_refs(db, position, None)?; | 46 | let refs = references::find_all_refs(&sema, position, None)?; |
46 | 47 | ||
47 | let mut calls = CallLocations::default(); | 48 | let mut calls = CallLocations::default(); |
48 | 49 | ||
@@ -355,4 +356,41 @@ fn caller3() { | |||
355 | &["caller3 FN_DEF FileId(1) 66..83 69..76 : [52..59]"], | 356 | &["caller3 FN_DEF FileId(1) 66..83 69..76 : [52..59]"], |
356 | ); | 357 | ); |
357 | } | 358 | } |
359 | |||
360 | #[test] | ||
361 | fn test_call_hierarchy_issue_5103() { | ||
362 | check_hierarchy( | ||
363 | r#" | ||
364 | fn a() { | ||
365 | b() | ||
366 | } | ||
367 | |||
368 | fn b() {} | ||
369 | |||
370 | fn main() { | ||
371 | a<|>() | ||
372 | } | ||
373 | "#, | ||
374 | "a FN_DEF FileId(1) 0..18 3..4", | ||
375 | &["main FN_DEF FileId(1) 31..52 34..38 : [47..48]"], | ||
376 | &["b FN_DEF FileId(1) 20..29 23..24 : [13..14]"], | ||
377 | ); | ||
378 | |||
379 | check_hierarchy( | ||
380 | r#" | ||
381 | fn a() { | ||
382 | b<|>() | ||
383 | } | ||
384 | |||
385 | fn b() {} | ||
386 | |||
387 | fn main() { | ||
388 | a() | ||
389 | } | ||
390 | "#, | ||
391 | "b FN_DEF FileId(1) 20..29 23..24", | ||
392 | &["a FN_DEF FileId(1) 0..18 3..4 : [13..14]"], | ||
393 | &[], | ||
394 | ); | ||
395 | } | ||
358 | } | 396 | } |
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index e1fcf379d..9ebb8ebb7 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs | |||
@@ -63,11 +63,11 @@ pub use crate::completion::{ | |||
63 | // There also snippet completions: | 63 | // There also snippet completions: |
64 | // | 64 | // |
65 | // .Expressions | 65 | // .Expressions |
66 | // - `pd` -> `println!("{:?}")` | 66 | // - `pd` -> `eprintln!(" = {:?}", );")` |
67 | // - `ppd` -> `println!("{:#?}")` | 67 | // - `ppd` -> `eprintln!(" = {:#?}", );` |
68 | // | 68 | // |
69 | // .Items | 69 | // .Items |
70 | // - `tfn` -> `#[test] fn f(){}` | 70 | // - `tfn` -> `#[test] fn feature(){}` |
71 | // - `tmod` -> | 71 | // - `tmod` -> |
72 | // ```rust | 72 | // ```rust |
73 | // #[cfg(test)] | 73 | // #[cfg(test)] |
@@ -75,7 +75,7 @@ pub use crate::completion::{ | |||
75 | // use super::*; | 75 | // use super::*; |
76 | // | 76 | // |
77 | // #[test] | 77 | // #[test] |
78 | // fn test_fn() {} | 78 | // fn test_name() {} |
79 | // } | 79 | // } |
80 | // ``` | 80 | // ``` |
81 | 81 | ||
@@ -137,8 +137,8 @@ mod tests { | |||
137 | documentation: &'a str, | 137 | documentation: &'a str, |
138 | } | 138 | } |
139 | 139 | ||
140 | fn check_detail_and_documentation(fixture: &str, expected: DetailAndDocumentation) { | 140 | fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) { |
141 | let (analysis, position) = analysis_and_position(fixture); | 141 | let (analysis, position) = analysis_and_position(ra_fixture); |
142 | let config = CompletionConfig::default(); | 142 | let config = CompletionConfig::default(); |
143 | let completions = analysis.completions(&config, position).unwrap().unwrap(); | 143 | let completions = analysis.completions(&config, position).unwrap().unwrap(); |
144 | for item in completions { | 144 | for item in completions { |
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 3b174f916..e599cc3d1 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_syntax::ast; | 3 | use ra_syntax::{ast, SyntaxKind}; |
4 | 4 | ||
5 | use crate::completion::{ | 5 | use crate::completion::{ |
6 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, | 6 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, |
@@ -37,6 +37,10 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC | |||
37 | } | 37 | } |
38 | 38 | ||
39 | pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { | 39 | pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { |
40 | if ctx.token.kind() == SyntaxKind::COMMENT { | ||
41 | return; | ||
42 | } | ||
43 | |||
40 | let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; | 44 | let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; |
41 | if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { | 45 | if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { |
42 | add_keyword(ctx, acc, "where", "where "); | 46 | add_keyword(ctx, acc, "where", "where "); |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 560fb19e6..ef22ea54d 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -213,7 +213,7 @@ impl<'a> CompletionContext<'a> { | |||
213 | } | 213 | } |
214 | } | 214 | } |
215 | 215 | ||
216 | pub(crate) fn scope(&self) -> SemanticsScope<'_, RootDatabase> { | 216 | pub(crate) fn scope(&self) -> SemanticsScope<'_> { |
217 | self.sema.scope_at_offset(&self.token.parent(), self.offset) | 217 | self.sema.scope_at_offset(&self.token.parent(), self.offset) |
218 | } | 218 | } |
219 | 219 | ||
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 4fdc2f0bb..b18279746 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -1516,4 +1516,54 @@ mod tests { | |||
1516 | "### | 1516 | "### |
1517 | ); | 1517 | ); |
1518 | } | 1518 | } |
1519 | |||
1520 | #[test] | ||
1521 | fn no_keyword_autocompletion_on_line_comments() { | ||
1522 | assert_debug_snapshot!( | ||
1523 | do_completion( | ||
1524 | r" | ||
1525 | fn test() { | ||
1526 | let x = 2; // A comment<|> | ||
1527 | } | ||
1528 | ", | ||
1529 | CompletionKind::Keyword | ||
1530 | ), | ||
1531 | @r###" | ||
1532 | [] | ||
1533 | "### | ||
1534 | ); | ||
1535 | } | ||
1536 | |||
1537 | #[test] | ||
1538 | fn no_keyword_autocompletion_on_multi_line_comments() { | ||
1539 | assert_debug_snapshot!( | ||
1540 | do_completion( | ||
1541 | r" | ||
1542 | /* | ||
1543 | Some multi-line comment<|> | ||
1544 | */ | ||
1545 | ", | ||
1546 | CompletionKind::Keyword | ||
1547 | ), | ||
1548 | @r###" | ||
1549 | [] | ||
1550 | "### | ||
1551 | ); | ||
1552 | } | ||
1553 | |||
1554 | #[test] | ||
1555 | fn no_keyword_autocompletion_on_doc_comments() { | ||
1556 | assert_debug_snapshot!( | ||
1557 | do_completion( | ||
1558 | r" | ||
1559 | /// Some doc comment | ||
1560 | /// let test<|> = 1 | ||
1561 | ", | ||
1562 | CompletionKind::Keyword | ||
1563 | ), | ||
1564 | @r###" | ||
1565 | [] | ||
1566 | "### | ||
1567 | ); | ||
1568 | } | ||
1519 | } | 1569 | } |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 05fb799d6..46f8c31c7 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -324,10 +324,10 @@ mod tests { | |||
324 | /// * a diagnostic is produced | 324 | /// * a diagnostic is produced |
325 | /// * this diagnostic touches the input cursor position | 325 | /// * this diagnostic touches the input cursor position |
326 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied | 326 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied |
327 | fn check_apply_diagnostic_fix_from_position(fixture: &str, after: &str) { | 327 | fn check_apply_diagnostic_fix_from_position(ra_fixture: &str, after: &str) { |
328 | let after = trim_indent(after); | 328 | let after = trim_indent(after); |
329 | 329 | ||
330 | let (analysis, file_position) = analysis_and_position(fixture); | 330 | let (analysis, file_position) = analysis_and_position(ra_fixture); |
331 | let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); | 331 | let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); |
332 | let mut fix = diagnostic.fix.unwrap(); | 332 | let mut fix = diagnostic.fix.unwrap(); |
333 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; | 333 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; |
@@ -365,14 +365,14 @@ mod tests { | |||
365 | 365 | ||
366 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics | 366 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics |
367 | /// apply to the file containing the cursor. | 367 | /// apply to the file containing the cursor. |
368 | fn check_no_diagnostic_for_target_file(fixture: &str) { | 368 | fn check_no_diagnostic_for_target_file(ra_fixture: &str) { |
369 | let (analysis, file_position) = analysis_and_position(fixture); | 369 | let (analysis, file_position) = analysis_and_position(ra_fixture); |
370 | let diagnostics = analysis.diagnostics(file_position.file_id).unwrap(); | 370 | let diagnostics = analysis.diagnostics(file_position.file_id).unwrap(); |
371 | assert_eq!(diagnostics.len(), 0); | 371 | assert_eq!(diagnostics.len(), 0); |
372 | } | 372 | } |
373 | 373 | ||
374 | fn check_no_diagnostic(content: &str) { | 374 | fn check_no_diagnostic(ra_fixture: &str) { |
375 | let (analysis, file_id) = single_file(content); | 375 | let (analysis, file_id) = single_file(ra_fixture); |
376 | let diagnostics = analysis.diagnostics(file_id).unwrap(); | 376 | let diagnostics = analysis.diagnostics(file_id).unwrap(); |
377 | assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one"); | 377 | assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one"); |
378 | } | 378 | } |
@@ -473,7 +473,8 @@ mod tests { | |||
473 | 473 | ||
474 | #[test] | 474 | #[test] |
475 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { | 475 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { |
476 | let content = r#" | 476 | check_no_diagnostic_for_target_file( |
477 | r" | ||
477 | //- /main.rs | 478 | //- /main.rs |
478 | use core::result::Result::{self, Ok, Err}; | 479 | use core::result::Result::{self, Ok, Err}; |
479 | 480 | ||
@@ -485,13 +486,14 @@ mod tests { | |||
485 | pub mod result { | 486 | pub mod result { |
486 | pub enum Result<T, E> { Ok(T), Err(E) } | 487 | pub enum Result<T, E> { Ok(T), Err(E) } |
487 | } | 488 | } |
488 | "#; | 489 | ", |
489 | check_no_diagnostic_for_target_file(content); | 490 | ); |
490 | } | 491 | } |
491 | 492 | ||
492 | #[test] | 493 | #[test] |
493 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { | 494 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { |
494 | let content = r#" | 495 | check_no_diagnostic_for_target_file( |
496 | r" | ||
495 | //- /main.rs | 497 | //- /main.rs |
496 | use core::result::Result::{self, Ok, Err}; | 498 | use core::result::Result::{self, Ok, Err}; |
497 | 499 | ||
@@ -508,8 +510,8 @@ mod tests { | |||
508 | pub mod result { | 510 | pub mod result { |
509 | pub enum Result<T, E> { Ok(T), Err(E) } | 511 | pub enum Result<T, E> { Ok(T), Err(E) } |
510 | } | 512 | } |
511 | "#; | 513 | ", |
512 | check_no_diagnostic_for_target_file(content); | 514 | ); |
513 | } | 515 | } |
514 | 516 | ||
515 | #[test] | 517 | #[test] |
@@ -618,7 +620,8 @@ mod tests { | |||
618 | 620 | ||
619 | #[test] | 621 | #[test] |
620 | fn test_fill_struct_fields_no_diagnostic() { | 622 | fn test_fill_struct_fields_no_diagnostic() { |
621 | let content = r" | 623 | check_no_diagnostic( |
624 | r" | ||
622 | struct TestStruct { | 625 | struct TestStruct { |
623 | one: i32, | 626 | one: i32, |
624 | two: i64, | 627 | two: i64, |
@@ -628,14 +631,14 @@ mod tests { | |||
628 | let one = 1; | 631 | let one = 1; |
629 | let s = TestStruct{ one, two: 2 }; | 632 | let s = TestStruct{ one, two: 2 }; |
630 | } | 633 | } |
631 | "; | 634 | ", |
632 | 635 | ); | |
633 | check_no_diagnostic(content); | ||
634 | } | 636 | } |
635 | 637 | ||
636 | #[test] | 638 | #[test] |
637 | fn test_fill_struct_fields_no_diagnostic_on_spread() { | 639 | fn test_fill_struct_fields_no_diagnostic_on_spread() { |
638 | let content = r" | 640 | check_no_diagnostic( |
641 | r" | ||
639 | struct TestStruct { | 642 | struct TestStruct { |
640 | one: i32, | 643 | one: i32, |
641 | two: i64, | 644 | two: i64, |
@@ -645,9 +648,8 @@ mod tests { | |||
645 | let one = 1; | 648 | let one = 1; |
646 | let s = TestStruct{ ..a }; | 649 | let s = TestStruct{ ..a }; |
647 | } | 650 | } |
648 | "; | 651 | ", |
649 | 652 | ); | |
650 | check_no_diagnostic(content); | ||
651 | } | 653 | } |
652 | 654 | ||
653 | #[test] | 655 | #[test] |
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index 0b52b01ab..8bf2428ed 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs | |||
@@ -11,7 +11,7 @@ use ra_syntax::{ | |||
11 | TextRange, | 11 | TextRange, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use crate::{FileRange, FileSymbol}; | 14 | use crate::FileSymbol; |
15 | 15 | ||
16 | use super::short_label::ShortLabel; | 16 | use super::short_label::ShortLabel; |
17 | 17 | ||
@@ -47,6 +47,19 @@ impl NavigationTarget { | |||
47 | pub fn range(&self) -> TextRange { | 47 | pub fn range(&self) -> TextRange { |
48 | self.focus_range.unwrap_or(self.full_range) | 48 | self.focus_range.unwrap_or(self.full_range) |
49 | } | 49 | } |
50 | /// A "most interesting" range withing the `full_range`. | ||
51 | /// | ||
52 | /// Typically, `full_range` is the whole syntax node, | ||
53 | /// including doc comments, and `focus_range` is the range of the identifier. | ||
54 | pub fn focus_range(&self) -> Option<TextRange> { | ||
55 | self.focus_range | ||
56 | } | ||
57 | pub fn full_range(&self) -> TextRange { | ||
58 | self.full_range | ||
59 | } | ||
60 | pub fn file_id(&self) -> FileId { | ||
61 | self.file_id | ||
62 | } | ||
50 | 63 | ||
51 | pub fn name(&self) -> &SmolStr { | 64 | pub fn name(&self) -> &SmolStr { |
52 | &self.name | 65 | &self.name |
@@ -60,18 +73,6 @@ impl NavigationTarget { | |||
60 | self.kind | 73 | self.kind |
61 | } | 74 | } |
62 | 75 | ||
63 | pub fn file_id(&self) -> FileId { | ||
64 | self.file_id | ||
65 | } | ||
66 | |||
67 | pub fn file_range(&self) -> FileRange { | ||
68 | FileRange { file_id: self.file_id, range: self.full_range } | ||
69 | } | ||
70 | |||
71 | pub fn full_range(&self) -> TextRange { | ||
72 | self.full_range | ||
73 | } | ||
74 | |||
75 | pub fn docs(&self) -> Option<&str> { | 76 | pub fn docs(&self) -> Option<&str> { |
76 | self.docs.as_deref() | 77 | self.docs.as_deref() |
77 | } | 78 | } |
@@ -80,14 +81,6 @@ impl NavigationTarget { | |||
80 | self.description.as_deref() | 81 | self.description.as_deref() |
81 | } | 82 | } |
82 | 83 | ||
83 | /// A "most interesting" range withing the `full_range`. | ||
84 | /// | ||
85 | /// Typically, `full_range` is the whole syntax node, | ||
86 | /// including doc comments, and `focus_range` is the range of the identifier. | ||
87 | pub fn focus_range(&self) -> Option<TextRange> { | ||
88 | self.focus_range | ||
89 | } | ||
90 | |||
91 | pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { | 84 | pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { |
92 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); | 85 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); |
93 | if let Some(src) = module.declaration_source(db) { | 86 | if let Some(src) = module.declaration_source(db) { |
@@ -278,16 +271,22 @@ impl ToNav for hir::Module { | |||
278 | impl ToNav for hir::ImplDef { | 271 | impl ToNav for hir::ImplDef { |
279 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 272 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
280 | let src = self.source(db); | 273 | let src = self.source(db); |
281 | let frange = if let Some(item) = self.is_builtin_derive(db) { | 274 | let derive_attr = self.is_builtin_derive(db); |
275 | let frange = if let Some(item) = &derive_attr { | ||
282 | original_range(db, item.syntax()) | 276 | original_range(db, item.syntax()) |
283 | } else { | 277 | } else { |
284 | original_range(db, src.as_ref().map(|it| it.syntax())) | 278 | original_range(db, src.as_ref().map(|it| it.syntax())) |
285 | }; | 279 | }; |
280 | let focus_range = if derive_attr.is_some() { | ||
281 | None | ||
282 | } else { | ||
283 | src.value.target_type().map(|ty| original_range(db, src.with_value(ty.syntax())).range) | ||
284 | }; | ||
286 | 285 | ||
287 | NavigationTarget::from_syntax( | 286 | NavigationTarget::from_syntax( |
288 | frange.file_id, | 287 | frange.file_id, |
289 | "impl".into(), | 288 | "impl".into(), |
290 | None, | 289 | focus_range, |
291 | frange.range, | 290 | frange.range, |
292 | src.value.syntax().kind(), | 291 | src.value.syntax().kind(), |
293 | ) | 292 | ) |
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs index aad5a8e4d..c22a5d17b 100644 --- a/crates/ra_ide/src/display/structure.rs +++ b/crates/ra_ide/src/display/structure.rs | |||
@@ -173,12 +173,19 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> { | |||
173 | 173 | ||
174 | #[cfg(test)] | 174 | #[cfg(test)] |
175 | mod tests { | 175 | mod tests { |
176 | use expect::{expect, Expect}; | ||
177 | |||
176 | use super::*; | 178 | use super::*; |
177 | use insta::assert_debug_snapshot; | 179 | |
180 | fn check(ra_fixture: &str, expect: Expect) { | ||
181 | let file = SourceFile::parse(ra_fixture).ok().unwrap(); | ||
182 | let structure = file_structure(&file); | ||
183 | expect.assert_debug_eq(&structure) | ||
184 | } | ||
178 | 185 | ||
179 | #[test] | 186 | #[test] |
180 | fn test_file_structure() { | 187 | fn test_file_structure() { |
181 | let file = SourceFile::parse( | 188 | check( |
182 | r#" | 189 | r#" |
183 | struct Foo { | 190 | struct Foo { |
184 | x: i32 | 191 | x: i32 |
@@ -223,216 +230,211 @@ fn obsolete() {} | |||
223 | #[deprecated(note = "for awhile")] | 230 | #[deprecated(note = "for awhile")] |
224 | fn very_obsolete() {} | 231 | fn very_obsolete() {} |
225 | "#, | 232 | "#, |
226 | ) | 233 | expect![[r#" |
227 | .ok() | 234 | [ |
228 | .unwrap(); | 235 | StructureNode { |
229 | let structure = file_structure(&file); | 236 | parent: None, |
230 | assert_debug_snapshot!(structure, | 237 | label: "Foo", |
231 | @r###" | 238 | navigation_range: 8..11, |
232 | [ | 239 | node_range: 1..26, |
233 | StructureNode { | 240 | kind: STRUCT_DEF, |
234 | parent: None, | 241 | detail: None, |
235 | label: "Foo", | 242 | deprecated: false, |
236 | navigation_range: 8..11, | 243 | }, |
237 | node_range: 1..26, | 244 | StructureNode { |
238 | kind: STRUCT_DEF, | 245 | parent: Some( |
239 | detail: None, | 246 | 0, |
240 | deprecated: false, | 247 | ), |
241 | }, | 248 | label: "x", |
242 | StructureNode { | 249 | navigation_range: 18..19, |
243 | parent: Some( | 250 | node_range: 18..24, |
244 | 0, | 251 | kind: RECORD_FIELD_DEF, |
245 | ), | 252 | detail: Some( |
246 | label: "x", | 253 | "i32", |
247 | navigation_range: 18..19, | 254 | ), |
248 | node_range: 18..24, | 255 | deprecated: false, |
249 | kind: RECORD_FIELD_DEF, | 256 | }, |
250 | detail: Some( | 257 | StructureNode { |
251 | "i32", | 258 | parent: None, |
252 | ), | 259 | label: "m", |
253 | deprecated: false, | 260 | navigation_range: 32..33, |
254 | }, | 261 | node_range: 28..158, |
255 | StructureNode { | 262 | kind: MODULE, |
256 | parent: None, | 263 | detail: None, |
257 | label: "m", | 264 | deprecated: false, |
258 | navigation_range: 32..33, | 265 | }, |
259 | node_range: 28..158, | 266 | StructureNode { |
260 | kind: MODULE, | 267 | parent: Some( |
261 | detail: None, | 268 | 2, |
262 | deprecated: false, | 269 | ), |
263 | }, | 270 | label: "bar1", |
264 | StructureNode { | 271 | navigation_range: 43..47, |
265 | parent: Some( | 272 | node_range: 40..52, |
266 | 2, | 273 | kind: FN_DEF, |
267 | ), | 274 | detail: Some( |
268 | label: "bar1", | 275 | "fn()", |
269 | navigation_range: 43..47, | 276 | ), |
270 | node_range: 40..52, | 277 | deprecated: false, |
271 | kind: FN_DEF, | 278 | }, |
272 | detail: Some( | 279 | StructureNode { |
273 | "fn()", | 280 | parent: Some( |
274 | ), | 281 | 2, |
275 | deprecated: false, | 282 | ), |
276 | }, | 283 | label: "bar2", |
277 | StructureNode { | 284 | navigation_range: 60..64, |
278 | parent: Some( | 285 | node_range: 57..81, |
279 | 2, | 286 | kind: FN_DEF, |
280 | ), | 287 | detail: Some( |
281 | label: "bar2", | 288 | "fn<T>(t: T) -> T", |
282 | navigation_range: 60..64, | 289 | ), |
283 | node_range: 57..81, | 290 | deprecated: false, |
284 | kind: FN_DEF, | 291 | }, |
285 | detail: Some( | 292 | StructureNode { |
286 | "fn<T>(t: T) -> T", | 293 | parent: Some( |
287 | ), | 294 | 2, |
288 | deprecated: false, | 295 | ), |
289 | }, | 296 | label: "bar3", |
290 | StructureNode { | 297 | navigation_range: 89..93, |
291 | parent: Some( | 298 | node_range: 86..156, |
292 | 2, | 299 | kind: FN_DEF, |
293 | ), | 300 | detail: Some( |
294 | label: "bar3", | 301 | "fn<A, B>(a: A, b: B) -> Vec< u32 >", |
295 | navigation_range: 89..93, | 302 | ), |
296 | node_range: 86..156, | 303 | deprecated: false, |
297 | kind: FN_DEF, | 304 | }, |
298 | detail: Some( | 305 | StructureNode { |
299 | "fn<A, B>(a: A, b: B) -> Vec< u32 >", | 306 | parent: None, |
300 | ), | 307 | label: "E", |
301 | deprecated: false, | 308 | navigation_range: 165..166, |
302 | }, | 309 | node_range: 160..180, |
303 | StructureNode { | 310 | kind: ENUM_DEF, |
304 | parent: None, | 311 | detail: None, |
305 | label: "E", | 312 | deprecated: false, |
306 | navigation_range: 165..166, | 313 | }, |
307 | node_range: 160..180, | 314 | StructureNode { |
308 | kind: ENUM_DEF, | 315 | parent: Some( |
309 | detail: None, | 316 | 6, |
310 | deprecated: false, | 317 | ), |
311 | }, | 318 | label: "X", |
312 | StructureNode { | 319 | navigation_range: 169..170, |
313 | parent: Some( | 320 | node_range: 169..170, |
314 | 6, | 321 | kind: ENUM_VARIANT, |
315 | ), | 322 | detail: None, |
316 | label: "X", | 323 | deprecated: false, |
317 | navigation_range: 169..170, | 324 | }, |
318 | node_range: 169..170, | 325 | StructureNode { |
319 | kind: ENUM_VARIANT, | 326 | parent: Some( |
320 | detail: None, | 327 | 6, |
321 | deprecated: false, | 328 | ), |
322 | }, | 329 | label: "Y", |
323 | StructureNode { | 330 | navigation_range: 172..173, |
324 | parent: Some( | 331 | node_range: 172..178, |
325 | 6, | 332 | kind: ENUM_VARIANT, |
326 | ), | 333 | detail: None, |
327 | label: "Y", | 334 | deprecated: false, |
328 | navigation_range: 172..173, | 335 | }, |
329 | node_range: 172..178, | 336 | StructureNode { |
330 | kind: ENUM_VARIANT, | 337 | parent: None, |
331 | detail: None, | 338 | label: "T", |
332 | deprecated: false, | 339 | navigation_range: 186..187, |
333 | }, | 340 | node_range: 181..193, |
334 | StructureNode { | 341 | kind: TYPE_ALIAS_DEF, |
335 | parent: None, | 342 | detail: Some( |
336 | label: "T", | 343 | "()", |
337 | navigation_range: 186..187, | 344 | ), |
338 | node_range: 181..193, | 345 | deprecated: false, |
339 | kind: TYPE_ALIAS_DEF, | 346 | }, |
340 | detail: Some( | 347 | StructureNode { |
341 | "()", | 348 | parent: None, |
342 | ), | 349 | label: "S", |
343 | deprecated: false, | 350 | navigation_range: 201..202, |
344 | }, | 351 | node_range: 194..213, |
345 | StructureNode { | 352 | kind: STATIC_DEF, |
346 | parent: None, | 353 | detail: Some( |
347 | label: "S", | 354 | "i32", |
348 | navigation_range: 201..202, | 355 | ), |
349 | node_range: 194..213, | 356 | deprecated: false, |
350 | kind: STATIC_DEF, | 357 | }, |
351 | detail: Some( | 358 | StructureNode { |
352 | "i32", | 359 | parent: None, |
353 | ), | 360 | label: "C", |
354 | deprecated: false, | 361 | navigation_range: 220..221, |
355 | }, | 362 | node_range: 214..232, |
356 | StructureNode { | 363 | kind: CONST_DEF, |
357 | parent: None, | 364 | detail: Some( |
358 | label: "C", | 365 | "i32", |
359 | navigation_range: 220..221, | 366 | ), |
360 | node_range: 214..232, | 367 | deprecated: false, |
361 | kind: CONST_DEF, | 368 | }, |
362 | detail: Some( | 369 | StructureNode { |
363 | "i32", | 370 | parent: None, |
364 | ), | 371 | label: "impl E", |
365 | deprecated: false, | 372 | navigation_range: 239..240, |
366 | }, | 373 | node_range: 234..243, |
367 | StructureNode { | 374 | kind: IMPL_DEF, |
368 | parent: None, | 375 | detail: None, |
369 | label: "impl E", | 376 | deprecated: false, |
370 | navigation_range: 239..240, | 377 | }, |
371 | node_range: 234..243, | 378 | StructureNode { |
372 | kind: IMPL_DEF, | 379 | parent: None, |
373 | detail: None, | 380 | label: "impl fmt::Debug for E", |
374 | deprecated: false, | 381 | navigation_range: 265..266, |
375 | }, | 382 | node_range: 245..269, |
376 | StructureNode { | 383 | kind: IMPL_DEF, |
377 | parent: None, | 384 | detail: None, |
378 | label: "impl fmt::Debug for E", | 385 | deprecated: false, |
379 | navigation_range: 265..266, | 386 | }, |
380 | node_range: 245..269, | 387 | StructureNode { |
381 | kind: IMPL_DEF, | 388 | parent: None, |
382 | detail: None, | 389 | label: "mc", |
383 | deprecated: false, | 390 | navigation_range: 284..286, |
384 | }, | 391 | node_range: 271..303, |
385 | StructureNode { | 392 | kind: MACRO_CALL, |
386 | parent: None, | 393 | detail: None, |
387 | label: "mc", | 394 | deprecated: false, |
388 | navigation_range: 284..286, | 395 | }, |
389 | node_range: 271..303, | 396 | StructureNode { |
390 | kind: MACRO_CALL, | 397 | parent: None, |
391 | detail: None, | 398 | label: "mcexp", |
392 | deprecated: false, | 399 | navigation_range: 334..339, |
393 | }, | 400 | node_range: 305..356, |
394 | StructureNode { | 401 | kind: MACRO_CALL, |
395 | parent: None, | 402 | detail: None, |
396 | label: "mcexp", | 403 | deprecated: false, |
397 | navigation_range: 334..339, | 404 | }, |
398 | node_range: 305..356, | 405 | StructureNode { |
399 | kind: MACRO_CALL, | 406 | parent: None, |
400 | detail: None, | 407 | label: "mcexp", |
401 | deprecated: false, | 408 | navigation_range: 387..392, |
402 | }, | 409 | node_range: 358..409, |
403 | StructureNode { | 410 | kind: MACRO_CALL, |
404 | parent: None, | 411 | detail: None, |
405 | label: "mcexp", | 412 | deprecated: false, |
406 | navigation_range: 387..392, | 413 | }, |
407 | node_range: 358..409, | 414 | StructureNode { |
408 | kind: MACRO_CALL, | 415 | parent: None, |
409 | detail: None, | 416 | label: "obsolete", |
410 | deprecated: false, | 417 | navigation_range: 428..436, |
411 | }, | 418 | node_range: 411..441, |
412 | StructureNode { | 419 | kind: FN_DEF, |
413 | parent: None, | 420 | detail: Some( |
414 | label: "obsolete", | 421 | "fn()", |
415 | navigation_range: 428..436, | 422 | ), |
416 | node_range: 411..441, | 423 | deprecated: true, |
417 | kind: FN_DEF, | 424 | }, |
418 | detail: Some( | 425 | StructureNode { |
419 | "fn()", | 426 | parent: None, |
420 | ), | 427 | label: "very_obsolete", |
421 | deprecated: true, | 428 | navigation_range: 481..494, |
422 | }, | 429 | node_range: 443..499, |
423 | StructureNode { | 430 | kind: FN_DEF, |
424 | parent: None, | 431 | detail: Some( |
425 | label: "very_obsolete", | 432 | "fn()", |
426 | navigation_range: 481..494, | 433 | ), |
427 | node_range: 443..499, | 434 | deprecated: true, |
428 | kind: FN_DEF, | 435 | }, |
429 | detail: Some( | 436 | ] |
430 | "fn()", | 437 | "#]], |
431 | ), | 438 | ); |
432 | deprecated: true, | ||
433 | }, | ||
434 | ] | ||
435 | "### | ||
436 | ); | ||
437 | } | 439 | } |
438 | } | 440 | } |
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs index 54a47aac0..043515f54 100644 --- a/crates/ra_ide/src/expand_macro.rs +++ b/crates/ra_ide/src/expand_macro.rs | |||
@@ -2,7 +2,9 @@ use hir::Semantics; | |||
2 | use ra_ide_db::RootDatabase; | 2 | use ra_ide_db::RootDatabase; |
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::{find_node_at_offset, SyntaxRewriter}, | 4 | algo::{find_node_at_offset, SyntaxRewriter}, |
5 | ast, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T, | 5 | ast, AstNode, NodeOrToken, SyntaxKind, |
6 | SyntaxKind::*, | ||
7 | SyntaxNode, WalkEvent, T, | ||
6 | }; | 8 | }; |
7 | 9 | ||
8 | use crate::FilePosition; | 10 | use crate::FilePosition; |
@@ -65,8 +67,6 @@ fn expand_macro_recur( | |||
65 | // FIXME: It would also be cool to share logic here and in the mbe tests, | 67 | // FIXME: It would also be cool to share logic here and in the mbe tests, |
66 | // which are pretty unreadable at the moment. | 68 | // which are pretty unreadable at the moment. |
67 | fn insert_whitespaces(syn: SyntaxNode) -> String { | 69 | fn insert_whitespaces(syn: SyntaxNode) -> String { |
68 | use SyntaxKind::*; | ||
69 | |||
70 | let mut res = String::new(); | 70 | let mut res = String::new(); |
71 | let mut token_iter = syn | 71 | let mut token_iter = syn |
72 | .preorder_with_tokens() | 72 | .preorder_with_tokens() |
@@ -120,175 +120,164 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { | |||
120 | 120 | ||
121 | #[cfg(test)] | 121 | #[cfg(test)] |
122 | mod tests { | 122 | mod tests { |
123 | use insta::assert_snapshot; | 123 | use expect::{expect, Expect}; |
124 | 124 | ||
125 | use crate::mock_analysis::analysis_and_position; | 125 | use crate::mock_analysis::analysis_and_position; |
126 | 126 | ||
127 | use super::*; | 127 | fn check(ra_fixture: &str, expect: Expect) { |
128 | 128 | let (analysis, pos) = analysis_and_position(ra_fixture); | |
129 | fn check_expand_macro(fixture: &str) -> ExpandedMacro { | 129 | let expansion = analysis.expand_macro(pos).unwrap().unwrap(); |
130 | let (analysis, pos) = analysis_and_position(fixture); | 130 | let actual = format!("{}\n{}", expansion.name, expansion.expansion); |
131 | analysis.expand_macro(pos).unwrap().unwrap() | 131 | expect.assert_eq(&actual); |
132 | } | 132 | } |
133 | 133 | ||
134 | #[test] | 134 | #[test] |
135 | fn macro_expand_recursive_expansion() { | 135 | fn macro_expand_recursive_expansion() { |
136 | let res = check_expand_macro( | 136 | check( |
137 | r#" | 137 | r#" |
138 | //- /lib.rs | 138 | macro_rules! bar { |
139 | macro_rules! bar { | 139 | () => { fn b() {} } |
140 | () => { fn b() {} } | 140 | } |
141 | } | 141 | macro_rules! foo { |
142 | macro_rules! foo { | 142 | () => { bar!(); } |
143 | () => { bar!(); } | 143 | } |
144 | } | 144 | macro_rules! baz { |
145 | macro_rules! baz { | 145 | () => { foo!(); } |
146 | () => { foo!(); } | 146 | } |
147 | } | 147 | f<|>oo!(); |
148 | f<|>oo!(); | 148 | "#, |
149 | "#, | 149 | expect![[r#" |
150 | foo | ||
151 | fn b(){} | ||
152 | "#]], | ||
150 | ); | 153 | ); |
151 | |||
152 | assert_eq!(res.name, "foo"); | ||
153 | assert_snapshot!(res.expansion, @r###" | ||
154 | fn b(){} | ||
155 | "###); | ||
156 | } | 154 | } |
157 | 155 | ||
158 | #[test] | 156 | #[test] |
159 | fn macro_expand_multiple_lines() { | 157 | fn macro_expand_multiple_lines() { |
160 | let res = check_expand_macro( | 158 | check( |
161 | r#" | 159 | r#" |
162 | //- /lib.rs | 160 | macro_rules! foo { |
163 | macro_rules! foo { | 161 | () => { |
164 | () => { | 162 | fn some_thing() -> u32 { |
165 | fn some_thing() -> u32 { | 163 | let a = 0; |
166 | let a = 0; | 164 | a + 10 |
167 | a + 10 | ||
168 | } | ||
169 | } | ||
170 | } | 165 | } |
171 | f<|>oo!(); | 166 | } |
167 | } | ||
168 | f<|>oo!(); | ||
172 | "#, | 169 | "#, |
170 | expect![[r#" | ||
171 | foo | ||
172 | fn some_thing() -> u32 { | ||
173 | let a = 0; | ||
174 | a+10 | ||
175 | }"#]], | ||
173 | ); | 176 | ); |
174 | |||
175 | assert_eq!(res.name, "foo"); | ||
176 | assert_snapshot!(res.expansion, @r###" | ||
177 | fn some_thing() -> u32 { | ||
178 | let a = 0; | ||
179 | a+10 | ||
180 | } | ||
181 | "###); | ||
182 | } | 177 | } |
183 | 178 | ||
184 | #[test] | 179 | #[test] |
185 | fn macro_expand_match_ast() { | 180 | fn macro_expand_match_ast() { |
186 | let res = check_expand_macro( | 181 | check( |
187 | r#" | 182 | r#" |
188 | //- /lib.rs | 183 | macro_rules! match_ast { |
189 | macro_rules! match_ast { | 184 | (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; |
190 | (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; | 185 | (match ($node:expr) { |
186 | $( ast::$ast:ident($it:ident) => $res:block, )* | ||
187 | _ => $catch_all:expr $(,)? | ||
188 | }) => {{ | ||
189 | $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* | ||
190 | { $catch_all } | ||
191 | }}; | ||
192 | } | ||
191 | 193 | ||
192 | (match ($node:expr) { | 194 | fn main() { |
193 | $( ast::$ast:ident($it:ident) => $res:block, )* | 195 | mat<|>ch_ast! { |
194 | _ => $catch_all:expr $(,)? | 196 | match container { |
195 | }) => {{ | 197 | ast::TraitDef(it) => {}, |
196 | $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* | 198 | ast::ImplDef(it) => {}, |
197 | { $catch_all } | 199 | _ => { continue }, |
198 | }}; | ||
199 | } | 200 | } |
200 | |||
201 | fn main() { | ||
202 | mat<|>ch_ast! { | ||
203 | match container { | ||
204 | ast::TraitDef(it) => {}, | ||
205 | ast::ImplDef(it) => {}, | ||
206 | _ => { continue }, | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | "#, | ||
211 | ); | ||
212 | |||
213 | assert_eq!(res.name, "match_ast"); | ||
214 | assert_snapshot!(res.expansion, @r###" | ||
215 | { | ||
216 | if let Some(it) = ast::TraitDef::cast(container.clone()){} | ||
217 | else if let Some(it) = ast::ImplDef::cast(container.clone()){} | ||
218 | else { | ||
219 | { | ||
220 | continue | ||
221 | } | 201 | } |
222 | } | ||
223 | } | 202 | } |
224 | "###); | 203 | "#, |
204 | expect![[r#" | ||
205 | match_ast | ||
206 | { | ||
207 | if let Some(it) = ast::TraitDef::cast(container.clone()){} | ||
208 | else if let Some(it) = ast::ImplDef::cast(container.clone()){} | ||
209 | else { | ||
210 | { | ||
211 | continue | ||
212 | } | ||
213 | } | ||
214 | }"#]], | ||
215 | ); | ||
225 | } | 216 | } |
226 | 217 | ||
227 | #[test] | 218 | #[test] |
228 | fn macro_expand_match_ast_inside_let_statement() { | 219 | fn macro_expand_match_ast_inside_let_statement() { |
229 | let res = check_expand_macro( | 220 | check( |
230 | r#" | 221 | r#" |
231 | //- /lib.rs | 222 | macro_rules! match_ast { |
232 | macro_rules! match_ast { | 223 | (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; |
233 | (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; | 224 | (match ($node:expr) {}) => {{}}; |
234 | (match ($node:expr) {}) => {{}}; | 225 | } |
235 | } | ||
236 | 226 | ||
237 | fn main() { | 227 | fn main() { |
238 | let p = f(|it| { | 228 | let p = f(|it| { |
239 | let res = mat<|>ch_ast! { match c {}}; | 229 | let res = mat<|>ch_ast! { match c {}}; |
240 | Some(res) | 230 | Some(res) |
241 | })?; | 231 | })?; |
242 | } | 232 | } |
243 | "#, | 233 | "#, |
234 | expect![[r#" | ||
235 | match_ast | ||
236 | {} | ||
237 | "#]], | ||
244 | ); | 238 | ); |
245 | |||
246 | assert_eq!(res.name, "match_ast"); | ||
247 | assert_snapshot!(res.expansion, @r###"{}"###); | ||
248 | } | 239 | } |
249 | 240 | ||
250 | #[test] | 241 | #[test] |
251 | fn macro_expand_inner_macro_fail_to_expand() { | 242 | fn macro_expand_inner_macro_fail_to_expand() { |
252 | let res = check_expand_macro( | 243 | check( |
253 | r#" | 244 | r#" |
254 | //- /lib.rs | 245 | macro_rules! bar { |
255 | macro_rules! bar { | 246 | (BAD) => {}; |
256 | (BAD) => {}; | 247 | } |
257 | } | 248 | macro_rules! foo { |
258 | macro_rules! foo { | 249 | () => {bar!()}; |
259 | () => {bar!()}; | 250 | } |
260 | } | ||
261 | 251 | ||
262 | fn main() { | 252 | fn main() { |
263 | let res = fo<|>o!(); | 253 | let res = fo<|>o!(); |
264 | } | 254 | } |
265 | "#, | 255 | "#, |
256 | expect![[r#" | ||
257 | foo | ||
258 | "#]], | ||
266 | ); | 259 | ); |
267 | |||
268 | assert_eq!(res.name, "foo"); | ||
269 | assert_snapshot!(res.expansion, @r###""###); | ||
270 | } | 260 | } |
271 | 261 | ||
272 | #[test] | 262 | #[test] |
273 | fn macro_expand_with_dollar_crate() { | 263 | fn macro_expand_with_dollar_crate() { |
274 | let res = check_expand_macro( | 264 | check( |
275 | r#" | 265 | r#" |
276 | //- /lib.rs | 266 | #[macro_export] |
277 | #[macro_export] | 267 | macro_rules! bar { |
278 | macro_rules! bar { | 268 | () => {0}; |
279 | () => {0}; | 269 | } |
280 | } | 270 | macro_rules! foo { |
281 | macro_rules! foo { | 271 | () => {$crate::bar!()}; |
282 | () => {$crate::bar!()}; | 272 | } |
283 | } | ||
284 | 273 | ||
285 | fn main() { | 274 | fn main() { |
286 | let res = fo<|>o!(); | 275 | let res = fo<|>o!(); |
287 | } | 276 | } |
288 | "#, | 277 | "#, |
278 | expect![[r#" | ||
279 | foo | ||
280 | 0 "#]], | ||
289 | ); | 281 | ); |
290 | |||
291 | assert_eq!(res.name, "foo"); | ||
292 | assert_snapshot!(res.expansion, @r###"0"###); | ||
293 | } | 282 | } |
294 | } | 283 | } |
diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs index 8657377de..5cec689f8 100644 --- a/crates/ra_ide/src/folding_ranges.rs +++ b/crates/ra_ide/src/folding_ranges.rs | |||
@@ -15,6 +15,7 @@ pub enum FoldKind { | |||
15 | Imports, | 15 | Imports, |
16 | Mods, | 16 | Mods, |
17 | Block, | 17 | Block, |
18 | ArgList, | ||
18 | } | 19 | } |
19 | 20 | ||
20 | #[derive(Debug)] | 21 | #[derive(Debug)] |
@@ -83,6 +84,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { | |||
83 | match kind { | 84 | match kind { |
84 | COMMENT => Some(FoldKind::Comment), | 85 | COMMENT => Some(FoldKind::Comment), |
85 | USE_ITEM => Some(FoldKind::Imports), | 86 | USE_ITEM => Some(FoldKind::Imports), |
87 | ARG_LIST => Some(FoldKind::ArgList), | ||
86 | RECORD_FIELD_DEF_LIST | 88 | RECORD_FIELD_DEF_LIST |
87 | | RECORD_FIELD_PAT_LIST | 89 | | RECORD_FIELD_PAT_LIST |
88 | | ITEM_LIST | 90 | | ITEM_LIST |
@@ -196,89 +198,85 @@ fn contiguous_range_for_comment( | |||
196 | 198 | ||
197 | #[cfg(test)] | 199 | #[cfg(test)] |
198 | mod tests { | 200 | mod tests { |
201 | use test_utils::extract_tags; | ||
202 | |||
199 | use super::*; | 203 | use super::*; |
200 | use test_utils::extract_ranges; | ||
201 | 204 | ||
202 | fn do_check(text: &str, fold_kinds: &[FoldKind]) { | 205 | fn check(ra_fixture: &str) { |
203 | let (ranges, text) = extract_ranges(text, "fold"); | 206 | let (ranges, text) = extract_tags(ra_fixture, "fold"); |
207 | |||
204 | let parse = SourceFile::parse(&text); | 208 | let parse = SourceFile::parse(&text); |
205 | let folds = folding_ranges(&parse.tree()); | 209 | let folds = folding_ranges(&parse.tree()); |
206 | |||
207 | assert_eq!( | 210 | assert_eq!( |
208 | folds.len(), | 211 | folds.len(), |
209 | ranges.len(), | 212 | ranges.len(), |
210 | "The amount of folds is different than the expected amount" | 213 | "The amount of folds is different than the expected amount" |
211 | ); | 214 | ); |
212 | assert_eq!( | 215 | |
213 | folds.len(), | 216 | for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) { |
214 | fold_kinds.len(), | ||
215 | "The amount of fold kinds is different than the expected amount" | ||
216 | ); | ||
217 | for ((fold, range), fold_kind) in | ||
218 | folds.iter().zip(ranges.into_iter()).zip(fold_kinds.iter()) | ||
219 | { | ||
220 | assert_eq!(fold.range.start(), range.start()); | 217 | assert_eq!(fold.range.start(), range.start()); |
221 | assert_eq!(fold.range.end(), range.end()); | 218 | assert_eq!(fold.range.end(), range.end()); |
222 | assert_eq!(&fold.kind, fold_kind); | 219 | |
220 | let kind = match fold.kind { | ||
221 | FoldKind::Comment => "comment", | ||
222 | FoldKind::Imports => "imports", | ||
223 | FoldKind::Mods => "mods", | ||
224 | FoldKind::Block => "block", | ||
225 | FoldKind::ArgList => "arglist", | ||
226 | }; | ||
227 | assert_eq!(kind, &attr.unwrap()); | ||
223 | } | 228 | } |
224 | } | 229 | } |
225 | 230 | ||
226 | #[test] | 231 | #[test] |
227 | fn test_fold_comments() { | 232 | fn test_fold_comments() { |
228 | let text = r#" | 233 | check( |
229 | <fold>// Hello | 234 | r#" |
235 | <fold comment>// Hello | ||
230 | // this is a multiline | 236 | // this is a multiline |
231 | // comment | 237 | // comment |
232 | //</fold> | 238 | //</fold> |
233 | 239 | ||
234 | // But this is not | 240 | // But this is not |
235 | 241 | ||
236 | fn main() <fold>{ | 242 | fn main() <fold block>{ |
237 | <fold>// We should | 243 | <fold comment>// We should |
238 | // also | 244 | // also |
239 | // fold | 245 | // fold |
240 | // this one.</fold> | 246 | // this one.</fold> |
241 | <fold>//! But this one is different | 247 | <fold comment>//! But this one is different |
242 | //! because it has another flavor</fold> | 248 | //! because it has another flavor</fold> |
243 | <fold>/* As does this | 249 | <fold comment>/* As does this |
244 | multiline comment */</fold> | 250 | multiline comment */</fold> |
245 | }</fold>"#; | 251 | }</fold>"#, |
246 | 252 | ); | |
247 | let fold_kinds = &[ | ||
248 | FoldKind::Comment, | ||
249 | FoldKind::Block, | ||
250 | FoldKind::Comment, | ||
251 | FoldKind::Comment, | ||
252 | FoldKind::Comment, | ||
253 | ]; | ||
254 | do_check(text, fold_kinds); | ||
255 | } | 253 | } |
256 | 254 | ||
257 | #[test] | 255 | #[test] |
258 | fn test_fold_imports() { | 256 | fn test_fold_imports() { |
259 | let text = r#" | 257 | check( |
260 | <fold>use std::<fold>{ | 258 | r#" |
259 | <fold imports>use std::<fold block>{ | ||
261 | str, | 260 | str, |
262 | vec, | 261 | vec, |
263 | io as iop | 262 | io as iop |
264 | }</fold>;</fold> | 263 | }</fold>;</fold> |
265 | 264 | ||
266 | fn main() <fold>{ | 265 | fn main() <fold block>{ |
267 | }</fold>"#; | 266 | }</fold>"#, |
268 | 267 | ); | |
269 | let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block]; | ||
270 | do_check(text, folds); | ||
271 | } | 268 | } |
272 | 269 | ||
273 | #[test] | 270 | #[test] |
274 | fn test_fold_mods() { | 271 | fn test_fold_mods() { |
275 | let text = r#" | 272 | check( |
273 | r#" | ||
276 | 274 | ||
277 | pub mod foo; | 275 | pub mod foo; |
278 | <fold>mod after_pub; | 276 | <fold mods>mod after_pub; |
279 | mod after_pub_next;</fold> | 277 | mod after_pub_next;</fold> |
280 | 278 | ||
281 | <fold>mod before_pub; | 279 | <fold mods>mod before_pub; |
282 | mod before_pub_next;</fold> | 280 | mod before_pub_next;</fold> |
283 | pub mod bar; | 281 | pub mod bar; |
284 | 282 | ||
@@ -286,90 +284,93 @@ mod not_folding_single; | |||
286 | pub mod foobar; | 284 | pub mod foobar; |
287 | pub not_folding_single_next; | 285 | pub not_folding_single_next; |
288 | 286 | ||
289 | <fold>#[cfg(test)] | 287 | <fold mods>#[cfg(test)] |
290 | mod with_attribute; | 288 | mod with_attribute; |
291 | mod with_attribute_next;</fold> | 289 | mod with_attribute_next;</fold> |
292 | 290 | ||
293 | fn main() <fold>{ | 291 | fn main() <fold block>{ |
294 | }</fold>"#; | 292 | }</fold>"#, |
295 | 293 | ); | |
296 | let folds = &[FoldKind::Mods, FoldKind::Mods, FoldKind::Mods, FoldKind::Block]; | ||
297 | do_check(text, folds); | ||
298 | } | 294 | } |
299 | 295 | ||
300 | #[test] | 296 | #[test] |
301 | fn test_fold_import_groups() { | 297 | fn test_fold_import_groups() { |
302 | let text = r#" | 298 | check( |
303 | <fold>use std::str; | 299 | r#" |
300 | <fold imports>use std::str; | ||
304 | use std::vec; | 301 | use std::vec; |
305 | use std::io as iop;</fold> | 302 | use std::io as iop;</fold> |
306 | 303 | ||
307 | <fold>use std::mem; | 304 | <fold imports>use std::mem; |
308 | use std::f64;</fold> | 305 | use std::f64;</fold> |
309 | 306 | ||
310 | use std::collections::HashMap; | 307 | use std::collections::HashMap; |
311 | // Some random comment | 308 | // Some random comment |
312 | use std::collections::VecDeque; | 309 | use std::collections::VecDeque; |
313 | 310 | ||
314 | fn main() <fold>{ | 311 | fn main() <fold block>{ |
315 | }</fold>"#; | 312 | }</fold>"#, |
316 | 313 | ); | |
317 | let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block]; | ||
318 | do_check(text, folds); | ||
319 | } | 314 | } |
320 | 315 | ||
321 | #[test] | 316 | #[test] |
322 | fn test_fold_import_and_groups() { | 317 | fn test_fold_import_and_groups() { |
323 | let text = r#" | 318 | check( |
324 | <fold>use std::str; | 319 | r#" |
320 | <fold imports>use std::str; | ||
325 | use std::vec; | 321 | use std::vec; |
326 | use std::io as iop;</fold> | 322 | use std::io as iop;</fold> |
327 | 323 | ||
328 | <fold>use std::mem; | 324 | <fold imports>use std::mem; |
329 | use std::f64;</fold> | 325 | use std::f64;</fold> |
330 | 326 | ||
331 | <fold>use std::collections::<fold>{ | 327 | <fold imports>use std::collections::<fold block>{ |
332 | HashMap, | 328 | HashMap, |
333 | VecDeque, | 329 | VecDeque, |
334 | }</fold>;</fold> | 330 | }</fold>;</fold> |
335 | // Some random comment | 331 | // Some random comment |
336 | 332 | ||
337 | fn main() <fold>{ | 333 | fn main() <fold block>{ |
338 | }</fold>"#; | 334 | }</fold>"#, |
339 | 335 | ); | |
340 | let folds = &[ | ||
341 | FoldKind::Imports, | ||
342 | FoldKind::Imports, | ||
343 | FoldKind::Imports, | ||
344 | FoldKind::Block, | ||
345 | FoldKind::Block, | ||
346 | ]; | ||
347 | do_check(text, folds); | ||
348 | } | 336 | } |
349 | 337 | ||
350 | #[test] | 338 | #[test] |
351 | fn test_folds_macros() { | 339 | fn test_folds_macros() { |
352 | let text = r#" | 340 | check( |
353 | macro_rules! foo <fold>{ | 341 | r#" |
342 | macro_rules! foo <fold block>{ | ||
354 | ($($tt:tt)*) => { $($tt)* } | 343 | ($($tt:tt)*) => { $($tt)* } |
355 | }</fold> | 344 | }</fold> |
356 | "#; | 345 | "#, |
357 | 346 | ); | |
358 | let folds = &[FoldKind::Block]; | ||
359 | do_check(text, folds); | ||
360 | } | 347 | } |
361 | 348 | ||
362 | #[test] | 349 | #[test] |
363 | fn test_fold_match_arms() { | 350 | fn test_fold_match_arms() { |
364 | let text = r#" | 351 | check( |
365 | fn main() <fold>{ | 352 | r#" |
366 | match 0 <fold>{ | 353 | fn main() <fold block>{ |
354 | match 0 <fold block>{ | ||
367 | 0 => 0, | 355 | 0 => 0, |
368 | _ => 1, | 356 | _ => 1, |
369 | }</fold> | 357 | }</fold> |
370 | }</fold>"#; | 358 | }</fold>"#, |
359 | ); | ||
360 | } | ||
371 | 361 | ||
372 | let folds = &[FoldKind::Block, FoldKind::Block]; | 362 | #[test] |
373 | do_check(text, folds); | 363 | fn fold_big_calls() { |
364 | check( | ||
365 | r#" | ||
366 | fn main() <fold block>{ | ||
367 | frobnicate<fold arglist>( | ||
368 | 1, | ||
369 | 2, | ||
370 | 3, | ||
371 | )</fold> | ||
372 | }</fold> | ||
373 | "#, | ||
374 | ) | ||
374 | } | 375 | } |
375 | } | 376 | } |
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index bea7fbfa7..4c78fa214 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -103,205 +103,149 @@ pub(crate) fn reference_definition( | |||
103 | 103 | ||
104 | #[cfg(test)] | 104 | #[cfg(test)] |
105 | mod tests { | 105 | mod tests { |
106 | use test_utils::assert_eq_text; | 106 | use ra_db::FileRange; |
107 | 107 | use ra_syntax::{TextRange, TextSize}; | |
108 | use crate::mock_analysis::analysis_and_position; | 108 | |
109 | 109 | use crate::mock_analysis::MockAnalysis; | |
110 | fn check_goto(ra_fixture: &str, expected: &str, expected_range: &str) { | 110 | |
111 | let (analysis, pos) = analysis_and_position(ra_fixture); | 111 | fn check(ra_fixture: &str) { |
112 | let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); | ||
113 | let (mut expected, data) = mock.annotation(); | ||
114 | let analysis = mock.analysis(); | ||
115 | match data.as_str() { | ||
116 | "" => (), | ||
117 | "file" => { | ||
118 | expected.range = | ||
119 | TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap())) | ||
120 | } | ||
121 | data => panic!("bad data: {}", data), | ||
122 | } | ||
112 | 123 | ||
113 | let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; | 124 | let mut navs = analysis.goto_definition(position).unwrap().unwrap().info; |
114 | if navs.len() == 0 { | 125 | if navs.len() == 0 { |
115 | panic!("unresolved reference") | 126 | panic!("unresolved reference") |
116 | } | 127 | } |
117 | assert_eq!(navs.len(), 1); | 128 | assert_eq!(navs.len(), 1); |
118 | 129 | ||
119 | let nav = navs.pop().unwrap(); | 130 | let nav = navs.pop().unwrap(); |
120 | let file_text = analysis.file_text(nav.file_id()).unwrap(); | 131 | assert_eq!(expected, FileRange { file_id: nav.file_id(), range: nav.range() }); |
121 | |||
122 | let mut actual = file_text[nav.full_range()].to_string(); | ||
123 | if let Some(focus) = nav.focus_range() { | ||
124 | actual += "|"; | ||
125 | actual += &file_text[focus]; | ||
126 | } | ||
127 | |||
128 | if !expected_range.contains("...") { | ||
129 | test_utils::assert_eq_text!(&actual, expected_range); | ||
130 | } else { | ||
131 | let mut parts = expected_range.split("..."); | ||
132 | let prefix = parts.next().unwrap(); | ||
133 | let suffix = parts.next().unwrap(); | ||
134 | assert!( | ||
135 | actual.starts_with(prefix) && actual.ends_with(suffix), | ||
136 | "\nExpected: {}\n Actual: {}\n", | ||
137 | expected_range, | ||
138 | actual | ||
139 | ); | ||
140 | } | ||
141 | |||
142 | nav.assert_match(expected); | ||
143 | } | 132 | } |
144 | 133 | ||
145 | #[test] | 134 | #[test] |
146 | fn goto_def_in_items() { | 135 | fn goto_def_in_items() { |
147 | check_goto( | 136 | check( |
148 | " | 137 | r#" |
149 | //- /lib.rs | 138 | struct Foo; |
150 | struct Foo; | 139 | //^^^ |
151 | enum E { X(Foo<|>) } | 140 | enum E { X(Foo<|>) } |
152 | ", | 141 | "#, |
153 | "Foo STRUCT_DEF FileId(1) 0..11 7..10", | ||
154 | "struct Foo;|Foo", | ||
155 | ); | 142 | ); |
156 | } | 143 | } |
157 | 144 | ||
158 | #[test] | 145 | #[test] |
159 | fn goto_def_at_start_of_item() { | 146 | fn goto_def_at_start_of_item() { |
160 | check_goto( | 147 | check( |
161 | " | 148 | r#" |
162 | //- /lib.rs | 149 | struct Foo; |
163 | struct Foo; | 150 | //^^^ |
164 | enum E { X(<|>Foo) } | 151 | enum E { X(<|>Foo) } |
165 | ", | 152 | "#, |
166 | "Foo STRUCT_DEF FileId(1) 0..11 7..10", | ||
167 | "struct Foo;|Foo", | ||
168 | ); | 153 | ); |
169 | } | 154 | } |
170 | 155 | ||
171 | #[test] | 156 | #[test] |
172 | fn goto_definition_resolves_correct_name() { | 157 | fn goto_definition_resolves_correct_name() { |
173 | check_goto( | 158 | check( |
174 | " | 159 | r#" |
175 | //- /lib.rs | 160 | //- /lib.rs |
176 | use a::Foo; | 161 | use a::Foo; |
177 | mod a; | 162 | mod a; |
178 | mod b; | 163 | mod b; |
179 | enum E { X(Foo<|>) } | 164 | enum E { X(Foo<|>) } |
180 | 165 | ||
181 | //- /a.rs | 166 | //- /a.rs |
182 | struct Foo; | 167 | struct Foo; |
183 | 168 | //^^^ | |
184 | //- /b.rs | 169 | //- /b.rs |
185 | struct Foo; | 170 | struct Foo; |
186 | ", | 171 | "#, |
187 | "Foo STRUCT_DEF FileId(2) 0..11 7..10", | ||
188 | "struct Foo;|Foo", | ||
189 | ); | 172 | ); |
190 | } | 173 | } |
191 | 174 | ||
192 | #[test] | 175 | #[test] |
193 | fn goto_def_for_module_declaration() { | 176 | fn goto_def_for_module_declaration() { |
194 | check_goto( | 177 | check( |
195 | r#" | 178 | r#" |
196 | //- /lib.rs | 179 | //- /lib.rs |
197 | mod <|>foo; | 180 | mod <|>foo; |
198 | 181 | ||
199 | //- /foo.rs | 182 | //- /foo.rs |
200 | // empty | 183 | // empty |
184 | //^ file | ||
201 | "#, | 185 | "#, |
202 | "foo SOURCE_FILE FileId(2) 0..9", | ||
203 | "// empty\n", | ||
204 | ); | 186 | ); |
205 | 187 | ||
206 | check_goto( | 188 | check( |
207 | r#" | 189 | r#" |
208 | //- /lib.rs | 190 | //- /lib.rs |
209 | mod <|>foo; | 191 | mod <|>foo; |
210 | 192 | ||
211 | //- /foo/mod.rs | 193 | //- /foo/mod.rs |
212 | // empty | 194 | // empty |
195 | //^ file | ||
213 | "#, | 196 | "#, |
214 | "foo SOURCE_FILE FileId(2) 0..9", | ||
215 | "// empty\n", | ||
216 | ); | 197 | ); |
217 | } | 198 | } |
218 | 199 | ||
219 | #[test] | 200 | #[test] |
220 | fn goto_def_for_macros() { | 201 | fn goto_def_for_macros() { |
221 | check_goto( | 202 | check( |
222 | " | 203 | r#" |
223 | //- /lib.rs | 204 | macro_rules! foo { () => { () } } |
224 | macro_rules! foo { () => { () } } | 205 | //^^^ |
225 | 206 | fn bar() { | |
226 | fn bar() { | 207 | <|>foo!(); |
227 | <|>foo!(); | 208 | } |
228 | } | 209 | "#, |
229 | ", | ||
230 | "foo MACRO_CALL FileId(1) 0..33 13..16", | ||
231 | "macro_rules! foo { () => { () } }|foo", | ||
232 | ); | 210 | ); |
233 | } | 211 | } |
234 | 212 | ||
235 | #[test] | 213 | #[test] |
236 | fn goto_def_for_macros_from_other_crates() { | 214 | fn goto_def_for_macros_from_other_crates() { |
237 | check_goto( | 215 | check( |
238 | " | ||
239 | //- /lib.rs | ||
240 | use foo::foo; | ||
241 | fn bar() { | ||
242 | <|>foo!(); | ||
243 | } | ||
244 | |||
245 | //- /foo/lib.rs | ||
246 | #[macro_export] | ||
247 | macro_rules! foo { () => { () } } | ||
248 | ", | ||
249 | "foo MACRO_CALL FileId(2) 0..49 29..32", | ||
250 | "#[macro_export]\nmacro_rules! foo { () => { () } }|foo", | ||
251 | ); | ||
252 | } | ||
253 | |||
254 | #[test] | ||
255 | fn goto_def_for_use_alias() { | ||
256 | check_goto( | ||
257 | r#" | 216 | r#" |
258 | //- /lib.rs | 217 | //- /lib.rs |
259 | use foo as bar<|>; | 218 | use foo::foo; |
219 | fn bar() { | ||
220 | <|>foo!(); | ||
221 | } | ||
260 | 222 | ||
261 | //- /foo/lib.rs | 223 | //- /foo/lib.rs |
262 | #[macro_export] | 224 | #[macro_export] |
263 | macro_rules! foo { () => { () } } | 225 | macro_rules! foo { () => { () } } |
226 | //^^^ | ||
264 | "#, | 227 | "#, |
265 | "SOURCE_FILE FileId(2) 0..50", | ||
266 | "#[macro_export]\nmacro_rules! foo { () => { () } }\n", | ||
267 | ); | ||
268 | } | ||
269 | |||
270 | #[test] | ||
271 | fn goto_def_for_use_alias_foo_macro() { | ||
272 | check_goto( | ||
273 | " | ||
274 | //- /lib.rs | ||
275 | use foo::foo as bar<|>; | ||
276 | |||
277 | //- /foo/lib.rs | ||
278 | #[macro_export] | ||
279 | macro_rules! foo { () => { () } } | ||
280 | ", | ||
281 | "foo MACRO_CALL FileId(2) 0..49 29..32", | ||
282 | "#[macro_export]\nmacro_rules! foo { () => { () } }|foo", | ||
283 | ); | 228 | ); |
284 | } | 229 | } |
285 | 230 | ||
286 | #[test] | 231 | #[test] |
287 | fn goto_def_for_macros_in_use_tree() { | 232 | fn goto_def_for_macros_in_use_tree() { |
288 | check_goto( | 233 | check( |
289 | " | 234 | r#" |
290 | //- /lib.rs | 235 | //- /lib.rs |
291 | use foo::foo<|>; | 236 | use foo::foo<|>; |
292 | 237 | ||
293 | //- /foo/lib.rs | 238 | //- /foo/lib.rs |
294 | #[macro_export] | 239 | #[macro_export] |
295 | macro_rules! foo { () => { () } } | 240 | macro_rules! foo { () => { () } } |
296 | ", | 241 | //^^^ |
297 | "foo MACRO_CALL FileId(2) 0..49 29..32", | 242 | "#, |
298 | "#[macro_export]\nmacro_rules! foo { () => { () } }|foo", | ||
299 | ); | 243 | ); |
300 | } | 244 | } |
301 | 245 | ||
302 | #[test] | 246 | #[test] |
303 | fn goto_def_for_macro_defined_fn_with_arg() { | 247 | fn goto_def_for_macro_defined_fn_with_arg() { |
304 | check_goto( | 248 | check( |
305 | r#" | 249 | r#" |
306 | //- /lib.rs | 250 | //- /lib.rs |
307 | macro_rules! define_fn { | 251 | macro_rules! define_fn { |
@@ -309,522 +253,478 @@ macro_rules! define_fn { | |||
309 | } | 253 | } |
310 | 254 | ||
311 | define_fn!(foo); | 255 | define_fn!(foo); |
256 | //^^^ | ||
312 | 257 | ||
313 | fn bar() { | 258 | fn bar() { |
314 | <|>foo(); | 259 | <|>foo(); |
315 | } | 260 | } |
316 | "#, | 261 | "#, |
317 | "foo FN_DEF FileId(1) 65..81 76..79", | ||
318 | "define_fn!(foo);|foo", | ||
319 | ); | 262 | ); |
320 | } | 263 | } |
321 | 264 | ||
322 | #[test] | 265 | #[test] |
323 | fn goto_def_for_macro_defined_fn_no_arg() { | 266 | fn goto_def_for_macro_defined_fn_no_arg() { |
324 | check_goto( | 267 | check( |
325 | r#" | 268 | r#" |
326 | //- /lib.rs | 269 | //- /lib.rs |
327 | macro_rules! define_fn { | 270 | macro_rules! define_fn { |
328 | () => (fn foo() {}) | 271 | () => (fn foo() {}) |
329 | } | 272 | } |
330 | 273 | ||
331 | define_fn!(); | 274 | define_fn!(); |
275 | //^^^^^^^^^^^^^ | ||
332 | 276 | ||
333 | fn bar() { | 277 | fn bar() { |
334 | <|>foo(); | 278 | <|>foo(); |
335 | } | 279 | } |
336 | "#, | 280 | "#, |
337 | "foo FN_DEF FileId(1) 52..65 52..65", | ||
338 | "define_fn!();|define_fn!();", | ||
339 | ); | 281 | ); |
340 | } | 282 | } |
341 | 283 | ||
342 | #[test] | 284 | #[test] |
343 | fn goto_definition_works_for_macro_inside_pattern() { | 285 | fn goto_definition_works_for_macro_inside_pattern() { |
344 | check_goto( | 286 | check( |
345 | " | 287 | r#" |
346 | //- /lib.rs | 288 | //- /lib.rs |
347 | macro_rules! foo {() => {0}} | 289 | macro_rules! foo {() => {0}} |
348 | 290 | //^^^ | |
349 | fn bar() { | 291 | |
350 | match (0,1) { | 292 | fn bar() { |
351 | (<|>foo!(), _) => {} | 293 | match (0,1) { |
352 | } | 294 | (<|>foo!(), _) => {} |
353 | } | 295 | } |
354 | ", | 296 | } |
355 | "foo MACRO_CALL FileId(1) 0..28 13..16", | 297 | "#, |
356 | "macro_rules! foo {() => {0}}|foo", | ||
357 | ); | 298 | ); |
358 | } | 299 | } |
359 | 300 | ||
360 | #[test] | 301 | #[test] |
361 | fn goto_definition_works_for_macro_inside_match_arm_lhs() { | 302 | fn goto_definition_works_for_macro_inside_match_arm_lhs() { |
362 | check_goto( | 303 | check( |
363 | " | 304 | r#" |
364 | //- /lib.rs | 305 | //- /lib.rs |
365 | macro_rules! foo {() => {0}} | 306 | macro_rules! foo {() => {0}} |
366 | 307 | //^^^ | |
367 | fn bar() { | 308 | fn bar() { |
368 | match 0 { | 309 | match 0 { |
369 | <|>foo!() => {} | 310 | <|>foo!() => {} |
370 | } | 311 | } |
371 | } | 312 | } |
372 | ", | 313 | "#, |
373 | "foo MACRO_CALL FileId(1) 0..28 13..16", | 314 | ); |
374 | "macro_rules! foo {() => {0}}|foo", | 315 | } |
316 | |||
317 | #[test] | ||
318 | fn goto_def_for_use_alias() { | ||
319 | check( | ||
320 | r#" | ||
321 | //- /lib.rs | ||
322 | use foo as bar<|>; | ||
323 | |||
324 | //- /foo/lib.rs | ||
325 | // empty | ||
326 | //^ file | ||
327 | "#, | ||
328 | ); | ||
329 | } | ||
330 | |||
331 | #[test] | ||
332 | fn goto_def_for_use_alias_foo_macro() { | ||
333 | check( | ||
334 | r#" | ||
335 | //- /lib.rs | ||
336 | use foo::foo as bar<|>; | ||
337 | |||
338 | //- /foo/lib.rs | ||
339 | #[macro_export] | ||
340 | macro_rules! foo { () => { () } } | ||
341 | //^^^ | ||
342 | "#, | ||
375 | ); | 343 | ); |
376 | } | 344 | } |
377 | 345 | ||
378 | #[test] | 346 | #[test] |
379 | fn goto_def_for_methods() { | 347 | fn goto_def_for_methods() { |
380 | check_goto( | 348 | check( |
381 | " | 349 | r#" |
382 | //- /lib.rs | 350 | //- /lib.rs |
383 | struct Foo; | 351 | struct Foo; |
384 | impl Foo { | 352 | impl Foo { |
385 | fn frobnicate(&self) { } | 353 | fn frobnicate(&self) { } |
386 | } | 354 | //^^^^^^^^^^ |
355 | } | ||
387 | 356 | ||
388 | fn bar(foo: &Foo) { | 357 | fn bar(foo: &Foo) { |
389 | foo.frobnicate<|>(); | 358 | foo.frobnicate<|>(); |
390 | } | 359 | } |
391 | ", | 360 | "#, |
392 | "frobnicate FN_DEF FileId(1) 27..51 30..40", | ||
393 | "fn frobnicate(&self) { }|frobnicate", | ||
394 | ); | 361 | ); |
395 | } | 362 | } |
396 | 363 | ||
397 | #[test] | 364 | #[test] |
398 | fn goto_def_for_fields() { | 365 | fn goto_def_for_fields() { |
399 | check_goto( | 366 | check( |
400 | r" | 367 | r#" |
401 | //- /lib.rs | 368 | struct Foo { |
402 | struct Foo { | 369 | spam: u32, |
403 | spam: u32, | 370 | } //^^^^ |
404 | } | ||
405 | 371 | ||
406 | fn bar(foo: &Foo) { | 372 | fn bar(foo: &Foo) { |
407 | foo.spam<|>; | 373 | foo.spam<|>; |
408 | } | 374 | } |
409 | ", | 375 | "#, |
410 | "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21", | ||
411 | "spam: u32|spam", | ||
412 | ); | 376 | ); |
413 | } | 377 | } |
414 | 378 | ||
415 | #[test] | 379 | #[test] |
416 | fn goto_def_for_record_fields() { | 380 | fn goto_def_for_record_fields() { |
417 | check_goto( | 381 | check( |
418 | r" | 382 | r#" |
419 | //- /lib.rs | 383 | //- /lib.rs |
420 | struct Foo { | 384 | struct Foo { |
421 | spam: u32, | 385 | spam: u32, |
422 | } | 386 | } //^^^^ |
423 | 387 | ||
424 | fn bar() -> Foo { | 388 | fn bar() -> Foo { |
425 | Foo { | 389 | Foo { |
426 | spam<|>: 0, | 390 | spam<|>: 0, |
427 | } | 391 | } |
428 | } | 392 | } |
429 | ", | 393 | "#, |
430 | "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21", | ||
431 | "spam: u32|spam", | ||
432 | ); | 394 | ); |
433 | } | 395 | } |
434 | 396 | ||
435 | #[test] | 397 | #[test] |
436 | fn goto_def_for_record_pat_fields() { | 398 | fn goto_def_for_record_pat_fields() { |
437 | check_goto( | 399 | check( |
438 | r" | 400 | r#" |
439 | //- /lib.rs | 401 | //- /lib.rs |
440 | struct Foo { | 402 | struct Foo { |
441 | spam: u32, | 403 | spam: u32, |
442 | } | 404 | } //^^^^ |
443 | 405 | ||
444 | fn bar(foo: Foo) -> Foo { | 406 | fn bar(foo: Foo) -> Foo { |
445 | let Foo { spam<|>: _, } = foo | 407 | let Foo { spam<|>: _, } = foo |
446 | } | 408 | } |
447 | ", | 409 | "#, |
448 | "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21", | ||
449 | "spam: u32|spam", | ||
450 | ); | 410 | ); |
451 | } | 411 | } |
452 | 412 | ||
453 | #[test] | 413 | #[test] |
454 | fn goto_def_for_record_fields_macros() { | 414 | fn goto_def_for_record_fields_macros() { |
455 | check_goto( | 415 | check( |
456 | r" | 416 | r" |
457 | //- /lib.rs | 417 | macro_rules! m { () => { 92 };} |
458 | macro_rules! m { () => { 92 };} | 418 | struct Foo { spam: u32 } |
459 | struct Foo { spam: u32 } | 419 | //^^^^ |
460 | 420 | ||
461 | fn bar() -> Foo { | 421 | fn bar() -> Foo { |
462 | Foo { spam<|>: m!() } | 422 | Foo { spam<|>: m!() } |
463 | } | 423 | } |
464 | ", | 424 | ", |
465 | "spam RECORD_FIELD_DEF FileId(1) 45..54 45..49", | ||
466 | "spam: u32|spam", | ||
467 | ); | 425 | ); |
468 | } | 426 | } |
469 | 427 | ||
470 | #[test] | 428 | #[test] |
471 | fn goto_for_tuple_fields() { | 429 | fn goto_for_tuple_fields() { |
472 | check_goto( | 430 | check( |
473 | " | 431 | r#" |
474 | //- /lib.rs | 432 | struct Foo(u32); |
475 | struct Foo(u32); | 433 | //^^^ |
476 | 434 | ||
477 | fn bar() { | 435 | fn bar() { |
478 | let foo = Foo(0); | 436 | let foo = Foo(0); |
479 | foo.<|>0; | 437 | foo.<|>0; |
480 | } | 438 | } |
481 | ", | 439 | "#, |
482 | "TUPLE_FIELD_DEF FileId(1) 11..14", | ||
483 | "u32", | ||
484 | ); | 440 | ); |
485 | } | 441 | } |
486 | 442 | ||
487 | #[test] | 443 | #[test] |
488 | fn goto_def_for_ufcs_inherent_methods() { | 444 | fn goto_def_for_ufcs_inherent_methods() { |
489 | check_goto( | 445 | check( |
490 | " | 446 | r#" |
491 | //- /lib.rs | 447 | struct Foo; |
492 | struct Foo; | 448 | impl Foo { |
493 | impl Foo { | 449 | fn frobnicate() { } |
494 | fn frobnicate() { } | 450 | } //^^^^^^^^^^ |
495 | } | ||
496 | 451 | ||
497 | fn bar(foo: &Foo) { | 452 | fn bar(foo: &Foo) { |
498 | Foo::frobnicate<|>(); | 453 | Foo::frobnicate<|>(); |
499 | } | 454 | } |
500 | ", | 455 | "#, |
501 | "frobnicate FN_DEF FileId(1) 27..46 30..40", | ||
502 | "fn frobnicate() { }|frobnicate", | ||
503 | ); | 456 | ); |
504 | } | 457 | } |
505 | 458 | ||
506 | #[test] | 459 | #[test] |
507 | fn goto_def_for_ufcs_trait_methods_through_traits() { | 460 | fn goto_def_for_ufcs_trait_methods_through_traits() { |
508 | check_goto( | 461 | check( |
509 | " | 462 | r#" |
510 | //- /lib.rs | 463 | trait Foo { |
511 | trait Foo { | 464 | fn frobnicate(); |
512 | fn frobnicate(); | 465 | } //^^^^^^^^^^ |
513 | } | ||
514 | 466 | ||
515 | fn bar() { | 467 | fn bar() { |
516 | Foo::frobnicate<|>(); | 468 | Foo::frobnicate<|>(); |
517 | } | 469 | } |
518 | ", | 470 | "#, |
519 | "frobnicate FN_DEF FileId(1) 16..32 19..29", | ||
520 | "fn frobnicate();|frobnicate", | ||
521 | ); | 471 | ); |
522 | } | 472 | } |
523 | 473 | ||
524 | #[test] | 474 | #[test] |
525 | fn goto_def_for_ufcs_trait_methods_through_self() { | 475 | fn goto_def_for_ufcs_trait_methods_through_self() { |
526 | check_goto( | 476 | check( |
527 | " | 477 | r#" |
528 | //- /lib.rs | 478 | struct Foo; |
529 | struct Foo; | 479 | trait Trait { |
530 | trait Trait { | 480 | fn frobnicate(); |
531 | fn frobnicate(); | 481 | } //^^^^^^^^^^ |
532 | } | 482 | impl Trait for Foo {} |
533 | impl Trait for Foo {} | ||
534 | 483 | ||
535 | fn bar() { | 484 | fn bar() { |
536 | Foo::frobnicate<|>(); | 485 | Foo::frobnicate<|>(); |
537 | } | 486 | } |
538 | ", | 487 | "#, |
539 | "frobnicate FN_DEF FileId(1) 30..46 33..43", | ||
540 | "fn frobnicate();|frobnicate", | ||
541 | ); | 488 | ); |
542 | } | 489 | } |
543 | 490 | ||
544 | #[test] | 491 | #[test] |
545 | fn goto_definition_on_self() { | 492 | fn goto_definition_on_self() { |
546 | check_goto( | 493 | check( |
547 | " | 494 | r#" |
548 | //- /lib.rs | 495 | struct Foo; |
549 | struct Foo; | 496 | impl Foo { |
550 | impl Foo { | 497 | //^^^ |
551 | pub fn new() -> Self { | 498 | pub fn new() -> Self { |
552 | Self<|> {} | 499 | Self<|> {} |
553 | } | 500 | } |
554 | } | 501 | } |
555 | ", | 502 | "#, |
556 | "impl IMPL_DEF FileId(1) 12..73", | 503 | ); |
557 | "impl Foo {...}", | 504 | check( |
558 | ); | 505 | r#" |
559 | 506 | struct Foo; | |
560 | check_goto( | 507 | impl Foo { |
561 | " | 508 | //^^^ |
562 | //- /lib.rs | 509 | pub fn new() -> Self<|> { |
563 | struct Foo; | 510 | Self {} |
564 | impl Foo { | 511 | } |
565 | pub fn new() -> Self<|> { | 512 | } |
566 | Self {} | 513 | "#, |
567 | } | 514 | ); |
568 | } | 515 | |
569 | ", | 516 | check( |
570 | "impl IMPL_DEF FileId(1) 12..73", | 517 | r#" |
571 | "impl Foo {...}", | 518 | enum Foo { A } |
572 | ); | 519 | impl Foo { |
573 | 520 | //^^^ | |
574 | check_goto( | 521 | pub fn new() -> Self<|> { |
575 | " | 522 | Foo::A |
576 | //- /lib.rs | 523 | } |
577 | enum Foo { A } | 524 | } |
578 | impl Foo { | 525 | "#, |
579 | pub fn new() -> Self<|> { | 526 | ); |
580 | Foo::A | 527 | |
581 | } | 528 | check( |
582 | } | 529 | r#" |
583 | ", | 530 | enum Foo { A } |
584 | "impl IMPL_DEF FileId(1) 15..75", | 531 | impl Foo { |
585 | "impl Foo {...}", | 532 | //^^^ |
586 | ); | 533 | pub fn thing(a: &Self<|>) { |
587 | 534 | } | |
588 | check_goto( | 535 | } |
589 | " | 536 | "#, |
590 | //- /lib.rs | ||
591 | enum Foo { A } | ||
592 | impl Foo { | ||
593 | pub fn thing(a: &Self<|>) { | ||
594 | } | ||
595 | } | ||
596 | ", | ||
597 | "impl IMPL_DEF FileId(1) 15..62", | ||
598 | "impl Foo {...}", | ||
599 | ); | 537 | ); |
600 | } | 538 | } |
601 | 539 | ||
602 | #[test] | 540 | #[test] |
603 | fn goto_definition_on_self_in_trait_impl() { | 541 | fn goto_definition_on_self_in_trait_impl() { |
604 | check_goto( | 542 | check( |
605 | " | 543 | r#" |
606 | //- /lib.rs | 544 | struct Foo; |
607 | struct Foo; | 545 | trait Make { |
608 | trait Make { | 546 | fn new() -> Self; |
609 | fn new() -> Self; | 547 | } |
610 | } | 548 | impl Make for Foo { |
611 | impl Make for Foo { | 549 | //^^^ |
612 | fn new() -> Self { | 550 | fn new() -> Self { |
613 | Self<|> {} | 551 | Self<|> {} |
614 | } | 552 | } |
615 | } | 553 | } |
616 | ", | 554 | "#, |
617 | "impl IMPL_DEF FileId(1) 49..115", | ||
618 | "impl Make for Foo {...}", | ||
619 | ); | 555 | ); |
620 | 556 | ||
621 | check_goto( | 557 | check( |
622 | " | 558 | r#" |
623 | //- /lib.rs | 559 | struct Foo; |
624 | struct Foo; | 560 | trait Make { |
625 | trait Make { | 561 | fn new() -> Self; |
626 | fn new() -> Self; | 562 | } |
627 | } | 563 | impl Make for Foo { |
628 | impl Make for Foo { | 564 | //^^^ |
629 | fn new() -> Self<|> { | 565 | fn new() -> Self<|> { |
630 | Self {} | 566 | Self {} |
631 | } | 567 | } |
632 | } | 568 | } |
633 | ", | 569 | "#, |
634 | "impl IMPL_DEF FileId(1) 49..115", | ||
635 | "impl Make for Foo {...}", | ||
636 | ); | 570 | ); |
637 | } | 571 | } |
638 | 572 | ||
639 | #[test] | 573 | #[test] |
640 | fn goto_def_when_used_on_definition_name_itself() { | 574 | fn goto_def_when_used_on_definition_name_itself() { |
641 | check_goto( | 575 | check( |
642 | " | 576 | r#" |
643 | //- /lib.rs | 577 | struct Foo<|> { value: u32 } |
644 | struct Foo<|> { value: u32 } | 578 | //^^^ |
645 | ", | 579 | "#, |
646 | "Foo STRUCT_DEF FileId(1) 0..25 7..10", | ||
647 | "struct Foo { value: u32 }|Foo", | ||
648 | ); | 580 | ); |
649 | 581 | ||
650 | check_goto( | 582 | check( |
651 | r#" | 583 | r#" |
652 | //- /lib.rs | 584 | struct Foo { |
653 | struct Foo { | 585 | field<|>: string, |
654 | field<|>: string, | 586 | } //^^^^^ |
655 | } | 587 | "#, |
656 | "#, | ||
657 | "field RECORD_FIELD_DEF FileId(1) 17..30 17..22", | ||
658 | "field: string|field", | ||
659 | ); | 588 | ); |
660 | 589 | ||
661 | check_goto( | 590 | check( |
662 | " | 591 | r#" |
663 | //- /lib.rs | 592 | fn foo_test<|>() { } |
664 | fn foo_test<|>() { } | 593 | //^^^^^^^^ |
665 | ", | 594 | "#, |
666 | "foo_test FN_DEF FileId(1) 0..17 3..11", | ||
667 | "fn foo_test() { }|foo_test", | ||
668 | ); | 595 | ); |
669 | 596 | ||
670 | check_goto( | 597 | check( |
671 | " | 598 | r#" |
672 | //- /lib.rs | 599 | enum Foo<|> { Variant } |
673 | enum Foo<|> { | 600 | //^^^ |
674 | Variant, | 601 | "#, |
675 | } | ||
676 | ", | ||
677 | "Foo ENUM_DEF FileId(1) 0..25 5..8", | ||
678 | "enum Foo {...}|Foo", | ||
679 | ); | ||
680 | |||
681 | check_goto( | ||
682 | " | ||
683 | //- /lib.rs | ||
684 | enum Foo { | ||
685 | Variant1, | ||
686 | Variant2<|>, | ||
687 | Variant3, | ||
688 | } | ||
689 | ", | ||
690 | "Variant2 ENUM_VARIANT FileId(1) 29..37 29..37", | ||
691 | "Variant2|Variant2", | ||
692 | ); | 602 | ); |
693 | 603 | ||
694 | check_goto( | 604 | check( |
695 | r#" | 605 | r#" |
696 | //- /lib.rs | 606 | enum Foo { |
697 | static INNER<|>: &str = ""; | 607 | Variant1, |
698 | "#, | 608 | Variant2<|>, |
699 | "INNER STATIC_DEF FileId(1) 0..24 7..12", | 609 | //^^^^^^^^ |
700 | "static INNER: &str = \"\";|INNER", | 610 | Variant3, |
611 | } | ||
612 | "#, | ||
701 | ); | 613 | ); |
702 | 614 | ||
703 | check_goto( | 615 | check( |
704 | r#" | 616 | r#" |
705 | //- /lib.rs | 617 | static INNER<|>: &str = ""; |
706 | const INNER<|>: &str = ""; | 618 | //^^^^^ |
707 | "#, | 619 | "#, |
708 | "INNER CONST_DEF FileId(1) 0..23 6..11", | ||
709 | "const INNER: &str = \"\";|INNER", | ||
710 | ); | 620 | ); |
711 | 621 | ||
712 | check_goto( | 622 | check( |
713 | r#" | 623 | r#" |
714 | //- /lib.rs | 624 | const INNER<|>: &str = ""; |
715 | type Thing<|> = Option<()>; | 625 | //^^^^^ |
716 | "#, | 626 | "#, |
717 | "Thing TYPE_ALIAS_DEF FileId(1) 0..24 5..10", | ||
718 | "type Thing = Option<()>;|Thing", | ||
719 | ); | 627 | ); |
720 | 628 | ||
721 | check_goto( | 629 | check( |
722 | r#" | 630 | r#" |
723 | //- /lib.rs | 631 | type Thing<|> = Option<()>; |
724 | trait Foo<|> { } | 632 | //^^^^^ |
725 | "#, | 633 | "#, |
726 | "Foo TRAIT_DEF FileId(1) 0..13 6..9", | ||
727 | "trait Foo { }|Foo", | ||
728 | ); | 634 | ); |
729 | 635 | ||
730 | check_goto( | 636 | check( |
731 | r#" | 637 | r#" |
732 | //- /lib.rs | 638 | trait Foo<|> { } |
733 | mod bar<|> { } | 639 | //^^^ |
734 | "#, | 640 | "#, |
735 | "bar MODULE FileId(1) 0..11 4..7", | 641 | ); |
736 | "mod bar { }|bar", | 642 | |
643 | check( | ||
644 | r#" | ||
645 | mod bar<|> { } | ||
646 | //^^^ | ||
647 | "#, | ||
737 | ); | 648 | ); |
738 | } | 649 | } |
739 | 650 | ||
740 | #[test] | 651 | #[test] |
741 | fn goto_from_macro() { | 652 | fn goto_from_macro() { |
742 | check_goto( | 653 | check( |
743 | " | 654 | r#" |
744 | //- /lib.rs | 655 | macro_rules! id { |
745 | macro_rules! id { | 656 | ($($tt:tt)*) => { $($tt)* } |
746 | ($($tt:tt)*) => { $($tt)* } | 657 | } |
747 | } | 658 | fn foo() {} |
748 | fn foo() {} | 659 | //^^^ |
749 | id! { | 660 | id! { |
750 | fn bar() { | 661 | fn bar() { |
751 | fo<|>o(); | 662 | fo<|>o(); |
752 | } | 663 | } |
753 | } | 664 | } |
754 | mod confuse_index { fn foo(); } | 665 | mod confuse_index { fn foo(); } |
755 | ", | 666 | "#, |
756 | "foo FN_DEF FileId(1) 52..63 55..58", | ||
757 | "fn foo() {}|foo", | ||
758 | ); | 667 | ); |
759 | } | 668 | } |
760 | 669 | ||
761 | #[test] | 670 | #[test] |
762 | fn goto_through_format() { | 671 | fn goto_through_format() { |
763 | check_goto( | 672 | check( |
764 | " | 673 | r#" |
765 | //- /lib.rs | 674 | #[macro_export] |
766 | #[macro_export] | 675 | macro_rules! format { |
767 | macro_rules! format { | 676 | ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) |
768 | ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) | 677 | } |
769 | } | 678 | #[rustc_builtin_macro] |
770 | #[rustc_builtin_macro] | 679 | #[macro_export] |
771 | #[macro_export] | 680 | macro_rules! format_args { |
772 | macro_rules! format_args { | 681 | ($fmt:expr) => ({ /* compiler built-in */ }); |
773 | ($fmt:expr) => ({ /* compiler built-in */ }); | 682 | ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) |
774 | ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) | 683 | } |
775 | } | 684 | pub mod __export { |
776 | pub mod __export { | 685 | pub use crate::format_args; |
777 | pub use crate::format_args; | 686 | fn foo() {} // for index confusion |
778 | fn foo() {} // for index confusion | 687 | } |
779 | } | 688 | fn foo() -> i8 {} |
780 | fn foo() -> i8 {} | 689 | //^^^ |
781 | fn test() { | 690 | fn test() { |
782 | format!(\"{}\", fo<|>o()) | 691 | format!("{}", fo<|>o()) |
783 | } | 692 | } |
784 | ", | 693 | "#, |
785 | "foo FN_DEF FileId(1) 398..415 401..404", | ||
786 | "fn foo() -> i8 {}|foo", | ||
787 | ); | 694 | ); |
788 | } | 695 | } |
789 | 696 | ||
790 | #[test] | 697 | #[test] |
791 | fn goto_for_type_param() { | 698 | fn goto_for_type_param() { |
792 | check_goto( | 699 | check( |
793 | r#" | 700 | r#" |
794 | //- /lib.rs | 701 | struct Foo<T: Clone> { t: <|>T } |
795 | struct Foo<T: Clone> { | 702 | //^ |
796 | t: <|>T, | 703 | "#, |
797 | } | ||
798 | "#, | ||
799 | "T TYPE_PARAM FileId(1) 11..19 11..12", | ||
800 | "T: Clone|T", | ||
801 | ); | 704 | ); |
802 | } | 705 | } |
803 | 706 | ||
804 | #[test] | 707 | #[test] |
805 | fn goto_within_macro() { | 708 | fn goto_within_macro() { |
806 | check_goto( | 709 | check( |
807 | r#" | 710 | r#" |
808 | //- /lib.rs | ||
809 | macro_rules! id { | 711 | macro_rules! id { |
810 | ($($tt:tt)*) => ($($tt)*) | 712 | ($($tt:tt)*) => ($($tt)*) |
811 | } | 713 | } |
812 | 714 | ||
813 | fn foo() { | 715 | fn foo() { |
814 | let x = 1; | 716 | let x = 1; |
717 | //^ | ||
815 | id!({ | 718 | id!({ |
816 | let y = <|>x; | 719 | let y = <|>x; |
817 | let z = y; | 720 | let z = y; |
818 | }); | 721 | }); |
819 | } | 722 | } |
820 | "#, | 723 | "#, |
821 | "x BIND_PAT FileId(1) 70..71", | ||
822 | "x", | ||
823 | ); | 724 | ); |
824 | 725 | ||
825 | check_goto( | 726 | check( |
826 | r#" | 727 | r#" |
827 | //- /lib.rs | ||
828 | macro_rules! id { | 728 | macro_rules! id { |
829 | ($($tt:tt)*) => ($($tt)*) | 729 | ($($tt:tt)*) => ($($tt)*) |
830 | } | 730 | } |
@@ -833,159 +733,125 @@ fn foo() { | |||
833 | let x = 1; | 733 | let x = 1; |
834 | id!({ | 734 | id!({ |
835 | let y = x; | 735 | let y = x; |
736 | //^ | ||
836 | let z = <|>y; | 737 | let z = <|>y; |
837 | }); | 738 | }); |
838 | } | 739 | } |
839 | "#, | 740 | "#, |
840 | "y BIND_PAT FileId(1) 99..100", | ||
841 | "y", | ||
842 | ); | 741 | ); |
843 | } | 742 | } |
844 | 743 | ||
845 | #[test] | 744 | #[test] |
846 | fn goto_def_in_local_fn() { | 745 | fn goto_def_in_local_fn() { |
847 | check_goto( | 746 | check( |
848 | " | 747 | r#" |
849 | //- /lib.rs | 748 | fn main() { |
850 | fn main() { | 749 | fn foo() { |
851 | fn foo() { | 750 | let x = 92; |
852 | let x = 92; | 751 | //^ |
853 | <|>x; | 752 | <|>x; |
854 | } | 753 | } |
855 | } | 754 | } |
856 | ", | 755 | "#, |
857 | "x BIND_PAT FileId(1) 39..40", | ||
858 | "x", | ||
859 | ); | 756 | ); |
860 | } | 757 | } |
861 | 758 | ||
862 | #[test] | 759 | #[test] |
863 | fn goto_def_in_local_macro() { | 760 | fn goto_def_in_local_macro() { |
864 | check_goto( | 761 | check( |
865 | r" | 762 | r#" |
866 | //- /lib.rs | 763 | fn bar() { |
867 | fn bar() { | 764 | macro_rules! foo { () => { () } } |
868 | macro_rules! foo { () => { () } } | 765 | //^^^ |
869 | <|>foo!(); | 766 | <|>foo!(); |
870 | } | 767 | } |
871 | ", | 768 | "#, |
872 | "foo MACRO_CALL FileId(1) 15..48 28..31", | ||
873 | "macro_rules! foo { () => { () } }|foo", | ||
874 | ); | 769 | ); |
875 | } | 770 | } |
876 | 771 | ||
877 | #[test] | 772 | #[test] |
878 | fn goto_def_for_field_init_shorthand() { | 773 | fn goto_def_for_field_init_shorthand() { |
879 | check_goto( | 774 | check( |
880 | " | 775 | r#" |
881 | //- /lib.rs | 776 | struct Foo { x: i32 } |
882 | struct Foo { x: i32 } | 777 | fn main() { |
883 | fn main() { | 778 | let x = 92; |
884 | let x = 92; | 779 | //^ |
885 | Foo { x<|> }; | 780 | Foo { x<|> }; |
886 | } | 781 | } |
887 | ", | 782 | "#, |
888 | "x BIND_PAT FileId(1) 42..43", | ||
889 | "x", | ||
890 | ) | 783 | ) |
891 | } | 784 | } |
892 | 785 | ||
893 | #[test] | 786 | #[test] |
894 | fn goto_def_for_enum_variant_field() { | 787 | fn goto_def_for_enum_variant_field() { |
895 | check_goto( | 788 | check( |
896 | " | 789 | r#" |
897 | //- /lib.rs | 790 | enum Foo { |
898 | enum Foo { | 791 | Bar { x: i32 } |
899 | Bar { x: i32 } | 792 | } //^ |
900 | } | 793 | fn baz(foo: Foo) { |
901 | fn baz(foo: Foo) { | 794 | match foo { |
902 | match foo { | 795 | Foo::Bar { x<|> } => x |
903 | Foo::Bar { x<|> } => x | 796 | }; |
904 | }; | 797 | } |
905 | } | 798 | "#, |
906 | ", | ||
907 | "x RECORD_FIELD_DEF FileId(1) 21..27 21..22", | ||
908 | "x: i32|x", | ||
909 | ); | 799 | ); |
910 | } | 800 | } |
911 | 801 | ||
912 | #[test] | 802 | #[test] |
913 | fn goto_def_for_enum_variant_self_pattern_const() { | 803 | fn goto_def_for_enum_variant_self_pattern_const() { |
914 | check_goto( | 804 | check( |
915 | " | 805 | r#" |
916 | //- /lib.rs | 806 | enum Foo { Bar } |
917 | enum Foo { | 807 | //^^^ |
918 | Bar, | 808 | impl Foo { |
919 | } | 809 | fn baz(self) { |
920 | impl Foo { | 810 | match self { Self::Bar<|> => {} } |
921 | fn baz(self) { | 811 | } |
922 | match self { | 812 | } |
923 | Self::Bar<|> => {} | 813 | "#, |
924 | } | ||
925 | } | ||
926 | } | ||
927 | ", | ||
928 | "Bar ENUM_VARIANT FileId(1) 15..18 15..18", | ||
929 | "Bar|Bar", | ||
930 | ); | 814 | ); |
931 | } | 815 | } |
932 | 816 | ||
933 | #[test] | 817 | #[test] |
934 | fn goto_def_for_enum_variant_self_pattern_record() { | 818 | fn goto_def_for_enum_variant_self_pattern_record() { |
935 | check_goto( | 819 | check( |
936 | " | 820 | r#" |
937 | //- /lib.rs | 821 | enum Foo { Bar { val: i32 } } |
938 | enum Foo { | 822 | //^^^ |
939 | Bar { val: i32 }, | 823 | impl Foo { |
940 | } | 824 | fn baz(self) -> i32 { |
941 | impl Foo { | 825 | match self { Self::Bar<|> { val } => {} } |
942 | fn baz(self) -> i32 { | 826 | } |
943 | match self { | 827 | } |
944 | Self::Bar<|> { val } => {} | 828 | "#, |
945 | } | ||
946 | } | ||
947 | } | ||
948 | ", | ||
949 | "Bar ENUM_VARIANT FileId(1) 15..31 15..18", | ||
950 | "Bar { val: i32 }|Bar", | ||
951 | ); | 829 | ); |
952 | } | 830 | } |
953 | 831 | ||
954 | #[test] | 832 | #[test] |
955 | fn goto_def_for_enum_variant_self_expr_const() { | 833 | fn goto_def_for_enum_variant_self_expr_const() { |
956 | check_goto( | 834 | check( |
957 | " | 835 | r#" |
958 | //- /lib.rs | 836 | enum Foo { Bar } |
959 | enum Foo { | 837 | //^^^ |
960 | Bar, | 838 | impl Foo { |
961 | } | 839 | fn baz(self) { Self::Bar<|>; } |
962 | impl Foo { | 840 | } |
963 | fn baz(self) { | 841 | "#, |
964 | Self::Bar<|>; | ||
965 | } | ||
966 | } | ||
967 | ", | ||
968 | "Bar ENUM_VARIANT FileId(1) 15..18 15..18", | ||
969 | "Bar|Bar", | ||
970 | ); | 842 | ); |
971 | } | 843 | } |
972 | 844 | ||
973 | #[test] | 845 | #[test] |
974 | fn goto_def_for_enum_variant_self_expr_record() { | 846 | fn goto_def_for_enum_variant_self_expr_record() { |
975 | check_goto( | 847 | check( |
976 | " | 848 | r#" |
977 | //- /lib.rs | 849 | enum Foo { Bar { val: i32 } } |
978 | enum Foo { | 850 | //^^^ |
979 | Bar { val: i32 }, | 851 | impl Foo { |
980 | } | 852 | fn baz(self) { Self::Bar<|> {val: 4}; } |
981 | impl Foo { | 853 | } |
982 | fn baz(self) { | 854 | "#, |
983 | Self::Bar<|> {val: 4}; | ||
984 | } | ||
985 | } | ||
986 | ", | ||
987 | "Bar ENUM_VARIANT FileId(1) 15..31 15..18", | ||
988 | "Bar { val: i32 }|Bar", | ||
989 | ); | 855 | ); |
990 | } | 856 | } |
991 | } | 857 | } |
diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs index 0cec0657e..9acc960fc 100644 --- a/crates/ra_ide/src/goto_implementation.rs +++ b/crates/ra_ide/src/goto_implementation.rs | |||
@@ -74,135 +74,156 @@ fn impls_for_trait( | |||
74 | 74 | ||
75 | #[cfg(test)] | 75 | #[cfg(test)] |
76 | mod tests { | 76 | mod tests { |
77 | use crate::mock_analysis::analysis_and_position; | 77 | use ra_db::FileRange; |
78 | 78 | ||
79 | fn check_goto(fixture: &str, expected: &[&str]) { | 79 | use crate::mock_analysis::MockAnalysis; |
80 | let (analysis, pos) = analysis_and_position(fixture); | ||
81 | 80 | ||
82 | let mut navs = analysis.goto_implementation(pos).unwrap().unwrap().info; | 81 | fn check(ra_fixture: &str) { |
83 | assert_eq!(navs.len(), expected.len()); | 82 | let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); |
84 | navs.sort_by_key(|nav| (nav.file_id(), nav.full_range().start())); | 83 | let annotations = mock.annotations(); |
85 | navs.into_iter().enumerate().for_each(|(i, nav)| nav.assert_match(expected[i])); | 84 | let analysis = mock.analysis(); |
85 | |||
86 | let navs = analysis.goto_implementation(position).unwrap().unwrap().info; | ||
87 | |||
88 | let key = |frange: &FileRange| (frange.file_id, frange.range.start()); | ||
89 | |||
90 | let mut expected = annotations | ||
91 | .into_iter() | ||
92 | .map(|(range, data)| { | ||
93 | assert!(data.is_empty()); | ||
94 | range | ||
95 | }) | ||
96 | .collect::<Vec<_>>(); | ||
97 | expected.sort_by_key(key); | ||
98 | |||
99 | let mut actual = navs | ||
100 | .into_iter() | ||
101 | .map(|nav| FileRange { file_id: nav.file_id(), range: nav.range() }) | ||
102 | .collect::<Vec<_>>(); | ||
103 | actual.sort_by_key(key); | ||
104 | |||
105 | assert_eq!(expected, actual); | ||
86 | } | 106 | } |
87 | 107 | ||
88 | #[test] | 108 | #[test] |
89 | fn goto_implementation_works() { | 109 | fn goto_implementation_works() { |
90 | check_goto( | 110 | check( |
91 | " | 111 | r#" |
92 | //- /lib.rs | 112 | struct Foo<|>; |
93 | struct Foo<|>; | 113 | impl Foo {} |
94 | impl Foo {} | 114 | //^^^ |
95 | ", | 115 | "#, |
96 | &["impl IMPL_DEF FileId(1) 12..23"], | ||
97 | ); | 116 | ); |
98 | } | 117 | } |
99 | 118 | ||
100 | #[test] | 119 | #[test] |
101 | fn goto_implementation_works_multiple_blocks() { | 120 | fn goto_implementation_works_multiple_blocks() { |
102 | check_goto( | 121 | check( |
103 | " | 122 | r#" |
104 | //- /lib.rs | 123 | struct Foo<|>; |
105 | struct Foo<|>; | 124 | impl Foo {} |
106 | impl Foo {} | 125 | //^^^ |
107 | impl Foo {} | 126 | impl Foo {} |
108 | ", | 127 | //^^^ |
109 | &["impl IMPL_DEF FileId(1) 12..23", "impl IMPL_DEF FileId(1) 24..35"], | 128 | "#, |
110 | ); | 129 | ); |
111 | } | 130 | } |
112 | 131 | ||
113 | #[test] | 132 | #[test] |
114 | fn goto_implementation_works_multiple_mods() { | 133 | fn goto_implementation_works_multiple_mods() { |
115 | check_goto( | 134 | check( |
116 | " | 135 | r#" |
117 | //- /lib.rs | 136 | struct Foo<|>; |
118 | struct Foo<|>; | 137 | mod a { |
119 | mod a { | 138 | impl super::Foo {} |
120 | impl super::Foo {} | 139 | //^^^^^^^^^^ |
121 | } | 140 | } |
122 | mod b { | 141 | mod b { |
123 | impl super::Foo {} | 142 | impl super::Foo {} |
124 | } | 143 | //^^^^^^^^^^ |
125 | ", | 144 | } |
126 | &["impl IMPL_DEF FileId(1) 24..42", "impl IMPL_DEF FileId(1) 57..75"], | 145 | "#, |
127 | ); | 146 | ); |
128 | } | 147 | } |
129 | 148 | ||
130 | #[test] | 149 | #[test] |
131 | fn goto_implementation_works_multiple_files() { | 150 | fn goto_implementation_works_multiple_files() { |
132 | check_goto( | 151 | check( |
133 | " | 152 | r#" |
134 | //- /lib.rs | 153 | //- /lib.rs |
135 | struct Foo<|>; | 154 | struct Foo<|>; |
136 | mod a; | 155 | mod a; |
137 | mod b; | 156 | mod b; |
138 | //- /a.rs | 157 | //- /a.rs |
139 | impl crate::Foo {} | 158 | impl crate::Foo {} |
140 | //- /b.rs | 159 | //^^^^^^^^^^ |
141 | impl crate::Foo {} | 160 | //- /b.rs |
142 | ", | 161 | impl crate::Foo {} |
143 | &["impl IMPL_DEF FileId(2) 0..18", "impl IMPL_DEF FileId(3) 0..18"], | 162 | //^^^^^^^^^^ |
163 | "#, | ||
144 | ); | 164 | ); |
145 | } | 165 | } |
146 | 166 | ||
147 | #[test] | 167 | #[test] |
148 | fn goto_implementation_for_trait() { | 168 | fn goto_implementation_for_trait() { |
149 | check_goto( | 169 | check( |
150 | " | 170 | r#" |
151 | //- /lib.rs | 171 | trait T<|> {} |
152 | trait T<|> {} | 172 | struct Foo; |
153 | struct Foo; | 173 | impl T for Foo {} |
154 | impl T for Foo {} | 174 | //^^^ |
155 | ", | 175 | "#, |
156 | &["impl IMPL_DEF FileId(1) 23..40"], | ||
157 | ); | 176 | ); |
158 | } | 177 | } |
159 | 178 | ||
160 | #[test] | 179 | #[test] |
161 | fn goto_implementation_for_trait_multiple_files() { | 180 | fn goto_implementation_for_trait_multiple_files() { |
162 | check_goto( | 181 | check( |
163 | " | 182 | r#" |
164 | //- /lib.rs | 183 | //- /lib.rs |
165 | trait T<|> {}; | 184 | trait T<|> {}; |
166 | struct Foo; | 185 | struct Foo; |
167 | mod a; | 186 | mod a; |
168 | mod b; | 187 | mod b; |
169 | //- /a.rs | 188 | //- /a.rs |
170 | impl crate::T for crate::Foo {} | 189 | impl crate::T for crate::Foo {} |
171 | //- /b.rs | 190 | //^^^^^^^^^^ |
172 | impl crate::T for crate::Foo {} | 191 | //- /b.rs |
173 | ", | 192 | impl crate::T for crate::Foo {} |
174 | &["impl IMPL_DEF FileId(2) 0..31", "impl IMPL_DEF FileId(3) 0..31"], | 193 | //^^^^^^^^^^ |
194 | "#, | ||
175 | ); | 195 | ); |
176 | } | 196 | } |
177 | 197 | ||
178 | #[test] | 198 | #[test] |
179 | fn goto_implementation_all_impls() { | 199 | fn goto_implementation_all_impls() { |
180 | check_goto( | 200 | check( |
181 | " | 201 | r#" |
182 | //- /lib.rs | 202 | //- /lib.rs |
183 | trait T {} | 203 | trait T {} |
184 | struct Foo<|>; | 204 | struct Foo<|>; |
185 | impl Foo {} | 205 | impl Foo {} |
186 | impl T for Foo {} | 206 | //^^^ |
187 | impl T for &Foo {} | 207 | impl T for Foo {} |
188 | ", | 208 | //^^^ |
189 | &[ | 209 | impl T for &Foo {} |
190 | "impl IMPL_DEF FileId(1) 23..34", | 210 | //^^^^ |
191 | "impl IMPL_DEF FileId(1) 35..52", | 211 | "#, |
192 | "impl IMPL_DEF FileId(1) 53..71", | ||
193 | ], | ||
194 | ); | 212 | ); |
195 | } | 213 | } |
196 | 214 | ||
197 | #[test] | 215 | #[test] |
198 | fn goto_implementation_to_builtin_derive() { | 216 | fn goto_implementation_to_builtin_derive() { |
199 | check_goto( | 217 | check( |
200 | " | 218 | r#" |
201 | //- /lib.rs | 219 | #[derive(Copy)] |
202 | #[derive(Copy)] | 220 | //^^^^^^^^^^^^^^^ |
203 | struct Foo<|>; | 221 | struct Foo<|>; |
204 | ", | 222 | |
205 | &["impl IMPL_DEF FileId(1) 0..15"], | 223 | mod marker { |
224 | trait Copy {} | ||
225 | } | ||
226 | "#, | ||
206 | ); | 227 | ); |
207 | } | 228 | } |
208 | } | 229 | } |
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index 91a3097fb..7eb40d637 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs | |||
@@ -55,8 +55,8 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
55 | mod tests { | 55 | mod tests { |
56 | use crate::mock_analysis::analysis_and_position; | 56 | use crate::mock_analysis::analysis_and_position; |
57 | 57 | ||
58 | fn check_goto(fixture: &str, expected: &str) { | 58 | fn check_goto(ra_fixture: &str, expected: &str) { |
59 | let (analysis, pos) = analysis_and_position(fixture); | 59 | let (analysis, pos) = analysis_and_position(ra_fixture); |
60 | 60 | ||
61 | let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info; | 61 | let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info; |
62 | assert_eq!(navs.len(), 1); | 62 | assert_eq!(navs.len(), 1); |
@@ -67,7 +67,7 @@ mod tests { | |||
67 | #[test] | 67 | #[test] |
68 | fn goto_type_definition_works_simple() { | 68 | fn goto_type_definition_works_simple() { |
69 | check_goto( | 69 | check_goto( |
70 | " | 70 | r" |
71 | //- /lib.rs | 71 | //- /lib.rs |
72 | struct Foo; | 72 | struct Foo; |
73 | fn foo() { | 73 | fn foo() { |
@@ -82,7 +82,7 @@ mod tests { | |||
82 | #[test] | 82 | #[test] |
83 | fn goto_type_definition_works_simple_ref() { | 83 | fn goto_type_definition_works_simple_ref() { |
84 | check_goto( | 84 | check_goto( |
85 | " | 85 | r" |
86 | //- /lib.rs | 86 | //- /lib.rs |
87 | struct Foo; | 87 | struct Foo; |
88 | fn foo() { | 88 | fn foo() { |
@@ -97,7 +97,7 @@ mod tests { | |||
97 | #[test] | 97 | #[test] |
98 | fn goto_type_definition_works_through_macro() { | 98 | fn goto_type_definition_works_through_macro() { |
99 | check_goto( | 99 | check_goto( |
100 | " | 100 | r" |
101 | //- /lib.rs | 101 | //- /lib.rs |
102 | macro_rules! id { | 102 | macro_rules! id { |
103 | ($($tt:tt)*) => { $($tt)* } | 103 | ($($tt:tt)*) => { $($tt)* } |
@@ -116,7 +116,7 @@ mod tests { | |||
116 | #[test] | 116 | #[test] |
117 | fn goto_type_definition_for_param() { | 117 | fn goto_type_definition_for_param() { |
118 | check_goto( | 118 | check_goto( |
119 | " | 119 | r" |
120 | //- /lib.rs | 120 | //- /lib.rs |
121 | struct Foo; | 121 | struct Foo; |
122 | fn foo(<|>f: Foo) {} | 122 | fn foo(<|>f: Foo) {} |
@@ -128,7 +128,7 @@ mod tests { | |||
128 | #[test] | 128 | #[test] |
129 | fn goto_type_definition_for_tuple_field() { | 129 | fn goto_type_definition_for_tuple_field() { |
130 | check_goto( | 130 | check_goto( |
131 | " | 131 | r" |
132 | //- /lib.rs | 132 | //- /lib.rs |
133 | struct Foo; | 133 | struct Foo; |
134 | struct Bar(Foo); | 134 | struct Bar(Foo); |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index c3e36a387..eaba2b61e 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -417,8 +417,8 @@ mod tests { | |||
417 | assert_eq!(offset, position.into()); | 417 | assert_eq!(offset, position.into()); |
418 | } | 418 | } |
419 | 419 | ||
420 | fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) { | 420 | fn check_hover_result(ra_fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) { |
421 | let (analysis, position) = analysis_and_position(fixture); | 421 | let (analysis, position) = analysis_and_position(ra_fixture); |
422 | let hover = analysis.hover(position).unwrap().unwrap(); | 422 | let hover = analysis.hover(position).unwrap().unwrap(); |
423 | let mut results = Vec::from(hover.info.results()); | 423 | let mut results = Vec::from(hover.info.results()); |
424 | results.sort(); | 424 | results.sort(); |
@@ -435,8 +435,8 @@ mod tests { | |||
435 | (content[hover.range].to_string(), hover.info.actions().to_vec()) | 435 | (content[hover.range].to_string(), hover.info.actions().to_vec()) |
436 | } | 436 | } |
437 | 437 | ||
438 | fn check_hover_no_result(fixture: &str) { | 438 | fn check_hover_no_result(ra_fixture: &str) { |
439 | let (analysis, position) = analysis_and_position(fixture); | 439 | let (analysis, position) = analysis_and_position(ra_fixture); |
440 | assert!(analysis.hover(position).unwrap().is_none()); | 440 | assert!(analysis.hover(position).unwrap().is_none()); |
441 | } | 441 | } |
442 | 442 | ||
@@ -923,7 +923,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
923 | #[test] | 923 | #[test] |
924 | fn test_hover_through_macro() { | 924 | fn test_hover_through_macro() { |
925 | let (hover_on, _) = check_hover_result( | 925 | let (hover_on, _) = check_hover_result( |
926 | " | 926 | r" |
927 | //- /lib.rs | 927 | //- /lib.rs |
928 | macro_rules! id { | 928 | macro_rules! id { |
929 | ($($tt:tt)*) => { $($tt)* } | 929 | ($($tt:tt)*) => { $($tt)* } |
@@ -944,7 +944,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
944 | #[test] | 944 | #[test] |
945 | fn test_hover_through_expr_in_macro() { | 945 | fn test_hover_through_expr_in_macro() { |
946 | let (hover_on, _) = check_hover_result( | 946 | let (hover_on, _) = check_hover_result( |
947 | " | 947 | r" |
948 | //- /lib.rs | 948 | //- /lib.rs |
949 | macro_rules! id { | 949 | macro_rules! id { |
950 | ($($tt:tt)*) => { $($tt)* } | 950 | ($($tt:tt)*) => { $($tt)* } |
@@ -962,7 +962,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
962 | #[test] | 962 | #[test] |
963 | fn test_hover_through_expr_in_macro_recursive() { | 963 | fn test_hover_through_expr_in_macro_recursive() { |
964 | let (hover_on, _) = check_hover_result( | 964 | let (hover_on, _) = check_hover_result( |
965 | " | 965 | r" |
966 | //- /lib.rs | 966 | //- /lib.rs |
967 | macro_rules! id_deep { | 967 | macro_rules! id_deep { |
968 | ($($tt:tt)*) => { $($tt)* } | 968 | ($($tt:tt)*) => { $($tt)* } |
@@ -983,7 +983,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
983 | #[test] | 983 | #[test] |
984 | fn test_hover_through_func_in_macro_recursive() { | 984 | fn test_hover_through_func_in_macro_recursive() { |
985 | let (hover_on, _) = check_hover_result( | 985 | let (hover_on, _) = check_hover_result( |
986 | " | 986 | r" |
987 | //- /lib.rs | 987 | //- /lib.rs |
988 | macro_rules! id_deep { | 988 | macro_rules! id_deep { |
989 | ($($tt:tt)*) => { $($tt)* } | 989 | ($($tt:tt)*) => { $($tt)* } |
@@ -1026,7 +1026,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1026 | #[test] | 1026 | #[test] |
1027 | fn test_hover_through_assert_macro() { | 1027 | fn test_hover_through_assert_macro() { |
1028 | let (hover_on, _) = check_hover_result( | 1028 | let (hover_on, _) = check_hover_result( |
1029 | r#" | 1029 | r" |
1030 | //- /lib.rs | 1030 | //- /lib.rs |
1031 | #[rustc_builtin_macro] | 1031 | #[rustc_builtin_macro] |
1032 | macro_rules! assert {} | 1032 | macro_rules! assert {} |
@@ -1035,7 +1035,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1035 | fn foo() { | 1035 | fn foo() { |
1036 | assert!(ba<|>r()); | 1036 | assert!(ba<|>r()); |
1037 | } | 1037 | } |
1038 | "#, | 1038 | ", |
1039 | &["fn bar() -> bool"], | 1039 | &["fn bar() -> bool"], |
1040 | ); | 1040 | ); |
1041 | 1041 | ||
@@ -1077,14 +1077,14 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1077 | #[test] | 1077 | #[test] |
1078 | fn test_hover_function_show_qualifiers() { | 1078 | fn test_hover_function_show_qualifiers() { |
1079 | check_hover_result( | 1079 | check_hover_result( |
1080 | " | 1080 | r" |
1081 | //- /lib.rs | 1081 | //- /lib.rs |
1082 | async fn foo<|>() {} | 1082 | async fn foo<|>() {} |
1083 | ", | 1083 | ", |
1084 | &["async fn foo()"], | 1084 | &["async fn foo()"], |
1085 | ); | 1085 | ); |
1086 | check_hover_result( | 1086 | check_hover_result( |
1087 | " | 1087 | r" |
1088 | //- /lib.rs | 1088 | //- /lib.rs |
1089 | pub const unsafe fn foo<|>() {} | 1089 | pub const unsafe fn foo<|>() {} |
1090 | ", | 1090 | ", |
@@ -1102,7 +1102,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1102 | #[test] | 1102 | #[test] |
1103 | fn test_hover_trait_show_qualifiers() { | 1103 | fn test_hover_trait_show_qualifiers() { |
1104 | let (_, actions) = check_hover_result( | 1104 | let (_, actions) = check_hover_result( |
1105 | " | 1105 | r" |
1106 | //- /lib.rs | 1106 | //- /lib.rs |
1107 | unsafe trait foo<|>() {} | 1107 | unsafe trait foo<|>() {} |
1108 | ", | 1108 | ", |
@@ -1114,7 +1114,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1114 | #[test] | 1114 | #[test] |
1115 | fn test_hover_mod_with_same_name_as_function() { | 1115 | fn test_hover_mod_with_same_name_as_function() { |
1116 | check_hover_result( | 1116 | check_hover_result( |
1117 | " | 1117 | r" |
1118 | //- /lib.rs | 1118 | //- /lib.rs |
1119 | use self::m<|>y::Bar; | 1119 | use self::m<|>y::Bar; |
1120 | 1120 | ||
@@ -1237,7 +1237,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1237 | #[test] | 1237 | #[test] |
1238 | fn test_hover_trait_has_impl_action() { | 1238 | fn test_hover_trait_has_impl_action() { |
1239 | let (_, actions) = check_hover_result( | 1239 | let (_, actions) = check_hover_result( |
1240 | " | 1240 | r" |
1241 | //- /lib.rs | 1241 | //- /lib.rs |
1242 | trait foo<|>() {} | 1242 | trait foo<|>() {} |
1243 | ", | 1243 | ", |
@@ -1249,7 +1249,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1249 | #[test] | 1249 | #[test] |
1250 | fn test_hover_struct_has_impl_action() { | 1250 | fn test_hover_struct_has_impl_action() { |
1251 | let (_, actions) = check_hover_result( | 1251 | let (_, actions) = check_hover_result( |
1252 | " | 1252 | r" |
1253 | //- /lib.rs | 1253 | //- /lib.rs |
1254 | struct foo<|>() {} | 1254 | struct foo<|>() {} |
1255 | ", | 1255 | ", |
@@ -1261,7 +1261,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1261 | #[test] | 1261 | #[test] |
1262 | fn test_hover_union_has_impl_action() { | 1262 | fn test_hover_union_has_impl_action() { |
1263 | let (_, actions) = check_hover_result( | 1263 | let (_, actions) = check_hover_result( |
1264 | " | 1264 | r" |
1265 | //- /lib.rs | 1265 | //- /lib.rs |
1266 | union foo<|>() {} | 1266 | union foo<|>() {} |
1267 | ", | 1267 | ", |
@@ -1273,7 +1273,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1273 | #[test] | 1273 | #[test] |
1274 | fn test_hover_enum_has_impl_action() { | 1274 | fn test_hover_enum_has_impl_action() { |
1275 | let (_, actions) = check_hover_result( | 1275 | let (_, actions) = check_hover_result( |
1276 | " | 1276 | r" |
1277 | //- /lib.rs | 1277 | //- /lib.rs |
1278 | enum foo<|>() { | 1278 | enum foo<|>() { |
1279 | A, | 1279 | A, |
@@ -1288,7 +1288,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1288 | #[test] | 1288 | #[test] |
1289 | fn test_hover_test_has_action() { | 1289 | fn test_hover_test_has_action() { |
1290 | let (_, actions) = check_hover_result( | 1290 | let (_, actions) = check_hover_result( |
1291 | " | 1291 | r" |
1292 | //- /lib.rs | 1292 | //- /lib.rs |
1293 | #[test] | 1293 | #[test] |
1294 | fn foo_<|>test() {} | 1294 | fn foo_<|>test() {} |
@@ -1332,7 +1332,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1332 | #[test] | 1332 | #[test] |
1333 | fn test_hover_test_mod_has_action() { | 1333 | fn test_hover_test_mod_has_action() { |
1334 | let (_, actions) = check_hover_result( | 1334 | let (_, actions) = check_hover_result( |
1335 | " | 1335 | r" |
1336 | //- /lib.rs | 1336 | //- /lib.rs |
1337 | mod tests<|> { | 1337 | mod tests<|> { |
1338 | #[test] | 1338 | #[test] |
@@ -1373,7 +1373,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1373 | #[test] | 1373 | #[test] |
1374 | fn test_hover_struct_has_goto_type_action() { | 1374 | fn test_hover_struct_has_goto_type_action() { |
1375 | let (_, actions) = check_hover_result( | 1375 | let (_, actions) = check_hover_result( |
1376 | " | 1376 | r" |
1377 | //- /main.rs | 1377 | //- /main.rs |
1378 | struct S{ f1: u32 } | 1378 | struct S{ f1: u32 } |
1379 | 1379 | ||
@@ -1416,7 +1416,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1416 | #[test] | 1416 | #[test] |
1417 | fn test_hover_generic_struct_has_goto_type_actions() { | 1417 | fn test_hover_generic_struct_has_goto_type_actions() { |
1418 | let (_, actions) = check_hover_result( | 1418 | let (_, actions) = check_hover_result( |
1419 | " | 1419 | r" |
1420 | //- /main.rs | 1420 | //- /main.rs |
1421 | struct Arg(u32); | 1421 | struct Arg(u32); |
1422 | struct S<T>{ f1: T } | 1422 | struct S<T>{ f1: T } |
@@ -1479,7 +1479,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1479 | #[test] | 1479 | #[test] |
1480 | fn test_hover_generic_struct_has_flattened_goto_type_actions() { | 1480 | fn test_hover_generic_struct_has_flattened_goto_type_actions() { |
1481 | let (_, actions) = check_hover_result( | 1481 | let (_, actions) = check_hover_result( |
1482 | " | 1482 | r" |
1483 | //- /main.rs | 1483 | //- /main.rs |
1484 | struct Arg(u32); | 1484 | struct Arg(u32); |
1485 | struct S<T>{ f1: T } | 1485 | struct S<T>{ f1: T } |
@@ -1542,7 +1542,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1542 | #[test] | 1542 | #[test] |
1543 | fn test_hover_tuple_has_goto_type_actions() { | 1543 | fn test_hover_tuple_has_goto_type_actions() { |
1544 | let (_, actions) = check_hover_result( | 1544 | let (_, actions) = check_hover_result( |
1545 | " | 1545 | r" |
1546 | //- /main.rs | 1546 | //- /main.rs |
1547 | struct A(u32); | 1547 | struct A(u32); |
1548 | struct B(u32); | 1548 | struct B(u32); |
@@ -1627,7 +1627,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1627 | #[test] | 1627 | #[test] |
1628 | fn test_hover_return_impl_trait_has_goto_type_action() { | 1628 | fn test_hover_return_impl_trait_has_goto_type_action() { |
1629 | let (_, actions) = check_hover_result( | 1629 | let (_, actions) = check_hover_result( |
1630 | " | 1630 | r" |
1631 | //- /main.rs | 1631 | //- /main.rs |
1632 | trait Foo {} | 1632 | trait Foo {} |
1633 | 1633 | ||
@@ -1672,7 +1672,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1672 | #[test] | 1672 | #[test] |
1673 | fn test_hover_generic_return_impl_trait_has_goto_type_action() { | 1673 | fn test_hover_generic_return_impl_trait_has_goto_type_action() { |
1674 | let (_, actions) = check_hover_result( | 1674 | let (_, actions) = check_hover_result( |
1675 | " | 1675 | r" |
1676 | //- /main.rs | 1676 | //- /main.rs |
1677 | trait Foo<T> {} | 1677 | trait Foo<T> {} |
1678 | struct S; | 1678 | struct S; |
@@ -1737,7 +1737,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1737 | #[test] | 1737 | #[test] |
1738 | fn test_hover_return_impl_traits_has_goto_type_action() { | 1738 | fn test_hover_return_impl_traits_has_goto_type_action() { |
1739 | let (_, actions) = check_hover_result( | 1739 | let (_, actions) = check_hover_result( |
1740 | " | 1740 | r" |
1741 | //- /main.rs | 1741 | //- /main.rs |
1742 | trait Foo {} | 1742 | trait Foo {} |
1743 | trait Bar {} | 1743 | trait Bar {} |
@@ -1802,7 +1802,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1802 | #[test] | 1802 | #[test] |
1803 | fn test_hover_generic_return_impl_traits_has_goto_type_action() { | 1803 | fn test_hover_generic_return_impl_traits_has_goto_type_action() { |
1804 | let (_, actions) = check_hover_result( | 1804 | let (_, actions) = check_hover_result( |
1805 | " | 1805 | r" |
1806 | //- /main.rs | 1806 | //- /main.rs |
1807 | trait Foo<T> {} | 1807 | trait Foo<T> {} |
1808 | trait Bar<T> {} | 1808 | trait Bar<T> {} |
@@ -1907,7 +1907,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1907 | #[test] | 1907 | #[test] |
1908 | fn test_hover_arg_impl_trait_has_goto_type_action() { | 1908 | fn test_hover_arg_impl_trait_has_goto_type_action() { |
1909 | let (_, actions) = check_hover_result( | 1909 | let (_, actions) = check_hover_result( |
1910 | " | 1910 | r" |
1911 | //- /lib.rs | 1911 | //- /lib.rs |
1912 | trait Foo {} | 1912 | trait Foo {} |
1913 | fn foo(ar<|>g: &impl Foo) {} | 1913 | fn foo(ar<|>g: &impl Foo) {} |
@@ -1947,7 +1947,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1947 | #[test] | 1947 | #[test] |
1948 | fn test_hover_arg_impl_traits_has_goto_type_action() { | 1948 | fn test_hover_arg_impl_traits_has_goto_type_action() { |
1949 | let (_, actions) = check_hover_result( | 1949 | let (_, actions) = check_hover_result( |
1950 | " | 1950 | r" |
1951 | //- /lib.rs | 1951 | //- /lib.rs |
1952 | trait Foo {} | 1952 | trait Foo {} |
1953 | trait Bar<T> {} | 1953 | trait Bar<T> {} |
@@ -2028,7 +2028,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
2028 | #[test] | 2028 | #[test] |
2029 | fn test_hover_arg_generic_impl_trait_has_goto_type_action() { | 2029 | fn test_hover_arg_generic_impl_trait_has_goto_type_action() { |
2030 | let (_, actions) = check_hover_result( | 2030 | let (_, actions) = check_hover_result( |
2031 | " | 2031 | r" |
2032 | //- /lib.rs | 2032 | //- /lib.rs |
2033 | trait Foo<T> {} | 2033 | trait Foo<T> {} |
2034 | struct S {} | 2034 | struct S {} |
@@ -2088,7 +2088,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
2088 | #[test] | 2088 | #[test] |
2089 | fn test_hover_dyn_return_has_goto_type_action() { | 2089 | fn test_hover_dyn_return_has_goto_type_action() { |
2090 | let (_, actions) = check_hover_result( | 2090 | let (_, actions) = check_hover_result( |
2091 | " | 2091 | r" |
2092 | //- /main.rs | 2092 | //- /main.rs |
2093 | trait Foo {} | 2093 | trait Foo {} |
2094 | struct S; | 2094 | struct S; |
@@ -2156,7 +2156,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
2156 | #[test] | 2156 | #[test] |
2157 | fn test_hover_dyn_arg_has_goto_type_action() { | 2157 | fn test_hover_dyn_arg_has_goto_type_action() { |
2158 | let (_, actions) = check_hover_result( | 2158 | let (_, actions) = check_hover_result( |
2159 | " | 2159 | r" |
2160 | //- /lib.rs | 2160 | //- /lib.rs |
2161 | trait Foo {} | 2161 | trait Foo {} |
2162 | fn foo(ar<|>g: &dyn Foo) {} | 2162 | fn foo(ar<|>g: &dyn Foo) {} |
@@ -2196,7 +2196,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
2196 | #[test] | 2196 | #[test] |
2197 | fn test_hover_generic_dyn_arg_has_goto_type_action() { | 2197 | fn test_hover_generic_dyn_arg_has_goto_type_action() { |
2198 | let (_, actions) = check_hover_result( | 2198 | let (_, actions) = check_hover_result( |
2199 | " | 2199 | r" |
2200 | //- /lib.rs | 2200 | //- /lib.rs |
2201 | trait Foo<T> {} | 2201 | trait Foo<T> {} |
2202 | struct S {} | 2202 | struct S {} |
@@ -2256,7 +2256,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
2256 | #[test] | 2256 | #[test] |
2257 | fn test_hover_goto_type_action_links_order() { | 2257 | fn test_hover_goto_type_action_links_order() { |
2258 | let (_, actions) = check_hover_result( | 2258 | let (_, actions) = check_hover_result( |
2259 | " | 2259 | r" |
2260 | //- /lib.rs | 2260 | //- /lib.rs |
2261 | trait ImplTrait<T> {} | 2261 | trait ImplTrait<T> {} |
2262 | trait DynTrait<T> {} | 2262 | trait DynTrait<T> {} |
@@ -2357,7 +2357,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
2357 | #[test] | 2357 | #[test] |
2358 | fn test_hover_associated_type_has_goto_type_action() { | 2358 | fn test_hover_associated_type_has_goto_type_action() { |
2359 | let (_, actions) = check_hover_result( | 2359 | let (_, actions) = check_hover_result( |
2360 | " | 2360 | r" |
2361 | //- /main.rs | 2361 | //- /main.rs |
2362 | trait Foo { | 2362 | trait Foo { |
2363 | type Item; | 2363 | type Item; |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index c87652555..62d364bfa 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -3,7 +3,7 @@ use ra_ide_db::RootDatabase; | |||
3 | use ra_prof::profile; | 3 | use ra_prof::profile; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, | 5 | ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, |
6 | match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, | 6 | match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | use crate::{FileId, FunctionSignature}; | 9 | use crate::{FileId, FunctionSignature}; |
@@ -112,7 +112,7 @@ fn get_chaining_hints( | |||
112 | // Ignoring extra whitespace and comments | 112 | // Ignoring extra whitespace and comments |
113 | let next = tokens.next()?.kind(); | 113 | let next = tokens.next()?.kind(); |
114 | let next_next = tokens.next()?.kind(); | 114 | let next_next = tokens.next()?.kind(); |
115 | if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { | 115 | if next == SyntaxKind::WHITESPACE && next_next == T![.] { |
116 | let ty = sema.type_of_expr(&expr)?; | 116 | let ty = sema.type_of_expr(&expr)?; |
117 | if ty.is_unknown() { | 117 | if ty.is_unknown() { |
118 | return None; | 118 | return None; |
@@ -345,583 +345,251 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option< | |||
345 | 345 | ||
346 | #[cfg(test)] | 346 | #[cfg(test)] |
347 | mod tests { | 347 | mod tests { |
348 | use crate::inlay_hints::InlayHintsConfig; | 348 | use expect::{expect, Expect}; |
349 | use insta::assert_debug_snapshot; | 349 | use test_utils::extract_annotations; |
350 | 350 | ||
351 | use crate::mock_analysis::single_file; | 351 | use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file}; |
352 | |||
353 | fn check(ra_fixture: &str) { | ||
354 | check_with_config(ra_fixture, InlayHintsConfig::default()); | ||
355 | } | ||
356 | |||
357 | fn check_with_config(ra_fixture: &str, config: InlayHintsConfig) { | ||
358 | let (analysis, file_id) = single_file(ra_fixture); | ||
359 | let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); | ||
360 | let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); | ||
361 | let actual = | ||
362 | inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>(); | ||
363 | assert_eq!(expected, actual); | ||
364 | } | ||
365 | |||
366 | fn check_expect(ra_fixture: &str, config: InlayHintsConfig, expect: Expect) { | ||
367 | let (analysis, file_id) = single_file(ra_fixture); | ||
368 | let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); | ||
369 | expect.assert_debug_eq(&inlay_hints) | ||
370 | } | ||
352 | 371 | ||
353 | #[test] | 372 | #[test] |
354 | fn param_hints_only() { | 373 | fn param_hints_only() { |
355 | let (analysis, file_id) = single_file( | 374 | check_with_config( |
356 | r#" | 375 | r#" |
357 | fn foo(a: i32, b: i32) -> i32 { a + b } | 376 | fn foo(a: i32, b: i32) -> i32 { a + b } |
358 | fn main() { | 377 | fn main() { |
359 | let _x = foo(4, 4); | 378 | let _x = foo( |
360 | }"#, | 379 | 4, |
361 | ); | 380 | //^ a |
362 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" | 381 | 4, |
363 | [ | 382 | //^ b |
364 | InlayHint { | 383 | ); |
365 | range: 69..70, | 384 | }"#, |
366 | kind: ParameterHint, | 385 | InlayHintsConfig { |
367 | label: "a", | 386 | parameter_hints: true, |
368 | }, | 387 | type_hints: false, |
369 | InlayHint { | 388 | chaining_hints: false, |
370 | range: 72..73, | 389 | max_length: None, |
371 | kind: ParameterHint, | ||
372 | label: "b", | ||
373 | }, | 390 | }, |
374 | ] | 391 | ); |
375 | "###); | ||
376 | } | 392 | } |
377 | 393 | ||
378 | #[test] | 394 | #[test] |
379 | fn hints_disabled() { | 395 | fn hints_disabled() { |
380 | let (analysis, file_id) = single_file( | 396 | check_with_config( |
381 | r#" | 397 | r#" |
382 | fn foo(a: i32, b: i32) -> i32 { a + b } | 398 | fn foo(a: i32, b: i32) -> i32 { a + b } |
383 | fn main() { | 399 | fn main() { |
384 | let _x = foo(4, 4); | 400 | let _x = foo(4, 4); |
385 | }"#, | 401 | }"#, |
402 | InlayHintsConfig { | ||
403 | type_hints: false, | ||
404 | parameter_hints: false, | ||
405 | chaining_hints: false, | ||
406 | max_length: None, | ||
407 | }, | ||
386 | ); | 408 | ); |
387 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###); | ||
388 | } | 409 | } |
389 | 410 | ||
390 | #[test] | 411 | #[test] |
391 | fn type_hints_only() { | 412 | fn type_hints_only() { |
392 | let (analysis, file_id) = single_file( | 413 | check_with_config( |
393 | r#" | 414 | r#" |
394 | fn foo(a: i32, b: i32) -> i32 { a + b } | 415 | fn foo(a: i32, b: i32) -> i32 { a + b } |
395 | fn main() { | 416 | fn main() { |
396 | let _x = foo(4, 4); | 417 | let _x = foo(4, 4); |
397 | }"#, | 418 | //^^ i32 |
398 | ); | 419 | }"#, |
399 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" | 420 | InlayHintsConfig { |
400 | [ | 421 | type_hints: true, |
401 | InlayHint { | 422 | parameter_hints: false, |
402 | range: 60..62, | 423 | chaining_hints: false, |
403 | kind: TypeHint, | 424 | max_length: None, |
404 | label: "i32", | ||
405 | }, | 425 | }, |
406 | ] | 426 | ); |
407 | "###); | ||
408 | } | 427 | } |
428 | |||
409 | #[test] | 429 | #[test] |
410 | fn default_generic_types_should_not_be_displayed() { | 430 | fn default_generic_types_should_not_be_displayed() { |
411 | let (analysis, file_id) = single_file( | 431 | check( |
412 | r#" | 432 | r#" |
413 | struct Test<K, T = u8> { | 433 | struct Test<K, T = u8> { k: K, t: T } |
414 | k: K, | ||
415 | t: T, | ||
416 | } | ||
417 | 434 | ||
418 | fn main() { | 435 | fn main() { |
419 | let zz = Test { t: 23u8, k: 33 }; | 436 | let zz = Test { t: 23u8, k: 33 }; |
437 | //^^ Test<i32> | ||
420 | let zz_ref = &zz; | 438 | let zz_ref = &zz; |
439 | //^^^^^^ &Test<i32> | ||
421 | }"#, | 440 | }"#, |
422 | ); | 441 | ); |
423 | |||
424 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" | ||
425 | [ | ||
426 | InlayHint { | ||
427 | range: 68..70, | ||
428 | kind: TypeHint, | ||
429 | label: "Test<i32>", | ||
430 | }, | ||
431 | InlayHint { | ||
432 | range: 106..112, | ||
433 | kind: TypeHint, | ||
434 | label: "&Test<i32>", | ||
435 | }, | ||
436 | ] | ||
437 | "### | ||
438 | ); | ||
439 | } | 442 | } |
440 | 443 | ||
441 | #[test] | 444 | #[test] |
442 | fn let_statement() { | 445 | fn let_statement() { |
443 | let (analysis, file_id) = single_file( | 446 | check( |
444 | r#" | 447 | r#" |
445 | #[derive(PartialEq)] | 448 | #[derive(PartialEq)] |
446 | enum CustomOption<T> { | 449 | enum Option<T> { None, Some(T) } |
447 | None, | ||
448 | Some(T), | ||
449 | } | ||
450 | 450 | ||
451 | #[derive(PartialEq)] | 451 | #[derive(PartialEq)] |
452 | struct Test { | 452 | struct Test { a: Option<u32>, b: u8 } |
453 | a: CustomOption<u32>, | ||
454 | b: u8, | ||
455 | } | ||
456 | 453 | ||
457 | fn main() { | 454 | fn main() { |
458 | struct InnerStruct {} | 455 | struct InnerStruct {} |
459 | 456 | ||
460 | let test = 54; | 457 | let test = 54; |
458 | //^^^^ i32 | ||
461 | let test: i32 = 33; | 459 | let test: i32 = 33; |
462 | let mut test = 33; | 460 | let mut test = 33; |
461 | //^^^^^^^^ i32 | ||
463 | let _ = 22; | 462 | let _ = 22; |
464 | let test = "test"; | 463 | let test = "test"; |
464 | //^^^^ &str | ||
465 | let test = InnerStruct {}; | 465 | let test = InnerStruct {}; |
466 | 466 | ||
467 | let test = vec![222]; | 467 | let test = unresolved(); |
468 | let test: Vec<_> = (0..3).collect(); | ||
469 | let test = (0..3).collect::<Vec<i128>>(); | ||
470 | let test = (0..3).collect::<Vec<_>>(); | ||
471 | |||
472 | let mut test = Vec::new(); | ||
473 | test.push(333); | ||
474 | 468 | ||
475 | let test = (42, 'a'); | 469 | let test = (42, 'a'); |
476 | let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); | 470 | //^^^^ (i32, char) |
471 | let (a, (b, (c,)) = (2, (3, (9.2,)); | ||
472 | //^ i32 ^ i32 ^ f64 | ||
477 | let &x = &92; | 473 | let &x = &92; |
474 | //^ i32 | ||
478 | }"#, | 475 | }"#, |
479 | ); | 476 | ); |
480 | |||
481 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" | ||
482 | [ | ||
483 | InlayHint { | ||
484 | range: 192..196, | ||
485 | kind: TypeHint, | ||
486 | label: "i32", | ||
487 | }, | ||
488 | InlayHint { | ||
489 | range: 235..243, | ||
490 | kind: TypeHint, | ||
491 | label: "i32", | ||
492 | }, | ||
493 | InlayHint { | ||
494 | range: 274..278, | ||
495 | kind: TypeHint, | ||
496 | label: "&str", | ||
497 | }, | ||
498 | InlayHint { | ||
499 | range: 538..542, | ||
500 | kind: TypeHint, | ||
501 | label: "(i32, char)", | ||
502 | }, | ||
503 | InlayHint { | ||
504 | range: 565..566, | ||
505 | kind: TypeHint, | ||
506 | label: "i32", | ||
507 | }, | ||
508 | InlayHint { | ||
509 | range: 569..570, | ||
510 | kind: TypeHint, | ||
511 | label: "i32", | ||
512 | }, | ||
513 | InlayHint { | ||
514 | range: 572..573, | ||
515 | kind: TypeHint, | ||
516 | label: "i32", | ||
517 | }, | ||
518 | InlayHint { | ||
519 | range: 576..577, | ||
520 | kind: TypeHint, | ||
521 | label: "f64", | ||
522 | }, | ||
523 | InlayHint { | ||
524 | range: 579..580, | ||
525 | kind: TypeHint, | ||
526 | label: "f64", | ||
527 | }, | ||
528 | InlayHint { | ||
529 | range: 583..584, | ||
530 | kind: TypeHint, | ||
531 | label: "i32", | ||
532 | }, | ||
533 | InlayHint { | ||
534 | range: 626..627, | ||
535 | kind: TypeHint, | ||
536 | label: "i32", | ||
537 | }, | ||
538 | ] | ||
539 | "### | ||
540 | ); | ||
541 | } | 477 | } |
542 | 478 | ||
543 | #[test] | 479 | #[test] |
544 | fn closure_parameters() { | 480 | fn closure_parameters() { |
545 | let (analysis, file_id) = single_file( | 481 | check( |
546 | r#" | 482 | r#" |
547 | fn main() { | 483 | fn main() { |
548 | let mut start = 0; | 484 | let mut start = 0; |
549 | (0..2).for_each(|increment| { | 485 | //^^^^^^^^^ i32 |
550 | start += increment; | 486 | (0..2).for_each(|increment| { start += increment; }); |
551 | }); | 487 | //^^^^^^^^^ i32 |
488 | |||
489 | let multiply = | ||
490 | //^^^^^^^^ |…| -> i32 | ||
491 | | a, b| a * b | ||
492 | //^ i32 ^ i32 | ||
493 | ; | ||
552 | 494 | ||
553 | let multiply = |a, b, c, d| a * b * c * d; | 495 | let _: i32 = multiply(1, 2); |
554 | let _: i32 = multiply(1, 2, 3, 4); | ||
555 | let multiply_ref = &multiply; | 496 | let multiply_ref = &multiply; |
497 | //^^^^^^^^^^^^ &|…| -> i32 | ||
556 | 498 | ||
557 | let return_42 = || 42; | 499 | let return_42 = || 42; |
500 | //^^^^^^^^^ || -> i32 | ||
558 | }"#, | 501 | }"#, |
559 | ); | 502 | ); |
560 | |||
561 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" | ||
562 | [ | ||
563 | InlayHint { | ||
564 | range: 20..29, | ||
565 | kind: TypeHint, | ||
566 | label: "i32", | ||
567 | }, | ||
568 | InlayHint { | ||
569 | range: 56..65, | ||
570 | kind: TypeHint, | ||
571 | label: "i32", | ||
572 | }, | ||
573 | InlayHint { | ||
574 | range: 114..122, | ||
575 | kind: TypeHint, | ||
576 | label: "|…| -> i32", | ||
577 | }, | ||
578 | InlayHint { | ||
579 | range: 126..127, | ||
580 | kind: TypeHint, | ||
581 | label: "i32", | ||
582 | }, | ||
583 | InlayHint { | ||
584 | range: 129..130, | ||
585 | kind: TypeHint, | ||
586 | label: "i32", | ||
587 | }, | ||
588 | InlayHint { | ||
589 | range: 132..133, | ||
590 | kind: TypeHint, | ||
591 | label: "i32", | ||
592 | }, | ||
593 | InlayHint { | ||
594 | range: 135..136, | ||
595 | kind: TypeHint, | ||
596 | label: "i32", | ||
597 | }, | ||
598 | InlayHint { | ||
599 | range: 200..212, | ||
600 | kind: TypeHint, | ||
601 | label: "&|…| -> i32", | ||
602 | }, | ||
603 | InlayHint { | ||
604 | range: 235..244, | ||
605 | kind: TypeHint, | ||
606 | label: "|| -> i32", | ||
607 | }, | ||
608 | ] | ||
609 | "### | ||
610 | ); | ||
611 | } | 503 | } |
612 | 504 | ||
613 | #[test] | 505 | #[test] |
614 | fn for_expression() { | 506 | fn for_expression() { |
615 | let (analysis, file_id) = single_file( | 507 | check( |
616 | r#" | 508 | r#" |
617 | fn main() { | 509 | fn main() { |
618 | let mut start = 0; | 510 | let mut start = 0; |
619 | for increment in 0..2 { | 511 | //^^^^^^^^^ i32 |
620 | start += increment; | 512 | for increment in 0..2 { start += increment; } |
621 | } | 513 | //^^^^^^^^^ i32 |
622 | }"#, | 514 | }"#, |
623 | ); | 515 | ); |
624 | |||
625 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" | ||
626 | [ | ||
627 | InlayHint { | ||
628 | range: 20..29, | ||
629 | kind: TypeHint, | ||
630 | label: "i32", | ||
631 | }, | ||
632 | InlayHint { | ||
633 | range: 43..52, | ||
634 | kind: TypeHint, | ||
635 | label: "i32", | ||
636 | }, | ||
637 | ] | ||
638 | "### | ||
639 | ); | ||
640 | } | 516 | } |
641 | 517 | ||
642 | #[test] | 518 | #[test] |
643 | fn if_expr() { | 519 | fn if_expr() { |
644 | let (analysis, file_id) = single_file( | 520 | check( |
645 | r#" | 521 | r#" |
646 | #[derive(PartialEq)] | 522 | enum Option<T> { None, Some(T) } |
647 | enum CustomOption<T> { | 523 | use Option::*; |
648 | None, | ||
649 | Some(T), | ||
650 | } | ||
651 | |||
652 | #[derive(PartialEq)] | ||
653 | struct Test { | ||
654 | a: CustomOption<u32>, | ||
655 | b: u8, | ||
656 | } | ||
657 | 524 | ||
658 | use CustomOption::*; | 525 | struct Test { a: Option<u32>, b: u8 } |
659 | 526 | ||
660 | fn main() { | 527 | fn main() { |
661 | let test = Some(Test { a: Some(3), b: 1 }); | 528 | let test = Some(Test { a: Some(3), b: 1 }); |
529 | //^^^^ Option<Test> | ||
662 | if let None = &test {}; | 530 | if let None = &test {}; |
663 | if let test = &test {}; | 531 | if let test = &test {}; |
532 | //^^^^ &Option<Test> | ||
664 | if let Some(test) = &test {}; | 533 | if let Some(test) = &test {}; |
665 | if let Some(Test { a, b }) = &test {}; | 534 | //^^^^ &Test |
666 | if let Some(Test { a: x, b: y }) = &test {}; | 535 | if let Some(Test { a, b }) = &test {}; |
667 | if let Some(Test { a: Some(x), b: y }) = &test {}; | 536 | //^ &Option<u32> ^ &u8 |
668 | if let Some(Test { a: None, b: y }) = &test {}; | 537 | if let Some(Test { a: x, b: y }) = &test {}; |
538 | //^ &Option<u32> ^ &u8 | ||
539 | if let Some(Test { a: Some(x), b: y }) = &test {}; | ||
540 | //^ &u32 ^ &u8 | ||
541 | if let Some(Test { a: None, b: y }) = &test {}; | ||
542 | //^ &u8 | ||
669 | if let Some(Test { b: y, .. }) = &test {}; | 543 | if let Some(Test { b: y, .. }) = &test {}; |
670 | 544 | //^ &u8 | |
671 | if test == None {} | 545 | if test == None {} |
672 | }"#, | 546 | }"#, |
673 | ); | 547 | ); |
674 | |||
675 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" | ||
676 | [ | ||
677 | InlayHint { | ||
678 | range: 187..191, | ||
679 | kind: TypeHint, | ||
680 | label: "CustomOption<Test>", | ||
681 | }, | ||
682 | InlayHint { | ||
683 | range: 266..270, | ||
684 | kind: TypeHint, | ||
685 | label: "&CustomOption<Test>", | ||
686 | }, | ||
687 | InlayHint { | ||
688 | range: 299..303, | ||
689 | kind: TypeHint, | ||
690 | label: "&Test", | ||
691 | }, | ||
692 | InlayHint { | ||
693 | range: 340..341, | ||
694 | kind: TypeHint, | ||
695 | label: "&CustomOption<u32>", | ||
696 | }, | ||
697 | InlayHint { | ||
698 | range: 343..344, | ||
699 | kind: TypeHint, | ||
700 | label: "&u8", | ||
701 | }, | ||
702 | InlayHint { | ||
703 | range: 386..387, | ||
704 | kind: TypeHint, | ||
705 | label: "&CustomOption<u32>", | ||
706 | }, | ||
707 | InlayHint { | ||
708 | range: 392..393, | ||
709 | kind: TypeHint, | ||
710 | label: "&u8", | ||
711 | }, | ||
712 | InlayHint { | ||
713 | range: 440..441, | ||
714 | kind: TypeHint, | ||
715 | label: "&u32", | ||
716 | }, | ||
717 | InlayHint { | ||
718 | range: 447..448, | ||
719 | kind: TypeHint, | ||
720 | label: "&u8", | ||
721 | }, | ||
722 | InlayHint { | ||
723 | range: 499..500, | ||
724 | kind: TypeHint, | ||
725 | label: "&u8", | ||
726 | }, | ||
727 | InlayHint { | ||
728 | range: 542..543, | ||
729 | kind: TypeHint, | ||
730 | label: "&u8", | ||
731 | }, | ||
732 | ] | ||
733 | "### | ||
734 | ); | ||
735 | } | 548 | } |
736 | 549 | ||
737 | #[test] | 550 | #[test] |
738 | fn while_expr() { | 551 | fn while_expr() { |
739 | let (analysis, file_id) = single_file( | 552 | check( |
740 | r#" | 553 | r#" |
741 | #[derive(PartialEq)] | 554 | enum Option<T> { None, Some(T) } |
742 | enum CustomOption<T> { | 555 | use Option::*; |
743 | None, | ||
744 | Some(T), | ||
745 | } | ||
746 | 556 | ||
747 | #[derive(PartialEq)] | 557 | struct Test { a: Option<u32>, b: u8 } |
748 | struct Test { | ||
749 | a: CustomOption<u32>, | ||
750 | b: u8, | ||
751 | } | ||
752 | |||
753 | use CustomOption::*; | ||
754 | 558 | ||
755 | fn main() { | 559 | fn main() { |
756 | let test = Some(Test { a: Some(3), b: 1 }); | 560 | let test = Some(Test { a: Some(3), b: 1 }); |
757 | while let None = &test {}; | 561 | //^^^^ Option<Test> |
758 | while let test = &test {}; | 562 | while let Some(Test { a: Some(x), b: y }) = &test {}; |
759 | while let Some(test) = &test {}; | 563 | //^ &u32 ^ &u8 |
760 | while let Some(Test { a, b }) = &test {}; | ||
761 | while let Some(Test { a: x, b: y }) = &test {}; | ||
762 | while let Some(Test { a: Some(x), b: y }) = &test {}; | ||
763 | while let Some(Test { a: None, b: y }) = &test {}; | ||
764 | while let Some(Test { b: y, .. }) = &test {}; | ||
765 | |||
766 | while test == None {} | ||
767 | }"#, | 564 | }"#, |
768 | ); | 565 | ); |
769 | |||
770 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" | ||
771 | [ | ||
772 | InlayHint { | ||
773 | range: 187..191, | ||
774 | kind: TypeHint, | ||
775 | label: "CustomOption<Test>", | ||
776 | }, | ||
777 | InlayHint { | ||
778 | range: 272..276, | ||
779 | kind: TypeHint, | ||
780 | label: "&CustomOption<Test>", | ||
781 | }, | ||
782 | InlayHint { | ||
783 | range: 308..312, | ||
784 | kind: TypeHint, | ||
785 | label: "&Test", | ||
786 | }, | ||
787 | InlayHint { | ||
788 | range: 352..353, | ||
789 | kind: TypeHint, | ||
790 | label: "&CustomOption<u32>", | ||
791 | }, | ||
792 | InlayHint { | ||
793 | range: 355..356, | ||
794 | kind: TypeHint, | ||
795 | label: "&u8", | ||
796 | }, | ||
797 | InlayHint { | ||
798 | range: 401..402, | ||
799 | kind: TypeHint, | ||
800 | label: "&CustomOption<u32>", | ||
801 | }, | ||
802 | InlayHint { | ||
803 | range: 407..408, | ||
804 | kind: TypeHint, | ||
805 | label: "&u8", | ||
806 | }, | ||
807 | InlayHint { | ||
808 | range: 458..459, | ||
809 | kind: TypeHint, | ||
810 | label: "&u32", | ||
811 | }, | ||
812 | InlayHint { | ||
813 | range: 465..466, | ||
814 | kind: TypeHint, | ||
815 | label: "&u8", | ||
816 | }, | ||
817 | InlayHint { | ||
818 | range: 520..521, | ||
819 | kind: TypeHint, | ||
820 | label: "&u8", | ||
821 | }, | ||
822 | InlayHint { | ||
823 | range: 566..567, | ||
824 | kind: TypeHint, | ||
825 | label: "&u8", | ||
826 | }, | ||
827 | ] | ||
828 | "### | ||
829 | ); | ||
830 | } | 566 | } |
831 | 567 | ||
832 | #[test] | 568 | #[test] |
833 | fn match_arm_list() { | 569 | fn match_arm_list() { |
834 | let (analysis, file_id) = single_file( | 570 | check( |
835 | r#" | 571 | r#" |
836 | #[derive(PartialEq)] | 572 | enum Option<T> { None, Some(T) } |
837 | enum CustomOption<T> { | 573 | use Option::*; |
838 | None, | ||
839 | Some(T), | ||
840 | } | ||
841 | |||
842 | #[derive(PartialEq)] | ||
843 | struct Test { | ||
844 | a: CustomOption<u32>, | ||
845 | b: u8, | ||
846 | } | ||
847 | 574 | ||
848 | use CustomOption::*; | 575 | struct Test { a: Option<u32>, b: u8 } |
849 | 576 | ||
850 | fn main() { | 577 | fn main() { |
851 | match Some(Test { a: Some(3), b: 1 }) { | 578 | match Some(Test { a: Some(3), b: 1 }) { |
852 | None => (), | 579 | None => (), |
853 | test => (), | 580 | test => (), |
854 | Some(test) => (), | 581 | //^^^^ Option<Test> |
855 | Some(Test { a, b }) => (), | ||
856 | Some(Test { a: x, b: y }) => (), | ||
857 | Some(Test { a: Some(x), b: y }) => (), | 582 | Some(Test { a: Some(x), b: y }) => (), |
858 | Some(Test { a: None, b: y }) => (), | 583 | //^ u32 ^ u8 |
859 | Some(Test { b: y, .. }) => (), | ||
860 | _ => {} | 584 | _ => {} |
861 | } | 585 | } |
862 | }"#, | 586 | }"#, |
863 | ); | 587 | ); |
864 | |||
865 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" | ||
866 | [ | ||
867 | InlayHint { | ||
868 | range: 251..255, | ||
869 | kind: TypeHint, | ||
870 | label: "CustomOption<Test>", | ||
871 | }, | ||
872 | InlayHint { | ||
873 | range: 276..280, | ||
874 | kind: TypeHint, | ||
875 | label: "Test", | ||
876 | }, | ||
877 | InlayHint { | ||
878 | range: 309..310, | ||
879 | kind: TypeHint, | ||
880 | label: "CustomOption<u32>", | ||
881 | }, | ||
882 | InlayHint { | ||
883 | range: 312..313, | ||
884 | kind: TypeHint, | ||
885 | label: "u8", | ||
886 | }, | ||
887 | InlayHint { | ||
888 | range: 347..348, | ||
889 | kind: TypeHint, | ||
890 | label: "CustomOption<u32>", | ||
891 | }, | ||
892 | InlayHint { | ||
893 | range: 353..354, | ||
894 | kind: TypeHint, | ||
895 | label: "u8", | ||
896 | }, | ||
897 | InlayHint { | ||
898 | range: 393..394, | ||
899 | kind: TypeHint, | ||
900 | label: "u32", | ||
901 | }, | ||
902 | InlayHint { | ||
903 | range: 400..401, | ||
904 | kind: TypeHint, | ||
905 | label: "u8", | ||
906 | }, | ||
907 | InlayHint { | ||
908 | range: 444..445, | ||
909 | kind: TypeHint, | ||
910 | label: "u8", | ||
911 | }, | ||
912 | InlayHint { | ||
913 | range: 479..480, | ||
914 | kind: TypeHint, | ||
915 | label: "u8", | ||
916 | }, | ||
917 | ] | ||
918 | "### | ||
919 | ); | ||
920 | } | 588 | } |
921 | 589 | ||
922 | #[test] | 590 | #[test] |
923 | fn hint_truncation() { | 591 | fn hint_truncation() { |
924 | let (analysis, file_id) = single_file( | 592 | check_with_config( |
925 | r#" | 593 | r#" |
926 | struct Smol<T>(T); | 594 | struct Smol<T>(T); |
927 | 595 | ||
@@ -929,52 +597,26 @@ struct VeryLongOuterName<T>(T); | |||
929 | 597 | ||
930 | fn main() { | 598 | fn main() { |
931 | let a = Smol(0u32); | 599 | let a = Smol(0u32); |
600 | //^ Smol<u32> | ||
932 | let b = VeryLongOuterName(0usize); | 601 | let b = VeryLongOuterName(0usize); |
602 | //^ VeryLongOuterName<…> | ||
933 | let c = Smol(Smol(0u32)) | 603 | let c = Smol(Smol(0u32)) |
604 | //^ Smol<Smol<…>> | ||
934 | }"#, | 605 | }"#, |
935 | ); | 606 | InlayHintsConfig { max_length: Some(8), ..Default::default() }, |
936 | |||
937 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###" | ||
938 | [ | ||
939 | InlayHint { | ||
940 | range: 73..74, | ||
941 | kind: TypeHint, | ||
942 | label: "Smol<u32>", | ||
943 | }, | ||
944 | InlayHint { | ||
945 | range: 97..98, | ||
946 | kind: TypeHint, | ||
947 | label: "VeryLongOuterName<…>", | ||
948 | }, | ||
949 | InlayHint { | ||
950 | range: 136..137, | ||
951 | kind: TypeHint, | ||
952 | label: "Smol<Smol<…>>", | ||
953 | }, | ||
954 | ] | ||
955 | "### | ||
956 | ); | 607 | ); |
957 | } | 608 | } |
958 | 609 | ||
959 | #[test] | 610 | #[test] |
960 | fn function_call_parameter_hint() { | 611 | fn function_call_parameter_hint() { |
961 | let (analysis, file_id) = single_file( | 612 | check( |
962 | r#" | 613 | r#" |
963 | enum CustomOption<T> { | 614 | enum Option<T> { None, Some(T) } |
964 | None, | 615 | use Option::*; |
965 | Some(T), | ||
966 | } | ||
967 | use CustomOption::*; | ||
968 | 616 | ||
969 | struct FileId {} | 617 | struct FileId {} |
970 | struct SmolStr {} | 618 | struct SmolStr {} |
971 | 619 | ||
972 | impl From<&str> for SmolStr { | ||
973 | fn from(_: &str) -> Self { | ||
974 | unimplemented!() | ||
975 | } | ||
976 | } | ||
977 | |||
978 | struct TextRange {} | 620 | struct TextRange {} |
979 | struct SyntaxKind {} | 621 | struct SyntaxKind {} |
980 | struct NavigationTarget {} | 622 | struct NavigationTarget {} |
@@ -982,18 +624,15 @@ struct NavigationTarget {} | |||
982 | struct Test {} | 624 | struct Test {} |
983 | 625 | ||
984 | impl Test { | 626 | impl Test { |
985 | fn method(&self, mut param: i32) -> i32 { | 627 | fn method(&self, mut param: i32) -> i32 { param * 2 } |
986 | param * 2 | ||
987 | } | ||
988 | 628 | ||
989 | fn from_syntax( | 629 | fn from_syntax( |
990 | file_id: FileId, | 630 | file_id: FileId, |
991 | name: SmolStr, | 631 | name: SmolStr, |
992 | focus_range: CustomOption<TextRange>, | 632 | focus_range: Option<TextRange>, |
993 | full_range: TextRange, | 633 | full_range: TextRange, |
994 | kind: SyntaxKind, | 634 | kind: SyntaxKind, |
995 | docs: CustomOption<String>, | 635 | docs: Option<String>, |
996 | description: CustomOption<String>, | ||
997 | ) -> NavigationTarget { | 636 | ) -> NavigationTarget { |
998 | NavigationTarget {} | 637 | NavigationTarget {} |
999 | } | 638 | } |
@@ -1005,108 +644,35 @@ fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { | |||
1005 | 644 | ||
1006 | fn main() { | 645 | fn main() { |
1007 | let not_literal = 1; | 646 | let not_literal = 1; |
1008 | let _: i32 = test_func(1, 2, "hello", 3, not_literal); | 647 | //^^^^^^^^^^^ i32 |
648 | let _: i32 = test_func(1, 2, "hello", 3, not_literal); | ||
649 | //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last | ||
1009 | let t: Test = Test {}; | 650 | let t: Test = Test {}; |
1010 | t.method(123); | 651 | t.method(123); |
1011 | Test::method(&t, 3456); | 652 | //^^^ param |
1012 | 653 | Test::method(&t, 3456); | |
654 | //^^ &self ^^^^ param | ||
1013 | Test::from_syntax( | 655 | Test::from_syntax( |
1014 | FileId {}, | 656 | FileId {}, |
657 | //^^^^^^^^^ file_id | ||
1015 | "impl".into(), | 658 | "impl".into(), |
659 | //^^^^^^^^^^^^^ name | ||
1016 | None, | 660 | None, |
661 | //^^^^ focus_range | ||
1017 | TextRange {}, | 662 | TextRange {}, |
663 | //^^^^^^^^^^^^ full_range | ||
1018 | SyntaxKind {}, | 664 | SyntaxKind {}, |
665 | //^^^^^^^^^^^^^ kind | ||
1019 | None, | 666 | None, |
1020 | None, | 667 | //^^^^ docs |
1021 | ); | 668 | ); |
1022 | }"#, | 669 | }"#, |
1023 | ); | 670 | ); |
1024 | |||
1025 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" | ||
1026 | [ | ||
1027 | InlayHint { | ||
1028 | range: 797..808, | ||
1029 | kind: TypeHint, | ||
1030 | label: "i32", | ||
1031 | }, | ||
1032 | InlayHint { | ||
1033 | range: 841..842, | ||
1034 | kind: ParameterHint, | ||
1035 | label: "foo", | ||
1036 | }, | ||
1037 | InlayHint { | ||
1038 | range: 844..845, | ||
1039 | kind: ParameterHint, | ||
1040 | label: "bar", | ||
1041 | }, | ||
1042 | InlayHint { | ||
1043 | range: 847..854, | ||
1044 | kind: ParameterHint, | ||
1045 | label: "msg", | ||
1046 | }, | ||
1047 | InlayHint { | ||
1048 | range: 859..870, | ||
1049 | kind: ParameterHint, | ||
1050 | label: "last", | ||
1051 | }, | ||
1052 | InlayHint { | ||
1053 | range: 913..916, | ||
1054 | kind: ParameterHint, | ||
1055 | label: "param", | ||
1056 | }, | ||
1057 | InlayHint { | ||
1058 | range: 936..938, | ||
1059 | kind: ParameterHint, | ||
1060 | label: "&self", | ||
1061 | }, | ||
1062 | InlayHint { | ||
1063 | range: 940..944, | ||
1064 | kind: ParameterHint, | ||
1065 | label: "param", | ||
1066 | }, | ||
1067 | InlayHint { | ||
1068 | range: 979..988, | ||
1069 | kind: ParameterHint, | ||
1070 | label: "file_id", | ||
1071 | }, | ||
1072 | InlayHint { | ||
1073 | range: 998..1011, | ||
1074 | kind: ParameterHint, | ||
1075 | label: "name", | ||
1076 | }, | ||
1077 | InlayHint { | ||
1078 | range: 1021..1025, | ||
1079 | kind: ParameterHint, | ||
1080 | label: "focus_range", | ||
1081 | }, | ||
1082 | InlayHint { | ||
1083 | range: 1035..1047, | ||
1084 | kind: ParameterHint, | ||
1085 | label: "full_range", | ||
1086 | }, | ||
1087 | InlayHint { | ||
1088 | range: 1057..1070, | ||
1089 | kind: ParameterHint, | ||
1090 | label: "kind", | ||
1091 | }, | ||
1092 | InlayHint { | ||
1093 | range: 1080..1084, | ||
1094 | kind: ParameterHint, | ||
1095 | label: "docs", | ||
1096 | }, | ||
1097 | InlayHint { | ||
1098 | range: 1094..1098, | ||
1099 | kind: ParameterHint, | ||
1100 | label: "description", | ||
1101 | }, | ||
1102 | ] | ||
1103 | "### | ||
1104 | ); | ||
1105 | } | 671 | } |
1106 | 672 | ||
1107 | #[test] | 673 | #[test] |
1108 | fn omitted_parameters_hints_heuristics() { | 674 | fn omitted_parameters_hints_heuristics() { |
1109 | let (analysis, file_id) = single_file( | 675 | check_with_config( |
1110 | r#" | 676 | r#" |
1111 | fn map(f: i32) {} | 677 | fn map(f: i32) {} |
1112 | fn filter(predicate: i32) {} | 678 | fn filter(predicate: i32) {} |
@@ -1187,23 +753,16 @@ fn main() { | |||
1187 | let _: f64 = a.div_euclid(b); | 753 | let _: f64 = a.div_euclid(b); |
1188 | let _: f64 = a.abs_sub(b); | 754 | let _: f64 = a.abs_sub(b); |
1189 | }"#, | 755 | }"#, |
1190 | ); | 756 | InlayHintsConfig { max_length: Some(8), ..Default::default() }, |
1191 | |||
1192 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###" | ||
1193 | [] | ||
1194 | "### | ||
1195 | ); | 757 | ); |
1196 | } | 758 | } |
1197 | 759 | ||
1198 | #[test] | 760 | #[test] |
1199 | fn unit_structs_have_no_type_hints() { | 761 | fn unit_structs_have_no_type_hints() { |
1200 | let (analysis, file_id) = single_file( | 762 | check_with_config( |
1201 | r#" | 763 | r#" |
1202 | enum CustomResult<T, E> { | 764 | enum Result<T, E> { Ok(T), Err(E) } |
1203 | Ok(T), | 765 | use Result::*; |
1204 | Err(E), | ||
1205 | } | ||
1206 | use CustomResult::*; | ||
1207 | 766 | ||
1208 | struct SyntheticSyntax; | 767 | struct SyntheticSyntax; |
1209 | 768 | ||
@@ -1213,136 +772,157 @@ fn main() { | |||
1213 | Err(SyntheticSyntax) => (), | 772 | Err(SyntheticSyntax) => (), |
1214 | } | 773 | } |
1215 | }"#, | 774 | }"#, |
1216 | ); | 775 | InlayHintsConfig { max_length: Some(8), ..Default::default() }, |
1217 | |||
1218 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###" | ||
1219 | [] | ||
1220 | "### | ||
1221 | ); | 776 | ); |
1222 | } | 777 | } |
1223 | 778 | ||
1224 | #[test] | 779 | #[test] |
1225 | fn chaining_hints_ignore_comments() { | 780 | fn chaining_hints_ignore_comments() { |
1226 | let (analysis, file_id) = single_file( | 781 | check_expect( |
1227 | r#" | 782 | r#" |
1228 | struct A(B); | 783 | struct A(B); |
1229 | impl A { fn into_b(self) -> B { self.0 } } | 784 | impl A { fn into_b(self) -> B { self.0 } } |
1230 | struct B(C); | 785 | struct B(C); |
1231 | impl B { fn into_c(self) -> C { self.0 } } | 786 | impl B { fn into_c(self) -> C { self.0 } } |
1232 | struct C; | 787 | struct C; |
1233 | 788 | ||
1234 | fn main() { | 789 | fn main() { |
1235 | let c = A(B(C)) | 790 | let c = A(B(C)) |
1236 | .into_b() // This is a comment | 791 | .into_b() // This is a comment |
1237 | .into_c(); | 792 | .into_c(); |
1238 | }"#, | 793 | } |
794 | "#, | ||
795 | InlayHintsConfig { | ||
796 | parameter_hints: false, | ||
797 | type_hints: false, | ||
798 | chaining_hints: true, | ||
799 | max_length: None, | ||
800 | }, | ||
801 | expect![[r#" | ||
802 | [ | ||
803 | InlayHint { | ||
804 | range: 147..172, | ||
805 | kind: ChainingHint, | ||
806 | label: "B", | ||
807 | }, | ||
808 | InlayHint { | ||
809 | range: 147..154, | ||
810 | kind: ChainingHint, | ||
811 | label: "A", | ||
812 | }, | ||
813 | ] | ||
814 | "#]], | ||
1239 | ); | 815 | ); |
1240 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" | ||
1241 | [ | ||
1242 | InlayHint { | ||
1243 | range: 147..172, | ||
1244 | kind: ChainingHint, | ||
1245 | label: "B", | ||
1246 | }, | ||
1247 | InlayHint { | ||
1248 | range: 147..154, | ||
1249 | kind: ChainingHint, | ||
1250 | label: "A", | ||
1251 | }, | ||
1252 | ] | ||
1253 | "###); | ||
1254 | } | 816 | } |
1255 | 817 | ||
1256 | #[test] | 818 | #[test] |
1257 | fn chaining_hints_without_newlines() { | 819 | fn chaining_hints_without_newlines() { |
1258 | let (analysis, file_id) = single_file( | 820 | check_with_config( |
1259 | r#" | 821 | r#" |
1260 | struct A(B); | 822 | struct A(B); |
1261 | impl A { fn into_b(self) -> B { self.0 } } | 823 | impl A { fn into_b(self) -> B { self.0 } } |
1262 | struct B(C); | 824 | struct B(C); |
1263 | impl B { fn into_c(self) -> C { self.0 } } | 825 | impl B { fn into_c(self) -> C { self.0 } } |
1264 | struct C; | 826 | struct C; |
1265 | 827 | ||
1266 | fn main() { | 828 | fn main() { |
1267 | let c = A(B(C)).into_b().into_c(); | 829 | let c = A(B(C)).into_b().into_c(); |
1268 | }"#, | 830 | }"#, |
831 | InlayHintsConfig { | ||
832 | parameter_hints: false, | ||
833 | type_hints: false, | ||
834 | chaining_hints: true, | ||
835 | max_length: None, | ||
836 | }, | ||
1269 | ); | 837 | ); |
1270 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###); | ||
1271 | } | 838 | } |
1272 | 839 | ||
1273 | #[test] | 840 | #[test] |
1274 | fn struct_access_chaining_hints() { | 841 | fn struct_access_chaining_hints() { |
1275 | let (analysis, file_id) = single_file( | 842 | check_expect( |
1276 | r#" | 843 | r#" |
1277 | struct A { pub b: B } | 844 | struct A { pub b: B } |
1278 | struct B { pub c: C } | 845 | struct B { pub c: C } |
1279 | struct C(pub bool); | 846 | struct C(pub bool); |
1280 | struct D; | 847 | struct D; |
1281 | 848 | ||
1282 | impl D { | 849 | impl D { |
1283 | fn foo(&self) -> i32 { 42 } | 850 | fn foo(&self) -> i32 { 42 } |
1284 | } | 851 | } |
1285 | 852 | ||
1286 | fn main() { | 853 | fn main() { |
1287 | let x = A { b: B { c: C(true) } } | 854 | let x = A { b: B { c: C(true) } } |
1288 | .b | 855 | .b |
1289 | .c | 856 | .c |
1290 | .0; | 857 | .0; |
1291 | let x = D | 858 | let x = D |
1292 | .foo(); | 859 | .foo(); |
1293 | }"#, | 860 | }"#, |
861 | InlayHintsConfig { | ||
862 | parameter_hints: false, | ||
863 | type_hints: false, | ||
864 | chaining_hints: true, | ||
865 | max_length: None, | ||
866 | }, | ||
867 | expect![[r#" | ||
868 | [ | ||
869 | InlayHint { | ||
870 | range: 143..190, | ||
871 | kind: ChainingHint, | ||
872 | label: "C", | ||
873 | }, | ||
874 | InlayHint { | ||
875 | range: 143..179, | ||
876 | kind: ChainingHint, | ||
877 | label: "B", | ||
878 | }, | ||
879 | ] | ||
880 | "#]], | ||
1294 | ); | 881 | ); |
1295 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" | ||
1296 | [ | ||
1297 | InlayHint { | ||
1298 | range: 143..190, | ||
1299 | kind: ChainingHint, | ||
1300 | label: "C", | ||
1301 | }, | ||
1302 | InlayHint { | ||
1303 | range: 143..179, | ||
1304 | kind: ChainingHint, | ||
1305 | label: "B", | ||
1306 | }, | ||
1307 | ] | ||
1308 | "###); | ||
1309 | } | 882 | } |
1310 | 883 | ||
1311 | #[test] | 884 | #[test] |
1312 | fn generic_chaining_hints() { | 885 | fn generic_chaining_hints() { |
1313 | let (analysis, file_id) = single_file( | 886 | check_expect( |
1314 | r#" | 887 | r#" |
1315 | struct A<T>(T); | 888 | struct A<T>(T); |
1316 | struct B<T>(T); | 889 | struct B<T>(T); |
1317 | struct C<T>(T); | 890 | struct C<T>(T); |
1318 | struct X<T,R>(T, R); | 891 | struct X<T,R>(T, R); |
1319 | 892 | ||
1320 | impl<T> A<T> { | 893 | impl<T> A<T> { |
1321 | fn new(t: T) -> Self { A(t) } | 894 | fn new(t: T) -> Self { A(t) } |
1322 | fn into_b(self) -> B<T> { B(self.0) } | 895 | fn into_b(self) -> B<T> { B(self.0) } |
1323 | } | 896 | } |
1324 | impl<T> B<T> { | 897 | impl<T> B<T> { |
1325 | fn into_c(self) -> C<T> { C(self.0) } | 898 | fn into_c(self) -> C<T> { C(self.0) } |
1326 | } | 899 | } |
1327 | fn main() { | 900 | fn main() { |
1328 | let c = A::new(X(42, true)) | 901 | let c = A::new(X(42, true)) |
1329 | .into_b() | 902 | .into_b() |
1330 | .into_c(); | 903 | .into_c(); |
1331 | }"#, | 904 | } |
905 | "#, | ||
906 | InlayHintsConfig { | ||
907 | parameter_hints: false, | ||
908 | type_hints: false, | ||
909 | chaining_hints: true, | ||
910 | max_length: None, | ||
911 | }, | ||
912 | expect![[r#" | ||
913 | [ | ||
914 | InlayHint { | ||
915 | range: 246..283, | ||
916 | kind: ChainingHint, | ||
917 | label: "B<X<i32, bool>>", | ||
918 | }, | ||
919 | InlayHint { | ||
920 | range: 246..265, | ||
921 | kind: ChainingHint, | ||
922 | label: "A<X<i32, bool>>", | ||
923 | }, | ||
924 | ] | ||
925 | "#]], | ||
1332 | ); | 926 | ); |
1333 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" | ||
1334 | [ | ||
1335 | InlayHint { | ||
1336 | range: 246..283, | ||
1337 | kind: ChainingHint, | ||
1338 | label: "B<X<i32, bool>>", | ||
1339 | }, | ||
1340 | InlayHint { | ||
1341 | range: 246..265, | ||
1342 | kind: ChainingHint, | ||
1343 | label: "A<X<i32, bool>>", | ||
1344 | }, | ||
1345 | ] | ||
1346 | "###); | ||
1347 | } | 927 | } |
1348 | } | 928 | } |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index ecac5134e..8660278f1 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -75,7 +75,7 @@ pub use crate::{ | |||
75 | }, | 75 | }, |
76 | }; | 76 | }; |
77 | 77 | ||
78 | pub use hir::Documentation; | 78 | pub use hir::{Documentation, Semantics}; |
79 | pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist}; | 79 | pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist}; |
80 | pub use ra_db::{ | 80 | pub use ra_db::{ |
81 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, | 81 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, |
@@ -385,7 +385,9 @@ impl Analysis { | |||
385 | position: FilePosition, | 385 | position: FilePosition, |
386 | search_scope: Option<SearchScope>, | 386 | search_scope: Option<SearchScope>, |
387 | ) -> Cancelable<Option<ReferenceSearchResult>> { | 387 | ) -> Cancelable<Option<ReferenceSearchResult>> { |
388 | self.with_db(|db| references::find_all_refs(db, position, search_scope).map(|it| it.info)) | 388 | self.with_db(|db| { |
389 | references::find_all_refs(&Semantics::new(db), position, search_scope).map(|it| it.info) | ||
390 | }) | ||
389 | } | 391 | } |
390 | 392 | ||
391 | /// Returns a short text describing element at position. | 393 | /// Returns a short text describing element at position. |
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index 889b84c59..a393d3dba 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs | |||
@@ -3,7 +3,9 @@ use std::sync::Arc; | |||
3 | 3 | ||
4 | use ra_cfg::CfgOptions; | 4 | use ra_cfg::CfgOptions; |
5 | use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath}; | 5 | use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath}; |
6 | use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER}; | 6 | use test_utils::{ |
7 | extract_annotations, extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, | ||
8 | }; | ||
7 | 9 | ||
8 | use crate::{ | 10 | use crate::{ |
9 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, | 11 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, |
@@ -77,6 +79,24 @@ impl MockAnalysis { | |||
77 | .expect("no file in this mock"); | 79 | .expect("no file in this mock"); |
78 | FileId(idx as u32 + 1) | 80 | FileId(idx as u32 + 1) |
79 | } | 81 | } |
82 | pub fn annotations(&self) -> Vec<(FileRange, String)> { | ||
83 | self.files | ||
84 | .iter() | ||
85 | .enumerate() | ||
86 | .flat_map(|(idx, fixture)| { | ||
87 | let file_id = FileId(idx as u32 + 1); | ||
88 | let annotations = extract_annotations(&fixture.text); | ||
89 | annotations | ||
90 | .into_iter() | ||
91 | .map(move |(range, data)| (FileRange { file_id, range }, data)) | ||
92 | }) | ||
93 | .collect() | ||
94 | } | ||
95 | pub fn annotation(&self) -> (FileRange, String) { | ||
96 | let mut all = self.annotations(); | ||
97 | assert_eq!(all.len(), 1); | ||
98 | all.pop().unwrap() | ||
99 | } | ||
80 | pub fn analysis_host(self) -> AnalysisHost { | 100 | pub fn analysis_host(self) -> AnalysisHost { |
81 | let mut host = AnalysisHost::default(); | 101 | let mut host = AnalysisHost::default(); |
82 | let mut change = AnalysisChange::new(); | 102 | let mut change = AnalysisChange::new(); |
@@ -110,7 +130,7 @@ impl MockAnalysis { | |||
110 | let other_crate = crate_graph.add_crate_root( | 130 | let other_crate = crate_graph.add_crate_root( |
111 | file_id, | 131 | file_id, |
112 | edition, | 132 | edition, |
113 | Some(CrateName::new(crate_name).unwrap()), | 133 | Some(crate_name.to_string()), |
114 | cfg, | 134 | cfg, |
115 | env, | 135 | env, |
116 | Default::default(), | 136 | Default::default(), |
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 3433fdae3..c2b0d5efe 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -86,12 +86,11 @@ impl IntoIterator for ReferenceSearchResult { | |||
86 | } | 86 | } |
87 | 87 | ||
88 | pub(crate) fn find_all_refs( | 88 | pub(crate) fn find_all_refs( |
89 | db: &RootDatabase, | 89 | sema: &Semantics<RootDatabase>, |
90 | position: FilePosition, | 90 | position: FilePosition, |
91 | search_scope: Option<SearchScope>, | 91 | search_scope: Option<SearchScope>, |
92 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 92 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
93 | let _p = profile("find_all_refs"); | 93 | let _p = profile("find_all_refs"); |
94 | let sema = Semantics::new(db); | ||
95 | let syntax = sema.parse(position.file_id).syntax().clone(); | 94 | let syntax = sema.parse(position.file_id).syntax().clone(); |
96 | 95 | ||
97 | let (opt_name, search_kind) = if let Some(name) = | 96 | let (opt_name, search_kind) = if let Some(name) = |
@@ -108,15 +107,15 @@ pub(crate) fn find_all_refs( | |||
108 | let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; | 107 | let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; |
109 | 108 | ||
110 | let references = def | 109 | let references = def |
111 | .find_usages(db, search_scope) | 110 | .find_usages(sema, search_scope) |
112 | .into_iter() | 111 | .into_iter() |
113 | .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) | 112 | .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) |
114 | .collect(); | 113 | .collect(); |
115 | 114 | ||
116 | let decl_range = def.try_to_nav(db)?.range(); | 115 | let decl_range = def.try_to_nav(sema.db)?.range(); |
117 | 116 | ||
118 | let declaration = Declaration { | 117 | let declaration = Declaration { |
119 | nav: def.try_to_nav(db)?, | 118 | nav: def.try_to_nav(sema.db)?, |
120 | kind: ReferenceKind::Other, | 119 | kind: ReferenceKind::Other, |
121 | access: decl_access(&def, &syntax, decl_range), | 120 | access: decl_access(&def, &syntax, decl_range), |
122 | }; | 121 | }; |
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 7ebc0adcf..b6a2266b4 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -24,23 +24,24 @@ pub(crate) fn rename( | |||
24 | position: FilePosition, | 24 | position: FilePosition, |
25 | new_name: &str, | 25 | new_name: &str, |
26 | ) -> Option<RangeInfo<SourceChange>> { | 26 | ) -> Option<RangeInfo<SourceChange>> { |
27 | let sema = Semantics::new(db); | ||
28 | |||
27 | match lex_single_valid_syntax_kind(new_name)? { | 29 | match lex_single_valid_syntax_kind(new_name)? { |
28 | SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), | 30 | SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), |
29 | SyntaxKind::SELF_KW => return rename_to_self(db, position), | 31 | SyntaxKind::SELF_KW => return rename_to_self(&sema, position), |
30 | _ => return None, | 32 | _ => return None, |
31 | } | 33 | } |
32 | 34 | ||
33 | let sema = Semantics::new(db); | ||
34 | let source_file = sema.parse(position.file_id); | 35 | let source_file = sema.parse(position.file_id); |
35 | let syntax = source_file.syntax(); | 36 | let syntax = source_file.syntax(); |
36 | if let Some(module) = find_module_at_offset(&sema, position, syntax) { | 37 | if let Some(module) = find_module_at_offset(&sema, position, syntax) { |
37 | rename_mod(db, position, module, new_name) | 38 | rename_mod(&sema, position, module, new_name) |
38 | } else if let Some(self_token) = | 39 | } else if let Some(self_token) = |
39 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) | 40 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) |
40 | { | 41 | { |
41 | rename_self_to_param(db, position, self_token, new_name) | 42 | rename_self_to_param(&sema, position, self_token, new_name) |
42 | } else { | 43 | } else { |
43 | rename_reference(sema.db, position, new_name) | 44 | rename_reference(&sema, position, new_name) |
44 | } | 45 | } |
45 | } | 46 | } |
46 | 47 | ||
@@ -97,7 +98,7 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil | |||
97 | } | 98 | } |
98 | 99 | ||
99 | fn rename_mod( | 100 | fn rename_mod( |
100 | db: &RootDatabase, | 101 | sema: &Semantics<RootDatabase>, |
101 | position: FilePosition, | 102 | position: FilePosition, |
102 | module: Module, | 103 | module: Module, |
103 | new_name: &str, | 104 | new_name: &str, |
@@ -105,12 +106,12 @@ fn rename_mod( | |||
105 | let mut source_file_edits = Vec::new(); | 106 | let mut source_file_edits = Vec::new(); |
106 | let mut file_system_edits = Vec::new(); | 107 | let mut file_system_edits = Vec::new(); |
107 | 108 | ||
108 | let src = module.definition_source(db); | 109 | let src = module.definition_source(sema.db); |
109 | let file_id = src.file_id.original_file(db); | 110 | let file_id = src.file_id.original_file(sema.db); |
110 | match src.value { | 111 | match src.value { |
111 | ModuleSource::SourceFile(..) => { | 112 | ModuleSource::SourceFile(..) => { |
112 | // mod is defined in path/to/dir/mod.rs | 113 | // mod is defined in path/to/dir/mod.rs |
113 | let dst = if module.is_mod_rs(db) { | 114 | let dst = if module.is_mod_rs(sema.db) { |
114 | format!("../{}/mod.rs", new_name) | 115 | format!("../{}/mod.rs", new_name) |
115 | } else { | 116 | } else { |
116 | format!("{}.rs", new_name) | 117 | format!("{}.rs", new_name) |
@@ -122,17 +123,17 @@ fn rename_mod( | |||
122 | ModuleSource::Module(..) => {} | 123 | ModuleSource::Module(..) => {} |
123 | } | 124 | } |
124 | 125 | ||
125 | if let Some(src) = module.declaration_source(db) { | 126 | if let Some(src) = module.declaration_source(sema.db) { |
126 | let file_id = src.file_id.original_file(db); | 127 | let file_id = src.file_id.original_file(sema.db); |
127 | let name = src.value.name()?; | 128 | let name = src.value.name()?; |
128 | let edit = SourceFileEdit { | 129 | let edit = SourceFileEdit { |
129 | file_id: file_id, | 130 | file_id, |
130 | edit: TextEdit::replace(name.syntax().text_range(), new_name.into()), | 131 | edit: TextEdit::replace(name.syntax().text_range(), new_name.into()), |
131 | }; | 132 | }; |
132 | source_file_edits.push(edit); | 133 | source_file_edits.push(edit); |
133 | } | 134 | } |
134 | 135 | ||
135 | let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; | 136 | let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?; |
136 | let ref_edits = refs | 137 | let ref_edits = refs |
137 | .references | 138 | .references |
138 | .into_iter() | 139 | .into_iter() |
@@ -142,8 +143,10 @@ fn rename_mod( | |||
142 | Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) | 143 | Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) |
143 | } | 144 | } |
144 | 145 | ||
145 | fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { | 146 | fn rename_to_self( |
146 | let sema = Semantics::new(db); | 147 | sema: &Semantics<RootDatabase>, |
148 | position: FilePosition, | ||
149 | ) -> Option<RangeInfo<SourceChange>> { | ||
147 | let source_file = sema.parse(position.file_id); | 150 | let source_file = sema.parse(position.file_id); |
148 | let syn = source_file.syntax(); | 151 | let syn = source_file.syntax(); |
149 | 152 | ||
@@ -158,7 +161,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo | |||
158 | _ => return None, // not renaming other types | 161 | _ => return None, // not renaming other types |
159 | }; | 162 | }; |
160 | 163 | ||
161 | let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; | 164 | let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?; |
162 | 165 | ||
163 | let param_range = first_param.syntax().text_range(); | 166 | let param_range = first_param.syntax().text_range(); |
164 | let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs | 167 | let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs |
@@ -210,16 +213,15 @@ fn text_edit_from_self_param( | |||
210 | } | 213 | } |
211 | 214 | ||
212 | fn rename_self_to_param( | 215 | fn rename_self_to_param( |
213 | db: &RootDatabase, | 216 | sema: &Semantics<RootDatabase>, |
214 | position: FilePosition, | 217 | position: FilePosition, |
215 | self_token: SyntaxToken, | 218 | self_token: SyntaxToken, |
216 | new_name: &str, | 219 | new_name: &str, |
217 | ) -> Option<RangeInfo<SourceChange>> { | 220 | ) -> Option<RangeInfo<SourceChange>> { |
218 | let sema = Semantics::new(db); | ||
219 | let source_file = sema.parse(position.file_id); | 221 | let source_file = sema.parse(position.file_id); |
220 | let syn = source_file.syntax(); | 222 | let syn = source_file.syntax(); |
221 | 223 | ||
222 | let text = db.file_text(position.file_id); | 224 | let text = sema.db.file_text(position.file_id); |
223 | let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; | 225 | let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; |
224 | let search_range = fn_def.syntax().text_range(); | 226 | let search_range = fn_def.syntax().text_range(); |
225 | 227 | ||
@@ -249,11 +251,11 @@ fn rename_self_to_param( | |||
249 | } | 251 | } |
250 | 252 | ||
251 | fn rename_reference( | 253 | fn rename_reference( |
252 | db: &RootDatabase, | 254 | sema: &Semantics<RootDatabase>, |
253 | position: FilePosition, | 255 | position: FilePosition, |
254 | new_name: &str, | 256 | new_name: &str, |
255 | ) -> Option<RangeInfo<SourceChange>> { | 257 | ) -> Option<RangeInfo<SourceChange>> { |
256 | let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; | 258 | let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?; |
257 | 259 | ||
258 | let edit = refs | 260 | let edit = refs |
259 | .into_iter() | 261 | .into_iter() |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index f569a3f17..ed15d6494 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -251,13 +251,21 @@ fn runnable_mod( | |||
251 | 251 | ||
252 | #[cfg(test)] | 252 | #[cfg(test)] |
253 | mod tests { | 253 | mod tests { |
254 | use insta::assert_debug_snapshot; | 254 | use expect::{expect, Expect}; |
255 | 255 | ||
256 | use crate::mock_analysis::analysis_and_position; | 256 | use crate::mock_analysis::analysis_and_position; |
257 | 257 | ||
258 | use super::{Runnable, RunnableAction, BENCH, BIN, DOCTEST, TEST}; | 258 | use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST}; |
259 | 259 | ||
260 | fn assert_actions(runnables: &[Runnable], actions: &[&RunnableAction]) { | 260 | fn check( |
261 | ra_fixture: &str, | ||
262 | // FIXME: fold this into `expect` as well | ||
263 | actions: &[&RunnableAction], | ||
264 | expect: Expect, | ||
265 | ) { | ||
266 | let (analysis, position) = analysis_and_position(ra_fixture); | ||
267 | let runnables = analysis.runnables(position.file_id).unwrap(); | ||
268 | expect.assert_debug_eq(&runnables); | ||
261 | assert_eq!( | 269 | assert_eq!( |
262 | actions, | 270 | actions, |
263 | runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice() | 271 | runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice() |
@@ -266,579 +274,557 @@ mod tests { | |||
266 | 274 | ||
267 | #[test] | 275 | #[test] |
268 | fn test_runnables() { | 276 | fn test_runnables() { |
269 | let (analysis, pos) = analysis_and_position( | 277 | check( |
270 | r#" | 278 | r#" |
271 | //- /lib.rs | 279 | //- /lib.rs |
272 | <|> //empty | 280 | <|> |
273 | fn main() {} | 281 | fn main() {} |
274 | 282 | ||
275 | #[test] | 283 | #[test] |
276 | fn test_foo() {} | 284 | fn test_foo() {} |
277 | 285 | ||
278 | #[test] | 286 | #[test] |
279 | #[ignore] | 287 | #[ignore] |
280 | fn test_foo() {} | 288 | fn test_foo() {} |
281 | 289 | ||
282 | #[bench] | 290 | #[bench] |
283 | fn bench() {} | 291 | fn bench() {} |
284 | "#, | 292 | "#, |
285 | ); | 293 | &[&BIN, &TEST, &TEST, &BENCH], |
286 | let runnables = analysis.runnables(pos.file_id).unwrap(); | 294 | expect![[r#" |
287 | assert_debug_snapshot!(&runnables, | 295 | [ |
288 | @r###" | 296 | Runnable { |
289 | [ | 297 | nav: NavigationTarget { |
290 | Runnable { | 298 | file_id: FileId( |
291 | nav: NavigationTarget { | 299 | 1, |
292 | file_id: FileId( | 300 | ), |
293 | 1, | 301 | full_range: 1..13, |
294 | ), | 302 | name: "main", |
295 | full_range: 1..21, | 303 | kind: FN_DEF, |
296 | name: "main", | 304 | focus_range: Some( |
297 | kind: FN_DEF, | 305 | 4..8, |
298 | focus_range: Some( | 306 | ), |
299 | 12..16, | 307 | container_name: None, |
300 | ), | 308 | description: None, |
301 | container_name: None, | 309 | docs: None, |
302 | description: None, | 310 | }, |
303 | docs: None, | 311 | kind: Bin, |
304 | }, | 312 | cfg_exprs: [], |
305 | kind: Bin, | ||
306 | cfg_exprs: [], | ||
307 | }, | ||
308 | Runnable { | ||
309 | nav: NavigationTarget { | ||
310 | file_id: FileId( | ||
311 | 1, | ||
312 | ), | ||
313 | full_range: 23..47, | ||
314 | name: "test_foo", | ||
315 | kind: FN_DEF, | ||
316 | focus_range: Some( | ||
317 | 34..42, | ||
318 | ), | ||
319 | container_name: None, | ||
320 | description: None, | ||
321 | docs: None, | ||
322 | }, | ||
323 | kind: Test { | ||
324 | test_id: Path( | ||
325 | "test_foo", | ||
326 | ), | ||
327 | attr: TestAttr { | ||
328 | ignore: false, | ||
329 | }, | 313 | }, |
330 | }, | 314 | Runnable { |
331 | cfg_exprs: [], | 315 | nav: NavigationTarget { |
332 | }, | 316 | file_id: FileId( |
333 | Runnable { | 317 | 1, |
334 | nav: NavigationTarget { | 318 | ), |
335 | file_id: FileId( | 319 | full_range: 15..39, |
336 | 1, | 320 | name: "test_foo", |
337 | ), | 321 | kind: FN_DEF, |
338 | full_range: 49..83, | 322 | focus_range: Some( |
339 | name: "test_foo", | 323 | 26..34, |
340 | kind: FN_DEF, | 324 | ), |
341 | focus_range: Some( | 325 | container_name: None, |
342 | 70..78, | 326 | description: None, |
343 | ), | 327 | docs: None, |
344 | container_name: None, | 328 | }, |
345 | description: None, | 329 | kind: Test { |
346 | docs: None, | 330 | test_id: Path( |
347 | }, | 331 | "test_foo", |
348 | kind: Test { | 332 | ), |
349 | test_id: Path( | 333 | attr: TestAttr { |
350 | "test_foo", | 334 | ignore: false, |
351 | ), | 335 | }, |
352 | attr: TestAttr { | 336 | }, |
353 | ignore: true, | 337 | cfg_exprs: [], |
338 | }, | ||
339 | Runnable { | ||
340 | nav: NavigationTarget { | ||
341 | file_id: FileId( | ||
342 | 1, | ||
343 | ), | ||
344 | full_range: 41..75, | ||
345 | name: "test_foo", | ||
346 | kind: FN_DEF, | ||
347 | focus_range: Some( | ||
348 | 62..70, | ||
349 | ), | ||
350 | container_name: None, | ||
351 | description: None, | ||
352 | docs: None, | ||
353 | }, | ||
354 | kind: Test { | ||
355 | test_id: Path( | ||
356 | "test_foo", | ||
357 | ), | ||
358 | attr: TestAttr { | ||
359 | ignore: true, | ||
360 | }, | ||
361 | }, | ||
362 | cfg_exprs: [], | ||
354 | }, | 363 | }, |
355 | }, | 364 | Runnable { |
356 | cfg_exprs: [], | 365 | nav: NavigationTarget { |
357 | }, | 366 | file_id: FileId( |
358 | Runnable { | 367 | 1, |
359 | nav: NavigationTarget { | 368 | ), |
360 | file_id: FileId( | 369 | full_range: 77..99, |
361 | 1, | 370 | name: "bench", |
362 | ), | 371 | kind: FN_DEF, |
363 | full_range: 85..107, | 372 | focus_range: Some( |
364 | name: "bench", | 373 | 89..94, |
365 | kind: FN_DEF, | 374 | ), |
366 | focus_range: Some( | 375 | container_name: None, |
367 | 97..102, | 376 | description: None, |
368 | ), | 377 | docs: None, |
369 | container_name: None, | 378 | }, |
370 | description: None, | 379 | kind: Bench { |
371 | docs: None, | 380 | test_id: Path( |
372 | }, | 381 | "bench", |
373 | kind: Bench { | 382 | ), |
374 | test_id: Path( | 383 | }, |
375 | "bench", | 384 | cfg_exprs: [], |
376 | ), | 385 | }, |
377 | }, | 386 | ] |
378 | cfg_exprs: [], | 387 | "#]], |
379 | }, | 388 | ); |
380 | ] | ||
381 | "### | ||
382 | ); | ||
383 | assert_actions(&runnables, &[&BIN, &TEST, &TEST, &BENCH]); | ||
384 | } | 389 | } |
385 | 390 | ||
386 | #[test] | 391 | #[test] |
387 | fn test_runnables_doc_test() { | 392 | fn test_runnables_doc_test() { |
388 | let (analysis, pos) = analysis_and_position( | 393 | check( |
389 | r#" | 394 | r#" |
390 | //- /lib.rs | 395 | //- /lib.rs |
391 | <|> //empty | 396 | <|> |
392 | fn main() {} | 397 | fn main() {} |
393 | 398 | ||
394 | /// ``` | 399 | /// ``` |
395 | /// let x = 5; | 400 | /// let x = 5; |
396 | /// ``` | 401 | /// ``` |
397 | fn foo() {} | 402 | fn foo() {} |
398 | "#, | 403 | "#, |
404 | &[&BIN, &DOCTEST], | ||
405 | expect![[r#" | ||
406 | [ | ||
407 | Runnable { | ||
408 | nav: NavigationTarget { | ||
409 | file_id: FileId( | ||
410 | 1, | ||
411 | ), | ||
412 | full_range: 1..13, | ||
413 | name: "main", | ||
414 | kind: FN_DEF, | ||
415 | focus_range: Some( | ||
416 | 4..8, | ||
417 | ), | ||
418 | container_name: None, | ||
419 | description: None, | ||
420 | docs: None, | ||
421 | }, | ||
422 | kind: Bin, | ||
423 | cfg_exprs: [], | ||
424 | }, | ||
425 | Runnable { | ||
426 | nav: NavigationTarget { | ||
427 | file_id: FileId( | ||
428 | 1, | ||
429 | ), | ||
430 | full_range: 15..57, | ||
431 | name: "foo", | ||
432 | kind: FN_DEF, | ||
433 | focus_range: None, | ||
434 | container_name: None, | ||
435 | description: None, | ||
436 | docs: None, | ||
437 | }, | ||
438 | kind: DocTest { | ||
439 | test_id: Path( | ||
440 | "foo", | ||
441 | ), | ||
442 | }, | ||
443 | cfg_exprs: [], | ||
444 | }, | ||
445 | ] | ||
446 | "#]], | ||
399 | ); | 447 | ); |
400 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
401 | assert_debug_snapshot!(&runnables, | ||
402 | @r###" | ||
403 | [ | ||
404 | Runnable { | ||
405 | nav: NavigationTarget { | ||
406 | file_id: FileId( | ||
407 | 1, | ||
408 | ), | ||
409 | full_range: 1..21, | ||
410 | name: "main", | ||
411 | kind: FN_DEF, | ||
412 | focus_range: Some( | ||
413 | 12..16, | ||
414 | ), | ||
415 | container_name: None, | ||
416 | description: None, | ||
417 | docs: None, | ||
418 | }, | ||
419 | kind: Bin, | ||
420 | cfg_exprs: [], | ||
421 | }, | ||
422 | Runnable { | ||
423 | nav: NavigationTarget { | ||
424 | file_id: FileId( | ||
425 | 1, | ||
426 | ), | ||
427 | full_range: 23..65, | ||
428 | name: "foo", | ||
429 | kind: FN_DEF, | ||
430 | focus_range: None, | ||
431 | container_name: None, | ||
432 | description: None, | ||
433 | docs: None, | ||
434 | }, | ||
435 | kind: DocTest { | ||
436 | test_id: Path( | ||
437 | "foo", | ||
438 | ), | ||
439 | }, | ||
440 | cfg_exprs: [], | ||
441 | }, | ||
442 | ] | ||
443 | "### | ||
444 | ); | ||
445 | assert_actions(&runnables, &[&BIN, &DOCTEST]); | ||
446 | } | 448 | } |
447 | 449 | ||
448 | #[test] | 450 | #[test] |
449 | fn test_runnables_doc_test_in_impl() { | 451 | fn test_runnables_doc_test_in_impl() { |
450 | let (analysis, pos) = analysis_and_position( | 452 | check( |
451 | r#" | 453 | r#" |
452 | //- /lib.rs | 454 | //- /lib.rs |
453 | <|> //empty | 455 | <|> |
454 | fn main() {} | 456 | fn main() {} |
455 | 457 | ||
456 | struct Data; | 458 | struct Data; |
457 | impl Data { | 459 | impl Data { |
458 | /// ``` | 460 | /// ``` |
459 | /// let x = 5; | 461 | /// let x = 5; |
460 | /// ``` | 462 | /// ``` |
461 | fn foo() {} | 463 | fn foo() {} |
462 | } | 464 | } |
463 | "#, | 465 | "#, |
466 | &[&BIN, &DOCTEST], | ||
467 | expect![[r#" | ||
468 | [ | ||
469 | Runnable { | ||
470 | nav: NavigationTarget { | ||
471 | file_id: FileId( | ||
472 | 1, | ||
473 | ), | ||
474 | full_range: 1..13, | ||
475 | name: "main", | ||
476 | kind: FN_DEF, | ||
477 | focus_range: Some( | ||
478 | 4..8, | ||
479 | ), | ||
480 | container_name: None, | ||
481 | description: None, | ||
482 | docs: None, | ||
483 | }, | ||
484 | kind: Bin, | ||
485 | cfg_exprs: [], | ||
486 | }, | ||
487 | Runnable { | ||
488 | nav: NavigationTarget { | ||
489 | file_id: FileId( | ||
490 | 1, | ||
491 | ), | ||
492 | full_range: 44..98, | ||
493 | name: "foo", | ||
494 | kind: FN_DEF, | ||
495 | focus_range: None, | ||
496 | container_name: None, | ||
497 | description: None, | ||
498 | docs: None, | ||
499 | }, | ||
500 | kind: DocTest { | ||
501 | test_id: Path( | ||
502 | "Data::foo", | ||
503 | ), | ||
504 | }, | ||
505 | cfg_exprs: [], | ||
506 | }, | ||
507 | ] | ||
508 | "#]], | ||
464 | ); | 509 | ); |
465 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
466 | assert_debug_snapshot!(&runnables, | ||
467 | @r###" | ||
468 | [ | ||
469 | Runnable { | ||
470 | nav: NavigationTarget { | ||
471 | file_id: FileId( | ||
472 | 1, | ||
473 | ), | ||
474 | full_range: 1..21, | ||
475 | name: "main", | ||
476 | kind: FN_DEF, | ||
477 | focus_range: Some( | ||
478 | 12..16, | ||
479 | ), | ||
480 | container_name: None, | ||
481 | description: None, | ||
482 | docs: None, | ||
483 | }, | ||
484 | kind: Bin, | ||
485 | cfg_exprs: [], | ||
486 | }, | ||
487 | Runnable { | ||
488 | nav: NavigationTarget { | ||
489 | file_id: FileId( | ||
490 | 1, | ||
491 | ), | ||
492 | full_range: 52..106, | ||
493 | name: "foo", | ||
494 | kind: FN_DEF, | ||
495 | focus_range: None, | ||
496 | container_name: None, | ||
497 | description: None, | ||
498 | docs: None, | ||
499 | }, | ||
500 | kind: DocTest { | ||
501 | test_id: Path( | ||
502 | "Data::foo", | ||
503 | ), | ||
504 | }, | ||
505 | cfg_exprs: [], | ||
506 | }, | ||
507 | ] | ||
508 | "### | ||
509 | ); | ||
510 | assert_actions(&runnables, &[&BIN, &DOCTEST]); | ||
511 | } | 510 | } |
512 | 511 | ||
513 | #[test] | 512 | #[test] |
514 | fn test_runnables_module() { | 513 | fn test_runnables_module() { |
515 | let (analysis, pos) = analysis_and_position( | 514 | check( |
516 | r#" | 515 | r#" |
517 | //- /lib.rs | 516 | //- /lib.rs |
518 | <|> //empty | 517 | <|> |
519 | mod test_mod { | 518 | mod test_mod { |
520 | #[test] | 519 | #[test] |
521 | fn test_foo1() {} | 520 | fn test_foo1() {} |
522 | } | 521 | } |
523 | "#, | 522 | "#, |
524 | ); | 523 | &[&TEST, &TEST], |
525 | let runnables = analysis.runnables(pos.file_id).unwrap(); | 524 | expect![[r#" |
526 | assert_debug_snapshot!(&runnables, | 525 | [ |
527 | @r###" | 526 | Runnable { |
528 | [ | 527 | nav: NavigationTarget { |
529 | Runnable { | 528 | file_id: FileId( |
530 | nav: NavigationTarget { | 529 | 1, |
531 | file_id: FileId( | 530 | ), |
532 | 1, | 531 | full_range: 1..51, |
533 | ), | 532 | name: "test_mod", |
534 | full_range: 1..59, | 533 | kind: MODULE, |
535 | name: "test_mod", | 534 | focus_range: Some( |
536 | kind: MODULE, | 535 | 5..13, |
537 | focus_range: Some( | 536 | ), |
538 | 13..21, | 537 | container_name: None, |
539 | ), | 538 | description: None, |
540 | container_name: None, | 539 | docs: None, |
541 | description: None, | 540 | }, |
542 | docs: None, | 541 | kind: TestMod { |
543 | }, | 542 | path: "test_mod", |
544 | kind: TestMod { | 543 | }, |
545 | path: "test_mod", | 544 | cfg_exprs: [], |
546 | }, | 545 | }, |
547 | cfg_exprs: [], | 546 | Runnable { |
548 | }, | 547 | nav: NavigationTarget { |
549 | Runnable { | 548 | file_id: FileId( |
550 | nav: NavigationTarget { | 549 | 1, |
551 | file_id: FileId( | 550 | ), |
552 | 1, | 551 | full_range: 20..49, |
553 | ), | 552 | name: "test_foo1", |
554 | full_range: 28..57, | 553 | kind: FN_DEF, |
555 | name: "test_foo1", | 554 | focus_range: Some( |
556 | kind: FN_DEF, | 555 | 35..44, |
557 | focus_range: Some( | 556 | ), |
558 | 43..52, | 557 | container_name: None, |
559 | ), | 558 | description: None, |
560 | container_name: None, | 559 | docs: None, |
561 | description: None, | 560 | }, |
562 | docs: None, | 561 | kind: Test { |
563 | }, | 562 | test_id: Path( |
564 | kind: Test { | 563 | "test_mod::test_foo1", |
565 | test_id: Path( | 564 | ), |
566 | "test_mod::test_foo1", | 565 | attr: TestAttr { |
567 | ), | 566 | ignore: false, |
568 | attr: TestAttr { | 567 | }, |
569 | ignore: false, | 568 | }, |
569 | cfg_exprs: [], | ||
570 | }, | 570 | }, |
571 | }, | 571 | ] |
572 | cfg_exprs: [], | 572 | "#]], |
573 | }, | 573 | ); |
574 | ] | ||
575 | "### | ||
576 | ); | ||
577 | assert_actions(&runnables, &[&TEST, &TEST]); | ||
578 | } | 574 | } |
579 | 575 | ||
580 | #[test] | 576 | #[test] |
581 | fn test_runnables_one_depth_layer_module() { | 577 | fn test_runnables_one_depth_layer_module() { |
582 | let (analysis, pos) = analysis_and_position( | 578 | check( |
583 | r#" | 579 | r#" |
584 | //- /lib.rs | 580 | //- /lib.rs |
585 | <|> //empty | 581 | <|> |
586 | mod foo { | 582 | mod foo { |
587 | mod test_mod { | 583 | mod test_mod { |
588 | #[test] | 584 | #[test] |
589 | fn test_foo1() {} | 585 | fn test_foo1() {} |
590 | } | 586 | } |
591 | } | 587 | } |
592 | "#, | 588 | "#, |
593 | ); | 589 | &[&TEST, &TEST], |
594 | let runnables = analysis.runnables(pos.file_id).unwrap(); | 590 | expect![[r#" |
595 | assert_debug_snapshot!(&runnables, | 591 | [ |
596 | @r###" | 592 | Runnable { |
597 | [ | 593 | nav: NavigationTarget { |
598 | Runnable { | 594 | file_id: FileId( |
599 | nav: NavigationTarget { | 595 | 1, |
600 | file_id: FileId( | 596 | ), |
601 | 1, | 597 | full_range: 15..77, |
602 | ), | 598 | name: "test_mod", |
603 | full_range: 23..85, | 599 | kind: MODULE, |
604 | name: "test_mod", | 600 | focus_range: Some( |
605 | kind: MODULE, | 601 | 19..27, |
606 | focus_range: Some( | 602 | ), |
607 | 27..35, | 603 | container_name: None, |
608 | ), | 604 | description: None, |
609 | container_name: None, | 605 | docs: None, |
610 | description: None, | 606 | }, |
611 | docs: None, | 607 | kind: TestMod { |
612 | }, | 608 | path: "foo::test_mod", |
613 | kind: TestMod { | 609 | }, |
614 | path: "foo::test_mod", | 610 | cfg_exprs: [], |
615 | }, | 611 | }, |
616 | cfg_exprs: [], | 612 | Runnable { |
617 | }, | 613 | nav: NavigationTarget { |
618 | Runnable { | 614 | file_id: FileId( |
619 | nav: NavigationTarget { | 615 | 1, |
620 | file_id: FileId( | 616 | ), |
621 | 1, | 617 | full_range: 38..71, |
622 | ), | 618 | name: "test_foo1", |
623 | full_range: 46..79, | 619 | kind: FN_DEF, |
624 | name: "test_foo1", | 620 | focus_range: Some( |
625 | kind: FN_DEF, | 621 | 57..66, |
626 | focus_range: Some( | 622 | ), |
627 | 65..74, | 623 | container_name: None, |
628 | ), | 624 | description: None, |
629 | container_name: None, | 625 | docs: None, |
630 | description: None, | 626 | }, |
631 | docs: None, | 627 | kind: Test { |
632 | }, | 628 | test_id: Path( |
633 | kind: Test { | 629 | "foo::test_mod::test_foo1", |
634 | test_id: Path( | 630 | ), |
635 | "foo::test_mod::test_foo1", | 631 | attr: TestAttr { |
636 | ), | 632 | ignore: false, |
637 | attr: TestAttr { | 633 | }, |
638 | ignore: false, | 634 | }, |
635 | cfg_exprs: [], | ||
639 | }, | 636 | }, |
640 | }, | 637 | ] |
641 | cfg_exprs: [], | 638 | "#]], |
642 | }, | 639 | ); |
643 | ] | ||
644 | "### | ||
645 | ); | ||
646 | assert_actions(&runnables, &[&TEST, &TEST]); | ||
647 | } | 640 | } |
648 | 641 | ||
649 | #[test] | 642 | #[test] |
650 | fn test_runnables_multiple_depth_module() { | 643 | fn test_runnables_multiple_depth_module() { |
651 | let (analysis, pos) = analysis_and_position( | 644 | check( |
652 | r#" | 645 | r#" |
653 | //- /lib.rs | 646 | //- /lib.rs |
654 | <|> //empty | 647 | <|> |
655 | mod foo { | 648 | mod foo { |
656 | mod bar { | 649 | mod bar { |
657 | mod test_mod { | 650 | mod test_mod { |
658 | #[test] | 651 | #[test] |
659 | fn test_foo1() {} | 652 | fn test_foo1() {} |
660 | } | ||
661 | } | ||
662 | } | 653 | } |
663 | "#, | 654 | } |
664 | ); | 655 | } |
665 | let runnables = analysis.runnables(pos.file_id).unwrap(); | 656 | "#, |
666 | assert_debug_snapshot!(&runnables, | 657 | &[&TEST, &TEST], |
667 | @r###" | 658 | expect![[r#" |
668 | [ | 659 | [ |
669 | Runnable { | 660 | Runnable { |
670 | nav: NavigationTarget { | 661 | nav: NavigationTarget { |
671 | file_id: FileId( | 662 | file_id: FileId( |
672 | 1, | 663 | 1, |
673 | ), | 664 | ), |
674 | full_range: 41..115, | 665 | full_range: 33..107, |
675 | name: "test_mod", | 666 | name: "test_mod", |
676 | kind: MODULE, | 667 | kind: MODULE, |
677 | focus_range: Some( | 668 | focus_range: Some( |
678 | 45..53, | 669 | 37..45, |
679 | ), | 670 | ), |
680 | container_name: None, | 671 | container_name: None, |
681 | description: None, | 672 | description: None, |
682 | docs: None, | 673 | docs: None, |
683 | }, | 674 | }, |
684 | kind: TestMod { | 675 | kind: TestMod { |
685 | path: "foo::bar::test_mod", | 676 | path: "foo::bar::test_mod", |
686 | }, | 677 | }, |
687 | cfg_exprs: [], | 678 | cfg_exprs: [], |
688 | }, | 679 | }, |
689 | Runnable { | 680 | Runnable { |
690 | nav: NavigationTarget { | 681 | nav: NavigationTarget { |
691 | file_id: FileId( | 682 | file_id: FileId( |
692 | 1, | 683 | 1, |
693 | ), | 684 | ), |
694 | full_range: 68..105, | 685 | full_range: 60..97, |
695 | name: "test_foo1", | 686 | name: "test_foo1", |
696 | kind: FN_DEF, | 687 | kind: FN_DEF, |
697 | focus_range: Some( | 688 | focus_range: Some( |
698 | 91..100, | 689 | 83..92, |
699 | ), | 690 | ), |
700 | container_name: None, | 691 | container_name: None, |
701 | description: None, | 692 | description: None, |
702 | docs: None, | 693 | docs: None, |
703 | }, | 694 | }, |
704 | kind: Test { | 695 | kind: Test { |
705 | test_id: Path( | 696 | test_id: Path( |
706 | "foo::bar::test_mod::test_foo1", | 697 | "foo::bar::test_mod::test_foo1", |
707 | ), | 698 | ), |
708 | attr: TestAttr { | 699 | attr: TestAttr { |
709 | ignore: false, | 700 | ignore: false, |
701 | }, | ||
702 | }, | ||
703 | cfg_exprs: [], | ||
710 | }, | 704 | }, |
711 | }, | 705 | ] |
712 | cfg_exprs: [], | 706 | "#]], |
713 | }, | 707 | ); |
714 | ] | ||
715 | "### | ||
716 | ); | ||
717 | assert_actions(&runnables, &[&TEST, &TEST]); | ||
718 | } | 708 | } |
719 | 709 | ||
720 | #[test] | 710 | #[test] |
721 | fn test_runnables_with_feature() { | 711 | fn test_runnables_with_feature() { |
722 | let (analysis, pos) = analysis_and_position( | 712 | check( |
723 | r#" | 713 | r#" |
724 | //- /lib.rs crate:foo cfg:feature=foo | 714 | //- /lib.rs crate:foo cfg:feature=foo |
725 | <|> //empty | 715 | <|> |
726 | #[test] | 716 | #[test] |
727 | #[cfg(feature = "foo")] | 717 | #[cfg(feature = "foo")] |
728 | fn test_foo1() {} | 718 | fn test_foo1() {} |
729 | "#, | 719 | "#, |
730 | ); | 720 | &[&TEST], |
731 | let runnables = analysis.runnables(pos.file_id).unwrap(); | 721 | expect![[r#" |
732 | assert_debug_snapshot!(&runnables, | 722 | [ |
733 | @r###" | 723 | Runnable { |
734 | [ | 724 | nav: NavigationTarget { |
735 | Runnable { | 725 | file_id: FileId( |
736 | nav: NavigationTarget { | 726 | 1, |
737 | file_id: FileId( | 727 | ), |
738 | 1, | 728 | full_range: 1..50, |
739 | ), | 729 | name: "test_foo1", |
740 | full_range: 1..58, | 730 | kind: FN_DEF, |
741 | name: "test_foo1", | 731 | focus_range: Some( |
742 | kind: FN_DEF, | 732 | 36..45, |
743 | focus_range: Some( | 733 | ), |
744 | 44..53, | 734 | container_name: None, |
745 | ), | 735 | description: None, |
746 | container_name: None, | 736 | docs: None, |
747 | description: None, | 737 | }, |
748 | docs: None, | 738 | kind: Test { |
749 | }, | 739 | test_id: Path( |
750 | kind: Test { | 740 | "test_foo1", |
751 | test_id: Path( | 741 | ), |
752 | "test_foo1", | 742 | attr: TestAttr { |
753 | ), | 743 | ignore: false, |
754 | attr: TestAttr { | 744 | }, |
755 | ignore: false, | 745 | }, |
756 | }, | 746 | cfg_exprs: [ |
757 | }, | 747 | KeyValue { |
758 | cfg_exprs: [ | 748 | key: "feature", |
759 | KeyValue { | 749 | value: "foo", |
760 | key: "feature", | 750 | }, |
761 | value: "foo", | 751 | ], |
762 | }, | 752 | }, |
763 | ], | 753 | ] |
764 | }, | 754 | "#]], |
765 | ] | 755 | ); |
766 | "### | ||
767 | ); | ||
768 | assert_actions(&runnables, &[&TEST]); | ||
769 | } | 756 | } |
770 | 757 | ||
771 | #[test] | 758 | #[test] |
772 | fn test_runnables_with_features() { | 759 | fn test_runnables_with_features() { |
773 | let (analysis, pos) = analysis_and_position( | 760 | check( |
774 | r#" | 761 | r#" |
775 | //- /lib.rs crate:foo cfg:feature=foo,feature=bar | 762 | //- /lib.rs crate:foo cfg:feature=foo,feature=bar |
776 | <|> //empty | 763 | <|> |
777 | #[test] | 764 | #[test] |
778 | #[cfg(all(feature = "foo", feature = "bar"))] | 765 | #[cfg(all(feature = "foo", feature = "bar"))] |
779 | fn test_foo1() {} | 766 | fn test_foo1() {} |
780 | "#, | 767 | "#, |
781 | ); | 768 | &[&TEST], |
782 | let runnables = analysis.runnables(pos.file_id).unwrap(); | 769 | expect![[r#" |
783 | assert_debug_snapshot!(&runnables, | 770 | [ |
784 | @r###" | 771 | Runnable { |
785 | [ | 772 | nav: NavigationTarget { |
786 | Runnable { | 773 | file_id: FileId( |
787 | nav: NavigationTarget { | 774 | 1, |
788 | file_id: FileId( | 775 | ), |
789 | 1, | 776 | full_range: 1..72, |
790 | ), | 777 | name: "test_foo1", |
791 | full_range: 1..80, | 778 | kind: FN_DEF, |
792 | name: "test_foo1", | 779 | focus_range: Some( |
793 | kind: FN_DEF, | 780 | 58..67, |
794 | focus_range: Some( | 781 | ), |
795 | 66..75, | 782 | container_name: None, |
796 | ), | 783 | description: None, |
797 | container_name: None, | 784 | docs: None, |
798 | description: None, | 785 | }, |
799 | docs: None, | 786 | kind: Test { |
800 | }, | 787 | test_id: Path( |
801 | kind: Test { | 788 | "test_foo1", |
802 | test_id: Path( | 789 | ), |
803 | "test_foo1", | 790 | attr: TestAttr { |
804 | ), | 791 | ignore: false, |
805 | attr: TestAttr { | ||
806 | ignore: false, | ||
807 | }, | ||
808 | }, | ||
809 | cfg_exprs: [ | ||
810 | All( | ||
811 | [ | ||
812 | KeyValue { | ||
813 | key: "feature", | ||
814 | value: "foo", | ||
815 | }, | ||
816 | KeyValue { | ||
817 | key: "feature", | ||
818 | value: "bar", | ||
819 | }, | 792 | }, |
793 | }, | ||
794 | cfg_exprs: [ | ||
795 | All( | ||
796 | [ | ||
797 | KeyValue { | ||
798 | key: "feature", | ||
799 | value: "foo", | ||
800 | }, | ||
801 | KeyValue { | ||
802 | key: "feature", | ||
803 | value: "bar", | ||
804 | }, | ||
805 | ], | ||
806 | ), | ||
820 | ], | 807 | ], |
821 | ), | 808 | }, |
822 | ], | 809 | ] |
823 | }, | 810 | "#]], |
824 | ] | 811 | ); |
825 | "### | ||
826 | ); | ||
827 | assert_actions(&runnables, &[&TEST]); | ||
828 | } | 812 | } |
829 | 813 | ||
830 | #[test] | 814 | #[test] |
831 | fn test_runnables_no_test_function_in_module() { | 815 | fn test_runnables_no_test_function_in_module() { |
832 | let (analysis, pos) = analysis_and_position( | 816 | check( |
833 | r#" | 817 | r#" |
834 | //- /lib.rs | 818 | //- /lib.rs |
835 | <|> //empty | 819 | <|> |
836 | mod test_mod { | 820 | mod test_mod { |
837 | fn foo1() {} | 821 | fn foo1() {} |
838 | } | 822 | } |
839 | "#, | 823 | "#, |
824 | &[], | ||
825 | expect![[r#" | ||
826 | [] | ||
827 | "#]], | ||
840 | ); | 828 | ); |
841 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
842 | assert!(runnables.is_empty()) | ||
843 | } | 829 | } |
844 | } | 830 | } |
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs index 6cb96608b..b3e9e5dfe 100644 --- a/crates/ra_ide/src/ssr.rs +++ b/crates/ra_ide/src/ssr.rs | |||
@@ -4,12 +4,24 @@ use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; | |||
4 | use crate::SourceFileEdit; | 4 | use crate::SourceFileEdit; |
5 | use ra_ssr::{MatchFinder, SsrError, SsrRule}; | 5 | use ra_ssr::{MatchFinder, SsrError, SsrRule}; |
6 | 6 | ||
7 | // Feature: Structural Seach and Replace | 7 | // Feature: Structural Search and Replace |
8 | // | 8 | // |
9 | // Search and replace with named wildcards that will match any expression, type, path, pattern or item. | 9 | // Search and replace with named wildcards that will match any expression, type, path, pattern or item. |
10 | // The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. | 10 | // The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. |
11 | // A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement. | 11 | // A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement. |
12 | // Within a macro call, a placeholder will match up until whatever token follows the placeholder. | 12 | // Within a macro call, a placeholder will match up until whatever token follows the placeholder. |
13 | // | ||
14 | // Placeholders may be given constraints by writing them as `${<name>:<constraint1>:<constraint2>...}`. | ||
15 | // | ||
16 | // Supported constraints: | ||
17 | // | ||
18 | // |=== | ||
19 | // | Constraint | Restricts placeholder | ||
20 | // | ||
21 | // | kind(literal) | Is a literal (e.g. `42` or `"forty two"`) | ||
22 | // | not(a) | Negates the constraint `a` | ||
23 | // |=== | ||
24 | // | ||
13 | // Available via the command `rust-analyzer.ssr`. | 25 | // Available via the command `rust-analyzer.ssr`. |
14 | // | 26 | // |
15 | // ```rust | 27 | // ```rust |
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index b7fad9719..aa7c887d6 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | use std::fs; | 1 | use std::fs; |
2 | 2 | ||
3 | use test_utils::{assert_eq_text, project_dir, read_text}; | 3 | use expect::{expect_file, ExpectFile}; |
4 | use test_utils::project_dir; | ||
4 | 5 | ||
5 | use crate::{mock_analysis::single_file, FileRange, TextRange}; | 6 | use crate::{mock_analysis::single_file, FileRange, TextRange}; |
6 | 7 | ||
@@ -91,7 +92,7 @@ impl<T> Option<T> { | |||
91 | } | 92 | } |
92 | "# | 93 | "# |
93 | .trim(), | 94 | .trim(), |
94 | "crates/ra_ide/src/snapshots/highlighting.html", | 95 | expect_file!["crates/ra_ide/test_data/highlighting.html"], |
95 | false, | 96 | false, |
96 | ); | 97 | ); |
97 | } | 98 | } |
@@ -114,7 +115,7 @@ fn bar() { | |||
114 | } | 115 | } |
115 | "# | 116 | "# |
116 | .trim(), | 117 | .trim(), |
117 | "crates/ra_ide/src/snapshots/rainbow_highlighting.html", | 118 | expect_file!["crates/ra_ide/test_data/rainbow_highlighting.html"], |
118 | true, | 119 | true, |
119 | ); | 120 | ); |
120 | } | 121 | } |
@@ -167,7 +168,7 @@ fn main() { | |||
167 | ); | 168 | ); |
168 | }"## | 169 | }"## |
169 | .trim(), | 170 | .trim(), |
170 | "crates/ra_ide/src/snapshots/highlight_injection.html", | 171 | expect_file!["crates/ra_ide/test_data/highlight_injection.html"], |
171 | false, | 172 | false, |
172 | ); | 173 | ); |
173 | } | 174 | } |
@@ -250,7 +251,7 @@ fn main() { | |||
250 | println!("{ничоси}", ничоси = 92); | 251 | println!("{ничоси}", ничоси = 92); |
251 | }"# | 252 | }"# |
252 | .trim(), | 253 | .trim(), |
253 | "crates/ra_ide/src/snapshots/highlight_strings.html", | 254 | expect_file!["crates/ra_ide/test_data/highlight_strings.html"], |
254 | false, | 255 | false, |
255 | ); | 256 | ); |
256 | } | 257 | } |
@@ -278,7 +279,7 @@ fn main() { | |||
278 | } | 279 | } |
279 | "# | 280 | "# |
280 | .trim(), | 281 | .trim(), |
281 | "crates/ra_ide/src/snapshots/highlight_unsafe.html", | 282 | expect_file!["crates/ra_ide/test_data/highlight_unsafe.html"], |
282 | false, | 283 | false, |
283 | ); | 284 | ); |
284 | } | 285 | } |
@@ -354,7 +355,7 @@ macro_rules! noop { | |||
354 | } | 355 | } |
355 | "# | 356 | "# |
356 | .trim(), | 357 | .trim(), |
357 | "crates/ra_ide/src/snapshots/highlight_doctest.html", | 358 | expect_file!["crates/ra_ide/test_data/highlight_doctest.html"], |
358 | false, | 359 | false, |
359 | ); | 360 | ); |
360 | } | 361 | } |
@@ -362,11 +363,8 @@ macro_rules! noop { | |||
362 | /// Highlights the code given by the `ra_fixture` argument, renders the | 363 | /// Highlights the code given by the `ra_fixture` argument, renders the |
363 | /// result as HTML, and compares it with the HTML file given as `snapshot`. | 364 | /// result as HTML, and compares it with the HTML file given as `snapshot`. |
364 | /// Note that the `snapshot` file is overwritten by the rendered HTML. | 365 | /// Note that the `snapshot` file is overwritten by the rendered HTML. |
365 | fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) { | 366 | fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) { |
366 | let (analysis, file_id) = single_file(ra_fixture); | 367 | let (analysis, file_id) = single_file(ra_fixture); |
367 | let dst_file = project_dir().join(snapshot); | ||
368 | let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); | 368 | let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); |
369 | let expected_html = &read_text(&dst_file); | 369 | expect.assert_eq(actual_html) |
370 | fs::write(dst_file, &actual_html).unwrap(); | ||
371 | assert_eq_text!(expected_html, actual_html); | ||
372 | } | 370 | } |
diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/test_data/highlight_doctest.html index e8155def7..e8155def7 100644 --- a/crates/ra_ide/src/snapshots/highlight_doctest.html +++ b/crates/ra_ide/test_data/highlight_doctest.html | |||
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/test_data/highlight_injection.html index 1b0349bae..1b0349bae 100644 --- a/crates/ra_ide/src/snapshots/highlight_injection.html +++ b/crates/ra_ide/test_data/highlight_injection.html | |||
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/test_data/highlight_strings.html index d184b5691..d184b5691 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/test_data/highlight_strings.html | |||
diff --git a/crates/ra_ide/src/snapshots/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html index 6936e949f..6936e949f 100644 --- a/crates/ra_ide/src/snapshots/highlight_unsafe.html +++ b/crates/ra_ide/test_data/highlight_unsafe.html | |||
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/test_data/highlighting.html index 8d0b38f95..8d0b38f95 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/test_data/highlighting.html | |||
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/test_data/rainbow_highlighting.html index 9516c7441..9516c7441 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/test_data/rainbow_highlighting.html | |||
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs index b507000f2..dbe6eacc5 100644 --- a/crates/ra_ide_db/src/change.rs +++ b/crates/ra_ide_db/src/change.rs | |||
@@ -243,8 +243,9 @@ impl RootDatabase { | |||
243 | hir::db::GenericPredicatesForParamQuery | 243 | hir::db::GenericPredicatesForParamQuery |
244 | hir::db::GenericPredicatesQuery | 244 | hir::db::GenericPredicatesQuery |
245 | hir::db::GenericDefaultsQuery | 245 | hir::db::GenericDefaultsQuery |
246 | hir::db::ImplsInCrateQuery | 246 | hir::db::InherentImplsInCrateQuery |
247 | hir::db::ImplsFromDepsQuery | 247 | hir::db::TraitImplsInCrateQuery |
248 | hir::db::TraitImplsInDepsQuery | ||
248 | hir::db::InternTypeCtorQuery | 249 | hir::db::InternTypeCtorQuery |
249 | hir::db::InternTypeParamIdQuery | 250 | hir::db::InternTypeParamIdQuery |
250 | hir::db::InternChalkImplQuery | 251 | hir::db::InternChalkImplQuery |
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs index fff112e66..1fba71ff8 100644 --- a/crates/ra_ide_db/src/imports_locator.rs +++ b/crates/ra_ide_db/src/imports_locator.rs | |||
@@ -13,57 +13,53 @@ use crate::{ | |||
13 | use either::Either; | 13 | use either::Either; |
14 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
15 | 15 | ||
16 | pub struct ImportsLocator<'a> { | 16 | pub fn find_imports<'a>( |
17 | sema: Semantics<'a, RootDatabase>, | 17 | sema: &Semantics<'a, RootDatabase>, |
18 | krate: Crate, | 18 | krate: Crate, |
19 | } | 19 | name_to_import: &str, |
20 | 20 | ) -> Vec<Either<ModuleDef, MacroDef>> { | |
21 | impl<'a> ImportsLocator<'a> { | 21 | let _p = profile("search_for_imports"); |
22 | pub fn new(db: &'a RootDatabase, krate: Crate) -> Self { | 22 | let db = sema.db; |
23 | Self { sema: Semantics::new(db), krate } | ||
24 | } | ||
25 | 23 | ||
26 | pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> { | 24 | // Query dependencies first. |
27 | let _p = profile("search_for_imports"); | 25 | let mut candidates: FxHashSet<_> = |
28 | let db = self.sema.db; | 26 | krate.query_external_importables(db, name_to_import).collect(); |
29 | 27 | ||
30 | // Query dependencies first. | 28 | // Query the local crate using the symbol index. |
31 | let mut candidates: FxHashSet<_> = | 29 | let local_results = { |
32 | self.krate.query_external_importables(db, name_to_import).collect(); | 30 | let mut query = Query::new(name_to_import.to_string()); |
31 | query.exact(); | ||
32 | query.limit(40); | ||
33 | symbol_index::crate_symbols(db, krate.into(), query) | ||
34 | }; | ||
33 | 35 | ||
34 | // Query the local crate using the symbol index. | 36 | candidates.extend( |
35 | let local_results = { | 37 | local_results |
36 | let mut query = Query::new(name_to_import.to_string()); | 38 | .into_iter() |
37 | query.exact(); | 39 | .filter_map(|import_candidate| get_name_definition(sema, &import_candidate)) |
38 | query.limit(40); | 40 | .filter_map(|name_definition_to_import| match name_definition_to_import { |
39 | symbol_index::crate_symbols(db, self.krate.into(), query) | 41 | Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), |
40 | }; | 42 | Definition::Macro(macro_def) => Some(Either::Right(macro_def)), |
43 | _ => None, | ||
44 | }), | ||
45 | ); | ||
41 | 46 | ||
42 | candidates.extend( | 47 | candidates.into_iter().collect() |
43 | local_results | 48 | } |
44 | .into_iter() | ||
45 | .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) | ||
46 | .filter_map(|name_definition_to_import| match name_definition_to_import { | ||
47 | Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), | ||
48 | Definition::Macro(macro_def) => Some(Either::Right(macro_def)), | ||
49 | _ => None, | ||
50 | }), | ||
51 | ); | ||
52 | |||
53 | candidates.into_iter().collect() | ||
54 | } | ||
55 | 49 | ||
56 | fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> { | 50 | fn get_name_definition<'a>( |
57 | let _p = profile("get_name_definition"); | 51 | sema: &Semantics<'a, RootDatabase>, |
58 | let file_id = import_candidate.file_id; | 52 | import_candidate: &FileSymbol, |
53 | ) -> Option<Definition> { | ||
54 | let _p = profile("get_name_definition"); | ||
55 | let file_id = import_candidate.file_id; | ||
59 | 56 | ||
60 | let candidate_node = import_candidate.ptr.to_node(self.sema.parse(file_id).syntax()); | 57 | let candidate_node = import_candidate.ptr.to_node(sema.parse(file_id).syntax()); |
61 | let candidate_name_node = if candidate_node.kind() != NAME { | 58 | let candidate_name_node = if candidate_node.kind() != NAME { |
62 | candidate_node.children().find(|it| it.kind() == NAME)? | 59 | candidate_node.children().find(|it| it.kind() == NAME)? |
63 | } else { | 60 | } else { |
64 | candidate_node | 61 | candidate_node |
65 | }; | 62 | }; |
66 | let name = ast::Name::cast(candidate_name_node)?; | 63 | let name = ast::Name::cast(candidate_name_node)?; |
67 | classify_name(&self.sema, &name)?.into_definition() | 64 | classify_name(sema, &name)?.into_definition() |
68 | } | ||
69 | } | 65 | } |
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index a808de4f1..c78071ad6 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs | |||
@@ -13,7 +13,7 @@ mod wasm_shims; | |||
13 | 13 | ||
14 | use std::sync::Arc; | 14 | use std::sync::Arc; |
15 | 15 | ||
16 | use hir::db::{AstDatabase, DefDatabase}; | 16 | use hir::db::{AstDatabase, DefDatabase, HirDatabase}; |
17 | use ra_db::{ | 17 | use ra_db::{ |
18 | salsa::{self, Database, Durability}, | 18 | salsa::{self, Database, Durability}, |
19 | Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, | 19 | Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, |
@@ -52,6 +52,12 @@ impl Upcast<dyn DefDatabase> for RootDatabase { | |||
52 | } | 52 | } |
53 | } | 53 | } |
54 | 54 | ||
55 | impl Upcast<dyn HirDatabase> for RootDatabase { | ||
56 | fn upcast(&self) -> &(dyn HirDatabase + 'static) { | ||
57 | &*self | ||
58 | } | ||
59 | } | ||
60 | |||
55 | impl FileLoader for RootDatabase { | 61 | impl FileLoader for RootDatabase { |
56 | fn file_text(&self, file_id: FileId) -> Arc<String> { | 62 | fn file_text(&self, file_id: FileId) -> Arc<String> { |
57 | FileLoaderDelegate(self).file_text(file_id) | 63 | FileLoaderDelegate(self).file_text(file_id) |
diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs index 44d5c35e6..81553150b 100644 --- a/crates/ra_ide_db/src/search.rs +++ b/crates/ra_ide_db/src/search.rs | |||
@@ -180,20 +180,20 @@ impl Definition { | |||
180 | 180 | ||
181 | pub fn find_usages( | 181 | pub fn find_usages( |
182 | &self, | 182 | &self, |
183 | db: &RootDatabase, | 183 | sema: &Semantics<RootDatabase>, |
184 | search_scope: Option<SearchScope>, | 184 | search_scope: Option<SearchScope>, |
185 | ) -> Vec<Reference> { | 185 | ) -> Vec<Reference> { |
186 | let _p = profile("Definition::find_usages"); | 186 | let _p = profile("Definition::find_usages"); |
187 | 187 | ||
188 | let search_scope = { | 188 | let search_scope = { |
189 | let base = self.search_scope(db); | 189 | let base = self.search_scope(sema.db); |
190 | match search_scope { | 190 | match search_scope { |
191 | None => base, | 191 | None => base, |
192 | Some(scope) => base.intersection(&scope), | 192 | Some(scope) => base.intersection(&scope), |
193 | } | 193 | } |
194 | }; | 194 | }; |
195 | 195 | ||
196 | let name = match self.name(db) { | 196 | let name = match self.name(sema.db) { |
197 | None => return Vec::new(), | 197 | None => return Vec::new(), |
198 | Some(it) => it.to_string(), | 198 | Some(it) => it.to_string(), |
199 | }; | 199 | }; |
@@ -202,11 +202,10 @@ impl Definition { | |||
202 | let mut refs = vec![]; | 202 | let mut refs = vec![]; |
203 | 203 | ||
204 | for (file_id, search_range) in search_scope { | 204 | for (file_id, search_range) in search_scope { |
205 | let text = db.file_text(file_id); | 205 | let text = sema.db.file_text(file_id); |
206 | let search_range = | 206 | let search_range = |
207 | search_range.unwrap_or(TextRange::up_to(TextSize::of(text.as_str()))); | 207 | search_range.unwrap_or(TextRange::up_to(TextSize::of(text.as_str()))); |
208 | 208 | ||
209 | let sema = Semantics::new(db); | ||
210 | let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); | 209 | let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); |
211 | 210 | ||
212 | for (idx, _) in text.match_indices(pat) { | 211 | for (idx, _) in text.match_indices(pat) { |
@@ -222,9 +221,6 @@ impl Definition { | |||
222 | continue; | 221 | continue; |
223 | }; | 222 | }; |
224 | 223 | ||
225 | // FIXME: reuse sb | ||
226 | // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 | ||
227 | |||
228 | match classify_name_ref(&sema, &name_ref) { | 224 | match classify_name_ref(&sema, &name_ref) { |
229 | Some(NameRefClass::Definition(def)) if &def == self => { | 225 | Some(NameRefClass::Definition(def)) if &def == self => { |
230 | let kind = if is_record_lit_name_ref(&name_ref) | 226 | let kind = if is_record_lit_name_ref(&name_ref) |
diff --git a/crates/ra_proc_macro_srv/src/tests/mod.rs b/crates/ra_proc_macro_srv/src/tests/mod.rs index 82cefbb29..8e6f28abd 100644 --- a/crates/ra_proc_macro_srv/src/tests/mod.rs +++ b/crates/ra_proc_macro_srv/src/tests/mod.rs | |||
@@ -11,7 +11,7 @@ fn test_derive_serialize_proc_macro() { | |||
11 | "serde_derive", | 11 | "serde_derive", |
12 | "Serialize", | 12 | "Serialize", |
13 | "1.0", | 13 | "1.0", |
14 | r##"struct Foo {}"##, | 14 | r"struct Foo {}", |
15 | include_str!("fixtures/test_serialize_proc_macro.txt"), | 15 | include_str!("fixtures/test_serialize_proc_macro.txt"), |
16 | ); | 16 | ); |
17 | } | 17 | } |
@@ -22,9 +22,7 @@ fn test_derive_serialize_proc_macro_failed() { | |||
22 | "serde_derive", | 22 | "serde_derive", |
23 | "Serialize", | 23 | "Serialize", |
24 | "1.0", | 24 | "1.0", |
25 | r##" | 25 | r"struct {}", |
26 | struct {} | ||
27 | "##, | ||
28 | r##" | 26 | r##" |
29 | SUBTREE $ | 27 | SUBTREE $ |
30 | IDENT compile_error 4294967295 | 28 | IDENT compile_error 4294967295 |
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs index 8d85f2d8a..dcb00671f 100644 --- a/crates/ra_proc_macro_srv/src/tests/utils.rs +++ b/crates/ra_proc_macro_srv/src/tests/utils.rs | |||
@@ -44,12 +44,12 @@ pub fn assert_expand( | |||
44 | crate_name: &str, | 44 | crate_name: &str, |
45 | macro_name: &str, | 45 | macro_name: &str, |
46 | version: &str, | 46 | version: &str, |
47 | fixture: &str, | 47 | ra_fixture: &str, |
48 | expect: &str, | 48 | expect: &str, |
49 | ) { | 49 | ) { |
50 | let path = fixtures::dylib_path(crate_name, version); | 50 | let path = fixtures::dylib_path(crate_name, version); |
51 | let expander = dylib::Expander::new(&path).unwrap(); | 51 | let expander = dylib::Expander::new(&path).unwrap(); |
52 | let fixture = parse_string(fixture).unwrap(); | 52 | let fixture = parse_string(ra_fixture).unwrap(); |
53 | 53 | ||
54 | let res = expander.expand(macro_name, &fixture.subtree, None).unwrap(); | 54 | let res = expander.expand(macro_name, &fixture.subtree, None).unwrap(); |
55 | assert_eq_text!(&format!("{:?}", res), &expect.trim()); | 55 | assert_eq_text!(&format!("{:?}", res), &expect.trim()); |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 3b124020d..47a1d393d 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -1,6 +1,11 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::{ffi::OsStr, ops, path::Path, process::Command}; | 3 | use std::{ |
4 | ffi::OsStr, | ||
5 | ops, | ||
6 | path::{Path, PathBuf}, | ||
7 | process::Command, | ||
8 | }; | ||
4 | 9 | ||
5 | use anyhow::{Context, Result}; | 10 | use anyhow::{Context, Result}; |
6 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; | 11 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; |
@@ -308,9 +313,13 @@ pub fn load_extern_resources( | |||
308 | if let Ok(message) = message { | 313 | if let Ok(message) = message { |
309 | match message { | 314 | match message { |
310 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { | 315 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { |
311 | let out_dir = AbsPathBuf::assert(out_dir); | 316 | // cargo_metadata crate returns default (empty) path for |
312 | res.out_dirs.insert(package_id.clone(), out_dir); | 317 | // older cargos, which is not absolute, so work around that. |
313 | res.cfgs.insert(package_id, cfgs); | 318 | if out_dir != PathBuf::default() { |
319 | let out_dir = AbsPathBuf::assert(out_dir); | ||
320 | res.out_dirs.insert(package_id.clone(), out_dir); | ||
321 | res.cfgs.insert(package_id, cfgs); | ||
322 | } | ||
314 | } | 323 | } |
315 | Message::CompilerArtifact(message) => { | 324 | Message::CompilerArtifact(message) => { |
316 | if message.target.kind.contains(&"proc-macro".to_string()) { | 325 | if message.target.kind.contains(&"proc-macro".to_string()) { |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 8b85b4831..8dbf4e6ea 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -288,10 +288,7 @@ impl ProjectWorkspace { | |||
288 | if let (Some(&from), Some(&to)) = | 288 | if let (Some(&from), Some(&to)) = |
289 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) | 289 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) |
290 | { | 290 | { |
291 | if crate_graph | 291 | if crate_graph.add_dep(from, dep.name.clone(), to).is_err() { |
292 | .add_dep(from, CrateName::new(&dep.name).unwrap(), to) | ||
293 | .is_err() | ||
294 | { | ||
295 | log::error!( | 292 | log::error!( |
296 | "cyclic dependency {:?} -> {:?}", | 293 | "cyclic dependency {:?} -> {:?}", |
297 | from_crate_id, | 294 | from_crate_id, |
@@ -312,13 +309,11 @@ impl ProjectWorkspace { | |||
312 | 309 | ||
313 | let env = Env::default(); | 310 | let env = Env::default(); |
314 | let proc_macro = vec![]; | 311 | let proc_macro = vec![]; |
315 | let crate_name = CrateName::new(&sysroot[krate].name) | 312 | let name = sysroot[krate].name.clone(); |
316 | .expect("Sysroot crate names should not contain dashes"); | ||
317 | |||
318 | let crate_id = crate_graph.add_crate_root( | 313 | let crate_id = crate_graph.add_crate_root( |
319 | file_id, | 314 | file_id, |
320 | Edition::Edition2018, | 315 | Edition::Edition2018, |
321 | Some(crate_name), | 316 | Some(name), |
322 | cfg_options.clone(), | 317 | cfg_options.clone(), |
323 | env, | 318 | env, |
324 | proc_macro, | 319 | proc_macro, |
@@ -392,7 +387,7 @@ impl ProjectWorkspace { | |||
392 | let crate_id = crate_graph.add_crate_root( | 387 | let crate_id = crate_graph.add_crate_root( |
393 | file_id, | 388 | file_id, |
394 | edition, | 389 | edition, |
395 | Some(CrateName::normalize_dashes(&cargo[pkg].name)), | 390 | Some(cargo[pkg].name.clone()), |
396 | cfg_options, | 391 | cfg_options, |
397 | env, | 392 | env, |
398 | proc_macro.clone(), | 393 | proc_macro.clone(), |
diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs index 4b5dcd634..b0fe09333 100644 --- a/crates/ra_project_model/src/project_json.rs +++ b/crates/ra_project_model/src/project_json.rs | |||
@@ -4,13 +4,13 @@ use std::path::PathBuf; | |||
4 | 4 | ||
5 | use paths::{AbsPath, AbsPathBuf}; | 5 | use paths::{AbsPath, AbsPathBuf}; |
6 | use ra_cfg::CfgOptions; | 6 | use ra_cfg::CfgOptions; |
7 | use ra_db::{CrateId, Dependency, Edition}; | 7 | use ra_db::{CrateId, CrateName, Dependency, Edition}; |
8 | use rustc_hash::FxHashSet; | 8 | use rustc_hash::FxHashSet; |
9 | use serde::Deserialize; | 9 | use serde::{de, Deserialize}; |
10 | use stdx::split_delim; | 10 | use stdx::split_delim; |
11 | 11 | ||
12 | /// Roots and crates that compose this Rust project. | 12 | /// Roots and crates that compose this Rust project. |
13 | #[derive(Clone, Debug)] | 13 | #[derive(Clone, Debug, Eq, PartialEq)] |
14 | pub struct ProjectJson { | 14 | pub struct ProjectJson { |
15 | pub(crate) roots: Vec<Root>, | 15 | pub(crate) roots: Vec<Root>, |
16 | pub(crate) crates: Vec<Crate>, | 16 | pub(crate) crates: Vec<Crate>, |
@@ -18,14 +18,14 @@ pub struct ProjectJson { | |||
18 | 18 | ||
19 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in | 19 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in |
20 | /// all roots. Roots might be nested. | 20 | /// all roots. Roots might be nested. |
21 | #[derive(Clone, Debug)] | 21 | #[derive(Clone, Debug, Eq, PartialEq)] |
22 | pub struct Root { | 22 | pub struct Root { |
23 | pub(crate) path: AbsPathBuf, | 23 | pub(crate) path: AbsPathBuf, |
24 | } | 24 | } |
25 | 25 | ||
26 | /// A crate points to the root module of a crate and lists the dependencies of the crate. This is | 26 | /// A crate points to the root module of a crate and lists the dependencies of the crate. This is |
27 | /// useful in creating the crate graph. | 27 | /// useful in creating the crate graph. |
28 | #[derive(Clone, Debug)] | 28 | #[derive(Clone, Debug, Eq, PartialEq)] |
29 | pub struct Crate { | 29 | pub struct Crate { |
30 | pub(crate) root_module: AbsPathBuf, | 30 | pub(crate) root_module: AbsPathBuf, |
31 | pub(crate) edition: Edition, | 31 | pub(crate) edition: Edition, |
@@ -50,7 +50,7 @@ impl ProjectJson { | |||
50 | .into_iter() | 50 | .into_iter() |
51 | .map(|dep_data| Dependency { | 51 | .map(|dep_data| Dependency { |
52 | crate_id: CrateId(dep_data.krate as u32), | 52 | crate_id: CrateId(dep_data.krate as u32), |
53 | name: dep_data.name.into(), | 53 | name: dep_data.name, |
54 | }) | 54 | }) |
55 | .collect::<Vec<_>>(), | 55 | .collect::<Vec<_>>(), |
56 | cfg: { | 56 | cfg: { |
@@ -113,5 +113,14 @@ struct DepData { | |||
113 | /// Identifies a crate by position in the crates array. | 113 | /// Identifies a crate by position in the crates array. |
114 | #[serde(rename = "crate")] | 114 | #[serde(rename = "crate")] |
115 | krate: usize, | 115 | krate: usize, |
116 | name: String, | 116 | #[serde(deserialize_with = "deserialize_crate_name")] |
117 | name: CrateName, | ||
118 | } | ||
119 | |||
120 | fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error> | ||
121 | where | ||
122 | D: de::Deserializer<'de>, | ||
123 | { | ||
124 | let name = String::deserialize(de)?; | ||
125 | CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {:?}", err))) | ||
117 | } | 126 | } |
diff --git a/crates/ra_ssr/Cargo.toml b/crates/ra_ssr/Cargo.toml index 3c2f15a83..fe098aaee 100644 --- a/crates/ra_ssr/Cargo.toml +++ b/crates/ra_ssr/Cargo.toml | |||
@@ -17,3 +17,4 @@ ra_db = { path = "../ra_db" } | |||
17 | ra_ide_db = { path = "../ra_ide_db" } | 17 | ra_ide_db = { path = "../ra_ide_db" } |
18 | hir = { path = "../ra_hir", package = "ra_hir" } | 18 | hir = { path = "../ra_hir", package = "ra_hir" } |
19 | rustc-hash = "1.1.0" | 19 | rustc-hash = "1.1.0" |
20 | test_utils = { path = "../test_utils" } | ||
diff --git a/crates/ra_ssr/src/lib.rs b/crates/ra_ssr/src/lib.rs index e148f4564..422e15ee6 100644 --- a/crates/ra_ssr/src/lib.rs +++ b/crates/ra_ssr/src/lib.rs | |||
@@ -9,10 +9,11 @@ mod replacing; | |||
9 | #[cfg(test)] | 9 | #[cfg(test)] |
10 | mod tests; | 10 | mod tests; |
11 | 11 | ||
12 | use crate::matching::Match; | 12 | pub use crate::matching::Match; |
13 | use crate::matching::{record_match_fails_reasons_scope, MatchFailureReason}; | ||
13 | use hir::Semantics; | 14 | use hir::Semantics; |
14 | use ra_db::{FileId, FileRange}; | 15 | use ra_db::{FileId, FileRange}; |
15 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxNode}; | 16 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, TextRange}; |
16 | use ra_text_edit::TextEdit; | 17 | use ra_text_edit::TextEdit; |
17 | use rustc_hash::FxHashMap; | 18 | use rustc_hash::FxHashMap; |
18 | 19 | ||
@@ -26,7 +27,7 @@ pub struct SsrRule { | |||
26 | } | 27 | } |
27 | 28 | ||
28 | #[derive(Debug)] | 29 | #[derive(Debug)] |
29 | struct SsrPattern { | 30 | pub struct SsrPattern { |
30 | raw: parsing::RawSearchPattern, | 31 | raw: parsing::RawSearchPattern, |
31 | /// Placeholders keyed by the stand-in ident that we use in Rust source code. | 32 | /// Placeholders keyed by the stand-in ident that we use in Rust source code. |
32 | placeholders_by_stand_in: FxHashMap<SmolStr, parsing::Placeholder>, | 33 | placeholders_by_stand_in: FxHashMap<SmolStr, parsing::Placeholder>, |
@@ -45,7 +46,7 @@ pub struct SsrError(String); | |||
45 | 46 | ||
46 | #[derive(Debug, Default)] | 47 | #[derive(Debug, Default)] |
47 | pub struct SsrMatches { | 48 | pub struct SsrMatches { |
48 | matches: Vec<Match>, | 49 | pub matches: Vec<Match>, |
49 | } | 50 | } |
50 | 51 | ||
51 | /// Searches a crate for pattern matches and possibly replaces them with something else. | 52 | /// Searches a crate for pattern matches and possibly replaces them with something else. |
@@ -64,6 +65,12 @@ impl<'db> MatchFinder<'db> { | |||
64 | self.rules.push(rule); | 65 | self.rules.push(rule); |
65 | } | 66 | } |
66 | 67 | ||
68 | /// Adds a search pattern. For use if you intend to only call `find_matches_in_file`. If you | ||
69 | /// intend to do replacement, use `add_rule` instead. | ||
70 | pub fn add_search_pattern(&mut self, pattern: SsrPattern) { | ||
71 | self.add_rule(SsrRule { pattern, template: "()".parse().unwrap() }) | ||
72 | } | ||
73 | |||
67 | pub fn edits_for_file(&self, file_id: FileId) -> Option<TextEdit> { | 74 | pub fn edits_for_file(&self, file_id: FileId) -> Option<TextEdit> { |
68 | let matches = self.find_matches_in_file(file_id); | 75 | let matches = self.find_matches_in_file(file_id); |
69 | if matches.matches.is_empty() { | 76 | if matches.matches.is_empty() { |
@@ -74,7 +81,7 @@ impl<'db> MatchFinder<'db> { | |||
74 | } | 81 | } |
75 | } | 82 | } |
76 | 83 | ||
77 | fn find_matches_in_file(&self, file_id: FileId) -> SsrMatches { | 84 | pub fn find_matches_in_file(&self, file_id: FileId) -> SsrMatches { |
78 | let file = self.sema.parse(file_id); | 85 | let file = self.sema.parse(file_id); |
79 | let code = file.syntax(); | 86 | let code = file.syntax(); |
80 | let mut matches = SsrMatches::default(); | 87 | let mut matches = SsrMatches::default(); |
@@ -82,6 +89,32 @@ impl<'db> MatchFinder<'db> { | |||
82 | matches | 89 | matches |
83 | } | 90 | } |
84 | 91 | ||
92 | /// Finds all nodes in `file_id` whose text is exactly equal to `snippet` and attempts to match | ||
93 | /// them, while recording reasons why they don't match. This API is useful for command | ||
94 | /// line-based debugging where providing a range is difficult. | ||
95 | pub fn debug_where_text_equal(&self, file_id: FileId, snippet: &str) -> Vec<MatchDebugInfo> { | ||
96 | use ra_db::SourceDatabaseExt; | ||
97 | let file = self.sema.parse(file_id); | ||
98 | let mut res = Vec::new(); | ||
99 | let file_text = self.sema.db.file_text(file_id); | ||
100 | let mut remaining_text = file_text.as_str(); | ||
101 | let mut base = 0; | ||
102 | let len = snippet.len() as u32; | ||
103 | while let Some(offset) = remaining_text.find(snippet) { | ||
104 | let start = base + offset as u32; | ||
105 | let end = start + len; | ||
106 | self.output_debug_for_nodes_at_range( | ||
107 | file.syntax(), | ||
108 | FileRange { file_id, range: TextRange::new(start.into(), end.into()) }, | ||
109 | &None, | ||
110 | &mut res, | ||
111 | ); | ||
112 | remaining_text = &remaining_text[offset + snippet.len()..]; | ||
113 | base = end; | ||
114 | } | ||
115 | res | ||
116 | } | ||
117 | |||
85 | fn find_matches( | 118 | fn find_matches( |
86 | &self, | 119 | &self, |
87 | code: &SyntaxNode, | 120 | code: &SyntaxNode, |
@@ -128,6 +161,59 @@ impl<'db> MatchFinder<'db> { | |||
128 | self.find_matches(&child, restrict_range, matches_out); | 161 | self.find_matches(&child, restrict_range, matches_out); |
129 | } | 162 | } |
130 | } | 163 | } |
164 | |||
165 | fn output_debug_for_nodes_at_range( | ||
166 | &self, | ||
167 | node: &SyntaxNode, | ||
168 | range: FileRange, | ||
169 | restrict_range: &Option<FileRange>, | ||
170 | out: &mut Vec<MatchDebugInfo>, | ||
171 | ) { | ||
172 | for node in node.children() { | ||
173 | let node_range = self.sema.original_range(&node); | ||
174 | if node_range.file_id != range.file_id || !node_range.range.contains_range(range.range) | ||
175 | { | ||
176 | continue; | ||
177 | } | ||
178 | if node_range.range == range.range { | ||
179 | for rule in &self.rules { | ||
180 | let pattern = | ||
181 | rule.pattern.tree_for_kind_with_reason(node.kind()).map(|p| p.clone()); | ||
182 | out.push(MatchDebugInfo { | ||
183 | matched: matching::get_match(true, rule, &node, restrict_range, &self.sema) | ||
184 | .map_err(|e| MatchFailureReason { | ||
185 | reason: e.reason.unwrap_or_else(|| { | ||
186 | "Match failed, but no reason was given".to_owned() | ||
187 | }), | ||
188 | }), | ||
189 | pattern, | ||
190 | node: node.clone(), | ||
191 | }); | ||
192 | } | ||
193 | } else if let Some(macro_call) = ast::MacroCall::cast(node.clone()) { | ||
194 | if let Some(expanded) = self.sema.expand(¯o_call) { | ||
195 | if let Some(tt) = macro_call.token_tree() { | ||
196 | self.output_debug_for_nodes_at_range( | ||
197 | &expanded, | ||
198 | range, | ||
199 | &Some(self.sema.original_range(tt.syntax())), | ||
200 | out, | ||
201 | ); | ||
202 | } | ||
203 | } | ||
204 | } else { | ||
205 | self.output_debug_for_nodes_at_range(&node, range, restrict_range, out); | ||
206 | } | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | |||
211 | pub struct MatchDebugInfo { | ||
212 | node: SyntaxNode, | ||
213 | /// Our search pattern parsed as the same kind of syntax node as `node`. e.g. expression, item, | ||
214 | /// etc. Will be absent if the pattern can't be parsed as that kind. | ||
215 | pattern: Result<SyntaxNode, MatchFailureReason>, | ||
216 | matched: Result<Match, MatchFailureReason>, | ||
131 | } | 217 | } |
132 | 218 | ||
133 | impl std::fmt::Display for SsrError { | 219 | impl std::fmt::Display for SsrError { |
@@ -136,4 +222,70 @@ impl std::fmt::Display for SsrError { | |||
136 | } | 222 | } |
137 | } | 223 | } |
138 | 224 | ||
225 | impl std::fmt::Debug for MatchDebugInfo { | ||
226 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
227 | write!(f, "========= PATTERN ==========\n")?; | ||
228 | match &self.pattern { | ||
229 | Ok(pattern) => { | ||
230 | write!(f, "{:#?}", pattern)?; | ||
231 | } | ||
232 | Err(err) => { | ||
233 | write!(f, "{}", err.reason)?; | ||
234 | } | ||
235 | } | ||
236 | write!( | ||
237 | f, | ||
238 | "\n============ AST ===========\n\ | ||
239 | {:#?}\n============================\n", | ||
240 | self.node | ||
241 | )?; | ||
242 | match &self.matched { | ||
243 | Ok(_) => write!(f, "Node matched")?, | ||
244 | Err(reason) => write!(f, "Node failed to match because: {}", reason.reason)?, | ||
245 | } | ||
246 | Ok(()) | ||
247 | } | ||
248 | } | ||
249 | |||
250 | impl SsrPattern { | ||
251 | fn tree_for_kind_with_reason( | ||
252 | &self, | ||
253 | kind: SyntaxKind, | ||
254 | ) -> Result<&SyntaxNode, MatchFailureReason> { | ||
255 | record_match_fails_reasons_scope(true, || self.tree_for_kind(kind)) | ||
256 | .map_err(|e| MatchFailureReason { reason: e.reason.unwrap() }) | ||
257 | } | ||
258 | } | ||
259 | |||
260 | impl SsrMatches { | ||
261 | /// Returns `self` with any nested matches removed and made into top-level matches. | ||
262 | pub fn flattened(self) -> SsrMatches { | ||
263 | let mut out = SsrMatches::default(); | ||
264 | self.flatten_into(&mut out); | ||
265 | out | ||
266 | } | ||
267 | |||
268 | fn flatten_into(self, out: &mut SsrMatches) { | ||
269 | for mut m in self.matches { | ||
270 | for p in m.placeholder_values.values_mut() { | ||
271 | std::mem::replace(&mut p.inner_matches, SsrMatches::default()).flatten_into(out); | ||
272 | } | ||
273 | out.matches.push(m); | ||
274 | } | ||
275 | } | ||
276 | } | ||
277 | |||
278 | impl Match { | ||
279 | pub fn matched_text(&self) -> String { | ||
280 | self.matched_node.text().to_string() | ||
281 | } | ||
282 | } | ||
283 | |||
139 | impl std::error::Error for SsrError {} | 284 | impl std::error::Error for SsrError {} |
285 | |||
286 | #[cfg(test)] | ||
287 | impl MatchDebugInfo { | ||
288 | pub(crate) fn match_failure_reason(&self) -> Option<&str> { | ||
289 | self.matched.as_ref().err().map(|r| r.reason.as_str()) | ||
290 | } | ||
291 | } | ||
diff --git a/crates/ra_ssr/src/matching.rs b/crates/ra_ssr/src/matching.rs index bb87bda43..ce53d46d2 100644 --- a/crates/ra_ssr/src/matching.rs +++ b/crates/ra_ssr/src/matching.rs | |||
@@ -2,17 +2,16 @@ | |||
2 | //! process of matching, placeholder values are recorded. | 2 | //! process of matching, placeholder values are recorded. |
3 | 3 | ||
4 | use crate::{ | 4 | use crate::{ |
5 | parsing::{Placeholder, SsrTemplate}, | 5 | parsing::{Constraint, NodeKind, Placeholder, SsrTemplate}, |
6 | SsrMatches, SsrPattern, SsrRule, | 6 | SsrMatches, SsrPattern, SsrRule, |
7 | }; | 7 | }; |
8 | use hir::Semantics; | 8 | use hir::Semantics; |
9 | use ra_db::FileRange; | 9 | use ra_db::FileRange; |
10 | use ra_syntax::ast::{AstNode, AstToken}; | 10 | use ra_syntax::ast::{AstNode, AstToken}; |
11 | use ra_syntax::{ | 11 | use ra_syntax::{ast, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken}; |
12 | ast, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, | ||
13 | }; | ||
14 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
15 | use std::{cell::Cell, iter::Peekable}; | 13 | use std::{cell::Cell, iter::Peekable}; |
14 | use test_utils::mark; | ||
16 | 15 | ||
17 | // Creates a match error. If we're currently attempting to match some code that we thought we were | 16 | // Creates a match error. If we're currently attempting to match some code that we thought we were |
18 | // going to match, as indicated by the --debug-snippet flag, then populate the reason field. | 17 | // going to match, as indicated by the --debug-snippet flag, then populate the reason field. |
@@ -44,8 +43,8 @@ macro_rules! fail_match { | |||
44 | 43 | ||
45 | /// Information about a match that was found. | 44 | /// Information about a match that was found. |
46 | #[derive(Debug)] | 45 | #[derive(Debug)] |
47 | pub(crate) struct Match { | 46 | pub struct Match { |
48 | pub(crate) range: TextRange, | 47 | pub(crate) range: FileRange, |
49 | pub(crate) matched_node: SyntaxNode, | 48 | pub(crate) matched_node: SyntaxNode, |
50 | pub(crate) placeholder_values: FxHashMap<Var, PlaceholderMatch>, | 49 | pub(crate) placeholder_values: FxHashMap<Var, PlaceholderMatch>, |
51 | pub(crate) ignored_comments: Vec<ast::Comment>, | 50 | pub(crate) ignored_comments: Vec<ast::Comment>, |
@@ -135,7 +134,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> { | |||
135 | match_state.attempt_match_node(&match_inputs, &pattern_tree, code)?; | 134 | match_state.attempt_match_node(&match_inputs, &pattern_tree, code)?; |
136 | match_state.validate_range(&sema.original_range(code))?; | 135 | match_state.validate_range(&sema.original_range(code))?; |
137 | match_state.match_out = Some(Match { | 136 | match_state.match_out = Some(Match { |
138 | range: sema.original_range(code).range, | 137 | range: sema.original_range(code), |
139 | matched_node: code.clone(), | 138 | matched_node: code.clone(), |
140 | placeholder_values: FxHashMap::default(), | 139 | placeholder_values: FxHashMap::default(), |
141 | ignored_comments: Vec::new(), | 140 | ignored_comments: Vec::new(), |
@@ -171,6 +170,9 @@ impl<'db, 'sema> MatchState<'db, 'sema> { | |||
171 | if let Some(placeholder) = | 170 | if let Some(placeholder) = |
172 | match_inputs.get_placeholder(&SyntaxElement::Node(pattern.clone())) | 171 | match_inputs.get_placeholder(&SyntaxElement::Node(pattern.clone())) |
173 | { | 172 | { |
173 | for constraint in &placeholder.constraints { | ||
174 | self.check_constraint(constraint, code)?; | ||
175 | } | ||
174 | if self.match_out.is_none() { | 176 | if self.match_out.is_none() { |
175 | return Ok(()); | 177 | return Ok(()); |
176 | } | 178 | } |
@@ -225,7 +227,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> { | |||
225 | match self.next_non_trivial(&mut code_it) { | 227 | match self.next_non_trivial(&mut code_it) { |
226 | None => { | 228 | None => { |
227 | if let Some(p) = pattern_it.next() { | 229 | if let Some(p) = pattern_it.next() { |
228 | fail_match!("Part of the pattern was unmached: {:?}", p); | 230 | fail_match!("Part of the pattern was unmatched: {:?}", p); |
229 | } | 231 | } |
230 | return Ok(()); | 232 | return Ok(()); |
231 | } | 233 | } |
@@ -294,6 +296,24 @@ impl<'db, 'sema> MatchState<'db, 'sema> { | |||
294 | Ok(()) | 296 | Ok(()) |
295 | } | 297 | } |
296 | 298 | ||
299 | fn check_constraint( | ||
300 | &self, | ||
301 | constraint: &Constraint, | ||
302 | code: &SyntaxNode, | ||
303 | ) -> Result<(), MatchFailed> { | ||
304 | match constraint { | ||
305 | Constraint::Kind(kind) => { | ||
306 | kind.matches(code)?; | ||
307 | } | ||
308 | Constraint::Not(sub) => { | ||
309 | if self.check_constraint(&*sub, code).is_ok() { | ||
310 | fail_match!("Constraint {:?} failed for '{}'", constraint, code.text()); | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | Ok(()) | ||
315 | } | ||
316 | |||
297 | /// We want to allow the records to match in any order, so we have special matching logic for | 317 | /// We want to allow the records to match in any order, so we have special matching logic for |
298 | /// them. | 318 | /// them. |
299 | fn attempt_match_record_field_list( | 319 | fn attempt_match_record_field_list( |
@@ -517,6 +537,21 @@ impl SsrPattern { | |||
517 | } | 537 | } |
518 | } | 538 | } |
519 | 539 | ||
540 | impl NodeKind { | ||
541 | fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> { | ||
542 | let ok = match self { | ||
543 | Self::Literal => { | ||
544 | mark::hit!(literal_constraint); | ||
545 | ast::Literal::can_cast(node.kind()) | ||
546 | } | ||
547 | }; | ||
548 | if !ok { | ||
549 | fail_match!("Code '{}' isn't of kind {:?}", node.text(), self); | ||
550 | } | ||
551 | Ok(()) | ||
552 | } | ||
553 | } | ||
554 | |||
520 | // If `node` contains nothing but an ident then return it, otherwise return None. | 555 | // If `node` contains nothing but an ident then return it, otherwise return None. |
521 | fn only_ident(element: SyntaxElement) -> Option<SyntaxToken> { | 556 | fn only_ident(element: SyntaxElement) -> Option<SyntaxToken> { |
522 | match element { | 557 | match element { |
diff --git a/crates/ra_ssr/src/parsing.rs b/crates/ra_ssr/src/parsing.rs index 1ae166d19..5ea125616 100644 --- a/crates/ra_ssr/src/parsing.rs +++ b/crates/ra_ssr/src/parsing.rs | |||
@@ -6,7 +6,7 @@ | |||
6 | //! e.g. expressions, type references etc. | 6 | //! e.g. expressions, type references etc. |
7 | 7 | ||
8 | use crate::{SsrError, SsrPattern, SsrRule}; | 8 | use crate::{SsrError, SsrPattern, SsrRule}; |
9 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind}; | 9 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, T}; |
10 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
11 | use std::str::FromStr; | 11 | use std::str::FromStr; |
12 | 12 | ||
@@ -39,6 +39,18 @@ pub(crate) struct Placeholder { | |||
39 | pub(crate) ident: SmolStr, | 39 | pub(crate) ident: SmolStr, |
40 | /// A unique name used in place of this placeholder when we parse the pattern as Rust code. | 40 | /// A unique name used in place of this placeholder when we parse the pattern as Rust code. |
41 | stand_in_name: String, | 41 | stand_in_name: String, |
42 | pub(crate) constraints: Vec<Constraint>, | ||
43 | } | ||
44 | |||
45 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
46 | pub(crate) enum Constraint { | ||
47 | Kind(NodeKind), | ||
48 | Not(Box<Constraint>), | ||
49 | } | ||
50 | |||
51 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
52 | pub(crate) enum NodeKind { | ||
53 | Literal, | ||
42 | } | 54 | } |
43 | 55 | ||
44 | #[derive(Debug, Clone, PartialEq, Eq)] | 56 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -55,7 +67,7 @@ impl FromStr for SsrRule { | |||
55 | let pattern = it.next().expect("at least empty string").trim(); | 67 | let pattern = it.next().expect("at least empty string").trim(); |
56 | let template = it | 68 | let template = it |
57 | .next() | 69 | .next() |
58 | .ok_or_else(|| SsrError("Cannot find delemiter `==>>`".into()))? | 70 | .ok_or_else(|| SsrError("Cannot find delimiter `==>>`".into()))? |
59 | .trim() | 71 | .trim() |
60 | .to_string(); | 72 | .to_string(); |
61 | if it.next().is_some() { | 73 | if it.next().is_some() { |
@@ -149,7 +161,7 @@ fn parse_pattern(pattern_str: &str) -> Result<Vec<PatternElement>, SsrError> { | |||
149 | let mut placeholder_names = FxHashSet::default(); | 161 | let mut placeholder_names = FxHashSet::default(); |
150 | let mut tokens = tokenize(pattern_str)?.into_iter(); | 162 | let mut tokens = tokenize(pattern_str)?.into_iter(); |
151 | while let Some(token) = tokens.next() { | 163 | while let Some(token) = tokens.next() { |
152 | if token.kind == SyntaxKind::DOLLAR { | 164 | if token.kind == T![$] { |
153 | let placeholder = parse_placeholder(&mut tokens)?; | 165 | let placeholder = parse_placeholder(&mut tokens)?; |
154 | if !placeholder_names.insert(placeholder.ident.clone()) { | 166 | if !placeholder_names.insert(placeholder.ident.clone()) { |
155 | bail!("Name `{}` repeats more than once", placeholder.ident); | 167 | bail!("Name `{}` repeats more than once", placeholder.ident); |
@@ -177,6 +189,9 @@ fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> { | |||
177 | if !defined_placeholders.contains(&placeholder.ident) { | 189 | if !defined_placeholders.contains(&placeholder.ident) { |
178 | undefined.push(format!("${}", placeholder.ident)); | 190 | undefined.push(format!("${}", placeholder.ident)); |
179 | } | 191 | } |
192 | if !placeholder.constraints.is_empty() { | ||
193 | bail!("Replacement placeholders cannot have constraints"); | ||
194 | } | ||
180 | } | 195 | } |
181 | } | 196 | } |
182 | if !undefined.is_empty() { | 197 | if !undefined.is_empty() { |
@@ -205,23 +220,90 @@ fn tokenize(source: &str) -> Result<Vec<Token>, SsrError> { | |||
205 | 220 | ||
206 | fn parse_placeholder(tokens: &mut std::vec::IntoIter<Token>) -> Result<Placeholder, SsrError> { | 221 | fn parse_placeholder(tokens: &mut std::vec::IntoIter<Token>) -> Result<Placeholder, SsrError> { |
207 | let mut name = None; | 222 | let mut name = None; |
223 | let mut constraints = Vec::new(); | ||
208 | if let Some(token) = tokens.next() { | 224 | if let Some(token) = tokens.next() { |
209 | match token.kind { | 225 | match token.kind { |
210 | SyntaxKind::IDENT => { | 226 | SyntaxKind::IDENT => { |
211 | name = Some(token.text); | 227 | name = Some(token.text); |
212 | } | 228 | } |
229 | T!['{'] => { | ||
230 | let token = | ||
231 | tokens.next().ok_or_else(|| SsrError::new("Unexpected end of placeholder"))?; | ||
232 | if token.kind == SyntaxKind::IDENT { | ||
233 | name = Some(token.text); | ||
234 | } | ||
235 | loop { | ||
236 | let token = tokens | ||
237 | .next() | ||
238 | .ok_or_else(|| SsrError::new("Placeholder is missing closing brace '}'"))?; | ||
239 | match token.kind { | ||
240 | T![:] => { | ||
241 | constraints.push(parse_constraint(tokens)?); | ||
242 | } | ||
243 | T!['}'] => break, | ||
244 | _ => bail!("Unexpected token while parsing placeholder: '{}'", token.text), | ||
245 | } | ||
246 | } | ||
247 | } | ||
213 | _ => { | 248 | _ => { |
214 | bail!("Placeholders should be $name"); | 249 | bail!("Placeholders should either be $name or ${name:constraints}"); |
215 | } | 250 | } |
216 | } | 251 | } |
217 | } | 252 | } |
218 | let name = name.ok_or_else(|| SsrError::new("Placeholder ($) with no name"))?; | 253 | let name = name.ok_or_else(|| SsrError::new("Placeholder ($) with no name"))?; |
219 | Ok(Placeholder::new(name)) | 254 | Ok(Placeholder::new(name, constraints)) |
255 | } | ||
256 | |||
257 | fn parse_constraint(tokens: &mut std::vec::IntoIter<Token>) -> Result<Constraint, SsrError> { | ||
258 | let constraint_type = tokens | ||
259 | .next() | ||
260 | .ok_or_else(|| SsrError::new("Found end of placeholder while looking for a constraint"))? | ||
261 | .text | ||
262 | .to_string(); | ||
263 | match constraint_type.as_str() { | ||
264 | "kind" => { | ||
265 | expect_token(tokens, "(")?; | ||
266 | let t = tokens.next().ok_or_else(|| { | ||
267 | SsrError::new("Unexpected end of constraint while looking for kind") | ||
268 | })?; | ||
269 | if t.kind != SyntaxKind::IDENT { | ||
270 | bail!("Expected ident, found {:?} while parsing kind constraint", t.kind); | ||
271 | } | ||
272 | expect_token(tokens, ")")?; | ||
273 | Ok(Constraint::Kind(NodeKind::from(&t.text)?)) | ||
274 | } | ||
275 | "not" => { | ||
276 | expect_token(tokens, "(")?; | ||
277 | let sub = parse_constraint(tokens)?; | ||
278 | expect_token(tokens, ")")?; | ||
279 | Ok(Constraint::Not(Box::new(sub))) | ||
280 | } | ||
281 | x => bail!("Unsupported constraint type '{}'", x), | ||
282 | } | ||
283 | } | ||
284 | |||
285 | fn expect_token(tokens: &mut std::vec::IntoIter<Token>, expected: &str) -> Result<(), SsrError> { | ||
286 | if let Some(t) = tokens.next() { | ||
287 | if t.text == expected { | ||
288 | return Ok(()); | ||
289 | } | ||
290 | bail!("Expected {} found {}", expected, t.text); | ||
291 | } | ||
292 | bail!("Expected {} found end of stream"); | ||
293 | } | ||
294 | |||
295 | impl NodeKind { | ||
296 | fn from(name: &SmolStr) -> Result<NodeKind, SsrError> { | ||
297 | Ok(match name.as_str() { | ||
298 | "literal" => NodeKind::Literal, | ||
299 | _ => bail!("Unknown node kind '{}'", name), | ||
300 | }) | ||
301 | } | ||
220 | } | 302 | } |
221 | 303 | ||
222 | impl Placeholder { | 304 | impl Placeholder { |
223 | fn new(name: SmolStr) -> Self { | 305 | fn new(name: SmolStr, constraints: Vec<Constraint>) -> Self { |
224 | Self { stand_in_name: format!("__placeholder_{}", name), ident: name } | 306 | Self { stand_in_name: format!("__placeholder_{}", name), constraints, ident: name } |
225 | } | 307 | } |
226 | } | 308 | } |
227 | 309 | ||
@@ -241,31 +323,31 @@ mod tests { | |||
241 | PatternElement::Token(Token { kind, text: SmolStr::new(text) }) | 323 | PatternElement::Token(Token { kind, text: SmolStr::new(text) }) |
242 | } | 324 | } |
243 | fn placeholder(name: &str) -> PatternElement { | 325 | fn placeholder(name: &str) -> PatternElement { |
244 | PatternElement::Placeholder(Placeholder::new(SmolStr::new(name))) | 326 | PatternElement::Placeholder(Placeholder::new(SmolStr::new(name), Vec::new())) |
245 | } | 327 | } |
246 | let result: SsrRule = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap(); | 328 | let result: SsrRule = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap(); |
247 | assert_eq!( | 329 | assert_eq!( |
248 | result.pattern.raw.tokens, | 330 | result.pattern.raw.tokens, |
249 | vec![ | 331 | vec![ |
250 | token(SyntaxKind::IDENT, "foo"), | 332 | token(SyntaxKind::IDENT, "foo"), |
251 | token(SyntaxKind::L_PAREN, "("), | 333 | token(T!['('], "("), |
252 | placeholder("a"), | 334 | placeholder("a"), |
253 | token(SyntaxKind::COMMA, ","), | 335 | token(T![,], ","), |
254 | token(SyntaxKind::WHITESPACE, " "), | 336 | token(SyntaxKind::WHITESPACE, " "), |
255 | placeholder("b"), | 337 | placeholder("b"), |
256 | token(SyntaxKind::R_PAREN, ")"), | 338 | token(T![')'], ")"), |
257 | ] | 339 | ] |
258 | ); | 340 | ); |
259 | assert_eq!( | 341 | assert_eq!( |
260 | result.template.tokens, | 342 | result.template.tokens, |
261 | vec![ | 343 | vec![ |
262 | token(SyntaxKind::IDENT, "bar"), | 344 | token(SyntaxKind::IDENT, "bar"), |
263 | token(SyntaxKind::L_PAREN, "("), | 345 | token(T!['('], "("), |
264 | placeholder("b"), | 346 | placeholder("b"), |
265 | token(SyntaxKind::COMMA, ","), | 347 | token(T![,], ","), |
266 | token(SyntaxKind::WHITESPACE, " "), | 348 | token(SyntaxKind::WHITESPACE, " "), |
267 | placeholder("a"), | 349 | placeholder("a"), |
268 | token(SyntaxKind::R_PAREN, ")"), | 350 | token(T![')'], ")"), |
269 | ] | 351 | ] |
270 | ); | 352 | ); |
271 | } | 353 | } |
diff --git a/crates/ra_ssr/src/replacing.rs b/crates/ra_ssr/src/replacing.rs index 70ce1c185..e43cc5167 100644 --- a/crates/ra_ssr/src/replacing.rs +++ b/crates/ra_ssr/src/replacing.rs | |||
@@ -21,8 +21,10 @@ fn matches_to_edit_at_offset( | |||
21 | ) -> TextEdit { | 21 | ) -> TextEdit { |
22 | let mut edit_builder = ra_text_edit::TextEditBuilder::default(); | 22 | let mut edit_builder = ra_text_edit::TextEditBuilder::default(); |
23 | for m in &matches.matches { | 23 | for m in &matches.matches { |
24 | edit_builder | 24 | edit_builder.replace( |
25 | .replace(m.range.checked_sub(relative_start).unwrap(), render_replace(m, file_src)); | 25 | m.range.range.checked_sub(relative_start).unwrap(), |
26 | render_replace(m, file_src), | ||
27 | ); | ||
26 | } | 28 | } |
27 | edit_builder.finish() | 29 | edit_builder.finish() |
28 | } | 30 | } |
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs index 8be60c293..9568d4432 100644 --- a/crates/ra_ssr/src/tests.rs +++ b/crates/ra_ssr/src/tests.rs | |||
@@ -1,150 +1,6 @@ | |||
1 | use crate::matching::MatchFailureReason; | 1 | use crate::{MatchFinder, SsrRule}; |
2 | use crate::{matching, Match, MatchFinder, SsrMatches, SsrPattern, SsrRule}; | 2 | use ra_db::{FileId, SourceDatabaseExt}; |
3 | use matching::record_match_fails_reasons_scope; | 3 | use test_utils::mark; |
4 | use ra_db::{FileId, FileRange, SourceDatabaseExt}; | ||
5 | use ra_syntax::ast::AstNode; | ||
6 | use ra_syntax::{ast, SyntaxKind, SyntaxNode, TextRange}; | ||
7 | |||
8 | struct MatchDebugInfo { | ||
9 | node: SyntaxNode, | ||
10 | /// Our search pattern parsed as the same kind of syntax node as `node`. e.g. expression, item, | ||
11 | /// etc. Will be absent if the pattern can't be parsed as that kind. | ||
12 | pattern: Result<SyntaxNode, MatchFailureReason>, | ||
13 | matched: Result<Match, MatchFailureReason>, | ||
14 | } | ||
15 | |||
16 | impl SsrPattern { | ||
17 | pub(crate) fn tree_for_kind_with_reason( | ||
18 | &self, | ||
19 | kind: SyntaxKind, | ||
20 | ) -> Result<&SyntaxNode, MatchFailureReason> { | ||
21 | record_match_fails_reasons_scope(true, || self.tree_for_kind(kind)) | ||
22 | .map_err(|e| MatchFailureReason { reason: e.reason.unwrap() }) | ||
23 | } | ||
24 | } | ||
25 | |||
26 | impl std::fmt::Debug for MatchDebugInfo { | ||
27 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
28 | write!(f, "========= PATTERN ==========\n")?; | ||
29 | match &self.pattern { | ||
30 | Ok(pattern) => { | ||
31 | write!(f, "{:#?}", pattern)?; | ||
32 | } | ||
33 | Err(err) => { | ||
34 | write!(f, "{}", err.reason)?; | ||
35 | } | ||
36 | } | ||
37 | write!( | ||
38 | f, | ||
39 | "\n============ AST ===========\n\ | ||
40 | {:#?}\n============================", | ||
41 | self.node | ||
42 | )?; | ||
43 | match &self.matched { | ||
44 | Ok(_) => write!(f, "Node matched")?, | ||
45 | Err(reason) => write!(f, "Node failed to match because: {}", reason.reason)?, | ||
46 | } | ||
47 | Ok(()) | ||
48 | } | ||
49 | } | ||
50 | |||
51 | impl SsrMatches { | ||
52 | /// Returns `self` with any nested matches removed and made into top-level matches. | ||
53 | pub(crate) fn flattened(self) -> SsrMatches { | ||
54 | let mut out = SsrMatches::default(); | ||
55 | self.flatten_into(&mut out); | ||
56 | out | ||
57 | } | ||
58 | |||
59 | fn flatten_into(self, out: &mut SsrMatches) { | ||
60 | for mut m in self.matches { | ||
61 | for p in m.placeholder_values.values_mut() { | ||
62 | std::mem::replace(&mut p.inner_matches, SsrMatches::default()).flatten_into(out); | ||
63 | } | ||
64 | out.matches.push(m); | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | impl Match { | ||
70 | pub(crate) fn matched_text(&self) -> String { | ||
71 | self.matched_node.text().to_string() | ||
72 | } | ||
73 | } | ||
74 | |||
75 | impl<'db> MatchFinder<'db> { | ||
76 | /// Adds a search pattern. For use if you intend to only call `find_matches_in_file`. If you | ||
77 | /// intend to do replacement, use `add_rule` instead. | ||
78 | fn add_search_pattern(&mut self, pattern: SsrPattern) { | ||
79 | self.add_rule(SsrRule { pattern, template: "()".parse().unwrap() }) | ||
80 | } | ||
81 | |||
82 | /// Finds all nodes in `file_id` whose text is exactly equal to `snippet` and attempts to match | ||
83 | /// them, while recording reasons why they don't match. This API is useful for command | ||
84 | /// line-based debugging where providing a range is difficult. | ||
85 | fn debug_where_text_equal(&self, file_id: FileId, snippet: &str) -> Vec<MatchDebugInfo> { | ||
86 | let file = self.sema.parse(file_id); | ||
87 | let mut res = Vec::new(); | ||
88 | let file_text = self.sema.db.file_text(file_id); | ||
89 | let mut remaining_text = file_text.as_str(); | ||
90 | let mut base = 0; | ||
91 | let len = snippet.len() as u32; | ||
92 | while let Some(offset) = remaining_text.find(snippet) { | ||
93 | let start = base + offset as u32; | ||
94 | let end = start + len; | ||
95 | self.output_debug_for_nodes_at_range( | ||
96 | file.syntax(), | ||
97 | TextRange::new(start.into(), end.into()), | ||
98 | &None, | ||
99 | &mut res, | ||
100 | ); | ||
101 | remaining_text = &remaining_text[offset + snippet.len()..]; | ||
102 | base = end; | ||
103 | } | ||
104 | res | ||
105 | } | ||
106 | |||
107 | fn output_debug_for_nodes_at_range( | ||
108 | &self, | ||
109 | node: &SyntaxNode, | ||
110 | range: TextRange, | ||
111 | restrict_range: &Option<FileRange>, | ||
112 | out: &mut Vec<MatchDebugInfo>, | ||
113 | ) { | ||
114 | for node in node.children() { | ||
115 | if !node.text_range().contains_range(range) { | ||
116 | continue; | ||
117 | } | ||
118 | if node.text_range() == range { | ||
119 | for rule in &self.rules { | ||
120 | let pattern = | ||
121 | rule.pattern.tree_for_kind_with_reason(node.kind()).map(|p| p.clone()); | ||
122 | out.push(MatchDebugInfo { | ||
123 | matched: matching::get_match(true, rule, &node, restrict_range, &self.sema) | ||
124 | .map_err(|e| MatchFailureReason { | ||
125 | reason: e.reason.unwrap_or_else(|| { | ||
126 | "Match failed, but no reason was given".to_owned() | ||
127 | }), | ||
128 | }), | ||
129 | pattern, | ||
130 | node: node.clone(), | ||
131 | }); | ||
132 | } | ||
133 | } else if let Some(macro_call) = ast::MacroCall::cast(node.clone()) { | ||
134 | if let Some(expanded) = self.sema.expand(¯o_call) { | ||
135 | if let Some(tt) = macro_call.token_tree() { | ||
136 | self.output_debug_for_nodes_at_range( | ||
137 | &expanded, | ||
138 | range, | ||
139 | &Some(self.sema.original_range(tt.syntax())), | ||
140 | out, | ||
141 | ); | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | 4 | ||
149 | fn parse_error_text(query: &str) -> String { | 5 | fn parse_error_text(query: &str) -> String { |
150 | format!("{}", query.parse::<SsrRule>().unwrap_err()) | 6 | format!("{}", query.parse::<SsrRule>().unwrap_err()) |
@@ -152,12 +8,12 @@ fn parse_error_text(query: &str) -> String { | |||
152 | 8 | ||
153 | #[test] | 9 | #[test] |
154 | fn parser_empty_query() { | 10 | fn parser_empty_query() { |
155 | assert_eq!(parse_error_text(""), "Parse error: Cannot find delemiter `==>>`"); | 11 | assert_eq!(parse_error_text(""), "Parse error: Cannot find delimiter `==>>`"); |
156 | } | 12 | } |
157 | 13 | ||
158 | #[test] | 14 | #[test] |
159 | fn parser_no_delimiter() { | 15 | fn parser_no_delimiter() { |
160 | assert_eq!(parse_error_text("foo()"), "Parse error: Cannot find delemiter `==>>`"); | 16 | assert_eq!(parse_error_text("foo()"), "Parse error: Cannot find delimiter `==>>`"); |
161 | } | 17 | } |
162 | 18 | ||
163 | #[test] | 19 | #[test] |
@@ -227,7 +83,7 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, result: &str) { | |||
227 | let mut after = db.file_text(file_id).to_string(); | 83 | let mut after = db.file_text(file_id).to_string(); |
228 | edits.apply(&mut after); | 84 | edits.apply(&mut after); |
229 | // Likewise, we need to make sure that whatever transformations fixture parsing applies, | 85 | // Likewise, we need to make sure that whatever transformations fixture parsing applies, |
230 | // also get appplied to our expected result. | 86 | // also get applied to our expected result. |
231 | let result = normalize_code(result); | 87 | let result = normalize_code(result); |
232 | assert_eq!(after, result); | 88 | assert_eq!(after, result); |
233 | } else { | 89 | } else { |
@@ -260,6 +116,19 @@ fn assert_no_match(pattern: &str, code: &str) { | |||
260 | assert_matches(pattern, code, &[]); | 116 | assert_matches(pattern, code, &[]); |
261 | } | 117 | } |
262 | 118 | ||
119 | fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) { | ||
120 | let (db, file_id) = single_file(code); | ||
121 | let mut match_finder = MatchFinder::new(&db); | ||
122 | match_finder.add_search_pattern(pattern.parse().unwrap()); | ||
123 | let mut reasons = Vec::new(); | ||
124 | for d in match_finder.debug_where_text_equal(file_id, snippet) { | ||
125 | if let Some(reason) = d.match_failure_reason() { | ||
126 | reasons.push(reason.to_owned()); | ||
127 | } | ||
128 | } | ||
129 | assert_eq!(reasons, vec![expected_reason]); | ||
130 | } | ||
131 | |||
263 | #[test] | 132 | #[test] |
264 | fn ssr_function_to_method() { | 133 | fn ssr_function_to_method() { |
265 | assert_ssr_transform( | 134 | assert_ssr_transform( |
@@ -434,6 +303,22 @@ fn match_pattern() { | |||
434 | } | 303 | } |
435 | 304 | ||
436 | #[test] | 305 | #[test] |
306 | fn literal_constraint() { | ||
307 | mark::check!(literal_constraint); | ||
308 | let code = r#" | ||
309 | fn f1() { | ||
310 | let x1 = Some(42); | ||
311 | let x2 = Some("foo"); | ||
312 | let x3 = Some(x1); | ||
313 | let x4 = Some(40 + 2); | ||
314 | let x5 = Some(true); | ||
315 | } | ||
316 | "#; | ||
317 | assert_matches("Some(${a:kind(literal)})", code, &["Some(42)", "Some(\"foo\")", "Some(true)"]); | ||
318 | assert_matches("Some(${a:not(kind(literal))})", code, &["Some(x1)", "Some(40 + 2)"]); | ||
319 | } | ||
320 | |||
321 | #[test] | ||
437 | fn match_reordered_struct_instantiation() { | 322 | fn match_reordered_struct_instantiation() { |
438 | assert_matches( | 323 | assert_matches( |
439 | "Foo {aa: 1, b: 2, ccc: 3}", | 324 | "Foo {aa: 1, b: 2, ccc: 3}", |
@@ -623,3 +508,30 @@ fn preserves_whitespace_within_macro_expansion() { | |||
623 | fn f() {macro1!(4 - 3 - 1 * 2}"#, | 508 | fn f() {macro1!(4 - 3 - 1 * 2}"#, |
624 | ) | 509 | ) |
625 | } | 510 | } |
511 | |||
512 | #[test] | ||
513 | fn match_failure_reasons() { | ||
514 | let code = r#" | ||
515 | macro_rules! foo { | ||
516 | ($a:expr) => { | ||
517 | 1 + $a + 2 | ||
518 | }; | ||
519 | } | ||
520 | fn f1() { | ||
521 | bar(1, 2); | ||
522 | foo!(5 + 43.to_string() + 5); | ||
523 | } | ||
524 | "#; | ||
525 | assert_match_failure_reason( | ||
526 | "bar($a, 3)", | ||
527 | code, | ||
528 | "bar(1, 2)", | ||
529 | r#"Pattern wanted token '3' (INT_NUMBER), but code had token '2' (INT_NUMBER)"#, | ||
530 | ); | ||
531 | assert_match_failure_reason( | ||
532 | "42.to_string()", | ||
533 | code, | ||
534 | "43.to_string()", | ||
535 | r#"Pattern wanted token '42' (INT_NUMBER), but code had token '43' (INT_NUMBER)"#, | ||
536 | ); | ||
537 | } | ||
diff --git a/crates/ra_syntax/src/tests.rs b/crates/ra_syntax/src/tests.rs index 959967b79..7b4232497 100644 --- a/crates/ra_syntax/src/tests.rs +++ b/crates/ra_syntax/src/tests.rs | |||
@@ -1,9 +1,11 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | env, | ||
2 | fmt::Write, | 3 | fmt::Write, |
4 | fs, | ||
3 | path::{Component, Path, PathBuf}, | 5 | path::{Component, Path, PathBuf}, |
4 | }; | 6 | }; |
5 | 7 | ||
6 | use test_utils::{collect_rust_files, dir_tests, project_dir, read_text}; | 8 | use test_utils::{assert_eq_text, project_dir}; |
7 | 9 | ||
8 | use crate::{fuzz, tokenize, SourceFile, SyntaxError, TextRange, TextSize, Token}; | 10 | use crate::{fuzz, tokenize, SourceFile, SyntaxError, TextRange, TextSize, Token}; |
9 | 11 | ||
@@ -200,3 +202,99 @@ where | |||
200 | } | 202 | } |
201 | }); | 203 | }); |
202 | } | 204 | } |
205 | |||
206 | /// Calls callback `f` with input code and file paths for each `.rs` file in `test_data_dir` | ||
207 | /// subdirectories defined by `paths`. | ||
208 | /// | ||
209 | /// If the content of the matching output file differs from the output of `f()` | ||
210 | /// the test will fail. | ||
211 | /// | ||
212 | /// If there is no matching output file it will be created and filled with the | ||
213 | /// output of `f()`, but the test will fail. | ||
214 | fn dir_tests<F>(test_data_dir: &Path, paths: &[&str], outfile_extension: &str, f: F) | ||
215 | where | ||
216 | F: Fn(&str, &Path) -> String, | ||
217 | { | ||
218 | for (path, input_code) in collect_rust_files(test_data_dir, paths) { | ||
219 | let actual = f(&input_code, &path); | ||
220 | let path = path.with_extension(outfile_extension); | ||
221 | if !path.exists() { | ||
222 | println!("\nfile: {}", path.display()); | ||
223 | println!("No .txt file with expected result, creating...\n"); | ||
224 | println!("{}\n{}", input_code, actual); | ||
225 | fs::write(&path, &actual).unwrap(); | ||
226 | panic!("No expected result"); | ||
227 | } | ||
228 | let expected = read_text(&path); | ||
229 | assert_equal_text(&expected, &actual, &path); | ||
230 | } | ||
231 | } | ||
232 | |||
233 | /// Collects all `.rs` files from `dir` subdirectories defined by `paths`. | ||
234 | fn collect_rust_files(root_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)> { | ||
235 | paths | ||
236 | .iter() | ||
237 | .flat_map(|path| { | ||
238 | let path = root_dir.to_owned().join(path); | ||
239 | rust_files_in_dir(&path).into_iter() | ||
240 | }) | ||
241 | .map(|path| { | ||
242 | let text = read_text(&path); | ||
243 | (path, text) | ||
244 | }) | ||
245 | .collect() | ||
246 | } | ||
247 | |||
248 | /// Collects paths to all `.rs` files from `dir` in a sorted `Vec<PathBuf>`. | ||
249 | fn rust_files_in_dir(dir: &Path) -> Vec<PathBuf> { | ||
250 | let mut acc = Vec::new(); | ||
251 | for file in fs::read_dir(&dir).unwrap() { | ||
252 | let file = file.unwrap(); | ||
253 | let path = file.path(); | ||
254 | if path.extension().unwrap_or_default() == "rs" { | ||
255 | acc.push(path); | ||
256 | } | ||
257 | } | ||
258 | acc.sort(); | ||
259 | acc | ||
260 | } | ||
261 | |||
262 | /// Asserts that `expected` and `actual` strings are equal. If they differ only | ||
263 | /// in trailing or leading whitespace the test won't fail and | ||
264 | /// the contents of `actual` will be written to the file located at `path`. | ||
265 | fn assert_equal_text(expected: &str, actual: &str, path: &Path) { | ||
266 | if expected == actual { | ||
267 | return; | ||
268 | } | ||
269 | let dir = project_dir(); | ||
270 | let pretty_path = path.strip_prefix(&dir).unwrap_or_else(|_| path); | ||
271 | if expected.trim() == actual.trim() { | ||
272 | println!("whitespace difference, rewriting"); | ||
273 | println!("file: {}\n", pretty_path.display()); | ||
274 | fs::write(path, actual).unwrap(); | ||
275 | return; | ||
276 | } | ||
277 | if env::var("UPDATE_EXPECT").is_ok() { | ||
278 | println!("rewriting {}", pretty_path.display()); | ||
279 | fs::write(path, actual).unwrap(); | ||
280 | return; | ||
281 | } | ||
282 | assert_eq_text!(expected, actual, "file: {}", pretty_path.display()); | ||
283 | } | ||
284 | |||
285 | /// Read file and normalize newlines. | ||
286 | /// | ||
287 | /// `rustc` seems to always normalize `\r\n` newlines to `\n`: | ||
288 | /// | ||
289 | /// ``` | ||
290 | /// let s = " | ||
291 | /// "; | ||
292 | /// assert_eq!(s.as_bytes(), &[10]); | ||
293 | /// ``` | ||
294 | /// | ||
295 | /// so this should always be correct. | ||
296 | fn read_text(path: &Path) -> String { | ||
297 | fs::read_to_string(path) | ||
298 | .unwrap_or_else(|_| panic!("File at {:?} should be valid", path)) | ||
299 | .replace("\r\n", "\n") | ||
300 | } | ||
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 53621fb44..837b6714d 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -28,6 +28,7 @@ rustc-hash = "1.1.0" | |||
28 | serde = { version = "1.0.106", features = ["derive"] } | 28 | serde = { version = "1.0.106", features = ["derive"] } |
29 | serde_json = "1.0.48" | 29 | serde_json = "1.0.48" |
30 | threadpool = "1.7.1" | 30 | threadpool = "1.7.1" |
31 | rayon = "1.3.1" | ||
31 | 32 | ||
32 | stdx = { path = "../stdx" } | 33 | stdx = { path = "../stdx" } |
33 | 34 | ||
@@ -45,6 +46,8 @@ ra_toolchain = { path = "../ra_toolchain" } | |||
45 | 46 | ||
46 | # This should only be used in CLI | 47 | # This should only be used in CLI |
47 | ra_db = { path = "../ra_db" } | 48 | ra_db = { path = "../ra_db" } |
49 | ra_ide_db = { path = "../ra_ide_db" } | ||
50 | ra_ssr = { path = "../ra_ssr" } | ||
48 | hir = { path = "../ra_hir", package = "ra_hir" } | 51 | hir = { path = "../ra_hir", package = "ra_hir" } |
49 | hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } | 52 | hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } |
50 | hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } | 53 | hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } |
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs index 8e3ca9343..8c0f4df8b 100644 --- a/crates/rust-analyzer/src/bin/args.rs +++ b/crates/rust-analyzer/src/bin/args.rs | |||
@@ -5,6 +5,7 @@ | |||
5 | 5 | ||
6 | use anyhow::{bail, Result}; | 6 | use anyhow::{bail, Result}; |
7 | use pico_args::Arguments; | 7 | use pico_args::Arguments; |
8 | use ra_ssr::{SsrPattern, SsrRule}; | ||
8 | use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; | 9 | use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; |
9 | 10 | ||
10 | use std::{fmt::Write, path::PathBuf}; | 11 | use std::{fmt::Write, path::PathBuf}; |
@@ -24,6 +25,7 @@ pub(crate) enum Command { | |||
24 | }, | 25 | }, |
25 | Stats { | 26 | Stats { |
26 | randomize: bool, | 27 | randomize: bool, |
28 | parallel: bool, | ||
27 | memory_usage: bool, | 29 | memory_usage: bool, |
28 | only: Option<String>, | 30 | only: Option<String>, |
29 | with_deps: bool, | 31 | with_deps: bool, |
@@ -45,6 +47,13 @@ pub(crate) enum Command { | |||
45 | /// this would include the parser test files. | 47 | /// this would include the parser test files. |
46 | all: bool, | 48 | all: bool, |
47 | }, | 49 | }, |
50 | Ssr { | ||
51 | rules: Vec<SsrRule>, | ||
52 | }, | ||
53 | StructuredSearch { | ||
54 | debug_snippet: Option<String>, | ||
55 | patterns: Vec<SsrPattern>, | ||
56 | }, | ||
48 | ProcMacro, | 57 | ProcMacro, |
49 | RunServer, | 58 | RunServer, |
50 | Version, | 59 | Version, |
@@ -94,7 +103,7 @@ USAGE: | |||
94 | rust-analyzer parse [FLAGS] | 103 | rust-analyzer parse [FLAGS] |
95 | 104 | ||
96 | FLAGS: | 105 | FLAGS: |
97 | -h, --help Prints help inforamtion | 106 | -h, --help Prints help information |
98 | --no-dump" | 107 | --no-dump" |
99 | ); | 108 | ); |
100 | return Ok(Err(HelpPrinted)); | 109 | return Ok(Err(HelpPrinted)); |
@@ -153,10 +162,14 @@ USAGE: | |||
153 | rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH] | 162 | rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH] |
154 | 163 | ||
155 | FLAGS: | 164 | FLAGS: |
165 | -o, --only Only analyze items matching this path | ||
156 | -h, --help Prints help information | 166 | -h, --help Prints help information |
157 | --memory-usage | 167 | --memory-usage Collect memory usage statistics (requires `--feature jemalloc`) |
168 | --randomize Randomize order in which crates, modules, and items are processed | ||
169 | --parallel Run type inference in parallel | ||
158 | --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis | 170 | --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis |
159 | --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding | 171 | --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding |
172 | --with-deps Also analyze all dependencies | ||
160 | -v, --verbose | 173 | -v, --verbose |
161 | -q, --quiet | 174 | -q, --quiet |
162 | 175 | ||
@@ -170,6 +183,7 @@ ARGS: | |||
170 | } | 183 | } |
171 | 184 | ||
172 | let randomize = matches.contains("--randomize"); | 185 | let randomize = matches.contains("--randomize"); |
186 | let parallel = matches.contains("--parallel"); | ||
173 | let memory_usage = matches.contains("--memory-usage"); | 187 | let memory_usage = matches.contains("--memory-usage"); |
174 | let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; | 188 | let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; |
175 | let with_deps: bool = matches.contains("--with-deps"); | 189 | let with_deps: bool = matches.contains("--with-deps"); |
@@ -185,6 +199,7 @@ ARGS: | |||
185 | 199 | ||
186 | Command::Stats { | 200 | Command::Stats { |
187 | randomize, | 201 | randomize, |
202 | parallel, | ||
188 | memory_usage, | 203 | memory_usage, |
189 | only, | 204 | only, |
190 | with_deps, | 205 | with_deps, |
@@ -205,7 +220,7 @@ USAGE: | |||
205 | FLAGS: | 220 | FLAGS: |
206 | -h, --help Prints help information | 221 | -h, --help Prints help information |
207 | --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis | 222 | --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis |
208 | --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding | 223 | --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding |
209 | -v, --verbose | 224 | -v, --verbose |
210 | 225 | ||
211 | OPTIONS: | 226 | OPTIONS: |
@@ -270,6 +285,61 @@ ARGS: | |||
270 | Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } | 285 | Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } |
271 | } | 286 | } |
272 | "proc-macro" => Command::ProcMacro, | 287 | "proc-macro" => Command::ProcMacro, |
288 | "ssr" => { | ||
289 | if matches.contains(["-h", "--help"]) { | ||
290 | eprintln!( | ||
291 | "\ | ||
292 | rust-analyzer ssr | ||
293 | |||
294 | USAGE: | ||
295 | rust-analyzer ssr [FLAGS] [RULE...] | ||
296 | |||
297 | EXAMPLE: | ||
298 | rust-analyzer ssr '$a.foo($b) ==> bar($a, $b)' | ||
299 | |||
300 | FLAGS: | ||
301 | --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet> | ||
302 | -h, --help Prints help information | ||
303 | |||
304 | ARGS: | ||
305 | <RULE> A structured search replace rule" | ||
306 | ); | ||
307 | return Ok(Err(HelpPrinted)); | ||
308 | } | ||
309 | let mut rules = Vec::new(); | ||
310 | while let Some(rule) = matches.free_from_str()? { | ||
311 | rules.push(rule); | ||
312 | } | ||
313 | Command::Ssr { rules } | ||
314 | } | ||
315 | "search" => { | ||
316 | if matches.contains(["-h", "--help"]) { | ||
317 | eprintln!( | ||
318 | "\ | ||
319 | rust-analyzer search | ||
320 | |||
321 | USAGE: | ||
322 | rust-analyzer search [FLAGS] [PATTERN...] | ||
323 | |||
324 | EXAMPLE: | ||
325 | rust-analyzer search '$a.foo($b)' | ||
326 | |||
327 | FLAGS: | ||
328 | --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet> | ||
329 | -h, --help Prints help information | ||
330 | |||
331 | ARGS: | ||
332 | <PATTERN> A structured search pattern" | ||
333 | ); | ||
334 | return Ok(Err(HelpPrinted)); | ||
335 | } | ||
336 | let debug_snippet = matches.opt_value_from_str("--debug")?; | ||
337 | let mut patterns = Vec::new(); | ||
338 | while let Some(rule) = matches.free_from_str()? { | ||
339 | patterns.push(rule); | ||
340 | } | ||
341 | Command::StructuredSearch { patterns, debug_snippet } | ||
342 | } | ||
273 | _ => { | 343 | _ => { |
274 | print_subcommands(); | 344 | print_subcommands(); |
275 | return Ok(Err(HelpPrinted)); | 345 | return Ok(Err(HelpPrinted)); |
@@ -297,6 +367,8 @@ SUBCOMMANDS: | |||
297 | diagnostics | 367 | diagnostics |
298 | proc-macro | 368 | proc-macro |
299 | parse | 369 | parse |
370 | search | ||
371 | ssr | ||
300 | symbols" | 372 | symbols" |
301 | ) | 373 | ) |
302 | } | 374 | } |
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 45204d1a3..eec76d415 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -32,6 +32,7 @@ fn main() -> Result<()> { | |||
32 | args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, | 32 | args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, |
33 | args::Command::Stats { | 33 | args::Command::Stats { |
34 | randomize, | 34 | randomize, |
35 | parallel, | ||
35 | memory_usage, | 36 | memory_usage, |
36 | only, | 37 | only, |
37 | with_deps, | 38 | with_deps, |
@@ -45,6 +46,7 @@ fn main() -> Result<()> { | |||
45 | only.as_ref().map(String::as_ref), | 46 | only.as_ref().map(String::as_ref), |
46 | with_deps, | 47 | with_deps, |
47 | randomize, | 48 | randomize, |
49 | parallel, | ||
48 | load_output_dirs, | 50 | load_output_dirs, |
49 | with_proc_macro, | 51 | with_proc_macro, |
50 | )?, | 52 | )?, |
@@ -60,6 +62,12 @@ fn main() -> Result<()> { | |||
60 | args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => { | 62 | args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => { |
61 | cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)? | 63 | cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)? |
62 | } | 64 | } |
65 | args::Command::Ssr { rules } => { | ||
66 | cli::apply_ssr_rules(rules)?; | ||
67 | } | ||
68 | args::Command::StructuredSearch { patterns, debug_snippet } => { | ||
69 | cli::search_for_patterns(patterns, debug_snippet)?; | ||
70 | } | ||
63 | args::Command::Version => println!("rust-analyzer {}", env!("REV")), | 71 | args::Command::Version => println!("rust-analyzer {}", env!("REV")), |
64 | } | 72 | } |
65 | Ok(()) | 73 | Ok(()) |
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs index c5faec83a..6863f100b 100644 --- a/crates/rust-analyzer/src/cli.rs +++ b/crates/rust-analyzer/src/cli.rs | |||
@@ -5,6 +5,7 @@ mod analysis_stats; | |||
5 | mod analysis_bench; | 5 | mod analysis_bench; |
6 | mod diagnostics; | 6 | mod diagnostics; |
7 | mod progress_report; | 7 | mod progress_report; |
8 | mod ssr; | ||
8 | 9 | ||
9 | use std::io::Read; | 10 | use std::io::Read; |
10 | 11 | ||
@@ -17,6 +18,7 @@ pub use analysis_bench::{analysis_bench, BenchWhat, Position}; | |||
17 | pub use analysis_stats::analysis_stats; | 18 | pub use analysis_stats::analysis_stats; |
18 | pub use diagnostics::diagnostics; | 19 | pub use diagnostics::diagnostics; |
19 | pub use load_cargo::load_cargo; | 20 | pub use load_cargo::load_cargo; |
21 | pub use ssr::{apply_ssr_rules, search_for_patterns}; | ||
20 | 22 | ||
21 | #[derive(Clone, Copy)] | 23 | #[derive(Clone, Copy)] |
22 | pub enum Verbosity { | 24 | pub enum Verbosity { |
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 9d09501cd..14982919d 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs | |||
@@ -5,6 +5,7 @@ use std::{path::Path, time::Instant}; | |||
5 | 5 | ||
6 | use itertools::Itertools; | 6 | use itertools::Itertools; |
7 | use rand::{seq::SliceRandom, thread_rng}; | 7 | use rand::{seq::SliceRandom, thread_rng}; |
8 | use rayon::prelude::*; | ||
8 | use rustc_hash::FxHashSet; | 9 | use rustc_hash::FxHashSet; |
9 | 10 | ||
10 | use hir::{ | 11 | use hir::{ |
@@ -13,12 +14,23 @@ use hir::{ | |||
13 | }; | 14 | }; |
14 | use hir_def::FunctionId; | 15 | use hir_def::FunctionId; |
15 | use hir_ty::{Ty, TypeWalk}; | 16 | use hir_ty::{Ty, TypeWalk}; |
16 | use ra_db::SourceDatabaseExt; | 17 | use ra_db::{ |
18 | salsa::{self, ParallelDatabase}, | ||
19 | SourceDatabaseExt, | ||
20 | }; | ||
17 | use ra_syntax::AstNode; | 21 | use ra_syntax::AstNode; |
18 | use stdx::format_to; | 22 | use stdx::format_to; |
19 | 23 | ||
20 | use crate::cli::{load_cargo::load_cargo, progress_report::ProgressReport, Result, Verbosity}; | 24 | use crate::cli::{load_cargo::load_cargo, progress_report::ProgressReport, Result, Verbosity}; |
21 | 25 | ||
26 | /// Need to wrap Snapshot to provide `Clone` impl for `map_with` | ||
27 | struct Snap<DB>(DB); | ||
28 | impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> { | ||
29 | fn clone(&self) -> Snap<salsa::Snapshot<DB>> { | ||
30 | Snap(self.0.snapshot()) | ||
31 | } | ||
32 | } | ||
33 | |||
22 | pub fn analysis_stats( | 34 | pub fn analysis_stats( |
23 | verbosity: Verbosity, | 35 | verbosity: Verbosity, |
24 | memory_usage: bool, | 36 | memory_usage: bool, |
@@ -26,6 +38,7 @@ pub fn analysis_stats( | |||
26 | only: Option<&str>, | 38 | only: Option<&str>, |
27 | with_deps: bool, | 39 | with_deps: bool, |
28 | randomize: bool, | 40 | randomize: bool, |
41 | parallel: bool, | ||
29 | load_output_dirs: bool, | 42 | load_output_dirs: bool, |
30 | with_proc_macro: bool, | 43 | with_proc_macro: bool, |
31 | ) -> Result<()> { | 44 | ) -> Result<()> { |
@@ -91,12 +104,26 @@ pub fn analysis_stats( | |||
91 | funcs.shuffle(&mut thread_rng()); | 104 | funcs.shuffle(&mut thread_rng()); |
92 | } | 105 | } |
93 | 106 | ||
94 | let inference_time = Instant::now(); | ||
95 | let mut bar = match verbosity { | 107 | let mut bar = match verbosity { |
96 | Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), | 108 | Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), |
97 | _ => ProgressReport::new(funcs.len() as u64), | 109 | _ => ProgressReport::new(funcs.len() as u64), |
98 | }; | 110 | }; |
99 | 111 | ||
112 | if parallel { | ||
113 | let inference_time = Instant::now(); | ||
114 | let snap = Snap(db.snapshot()); | ||
115 | funcs | ||
116 | .par_iter() | ||
117 | .map_with(snap, |snap, &f| { | ||
118 | let f_id = FunctionId::from(f); | ||
119 | snap.0.body(f_id.into()); | ||
120 | snap.0.infer(f_id.into()); | ||
121 | }) | ||
122 | .count(); | ||
123 | println!("Parallel Inference: {:?}, {}", inference_time.elapsed(), ra_prof::memory_usage()); | ||
124 | } | ||
125 | |||
126 | let inference_time = Instant::now(); | ||
100 | bar.tick(); | 127 | bar.tick(); |
101 | let mut num_exprs = 0; | 128 | let mut num_exprs = 0; |
102 | let mut num_exprs_unknown = 0; | 129 | let mut num_exprs_unknown = 0; |
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs new file mode 100644 index 000000000..4fb829ea5 --- /dev/null +++ b/crates/rust-analyzer/src/cli/ssr.rs | |||
@@ -0,0 +1,71 @@ | |||
1 | //! Applies structured search replace rules from the command line. | ||
2 | |||
3 | use crate::cli::{load_cargo::load_cargo, Result}; | ||
4 | use ra_ide::SourceFileEdit; | ||
5 | use ra_ssr::{MatchFinder, SsrPattern, SsrRule}; | ||
6 | |||
7 | pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> { | ||
8 | use ra_db::SourceDatabaseExt; | ||
9 | use ra_ide_db::symbol_index::SymbolsDatabase; | ||
10 | let (host, vfs) = load_cargo(&std::env::current_dir()?, true, true)?; | ||
11 | let db = host.raw_database(); | ||
12 | let mut match_finder = MatchFinder::new(db); | ||
13 | for rule in rules { | ||
14 | match_finder.add_rule(rule); | ||
15 | } | ||
16 | let mut edits = Vec::new(); | ||
17 | for &root in db.local_roots().iter() { | ||
18 | let sr = db.source_root(root); | ||
19 | for file_id in sr.iter() { | ||
20 | if let Some(edit) = match_finder.edits_for_file(file_id) { | ||
21 | edits.push(SourceFileEdit { file_id, edit }); | ||
22 | } | ||
23 | } | ||
24 | } | ||
25 | for edit in edits { | ||
26 | if let Some(path) = vfs.file_path(edit.file_id).as_path() { | ||
27 | let mut contents = db.file_text(edit.file_id).to_string(); | ||
28 | edit.edit.apply(&mut contents); | ||
29 | std::fs::write(path, contents)?; | ||
30 | } | ||
31 | } | ||
32 | Ok(()) | ||
33 | } | ||
34 | |||
35 | /// Searches for `patterns`, printing debug information for any nodes whose text exactly matches | ||
36 | /// `debug_snippet`. This is intended for debugging and probably isn't in it's current form useful | ||
37 | /// for much else. | ||
38 | pub fn search_for_patterns(patterns: Vec<SsrPattern>, debug_snippet: Option<String>) -> Result<()> { | ||
39 | use ra_db::SourceDatabaseExt; | ||
40 | use ra_ide_db::symbol_index::SymbolsDatabase; | ||
41 | let (host, vfs) = load_cargo(&std::env::current_dir()?, true, true)?; | ||
42 | let db = host.raw_database(); | ||
43 | let mut match_finder = MatchFinder::new(db); | ||
44 | for pattern in patterns { | ||
45 | match_finder.add_search_pattern(pattern); | ||
46 | } | ||
47 | for &root in db.local_roots().iter() { | ||
48 | let sr = db.source_root(root); | ||
49 | for file_id in sr.iter() { | ||
50 | if let Some(debug_snippet) = &debug_snippet { | ||
51 | for debug_info in match_finder.debug_where_text_equal(file_id, debug_snippet) { | ||
52 | println!("{:#?}", debug_info); | ||
53 | } | ||
54 | } else { | ||
55 | let matches = match_finder.find_matches_in_file(file_id); | ||
56 | if !matches.matches.is_empty() { | ||
57 | let matches = matches.flattened().matches; | ||
58 | if let Some(path) = vfs.file_path(file_id).as_path() { | ||
59 | println!("{} matches in '{}'", matches.len(), path.to_string_lossy()); | ||
60 | } | ||
61 | // We could possibly at some point do something more useful than just printing | ||
62 | // the matched text. For now though, that's the easiest thing to do. | ||
63 | for m in matches { | ||
64 | println!("{}", m.matched_text()); | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | Ok(()) | ||
71 | } | ||
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 6b17ce18b..6c311648a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -44,7 +44,7 @@ pub struct Config { | |||
44 | pub root_path: AbsPathBuf, | 44 | pub root_path: AbsPathBuf, |
45 | } | 45 | } |
46 | 46 | ||
47 | #[derive(Debug, Clone)] | 47 | #[derive(Debug, Clone, Eq, PartialEq)] |
48 | pub enum LinkedProject { | 48 | pub enum LinkedProject { |
49 | ProjectManifest(ProjectManifest), | 49 | ProjectManifest(ProjectManifest), |
50 | InlineJsonProject(ProjectJson), | 50 | InlineJsonProject(ProjectJson), |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index b8aa1e5b5..b7b4edf66 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -27,7 +27,7 @@ use crate::{ | |||
27 | Result, | 27 | Result, |
28 | }; | 28 | }; |
29 | 29 | ||
30 | #[derive(Eq, PartialEq)] | 30 | #[derive(Eq, PartialEq, Copy, Clone)] |
31 | pub(crate) enum Status { | 31 | pub(crate) enum Status { |
32 | Loading, | 32 | Loading, |
33 | Ready, | 33 | Ready, |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index e35a5e846..3cb532b62 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -23,7 +23,7 @@ use ra_ide::{ | |||
23 | }; | 23 | }; |
24 | use ra_prof::profile; | 24 | use ra_prof::profile; |
25 | use ra_project_model::TargetKind; | 25 | use ra_project_model::TargetKind; |
26 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; | 26 | use ra_syntax::{algo, ast, AstNode, SyntaxKind, TextRange, TextSize}; |
27 | use serde::{Deserialize, Serialize}; | 27 | use serde::{Deserialize, Serialize}; |
28 | use serde_json::to_value; | 28 | use serde_json::to_value; |
29 | use stdx::{format_to, split_delim}; | 29 | use stdx::{format_to, split_delim}; |
@@ -330,11 +330,12 @@ pub(crate) fn handle_workspace_symbol( | |||
330 | fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> { | 330 | fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> { |
331 | let mut res = Vec::new(); | 331 | let mut res = Vec::new(); |
332 | for nav in snap.analysis.symbol_search(query)? { | 332 | for nav in snap.analysis.symbol_search(query)? { |
333 | let container_name = nav.container_name().map(|v| v.to_string()); | ||
333 | let info = SymbolInformation { | 334 | let info = SymbolInformation { |
334 | name: nav.name().to_string(), | 335 | name: nav.name().to_string(), |
335 | kind: to_proto::symbol_kind(nav.kind()), | 336 | kind: to_proto::symbol_kind(nav.kind()), |
336 | location: to_proto::location(snap, nav.file_range())?, | 337 | location: to_proto::location_from_nav(snap, nav)?, |
337 | container_name: nav.container_name().map(|v| v.to_string()), | 338 | container_name, |
338 | deprecated: None, | 339 | deprecated: None, |
339 | }; | 340 | }; |
340 | res.push(info); | 341 | res.push(info); |
@@ -407,8 +408,19 @@ pub(crate) fn handle_runnables( | |||
407 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 408 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
408 | let line_index = snap.analysis.file_line_index(file_id)?; | 409 | let line_index = snap.analysis.file_line_index(file_id)?; |
409 | let offset = params.position.map(|it| from_proto::offset(&line_index, it)); | 410 | let offset = params.position.map(|it| from_proto::offset(&line_index, it)); |
410 | let mut res = Vec::new(); | ||
411 | let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; | 411 | let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; |
412 | |||
413 | let expect_test = match offset { | ||
414 | Some(offset) => { | ||
415 | let source_file = snap.analysis.parse(file_id)?; | ||
416 | algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset) | ||
417 | .and_then(|it| it.path()?.segment()?.name_ref()) | ||
418 | .map_or(false, |it| it.text() == "expect" || it.text() == "expect_file") | ||
419 | } | ||
420 | None => false, | ||
421 | }; | ||
422 | |||
423 | let mut res = Vec::new(); | ||
412 | for runnable in snap.analysis.runnables(file_id)? { | 424 | for runnable in snap.analysis.runnables(file_id)? { |
413 | if let Some(offset) = offset { | 425 | if let Some(offset) = offset { |
414 | if !runnable.nav.full_range().contains_inclusive(offset) { | 426 | if !runnable.nav.full_range().contains_inclusive(offset) { |
@@ -418,8 +430,12 @@ pub(crate) fn handle_runnables( | |||
418 | if should_skip_target(&runnable, cargo_spec.as_ref()) { | 430 | if should_skip_target(&runnable, cargo_spec.as_ref()) { |
419 | continue; | 431 | continue; |
420 | } | 432 | } |
421 | 433 | let mut runnable = to_proto::runnable(&snap, file_id, runnable)?; | |
422 | res.push(to_proto::runnable(&snap, file_id, runnable)?); | 434 | if expect_test { |
435 | runnable.label = format!("{} + expect", runnable.label); | ||
436 | runnable.args.expect_test = Some(true); | ||
437 | } | ||
438 | res.push(runnable); | ||
423 | } | 439 | } |
424 | 440 | ||
425 | // Add `cargo check` and `cargo test` for the whole package | 441 | // Add `cargo check` and `cargo test` for the whole package |
@@ -438,6 +454,7 @@ pub(crate) fn handle_runnables( | |||
438 | spec.package.clone(), | 454 | spec.package.clone(), |
439 | ], | 455 | ], |
440 | executable_args: Vec::new(), | 456 | executable_args: Vec::new(), |
457 | expect_test: None, | ||
441 | }, | 458 | }, |
442 | }) | 459 | }) |
443 | } | 460 | } |
@@ -451,6 +468,7 @@ pub(crate) fn handle_runnables( | |||
451 | workspace_root: None, | 468 | workspace_root: None, |
452 | cargo_args: vec!["check".to_string(), "--workspace".to_string()], | 469 | cargo_args: vec!["check".to_string(), "--workspace".to_string()], |
453 | executable_args: Vec::new(), | 470 | executable_args: Vec::new(), |
471 | expect_test: None, | ||
454 | }, | 472 | }, |
455 | }); | 473 | }); |
456 | } | 474 | } |
@@ -1027,7 +1045,7 @@ pub(crate) fn handle_call_hierarchy_incoming( | |||
1027 | let item = params.item; | 1045 | let item = params.item; |
1028 | 1046 | ||
1029 | let doc = TextDocumentIdentifier::new(item.uri); | 1047 | let doc = TextDocumentIdentifier::new(item.uri); |
1030 | let frange = from_proto::file_range(&snap, doc, item.range)?; | 1048 | let frange = from_proto::file_range(&snap, doc, item.selection_range)?; |
1031 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | 1049 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; |
1032 | 1050 | ||
1033 | let call_items = match snap.analysis.incoming_calls(fpos)? { | 1051 | let call_items = match snap.analysis.incoming_calls(fpos)? { |
@@ -1062,7 +1080,7 @@ pub(crate) fn handle_call_hierarchy_outgoing( | |||
1062 | let item = params.item; | 1080 | let item = params.item; |
1063 | 1081 | ||
1064 | let doc = TextDocumentIdentifier::new(item.uri); | 1082 | let doc = TextDocumentIdentifier::new(item.uri); |
1065 | let frange = from_proto::file_range(&snap, doc, item.range)?; | 1083 | let frange = from_proto::file_range(&snap, doc, item.selection_range)?; |
1066 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | 1084 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; |
1067 | 1085 | ||
1068 | let call_items = match snap.analysis.outgoing_calls(fpos)? { | 1086 | let call_items = match snap.analysis.outgoing_calls(fpos)? { |
@@ -1196,8 +1214,8 @@ fn show_impl_command_link( | |||
1196 | let position = to_proto::position(&line_index, position.offset); | 1214 | let position = to_proto::position(&line_index, position.offset); |
1197 | let locations: Vec<_> = nav_data | 1215 | let locations: Vec<_> = nav_data |
1198 | .info | 1216 | .info |
1199 | .iter() | 1217 | .into_iter() |
1200 | .filter_map(|it| to_proto::location(snap, it.file_range()).ok()) | 1218 | .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok()) |
1201 | .collect(); | 1219 | .collect(); |
1202 | let title = implementation_title(locations.len()); | 1220 | let title = implementation_title(locations.len()); |
1203 | let command = show_references_command(title, &uri, position, locations); | 1221 | let command = show_references_command(title, &uri, position, locations); |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 1371f6cb4..82207bbb8 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -14,12 +14,12 @@ impl Request for AnalyzerStatus { | |||
14 | const METHOD: &'static str = "rust-analyzer/analyzerStatus"; | 14 | const METHOD: &'static str = "rust-analyzer/analyzerStatus"; |
15 | } | 15 | } |
16 | 16 | ||
17 | pub enum CollectGarbage {} | 17 | pub enum ReloadWorkspace {} |
18 | 18 | ||
19 | impl Request for CollectGarbage { | 19 | impl Request for ReloadWorkspace { |
20 | type Params = (); | 20 | type Params = (); |
21 | type Result = (); | 21 | type Result = (); |
22 | const METHOD: &'static str = "rust-analyzer/collectGarbage"; | 22 | const METHOD: &'static str = "rust-analyzer/reloadWorkspace"; |
23 | } | 23 | } |
24 | 24 | ||
25 | pub enum SyntaxTree {} | 25 | pub enum SyntaxTree {} |
@@ -161,6 +161,8 @@ pub struct CargoRunnable { | |||
161 | pub cargo_args: Vec<String>, | 161 | pub cargo_args: Vec<String>, |
162 | // stuff after -- | 162 | // stuff after -- |
163 | pub executable_args: Vec<String>, | 163 | pub executable_args: Vec<String>, |
164 | #[serde(skip_serializing_if = "Option::is_none")] | ||
165 | pub expect_test: Option<bool>, | ||
164 | } | 166 | } |
165 | 167 | ||
166 | pub enum InlayHints {} | 168 | pub enum InlayHints {} |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 9fd16ef3b..e03038b25 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -136,7 +136,7 @@ impl GlobalState { | |||
136 | log::info!("queued count = {}", queue_count); | 136 | log::info!("queued count = {}", queue_count); |
137 | } | 137 | } |
138 | 138 | ||
139 | let mut became_ready = false; | 139 | let prev_status = self.status; |
140 | match event { | 140 | match event { |
141 | Event::Lsp(msg) => match msg { | 141 | Event::Lsp(msg) => match msg { |
142 | lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, | 142 | lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, |
@@ -168,22 +168,26 @@ impl GlobalState { | |||
168 | } | 168 | } |
169 | } | 169 | } |
170 | vfs::loader::Message::Progress { n_total, n_done } => { | 170 | vfs::loader::Message::Progress { n_total, n_done } => { |
171 | let state = if n_done == 0 { | 171 | if n_total == 0 { |
172 | Progress::Begin | ||
173 | } else if n_done < n_total { | ||
174 | Progress::Report | ||
175 | } else { | ||
176 | assert_eq!(n_done, n_total); | ||
177 | self.status = Status::Ready; | 172 | self.status = Status::Ready; |
178 | became_ready = true; | 173 | } else { |
179 | Progress::End | 174 | let state = if n_done == 0 { |
180 | }; | 175 | self.status = Status::Loading; |
181 | self.report_progress( | 176 | Progress::Begin |
182 | "roots scanned", | 177 | } else if n_done < n_total { |
183 | state, | 178 | Progress::Report |
184 | Some(format!("{}/{}", n_done, n_total)), | 179 | } else { |
185 | Some(Progress::percentage(n_done, n_total)), | 180 | assert_eq!(n_done, n_total); |
186 | ) | 181 | self.status = Status::Ready; |
182 | Progress::End | ||
183 | }; | ||
184 | self.report_progress( | ||
185 | "roots scanned", | ||
186 | state, | ||
187 | Some(format!("{}/{}", n_done, n_total)), | ||
188 | Some(Progress::percentage(n_done, n_total)), | ||
189 | ) | ||
190 | } | ||
187 | } | 191 | } |
188 | }, | 192 | }, |
189 | Event::Flycheck(task) => match task { | 193 | Event::Flycheck(task) => match task { |
@@ -231,13 +235,13 @@ impl GlobalState { | |||
231 | } | 235 | } |
232 | 236 | ||
233 | let state_changed = self.process_changes(); | 237 | let state_changed = self.process_changes(); |
234 | if became_ready { | 238 | if prev_status == Status::Loading && self.status == Status::Ready { |
235 | if let Some(flycheck) = &self.flycheck { | 239 | if let Some(flycheck) = &self.flycheck { |
236 | flycheck.handle.update(); | 240 | flycheck.handle.update(); |
237 | } | 241 | } |
238 | } | 242 | } |
239 | 243 | ||
240 | if self.status == Status::Ready && (state_changed || became_ready) { | 244 | if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) { |
241 | let subscriptions = self | 245 | let subscriptions = self |
242 | .mem_docs | 246 | .mem_docs |
243 | .iter() | 247 | .iter() |
@@ -274,7 +278,7 @@ impl GlobalState { | |||
274 | self.register_request(&req, request_received); | 278 | self.register_request(&req, request_received); |
275 | 279 | ||
276 | RequestDispatcher { req: Some(req), global_state: self } | 280 | RequestDispatcher { req: Some(req), global_state: self } |
277 | .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))? | 281 | .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.reload()))? |
278 | .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? | 282 | .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? |
279 | .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? | 283 | .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? |
280 | .on_sync::<lsp_types::request::Shutdown>(|_, ()| Ok(()))? | 284 | .on_sync::<lsp_types::request::Shutdown>(|_, ()| Ok(()))? |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index ec71f8b29..0c1fd1b8b 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -19,11 +19,14 @@ impl GlobalState { | |||
19 | if self.config.lru_capacity != old_config.lru_capacity { | 19 | if self.config.lru_capacity != old_config.lru_capacity { |
20 | self.analysis_host.update_lru_capacity(old_config.lru_capacity); | 20 | self.analysis_host.update_lru_capacity(old_config.lru_capacity); |
21 | } | 21 | } |
22 | if self.config.flycheck != old_config.flycheck { | 22 | if self.config.linked_projects != old_config.linked_projects { |
23 | self.reload() | ||
24 | } else if self.config.flycheck != old_config.flycheck { | ||
23 | self.reload_flycheck(); | 25 | self.reload_flycheck(); |
24 | } | 26 | } |
25 | } | 27 | } |
26 | pub(crate) fn reload(&mut self) { | 28 | pub(crate) fn reload(&mut self) { |
29 | log::info!("reloading projects: {:?}", self.config.linked_projects); | ||
27 | let workspaces = { | 30 | let workspaces = { |
28 | if self.config.linked_projects.is_empty() | 31 | if self.config.linked_projects.is_empty() |
29 | && self.config.notifications.cargo_toml_not_found | 32 | && self.config.notifications.cargo_toml_not_found |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index f6cb8e4bb..95dd8e408 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -352,7 +352,7 @@ pub(crate) fn folding_range( | |||
352 | let kind = match fold.kind { | 352 | let kind = match fold.kind { |
353 | FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), | 353 | FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), |
354 | FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), | 354 | FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), |
355 | FoldKind::Mods | FoldKind::Block => None, | 355 | FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None, |
356 | }; | 356 | }; |
357 | 357 | ||
358 | let range = range(line_index, fold.range); | 358 | let range = range(line_index, fold.range); |
@@ -446,6 +446,18 @@ pub(crate) fn location( | |||
446 | Ok(loc) | 446 | Ok(loc) |
447 | } | 447 | } |
448 | 448 | ||
449 | /// Perefer using `location_link`, if the client has the cap. | ||
450 | pub(crate) fn location_from_nav( | ||
451 | snap: &GlobalStateSnapshot, | ||
452 | nav: NavigationTarget, | ||
453 | ) -> Result<lsp_types::Location> { | ||
454 | let url = url(snap, nav.file_id()); | ||
455 | let line_index = snap.analysis.file_line_index(nav.file_id())?; | ||
456 | let range = range(&line_index, nav.full_range()); | ||
457 | let loc = lsp_types::Location::new(url, range); | ||
458 | Ok(loc) | ||
459 | } | ||
460 | |||
449 | pub(crate) fn location_link( | 461 | pub(crate) fn location_link( |
450 | snap: &GlobalStateSnapshot, | 462 | snap: &GlobalStateSnapshot, |
451 | src: Option<FileRange>, | 463 | src: Option<FileRange>, |
@@ -666,38 +678,34 @@ pub(crate) fn runnable( | |||
666 | workspace_root: workspace_root.map(|it| it.into()), | 678 | workspace_root: workspace_root.map(|it| it.into()), |
667 | cargo_args, | 679 | cargo_args, |
668 | executable_args, | 680 | executable_args, |
681 | expect_test: None, | ||
669 | }, | 682 | }, |
670 | }) | 683 | }) |
671 | } | 684 | } |
672 | 685 | ||
673 | #[cfg(test)] | 686 | #[cfg(test)] |
674 | mod tests { | 687 | mod tests { |
675 | use test_utils::extract_ranges; | 688 | use ra_ide::Analysis; |
676 | 689 | ||
677 | use super::*; | 690 | use super::*; |
678 | 691 | ||
679 | #[test] | 692 | #[test] |
680 | fn conv_fold_line_folding_only_fixup() { | 693 | fn conv_fold_line_folding_only_fixup() { |
681 | let text = r#"<fold>mod a; | 694 | let text = r#"mod a; |
682 | mod b; | 695 | mod b; |
683 | mod c;</fold> | 696 | mod c; |
684 | 697 | ||
685 | fn main() <fold>{ | 698 | fn main() { |
686 | if cond <fold>{ | 699 | if cond { |
687 | a::do_a(); | 700 | a::do_a(); |
688 | }</fold> else <fold>{ | 701 | } else { |
689 | b::do_b(); | 702 | b::do_b(); |
690 | }</fold> | 703 | } |
691 | }</fold>"#; | 704 | }"#; |
692 | 705 | ||
693 | let (ranges, text) = extract_ranges(text, "fold"); | 706 | let (analysis, file_id) = Analysis::from_single_file(text.to_string()); |
694 | assert_eq!(ranges.len(), 4); | 707 | let folds = analysis.folding_ranges(file_id).unwrap(); |
695 | let folds = vec![ | 708 | assert_eq!(folds.len(), 4); |
696 | Fold { range: ranges[0], kind: FoldKind::Mods }, | ||
697 | Fold { range: ranges[1], kind: FoldKind::Block }, | ||
698 | Fold { range: ranges[2], kind: FoldKind::Block }, | ||
699 | Fold { range: ranges[3], kind: FoldKind::Block }, | ||
700 | ]; | ||
701 | 709 | ||
702 | let line_index = LineIndex::new(&text); | 710 | let line_index = LineIndex::new(&text); |
703 | let converted: Vec<lsp_types::FoldingRange> = | 711 | let converted: Vec<lsp_types::FoldingRange> = |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index caf847273..e4aa894ac 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -11,8 +11,9 @@ pub mod mark; | |||
11 | mod fixture; | 11 | mod fixture; |
12 | 12 | ||
13 | use std::{ | 13 | use std::{ |
14 | convert::{TryFrom, TryInto}, | ||
14 | env, fs, | 15 | env, fs, |
15 | path::{Path, PathBuf}, | 16 | path::PathBuf, |
16 | }; | 17 | }; |
17 | 18 | ||
18 | use serde_json::Value; | 19 | use serde_json::Value; |
@@ -117,8 +118,8 @@ pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) { | |||
117 | } | 118 | } |
118 | 119 | ||
119 | /// Extracts ranges, marked with `<tag> </tag>` pairs from the `text` | 120 | /// Extracts ranges, marked with `<tag> </tag>` pairs from the `text` |
120 | pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) { | 121 | pub fn extract_tags(mut text: &str, tag: &str) -> (Vec<(TextRange, Option<String>)>, String) { |
121 | let open = format!("<{}>", tag); | 122 | let open = format!("<{}", tag); |
122 | let close = format!("</{}>", tag); | 123 | let close = format!("</{}>", tag); |
123 | let mut ranges = Vec::new(); | 124 | let mut ranges = Vec::new(); |
124 | let mut res = String::new(); | 125 | let mut res = String::new(); |
@@ -133,22 +134,35 @@ pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) { | |||
133 | res.push_str(&text[..i]); | 134 | res.push_str(&text[..i]); |
134 | text = &text[i..]; | 135 | text = &text[i..]; |
135 | if text.starts_with(&open) { | 136 | if text.starts_with(&open) { |
136 | text = &text[open.len()..]; | 137 | let close_open = text.find('>').unwrap(); |
138 | let attr = text[open.len()..close_open].trim(); | ||
139 | let attr = if attr.is_empty() { None } else { Some(attr.to_string()) }; | ||
140 | text = &text[close_open + '>'.len_utf8()..]; | ||
137 | let from = TextSize::of(&res); | 141 | let from = TextSize::of(&res); |
138 | stack.push(from); | 142 | stack.push((from, attr)); |
139 | } else if text.starts_with(&close) { | 143 | } else if text.starts_with(&close) { |
140 | text = &text[close.len()..]; | 144 | text = &text[close.len()..]; |
141 | let from = stack.pop().unwrap_or_else(|| panic!("unmatched </{}>", tag)); | 145 | let (from, attr) = |
146 | stack.pop().unwrap_or_else(|| panic!("unmatched </{}>", tag)); | ||
142 | let to = TextSize::of(&res); | 147 | let to = TextSize::of(&res); |
143 | ranges.push(TextRange::new(from, to)); | 148 | ranges.push((TextRange::new(from, to), attr)); |
149 | } else { | ||
150 | res.push('<'); | ||
151 | text = &text['<'.len_utf8()..]; | ||
144 | } | 152 | } |
145 | } | 153 | } |
146 | } | 154 | } |
147 | } | 155 | } |
148 | assert!(stack.is_empty(), "unmatched <{}>", tag); | 156 | assert!(stack.is_empty(), "unmatched <{}>", tag); |
149 | ranges.sort_by_key(|r| (r.start(), r.end())); | 157 | ranges.sort_by_key(|r| (r.0.start(), r.0.end())); |
150 | (ranges, res) | 158 | (ranges, res) |
151 | } | 159 | } |
160 | #[test] | ||
161 | fn test_extract_tags() { | ||
162 | let (tags, text) = extract_tags(r#"<tag fn>fn <tag>main</tag>() {}</tag>"#, "tag"); | ||
163 | let actual = tags.into_iter().map(|(range, attr)| (&text[range], attr)).collect::<Vec<_>>(); | ||
164 | assert_eq!(actual, vec![("fn main() {}", Some("fn".into())), ("main", None),]); | ||
165 | } | ||
152 | 166 | ||
153 | /// Inserts `<|>` marker into the `text` at `offset`. | 167 | /// Inserts `<|>` marker into the `text` at `offset`. |
154 | pub fn add_cursor(text: &str, offset: TextSize) -> String { | 168 | pub fn add_cursor(text: &str, offset: TextSize) -> String { |
@@ -168,8 +182,9 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { | |||
168 | for line in lines_with_ends(text) { | 182 | for line in lines_with_ends(text) { |
169 | if let Some(idx) = line.find("//^") { | 183 | if let Some(idx) = line.find("//^") { |
170 | let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]); | 184 | let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]); |
171 | let data = line[idx + "//^".len()..].trim().to_string(); | 185 | for (line_range, text) in extract_line_annotations(&line[idx + "//".len()..]) { |
172 | res.push((TextRange::at(offset, 1.into()), data)) | 186 | res.push((line_range + offset, text)) |
187 | } | ||
173 | } | 188 | } |
174 | prev_line_start = Some(line_start); | 189 | prev_line_start = Some(line_start); |
175 | line_start += TextSize::of(line); | 190 | line_start += TextSize::of(line); |
@@ -177,22 +192,37 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { | |||
177 | res | 192 | res |
178 | } | 193 | } |
179 | 194 | ||
195 | fn extract_line_annotations(mut line: &str) -> Vec<(TextRange, String)> { | ||
196 | let mut res = Vec::new(); | ||
197 | let mut offset: TextSize = 0.into(); | ||
198 | while !line.is_empty() { | ||
199 | let len = line.chars().take_while(|&it| it == '^').count(); | ||
200 | assert!(len > 0); | ||
201 | let range = TextRange::at(offset, len.try_into().unwrap()); | ||
202 | let next = line[len..].find('^').map_or(line.len(), |it| it + len); | ||
203 | res.push((range, line[len..][..next - len].trim().to_string())); | ||
204 | line = &line[next..]; | ||
205 | offset += TextSize::try_from(next).unwrap(); | ||
206 | } | ||
207 | res | ||
208 | } | ||
209 | |||
180 | #[test] | 210 | #[test] |
181 | fn test_extract_annotations() { | 211 | fn test_extract_annotations() { |
182 | let text = stdx::trim_indent( | 212 | let text = stdx::trim_indent( |
183 | r#" | 213 | r#" |
184 | fn main() { | 214 | fn main() { |
185 | let x = 92; | 215 | let (x, y) = (9, 2); |
186 | //^ def | 216 | //^ def ^ def |
187 | z + 1 | 217 | zoo + 1 |
188 | } //^ i32 | 218 | } //^^^ i32 |
189 | "#, | 219 | "#, |
190 | ); | 220 | ); |
191 | let res = extract_annotations(&text) | 221 | let res = extract_annotations(&text) |
192 | .into_iter() | 222 | .into_iter() |
193 | .map(|(range, ann)| (&text[range], ann)) | 223 | .map(|(range, ann)| (&text[range], ann)) |
194 | .collect::<Vec<_>>(); | 224 | .collect::<Vec<_>>(); |
195 | assert_eq!(res, vec![("x", "def".into()), ("z", "i32".into()),]); | 225 | assert_eq!(res, vec![("x", "def".into()), ("y", "def".into()), ("zoo", "i32".into()),]); |
196 | } | 226 | } |
197 | 227 | ||
198 | // Comparison functionality borrowed from cargo: | 228 | // Comparison functionality borrowed from cargo: |
@@ -282,85 +312,6 @@ pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a | |||
282 | } | 312 | } |
283 | } | 313 | } |
284 | 314 | ||
285 | /// Calls callback `f` with input code and file paths for each `.rs` file in `test_data_dir` | ||
286 | /// subdirectories defined by `paths`. | ||
287 | /// | ||
288 | /// If the content of the matching output file differs from the output of `f()` | ||
289 | /// the test will fail. | ||
290 | /// | ||
291 | /// If there is no matching output file it will be created and filled with the | ||
292 | /// output of `f()`, but the test will fail. | ||
293 | pub fn dir_tests<F>(test_data_dir: &Path, paths: &[&str], outfile_extension: &str, f: F) | ||
294 | where | ||
295 | F: Fn(&str, &Path) -> String, | ||
296 | { | ||
297 | for (path, input_code) in collect_rust_files(test_data_dir, paths) { | ||
298 | let actual = f(&input_code, &path); | ||
299 | let path = path.with_extension(outfile_extension); | ||
300 | if !path.exists() { | ||
301 | println!("\nfile: {}", path.display()); | ||
302 | println!("No .txt file with expected result, creating...\n"); | ||
303 | println!("{}\n{}", input_code, actual); | ||
304 | fs::write(&path, &actual).unwrap(); | ||
305 | panic!("No expected result"); | ||
306 | } | ||
307 | let expected = read_text(&path); | ||
308 | assert_equal_text(&expected, &actual, &path); | ||
309 | } | ||
310 | } | ||
311 | |||
312 | /// Collects all `.rs` files from `dir` subdirectories defined by `paths`. | ||
313 | pub fn collect_rust_files(root_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)> { | ||
314 | paths | ||
315 | .iter() | ||
316 | .flat_map(|path| { | ||
317 | let path = root_dir.to_owned().join(path); | ||
318 | rust_files_in_dir(&path).into_iter() | ||
319 | }) | ||
320 | .map(|path| { | ||
321 | let text = read_text(&path); | ||
322 | (path, text) | ||
323 | }) | ||
324 | .collect() | ||
325 | } | ||
326 | |||
327 | /// Collects paths to all `.rs` files from `dir` in a sorted `Vec<PathBuf>`. | ||
328 | fn rust_files_in_dir(dir: &Path) -> Vec<PathBuf> { | ||
329 | let mut acc = Vec::new(); | ||
330 | for file in fs::read_dir(&dir).unwrap() { | ||
331 | let file = file.unwrap(); | ||
332 | let path = file.path(); | ||
333 | if path.extension().unwrap_or_default() == "rs" { | ||
334 | acc.push(path); | ||
335 | } | ||
336 | } | ||
337 | acc.sort(); | ||
338 | acc | ||
339 | } | ||
340 | |||
341 | /// Returns the path to the root directory of `rust-analyzer` project. | ||
342 | pub fn project_dir() -> PathBuf { | ||
343 | let dir = env!("CARGO_MANIFEST_DIR"); | ||
344 | PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned() | ||
345 | } | ||
346 | |||
347 | /// Read file and normalize newlines. | ||
348 | /// | ||
349 | /// `rustc` seems to always normalize `\r\n` newlines to `\n`: | ||
350 | /// | ||
351 | /// ``` | ||
352 | /// let s = " | ||
353 | /// "; | ||
354 | /// assert_eq!(s.as_bytes(), &[10]); | ||
355 | /// ``` | ||
356 | /// | ||
357 | /// so this should always be correct. | ||
358 | pub fn read_text(path: &Path) -> String { | ||
359 | fs::read_to_string(path) | ||
360 | .unwrap_or_else(|_| panic!("File at {:?} should be valid", path)) | ||
361 | .replace("\r\n", "\n") | ||
362 | } | ||
363 | |||
364 | /// Returns `false` if slow tests should not run, otherwise returns `true` and | 315 | /// Returns `false` if slow tests should not run, otherwise returns `true` and |
365 | /// also creates a file at `./target/.slow_tests_cookie` which serves as a flag | 316 | /// also creates a file at `./target/.slow_tests_cookie` which serves as a flag |
366 | /// that slow tests did run. | 317 | /// that slow tests did run. |
@@ -375,25 +326,8 @@ pub fn skip_slow_tests() -> bool { | |||
375 | should_skip | 326 | should_skip |
376 | } | 327 | } |
377 | 328 | ||
378 | /// Asserts that `expected` and `actual` strings are equal. If they differ only | 329 | /// Returns the path to the root directory of `rust-analyzer` project. |
379 | /// in trailing or leading whitespace the test won't fail and | 330 | pub fn project_dir() -> PathBuf { |
380 | /// the contents of `actual` will be written to the file located at `path`. | 331 | let dir = env!("CARGO_MANIFEST_DIR"); |
381 | fn assert_equal_text(expected: &str, actual: &str, path: &Path) { | 332 | PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned() |
382 | if expected == actual { | ||
383 | return; | ||
384 | } | ||
385 | let dir = project_dir(); | ||
386 | let pretty_path = path.strip_prefix(&dir).unwrap_or_else(|_| path); | ||
387 | if expected.trim() == actual.trim() { | ||
388 | println!("whitespace difference, rewriting"); | ||
389 | println!("file: {}\n", pretty_path.display()); | ||
390 | fs::write(path, actual).unwrap(); | ||
391 | return; | ||
392 | } | ||
393 | if env::var("UPDATE_EXPECTATIONS").is_ok() { | ||
394 | println!("rewriting {}", pretty_path.display()); | ||
395 | fs::write(path, actual).unwrap(); | ||
396 | return; | ||
397 | } | ||
398 | assert_eq_text!(expected, actual, "file: {}", pretty_path.display()); | ||
399 | } | 333 | } |
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index d0ddeafe7..977ba3010 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs | |||
@@ -69,7 +69,7 @@ impl FileSetConfig { | |||
69 | Ok(it) => it, | 69 | Ok(it) => it, |
70 | Err(it) => it.saturating_sub(1), | 70 | Err(it) => it.saturating_sub(1), |
71 | }; | 71 | }; |
72 | if path.starts_with(&self.roots[idx].0) { | 72 | if !self.roots.is_empty() && path.starts_with(&self.roots[idx].0) { |
73 | self.roots[idx].1 | 73 | self.roots[idx].1 |
74 | } else { | 74 | } else { |
75 | self.len() - 1 | 75 | self.len() - 1 |