diff options
Diffstat (limited to 'crates/ide/src')
34 files changed, 998 insertions, 667 deletions
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index 58e26b94c..d2cf2cc7d 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs | |||
@@ -139,7 +139,7 @@ impl CallLocations { | |||
139 | mod tests { | 139 | mod tests { |
140 | use base_db::FilePosition; | 140 | use base_db::FilePosition; |
141 | 141 | ||
142 | use crate::mock_analysis::analysis_and_position; | 142 | use crate::fixture; |
143 | 143 | ||
144 | fn check_hierarchy( | 144 | fn check_hierarchy( |
145 | ra_fixture: &str, | 145 | ra_fixture: &str, |
@@ -147,7 +147,7 @@ mod tests { | |||
147 | expected_incoming: &[&str], | 147 | expected_incoming: &[&str], |
148 | expected_outgoing: &[&str], | 148 | expected_outgoing: &[&str], |
149 | ) { | 149 | ) { |
150 | let (analysis, pos) = analysis_and_position(ra_fixture); | 150 | let (analysis, pos) = fixture::position(ra_fixture); |
151 | 151 | ||
152 | let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info; | 152 | let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info; |
153 | assert_eq!(navs.len(), 1); | 153 | assert_eq!(navs.len(), 1); |
@@ -181,8 +181,8 @@ fn caller() { | |||
181 | call<|>ee(); | 181 | call<|>ee(); |
182 | } | 182 | } |
183 | "#, | 183 | "#, |
184 | "callee FN FileId(1) 0..14 3..9", | 184 | "callee FN FileId(0) 0..14 3..9", |
185 | &["caller FN FileId(1) 15..44 18..24 : [33..39]"], | 185 | &["caller FN FileId(0) 15..44 18..24 : [33..39]"], |
186 | &[], | 186 | &[], |
187 | ); | 187 | ); |
188 | } | 188 | } |
@@ -197,8 +197,8 @@ fn caller() { | |||
197 | callee(); | 197 | callee(); |
198 | } | 198 | } |
199 | "#, | 199 | "#, |
200 | "callee FN FileId(1) 0..14 3..9", | 200 | "callee FN FileId(0) 0..14 3..9", |
201 | &["caller FN FileId(1) 15..44 18..24 : [33..39]"], | 201 | &["caller FN FileId(0) 15..44 18..24 : [33..39]"], |
202 | &[], | 202 | &[], |
203 | ); | 203 | ); |
204 | } | 204 | } |
@@ -214,8 +214,8 @@ fn caller() { | |||
214 | callee(); | 214 | callee(); |
215 | } | 215 | } |
216 | "#, | 216 | "#, |
217 | "callee FN FileId(1) 0..14 3..9", | 217 | "callee FN FileId(0) 0..14 3..9", |
218 | &["caller FN FileId(1) 15..58 18..24 : [33..39, 47..53]"], | 218 | &["caller FN FileId(0) 15..58 18..24 : [33..39, 47..53]"], |
219 | &[], | 219 | &[], |
220 | ); | 220 | ); |
221 | } | 221 | } |
@@ -234,10 +234,10 @@ fn caller2() { | |||
234 | callee(); | 234 | callee(); |
235 | } | 235 | } |
236 | "#, | 236 | "#, |
237 | "callee FN FileId(1) 0..14 3..9", | 237 | "callee FN FileId(0) 0..14 3..9", |
238 | &[ | 238 | &[ |
239 | "caller1 FN FileId(1) 15..45 18..25 : [34..40]", | 239 | "caller1 FN FileId(0) 15..45 18..25 : [34..40]", |
240 | "caller2 FN FileId(1) 47..77 50..57 : [66..72]", | 240 | "caller2 FN FileId(0) 47..77 50..57 : [66..72]", |
241 | ], | 241 | ], |
242 | &[], | 242 | &[], |
243 | ); | 243 | ); |
@@ -263,10 +263,10 @@ mod tests { | |||
263 | } | 263 | } |
264 | } | 264 | } |
265 | "#, | 265 | "#, |
266 | "callee FN FileId(1) 0..14 3..9", | 266 | "callee FN FileId(0) 0..14 3..9", |
267 | &[ | 267 | &[ |
268 | "caller1 FN FileId(1) 15..45 18..25 : [34..40]", | 268 | "caller1 FN FileId(0) 15..45 18..25 : [34..40]", |
269 | "test_caller FN FileId(1) 95..149 110..121 : [134..140]", | 269 | "test_caller FN FileId(0) 95..149 110..121 : [134..140]", |
270 | ], | 270 | ], |
271 | &[], | 271 | &[], |
272 | ); | 272 | ); |
@@ -287,8 +287,8 @@ fn caller() { | |||
287 | //- /foo/mod.rs | 287 | //- /foo/mod.rs |
288 | pub fn callee() {} | 288 | pub fn callee() {} |
289 | "#, | 289 | "#, |
290 | "callee FN FileId(2) 0..18 7..13", | 290 | "callee FN FileId(1) 0..18 7..13", |
291 | &["caller FN FileId(1) 27..56 30..36 : [45..51]"], | 291 | &["caller FN FileId(0) 27..56 30..36 : [45..51]"], |
292 | &[], | 292 | &[], |
293 | ); | 293 | ); |
294 | } | 294 | } |
@@ -304,9 +304,9 @@ fn call<|>er() { | |||
304 | callee(); | 304 | callee(); |
305 | } | 305 | } |
306 | "#, | 306 | "#, |
307 | "caller FN FileId(1) 15..58 18..24", | 307 | "caller FN FileId(0) 15..58 18..24", |
308 | &[], | 308 | &[], |
309 | &["callee FN FileId(1) 0..14 3..9 : [33..39, 47..53]"], | 309 | &["callee FN FileId(0) 0..14 3..9 : [33..39, 47..53]"], |
310 | ); | 310 | ); |
311 | } | 311 | } |
312 | 312 | ||
@@ -325,9 +325,9 @@ fn call<|>er() { | |||
325 | //- /foo/mod.rs | 325 | //- /foo/mod.rs |
326 | pub fn callee() {} | 326 | pub fn callee() {} |
327 | "#, | 327 | "#, |
328 | "caller FN FileId(1) 27..56 30..36", | 328 | "caller FN FileId(0) 27..56 30..36", |
329 | &[], | 329 | &[], |
330 | &["callee FN FileId(2) 0..18 7..13 : [45..51]"], | 330 | &["callee FN FileId(1) 0..18 7..13 : [45..51]"], |
331 | ); | 331 | ); |
332 | } | 332 | } |
333 | 333 | ||
@@ -348,9 +348,9 @@ fn caller3() { | |||
348 | 348 | ||
349 | } | 349 | } |
350 | "#, | 350 | "#, |
351 | "caller2 FN FileId(1) 33..64 36..43", | 351 | "caller2 FN FileId(0) 33..64 36..43", |
352 | &["caller1 FN FileId(1) 0..31 3..10 : [19..26]"], | 352 | &["caller1 FN FileId(0) 0..31 3..10 : [19..26]"], |
353 | &["caller3 FN FileId(1) 66..83 69..76 : [52..59]"], | 353 | &["caller3 FN FileId(0) 66..83 69..76 : [52..59]"], |
354 | ); | 354 | ); |
355 | } | 355 | } |
356 | 356 | ||
@@ -368,9 +368,9 @@ fn main() { | |||
368 | a<|>() | 368 | a<|>() |
369 | } | 369 | } |
370 | "#, | 370 | "#, |
371 | "a FN FileId(1) 0..18 3..4", | 371 | "a FN FileId(0) 0..18 3..4", |
372 | &["main FN FileId(1) 31..52 34..38 : [47..48]"], | 372 | &["main FN FileId(0) 31..52 34..38 : [47..48]"], |
373 | &["b FN FileId(1) 20..29 23..24 : [13..14]"], | 373 | &["b FN FileId(0) 20..29 23..24 : [13..14]"], |
374 | ); | 374 | ); |
375 | 375 | ||
376 | check_hierarchy( | 376 | check_hierarchy( |
@@ -385,8 +385,8 @@ fn main() { | |||
385 | a() | 385 | a() |
386 | } | 386 | } |
387 | "#, | 387 | "#, |
388 | "b FN FileId(1) 20..29 23..24", | 388 | "b FN FileId(0) 20..29 23..24", |
389 | &["a FN FileId(1) 0..18 3..4 : [13..14]"], | 389 | &["a FN FileId(0) 0..18 3..4 : [13..14]"], |
390 | &[], | 390 | &[], |
391 | ); | 391 | ); |
392 | } | 392 | } |
diff --git a/crates/ide/src/call_info.rs b/crates/ide/src/call_info.rs index 7e99c6b72..d7b2b926e 100644 --- a/crates/ide/src/call_info.rs +++ b/crates/ide/src/call_info.rs | |||
@@ -232,10 +232,10 @@ mod tests { | |||
232 | use expect_test::{expect, Expect}; | 232 | use expect_test::{expect, Expect}; |
233 | use test_utils::mark; | 233 | use test_utils::mark; |
234 | 234 | ||
235 | use crate::mock_analysis::analysis_and_position; | 235 | use crate::fixture; |
236 | 236 | ||
237 | fn check(ra_fixture: &str, expect: Expect) { | 237 | fn check(ra_fixture: &str, expect: Expect) { |
238 | let (analysis, position) = analysis_and_position(ra_fixture); | 238 | let (analysis, position) = fixture::position(ra_fixture); |
239 | let call_info = analysis.call_info(position).unwrap(); | 239 | let call_info = analysis.call_info(position).unwrap(); |
240 | let actual = match call_info { | 240 | let actual = match call_info { |
241 | Some(call_info) => { | 241 | Some(call_info) => { |
diff --git a/crates/ide/src/completion.rs b/crates/ide/src/completion.rs index daea2aa95..697f691b0 100644 --- a/crates/ide/src/completion.rs +++ b/crates/ide/src/completion.rs | |||
@@ -133,7 +133,7 @@ pub(crate) fn completions( | |||
133 | #[cfg(test)] | 133 | #[cfg(test)] |
134 | mod tests { | 134 | mod tests { |
135 | use crate::completion::completion_config::CompletionConfig; | 135 | use crate::completion::completion_config::CompletionConfig; |
136 | use crate::mock_analysis::analysis_and_position; | 136 | use crate::fixture; |
137 | 137 | ||
138 | struct DetailAndDocumentation<'a> { | 138 | struct DetailAndDocumentation<'a> { |
139 | detail: &'a str, | 139 | detail: &'a str, |
@@ -141,7 +141,7 @@ mod tests { | |||
141 | } | 141 | } |
142 | 142 | ||
143 | fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) { | 143 | fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) { |
144 | let (analysis, position) = analysis_and_position(ra_fixture); | 144 | let (analysis, position) = fixture::position(ra_fixture); |
145 | let config = CompletionConfig::default(); | 145 | let config = CompletionConfig::default(); |
146 | let completions = analysis.completions(&config, position).unwrap().unwrap(); | 146 | let completions = analysis.completions(&config, position).unwrap().unwrap(); |
147 | for item in completions { | 147 | for item in completions { |
diff --git a/crates/ide/src/completion/complete_keyword.rs b/crates/ide/src/completion/complete_keyword.rs index 5645b41fa..e59747095 100644 --- a/crates/ide/src/completion/complete_keyword.rs +++ b/crates/ide/src/completion/complete_keyword.rs | |||
@@ -495,13 +495,13 @@ Some multi-line comment<|> | |||
495 | fn test_completion_await_impls_future() { | 495 | fn test_completion_await_impls_future() { |
496 | check( | 496 | check( |
497 | r#" | 497 | r#" |
498 | //- /main.rs | 498 | //- /main.rs crate:main deps:std |
499 | use std::future::*; | 499 | use std::future::*; |
500 | struct A {} | 500 | struct A {} |
501 | impl Future for A {} | 501 | impl Future for A {} |
502 | fn foo(a: A) { a.<|> } | 502 | fn foo(a: A) { a.<|> } |
503 | 503 | ||
504 | //- /std/lib.rs | 504 | //- /std/lib.rs crate:std |
505 | pub mod future { | 505 | pub mod future { |
506 | #[lang = "future_trait"] | 506 | #[lang = "future_trait"] |
507 | pub trait Future {} | 507 | pub trait Future {} |
@@ -514,14 +514,14 @@ pub mod future { | |||
514 | 514 | ||
515 | check( | 515 | check( |
516 | r#" | 516 | r#" |
517 | //- /main.rs | 517 | //- /main.rs crate:main deps:std |
518 | use std::future::*; | 518 | use std::future::*; |
519 | fn foo() { | 519 | fn foo() { |
520 | let a = async {}; | 520 | let a = async {}; |
521 | a.<|> | 521 | a.<|> |
522 | } | 522 | } |
523 | 523 | ||
524 | //- /std/lib.rs | 524 | //- /std/lib.rs crate:std |
525 | pub mod future { | 525 | pub mod future { |
526 | #[lang = "future_trait"] | 526 | #[lang = "future_trait"] |
527 | pub trait Future { | 527 | pub trait Future { |
diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index 3cfc2e131..c7a99bdc3 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs | |||
@@ -300,7 +300,7 @@ mod tests { | |||
300 | // "#, | 300 | // "#, |
301 | // expect![[r#" | 301 | // expect![[r#" |
302 | // md bar; | 302 | // md bar; |
303 | // "#]], | 303 | // "#]],foo |
304 | // ); | 304 | // ); |
305 | // } | 305 | // } |
306 | 306 | ||
@@ -308,7 +308,7 @@ mod tests { | |||
308 | fn already_declared_bin_module_completion_omitted() { | 308 | fn already_declared_bin_module_completion_omitted() { |
309 | check( | 309 | check( |
310 | r#" | 310 | r#" |
311 | //- /src/bin.rs | 311 | //- /src/bin.rs crate:main |
312 | fn main() {} | 312 | fn main() {} |
313 | //- /src/bin/foo.rs | 313 | //- /src/bin/foo.rs |
314 | mod <|> | 314 | mod <|> |
diff --git a/crates/ide/src/completion/complete_postfix.rs b/crates/ide/src/completion/complete_postfix.rs index 26a5af5b9..db5319618 100644 --- a/crates/ide/src/completion/complete_postfix.rs +++ b/crates/ide/src/completion/complete_postfix.rs | |||
@@ -1,11 +1,15 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | |||
3 | mod format_like; | ||
4 | |||
2 | use assists::utils::TryEnum; | 5 | use assists::utils::TryEnum; |
3 | use syntax::{ | 6 | use syntax::{ |
4 | ast::{self, AstNode}, | 7 | ast::{self, AstNode, AstToken}, |
5 | TextRange, TextSize, | 8 | TextRange, TextSize, |
6 | }; | 9 | }; |
7 | use text_edit::TextEdit; | 10 | use text_edit::TextEdit; |
8 | 11 | ||
12 | use self::format_like::add_format_like_completions; | ||
9 | use crate::{ | 13 | use crate::{ |
10 | completion::{ | 14 | completion::{ |
11 | completion_config::SnippetCap, | 15 | completion_config::SnippetCap, |
@@ -207,6 +211,12 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
207 | &format!("${{1}}({})", receiver_text), | 211 | &format!("${{1}}({})", receiver_text), |
208 | ) | 212 | ) |
209 | .add_to(acc); | 213 | .add_to(acc); |
214 | |||
215 | if let ast::Expr::Literal(literal) = dot_receiver.clone() { | ||
216 | if let Some(literal_text) = ast::String::cast(literal.token()) { | ||
217 | add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text); | ||
218 | } | ||
219 | } | ||
210 | } | 220 | } |
211 | 221 | ||
212 | fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { | 222 | fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { |
@@ -392,4 +402,53 @@ fn main() { | |||
392 | check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#); | 402 | check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#); |
393 | check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#); | 403 | check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#); |
394 | } | 404 | } |
405 | |||
406 | #[test] | ||
407 | fn postfix_completion_for_format_like_strings() { | ||
408 | check_edit( | ||
409 | "fmt", | ||
410 | r#"fn main() { "{some_var:?}".<|> }"#, | ||
411 | r#"fn main() { format!("{:?}", some_var) }"#, | ||
412 | ); | ||
413 | check_edit( | ||
414 | "panic", | ||
415 | r#"fn main() { "Panic with {a}".<|> }"#, | ||
416 | r#"fn main() { panic!("Panic with {}", a) }"#, | ||
417 | ); | ||
418 | check_edit( | ||
419 | "println", | ||
420 | r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".<|> }"#, | ||
421 | r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }) }"#, | ||
422 | ); | ||
423 | check_edit( | ||
424 | "loge", | ||
425 | r#"fn main() { "{2+2}".<|> }"#, | ||
426 | r#"fn main() { log::error!("{}", 2+2) }"#, | ||
427 | ); | ||
428 | check_edit( | ||
429 | "logt", | ||
430 | r#"fn main() { "{2+2}".<|> }"#, | ||
431 | r#"fn main() { log::trace!("{}", 2+2) }"#, | ||
432 | ); | ||
433 | check_edit( | ||
434 | "logd", | ||
435 | r#"fn main() { "{2+2}".<|> }"#, | ||
436 | r#"fn main() { log::debug!("{}", 2+2) }"#, | ||
437 | ); | ||
438 | check_edit( | ||
439 | "logi", | ||
440 | r#"fn main() { "{2+2}".<|> }"#, | ||
441 | r#"fn main() { log::info!("{}", 2+2) }"#, | ||
442 | ); | ||
443 | check_edit( | ||
444 | "logw", | ||
445 | r#"fn main() { "{2+2}".<|> }"#, | ||
446 | r#"fn main() { log::warn!("{}", 2+2) }"#, | ||
447 | ); | ||
448 | check_edit( | ||
449 | "loge", | ||
450 | r#"fn main() { "{2+2}".<|> }"#, | ||
451 | r#"fn main() { log::error!("{}", 2+2) }"#, | ||
452 | ); | ||
453 | } | ||
395 | } | 454 | } |
diff --git a/crates/ide/src/completion/complete_postfix/format_like.rs b/crates/ide/src/completion/complete_postfix/format_like.rs new file mode 100644 index 000000000..81c33bf3a --- /dev/null +++ b/crates/ide/src/completion/complete_postfix/format_like.rs | |||
@@ -0,0 +1,277 @@ | |||
1 | // Feature: Format String Completion. | ||
2 | // | ||
3 | // `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`. | ||
4 | // | ||
5 | // The following postfix snippets are available: | ||
6 | // | ||
7 | // - `format` -> `format!(...)` | ||
8 | // - `panic` -> `panic!(...)` | ||
9 | // - `println` -> `println!(...)` | ||
10 | // - `log`: | ||
11 | // + `logd` -> `log::debug!(...)` | ||
12 | // + `logt` -> `log::trace!(...)` | ||
13 | // + `logi` -> `log::info!(...)` | ||
14 | // + `logw` -> `log::warn!(...)` | ||
15 | // + `loge` -> `log::error!(...)` | ||
16 | |||
17 | use crate::completion::{ | ||
18 | complete_postfix::postfix_snippet, completion_config::SnippetCap, | ||
19 | completion_context::CompletionContext, completion_item::Completions, | ||
20 | }; | ||
21 | use syntax::ast::{self, AstToken}; | ||
22 | |||
23 | /// Mapping ("postfix completion item" => "macro to use") | ||
24 | static KINDS: &[(&str, &str)] = &[ | ||
25 | ("fmt", "format!"), | ||
26 | ("panic", "panic!"), | ||
27 | ("println", "println!"), | ||
28 | ("logd", "log::debug!"), | ||
29 | ("logt", "log::trace!"), | ||
30 | ("logi", "log::info!"), | ||
31 | ("logw", "log::warn!"), | ||
32 | ("loge", "log::error!"), | ||
33 | ]; | ||
34 | |||
35 | pub(super) fn add_format_like_completions( | ||
36 | acc: &mut Completions, | ||
37 | ctx: &CompletionContext, | ||
38 | dot_receiver: &ast::Expr, | ||
39 | cap: SnippetCap, | ||
40 | receiver_text: &ast::String, | ||
41 | ) { | ||
42 | let input = match string_literal_contents(receiver_text) { | ||
43 | // It's not a string literal, do not parse input. | ||
44 | Some(input) => input, | ||
45 | None => return, | ||
46 | }; | ||
47 | |||
48 | let mut parser = FormatStrParser::new(input); | ||
49 | |||
50 | if parser.parse().is_ok() { | ||
51 | for (label, macro_name) in KINDS { | ||
52 | let snippet = parser.into_suggestion(macro_name); | ||
53 | |||
54 | postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc); | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | /// Checks whether provided item is a string literal. | ||
60 | fn string_literal_contents(item: &ast::String) -> Option<String> { | ||
61 | let item = item.text(); | ||
62 | if item.len() >= 2 && item.starts_with("\"") && item.ends_with("\"") { | ||
63 | return Some(item[1..item.len() - 1].to_owned()); | ||
64 | } | ||
65 | |||
66 | None | ||
67 | } | ||
68 | |||
69 | /// Parser for a format-like string. It is more allowing in terms of string contents, | ||
70 | /// as we expect variable placeholders to be filled with expressions. | ||
71 | #[derive(Debug)] | ||
72 | pub struct FormatStrParser { | ||
73 | input: String, | ||
74 | output: String, | ||
75 | extracted_expressions: Vec<String>, | ||
76 | state: State, | ||
77 | parsed: bool, | ||
78 | } | ||
79 | |||
80 | #[derive(Debug, Clone, Copy, PartialEq)] | ||
81 | enum State { | ||
82 | NotExpr, | ||
83 | MaybeExpr, | ||
84 | Expr, | ||
85 | MaybeIncorrect, | ||
86 | FormatOpts, | ||
87 | } | ||
88 | |||
89 | impl FormatStrParser { | ||
90 | pub fn new(input: String) -> Self { | ||
91 | Self { | ||
92 | input: input.into(), | ||
93 | output: String::new(), | ||
94 | extracted_expressions: Vec::new(), | ||
95 | state: State::NotExpr, | ||
96 | parsed: false, | ||
97 | } | ||
98 | } | ||
99 | |||
100 | pub fn parse(&mut self) -> Result<(), ()> { | ||
101 | let mut current_expr = String::new(); | ||
102 | |||
103 | let mut placeholder_id = 1; | ||
104 | |||
105 | // Count of open braces inside of an expression. | ||
106 | // We assume that user knows what they're doing, thus we treat it like a correct pattern, e.g. | ||
107 | // "{MyStruct { val_a: 0, val_b: 1 }}". | ||
108 | let mut inexpr_open_count = 0; | ||
109 | |||
110 | for chr in self.input.chars() { | ||
111 | match (self.state, chr) { | ||
112 | (State::NotExpr, '{') => { | ||
113 | self.output.push(chr); | ||
114 | self.state = State::MaybeExpr; | ||
115 | } | ||
116 | (State::NotExpr, '}') => { | ||
117 | self.output.push(chr); | ||
118 | self.state = State::MaybeIncorrect; | ||
119 | } | ||
120 | (State::NotExpr, _) => { | ||
121 | self.output.push(chr); | ||
122 | } | ||
123 | (State::MaybeIncorrect, '}') => { | ||
124 | // It's okay, we met "}}". | ||
125 | self.output.push(chr); | ||
126 | self.state = State::NotExpr; | ||
127 | } | ||
128 | (State::MaybeIncorrect, _) => { | ||
129 | // Error in the string. | ||
130 | return Err(()); | ||
131 | } | ||
132 | (State::MaybeExpr, '{') => { | ||
133 | self.output.push(chr); | ||
134 | self.state = State::NotExpr; | ||
135 | } | ||
136 | (State::MaybeExpr, '}') => { | ||
137 | // This is an empty sequence '{}'. Replace it with placeholder. | ||
138 | self.output.push(chr); | ||
139 | self.extracted_expressions.push(format!("${}", placeholder_id)); | ||
140 | placeholder_id += 1; | ||
141 | self.state = State::NotExpr; | ||
142 | } | ||
143 | (State::MaybeExpr, _) => { | ||
144 | current_expr.push(chr); | ||
145 | self.state = State::Expr; | ||
146 | } | ||
147 | (State::Expr, '}') => { | ||
148 | if inexpr_open_count == 0 { | ||
149 | self.output.push(chr); | ||
150 | self.extracted_expressions.push(current_expr.trim().into()); | ||
151 | current_expr = String::new(); | ||
152 | self.state = State::NotExpr; | ||
153 | } else { | ||
154 | // We're closing one brace met before inside of the expression. | ||
155 | current_expr.push(chr); | ||
156 | inexpr_open_count -= 1; | ||
157 | } | ||
158 | } | ||
159 | (State::Expr, ':') => { | ||
160 | if inexpr_open_count == 0 { | ||
161 | // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}" | ||
162 | self.output.push(chr); | ||
163 | self.extracted_expressions.push(current_expr.trim().into()); | ||
164 | current_expr = String::new(); | ||
165 | self.state = State::FormatOpts; | ||
166 | } else { | ||
167 | // We're inside of braced expression, assume that it's a struct field name/value delimeter. | ||
168 | current_expr.push(chr); | ||
169 | } | ||
170 | } | ||
171 | (State::Expr, '{') => { | ||
172 | current_expr.push(chr); | ||
173 | inexpr_open_count += 1; | ||
174 | } | ||
175 | (State::Expr, _) => { | ||
176 | current_expr.push(chr); | ||
177 | } | ||
178 | (State::FormatOpts, '}') => { | ||
179 | self.output.push(chr); | ||
180 | self.state = State::NotExpr; | ||
181 | } | ||
182 | (State::FormatOpts, _) => { | ||
183 | self.output.push(chr); | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | if self.state != State::NotExpr { | ||
189 | return Err(()); | ||
190 | } | ||
191 | |||
192 | self.parsed = true; | ||
193 | Ok(()) | ||
194 | } | ||
195 | |||
196 | pub fn into_suggestion(&self, macro_name: &str) -> String { | ||
197 | assert!(self.parsed, "Attempt to get a suggestion from not parsed expression"); | ||
198 | |||
199 | let expressions_as_string = self.extracted_expressions.join(", "); | ||
200 | format!(r#"{}("{}", {})"#, macro_name, self.output, expressions_as_string) | ||
201 | } | ||
202 | } | ||
203 | |||
204 | #[cfg(test)] | ||
205 | mod tests { | ||
206 | use super::*; | ||
207 | use expect_test::{expect, Expect}; | ||
208 | |||
209 | fn check(input: &str, expect: &Expect) { | ||
210 | let mut parser = FormatStrParser::new((*input).to_owned()); | ||
211 | let outcome_repr = if parser.parse().is_ok() { | ||
212 | // Parsing should be OK, expected repr is "string; expr_1, expr_2". | ||
213 | if parser.extracted_expressions.is_empty() { | ||
214 | parser.output | ||
215 | } else { | ||
216 | format!("{}; {}", parser.output, parser.extracted_expressions.join(", ")) | ||
217 | } | ||
218 | } else { | ||
219 | // Parsing should fail, expected repr is "-". | ||
220 | "-".to_owned() | ||
221 | }; | ||
222 | |||
223 | expect.assert_eq(&outcome_repr); | ||
224 | } | ||
225 | |||
226 | #[test] | ||
227 | fn format_str_parser() { | ||
228 | let test_vector = &[ | ||
229 | ("no expressions", expect![["no expressions"]]), | ||
230 | ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]), | ||
231 | ("{expr:?}", expect![["{:?}; expr"]]), | ||
232 | ("{malformed", expect![["-"]]), | ||
233 | ("malformed}", expect![["-"]]), | ||
234 | ("{{correct", expect![["{{correct"]]), | ||
235 | ("correct}}", expect![["correct}}"]]), | ||
236 | ("{correct}}}", expect![["{}}}; correct"]]), | ||
237 | ("{correct}}}}}", expect![["{}}}}}; correct"]]), | ||
238 | ("{incorrect}}", expect![["-"]]), | ||
239 | ("placeholders {} {}", expect![["placeholders {} {}; $1, $2"]]), | ||
240 | ("mixed {} {2 + 2} {}", expect![["mixed {} {} {}; $1, 2 + 2, $2"]]), | ||
241 | ( | ||
242 | "{SomeStruct { val_a: 0, val_b: 1 }}", | ||
243 | expect![["{}; SomeStruct { val_a: 0, val_b: 1 }"]], | ||
244 | ), | ||
245 | ("{expr:?} is {2.32f64:.5}", expect![["{:?} is {:.5}; expr, 2.32f64"]]), | ||
246 | ( | ||
247 | "{SomeStruct { val_a: 0, val_b: 1 }:?}", | ||
248 | expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]], | ||
249 | ), | ||
250 | ("{ 2 + 2 }", expect![["{}; 2 + 2"]]), | ||
251 | ]; | ||
252 | |||
253 | for (input, output) in test_vector { | ||
254 | check(input, output) | ||
255 | } | ||
256 | } | ||
257 | |||
258 | #[test] | ||
259 | fn test_into_suggestion() { | ||
260 | let test_vector = &[ | ||
261 | ("println!", "{}", r#"println!("{}", $1)"#), | ||
262 | ( | ||
263 | "log::info!", | ||
264 | "{} {expr} {} {2 + 2}", | ||
265 | r#"log::info!("{} {} {} {}", $1, expr, $2, 2 + 2)"#, | ||
266 | ), | ||
267 | ("format!", "{expr:?}", r#"format!("{:?}", expr)"#), | ||
268 | ]; | ||
269 | |||
270 | for (kind, input, output) in test_vector { | ||
271 | let mut parser = FormatStrParser::new((*input).to_owned()); | ||
272 | parser.parse().expect("Parsing must succeed"); | ||
273 | |||
274 | assert_eq!(&parser.into_suggestion(*kind), output); | ||
275 | } | ||
276 | } | ||
277 | } | ||
diff --git a/crates/ide/src/completion/complete_qualified_path.rs b/crates/ide/src/completion/complete_qualified_path.rs index 00e89f0fd..2fafedd47 100644 --- a/crates/ide/src/completion/complete_qualified_path.rs +++ b/crates/ide/src/completion/complete_qualified_path.rs | |||
@@ -422,10 +422,10 @@ fn foo() { let _ = U::<|> } | |||
422 | fn completes_use_paths_across_crates() { | 422 | fn completes_use_paths_across_crates() { |
423 | check( | 423 | check( |
424 | r#" | 424 | r#" |
425 | //- /main.rs | 425 | //- /main.rs crate:main deps:foo |
426 | use foo::<|>; | 426 | use foo::<|>; |
427 | 427 | ||
428 | //- /foo/lib.rs | 428 | //- /foo/lib.rs crate:foo |
429 | pub mod bar { pub struct S; } | 429 | pub mod bar { pub struct S; } |
430 | "#, | 430 | "#, |
431 | expect![[r#" | 431 | expect![[r#" |
diff --git a/crates/ide/src/completion/complete_unqualified_path.rs b/crates/ide/src/completion/complete_unqualified_path.rs index 8eda4b64d..8b6757195 100644 --- a/crates/ide/src/completion/complete_unqualified_path.rs +++ b/crates/ide/src/completion/complete_unqualified_path.rs | |||
@@ -267,14 +267,34 @@ fn quux() { <|> } | |||
267 | ); | 267 | ); |
268 | } | 268 | } |
269 | 269 | ||
270 | /// Regression test for issue #6091. | ||
271 | #[test] | ||
272 | fn correctly_completes_module_items_prefixed_with_underscore() { | ||
273 | check_edit( | ||
274 | "_alpha", | ||
275 | r#" | ||
276 | fn main() { | ||
277 | _<|> | ||
278 | } | ||
279 | fn _alpha() {} | ||
280 | "#, | ||
281 | r#" | ||
282 | fn main() { | ||
283 | _alpha()$0 | ||
284 | } | ||
285 | fn _alpha() {} | ||
286 | "#, | ||
287 | ) | ||
288 | } | ||
289 | |||
270 | #[test] | 290 | #[test] |
271 | fn completes_extern_prelude() { | 291 | fn completes_extern_prelude() { |
272 | check( | 292 | check( |
273 | r#" | 293 | r#" |
274 | //- /lib.rs | 294 | //- /lib.rs crate:main deps:other_crate |
275 | use <|>; | 295 | use <|>; |
276 | 296 | ||
277 | //- /other_crate/lib.rs | 297 | //- /other_crate/lib.rs crate:other_crate |
278 | // nothing here | 298 | // nothing here |
279 | "#, | 299 | "#, |
280 | expect![[r#" | 300 | expect![[r#" |
@@ -350,10 +370,10 @@ fn foo() { | |||
350 | fn completes_prelude() { | 370 | fn completes_prelude() { |
351 | check( | 371 | check( |
352 | r#" | 372 | r#" |
353 | //- /main.rs | 373 | //- /main.rs crate:main deps:std |
354 | fn foo() { let x: <|> } | 374 | fn foo() { let x: <|> } |
355 | 375 | ||
356 | //- /std/lib.rs | 376 | //- /std/lib.rs crate:std |
357 | #[prelude_import] | 377 | #[prelude_import] |
358 | use prelude::*; | 378 | use prelude::*; |
359 | 379 | ||
@@ -371,16 +391,16 @@ mod prelude { struct Option; } | |||
371 | fn completes_std_prelude_if_core_is_defined() { | 391 | fn completes_std_prelude_if_core_is_defined() { |
372 | check( | 392 | check( |
373 | r#" | 393 | r#" |
374 | //- /main.rs | 394 | //- /main.rs crate:main deps:core,std |
375 | fn foo() { let x: <|> } | 395 | fn foo() { let x: <|> } |
376 | 396 | ||
377 | //- /core/lib.rs | 397 | //- /core/lib.rs crate:core |
378 | #[prelude_import] | 398 | #[prelude_import] |
379 | use prelude::*; | 399 | use prelude::*; |
380 | 400 | ||
381 | mod prelude { struct Option; } | 401 | mod prelude { struct Option; } |
382 | 402 | ||
383 | //- /std/lib.rs | 403 | //- /std/lib.rs crate:std deps:core |
384 | #[prelude_import] | 404 | #[prelude_import] |
385 | use prelude::*; | 405 | use prelude::*; |
386 | 406 | ||
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 671b13328..8dea8a4bf 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs | |||
@@ -221,10 +221,11 @@ impl<'a> CompletionContext<'a> { | |||
221 | Some(ctx) | 221 | Some(ctx) |
222 | } | 222 | } |
223 | 223 | ||
224 | // The range of the identifier that is being completed. | 224 | /// The range of the identifier that is being completed. |
225 | pub(crate) fn source_range(&self) -> TextRange { | 225 | pub(crate) fn source_range(&self) -> TextRange { |
226 | // check kind of macro-expanded token, but use range of original token | 226 | // check kind of macro-expanded token, but use range of original token |
227 | if self.token.kind() == IDENT || self.token.kind().is_keyword() { | 227 | let kind = self.token.kind(); |
228 | if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() { | ||
228 | mark::hit!(completes_if_prefix_is_keyword); | 229 | mark::hit!(completes_if_prefix_is_keyword); |
229 | self.original_token.text_range() | 230 | self.original_token.text_range() |
230 | } else { | 231 | } else { |
@@ -469,7 +470,7 @@ impl<'a> CompletionContext<'a> { | |||
469 | } | 470 | } |
470 | } else { | 471 | } else { |
471 | false | 472 | false |
472 | } | 473 | }; |
473 | } | 474 | } |
474 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { | 475 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { |
475 | // As above | 476 | // As above |
diff --git a/crates/ide/src/completion/presentation.rs b/crates/ide/src/completion/presentation.rs index 987cbfa7a..a5172b87e 100644 --- a/crates/ide/src/completion/presentation.rs +++ b/crates/ide/src/completion/presentation.rs | |||
@@ -1172,9 +1172,9 @@ fn foo(xs: Vec<i128>) | |||
1172 | check_edit( | 1172 | check_edit( |
1173 | "frobnicate!", | 1173 | "frobnicate!", |
1174 | r#" | 1174 | r#" |
1175 | //- /main.rs | 1175 | //- /main.rs crate:main deps:foo |
1176 | use foo::<|>; | 1176 | use foo::<|>; |
1177 | //- /foo/lib.rs | 1177 | //- /foo/lib.rs crate:foo |
1178 | #[macro_export] | 1178 | #[macro_export] |
1179 | macro_rules frobnicate { () => () } | 1179 | macro_rules frobnicate { () => () } |
1180 | "#, | 1180 | "#, |
diff --git a/crates/ide/src/completion/test_utils.rs b/crates/ide/src/completion/test_utils.rs index 1452d7e9e..feb8cd2a6 100644 --- a/crates/ide/src/completion/test_utils.rs +++ b/crates/ide/src/completion/test_utils.rs | |||
@@ -8,8 +8,7 @@ use test_utils::assert_eq_text; | |||
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | completion::{completion_item::CompletionKind, CompletionConfig}, | 10 | completion::{completion_item::CompletionKind, CompletionConfig}, |
11 | mock_analysis::analysis_and_position, | 11 | fixture, CompletionItem, |
12 | CompletionItem, | ||
13 | }; | 12 | }; |
14 | 13 | ||
15 | pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { | 14 | pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { |
@@ -80,7 +79,7 @@ pub(crate) fn check_edit_with_config( | |||
80 | ra_fixture_after: &str, | 79 | ra_fixture_after: &str, |
81 | ) { | 80 | ) { |
82 | let ra_fixture_after = trim_indent(ra_fixture_after); | 81 | let ra_fixture_after = trim_indent(ra_fixture_after); |
83 | let (analysis, position) = analysis_and_position(ra_fixture_before); | 82 | let (analysis, position) = fixture::position(ra_fixture_before); |
84 | let completions: Vec<CompletionItem> = | 83 | let completions: Vec<CompletionItem> = |
85 | analysis.completions(&config, position).unwrap().unwrap().into(); | 84 | analysis.completions(&config, position).unwrap().unwrap().into(); |
86 | let (completion,) = completions | 85 | let (completion,) = completions |
@@ -94,7 +93,7 @@ pub(crate) fn check_edit_with_config( | |||
94 | } | 93 | } |
95 | 94 | ||
96 | pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { | 95 | pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { |
97 | let (analysis, pos) = analysis_and_position(code); | 96 | let (analysis, pos) = fixture::position(code); |
98 | analysis | 97 | analysis |
99 | .with_db(|db| { | 98 | .with_db(|db| { |
100 | let sema = Semantics::new(db); | 99 | let sema = Semantics::new(db); |
@@ -109,6 +108,6 @@ pub(crate) fn get_all_completion_items( | |||
109 | config: CompletionConfig, | 108 | config: CompletionConfig, |
110 | code: &str, | 109 | code: &str, |
111 | ) -> Vec<CompletionItem> { | 110 | ) -> Vec<CompletionItem> { |
112 | let (analysis, position) = analysis_and_position(code); | 111 | let (analysis, position) = fixture::position(code); |
113 | analysis.completions(&config, position).unwrap().unwrap().into() | 112 | analysis.completions(&config, position).unwrap().unwrap().into() |
114 | } | 113 | } |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index dc815a483..f5d627b6e 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -218,10 +218,7 @@ mod tests { | |||
218 | use stdx::trim_indent; | 218 | use stdx::trim_indent; |
219 | use test_utils::assert_eq_text; | 219 | use test_utils::assert_eq_text; |
220 | 220 | ||
221 | use crate::{ | 221 | use crate::{fixture, DiagnosticsConfig}; |
222 | mock_analysis::{analysis_and_position, single_file, MockAnalysis}, | ||
223 | DiagnosticsConfig, | ||
224 | }; | ||
225 | 222 | ||
226 | /// Takes a multi-file input fixture with annotated cursor positions, | 223 | /// Takes a multi-file input fixture with annotated cursor positions, |
227 | /// and checks that: | 224 | /// and checks that: |
@@ -231,7 +228,7 @@ mod tests { | |||
231 | fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { | 228 | fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { |
232 | let after = trim_indent(ra_fixture_after); | 229 | let after = trim_indent(ra_fixture_after); |
233 | 230 | ||
234 | let (analysis, file_position) = analysis_and_position(ra_fixture_before); | 231 | let (analysis, file_position) = fixture::position(ra_fixture_before); |
235 | let diagnostic = analysis | 232 | let diagnostic = analysis |
236 | .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) | 233 | .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) |
237 | .unwrap() | 234 | .unwrap() |
@@ -260,7 +257,7 @@ mod tests { | |||
260 | /// which has a fix that can apply to other files. | 257 | /// which has a fix that can apply to other files. |
261 | fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) { | 258 | fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) { |
262 | let ra_fixture_after = &trim_indent(ra_fixture_after); | 259 | let ra_fixture_after = &trim_indent(ra_fixture_after); |
263 | let (analysis, file_pos) = analysis_and_position(ra_fixture_before); | 260 | let (analysis, file_pos) = fixture::position(ra_fixture_before); |
264 | let current_file_id = file_pos.file_id; | 261 | let current_file_id = file_pos.file_id; |
265 | let diagnostic = analysis | 262 | let diagnostic = analysis |
266 | .diagnostics(&DiagnosticsConfig::default(), current_file_id) | 263 | .diagnostics(&DiagnosticsConfig::default(), current_file_id) |
@@ -282,9 +279,7 @@ mod tests { | |||
282 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics | 279 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics |
283 | /// apply to the file containing the cursor. | 280 | /// apply to the file containing the cursor. |
284 | fn check_no_diagnostics(ra_fixture: &str) { | 281 | fn check_no_diagnostics(ra_fixture: &str) { |
285 | let mock = MockAnalysis::with_files(ra_fixture); | 282 | let (analysis, files) = fixture::files(ra_fixture); |
286 | let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>(); | ||
287 | let analysis = mock.analysis(); | ||
288 | let diagnostics = files | 283 | let diagnostics = files |
289 | .into_iter() | 284 | .into_iter() |
290 | .flat_map(|file_id| { | 285 | .flat_map(|file_id| { |
@@ -295,7 +290,7 @@ mod tests { | |||
295 | } | 290 | } |
296 | 291 | ||
297 | fn check_expect(ra_fixture: &str, expect: Expect) { | 292 | fn check_expect(ra_fixture: &str, expect: Expect) { |
298 | let (analysis, file_id) = single_file(ra_fixture); | 293 | let (analysis, file_id) = fixture::file(ra_fixture); |
299 | let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); | 294 | let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); |
300 | expect.assert_debug_eq(&diagnostics) | 295 | expect.assert_debug_eq(&diagnostics) |
301 | } | 296 | } |
@@ -304,7 +299,7 @@ mod tests { | |||
304 | fn test_wrap_return_type() { | 299 | fn test_wrap_return_type() { |
305 | check_fix( | 300 | check_fix( |
306 | r#" | 301 | r#" |
307 | //- /main.rs | 302 | //- /main.rs crate:main deps:core |
308 | use core::result::Result::{self, Ok, Err}; | 303 | use core::result::Result::{self, Ok, Err}; |
309 | 304 | ||
310 | fn div(x: i32, y: i32) -> Result<i32, ()> { | 305 | fn div(x: i32, y: i32) -> Result<i32, ()> { |
@@ -313,7 +308,7 @@ fn div(x: i32, y: i32) -> Result<i32, ()> { | |||
313 | } | 308 | } |
314 | x / y<|> | 309 | x / y<|> |
315 | } | 310 | } |
316 | //- /core/lib.rs | 311 | //- /core/lib.rs crate:core |
317 | pub mod result { | 312 | pub mod result { |
318 | pub enum Result<T, E> { Ok(T), Err(E) } | 313 | pub enum Result<T, E> { Ok(T), Err(E) } |
319 | } | 314 | } |
@@ -335,7 +330,7 @@ fn div(x: i32, y: i32) -> Result<i32, ()> { | |||
335 | fn test_wrap_return_type_handles_generic_functions() { | 330 | fn test_wrap_return_type_handles_generic_functions() { |
336 | check_fix( | 331 | check_fix( |
337 | r#" | 332 | r#" |
338 | //- /main.rs | 333 | //- /main.rs crate:main deps:core |
339 | use core::result::Result::{self, Ok, Err}; | 334 | use core::result::Result::{self, Ok, Err}; |
340 | 335 | ||
341 | fn div<T>(x: T) -> Result<T, i32> { | 336 | fn div<T>(x: T) -> Result<T, i32> { |
@@ -344,7 +339,7 @@ fn div<T>(x: T) -> Result<T, i32> { | |||
344 | } | 339 | } |
345 | <|>x | 340 | <|>x |
346 | } | 341 | } |
347 | //- /core/lib.rs | 342 | //- /core/lib.rs crate:core |
348 | pub mod result { | 343 | pub mod result { |
349 | pub enum Result<T, E> { Ok(T), Err(E) } | 344 | pub enum Result<T, E> { Ok(T), Err(E) } |
350 | } | 345 | } |
@@ -366,7 +361,7 @@ fn div<T>(x: T) -> Result<T, i32> { | |||
366 | fn test_wrap_return_type_handles_type_aliases() { | 361 | fn test_wrap_return_type_handles_type_aliases() { |
367 | check_fix( | 362 | check_fix( |
368 | r#" | 363 | r#" |
369 | //- /main.rs | 364 | //- /main.rs crate:main deps:core |
370 | use core::result::Result::{self, Ok, Err}; | 365 | use core::result::Result::{self, Ok, Err}; |
371 | 366 | ||
372 | type MyResult<T> = Result<T, ()>; | 367 | type MyResult<T> = Result<T, ()>; |
@@ -377,7 +372,7 @@ fn div(x: i32, y: i32) -> MyResult<i32> { | |||
377 | } | 372 | } |
378 | x <|>/ y | 373 | x <|>/ y |
379 | } | 374 | } |
380 | //- /core/lib.rs | 375 | //- /core/lib.rs crate:core |
381 | pub mod result { | 376 | pub mod result { |
382 | pub enum Result<T, E> { Ok(T), Err(E) } | 377 | pub enum Result<T, E> { Ok(T), Err(E) } |
383 | } | 378 | } |
@@ -401,12 +396,12 @@ fn div(x: i32, y: i32) -> MyResult<i32> { | |||
401 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { | 396 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { |
402 | check_no_diagnostics( | 397 | check_no_diagnostics( |
403 | r#" | 398 | r#" |
404 | //- /main.rs | 399 | //- /main.rs crate:main deps:core |
405 | use core::result::Result::{self, Ok, Err}; | 400 | use core::result::Result::{self, Ok, Err}; |
406 | 401 | ||
407 | fn foo() -> Result<(), i32> { 0 } | 402 | fn foo() -> Result<(), i32> { 0 } |
408 | 403 | ||
409 | //- /core/lib.rs | 404 | //- /core/lib.rs crate:core |
410 | pub mod result { | 405 | pub mod result { |
411 | pub enum Result<T, E> { Ok(T), Err(E) } | 406 | pub enum Result<T, E> { Ok(T), Err(E) } |
412 | } | 407 | } |
@@ -418,14 +413,14 @@ pub mod result { | |||
418 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { | 413 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { |
419 | check_no_diagnostics( | 414 | check_no_diagnostics( |
420 | r#" | 415 | r#" |
421 | //- /main.rs | 416 | //- /main.rs crate:main deps:core |
422 | use core::result::Result::{self, Ok, Err}; | 417 | use core::result::Result::{self, Ok, Err}; |
423 | 418 | ||
424 | enum SomeOtherEnum { Ok(i32), Err(String) } | 419 | enum SomeOtherEnum { Ok(i32), Err(String) } |
425 | 420 | ||
426 | fn foo() -> SomeOtherEnum { 0 } | 421 | fn foo() -> SomeOtherEnum { 0 } |
427 | 422 | ||
428 | //- /core/lib.rs | 423 | //- /core/lib.rs crate:core |
429 | pub mod result { | 424 | pub mod result { |
430 | pub enum Result<T, E> { Ok(T), Err(E) } | 425 | pub enum Result<T, E> { Ok(T), Err(E) } |
431 | } | 426 | } |
@@ -567,7 +562,7 @@ fn test_fn() { | |||
567 | file_system_edits: [ | 562 | file_system_edits: [ |
568 | CreateFile { | 563 | CreateFile { |
569 | anchor: FileId( | 564 | anchor: FileId( |
570 | 1, | 565 | 0, |
571 | ), | 566 | ), |
572 | dst: "foo.rs", | 567 | dst: "foo.rs", |
573 | }, | 568 | }, |
@@ -787,7 +782,7 @@ struct Foo { | |||
787 | let mut config = DiagnosticsConfig::default(); | 782 | let mut config = DiagnosticsConfig::default(); |
788 | config.disabled.insert("unresolved-module".into()); | 783 | config.disabled.insert("unresolved-module".into()); |
789 | 784 | ||
790 | let (analysis, file_id) = single_file(r#"mod foo;"#); | 785 | let (analysis, file_id) = fixture::file(r#"mod foo;"#); |
791 | 786 | ||
792 | let diagnostics = analysis.diagnostics(&config, file_id).unwrap(); | 787 | let diagnostics = analysis.diagnostics(&config, file_id).unwrap(); |
793 | assert!(diagnostics.is_empty()); | 788 | assert!(diagnostics.is_empty()); |
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index 1ee80c2dd..cf9d617dc 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -423,11 +423,11 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> | |||
423 | mod tests { | 423 | mod tests { |
424 | use expect_test::expect; | 424 | use expect_test::expect; |
425 | 425 | ||
426 | use crate::{mock_analysis::single_file, Query}; | 426 | use crate::{fixture, Query}; |
427 | 427 | ||
428 | #[test] | 428 | #[test] |
429 | fn test_nav_for_symbol() { | 429 | fn test_nav_for_symbol() { |
430 | let (analysis, _) = single_file( | 430 | let (analysis, _) = fixture::file( |
431 | r#" | 431 | r#" |
432 | enum FooInner { } | 432 | enum FooInner { } |
433 | fn foo() { enum FooInner { } } | 433 | fn foo() { enum FooInner { } } |
@@ -439,7 +439,7 @@ fn foo() { enum FooInner { } } | |||
439 | [ | 439 | [ |
440 | NavigationTarget { | 440 | NavigationTarget { |
441 | file_id: FileId( | 441 | file_id: FileId( |
442 | 1, | 442 | 0, |
443 | ), | 443 | ), |
444 | full_range: 0..17, | 444 | full_range: 0..17, |
445 | focus_range: Some( | 445 | focus_range: Some( |
@@ -455,7 +455,7 @@ fn foo() { enum FooInner { } } | |||
455 | }, | 455 | }, |
456 | NavigationTarget { | 456 | NavigationTarget { |
457 | file_id: FileId( | 457 | file_id: FileId( |
458 | 1, | 458 | 0, |
459 | ), | 459 | ), |
460 | full_range: 29..46, | 460 | full_range: 29..46, |
461 | focus_range: Some( | 461 | focus_range: Some( |
@@ -478,7 +478,7 @@ fn foo() { enum FooInner { } } | |||
478 | 478 | ||
479 | #[test] | 479 | #[test] |
480 | fn test_world_symbols_are_case_sensitive() { | 480 | fn test_world_symbols_are_case_sensitive() { |
481 | let (analysis, _) = single_file( | 481 | let (analysis, _) = fixture::file( |
482 | r#" | 482 | r#" |
483 | fn foo() {} | 483 | fn foo() {} |
484 | struct Foo; | 484 | struct Foo; |
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 8a285bcf7..8d75e0f05 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs | |||
@@ -122,10 +122,10 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { | |||
122 | mod tests { | 122 | mod tests { |
123 | use expect_test::{expect, Expect}; | 123 | use expect_test::{expect, Expect}; |
124 | 124 | ||
125 | use crate::mock_analysis::analysis_and_position; | 125 | use crate::fixture; |
126 | 126 | ||
127 | fn check(ra_fixture: &str, expect: Expect) { | 127 | fn check(ra_fixture: &str, expect: Expect) { |
128 | let (analysis, pos) = analysis_and_position(ra_fixture); | 128 | let (analysis, pos) = fixture::position(ra_fixture); |
129 | let expansion = analysis.expand_macro(pos).unwrap().unwrap(); | 129 | let expansion = analysis.expand_macro(pos).unwrap().unwrap(); |
130 | let actual = format!("{}\n{}", expansion.name, expansion.expansion); | 130 | let actual = format!("{}\n{}", expansion.name, expansion.expansion); |
131 | expect.assert_eq(&actual); | 131 | expect.assert_eq(&actual); |
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs index 34563a026..3ee0af8ad 100644 --- a/crates/ide/src/extend_selection.rs +++ b/crates/ide/src/extend_selection.rs | |||
@@ -315,12 +315,12 @@ fn adj_comments(comment: &ast::Comment, dir: Direction) -> ast::Comment { | |||
315 | 315 | ||
316 | #[cfg(test)] | 316 | #[cfg(test)] |
317 | mod tests { | 317 | mod tests { |
318 | use crate::mock_analysis::analysis_and_position; | 318 | use crate::fixture; |
319 | 319 | ||
320 | use super::*; | 320 | use super::*; |
321 | 321 | ||
322 | fn do_check(before: &str, afters: &[&str]) { | 322 | fn do_check(before: &str, afters: &[&str]) { |
323 | let (analysis, position) = analysis_and_position(&before); | 323 | let (analysis, position) = fixture::position(&before); |
324 | let before = analysis.file_text(position.file_id).unwrap(); | 324 | let before = analysis.file_text(position.file_id).unwrap(); |
325 | let range = TextRange::empty(position.offset); | 325 | let range = TextRange::empty(position.offset); |
326 | let mut frange = FileRange { file_id: position.file_id, range }; | 326 | let mut frange = FileRange { file_id: position.file_id, range }; |
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs new file mode 100644 index 000000000..ed06689f0 --- /dev/null +++ b/crates/ide/src/fixture.rs | |||
@@ -0,0 +1,70 @@ | |||
1 | //! Utilities for creating `Analysis` instances for tests. | ||
2 | use base_db::fixture::ChangeFixture; | ||
3 | use test_utils::{extract_annotations, RangeOrOffset}; | ||
4 | |||
5 | use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; | ||
6 | |||
7 | /// Creates analysis for a single file. | ||
8 | pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) { | ||
9 | let mut host = AnalysisHost::default(); | ||
10 | let change_fixture = ChangeFixture::parse(ra_fixture); | ||
11 | host.db.apply_change(change_fixture.change); | ||
12 | (host.analysis(), change_fixture.files[0]) | ||
13 | } | ||
14 | |||
15 | /// Creates analysis for many files. | ||
16 | pub(crate) fn files(ra_fixture: &str) -> (Analysis, Vec<FileId>) { | ||
17 | let mut host = AnalysisHost::default(); | ||
18 | let change_fixture = ChangeFixture::parse(ra_fixture); | ||
19 | host.db.apply_change(change_fixture.change); | ||
20 | (host.analysis(), change_fixture.files) | ||
21 | } | ||
22 | |||
23 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. | ||
24 | pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { | ||
25 | let mut host = AnalysisHost::default(); | ||
26 | let change_fixture = ChangeFixture::parse(ra_fixture); | ||
27 | host.db.apply_change(change_fixture.change); | ||
28 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); | ||
29 | let offset = match range_or_offset { | ||
30 | RangeOrOffset::Range(_) => panic!(), | ||
31 | RangeOrOffset::Offset(it) => it, | ||
32 | }; | ||
33 | (host.analysis(), FilePosition { file_id, offset }) | ||
34 | } | ||
35 | |||
36 | /// Creates analysis for a single file, returns range marked with a pair of <|>. | ||
37 | pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) { | ||
38 | let mut host = AnalysisHost::default(); | ||
39 | let change_fixture = ChangeFixture::parse(ra_fixture); | ||
40 | host.db.apply_change(change_fixture.change); | ||
41 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); | ||
42 | let range = match range_or_offset { | ||
43 | RangeOrOffset::Range(it) => it, | ||
44 | RangeOrOffset::Offset(_) => panic!(), | ||
45 | }; | ||
46 | (host.analysis(), FileRange { file_id, range }) | ||
47 | } | ||
48 | |||
49 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. | ||
50 | pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) { | ||
51 | let mut host = AnalysisHost::default(); | ||
52 | let change_fixture = ChangeFixture::parse(ra_fixture); | ||
53 | host.db.apply_change(change_fixture.change); | ||
54 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); | ||
55 | let offset = match range_or_offset { | ||
56 | RangeOrOffset::Range(_) => panic!(), | ||
57 | RangeOrOffset::Offset(it) => it, | ||
58 | }; | ||
59 | |||
60 | let annotations = change_fixture | ||
61 | .files | ||
62 | .iter() | ||
63 | .flat_map(|&file_id| { | ||
64 | let file_text = host.analysis().file_text(file_id).unwrap(); | ||
65 | let annotations = extract_annotations(&file_text); | ||
66 | annotations.into_iter().map(move |(range, data)| (FileRange { file_id, range }, data)) | ||
67 | }) | ||
68 | .collect(); | ||
69 | (host.analysis(), FilePosition { file_id, offset }, annotations) | ||
70 | } | ||
diff --git a/crates/ide/src/fn_references.rs b/crates/ide/src/fn_references.rs index 1989a562b..459f201ed 100644 --- a/crates/ide/src/fn_references.rs +++ b/crates/ide/src/fn_references.rs | |||
@@ -25,15 +25,14 @@ fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> { | |||
25 | 25 | ||
26 | #[cfg(test)] | 26 | #[cfg(test)] |
27 | mod tests { | 27 | mod tests { |
28 | use crate::mock_analysis::analysis_and_position; | 28 | use crate::fixture; |
29 | use crate::{FileRange, TextSize}; | 29 | use crate::{FileRange, TextSize}; |
30 | use std::ops::RangeInclusive; | 30 | use std::ops::RangeInclusive; |
31 | 31 | ||
32 | #[test] | 32 | #[test] |
33 | fn test_find_all_methods() { | 33 | fn test_find_all_methods() { |
34 | let (analysis, pos) = analysis_and_position( | 34 | let (analysis, pos) = fixture::position( |
35 | r#" | 35 | r#" |
36 | //- /lib.rs | ||
37 | fn private_fn() {<|>} | 36 | fn private_fn() {<|>} |
38 | 37 | ||
39 | pub fn pub_fn() {} | 38 | pub fn pub_fn() {} |
@@ -48,9 +47,8 @@ mod tests { | |||
48 | 47 | ||
49 | #[test] | 48 | #[test] |
50 | fn test_find_trait_methods() { | 49 | fn test_find_trait_methods() { |
51 | let (analysis, pos) = analysis_and_position( | 50 | let (analysis, pos) = fixture::position( |
52 | r#" | 51 | r#" |
53 | //- /lib.rs | ||
54 | trait Foo { | 52 | trait Foo { |
55 | fn bar() {<|>} | 53 | fn bar() {<|>} |
56 | fn baz() {} | 54 | fn baz() {} |
@@ -64,7 +62,7 @@ mod tests { | |||
64 | 62 | ||
65 | #[test] | 63 | #[test] |
66 | fn test_skip_tests() { | 64 | fn test_skip_tests() { |
67 | let (analysis, pos) = analysis_and_position( | 65 | let (analysis, pos) = fixture::position( |
68 | r#" | 66 | r#" |
69 | //- /lib.rs | 67 | //- /lib.rs |
70 | #[test] | 68 | #[test] |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 15e9b7fad..582bf4837 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -103,12 +103,11 @@ mod tests { | |||
103 | use base_db::FileRange; | 103 | use base_db::FileRange; |
104 | use syntax::{TextRange, TextSize}; | 104 | use syntax::{TextRange, TextSize}; |
105 | 105 | ||
106 | use crate::mock_analysis::MockAnalysis; | 106 | use crate::fixture; |
107 | 107 | ||
108 | fn check(ra_fixture: &str) { | 108 | fn check(ra_fixture: &str) { |
109 | let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); | 109 | let (analysis, position, mut annotations) = fixture::annotations(ra_fixture); |
110 | let (mut expected, data) = mock.annotation(); | 110 | let (mut expected, data) = annotations.pop().unwrap(); |
111 | let analysis = mock.analysis(); | ||
112 | match data.as_str() { | 111 | match data.as_str() { |
113 | "" => (), | 112 | "" => (), |
114 | "file" => { | 113 | "file" => { |
@@ -133,9 +132,9 @@ mod tests { | |||
133 | fn goto_def_for_extern_crate() { | 132 | fn goto_def_for_extern_crate() { |
134 | check( | 133 | check( |
135 | r#" | 134 | r#" |
136 | //- /main.rs | 135 | //- /main.rs crate:main deps:std |
137 | extern crate std<|>; | 136 | extern crate std<|>; |
138 | //- /std/lib.rs | 137 | //- /std/lib.rs crate:std |
139 | // empty | 138 | // empty |
140 | //^ file | 139 | //^ file |
141 | "#, | 140 | "#, |
@@ -146,9 +145,9 @@ mod tests { | |||
146 | fn goto_def_for_renamed_extern_crate() { | 145 | fn goto_def_for_renamed_extern_crate() { |
147 | check( | 146 | check( |
148 | r#" | 147 | r#" |
149 | //- /main.rs | 148 | //- /main.rs crate:main deps:std |
150 | extern crate std as abc<|>; | 149 | extern crate std as abc<|>; |
151 | //- /std/lib.rs | 150 | //- /std/lib.rs crate:std |
152 | // empty | 151 | // empty |
153 | //^ file | 152 | //^ file |
154 | "#, | 153 | "#, |
@@ -342,10 +341,10 @@ fn bar() { | |||
342 | fn goto_def_for_use_alias() { | 341 | fn goto_def_for_use_alias() { |
343 | check( | 342 | check( |
344 | r#" | 343 | r#" |
345 | //- /lib.rs | 344 | //- /lib.rs crate:main deps:foo |
346 | use foo as bar<|>; | 345 | use foo as bar<|>; |
347 | 346 | ||
348 | //- /foo/lib.rs | 347 | //- /foo/lib.rs crate:foo |
349 | // empty | 348 | // empty |
350 | //^ file | 349 | //^ file |
351 | "#, | 350 | "#, |
@@ -356,10 +355,10 @@ use foo as bar<|>; | |||
356 | fn goto_def_for_use_alias_foo_macro() { | 355 | fn goto_def_for_use_alias_foo_macro() { |
357 | check( | 356 | check( |
358 | r#" | 357 | r#" |
359 | //- /lib.rs | 358 | //- /lib.rs crate:main deps:foo |
360 | use foo::foo as bar<|>; | 359 | use foo::foo as bar<|>; |
361 | 360 | ||
362 | //- /foo/lib.rs | 361 | //- /foo/lib.rs crate:foo |
363 | #[macro_export] | 362 | #[macro_export] |
364 | macro_rules! foo { () => { () } } | 363 | macro_rules! foo { () => { () } } |
365 | //^^^ | 364 | //^^^ |
@@ -371,7 +370,6 @@ macro_rules! foo { () => { () } } | |||
371 | fn goto_def_for_methods() { | 370 | fn goto_def_for_methods() { |
372 | check( | 371 | check( |
373 | r#" | 372 | r#" |
374 | //- /lib.rs | ||
375 | struct Foo; | 373 | struct Foo; |
376 | impl Foo { | 374 | impl Foo { |
377 | fn frobnicate(&self) { } | 375 | fn frobnicate(&self) { } |
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index f503f4ec5..6c586bbd1 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs | |||
@@ -76,12 +76,10 @@ fn impls_for_trait( | |||
76 | mod tests { | 76 | mod tests { |
77 | use base_db::FileRange; | 77 | use base_db::FileRange; |
78 | 78 | ||
79 | use crate::mock_analysis::MockAnalysis; | 79 | use crate::fixture; |
80 | 80 | ||
81 | fn check(ra_fixture: &str) { | 81 | fn check(ra_fixture: &str) { |
82 | let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); | 82 | let (analysis, position, annotations) = fixture::annotations(ra_fixture); |
83 | let annotations = mock.annotations(); | ||
84 | let analysis = mock.analysis(); | ||
85 | 83 | ||
86 | let navs = analysis.goto_implementation(position).unwrap().unwrap().info; | 84 | let navs = analysis.goto_implementation(position).unwrap().unwrap().info; |
87 | 85 | ||
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index 4a151b150..6d0df04dd 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs | |||
@@ -56,13 +56,12 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
56 | mod tests { | 56 | mod tests { |
57 | use base_db::FileRange; | 57 | use base_db::FileRange; |
58 | 58 | ||
59 | use crate::mock_analysis::MockAnalysis; | 59 | use crate::fixture; |
60 | 60 | ||
61 | fn check(ra_fixture: &str) { | 61 | fn check(ra_fixture: &str) { |
62 | let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); | 62 | let (analysis, position, mut annotations) = fixture::annotations(ra_fixture); |
63 | let (expected, data) = mock.annotation(); | 63 | let (expected, data) = annotations.pop().unwrap(); |
64 | assert!(data.is_empty()); | 64 | assert!(data.is_empty()); |
65 | let analysis = mock.analysis(); | ||
66 | 65 | ||
67 | let mut navs = analysis.goto_type_definition(position).unwrap().unwrap().info; | 66 | let mut navs = analysis.goto_type_definition(position).unwrap().unwrap().info; |
68 | assert_eq!(navs.len(), 1); | 67 | assert_eq!(navs.len(), 1); |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index bb9f12cd3..9cf02f0a3 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -377,17 +377,17 @@ mod tests { | |||
377 | use base_db::FileLoader; | 377 | use base_db::FileLoader; |
378 | use expect_test::{expect, Expect}; | 378 | use expect_test::{expect, Expect}; |
379 | 379 | ||
380 | use crate::mock_analysis::analysis_and_position; | 380 | use crate::fixture; |
381 | 381 | ||
382 | use super::*; | 382 | use super::*; |
383 | 383 | ||
384 | fn check_hover_no_result(ra_fixture: &str) { | 384 | fn check_hover_no_result(ra_fixture: &str) { |
385 | let (analysis, position) = analysis_and_position(ra_fixture); | 385 | let (analysis, position) = fixture::position(ra_fixture); |
386 | assert!(analysis.hover(position, true).unwrap().is_none()); | 386 | assert!(analysis.hover(position, true).unwrap().is_none()); |
387 | } | 387 | } |
388 | 388 | ||
389 | fn check(ra_fixture: &str, expect: Expect) { | 389 | fn check(ra_fixture: &str, expect: Expect) { |
390 | let (analysis, position) = analysis_and_position(ra_fixture); | 390 | let (analysis, position) = fixture::position(ra_fixture); |
391 | let hover = analysis.hover(position, true).unwrap().unwrap(); | 391 | let hover = analysis.hover(position, true).unwrap().unwrap(); |
392 | 392 | ||
393 | let content = analysis.db.file_text(position.file_id); | 393 | let content = analysis.db.file_text(position.file_id); |
@@ -398,7 +398,7 @@ mod tests { | |||
398 | } | 398 | } |
399 | 399 | ||
400 | fn check_hover_no_links(ra_fixture: &str, expect: Expect) { | 400 | fn check_hover_no_links(ra_fixture: &str, expect: Expect) { |
401 | let (analysis, position) = analysis_and_position(ra_fixture); | 401 | let (analysis, position) = fixture::position(ra_fixture); |
402 | let hover = analysis.hover(position, false).unwrap().unwrap(); | 402 | let hover = analysis.hover(position, false).unwrap().unwrap(); |
403 | 403 | ||
404 | let content = analysis.db.file_text(position.file_id); | 404 | let content = analysis.db.file_text(position.file_id); |
@@ -409,7 +409,7 @@ mod tests { | |||
409 | } | 409 | } |
410 | 410 | ||
411 | fn check_actions(ra_fixture: &str, expect: Expect) { | 411 | fn check_actions(ra_fixture: &str, expect: Expect) { |
412 | let (analysis, position) = analysis_and_position(ra_fixture); | 412 | let (analysis, position) = fixture::position(ra_fixture); |
413 | let hover = analysis.hover(position, true).unwrap().unwrap(); | 413 | let hover = analysis.hover(position, true).unwrap().unwrap(); |
414 | expect.assert_debug_eq(&hover.info.actions) | 414 | expect.assert_debug_eq(&hover.info.actions) |
415 | } | 415 | } |
@@ -963,7 +963,7 @@ impl Thing { | |||
963 | "#]], | 963 | "#]], |
964 | ) | 964 | ) |
965 | } /* FIXME: revive these tests | 965 | } /* FIXME: revive these tests |
966 | let (analysis, position) = analysis_and_position( | 966 | let (analysis, position) = fixture::position( |
967 | " | 967 | " |
968 | struct Thing { x: u32 } | 968 | struct Thing { x: u32 } |
969 | impl Thing { | 969 | impl Thing { |
@@ -977,7 +977,7 @@ impl Thing { | |||
977 | let hover = analysis.hover(position).unwrap().unwrap(); | 977 | let hover = analysis.hover(position).unwrap().unwrap(); |
978 | assert_eq!(trim_markup(&hover.info.markup.as_str()), ("Thing")); | 978 | assert_eq!(trim_markup(&hover.info.markup.as_str()), ("Thing")); |
979 | 979 | ||
980 | let (analysis, position) = analysis_and_position( | 980 | let (analysis, position) = fixture::position( |
981 | " | 981 | " |
982 | enum Thing { A } | 982 | enum Thing { A } |
983 | impl Thing { | 983 | impl Thing { |
@@ -990,7 +990,7 @@ impl Thing { | |||
990 | let hover = analysis.hover(position).unwrap().unwrap(); | 990 | let hover = analysis.hover(position).unwrap().unwrap(); |
991 | assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing")); | 991 | assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing")); |
992 | 992 | ||
993 | let (analysis, position) = analysis_and_position( | 993 | let (analysis, position) = fixture::position( |
994 | " | 994 | " |
995 | enum Thing { A } | 995 | enum Thing { A } |
996 | impl Thing { | 996 | impl Thing { |
@@ -1275,7 +1275,7 @@ fn bar() { fo<|>o(); } | |||
1275 | Implementaion( | 1275 | Implementaion( |
1276 | FilePosition { | 1276 | FilePosition { |
1277 | file_id: FileId( | 1277 | file_id: FileId( |
1278 | 1, | 1278 | 0, |
1279 | ), | 1279 | ), |
1280 | offset: 13, | 1280 | offset: 13, |
1281 | }, | 1281 | }, |
@@ -1289,9 +1289,9 @@ fn bar() { fo<|>o(); } | |||
1289 | fn test_hover_extern_crate() { | 1289 | fn test_hover_extern_crate() { |
1290 | check( | 1290 | check( |
1291 | r#" | 1291 | r#" |
1292 | //- /main.rs | 1292 | //- /main.rs crate:main deps:std |
1293 | extern crate st<|>d; | 1293 | extern crate st<|>d; |
1294 | //- /std/lib.rs | 1294 | //- /std/lib.rs crate:std |
1295 | //! Standard library for this test | 1295 | //! Standard library for this test |
1296 | //! | 1296 | //! |
1297 | //! Printed? | 1297 | //! Printed? |
@@ -1307,9 +1307,9 @@ extern crate st<|>d; | |||
1307 | ); | 1307 | ); |
1308 | check( | 1308 | check( |
1309 | r#" | 1309 | r#" |
1310 | //- /main.rs | 1310 | //- /main.rs crate:main deps:std |
1311 | extern crate std as ab<|>c; | 1311 | extern crate std as ab<|>c; |
1312 | //- /std/lib.rs | 1312 | //- /std/lib.rs crate:std |
1313 | //! Standard library for this test | 1313 | //! Standard library for this test |
1314 | //! | 1314 | //! |
1315 | //! Printed? | 1315 | //! Printed? |
@@ -1989,7 +1989,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
1989 | Implementaion( | 1989 | Implementaion( |
1990 | FilePosition { | 1990 | FilePosition { |
1991 | file_id: FileId( | 1991 | file_id: FileId( |
1992 | 1, | 1992 | 0, |
1993 | ), | 1993 | ), |
1994 | offset: 6, | 1994 | offset: 6, |
1995 | }, | 1995 | }, |
@@ -2008,7 +2008,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
2008 | Implementaion( | 2008 | Implementaion( |
2009 | FilePosition { | 2009 | FilePosition { |
2010 | file_id: FileId( | 2010 | file_id: FileId( |
2011 | 1, | 2011 | 0, |
2012 | ), | 2012 | ), |
2013 | offset: 7, | 2013 | offset: 7, |
2014 | }, | 2014 | }, |
@@ -2027,7 +2027,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
2027 | Implementaion( | 2027 | Implementaion( |
2028 | FilePosition { | 2028 | FilePosition { |
2029 | file_id: FileId( | 2029 | file_id: FileId( |
2030 | 1, | 2030 | 0, |
2031 | ), | 2031 | ), |
2032 | offset: 6, | 2032 | offset: 6, |
2033 | }, | 2033 | }, |
@@ -2046,7 +2046,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
2046 | Implementaion( | 2046 | Implementaion( |
2047 | FilePosition { | 2047 | FilePosition { |
2048 | file_id: FileId( | 2048 | file_id: FileId( |
2049 | 1, | 2049 | 0, |
2050 | ), | 2050 | ), |
2051 | offset: 5, | 2051 | offset: 5, |
2052 | }, | 2052 | }, |
@@ -2069,7 +2069,7 @@ fn foo_<|>test() {} | |||
2069 | Runnable { | 2069 | Runnable { |
2070 | nav: NavigationTarget { | 2070 | nav: NavigationTarget { |
2071 | file_id: FileId( | 2071 | file_id: FileId( |
2072 | 1, | 2072 | 0, |
2073 | ), | 2073 | ), |
2074 | full_range: 0..24, | 2074 | full_range: 0..24, |
2075 | focus_range: Some( | 2075 | focus_range: Some( |
@@ -2112,7 +2112,7 @@ mod tests<|> { | |||
2112 | Runnable { | 2112 | Runnable { |
2113 | nav: NavigationTarget { | 2113 | nav: NavigationTarget { |
2114 | file_id: FileId( | 2114 | file_id: FileId( |
2115 | 1, | 2115 | 0, |
2116 | ), | 2116 | ), |
2117 | full_range: 0..46, | 2117 | full_range: 0..46, |
2118 | focus_range: Some( | 2118 | focus_range: Some( |
@@ -2151,7 +2151,7 @@ fn main() { let s<|>t = S{ f1:0 }; } | |||
2151 | mod_path: "test::S", | 2151 | mod_path: "test::S", |
2152 | nav: NavigationTarget { | 2152 | nav: NavigationTarget { |
2153 | file_id: FileId( | 2153 | file_id: FileId( |
2154 | 1, | 2154 | 0, |
2155 | ), | 2155 | ), |
2156 | full_range: 0..19, | 2156 | full_range: 0..19, |
2157 | focus_range: Some( | 2157 | focus_range: Some( |
@@ -2190,7 +2190,7 @@ fn main() { let s<|>t = S{ f1:Arg(0) }; } | |||
2190 | mod_path: "test::S", | 2190 | mod_path: "test::S", |
2191 | nav: NavigationTarget { | 2191 | nav: NavigationTarget { |
2192 | file_id: FileId( | 2192 | file_id: FileId( |
2193 | 1, | 2193 | 0, |
2194 | ), | 2194 | ), |
2195 | full_range: 17..37, | 2195 | full_range: 17..37, |
2196 | focus_range: Some( | 2196 | focus_range: Some( |
@@ -2209,7 +2209,7 @@ fn main() { let s<|>t = S{ f1:Arg(0) }; } | |||
2209 | mod_path: "test::Arg", | 2209 | mod_path: "test::Arg", |
2210 | nav: NavigationTarget { | 2210 | nav: NavigationTarget { |
2211 | file_id: FileId( | 2211 | file_id: FileId( |
2212 | 1, | 2212 | 0, |
2213 | ), | 2213 | ), |
2214 | full_range: 0..16, | 2214 | full_range: 0..16, |
2215 | focus_range: Some( | 2215 | focus_range: Some( |
@@ -2248,7 +2248,7 @@ fn main() { let s<|>t = S{ f1: S{ f1: Arg(0) } }; } | |||
2248 | mod_path: "test::S", | 2248 | mod_path: "test::S", |
2249 | nav: NavigationTarget { | 2249 | nav: NavigationTarget { |
2250 | file_id: FileId( | 2250 | file_id: FileId( |
2251 | 1, | 2251 | 0, |
2252 | ), | 2252 | ), |
2253 | full_range: 17..37, | 2253 | full_range: 17..37, |
2254 | focus_range: Some( | 2254 | focus_range: Some( |
@@ -2267,7 +2267,7 @@ fn main() { let s<|>t = S{ f1: S{ f1: Arg(0) } }; } | |||
2267 | mod_path: "test::Arg", | 2267 | mod_path: "test::Arg", |
2268 | nav: NavigationTarget { | 2268 | nav: NavigationTarget { |
2269 | file_id: FileId( | 2269 | file_id: FileId( |
2270 | 1, | 2270 | 0, |
2271 | ), | 2271 | ), |
2272 | full_range: 0..16, | 2272 | full_range: 0..16, |
2273 | focus_range: Some( | 2273 | focus_range: Some( |
@@ -2309,7 +2309,7 @@ fn main() { let s<|>t = (A(1), B(2), M::C(3) ); } | |||
2309 | mod_path: "test::A", | 2309 | mod_path: "test::A", |
2310 | nav: NavigationTarget { | 2310 | nav: NavigationTarget { |
2311 | file_id: FileId( | 2311 | file_id: FileId( |
2312 | 1, | 2312 | 0, |
2313 | ), | 2313 | ), |
2314 | full_range: 0..14, | 2314 | full_range: 0..14, |
2315 | focus_range: Some( | 2315 | focus_range: Some( |
@@ -2328,7 +2328,7 @@ fn main() { let s<|>t = (A(1), B(2), M::C(3) ); } | |||
2328 | mod_path: "test::B", | 2328 | mod_path: "test::B", |
2329 | nav: NavigationTarget { | 2329 | nav: NavigationTarget { |
2330 | file_id: FileId( | 2330 | file_id: FileId( |
2331 | 1, | 2331 | 0, |
2332 | ), | 2332 | ), |
2333 | full_range: 15..29, | 2333 | full_range: 15..29, |
2334 | focus_range: Some( | 2334 | focus_range: Some( |
@@ -2347,7 +2347,7 @@ fn main() { let s<|>t = (A(1), B(2), M::C(3) ); } | |||
2347 | mod_path: "test::M::C", | 2347 | mod_path: "test::M::C", |
2348 | nav: NavigationTarget { | 2348 | nav: NavigationTarget { |
2349 | file_id: FileId( | 2349 | file_id: FileId( |
2350 | 1, | 2350 | 0, |
2351 | ), | 2351 | ), |
2352 | full_range: 42..60, | 2352 | full_range: 42..60, |
2353 | focus_range: Some( | 2353 | focus_range: Some( |
@@ -2386,7 +2386,7 @@ fn main() { let s<|>t = foo(); } | |||
2386 | mod_path: "test::Foo", | 2386 | mod_path: "test::Foo", |
2387 | nav: NavigationTarget { | 2387 | nav: NavigationTarget { |
2388 | file_id: FileId( | 2388 | file_id: FileId( |
2389 | 1, | 2389 | 0, |
2390 | ), | 2390 | ), |
2391 | full_range: 0..12, | 2391 | full_range: 0..12, |
2392 | focus_range: Some( | 2392 | focus_range: Some( |
@@ -2426,7 +2426,7 @@ fn main() { let s<|>t = foo(); } | |||
2426 | mod_path: "test::Foo", | 2426 | mod_path: "test::Foo", |
2427 | nav: NavigationTarget { | 2427 | nav: NavigationTarget { |
2428 | file_id: FileId( | 2428 | file_id: FileId( |
2429 | 1, | 2429 | 0, |
2430 | ), | 2430 | ), |
2431 | full_range: 0..15, | 2431 | full_range: 0..15, |
2432 | focus_range: Some( | 2432 | focus_range: Some( |
@@ -2445,7 +2445,7 @@ fn main() { let s<|>t = foo(); } | |||
2445 | mod_path: "test::S", | 2445 | mod_path: "test::S", |
2446 | nav: NavigationTarget { | 2446 | nav: NavigationTarget { |
2447 | file_id: FileId( | 2447 | file_id: FileId( |
2448 | 1, | 2448 | 0, |
2449 | ), | 2449 | ), |
2450 | full_range: 16..25, | 2450 | full_range: 16..25, |
2451 | focus_range: Some( | 2451 | focus_range: Some( |
@@ -2485,7 +2485,7 @@ fn main() { let s<|>t = foo(); } | |||
2485 | mod_path: "test::Foo", | 2485 | mod_path: "test::Foo", |
2486 | nav: NavigationTarget { | 2486 | nav: NavigationTarget { |
2487 | file_id: FileId( | 2487 | file_id: FileId( |
2488 | 1, | 2488 | 0, |
2489 | ), | 2489 | ), |
2490 | full_range: 0..12, | 2490 | full_range: 0..12, |
2491 | focus_range: Some( | 2491 | focus_range: Some( |
@@ -2504,7 +2504,7 @@ fn main() { let s<|>t = foo(); } | |||
2504 | mod_path: "test::Bar", | 2504 | mod_path: "test::Bar", |
2505 | nav: NavigationTarget { | 2505 | nav: NavigationTarget { |
2506 | file_id: FileId( | 2506 | file_id: FileId( |
2507 | 1, | 2507 | 0, |
2508 | ), | 2508 | ), |
2509 | full_range: 13..25, | 2509 | full_range: 13..25, |
2510 | focus_range: Some( | 2510 | focus_range: Some( |
@@ -2547,7 +2547,7 @@ fn main() { let s<|>t = foo(); } | |||
2547 | mod_path: "test::Foo", | 2547 | mod_path: "test::Foo", |
2548 | nav: NavigationTarget { | 2548 | nav: NavigationTarget { |
2549 | file_id: FileId( | 2549 | file_id: FileId( |
2550 | 1, | 2550 | 0, |
2551 | ), | 2551 | ), |
2552 | full_range: 0..15, | 2552 | full_range: 0..15, |
2553 | focus_range: Some( | 2553 | focus_range: Some( |
@@ -2566,7 +2566,7 @@ fn main() { let s<|>t = foo(); } | |||
2566 | mod_path: "test::Bar", | 2566 | mod_path: "test::Bar", |
2567 | nav: NavigationTarget { | 2567 | nav: NavigationTarget { |
2568 | file_id: FileId( | 2568 | file_id: FileId( |
2569 | 1, | 2569 | 0, |
2570 | ), | 2570 | ), |
2571 | full_range: 16..31, | 2571 | full_range: 16..31, |
2572 | focus_range: Some( | 2572 | focus_range: Some( |
@@ -2585,7 +2585,7 @@ fn main() { let s<|>t = foo(); } | |||
2585 | mod_path: "test::S1", | 2585 | mod_path: "test::S1", |
2586 | nav: NavigationTarget { | 2586 | nav: NavigationTarget { |
2587 | file_id: FileId( | 2587 | file_id: FileId( |
2588 | 1, | 2588 | 0, |
2589 | ), | 2589 | ), |
2590 | full_range: 32..44, | 2590 | full_range: 32..44, |
2591 | focus_range: Some( | 2591 | focus_range: Some( |
@@ -2604,7 +2604,7 @@ fn main() { let s<|>t = foo(); } | |||
2604 | mod_path: "test::S2", | 2604 | mod_path: "test::S2", |
2605 | nav: NavigationTarget { | 2605 | nav: NavigationTarget { |
2606 | file_id: FileId( | 2606 | file_id: FileId( |
2607 | 1, | 2607 | 0, |
2608 | ), | 2608 | ), |
2609 | full_range: 45..57, | 2609 | full_range: 45..57, |
2610 | focus_range: Some( | 2610 | focus_range: Some( |
@@ -2641,7 +2641,7 @@ fn foo(ar<|>g: &impl Foo) {} | |||
2641 | mod_path: "test::Foo", | 2641 | mod_path: "test::Foo", |
2642 | nav: NavigationTarget { | 2642 | nav: NavigationTarget { |
2643 | file_id: FileId( | 2643 | file_id: FileId( |
2644 | 1, | 2644 | 0, |
2645 | ), | 2645 | ), |
2646 | full_range: 0..12, | 2646 | full_range: 0..12, |
2647 | focus_range: Some( | 2647 | focus_range: Some( |
@@ -2681,7 +2681,7 @@ fn foo(ar<|>g: &impl Foo + Bar<S>) {} | |||
2681 | mod_path: "test::Foo", | 2681 | mod_path: "test::Foo", |
2682 | nav: NavigationTarget { | 2682 | nav: NavigationTarget { |
2683 | file_id: FileId( | 2683 | file_id: FileId( |
2684 | 1, | 2684 | 0, |
2685 | ), | 2685 | ), |
2686 | full_range: 0..12, | 2686 | full_range: 0..12, |
2687 | focus_range: Some( | 2687 | focus_range: Some( |
@@ -2700,7 +2700,7 @@ fn foo(ar<|>g: &impl Foo + Bar<S>) {} | |||
2700 | mod_path: "test::Bar", | 2700 | mod_path: "test::Bar", |
2701 | nav: NavigationTarget { | 2701 | nav: NavigationTarget { |
2702 | file_id: FileId( | 2702 | file_id: FileId( |
2703 | 1, | 2703 | 0, |
2704 | ), | 2704 | ), |
2705 | full_range: 13..28, | 2705 | full_range: 13..28, |
2706 | focus_range: Some( | 2706 | focus_range: Some( |
@@ -2719,7 +2719,7 @@ fn foo(ar<|>g: &impl Foo + Bar<S>) {} | |||
2719 | mod_path: "test::S", | 2719 | mod_path: "test::S", |
2720 | nav: NavigationTarget { | 2720 | nav: NavigationTarget { |
2721 | file_id: FileId( | 2721 | file_id: FileId( |
2722 | 1, | 2722 | 0, |
2723 | ), | 2723 | ), |
2724 | full_range: 29..39, | 2724 | full_range: 29..39, |
2725 | focus_range: Some( | 2725 | focus_range: Some( |
@@ -2764,7 +2764,7 @@ mod future { | |||
2764 | mod_path: "test::future::Future", | 2764 | mod_path: "test::future::Future", |
2765 | nav: NavigationTarget { | 2765 | nav: NavigationTarget { |
2766 | file_id: FileId( | 2766 | file_id: FileId( |
2767 | 1, | 2767 | 0, |
2768 | ), | 2768 | ), |
2769 | full_range: 101..163, | 2769 | full_range: 101..163, |
2770 | focus_range: Some( | 2770 | focus_range: Some( |
@@ -2783,7 +2783,7 @@ mod future { | |||
2783 | mod_path: "test::S", | 2783 | mod_path: "test::S", |
2784 | nav: NavigationTarget { | 2784 | nav: NavigationTarget { |
2785 | file_id: FileId( | 2785 | file_id: FileId( |
2786 | 1, | 2786 | 0, |
2787 | ), | 2787 | ), |
2788 | full_range: 0..9, | 2788 | full_range: 0..9, |
2789 | focus_range: Some( | 2789 | focus_range: Some( |
@@ -2821,7 +2821,7 @@ fn foo(ar<|>g: &impl Foo<S>) {} | |||
2821 | mod_path: "test::Foo", | 2821 | mod_path: "test::Foo", |
2822 | nav: NavigationTarget { | 2822 | nav: NavigationTarget { |
2823 | file_id: FileId( | 2823 | file_id: FileId( |
2824 | 1, | 2824 | 0, |
2825 | ), | 2825 | ), |
2826 | full_range: 0..15, | 2826 | full_range: 0..15, |
2827 | focus_range: Some( | 2827 | focus_range: Some( |
@@ -2840,7 +2840,7 @@ fn foo(ar<|>g: &impl Foo<S>) {} | |||
2840 | mod_path: "test::S", | 2840 | mod_path: "test::S", |
2841 | nav: NavigationTarget { | 2841 | nav: NavigationTarget { |
2842 | file_id: FileId( | 2842 | file_id: FileId( |
2843 | 1, | 2843 | 0, |
2844 | ), | 2844 | ), |
2845 | full_range: 16..27, | 2845 | full_range: 16..27, |
2846 | focus_range: Some( | 2846 | focus_range: Some( |
@@ -2883,7 +2883,7 @@ fn main() { let s<|>t = foo(); } | |||
2883 | mod_path: "test::B", | 2883 | mod_path: "test::B", |
2884 | nav: NavigationTarget { | 2884 | nav: NavigationTarget { |
2885 | file_id: FileId( | 2885 | file_id: FileId( |
2886 | 1, | 2886 | 0, |
2887 | ), | 2887 | ), |
2888 | full_range: 42..55, | 2888 | full_range: 42..55, |
2889 | focus_range: Some( | 2889 | focus_range: Some( |
@@ -2902,7 +2902,7 @@ fn main() { let s<|>t = foo(); } | |||
2902 | mod_path: "test::Foo", | 2902 | mod_path: "test::Foo", |
2903 | nav: NavigationTarget { | 2903 | nav: NavigationTarget { |
2904 | file_id: FileId( | 2904 | file_id: FileId( |
2905 | 1, | 2905 | 0, |
2906 | ), | 2906 | ), |
2907 | full_range: 0..12, | 2907 | full_range: 0..12, |
2908 | focus_range: Some( | 2908 | focus_range: Some( |
@@ -2939,7 +2939,7 @@ fn foo(ar<|>g: &dyn Foo) {} | |||
2939 | mod_path: "test::Foo", | 2939 | mod_path: "test::Foo", |
2940 | nav: NavigationTarget { | 2940 | nav: NavigationTarget { |
2941 | file_id: FileId( | 2941 | file_id: FileId( |
2942 | 1, | 2942 | 0, |
2943 | ), | 2943 | ), |
2944 | full_range: 0..12, | 2944 | full_range: 0..12, |
2945 | focus_range: Some( | 2945 | focus_range: Some( |
@@ -2977,7 +2977,7 @@ fn foo(ar<|>g: &dyn Foo<S>) {} | |||
2977 | mod_path: "test::Foo", | 2977 | mod_path: "test::Foo", |
2978 | nav: NavigationTarget { | 2978 | nav: NavigationTarget { |
2979 | file_id: FileId( | 2979 | file_id: FileId( |
2980 | 1, | 2980 | 0, |
2981 | ), | 2981 | ), |
2982 | full_range: 0..15, | 2982 | full_range: 0..15, |
2983 | focus_range: Some( | 2983 | focus_range: Some( |
@@ -2996,7 +2996,7 @@ fn foo(ar<|>g: &dyn Foo<S>) {} | |||
2996 | mod_path: "test::S", | 2996 | mod_path: "test::S", |
2997 | nav: NavigationTarget { | 2997 | nav: NavigationTarget { |
2998 | file_id: FileId( | 2998 | file_id: FileId( |
2999 | 1, | 2999 | 0, |
3000 | ), | 3000 | ), |
3001 | full_range: 16..27, | 3001 | full_range: 16..27, |
3002 | focus_range: Some( | 3002 | focus_range: Some( |
@@ -3037,7 +3037,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
3037 | mod_path: "test::ImplTrait", | 3037 | mod_path: "test::ImplTrait", |
3038 | nav: NavigationTarget { | 3038 | nav: NavigationTarget { |
3039 | file_id: FileId( | 3039 | file_id: FileId( |
3040 | 1, | 3040 | 0, |
3041 | ), | 3041 | ), |
3042 | full_range: 0..21, | 3042 | full_range: 0..21, |
3043 | focus_range: Some( | 3043 | focus_range: Some( |
@@ -3056,7 +3056,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
3056 | mod_path: "test::B", | 3056 | mod_path: "test::B", |
3057 | nav: NavigationTarget { | 3057 | nav: NavigationTarget { |
3058 | file_id: FileId( | 3058 | file_id: FileId( |
3059 | 1, | 3059 | 0, |
3060 | ), | 3060 | ), |
3061 | full_range: 43..57, | 3061 | full_range: 43..57, |
3062 | focus_range: Some( | 3062 | focus_range: Some( |
@@ -3075,7 +3075,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
3075 | mod_path: "test::DynTrait", | 3075 | mod_path: "test::DynTrait", |
3076 | nav: NavigationTarget { | 3076 | nav: NavigationTarget { |
3077 | file_id: FileId( | 3077 | file_id: FileId( |
3078 | 1, | 3078 | 0, |
3079 | ), | 3079 | ), |
3080 | full_range: 22..42, | 3080 | full_range: 22..42, |
3081 | focus_range: Some( | 3081 | focus_range: Some( |
@@ -3094,7 +3094,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
3094 | mod_path: "test::S", | 3094 | mod_path: "test::S", |
3095 | nav: NavigationTarget { | 3095 | nav: NavigationTarget { |
3096 | file_id: FileId( | 3096 | file_id: FileId( |
3097 | 1, | 3097 | 0, |
3098 | ), | 3098 | ), |
3099 | full_range: 58..69, | 3099 | full_range: 58..69, |
3100 | focus_range: Some( | 3100 | focus_range: Some( |
@@ -3142,7 +3142,7 @@ fn main() { let s<|>t = test().get(); } | |||
3142 | mod_path: "test::Foo", | 3142 | mod_path: "test::Foo", |
3143 | nav: NavigationTarget { | 3143 | nav: NavigationTarget { |
3144 | file_id: FileId( | 3144 | file_id: FileId( |
3145 | 1, | 3145 | 0, |
3146 | ), | 3146 | ), |
3147 | full_range: 0..62, | 3147 | full_range: 0..62, |
3148 | focus_range: Some( | 3148 | focus_range: Some( |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 583f39d85..1d7e8de56 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -189,7 +189,7 @@ fn get_bind_pat_hints( | |||
189 | 189 | ||
190 | let ty = sema.type_of_pat(&pat.clone().into())?; | 190 | let ty = sema.type_of_pat(&pat.clone().into())?; |
191 | 191 | ||
192 | if should_not_display_type_hint(sema.db, &pat, &ty) { | 192 | if should_not_display_type_hint(sema, &pat, &ty) { |
193 | return None; | 193 | return None; |
194 | } | 194 | } |
195 | 195 | ||
@@ -215,10 +215,12 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Typ | |||
215 | } | 215 | } |
216 | 216 | ||
217 | fn should_not_display_type_hint( | 217 | fn should_not_display_type_hint( |
218 | db: &RootDatabase, | 218 | sema: &Semantics<RootDatabase>, |
219 | bind_pat: &ast::IdentPat, | 219 | bind_pat: &ast::IdentPat, |
220 | pat_ty: &Type, | 220 | pat_ty: &Type, |
221 | ) -> bool { | 221 | ) -> bool { |
222 | let db = sema.db; | ||
223 | |||
222 | if pat_ty.is_unknown() { | 224 | if pat_ty.is_unknown() { |
223 | return true; | 225 | return true; |
224 | } | 226 | } |
@@ -249,6 +251,15 @@ fn should_not_display_type_hint( | |||
249 | return it.condition().and_then(|condition| condition.pat()).is_some() | 251 | return it.condition().and_then(|condition| condition.pat()).is_some() |
250 | && pat_is_enum_variant(db, bind_pat, pat_ty); | 252 | && pat_is_enum_variant(db, bind_pat, pat_ty); |
251 | }, | 253 | }, |
254 | ast::ForExpr(it) => { | ||
255 | // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit). | ||
256 | // Type of expr should be iterable. | ||
257 | return it.in_token().is_none() || | ||
258 | it.iterable() | ||
259 | .and_then(|iterable_expr|sema.type_of_expr(&iterable_expr)) | ||
260 | .map(|iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit()) | ||
261 | .unwrap_or(true) | ||
262 | }, | ||
252 | _ => (), | 263 | _ => (), |
253 | } | 264 | } |
254 | } | 265 | } |
@@ -339,14 +350,14 @@ mod tests { | |||
339 | use expect_test::{expect, Expect}; | 350 | use expect_test::{expect, Expect}; |
340 | use test_utils::extract_annotations; | 351 | use test_utils::extract_annotations; |
341 | 352 | ||
342 | use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file}; | 353 | use crate::{fixture, inlay_hints::InlayHintsConfig}; |
343 | 354 | ||
344 | fn check(ra_fixture: &str) { | 355 | fn check(ra_fixture: &str) { |
345 | check_with_config(InlayHintsConfig::default(), ra_fixture); | 356 | check_with_config(InlayHintsConfig::default(), ra_fixture); |
346 | } | 357 | } |
347 | 358 | ||
348 | fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { | 359 | fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { |
349 | let (analysis, file_id) = single_file(ra_fixture); | 360 | let (analysis, file_id) = fixture::file(ra_fixture); |
350 | let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); | 361 | let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); |
351 | let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); | 362 | let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); |
352 | let actual = | 363 | let actual = |
@@ -355,7 +366,7 @@ mod tests { | |||
355 | } | 366 | } |
356 | 367 | ||
357 | fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { | 368 | fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { |
358 | let (analysis, file_id) = single_file(ra_fixture); | 369 | let (analysis, file_id) = fixture::file(ra_fixture); |
359 | let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); | 370 | let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); |
360 | expect.assert_debug_eq(&inlay_hints) | 371 | expect.assert_debug_eq(&inlay_hints) |
361 | } | 372 | } |
@@ -496,19 +507,6 @@ fn main() { | |||
496 | } | 507 | } |
497 | 508 | ||
498 | #[test] | 509 | #[test] |
499 | fn for_expression() { | ||
500 | check( | ||
501 | r#" | ||
502 | fn main() { | ||
503 | let mut start = 0; | ||
504 | //^^^^^^^^^ i32 | ||
505 | for increment in 0..2 { start += increment; } | ||
506 | //^^^^^^^^^ i32 | ||
507 | }"#, | ||
508 | ); | ||
509 | } | ||
510 | |||
511 | #[test] | ||
512 | fn if_expr() { | 510 | fn if_expr() { |
513 | check( | 511 | check( |
514 | r#" | 512 | r#" |
@@ -924,4 +922,108 @@ fn main() { | |||
924 | "#]], | 922 | "#]], |
925 | ); | 923 | ); |
926 | } | 924 | } |
925 | |||
926 | #[test] | ||
927 | fn incomplete_for_no_hint() { | ||
928 | check( | ||
929 | r#" | ||
930 | fn main() { | ||
931 | let data = &[1i32, 2, 3]; | ||
932 | //^^^^ &[i32; _] | ||
933 | for i | ||
934 | }"#, | ||
935 | ); | ||
936 | check( | ||
937 | r#" | ||
938 | //- /main.rs crate:main deps:core | ||
939 | pub struct Vec<T> {} | ||
940 | |||
941 | impl<T> Vec<T> { | ||
942 | pub fn new() -> Self { Vec {} } | ||
943 | pub fn push(&mut self, t: T) {} | ||
944 | } | ||
945 | |||
946 | impl<T> IntoIterator for Vec<T> { | ||
947 | type Item=T; | ||
948 | } | ||
949 | |||
950 | fn main() { | ||
951 | let mut data = Vec::new(); | ||
952 | //^^^^^^^^ Vec<&str> | ||
953 | data.push("foo"); | ||
954 | for i in | ||
955 | |||
956 | println!("Unit expr"); | ||
957 | } | ||
958 | |||
959 | //- /core.rs crate:core | ||
960 | #[prelude_import] use iter::*; | ||
961 | mod iter { | ||
962 | trait IntoIterator { | ||
963 | type Item; | ||
964 | } | ||
965 | } | ||
966 | //- /alloc.rs crate:alloc deps:core | ||
967 | mod collections { | ||
968 | struct Vec<T> {} | ||
969 | impl<T> Vec<T> { | ||
970 | fn new() -> Self { Vec {} } | ||
971 | fn push(&mut self, t: T) { } | ||
972 | } | ||
973 | impl<T> IntoIterator for Vec<T> { | ||
974 | type Item=T; | ||
975 | } | ||
976 | } | ||
977 | "#, | ||
978 | ); | ||
979 | } | ||
980 | |||
981 | #[test] | ||
982 | fn complete_for_hint() { | ||
983 | check( | ||
984 | r#" | ||
985 | //- /main.rs crate:main deps:core | ||
986 | pub struct Vec<T> {} | ||
987 | |||
988 | impl<T> Vec<T> { | ||
989 | pub fn new() -> Self { Vec {} } | ||
990 | pub fn push(&mut self, t: T) {} | ||
991 | } | ||
992 | |||
993 | impl<T> IntoIterator for Vec<T> { | ||
994 | type Item=T; | ||
995 | } | ||
996 | |||
997 | fn main() { | ||
998 | let mut data = Vec::new(); | ||
999 | //^^^^^^^^ Vec<&str> | ||
1000 | data.push("foo"); | ||
1001 | for i in data { | ||
1002 | //^ &str | ||
1003 | let z = i; | ||
1004 | //^ &str | ||
1005 | } | ||
1006 | } | ||
1007 | |||
1008 | //- /core.rs crate:core | ||
1009 | #[prelude_import] use iter::*; | ||
1010 | mod iter { | ||
1011 | trait IntoIterator { | ||
1012 | type Item; | ||
1013 | } | ||
1014 | } | ||
1015 | //- /alloc.rs crate:alloc deps:core | ||
1016 | mod collections { | ||
1017 | struct Vec<T> {} | ||
1018 | impl<T> Vec<T> { | ||
1019 | fn new() -> Self { Vec {} } | ||
1020 | fn push(&mut self, t: T) { } | ||
1021 | } | ||
1022 | impl<T> IntoIterator for Vec<T> { | ||
1023 | type Item=T; | ||
1024 | } | ||
1025 | } | ||
1026 | "#, | ||
1027 | ); | ||
1028 | } | ||
927 | } | 1029 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 31f2bcba3..1aa673cf8 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -15,7 +15,8 @@ macro_rules! eprintln { | |||
15 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; | 15 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; |
16 | } | 16 | } |
17 | 17 | ||
18 | pub mod mock_analysis; | 18 | #[cfg(test)] |
19 | mod fixture; | ||
19 | 20 | ||
20 | mod markup; | 21 | mod markup; |
21 | mod prime_caches; | 22 | mod prime_caches; |
@@ -86,12 +87,11 @@ pub use assists::{ | |||
86 | utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist, | 87 | utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist, |
87 | }; | 88 | }; |
88 | pub use base_db::{ | 89 | pub use base_db::{ |
89 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, | 90 | Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, |
90 | SourceRootId, | 91 | SourceRootId, |
91 | }; | 92 | }; |
92 | pub use hir::{Documentation, Semantics}; | 93 | pub use hir::{Documentation, Semantics}; |
93 | pub use ide_db::{ | 94 | pub use ide_db::{ |
94 | change::AnalysisChange, | ||
95 | label::Label, | 95 | label::Label, |
96 | line_index::{LineCol, LineIndex}, | 96 | line_index::{LineCol, LineIndex}, |
97 | search::SearchScope, | 97 | search::SearchScope, |
@@ -140,14 +140,10 @@ impl AnalysisHost { | |||
140 | 140 | ||
141 | /// Applies changes to the current state of the world. If there are | 141 | /// Applies changes to the current state of the world. If there are |
142 | /// outstanding snapshots, they will be canceled. | 142 | /// outstanding snapshots, they will be canceled. |
143 | pub fn apply_change(&mut self, change: AnalysisChange) { | 143 | pub fn apply_change(&mut self, change: Change) { |
144 | self.db.apply_change(change) | 144 | self.db.apply_change(change) |
145 | } | 145 | } |
146 | 146 | ||
147 | pub fn maybe_collect_garbage(&mut self) { | ||
148 | self.db.maybe_collect_garbage(); | ||
149 | } | ||
150 | |||
151 | pub fn collect_garbage(&mut self) { | 147 | pub fn collect_garbage(&mut self) { |
152 | self.db.collect_garbage(); | 148 | self.db.collect_garbage(); |
153 | } | 149 | } |
@@ -198,7 +194,7 @@ impl Analysis { | |||
198 | file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_string())); | 194 | file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_string())); |
199 | let source_root = SourceRoot::new_local(file_set); | 195 | let source_root = SourceRoot::new_local(file_set); |
200 | 196 | ||
201 | let mut change = AnalysisChange::new(); | 197 | let mut change = Change::new(); |
202 | change.set_roots(vec![source_root]); | 198 | change.set_roots(vec![source_root]); |
203 | let mut crate_graph = CrateGraph::default(); | 199 | let mut crate_graph = CrateGraph::default(); |
204 | // FIXME: cfg options | 200 | // FIXME: cfg options |
@@ -220,8 +216,8 @@ impl Analysis { | |||
220 | } | 216 | } |
221 | 217 | ||
222 | /// Debug info about the current state of the analysis. | 218 | /// Debug info about the current state of the analysis. |
223 | pub fn status(&self) -> Cancelable<String> { | 219 | pub fn status(&self, file_id: Option<FileId>) -> Cancelable<String> { |
224 | self.with_db(|db| status::status(&*db)) | 220 | self.with_db(|db| status::status(&*db, file_id)) |
225 | } | 221 | } |
226 | 222 | ||
227 | pub fn prime_caches(&self, files: Vec<FileId>) -> Cancelable<()> { | 223 | pub fn prime_caches(&self, files: Vec<FileId>) -> Cancelable<()> { |
diff --git a/crates/ide/src/link_rewrite.rs b/crates/ide/src/link_rewrite.rs index 107787bb9..a16f90e17 100644 --- a/crates/ide/src/link_rewrite.rs +++ b/crates/ide/src/link_rewrite.rs | |||
@@ -120,7 +120,7 @@ fn rewrite_intra_doc_link( | |||
120 | 120 | ||
121 | /// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`). | 121 | /// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`). |
122 | fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<String> { | 122 | fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<String> { |
123 | if !(target.contains("#") || target.contains(".html")) { | 123 | if !(target.contains('#') || target.contains(".html")) { |
124 | return None; | 124 | return None; |
125 | } | 125 | } |
126 | 126 | ||
@@ -190,7 +190,7 @@ fn strip_prefixes_suffixes(mut s: &str) -> &str { | |||
190 | prefixes.clone().for_each(|prefix| s = s.trim_start_matches(*prefix)); | 190 | prefixes.clone().for_each(|prefix| s = s.trim_start_matches(*prefix)); |
191 | suffixes.clone().for_each(|suffix| s = s.trim_end_matches(*suffix)); | 191 | suffixes.clone().for_each(|suffix| s = s.trim_end_matches(*suffix)); |
192 | }); | 192 | }); |
193 | s.trim_start_matches("@").trim() | 193 | s.trim_start_matches('@').trim() |
194 | } | 194 | } |
195 | 195 | ||
196 | static TYPES: ([&str; 7], [&str; 0]) = | 196 | static TYPES: ([&str; 7], [&str; 0]) = |
diff --git a/crates/ide/src/mock_analysis.rs b/crates/ide/src/mock_analysis.rs deleted file mode 100644 index 235796dbc..000000000 --- a/crates/ide/src/mock_analysis.rs +++ /dev/null | |||
@@ -1,176 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | use std::sync::Arc; | ||
3 | |||
4 | use base_db::{CrateName, FileSet, SourceRoot, VfsPath}; | ||
5 | use cfg::CfgOptions; | ||
6 | use test_utils::{ | ||
7 | extract_annotations, extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, | ||
8 | }; | ||
9 | |||
10 | use crate::{ | ||
11 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, | ||
12 | }; | ||
13 | |||
14 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis | ||
15 | /// from a set of in-memory files. | ||
16 | #[derive(Debug, Default)] | ||
17 | pub struct MockAnalysis { | ||
18 | files: Vec<Fixture>, | ||
19 | } | ||
20 | |||
21 | impl MockAnalysis { | ||
22 | /// Creates `MockAnalysis` using a fixture data in the following format: | ||
23 | /// | ||
24 | /// ```not_rust | ||
25 | /// //- /main.rs | ||
26 | /// mod foo; | ||
27 | /// fn main() {} | ||
28 | /// | ||
29 | /// //- /foo.rs | ||
30 | /// struct Baz; | ||
31 | /// ``` | ||
32 | pub fn with_files(ra_fixture: &str) -> MockAnalysis { | ||
33 | let (res, pos) = MockAnalysis::with_fixture(ra_fixture); | ||
34 | assert!(pos.is_none()); | ||
35 | res | ||
36 | } | ||
37 | |||
38 | /// Same as `with_files`, but requires that a single file contains a `<|>` marker, | ||
39 | /// whose position is also returned. | ||
40 | pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) { | ||
41 | let (res, position) = MockAnalysis::with_fixture(fixture); | ||
42 | let (file_id, range_or_offset) = position.expect("expected a marker (<|>)"); | ||
43 | let offset = match range_or_offset { | ||
44 | RangeOrOffset::Range(_) => panic!(), | ||
45 | RangeOrOffset::Offset(it) => it, | ||
46 | }; | ||
47 | (res, FilePosition { file_id, offset }) | ||
48 | } | ||
49 | |||
50 | fn with_fixture(fixture: &str) -> (MockAnalysis, Option<(FileId, RangeOrOffset)>) { | ||
51 | let mut position = None; | ||
52 | let mut res = MockAnalysis::default(); | ||
53 | for mut entry in Fixture::parse(fixture) { | ||
54 | if entry.text.contains(CURSOR_MARKER) { | ||
55 | assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); | ||
56 | let (range_or_offset, text) = extract_range_or_offset(&entry.text); | ||
57 | entry.text = text; | ||
58 | let file_id = res.add_file_fixture(entry); | ||
59 | position = Some((file_id, range_or_offset)); | ||
60 | } else { | ||
61 | res.add_file_fixture(entry); | ||
62 | } | ||
63 | } | ||
64 | (res, position) | ||
65 | } | ||
66 | |||
67 | fn add_file_fixture(&mut self, fixture: Fixture) -> FileId { | ||
68 | let file_id = FileId((self.files.len() + 1) as u32); | ||
69 | self.files.push(fixture); | ||
70 | file_id | ||
71 | } | ||
72 | |||
73 | pub fn id_of(&self, path: &str) -> FileId { | ||
74 | let (file_id, _) = | ||
75 | self.files().find(|(_, data)| path == data.path).expect("no file in this mock"); | ||
76 | file_id | ||
77 | } | ||
78 | pub fn annotations(&self) -> Vec<(FileRange, String)> { | ||
79 | self.files() | ||
80 | .flat_map(|(file_id, fixture)| { | ||
81 | let annotations = extract_annotations(&fixture.text); | ||
82 | annotations | ||
83 | .into_iter() | ||
84 | .map(move |(range, data)| (FileRange { file_id, range }, data)) | ||
85 | }) | ||
86 | .collect() | ||
87 | } | ||
88 | pub fn files(&self) -> impl Iterator<Item = (FileId, &Fixture)> + '_ { | ||
89 | self.files.iter().enumerate().map(|(idx, fixture)| (FileId(idx as u32 + 1), fixture)) | ||
90 | } | ||
91 | pub fn annotation(&self) -> (FileRange, String) { | ||
92 | let mut all = self.annotations(); | ||
93 | assert_eq!(all.len(), 1); | ||
94 | all.pop().unwrap() | ||
95 | } | ||
96 | pub fn analysis_host(self) -> AnalysisHost { | ||
97 | let mut host = AnalysisHost::default(); | ||
98 | let mut change = AnalysisChange::new(); | ||
99 | let mut file_set = FileSet::default(); | ||
100 | let mut crate_graph = CrateGraph::default(); | ||
101 | let mut root_crate = None; | ||
102 | for (i, data) in self.files.into_iter().enumerate() { | ||
103 | let path = data.path; | ||
104 | assert!(path.starts_with('/')); | ||
105 | |||
106 | let mut cfg = CfgOptions::default(); | ||
107 | data.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into())); | ||
108 | data.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into())); | ||
109 | let edition: Edition = | ||
110 | data.edition.and_then(|it| it.parse().ok()).unwrap_or(Edition::Edition2018); | ||
111 | |||
112 | let file_id = FileId(i as u32 + 1); | ||
113 | let env = data.env.into_iter().collect(); | ||
114 | if path == "/lib.rs" || path == "/main.rs" { | ||
115 | root_crate = Some(crate_graph.add_crate_root( | ||
116 | file_id, | ||
117 | edition, | ||
118 | Some("test".to_string()), | ||
119 | cfg, | ||
120 | env, | ||
121 | Default::default(), | ||
122 | )); | ||
123 | } else if path.ends_with("/lib.rs") { | ||
124 | let base = &path[..path.len() - "/lib.rs".len()]; | ||
125 | let crate_name = &base[base.rfind('/').unwrap() + '/'.len_utf8()..]; | ||
126 | let other_crate = crate_graph.add_crate_root( | ||
127 | file_id, | ||
128 | edition, | ||
129 | Some(crate_name.to_string()), | ||
130 | cfg, | ||
131 | env, | ||
132 | Default::default(), | ||
133 | ); | ||
134 | if let Some(root_crate) = root_crate { | ||
135 | crate_graph | ||
136 | .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate) | ||
137 | .unwrap(); | ||
138 | } | ||
139 | } | ||
140 | let path = VfsPath::new_virtual_path(path.to_string()); | ||
141 | file_set.insert(file_id, path); | ||
142 | change.change_file(file_id, Some(Arc::new(data.text).to_owned())); | ||
143 | } | ||
144 | change.set_crate_graph(crate_graph); | ||
145 | change.set_roots(vec![SourceRoot::new_local(file_set)]); | ||
146 | host.apply_change(change); | ||
147 | host | ||
148 | } | ||
149 | pub fn analysis(self) -> Analysis { | ||
150 | self.analysis_host().analysis() | ||
151 | } | ||
152 | } | ||
153 | |||
154 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. | ||
155 | pub fn analysis_and_position(ra_fixture: &str) -> (Analysis, FilePosition) { | ||
156 | let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); | ||
157 | (mock.analysis(), position) | ||
158 | } | ||
159 | |||
160 | /// Creates analysis for a single file. | ||
161 | pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) { | ||
162 | let mock = MockAnalysis::with_files(ra_fixture); | ||
163 | let file_id = mock.id_of("/main.rs"); | ||
164 | (mock.analysis(), file_id) | ||
165 | } | ||
166 | |||
167 | /// Creates analysis for a single file, returns range marked with a pair of <|>. | ||
168 | pub fn analysis_and_range(ra_fixture: &str) -> (Analysis, FileRange) { | ||
169 | let (res, position) = MockAnalysis::with_fixture(ra_fixture); | ||
170 | let (file_id, range_or_offset) = position.expect("expected a marker (<|>)"); | ||
171 | let range = match range_or_offset { | ||
172 | RangeOrOffset::Range(it) => it, | ||
173 | RangeOrOffset::Offset(_) => panic!(), | ||
174 | }; | ||
175 | (res.analysis(), FileRange { file_id, range }) | ||
176 | } | ||
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index 59ed2967c..ef94acfec 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs | |||
@@ -63,19 +63,13 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { | |||
63 | 63 | ||
64 | #[cfg(test)] | 64 | #[cfg(test)] |
65 | mod tests { | 65 | mod tests { |
66 | use base_db::Env; | ||
67 | use cfg::CfgOptions; | ||
68 | use test_utils::mark; | 66 | use test_utils::mark; |
69 | 67 | ||
70 | use crate::{ | 68 | use crate::fixture::{self}; |
71 | mock_analysis::{analysis_and_position, MockAnalysis}, | ||
72 | AnalysisChange, CrateGraph, | ||
73 | Edition::Edition2018, | ||
74 | }; | ||
75 | 69 | ||
76 | #[test] | 70 | #[test] |
77 | fn test_resolve_parent_module() { | 71 | fn test_resolve_parent_module() { |
78 | let (analysis, pos) = analysis_and_position( | 72 | let (analysis, pos) = fixture::position( |
79 | " | 73 | " |
80 | //- /lib.rs | 74 | //- /lib.rs |
81 | mod foo; | 75 | mod foo; |
@@ -84,13 +78,13 @@ mod tests { | |||
84 | ", | 78 | ", |
85 | ); | 79 | ); |
86 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | 80 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); |
87 | nav.assert_match("foo MODULE FileId(1) 0..8"); | 81 | nav.assert_match("foo MODULE FileId(0) 0..8"); |
88 | } | 82 | } |
89 | 83 | ||
90 | #[test] | 84 | #[test] |
91 | fn test_resolve_parent_module_on_module_decl() { | 85 | fn test_resolve_parent_module_on_module_decl() { |
92 | mark::check!(test_resolve_parent_module_on_module_decl); | 86 | mark::check!(test_resolve_parent_module_on_module_decl); |
93 | let (analysis, pos) = analysis_and_position( | 87 | let (analysis, pos) = fixture::position( |
94 | " | 88 | " |
95 | //- /lib.rs | 89 | //- /lib.rs |
96 | mod foo; | 90 | mod foo; |
@@ -103,12 +97,12 @@ mod tests { | |||
103 | ", | 97 | ", |
104 | ); | 98 | ); |
105 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | 99 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); |
106 | nav.assert_match("foo MODULE FileId(1) 0..8"); | 100 | nav.assert_match("foo MODULE FileId(0) 0..8"); |
107 | } | 101 | } |
108 | 102 | ||
109 | #[test] | 103 | #[test] |
110 | fn test_resolve_parent_module_for_inline() { | 104 | fn test_resolve_parent_module_for_inline() { |
111 | let (analysis, pos) = analysis_and_position( | 105 | let (analysis, pos) = fixture::position( |
112 | " | 106 | " |
113 | //- /lib.rs | 107 | //- /lib.rs |
114 | mod foo { | 108 | mod foo { |
@@ -119,37 +113,19 @@ mod tests { | |||
119 | ", | 113 | ", |
120 | ); | 114 | ); |
121 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | 115 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); |
122 | nav.assert_match("baz MODULE FileId(1) 32..44"); | 116 | nav.assert_match("baz MODULE FileId(0) 32..44"); |
123 | } | 117 | } |
124 | 118 | ||
125 | #[test] | 119 | #[test] |
126 | fn test_resolve_crate_root() { | 120 | fn test_resolve_crate_root() { |
127 | let mock = MockAnalysis::with_files( | 121 | let (analysis, file_id) = fixture::file( |
128 | r#" | 122 | r#" |
129 | //- /bar.rs | 123 | //- /main.rs |
130 | mod foo; | 124 | mod foo; |
131 | //- /foo.rs | 125 | //- /foo.rs |
132 | // empty | 126 | <|> |
133 | "#, | 127 | "#, |
134 | ); | 128 | ); |
135 | let root_file = mock.id_of("/bar.rs"); | 129 | assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1); |
136 | let mod_file = mock.id_of("/foo.rs"); | ||
137 | let mut host = mock.analysis_host(); | ||
138 | assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); | ||
139 | |||
140 | let mut crate_graph = CrateGraph::default(); | ||
141 | let crate_id = crate_graph.add_crate_root( | ||
142 | root_file, | ||
143 | Edition2018, | ||
144 | None, | ||
145 | CfgOptions::default(), | ||
146 | Env::default(), | ||
147 | Default::default(), | ||
148 | ); | ||
149 | let mut change = AnalysisChange::new(); | ||
150 | change.set_crate_graph(crate_graph); | ||
151 | host.apply_change(change); | ||
152 | |||
153 | assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]); | ||
154 | } | 130 | } |
155 | } | 131 | } |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 722c8f406..e0830eb4f 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -190,14 +190,15 @@ fn get_struct_def_name_for_struct_literal_search( | |||
190 | 190 | ||
191 | #[cfg(test)] | 191 | #[cfg(test)] |
192 | mod tests { | 192 | mod tests { |
193 | use crate::{ | 193 | use base_db::FileId; |
194 | mock_analysis::{analysis_and_position, MockAnalysis}, | 194 | use expect_test::{expect, Expect}; |
195 | Declaration, Reference, ReferenceSearchResult, SearchScope, | 195 | use stdx::format_to; |
196 | }; | 196 | |
197 | use crate::{fixture, SearchScope}; | ||
197 | 198 | ||
198 | #[test] | 199 | #[test] |
199 | fn test_struct_literal_after_space() { | 200 | fn test_struct_literal_after_space() { |
200 | let refs = get_all_refs( | 201 | check( |
201 | r#" | 202 | r#" |
202 | struct Foo <|>{ | 203 | struct Foo <|>{ |
203 | a: i32, | 204 | a: i32, |
@@ -210,17 +211,17 @@ fn main() { | |||
210 | f = Foo {a: Foo::f()}; | 211 | f = Foo {a: Foo::f()}; |
211 | } | 212 | } |
212 | "#, | 213 | "#, |
213 | ); | 214 | expect![[r#" |
214 | check_result( | 215 | Foo STRUCT FileId(0) 0..26 7..10 Other |
215 | refs, | 216 | |
216 | "Foo STRUCT FileId(1) 0..26 7..10 Other", | 217 | FileId(0) 101..104 StructLiteral |
217 | &["FileId(1) 101..104 StructLiteral"], | 218 | "#]], |
218 | ); | 219 | ); |
219 | } | 220 | } |
220 | 221 | ||
221 | #[test] | 222 | #[test] |
222 | fn test_struct_literal_before_space() { | 223 | fn test_struct_literal_before_space() { |
223 | let refs = get_all_refs( | 224 | check( |
224 | r#" | 225 | r#" |
225 | struct Foo<|> {} | 226 | struct Foo<|> {} |
226 | fn main() { | 227 | fn main() { |
@@ -228,17 +229,18 @@ struct Foo<|> {} | |||
228 | f = Foo {}; | 229 | f = Foo {}; |
229 | } | 230 | } |
230 | "#, | 231 | "#, |
231 | ); | 232 | expect![[r#" |
232 | check_result( | 233 | Foo STRUCT FileId(0) 0..13 7..10 Other |
233 | refs, | 234 | |
234 | "Foo STRUCT FileId(1) 0..13 7..10 Other", | 235 | FileId(0) 41..44 Other |
235 | &["FileId(1) 41..44 Other", "FileId(1) 54..57 StructLiteral"], | 236 | FileId(0) 54..57 StructLiteral |
237 | "#]], | ||
236 | ); | 238 | ); |
237 | } | 239 | } |
238 | 240 | ||
239 | #[test] | 241 | #[test] |
240 | fn test_struct_literal_with_generic_type() { | 242 | fn test_struct_literal_with_generic_type() { |
241 | let refs = get_all_refs( | 243 | check( |
242 | r#" | 244 | r#" |
243 | struct Foo<T> <|>{} | 245 | struct Foo<T> <|>{} |
244 | fn main() { | 246 | fn main() { |
@@ -246,17 +248,17 @@ struct Foo<T> <|>{} | |||
246 | f = Foo {}; | 248 | f = Foo {}; |
247 | } | 249 | } |
248 | "#, | 250 | "#, |
249 | ); | 251 | expect![[r#" |
250 | check_result( | 252 | Foo STRUCT FileId(0) 0..16 7..10 Other |
251 | refs, | 253 | |
252 | "Foo STRUCT FileId(1) 0..16 7..10 Other", | 254 | FileId(0) 64..67 StructLiteral |
253 | &["FileId(1) 64..67 StructLiteral"], | 255 | "#]], |
254 | ); | 256 | ); |
255 | } | 257 | } |
256 | 258 | ||
257 | #[test] | 259 | #[test] |
258 | fn test_struct_literal_for_tuple() { | 260 | fn test_struct_literal_for_tuple() { |
259 | let refs = get_all_refs( | 261 | check( |
260 | r#" | 262 | r#" |
261 | struct Foo<|>(i32); | 263 | struct Foo<|>(i32); |
262 | 264 | ||
@@ -265,17 +267,17 @@ fn main() { | |||
265 | f = Foo(1); | 267 | f = Foo(1); |
266 | } | 268 | } |
267 | "#, | 269 | "#, |
268 | ); | 270 | expect![[r#" |
269 | check_result( | 271 | Foo STRUCT FileId(0) 0..16 7..10 Other |
270 | refs, | 272 | |
271 | "Foo STRUCT FileId(1) 0..16 7..10 Other", | 273 | FileId(0) 54..57 StructLiteral |
272 | &["FileId(1) 54..57 StructLiteral"], | 274 | "#]], |
273 | ); | 275 | ); |
274 | } | 276 | } |
275 | 277 | ||
276 | #[test] | 278 | #[test] |
277 | fn test_find_all_refs_for_local() { | 279 | fn test_find_all_refs_for_local() { |
278 | let refs = get_all_refs( | 280 | check( |
279 | r#" | 281 | r#" |
280 | fn main() { | 282 | fn main() { |
281 | let mut i = 1; | 283 | let mut i = 1; |
@@ -288,22 +290,20 @@ fn main() { | |||
288 | 290 | ||
289 | i = 5; | 291 | i = 5; |
290 | }"#, | 292 | }"#, |
291 | ); | 293 | expect![[r#" |
292 | check_result( | 294 | i IDENT_PAT FileId(0) 24..25 Other Write |
293 | refs, | 295 | |
294 | "i IDENT_PAT FileId(1) 24..25 Other Write", | 296 | FileId(0) 50..51 Other Write |
295 | &[ | 297 | FileId(0) 54..55 Other Read |
296 | "FileId(1) 50..51 Other Write", | 298 | FileId(0) 76..77 Other Write |
297 | "FileId(1) 54..55 Other Read", | 299 | FileId(0) 94..95 Other Write |
298 | "FileId(1) 76..77 Other Write", | 300 | "#]], |
299 | "FileId(1) 94..95 Other Write", | ||
300 | ], | ||
301 | ); | 301 | ); |
302 | } | 302 | } |
303 | 303 | ||
304 | #[test] | 304 | #[test] |
305 | fn search_filters_by_range() { | 305 | fn search_filters_by_range() { |
306 | let refs = get_all_refs( | 306 | check( |
307 | r#" | 307 | r#" |
308 | fn foo() { | 308 | fn foo() { |
309 | let spam<|> = 92; | 309 | let spam<|> = 92; |
@@ -314,41 +314,46 @@ fn bar() { | |||
314 | spam + spam | 314 | spam + spam |
315 | } | 315 | } |
316 | "#, | 316 | "#, |
317 | ); | 317 | expect![[r#" |
318 | check_result( | 318 | spam IDENT_PAT FileId(0) 19..23 Other |
319 | refs, | 319 | |
320 | "spam IDENT_PAT FileId(1) 19..23 Other", | 320 | FileId(0) 34..38 Other Read |
321 | &["FileId(1) 34..38 Other Read", "FileId(1) 41..45 Other Read"], | 321 | FileId(0) 41..45 Other Read |
322 | "#]], | ||
322 | ); | 323 | ); |
323 | } | 324 | } |
324 | 325 | ||
325 | #[test] | 326 | #[test] |
326 | fn test_find_all_refs_for_param_inside() { | 327 | fn test_find_all_refs_for_param_inside() { |
327 | let refs = get_all_refs( | 328 | check( |
328 | r#" | 329 | r#" |
329 | fn foo(i : u32) -> u32 { | 330 | fn foo(i : u32) -> u32 { i<|> } |
330 | i<|> | ||
331 | } | ||
332 | "#, | 331 | "#, |
332 | expect![[r#" | ||
333 | i IDENT_PAT FileId(0) 7..8 Other | ||
334 | |||
335 | FileId(0) 25..26 Other Read | ||
336 | "#]], | ||
333 | ); | 337 | ); |
334 | check_result(refs, "i IDENT_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]); | ||
335 | } | 338 | } |
336 | 339 | ||
337 | #[test] | 340 | #[test] |
338 | fn test_find_all_refs_for_fn_param() { | 341 | fn test_find_all_refs_for_fn_param() { |
339 | let refs = get_all_refs( | 342 | check( |
340 | r#" | 343 | r#" |
341 | fn foo(i<|> : u32) -> u32 { | 344 | fn foo(i<|> : u32) -> u32 { i } |
342 | i | ||
343 | } | ||
344 | "#, | 345 | "#, |
346 | expect![[r#" | ||
347 | i IDENT_PAT FileId(0) 7..8 Other | ||
348 | |||
349 | FileId(0) 25..26 Other Read | ||
350 | "#]], | ||
345 | ); | 351 | ); |
346 | check_result(refs, "i IDENT_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]); | ||
347 | } | 352 | } |
348 | 353 | ||
349 | #[test] | 354 | #[test] |
350 | fn test_find_all_refs_field_name() { | 355 | fn test_find_all_refs_field_name() { |
351 | let refs = get_all_refs( | 356 | check( |
352 | r#" | 357 | r#" |
353 | //- /lib.rs | 358 | //- /lib.rs |
354 | struct Foo { | 359 | struct Foo { |
@@ -359,30 +364,33 @@ fn main(s: Foo) { | |||
359 | let f = s.spam; | 364 | let f = s.spam; |
360 | } | 365 | } |
361 | "#, | 366 | "#, |
362 | ); | 367 | expect![[r#" |
363 | check_result( | 368 | spam RECORD_FIELD FileId(0) 17..30 21..25 Other |
364 | refs, | 369 | |
365 | "spam RECORD_FIELD FileId(1) 17..30 21..25 Other", | 370 | FileId(0) 67..71 Other Read |
366 | &["FileId(1) 67..71 Other Read"], | 371 | "#]], |
367 | ); | 372 | ); |
368 | } | 373 | } |
369 | 374 | ||
370 | #[test] | 375 | #[test] |
371 | fn test_find_all_refs_impl_item_name() { | 376 | fn test_find_all_refs_impl_item_name() { |
372 | let refs = get_all_refs( | 377 | check( |
373 | r#" | 378 | r#" |
374 | struct Foo; | 379 | struct Foo; |
375 | impl Foo { | 380 | impl Foo { |
376 | fn f<|>(&self) { } | 381 | fn f<|>(&self) { } |
377 | } | 382 | } |
378 | "#, | 383 | "#, |
384 | expect![[r#" | ||
385 | f FN FileId(0) 27..43 30..31 Other | ||
386 | |||
387 | "#]], | ||
379 | ); | 388 | ); |
380 | check_result(refs, "f FN FileId(1) 27..43 30..31 Other", &[]); | ||
381 | } | 389 | } |
382 | 390 | ||
383 | #[test] | 391 | #[test] |
384 | fn test_find_all_refs_enum_var_name() { | 392 | fn test_find_all_refs_enum_var_name() { |
385 | let refs = get_all_refs( | 393 | check( |
386 | r#" | 394 | r#" |
387 | enum Foo { | 395 | enum Foo { |
388 | A, | 396 | A, |
@@ -390,13 +398,16 @@ enum Foo { | |||
390 | C, | 398 | C, |
391 | } | 399 | } |
392 | "#, | 400 | "#, |
401 | expect![[r#" | ||
402 | B VARIANT FileId(0) 22..23 22..23 Other | ||
403 | |||
404 | "#]], | ||
393 | ); | 405 | ); |
394 | check_result(refs, "B VARIANT FileId(1) 22..23 22..23 Other", &[]); | ||
395 | } | 406 | } |
396 | 407 | ||
397 | #[test] | 408 | #[test] |
398 | fn test_find_all_refs_two_modules() { | 409 | fn test_find_all_refs_two_modules() { |
399 | let (analysis, pos) = analysis_and_position( | 410 | check( |
400 | r#" | 411 | r#" |
401 | //- /lib.rs | 412 | //- /lib.rs |
402 | pub mod foo; | 413 | pub mod foo; |
@@ -428,12 +439,12 @@ fn f() { | |||
428 | let i = foo::Foo<|> { n: 5 }; | 439 | let i = foo::Foo<|> { n: 5 }; |
429 | } | 440 | } |
430 | "#, | 441 | "#, |
431 | ); | 442 | expect![[r#" |
432 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 443 | Foo STRUCT FileId(1) 17..51 28..31 Other |
433 | check_result( | 444 | |
434 | refs, | 445 | FileId(0) 53..56 StructLiteral |
435 | "Foo STRUCT FileId(2) 17..51 28..31 Other", | 446 | FileId(2) 79..82 StructLiteral |
436 | &["FileId(1) 53..56 StructLiteral", "FileId(3) 79..82 StructLiteral"], | 447 | "#]], |
437 | ); | 448 | ); |
438 | } | 449 | } |
439 | 450 | ||
@@ -442,7 +453,7 @@ fn f() { | |||
442 | // which is the whole `foo.rs`, and the second one is in `use foo::Foo`. | 453 | // which is the whole `foo.rs`, and the second one is in `use foo::Foo`. |
443 | #[test] | 454 | #[test] |
444 | fn test_find_all_refs_decl_module() { | 455 | fn test_find_all_refs_decl_module() { |
445 | let (analysis, pos) = analysis_and_position( | 456 | check( |
446 | r#" | 457 | r#" |
447 | //- /lib.rs | 458 | //- /lib.rs |
448 | mod foo<|>; | 459 | mod foo<|>; |
@@ -458,14 +469,17 @@ pub struct Foo { | |||
458 | pub n: u32, | 469 | pub n: u32, |
459 | } | 470 | } |
460 | "#, | 471 | "#, |
472 | expect![[r#" | ||
473 | foo SOURCE_FILE FileId(1) 0..35 Other | ||
474 | |||
475 | FileId(0) 14..17 Other | ||
476 | "#]], | ||
461 | ); | 477 | ); |
462 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | ||
463 | check_result(refs, "foo SOURCE_FILE FileId(2) 0..35 Other", &["FileId(1) 14..17 Other"]); | ||
464 | } | 478 | } |
465 | 479 | ||
466 | #[test] | 480 | #[test] |
467 | fn test_find_all_refs_super_mod_vis() { | 481 | fn test_find_all_refs_super_mod_vis() { |
468 | let (analysis, pos) = analysis_and_position( | 482 | check( |
469 | r#" | 483 | r#" |
470 | //- /lib.rs | 484 | //- /lib.rs |
471 | mod foo; | 485 | mod foo; |
@@ -483,12 +497,12 @@ pub(super) struct Foo<|> { | |||
483 | pub n: u32, | 497 | pub n: u32, |
484 | } | 498 | } |
485 | "#, | 499 | "#, |
486 | ); | 500 | expect![[r#" |
487 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 501 | Foo STRUCT FileId(2) 0..41 18..21 Other |
488 | check_result( | 502 | |
489 | refs, | 503 | FileId(1) 20..23 Other |
490 | "Foo STRUCT FileId(3) 0..41 18..21 Other", | 504 | FileId(1) 47..50 StructLiteral |
491 | &["FileId(2) 20..23 Other", "FileId(2) 47..50 StructLiteral"], | 505 | "#]], |
492 | ); | 506 | ); |
493 | } | 507 | } |
494 | 508 | ||
@@ -508,29 +522,31 @@ pub(super) struct Foo<|> { | |||
508 | fn f() { super::quux(); } | 522 | fn f() { super::quux(); } |
509 | "#; | 523 | "#; |
510 | 524 | ||
511 | let (mock, pos) = MockAnalysis::with_files_and_position(code); | 525 | check_with_scope( |
512 | let bar = mock.id_of("/bar.rs"); | 526 | code, |
513 | let analysis = mock.analysis(); | 527 | None, |
528 | expect![[r#" | ||
529 | quux FN FileId(0) 19..35 26..30 Other | ||
514 | 530 | ||
515 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 531 | FileId(1) 16..20 StructLiteral |
516 | check_result( | 532 | FileId(2) 16..20 StructLiteral |
517 | refs, | 533 | "#]], |
518 | "quux FN FileId(1) 19..35 26..30 Other", | ||
519 | &["FileId(2) 16..20 StructLiteral", "FileId(3) 16..20 StructLiteral"], | ||
520 | ); | 534 | ); |
521 | 535 | ||
522 | let refs = | 536 | check_with_scope( |
523 | analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); | 537 | code, |
524 | check_result( | 538 | Some(SearchScope::single_file(FileId(2))), |
525 | refs, | 539 | expect![[r#" |
526 | "quux FN FileId(1) 19..35 26..30 Other", | 540 | quux FN FileId(0) 19..35 26..30 Other |
527 | &["FileId(3) 16..20 StructLiteral"], | 541 | |
542 | FileId(2) 16..20 StructLiteral | ||
543 | "#]], | ||
528 | ); | 544 | ); |
529 | } | 545 | } |
530 | 546 | ||
531 | #[test] | 547 | #[test] |
532 | fn test_find_all_refs_macro_def() { | 548 | fn test_find_all_refs_macro_def() { |
533 | let refs = get_all_refs( | 549 | check( |
534 | r#" | 550 | r#" |
535 | #[macro_export] | 551 | #[macro_export] |
536 | macro_rules! m1<|> { () => (()) } | 552 | macro_rules! m1<|> { () => (()) } |
@@ -540,34 +556,36 @@ fn foo() { | |||
540 | m1(); | 556 | m1(); |
541 | } | 557 | } |
542 | "#, | 558 | "#, |
543 | ); | 559 | expect![[r#" |
544 | check_result( | 560 | m1 MACRO_CALL FileId(0) 0..46 29..31 Other |
545 | refs, | 561 | |
546 | "m1 MACRO_CALL FileId(1) 0..46 29..31 Other", | 562 | FileId(0) 63..65 StructLiteral |
547 | &["FileId(1) 63..65 StructLiteral", "FileId(1) 73..75 StructLiteral"], | 563 | FileId(0) 73..75 StructLiteral |
564 | "#]], | ||
548 | ); | 565 | ); |
549 | } | 566 | } |
550 | 567 | ||
551 | #[test] | 568 | #[test] |
552 | fn test_basic_highlight_read_write() { | 569 | fn test_basic_highlight_read_write() { |
553 | let refs = get_all_refs( | 570 | check( |
554 | r#" | 571 | r#" |
555 | fn foo() { | 572 | fn foo() { |
556 | let mut i<|> = 0; | 573 | let mut i<|> = 0; |
557 | i = i + 1; | 574 | i = i + 1; |
558 | } | 575 | } |
559 | "#, | 576 | "#, |
560 | ); | 577 | expect![[r#" |
561 | check_result( | 578 | i IDENT_PAT FileId(0) 23..24 Other Write |
562 | refs, | 579 | |
563 | "i IDENT_PAT FileId(1) 23..24 Other Write", | 580 | FileId(0) 34..35 Other Write |
564 | &["FileId(1) 34..35 Other Write", "FileId(1) 38..39 Other Read"], | 581 | FileId(0) 38..39 Other Read |
582 | "#]], | ||
565 | ); | 583 | ); |
566 | } | 584 | } |
567 | 585 | ||
568 | #[test] | 586 | #[test] |
569 | fn test_basic_highlight_field_read_write() { | 587 | fn test_basic_highlight_field_read_write() { |
570 | let refs = get_all_refs( | 588 | check( |
571 | r#" | 589 | r#" |
572 | struct S { | 590 | struct S { |
573 | f: u32, | 591 | f: u32, |
@@ -578,38 +596,41 @@ fn foo() { | |||
578 | s.f<|> = 0; | 596 | s.f<|> = 0; |
579 | } | 597 | } |
580 | "#, | 598 | "#, |
581 | ); | 599 | expect![[r#" |
582 | check_result( | 600 | f RECORD_FIELD FileId(0) 15..21 15..16 Other |
583 | refs, | 601 | |
584 | "f RECORD_FIELD FileId(1) 15..21 15..16 Other", | 602 | FileId(0) 55..56 Other Read |
585 | &["FileId(1) 55..56 Other Read", "FileId(1) 68..69 Other Write"], | 603 | FileId(0) 68..69 Other Write |
604 | "#]], | ||
586 | ); | 605 | ); |
587 | } | 606 | } |
588 | 607 | ||
589 | #[test] | 608 | #[test] |
590 | fn test_basic_highlight_decl_no_write() { | 609 | fn test_basic_highlight_decl_no_write() { |
591 | let refs = get_all_refs( | 610 | check( |
592 | r#" | 611 | r#" |
593 | fn foo() { | 612 | fn foo() { |
594 | let i<|>; | 613 | let i<|>; |
595 | i = 1; | 614 | i = 1; |
596 | } | 615 | } |
597 | "#, | 616 | "#, |
617 | expect![[r#" | ||
618 | i IDENT_PAT FileId(0) 19..20 Other | ||
619 | |||
620 | FileId(0) 26..27 Other Write | ||
621 | "#]], | ||
598 | ); | 622 | ); |
599 | check_result(refs, "i IDENT_PAT FileId(1) 19..20 Other", &["FileId(1) 26..27 Other Write"]); | ||
600 | } | 623 | } |
601 | 624 | ||
602 | #[test] | 625 | #[test] |
603 | fn test_find_struct_function_refs_outside_module() { | 626 | fn test_find_struct_function_refs_outside_module() { |
604 | let refs = get_all_refs( | 627 | check( |
605 | r#" | 628 | r#" |
606 | mod foo { | 629 | mod foo { |
607 | pub struct Foo; | 630 | pub struct Foo; |
608 | 631 | ||
609 | impl Foo { | 632 | impl Foo { |
610 | pub fn new<|>() -> Foo { | 633 | pub fn new<|>() -> Foo { Foo } |
611 | Foo | ||
612 | } | ||
613 | } | 634 | } |
614 | } | 635 | } |
615 | 636 | ||
@@ -617,80 +638,62 @@ fn main() { | |||
617 | let _f = foo::Foo::new(); | 638 | let _f = foo::Foo::new(); |
618 | } | 639 | } |
619 | "#, | 640 | "#, |
620 | ); | 641 | expect![[r#" |
621 | check_result( | 642 | new FN FileId(0) 54..81 61..64 Other |
622 | refs, | 643 | |
623 | "new FN FileId(1) 54..101 61..64 Other", | 644 | FileId(0) 126..129 StructLiteral |
624 | &["FileId(1) 146..149 StructLiteral"], | 645 | "#]], |
625 | ); | 646 | ); |
626 | } | 647 | } |
627 | 648 | ||
628 | #[test] | 649 | #[test] |
629 | fn test_find_all_refs_nested_module() { | 650 | fn test_find_all_refs_nested_module() { |
630 | let code = r#" | 651 | check( |
631 | //- /lib.rs | 652 | r#" |
632 | mod foo { | 653 | //- /lib.rs |
633 | mod bar; | 654 | mod foo { mod bar; } |
634 | } | ||
635 | 655 | ||
636 | fn f<|>() {} | 656 | fn f<|>() {} |
637 | 657 | ||
638 | //- /foo/bar.rs | 658 | //- /foo/bar.rs |
639 | use crate::f; | 659 | use crate::f; |
640 | 660 | ||
641 | fn g() { | 661 | fn g() { f(); } |
642 | f(); | 662 | "#, |
643 | } | 663 | expect![[r#" |
644 | "#; | 664 | f FN FileId(0) 22..31 25..26 Other |
645 | 665 | ||
646 | let (analysis, pos) = analysis_and_position(code); | 666 | FileId(1) 11..12 Other |
647 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 667 | FileId(1) 24..25 StructLiteral |
648 | check_result( | 668 | "#]], |
649 | refs, | ||
650 | "f FN FileId(1) 26..35 29..30 Other", | ||
651 | &["FileId(2) 11..12 Other", "FileId(2) 28..29 StructLiteral"], | ||
652 | ); | 669 | ); |
653 | } | 670 | } |
654 | 671 | ||
655 | fn get_all_refs(ra_fixture: &str) -> ReferenceSearchResult { | 672 | fn check(ra_fixture: &str, expect: Expect) { |
656 | let (analysis, position) = analysis_and_position(ra_fixture); | 673 | check_with_scope(ra_fixture, None, expect) |
657 | analysis.find_all_refs(position, None).unwrap().unwrap() | ||
658 | } | 674 | } |
659 | 675 | ||
660 | fn check_result(res: ReferenceSearchResult, expected_decl: &str, expected_refs: &[&str]) { | 676 | fn check_with_scope(ra_fixture: &str, search_scope: Option<SearchScope>, expect: Expect) { |
661 | res.declaration().assert_match(expected_decl); | 677 | let (analysis, pos) = fixture::position(ra_fixture); |
662 | assert_eq!(res.references.len(), expected_refs.len()); | 678 | let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap(); |
663 | res.references() | ||
664 | .iter() | ||
665 | .enumerate() | ||
666 | .for_each(|(i, r)| ref_assert_match(r, expected_refs[i])); | ||
667 | } | ||
668 | 679 | ||
669 | impl Declaration { | 680 | let mut actual = String::new(); |
670 | fn debug_render(&self) -> String { | 681 | { |
671 | let mut s = format!("{} {:?}", self.nav.debug_render(), self.kind); | 682 | let decl = refs.declaration; |
672 | if let Some(access) = self.access { | 683 | format_to!(actual, "{} {:?}", decl.nav.debug_render(), decl.kind); |
673 | s.push_str(&format!(" {:?}", access)); | 684 | if let Some(access) = decl.access { |
685 | format_to!(actual, " {:?}", access) | ||
674 | } | 686 | } |
675 | s | 687 | actual += "\n\n"; |
676 | } | 688 | } |
677 | 689 | ||
678 | fn assert_match(&self, expected: &str) { | 690 | for r in &refs.references { |
679 | let actual = self.debug_render(); | 691 | format_to!(actual, "{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind); |
680 | test_utils::assert_eq_text!(expected.trim(), actual.trim(),); | 692 | if let Some(access) = r.access { |
681 | } | 693 | format_to!(actual, " {:?}", access); |
682 | } | 694 | } |
683 | 695 | actual += "\n"; | |
684 | fn ref_debug_render(r: &Reference) -> String { | ||
685 | let mut s = format!("{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind); | ||
686 | if let Some(access) = r.access { | ||
687 | s.push_str(&format!(" {:?}", access)); | ||
688 | } | 696 | } |
689 | s | 697 | expect.assert_eq(&actual) |
690 | } | ||
691 | |||
692 | fn ref_assert_match(r: &Reference, expected: &str) { | ||
693 | let actual = ref_debug_render(r); | ||
694 | test_utils::assert_eq_text!(expected.trim(), actual.trim(),); | ||
695 | } | 698 | } |
696 | } | 699 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 301629763..8cbe1ae5a 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -275,11 +275,11 @@ mod tests { | |||
275 | use test_utils::{assert_eq_text, mark}; | 275 | use test_utils::{assert_eq_text, mark}; |
276 | use text_edit::TextEdit; | 276 | use text_edit::TextEdit; |
277 | 277 | ||
278 | use crate::{mock_analysis::analysis_and_position, FileId}; | 278 | use crate::{fixture, FileId}; |
279 | 279 | ||
280 | fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 280 | fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
281 | let ra_fixture_after = &trim_indent(ra_fixture_after); | 281 | let ra_fixture_after = &trim_indent(ra_fixture_after); |
282 | let (analysis, position) = analysis_and_position(ra_fixture_before); | 282 | let (analysis, position) = fixture::position(ra_fixture_before); |
283 | let source_change = analysis.rename(position, new_name).unwrap(); | 283 | let source_change = analysis.rename(position, new_name).unwrap(); |
284 | let mut text_edit_builder = TextEdit::builder(); | 284 | let mut text_edit_builder = TextEdit::builder(); |
285 | let mut file_id: Option<FileId> = None; | 285 | let mut file_id: Option<FileId> = None; |
@@ -297,7 +297,7 @@ mod tests { | |||
297 | } | 297 | } |
298 | 298 | ||
299 | fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { | 299 | fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { |
300 | let (analysis, position) = analysis_and_position(ra_fixture); | 300 | let (analysis, position) = fixture::position(ra_fixture); |
301 | let source_change = analysis.rename(position, new_name).unwrap().unwrap(); | 301 | let source_change = analysis.rename(position, new_name).unwrap().unwrap(); |
302 | expect.assert_debug_eq(&source_change) | 302 | expect.assert_debug_eq(&source_change) |
303 | } | 303 | } |
@@ -314,7 +314,7 @@ mod tests { | |||
314 | 314 | ||
315 | #[test] | 315 | #[test] |
316 | fn test_rename_to_invalid_identifier() { | 316 | fn test_rename_to_invalid_identifier() { |
317 | let (analysis, position) = analysis_and_position(r#"fn main() { let i<|> = 1; }"#); | 317 | let (analysis, position) = fixture::position(r#"fn main() { let i<|> = 1; }"#); |
318 | let new_name = "invalid!"; | 318 | let new_name = "invalid!"; |
319 | let source_change = analysis.rename(position, new_name).unwrap(); | 319 | let source_change = analysis.rename(position, new_name).unwrap(); |
320 | assert!(source_change.is_none()); | 320 | assert!(source_change.is_none()); |
@@ -602,7 +602,7 @@ mod foo<|>; | |||
602 | source_file_edits: [ | 602 | source_file_edits: [ |
603 | SourceFileEdit { | 603 | SourceFileEdit { |
604 | file_id: FileId( | 604 | file_id: FileId( |
605 | 2, | 605 | 1, |
606 | ), | 606 | ), |
607 | edit: TextEdit { | 607 | edit: TextEdit { |
608 | indels: [ | 608 | indels: [ |
@@ -617,10 +617,10 @@ mod foo<|>; | |||
617 | file_system_edits: [ | 617 | file_system_edits: [ |
618 | MoveFile { | 618 | MoveFile { |
619 | src: FileId( | 619 | src: FileId( |
620 | 3, | 620 | 2, |
621 | ), | 621 | ), |
622 | anchor: FileId( | 622 | anchor: FileId( |
623 | 3, | 623 | 2, |
624 | ), | 624 | ), |
625 | dst: "foo2.rs", | 625 | dst: "foo2.rs", |
626 | }, | 626 | }, |
@@ -655,7 +655,7 @@ use crate::foo<|>::FooContent; | |||
655 | source_file_edits: [ | 655 | source_file_edits: [ |
656 | SourceFileEdit { | 656 | SourceFileEdit { |
657 | file_id: FileId( | 657 | file_id: FileId( |
658 | 1, | 658 | 0, |
659 | ), | 659 | ), |
660 | edit: TextEdit { | 660 | edit: TextEdit { |
661 | indels: [ | 661 | indels: [ |
@@ -668,7 +668,7 @@ use crate::foo<|>::FooContent; | |||
668 | }, | 668 | }, |
669 | SourceFileEdit { | 669 | SourceFileEdit { |
670 | file_id: FileId( | 670 | file_id: FileId( |
671 | 3, | 671 | 2, |
672 | ), | 672 | ), |
673 | edit: TextEdit { | 673 | edit: TextEdit { |
674 | indels: [ | 674 | indels: [ |
@@ -683,10 +683,10 @@ use crate::foo<|>::FooContent; | |||
683 | file_system_edits: [ | 683 | file_system_edits: [ |
684 | MoveFile { | 684 | MoveFile { |
685 | src: FileId( | 685 | src: FileId( |
686 | 2, | 686 | 1, |
687 | ), | 687 | ), |
688 | anchor: FileId( | 688 | anchor: FileId( |
689 | 2, | 689 | 1, |
690 | ), | 690 | ), |
691 | dst: "quux.rs", | 691 | dst: "quux.rs", |
692 | }, | 692 | }, |
@@ -715,7 +715,7 @@ mod fo<|>o; | |||
715 | source_file_edits: [ | 715 | source_file_edits: [ |
716 | SourceFileEdit { | 716 | SourceFileEdit { |
717 | file_id: FileId( | 717 | file_id: FileId( |
718 | 1, | 718 | 0, |
719 | ), | 719 | ), |
720 | edit: TextEdit { | 720 | edit: TextEdit { |
721 | indels: [ | 721 | indels: [ |
@@ -730,10 +730,10 @@ mod fo<|>o; | |||
730 | file_system_edits: [ | 730 | file_system_edits: [ |
731 | MoveFile { | 731 | MoveFile { |
732 | src: FileId( | 732 | src: FileId( |
733 | 2, | 733 | 1, |
734 | ), | 734 | ), |
735 | anchor: FileId( | 735 | anchor: FileId( |
736 | 2, | 736 | 1, |
737 | ), | 737 | ), |
738 | dst: "../foo2/mod.rs", | 738 | dst: "../foo2/mod.rs", |
739 | }, | 739 | }, |
@@ -763,7 +763,7 @@ mod outer { mod fo<|>o; } | |||
763 | source_file_edits: [ | 763 | source_file_edits: [ |
764 | SourceFileEdit { | 764 | SourceFileEdit { |
765 | file_id: FileId( | 765 | file_id: FileId( |
766 | 1, | 766 | 0, |
767 | ), | 767 | ), |
768 | edit: TextEdit { | 768 | edit: TextEdit { |
769 | indels: [ | 769 | indels: [ |
@@ -778,10 +778,10 @@ mod outer { mod fo<|>o; } | |||
778 | file_system_edits: [ | 778 | file_system_edits: [ |
779 | MoveFile { | 779 | MoveFile { |
780 | src: FileId( | 780 | src: FileId( |
781 | 2, | 781 | 1, |
782 | ), | 782 | ), |
783 | anchor: FileId( | 783 | anchor: FileId( |
784 | 2, | 784 | 1, |
785 | ), | 785 | ), |
786 | dst: "bar.rs", | 786 | dst: "bar.rs", |
787 | }, | 787 | }, |
@@ -834,7 +834,7 @@ pub mod foo<|>; | |||
834 | source_file_edits: [ | 834 | source_file_edits: [ |
835 | SourceFileEdit { | 835 | SourceFileEdit { |
836 | file_id: FileId( | 836 | file_id: FileId( |
837 | 2, | 837 | 1, |
838 | ), | 838 | ), |
839 | edit: TextEdit { | 839 | edit: TextEdit { |
840 | indels: [ | 840 | indels: [ |
@@ -847,7 +847,7 @@ pub mod foo<|>; | |||
847 | }, | 847 | }, |
848 | SourceFileEdit { | 848 | SourceFileEdit { |
849 | file_id: FileId( | 849 | file_id: FileId( |
850 | 1, | 850 | 0, |
851 | ), | 851 | ), |
852 | edit: TextEdit { | 852 | edit: TextEdit { |
853 | indels: [ | 853 | indels: [ |
@@ -862,10 +862,10 @@ pub mod foo<|>; | |||
862 | file_system_edits: [ | 862 | file_system_edits: [ |
863 | MoveFile { | 863 | MoveFile { |
864 | src: FileId( | 864 | src: FileId( |
865 | 3, | 865 | 2, |
866 | ), | 866 | ), |
867 | anchor: FileId( | 867 | anchor: FileId( |
868 | 3, | 868 | 2, |
869 | ), | 869 | ), |
870 | dst: "foo2.rs", | 870 | dst: "foo2.rs", |
871 | }, | 871 | }, |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index cfeff40c1..752ef2f21 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -292,7 +292,7 @@ fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { | |||
292 | mod tests { | 292 | mod tests { |
293 | use expect_test::{expect, Expect}; | 293 | use expect_test::{expect, Expect}; |
294 | 294 | ||
295 | use crate::mock_analysis::analysis_and_position; | 295 | use crate::fixture; |
296 | 296 | ||
297 | use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST}; | 297 | use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST}; |
298 | 298 | ||
@@ -302,7 +302,7 @@ mod tests { | |||
302 | actions: &[&RunnableAction], | 302 | actions: &[&RunnableAction], |
303 | expect: Expect, | 303 | expect: Expect, |
304 | ) { | 304 | ) { |
305 | let (analysis, position) = analysis_and_position(ra_fixture); | 305 | let (analysis, position) = fixture::position(ra_fixture); |
306 | let runnables = analysis.runnables(position.file_id).unwrap(); | 306 | let runnables = analysis.runnables(position.file_id).unwrap(); |
307 | expect.assert_debug_eq(&runnables); | 307 | expect.assert_debug_eq(&runnables); |
308 | assert_eq!( | 308 | assert_eq!( |
@@ -335,7 +335,7 @@ fn bench() {} | |||
335 | Runnable { | 335 | Runnable { |
336 | nav: NavigationTarget { | 336 | nav: NavigationTarget { |
337 | file_id: FileId( | 337 | file_id: FileId( |
338 | 1, | 338 | 0, |
339 | ), | 339 | ), |
340 | full_range: 1..13, | 340 | full_range: 1..13, |
341 | focus_range: Some( | 341 | focus_range: Some( |
@@ -353,7 +353,7 @@ fn bench() {} | |||
353 | Runnable { | 353 | Runnable { |
354 | nav: NavigationTarget { | 354 | nav: NavigationTarget { |
355 | file_id: FileId( | 355 | file_id: FileId( |
356 | 1, | 356 | 0, |
357 | ), | 357 | ), |
358 | full_range: 15..39, | 358 | full_range: 15..39, |
359 | focus_range: Some( | 359 | focus_range: Some( |
@@ -378,7 +378,7 @@ fn bench() {} | |||
378 | Runnable { | 378 | Runnable { |
379 | nav: NavigationTarget { | 379 | nav: NavigationTarget { |
380 | file_id: FileId( | 380 | file_id: FileId( |
381 | 1, | 381 | 0, |
382 | ), | 382 | ), |
383 | full_range: 41..75, | 383 | full_range: 41..75, |
384 | focus_range: Some( | 384 | focus_range: Some( |
@@ -403,7 +403,7 @@ fn bench() {} | |||
403 | Runnable { | 403 | Runnable { |
404 | nav: NavigationTarget { | 404 | nav: NavigationTarget { |
405 | file_id: FileId( | 405 | file_id: FileId( |
406 | 1, | 406 | 0, |
407 | ), | 407 | ), |
408 | full_range: 77..99, | 408 | full_range: 77..99, |
409 | focus_range: Some( | 409 | focus_range: Some( |
@@ -494,7 +494,7 @@ fn should_have_no_runnable_6() {} | |||
494 | Runnable { | 494 | Runnable { |
495 | nav: NavigationTarget { | 495 | nav: NavigationTarget { |
496 | file_id: FileId( | 496 | file_id: FileId( |
497 | 1, | 497 | 0, |
498 | ), | 498 | ), |
499 | full_range: 1..13, | 499 | full_range: 1..13, |
500 | focus_range: Some( | 500 | focus_range: Some( |
@@ -512,7 +512,7 @@ fn should_have_no_runnable_6() {} | |||
512 | Runnable { | 512 | Runnable { |
513 | nav: NavigationTarget { | 513 | nav: NavigationTarget { |
514 | file_id: FileId( | 514 | file_id: FileId( |
515 | 1, | 515 | 0, |
516 | ), | 516 | ), |
517 | full_range: 15..74, | 517 | full_range: 15..74, |
518 | focus_range: None, | 518 | focus_range: None, |
@@ -532,7 +532,7 @@ fn should_have_no_runnable_6() {} | |||
532 | Runnable { | 532 | Runnable { |
533 | nav: NavigationTarget { | 533 | nav: NavigationTarget { |
534 | file_id: FileId( | 534 | file_id: FileId( |
535 | 1, | 535 | 0, |
536 | ), | 536 | ), |
537 | full_range: 76..148, | 537 | full_range: 76..148, |
538 | focus_range: None, | 538 | focus_range: None, |
@@ -552,7 +552,7 @@ fn should_have_no_runnable_6() {} | |||
552 | Runnable { | 552 | Runnable { |
553 | nav: NavigationTarget { | 553 | nav: NavigationTarget { |
554 | file_id: FileId( | 554 | file_id: FileId( |
555 | 1, | 555 | 0, |
556 | ), | 556 | ), |
557 | full_range: 150..254, | 557 | full_range: 150..254, |
558 | focus_range: None, | 558 | focus_range: None, |
@@ -596,7 +596,7 @@ impl Data { | |||
596 | Runnable { | 596 | Runnable { |
597 | nav: NavigationTarget { | 597 | nav: NavigationTarget { |
598 | file_id: FileId( | 598 | file_id: FileId( |
599 | 1, | 599 | 0, |
600 | ), | 600 | ), |
601 | full_range: 1..13, | 601 | full_range: 1..13, |
602 | focus_range: Some( | 602 | focus_range: Some( |
@@ -614,7 +614,7 @@ impl Data { | |||
614 | Runnable { | 614 | Runnable { |
615 | nav: NavigationTarget { | 615 | nav: NavigationTarget { |
616 | file_id: FileId( | 616 | file_id: FileId( |
617 | 1, | 617 | 0, |
618 | ), | 618 | ), |
619 | full_range: 44..98, | 619 | full_range: 44..98, |
620 | focus_range: None, | 620 | focus_range: None, |
@@ -653,7 +653,7 @@ mod test_mod { | |||
653 | Runnable { | 653 | Runnable { |
654 | nav: NavigationTarget { | 654 | nav: NavigationTarget { |
655 | file_id: FileId( | 655 | file_id: FileId( |
656 | 1, | 656 | 0, |
657 | ), | 657 | ), |
658 | full_range: 1..51, | 658 | full_range: 1..51, |
659 | focus_range: Some( | 659 | focus_range: Some( |
@@ -673,7 +673,7 @@ mod test_mod { | |||
673 | Runnable { | 673 | Runnable { |
674 | nav: NavigationTarget { | 674 | nav: NavigationTarget { |
675 | file_id: FileId( | 675 | file_id: FileId( |
676 | 1, | 676 | 0, |
677 | ), | 677 | ), |
678 | full_range: 20..49, | 678 | full_range: 20..49, |
679 | focus_range: Some( | 679 | focus_range: Some( |
@@ -733,7 +733,7 @@ mod root_tests { | |||
733 | Runnable { | 733 | Runnable { |
734 | nav: NavigationTarget { | 734 | nav: NavigationTarget { |
735 | file_id: FileId( | 735 | file_id: FileId( |
736 | 1, | 736 | 0, |
737 | ), | 737 | ), |
738 | full_range: 22..323, | 738 | full_range: 22..323, |
739 | focus_range: Some( | 739 | focus_range: Some( |
@@ -753,7 +753,7 @@ mod root_tests { | |||
753 | Runnable { | 753 | Runnable { |
754 | nav: NavigationTarget { | 754 | nav: NavigationTarget { |
755 | file_id: FileId( | 755 | file_id: FileId( |
756 | 1, | 756 | 0, |
757 | ), | 757 | ), |
758 | full_range: 51..192, | 758 | full_range: 51..192, |
759 | focus_range: Some( | 759 | focus_range: Some( |
@@ -773,7 +773,7 @@ mod root_tests { | |||
773 | Runnable { | 773 | Runnable { |
774 | nav: NavigationTarget { | 774 | nav: NavigationTarget { |
775 | file_id: FileId( | 775 | file_id: FileId( |
776 | 1, | 776 | 0, |
777 | ), | 777 | ), |
778 | full_range: 84..126, | 778 | full_range: 84..126, |
779 | focus_range: Some( | 779 | focus_range: Some( |
@@ -798,7 +798,7 @@ mod root_tests { | |||
798 | Runnable { | 798 | Runnable { |
799 | nav: NavigationTarget { | 799 | nav: NavigationTarget { |
800 | file_id: FileId( | 800 | file_id: FileId( |
801 | 1, | 801 | 0, |
802 | ), | 802 | ), |
803 | full_range: 140..182, | 803 | full_range: 140..182, |
804 | focus_range: Some( | 804 | focus_range: Some( |
@@ -823,7 +823,7 @@ mod root_tests { | |||
823 | Runnable { | 823 | Runnable { |
824 | nav: NavigationTarget { | 824 | nav: NavigationTarget { |
825 | file_id: FileId( | 825 | file_id: FileId( |
826 | 1, | 826 | 0, |
827 | ), | 827 | ), |
828 | full_range: 202..286, | 828 | full_range: 202..286, |
829 | focus_range: Some( | 829 | focus_range: Some( |
@@ -843,7 +843,7 @@ mod root_tests { | |||
843 | Runnable { | 843 | Runnable { |
844 | nav: NavigationTarget { | 844 | nav: NavigationTarget { |
845 | file_id: FileId( | 845 | file_id: FileId( |
846 | 1, | 846 | 0, |
847 | ), | 847 | ), |
848 | full_range: 235..276, | 848 | full_range: 235..276, |
849 | focus_range: Some( | 849 | focus_range: Some( |
@@ -886,7 +886,7 @@ fn test_foo1() {} | |||
886 | Runnable { | 886 | Runnable { |
887 | nav: NavigationTarget { | 887 | nav: NavigationTarget { |
888 | file_id: FileId( | 888 | file_id: FileId( |
889 | 1, | 889 | 0, |
890 | ), | 890 | ), |
891 | full_range: 1..50, | 891 | full_range: 1..50, |
892 | focus_range: Some( | 892 | focus_range: Some( |
@@ -934,7 +934,7 @@ fn test_foo1() {} | |||
934 | Runnable { | 934 | Runnable { |
935 | nav: NavigationTarget { | 935 | nav: NavigationTarget { |
936 | file_id: FileId( | 936 | file_id: FileId( |
937 | 1, | 937 | 0, |
938 | ), | 938 | ), |
939 | full_range: 1..72, | 939 | full_range: 1..72, |
940 | focus_range: Some( | 940 | focus_range: Some( |
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index c23708181..0af84daa0 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs | |||
@@ -2,19 +2,19 @@ use std::{fmt, iter::FromIterator, sync::Arc}; | |||
2 | 2 | ||
3 | use base_db::{ | 3 | use base_db::{ |
4 | salsa::debug::{DebugQueryTable, TableEntry}, | 4 | salsa::debug::{DebugQueryTable, TableEntry}, |
5 | FileTextQuery, SourceRootId, | 5 | CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, |
6 | }; | 6 | }; |
7 | use hir::MacroFile; | 7 | use hir::MacroFile; |
8 | use ide_db::{ | 8 | use ide_db::{ |
9 | symbol_index::{LibrarySymbolsQuery, SymbolIndex}, | 9 | symbol_index::{LibrarySymbolsQuery, SymbolIndex}, |
10 | RootDatabase, | 10 | RootDatabase, |
11 | }; | 11 | }; |
12 | use itertools::Itertools; | ||
12 | use profile::{memory_usage, Bytes}; | 13 | use profile::{memory_usage, Bytes}; |
13 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
15 | use stdx::format_to; | ||
14 | use syntax::{ast, Parse, SyntaxNode}; | 16 | use syntax::{ast, Parse, SyntaxNode}; |
15 | 17 | ||
16 | use crate::FileId; | ||
17 | |||
18 | fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | 18 | fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { |
19 | base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>() | 19 | base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>() |
20 | } | 20 | } |
@@ -31,20 +31,36 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | |||
31 | // | 31 | // |
32 | // | VS Code | **Rust Analyzer: Status** | 32 | // | VS Code | **Rust Analyzer: Status** |
33 | // |=== | 33 | // |=== |
34 | pub(crate) fn status(db: &RootDatabase) -> String { | 34 | pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { |
35 | let files_stats = FileTextQuery.in_db(db).entries::<FilesStats>(); | 35 | let mut buf = String::new(); |
36 | let syntax_tree_stats = syntax_tree_stats(db); | 36 | format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::<FilesStats>()); |
37 | let macro_syntax_tree_stats = macro_syntax_tree_stats(db); | 37 | format_to!(buf, "{}\n", LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>()); |
38 | let symbols_stats = LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>(); | 38 | format_to!(buf, "{}\n", syntax_tree_stats(db)); |
39 | format!( | 39 | format_to!(buf, "{} (macros)\n", macro_syntax_tree_stats(db)); |
40 | "{}\n{}\n{}\n{} (macros)\n\n\nmemory:\n{}\ngc {:?} seconds ago", | 40 | format_to!(buf, "{} total\n", memory_usage()); |
41 | files_stats, | 41 | |
42 | symbols_stats, | 42 | if let Some(file_id) = file_id { |
43 | syntax_tree_stats, | 43 | format_to!(buf, "\nfile info:\n"); |
44 | macro_syntax_tree_stats, | 44 | let krate = crate::parent_module::crate_for(db, file_id).pop(); |
45 | memory_usage(), | 45 | match krate { |
46 | db.last_gc.elapsed().as_secs(), | 46 | Some(krate) => { |
47 | ) | 47 | let crate_graph = db.crate_graph(); |
48 | let display_crate = |krate: CrateId| match &crate_graph[krate].display_name { | ||
49 | Some(it) => format!("{}({:?})", it, krate), | ||
50 | None => format!("{:?}", krate), | ||
51 | }; | ||
52 | format_to!(buf, "crate: {}\n", display_crate(krate)); | ||
53 | let deps = crate_graph[krate] | ||
54 | .dependencies | ||
55 | .iter() | ||
56 | .map(|dep| format!("{}={:?}", dep.name, dep.crate_id)) | ||
57 | .format(", "); | ||
58 | format_to!(buf, "deps: {}\n", deps); | ||
59 | } | ||
60 | None => format_to!(buf, "does not belong to any crate"), | ||
61 | } | ||
62 | } | ||
63 | buf | ||
48 | } | 64 | } |
49 | 65 | ||
50 | #[derive(Default)] | 66 | #[derive(Default)] |
@@ -121,7 +137,7 @@ struct LibrarySymbolsStats { | |||
121 | 137 | ||
122 | impl fmt::Display for LibrarySymbolsStats { | 138 | impl fmt::Display for LibrarySymbolsStats { |
123 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | 139 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
124 | write!(fmt, "{} ({}) symbols", self.total, self.size) | 140 | write!(fmt, "{} ({}) index symbols", self.total, self.size) |
125 | } | 141 | } |
126 | } | 142 | } |
127 | 143 | ||
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 211e62ea1..694c4b7fa 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -3,7 +3,7 @@ use std::fs; | |||
3 | use expect_test::{expect_file, ExpectFile}; | 3 | use expect_test::{expect_file, ExpectFile}; |
4 | use test_utils::project_dir; | 4 | use test_utils::project_dir; |
5 | 5 | ||
6 | use crate::{mock_analysis::single_file, FileRange, TextRange}; | 6 | use crate::{fixture, FileRange, TextRange}; |
7 | 7 | ||
8 | #[test] | 8 | #[test] |
9 | fn test_highlighting() { | 9 | fn test_highlighting() { |
@@ -178,7 +178,7 @@ fn accidentally_quadratic() { | |||
178 | let file = project_dir().join("crates/syntax/test_data/accidentally_quadratic"); | 178 | let file = project_dir().join("crates/syntax/test_data/accidentally_quadratic"); |
179 | let src = fs::read_to_string(file).unwrap(); | 179 | let src = fs::read_to_string(file).unwrap(); |
180 | 180 | ||
181 | let (analysis, file_id) = single_file(&src); | 181 | let (analysis, file_id) = fixture::file(&src); |
182 | 182 | ||
183 | // let t = std::time::Instant::now(); | 183 | // let t = std::time::Instant::now(); |
184 | let _ = analysis.highlight(file_id).unwrap(); | 184 | let _ = analysis.highlight(file_id).unwrap(); |
@@ -187,7 +187,7 @@ fn accidentally_quadratic() { | |||
187 | 187 | ||
188 | #[test] | 188 | #[test] |
189 | fn test_ranges() { | 189 | fn test_ranges() { |
190 | let (analysis, file_id) = single_file( | 190 | let (analysis, file_id) = fixture::file( |
191 | r#" | 191 | r#" |
192 | #[derive(Clone, Debug)] | 192 | #[derive(Clone, Debug)] |
193 | struct Foo { | 193 | struct Foo { |
@@ -228,7 +228,7 @@ fn main() { | |||
228 | 228 | ||
229 | #[test] | 229 | #[test] |
230 | fn ranges_sorted() { | 230 | fn ranges_sorted() { |
231 | let (analysis, file_id) = single_file( | 231 | let (analysis, file_id) = fixture::file( |
232 | r#" | 232 | r#" |
233 | #[foo(bar = "bar")] | 233 | #[foo(bar = "bar")] |
234 | macro_rules! test {} | 234 | macro_rules! test {} |
@@ -462,12 +462,12 @@ macro_rules! noop { | |||
462 | fn test_extern_crate() { | 462 | fn test_extern_crate() { |
463 | check_highlighting( | 463 | check_highlighting( |
464 | r#" | 464 | r#" |
465 | //- /main.rs | 465 | //- /main.rs crate:main deps:std,alloc |
466 | extern crate std; | 466 | extern crate std; |
467 | extern crate alloc as abc; | 467 | extern crate alloc as abc; |
468 | //- /std/lib.rs | 468 | //- /std/lib.rs crate:std |
469 | pub struct S; | 469 | pub struct S; |
470 | //- /alloc/lib.rs | 470 | //- /alloc/lib.rs crate:alloc |
471 | pub struct A | 471 | pub struct A |
472 | "#, | 472 | "#, |
473 | expect_file!["./test_data/highlight_extern_crate.html"], | 473 | expect_file!["./test_data/highlight_extern_crate.html"], |
@@ -479,7 +479,7 @@ fn test_extern_crate() { | |||
479 | /// result as HTML, and compares it with the HTML file given as `snapshot`. | 479 | /// result as HTML, and compares it with the HTML file given as `snapshot`. |
480 | /// Note that the `snapshot` file is overwritten by the rendered HTML. | 480 | /// Note that the `snapshot` file is overwritten by the rendered HTML. |
481 | fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) { | 481 | fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) { |
482 | let (analysis, file_id) = single_file(ra_fixture); | 482 | let (analysis, file_id) = fixture::file(ra_fixture); |
483 | let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); | 483 | let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); |
484 | expect.assert_eq(actual_html) | 484 | expect.assert_eq(actual_html) |
485 | } | 485 | } |
diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs index f80044959..0eed2dbd7 100644 --- a/crates/ide/src/syntax_tree.rs +++ b/crates/ide/src/syntax_tree.rs | |||
@@ -104,12 +104,12 @@ fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<St | |||
104 | mod tests { | 104 | mod tests { |
105 | use test_utils::assert_eq_text; | 105 | use test_utils::assert_eq_text; |
106 | 106 | ||
107 | use crate::mock_analysis::{analysis_and_range, single_file}; | 107 | use crate::fixture; |
108 | 108 | ||
109 | #[test] | 109 | #[test] |
110 | fn test_syntax_tree_without_range() { | 110 | fn test_syntax_tree_without_range() { |
111 | // Basic syntax | 111 | // Basic syntax |
112 | let (analysis, file_id) = single_file(r#"fn foo() {}"#); | 112 | let (analysis, file_id) = fixture::file(r#"fn foo() {}"#); |
113 | let syn = analysis.syntax_tree(file_id, None).unwrap(); | 113 | let syn = analysis.syntax_tree(file_id, None).unwrap(); |
114 | 114 | ||
115 | assert_eq_text!( | 115 | assert_eq_text!( |
@@ -132,7 +132,7 @@ [email protected] | |||
132 | .trim() | 132 | .trim() |
133 | ); | 133 | ); |
134 | 134 | ||
135 | let (analysis, file_id) = single_file( | 135 | let (analysis, file_id) = fixture::file( |
136 | r#" | 136 | r#" |
137 | fn test() { | 137 | fn test() { |
138 | assert!(" | 138 | assert!(" |
@@ -184,7 +184,7 @@ [email protected] | |||
184 | 184 | ||
185 | #[test] | 185 | #[test] |
186 | fn test_syntax_tree_with_range() { | 186 | fn test_syntax_tree_with_range() { |
187 | let (analysis, range) = analysis_and_range(r#"<|>fn foo() {}<|>"#.trim()); | 187 | let (analysis, range) = fixture::range(r#"<|>fn foo() {}<|>"#.trim()); |
188 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); | 188 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); |
189 | 189 | ||
190 | assert_eq_text!( | 190 | assert_eq_text!( |
@@ -206,7 +206,7 @@ [email protected] | |||
206 | .trim() | 206 | .trim() |
207 | ); | 207 | ); |
208 | 208 | ||
209 | let (analysis, range) = analysis_and_range( | 209 | let (analysis, range) = fixture::range( |
210 | r#"fn test() { | 210 | r#"fn test() { |
211 | <|>assert!(" | 211 | <|>assert!(" |
212 | fn foo() { | 212 | fn foo() { |
@@ -242,7 +242,7 @@ [email protected] | |||
242 | 242 | ||
243 | #[test] | 243 | #[test] |
244 | fn test_syntax_tree_inside_string() { | 244 | fn test_syntax_tree_inside_string() { |
245 | let (analysis, range) = analysis_and_range( | 245 | let (analysis, range) = fixture::range( |
246 | r#"fn test() { | 246 | r#"fn test() { |
247 | assert!(" | 247 | assert!(" |
248 | <|>fn foo() { | 248 | <|>fn foo() { |
@@ -276,7 +276,7 @@ [email protected] | |||
276 | ); | 276 | ); |
277 | 277 | ||
278 | // With a raw string | 278 | // With a raw string |
279 | let (analysis, range) = analysis_and_range( | 279 | let (analysis, range) = fixture::range( |
280 | r###"fn test() { | 280 | r###"fn test() { |
281 | assert!(r#" | 281 | assert!(r#" |
282 | <|>fn foo() { | 282 | <|>fn foo() { |
@@ -310,7 +310,7 @@ [email protected] | |||
310 | ); | 310 | ); |
311 | 311 | ||
312 | // With a raw string | 312 | // With a raw string |
313 | let (analysis, range) = analysis_and_range( | 313 | let (analysis, range) = fixture::range( |
314 | r###"fn test() { | 314 | r###"fn test() { |
315 | assert!(r<|>#" | 315 | assert!(r<|>#" |
316 | fn foo() { | 316 | fn foo() { |
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index f7d46146c..a0dc4b9df 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs | |||
@@ -109,10 +109,10 @@ mod tests { | |||
109 | use stdx::trim_indent; | 109 | use stdx::trim_indent; |
110 | use test_utils::{assert_eq_text, mark}; | 110 | use test_utils::{assert_eq_text, mark}; |
111 | 111 | ||
112 | use crate::mock_analysis::analysis_and_position; | 112 | use crate::fixture; |
113 | 113 | ||
114 | fn apply_on_enter(before: &str) -> Option<String> { | 114 | fn apply_on_enter(before: &str) -> Option<String> { |
115 | let (analysis, position) = analysis_and_position(&before); | 115 | let (analysis, position) = fixture::position(&before); |
116 | let result = analysis.on_enter(position).unwrap()?; | 116 | let result = analysis.on_enter(position).unwrap()?; |
117 | 117 | ||
118 | let mut actual = analysis.file_text(position.file_id).unwrap().to_string(); | 118 | let mut actual = analysis.file_text(position.file_id).unwrap().to_string(); |