aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/call_hierarchy.rs56
-rw-r--r--crates/ide/src/call_info.rs4
-rw-r--r--crates/ide/src/completion.rs4
-rw-r--r--crates/ide/src/completion/complete_keyword.rs8
-rw-r--r--crates/ide/src/completion/complete_mod.rs4
-rw-r--r--crates/ide/src/completion/complete_postfix.rs61
-rw-r--r--crates/ide/src/completion/complete_postfix/format_like.rs277
-rw-r--r--crates/ide/src/completion/complete_qualified_path.rs4
-rw-r--r--crates/ide/src/completion/complete_unqualified_path.rs34
-rw-r--r--crates/ide/src/completion/completion_context.rs7
-rw-r--r--crates/ide/src/completion/presentation.rs4
-rw-r--r--crates/ide/src/completion/test_utils.rs9
-rw-r--r--crates/ide/src/diagnostics.rs39
-rw-r--r--crates/ide/src/display/navigation_target.rs10
-rw-r--r--crates/ide/src/expand_macro.rs4
-rw-r--r--crates/ide/src/extend_selection.rs4
-rw-r--r--crates/ide/src/fixture.rs70
-rw-r--r--crates/ide/src/fn_references.rs10
-rw-r--r--crates/ide/src/goto_definition.rs24
-rw-r--r--crates/ide/src/goto_implementation.rs6
-rw-r--r--crates/ide/src/goto_type_definition.rs7
-rw-r--r--crates/ide/src/hover.rs108
-rw-r--r--crates/ide/src/inlay_hints.rs138
-rw-r--r--crates/ide/src/lib.rs18
-rw-r--r--crates/ide/src/link_rewrite.rs4
-rw-r--r--crates/ide/src/mock_analysis.rs176
-rw-r--r--crates/ide/src/parent_module.rs46
-rw-r--r--crates/ide/src/references.rs355
-rw-r--r--crates/ide/src/references/rename.rs42
-rw-r--r--crates/ide/src/runnables.rs44
-rw-r--r--crates/ide/src/status.rs52
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs16
-rw-r--r--crates/ide/src/syntax_tree.rs16
-rw-r--r--crates/ide/src/typing/on_enter.rs4
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 {
139mod tests { 139mod 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
288pub fn callee() {} 288pub 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
326pub fn callee() {} 326pub 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)]
134mod tests { 134mod 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
499use std::future::*; 499use std::future::*;
500struct A {} 500struct A {}
501impl Future for A {} 501impl Future for A {}
502fn foo(a: A) { a.<|> } 502fn foo(a: A) { a.<|> }
503 503
504//- /std/lib.rs 504//- /std/lib.rs crate:std
505pub mod future { 505pub 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
518use std::future::*; 518use std::future::*;
519fn foo() { 519fn 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
525pub mod future { 525pub 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
3mod format_like;
4
2use assists::utils::TryEnum; 5use assists::utils::TryEnum;
3use syntax::{ 6use syntax::{
4 ast::{self, AstNode}, 7 ast::{self, AstNode, AstToken},
5 TextRange, TextSize, 8 TextRange, TextSize,
6}; 9};
7use text_edit::TextEdit; 10use text_edit::TextEdit;
8 11
12use self::format_like::add_format_like_completions;
9use crate::{ 13use 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
212fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { 222fn 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
17use crate::completion::{
18 complete_postfix::postfix_snippet, completion_config::SnippetCap,
19 completion_context::CompletionContext, completion_item::Completions,
20};
21use syntax::ast::{self, AstToken};
22
23/// Mapping ("postfix completion item" => "macro to use")
24static 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
35pub(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.
60fn 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)]
72pub 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)]
81enum State {
82 NotExpr,
83 MaybeExpr,
84 Expr,
85 MaybeIncorrect,
86 FormatOpts,
87}
88
89impl 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)]
205mod 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
426use foo::<|>; 426use foo::<|>;
427 427
428//- /foo/lib.rs 428//- /foo/lib.rs crate:foo
429pub mod bar { pub struct S; } 429pub 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#"
276fn main() {
277 _<|>
278}
279fn _alpha() {}
280"#,
281 r#"
282fn main() {
283 _alpha()$0
284}
285fn _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
275use <|>; 295use <|>;
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
354fn foo() { let x: <|> } 374fn foo() { let x: <|> }
355 375
356//- /std/lib.rs 376//- /std/lib.rs crate:std
357#[prelude_import] 377#[prelude_import]
358use prelude::*; 378use 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
375fn foo() { let x: <|> } 395fn foo() { let x: <|> }
376 396
377//- /core/lib.rs 397//- /core/lib.rs crate:core
378#[prelude_import] 398#[prelude_import]
379use prelude::*; 399use prelude::*;
380 400
381mod prelude { struct Option; } 401mod prelude { struct Option; }
382 402
383//- /std/lib.rs 403//- /std/lib.rs crate:std deps:core
384#[prelude_import] 404#[prelude_import]
385use prelude::*; 405use 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
1176use foo::<|>; 1176use foo::<|>;
1177//- /foo/lib.rs 1177//- /foo/lib.rs crate:foo
1178#[macro_export] 1178#[macro_export]
1179macro_rules frobnicate { () => () } 1179macro_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
9use crate::{ 9use 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
15pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 14pub(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
96pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { 95pub(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
308use core::result::Result::{self, Ok, Err}; 303use core::result::Result::{self, Ok, Err};
309 304
310fn div(x: i32, y: i32) -> Result<i32, ()> { 305fn 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
317pub mod result { 312pub 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
339use core::result::Result::{self, Ok, Err}; 334use core::result::Result::{self, Ok, Err};
340 335
341fn div<T>(x: T) -> Result<T, i32> { 336fn 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
348pub mod result { 343pub 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
370use core::result::Result::{self, Ok, Err}; 365use core::result::Result::{self, Ok, Err};
371 366
372type MyResult<T> = Result<T, ()>; 367type 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
381pub mod result { 376pub 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
405use core::result::Result::{self, Ok, Err}; 400use core::result::Result::{self, Ok, Err};
406 401
407fn foo() -> Result<(), i32> { 0 } 402fn foo() -> Result<(), i32> { 0 }
408 403
409//- /core/lib.rs 404//- /core/lib.rs crate:core
410pub mod result { 405pub 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
422use core::result::Result::{self, Ok, Err}; 417use core::result::Result::{self, Ok, Err};
423 418
424enum SomeOtherEnum { Ok(i32), Err(String) } 419enum SomeOtherEnum { Ok(i32), Err(String) }
425 420
426fn foo() -> SomeOtherEnum { 0 } 421fn foo() -> SomeOtherEnum { 0 }
427 422
428//- /core/lib.rs 423//- /core/lib.rs crate:core
429pub mod result { 424pub 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) ->
423mod tests { 423mod 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#"
432enum FooInner { } 432enum FooInner { }
433fn foo() { enum FooInner { } } 433fn 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#"
483fn foo() {} 483fn foo() {}
484struct Foo; 484struct 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 {
122mod tests { 122mod 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)]
317mod tests { 317mod 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.
2use base_db::fixture::ChangeFixture;
3use test_utils::{extract_annotations, RangeOrOffset};
4
5use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
6
7/// Creates analysis for a single file.
8pub(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.
16pub(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 <|>.
24pub(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 <|>.
37pub(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 <|>.
50pub(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)]
27mod tests { 27mod 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
346use foo as bar<|>; 345use 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
360use foo::foo as bar<|>; 359use foo::foo as bar<|>;
361 360
362//- /foo/lib.rs 361//- /foo/lib.rs crate:foo
363#[macro_export] 362#[macro_export]
364macro_rules! foo { () => { () } } 363macro_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
375struct Foo; 373struct Foo;
376impl Foo { 374impl 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(
76mod tests { 76mod 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> {
56mod tests { 56mod 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
1293extern crate st<|>d; 1293extern 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
1311extern crate std as ab<|>c; 1311extern 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
217fn should_not_display_type_hint( 217fn 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#"
502fn 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#"
930fn 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
939pub struct Vec<T> {}
940
941impl<T> Vec<T> {
942 pub fn new() -> Self { Vec {} }
943 pub fn push(&mut self, t: T) {}
944}
945
946impl<T> IntoIterator for Vec<T> {
947 type Item=T;
948}
949
950fn 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::*;
961mod iter {
962 trait IntoIterator {
963 type Item;
964 }
965}
966//- /alloc.rs crate:alloc deps:core
967mod 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
986pub struct Vec<T> {}
987
988impl<T> Vec<T> {
989 pub fn new() -> Self { Vec {} }
990 pub fn push(&mut self, t: T) {}
991}
992
993impl<T> IntoIterator for Vec<T> {
994 type Item=T;
995}
996
997fn 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::*;
1010mod iter {
1011 trait IntoIterator {
1012 type Item;
1013 }
1014}
1015//- /alloc.rs crate:alloc deps:core
1016mod 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
18pub mod mock_analysis; 18#[cfg(test)]
19mod fixture;
19 20
20mod markup; 21mod markup;
21mod prime_caches; 22mod 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};
88pub use base_db::{ 89pub 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};
92pub use hir::{Documentation, Semantics}; 93pub use hir::{Documentation, Semantics};
93pub use ide_db::{ 94pub 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`).
122fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<String> { 122fn 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
196static TYPES: ([&str; 7], [&str; 0]) = 196static 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
2use std::sync::Arc;
3
4use base_db::{CrateName, FileSet, SourceRoot, VfsPath};
5use cfg::CfgOptions;
6use test_utils::{
7 extract_annotations, extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER,
8};
9
10use 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)]
17pub struct MockAnalysis {
18 files: Vec<Fixture>,
19}
20
21impl 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 <|>.
155pub 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.
161pub 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 <|>.
168pub 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)]
65mod tests { 65mod 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
130mod foo; 124mod 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)]
192mod tests { 192mod 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#"
202struct Foo <|>{ 203struct 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#"
225struct Foo<|> {} 226struct 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#"
243struct Foo<T> <|>{} 245struct 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#"
261struct Foo<|>(i32); 263struct 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#"
280fn main() { 282fn 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#"
308fn foo() { 308fn 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#"
329fn foo(i : u32) -> u32 { 330fn 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#"
341fn foo(i<|> : u32) -> u32 { 344fn 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
354struct Foo { 359struct 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#"
374struct Foo; 379struct Foo;
375impl Foo { 380impl 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#"
387enum Foo { 395enum 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
402pub mod foo; 413pub 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
448mod foo<|>; 459mod 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
471mod foo; 485mod 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]
536macro_rules! m1<|> { () => (()) } 552macro_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#"
555fn foo() { 572fn 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#"
572struct S { 590struct 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#"
593fn foo() { 612fn 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#"
606mod foo { 629mod 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; 654mod foo { mod bar; }
634 }
635 655
636 fn f<|>() {} 656fn f<|>() {}
637 657
638 //- /foo/bar.rs 658//- /foo/bar.rs
639 use crate::f; 659use crate::f;
640 660
641 fn g() { 661fn 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 {
292mod tests { 292mod 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
3use base_db::{ 3use base_db::{
4 salsa::debug::{DebugQueryTable, TableEntry}, 4 salsa::debug::{DebugQueryTable, TableEntry},
5 FileTextQuery, SourceRootId, 5 CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId,
6}; 6};
7use hir::MacroFile; 7use hir::MacroFile;
8use ide_db::{ 8use ide_db::{
9 symbol_index::{LibrarySymbolsQuery, SymbolIndex}, 9 symbol_index::{LibrarySymbolsQuery, SymbolIndex},
10 RootDatabase, 10 RootDatabase,
11}; 11};
12use itertools::Itertools;
12use profile::{memory_usage, Bytes}; 13use profile::{memory_usage, Bytes};
13use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
15use stdx::format_to;
14use syntax::{ast, Parse, SyntaxNode}; 16use syntax::{ast, Parse, SyntaxNode};
15 17
16use crate::FileId;
17
18fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { 18fn 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// |===
34pub(crate) fn status(db: &RootDatabase) -> String { 34pub(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
122impl fmt::Display for LibrarySymbolsStats { 138impl 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;
3use expect_test::{expect_file, ExpectFile}; 3use expect_test::{expect_file, ExpectFile};
4use test_utils::project_dir; 4use test_utils::project_dir;
5 5
6use crate::{mock_analysis::single_file, FileRange, TextRange}; 6use crate::{fixture, FileRange, TextRange};
7 7
8#[test] 8#[test]
9fn test_highlighting() { 9fn 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]
189fn test_ranges() { 189fn 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)]
193struct Foo { 193struct Foo {
@@ -228,7 +228,7 @@ fn main() {
228 228
229#[test] 229#[test]
230fn ranges_sorted() { 230fn 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")]
234macro_rules! test {} 234macro_rules! test {}
@@ -462,12 +462,12 @@ macro_rules! noop {
462fn test_extern_crate() { 462fn 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.
481fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) { 481fn 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
104mod tests { 104mod 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#"
137fn test() { 137fn 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<|>#"
316fn foo() { 316fn 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();