aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/expect/src/lib.rs97
-rw-r--r--crates/flycheck/src/lib.rs1
-rw-r--r--crates/ra_assists/src/assist_context.rs8
-rw-r--r--crates/ra_assists/src/ast_transform.rs16
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs2
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs6
-rw-r--r--crates/ra_assists/src/handlers/add_new.rs2
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs12
-rw-r--r--crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs10
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs8
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs24
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs2
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs4
-rw-r--r--crates/ra_db/src/fixture.rs8
-rw-r--r--crates/ra_db/src/input.rs41
-rw-r--r--crates/ra_hir/src/code_model.rs22
-rw-r--r--crates/ra_hir/src/db.rs8
-rw-r--r--crates/ra_hir/src/semantics.rs269
-rw-r--r--crates/ra_hir_def/src/item_tree/lower.rs2
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs2
-rw-r--r--crates/ra_hir_expand/src/diagnostics.rs2
-rw-r--r--crates/ra_hir_expand/src/name.rs2
-rw-r--r--crates/ra_hir_ty/src/db.rs13
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs10
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs203
-rw-r--r--crates/ra_hir_ty/src/tests.rs24
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs10
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs40
-rw-r--r--crates/ra_ide/src/completion.rs4
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs2
-rw-r--r--crates/ra_ide/src/diagnostics.rs42
-rw-r--r--crates/ra_ide/src/display/structure.rs428
-rw-r--r--crates/ra_ide/src/expand_macro.rs241
-rw-r--r--crates/ra_ide/src/folding_ranges.rs161
-rw-r--r--crates/ra_ide/src/goto_implementation.rs4
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs14
-rw-r--r--crates/ra_ide/src/hover.rs72
-rw-r--r--crates/ra_ide/src/inlay_hints.rs174
-rw-r--r--crates/ra_ide/src/lib.rs6
-rw-r--r--crates/ra_ide/src/mock_analysis.rs2
-rw-r--r--crates/ra_ide/src/references.rs9
-rw-r--r--crates/ra_ide/src/references/rename.rs44
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs22
-rw-r--r--crates/ra_ide/test_data/highlight_doctest.html (renamed from crates/ra_ide/src/snapshots/highlight_doctest.html)0
-rw-r--r--crates/ra_ide/test_data/highlight_injection.html (renamed from crates/ra_ide/src/snapshots/highlight_injection.html)0
-rw-r--r--crates/ra_ide/test_data/highlight_strings.html (renamed from crates/ra_ide/src/snapshots/highlight_strings.html)0
-rw-r--r--crates/ra_ide/test_data/highlight_unsafe.html (renamed from crates/ra_ide/src/snapshots/highlight_unsafe.html)0
-rw-r--r--crates/ra_ide/test_data/highlighting.html (renamed from crates/ra_ide/src/snapshots/highlighting.html)0
-rw-r--r--crates/ra_ide/test_data/rainbow_highlighting.html (renamed from crates/ra_ide/src/snapshots/rainbow_highlighting.html)0
-rw-r--r--crates/ra_ide_db/src/change.rs5
-rw-r--r--crates/ra_ide_db/src/imports_locator.rs88
-rw-r--r--crates/ra_ide_db/src/lib.rs8
-rw-r--r--crates/ra_ide_db/src/search.rs12
-rw-r--r--crates/ra_proc_macro_srv/src/tests/mod.rs6
-rw-r--r--crates/ra_proc_macro_srv/src/tests/utils.rs4
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs17
-rw-r--r--crates/ra_project_model/src/lib.rs13
-rw-r--r--crates/ra_project_model/src/project_json.rs23
-rw-r--r--crates/ra_ssr/src/lib.rs162
-rw-r--r--crates/ra_ssr/src/matching.rs10
-rw-r--r--crates/ra_ssr/src/replacing.rs6
-rw-r--r--crates/ra_ssr/src/tests.rs189
-rw-r--r--crates/ra_syntax/src/tests.rs100
-rw-r--r--crates/rust-analyzer/Cargo.toml1
-rw-r--r--crates/rust-analyzer/src/bin/args.rs49
-rw-r--r--crates/rust-analyzer/src/bin/main.rs5
-rw-r--r--crates/rust-analyzer/src/cli.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs31
-rw-r--r--crates/rust-analyzer/src/cli/ssr.rs40
-rw-r--r--crates/rust-analyzer/src/config.rs2
-rw-r--r--crates/rust-analyzer/src/global_state.rs2
-rw-r--r--crates/rust-analyzer/src/handlers.rs6
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs6
-rw-r--r--crates/rust-analyzer/src/main_loop.rs42
-rw-r--r--crates/rust-analyzer/src/reload.rs5
-rw-r--r--crates/rust-analyzer/src/to_proto.rs31
-rw-r--r--crates/test_utils/src/lib.rs133
-rw-r--r--crates/vfs/src/file_set.rs2
79 files changed, 1786 insertions, 1291 deletions
diff --git a/crates/expect/src/lib.rs b/crates/expect/src/lib.rs
index dd7b96aab..a5e26fade 100644
--- a/crates/expect/src/lib.rs
+++ b/crates/expect/src/lib.rs
@@ -2,7 +2,7 @@
2//! https://github.com/rust-analyzer/rust-analyzer/pull/5101 2//! https://github.com/rust-analyzer/rust-analyzer/pull/5101
3use std::{ 3use std::{
4 collections::HashMap, 4 collections::HashMap,
5 env, fmt, fs, 5 env, fmt, fs, mem,
6 ops::Range, 6 ops::Range,
7 panic, 7 panic,
8 path::{Path, PathBuf}, 8 path::{Path, PathBuf},
@@ -14,7 +14,7 @@ use once_cell::sync::Lazy;
14use stdx::{lines_with_ends, trim_indent}; 14use stdx::{lines_with_ends, trim_indent};
15 15
16const HELP: &str = " 16const HELP: &str = "
17You can update all `expect![[]]` tests by: 17You can update all `expect![[]]` tests by running:
18 18
19 env UPDATE_EXPECT=1 cargo test 19 env UPDATE_EXPECT=1 cargo test
20 20
@@ -25,24 +25,48 @@ fn update_expect() -> bool {
25 env::var("UPDATE_EXPECT").is_ok() 25 env::var("UPDATE_EXPECT").is_ok()
26} 26}
27 27
28/// expect![[""]] 28/// expect![[r#"inline snapshot"#]]
29#[macro_export] 29#[macro_export]
30macro_rules! expect { 30macro_rules! expect {
31 [[$lit:literal]] => {$crate::Expect { 31 [[$data:literal]] => {$crate::Expect {
32 file: file!(), 32 position: $crate::Position {
33 line: line!(), 33 file: file!(),
34 column: column!(), 34 line: line!(),
35 data: $lit, 35 column: column!(),
36 },
37 data: $data,
36 }}; 38 }};
37 [[]] => { $crate::expect![[""]] }; 39 [[]] => { $crate::expect![[""]] };
38} 40}
39 41
42/// expect_file!["/crates/foo/test_data/bar.html"]
43#[macro_export]
44macro_rules! expect_file {
45 [$path:literal] => {$crate::ExpectFile { path: $path }};
46}
47
40#[derive(Debug)] 48#[derive(Debug)]
41pub struct Expect { 49pub struct Expect {
50 pub position: Position,
51 pub data: &'static str,
52}
53
54#[derive(Debug)]
55pub struct ExpectFile {
56 pub path: &'static str,
57}
58
59#[derive(Debug)]
60pub struct Position {
42 pub file: &'static str, 61 pub file: &'static str,
43 pub line: u32, 62 pub line: u32,
44 pub column: u32, 63 pub column: u32,
45 pub data: &'static str, 64}
65
66impl fmt::Display for Position {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 write!(f, "{}:{}:{}", self.file, self.line, self.column)
69 }
46} 70}
47 71
48impl Expect { 72impl Expect {
@@ -51,7 +75,7 @@ impl Expect {
51 if &trimmed == actual { 75 if &trimmed == actual {
52 return; 76 return;
53 } 77 }
54 Runtime::fail(self, &trimmed, actual); 78 Runtime::fail_expect(self, &trimmed, actual);
55 } 79 }
56 pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) { 80 pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
57 let actual = format!("{:#?}\n", actual); 81 let actual = format!("{:#?}\n", actual);
@@ -69,7 +93,7 @@ impl Expect {
69 let mut target_line = None; 93 let mut target_line = None;
70 let mut line_start = 0; 94 let mut line_start = 0;
71 for (i, line) in lines_with_ends(file).enumerate() { 95 for (i, line) in lines_with_ends(file).enumerate() {
72 if i == self.line as usize - 1 { 96 if i == self.position.line as usize - 1 {
73 let pat = "expect![["; 97 let pat = "expect![[";
74 let offset = line.find(pat).unwrap(); 98 let offset = line.find(pat).unwrap();
75 let literal_start = line_start + offset + pat.len(); 99 let literal_start = line_start + offset + pat.len();
@@ -87,6 +111,25 @@ impl Expect {
87 } 111 }
88} 112}
89 113
114impl ExpectFile {
115 pub fn assert_eq(&self, actual: &str) {
116 let expected = self.read();
117 if actual == expected {
118 return;
119 }
120 Runtime::fail_file(self, &expected, actual);
121 }
122 fn read(&self) -> String {
123 fs::read_to_string(self.abs_path()).unwrap_or_default().replace("\r\n", "\n")
124 }
125 fn write(&self, contents: &str) {
126 fs::write(self.abs_path(), contents).unwrap()
127 }
128 fn abs_path(&self) -> PathBuf {
129 workspace_root().join(self.path)
130 }
131}
132
90#[derive(Default)] 133#[derive(Default)]
91struct Runtime { 134struct Runtime {
92 help_printed: bool, 135 help_printed: bool,
@@ -95,27 +138,39 @@ struct Runtime {
95static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default); 138static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default);
96 139
97impl Runtime { 140impl Runtime {
98 fn fail(expect: &Expect, expected: &str, actual: &str) { 141 fn fail_expect(expect: &Expect, expected: &str, actual: &str) {
99 let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); 142 let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
100 let mut updated = "";
101 if update_expect() { 143 if update_expect() {
102 updated = " (updated)"; 144 println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.position);
103 rt.per_file 145 rt.per_file
104 .entry(expect.file) 146 .entry(expect.position.file)
105 .or_insert_with(|| FileRuntime::new(expect)) 147 .or_insert_with(|| FileRuntime::new(expect))
106 .update(expect, actual); 148 .update(expect, actual);
149 return;
107 } 150 }
108 let print_help = !rt.help_printed && !update_expect(); 151 rt.panic(expect.position.to_string(), expected, actual);
109 rt.help_printed = true; 152 }
153
154 fn fail_file(expect: &ExpectFile, expected: &str, actual: &str) {
155 let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
156 if update_expect() {
157 println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.path);
158 expect.write(actual);
159 return;
160 }
161 rt.panic(expect.path.to_string(), expected, actual);
162 }
110 163
164 fn panic(&mut self, position: String, expected: &str, actual: &str) {
165 let print_help = !mem::replace(&mut self.help_printed, true);
111 let help = if print_help { HELP } else { "" }; 166 let help = if print_help { HELP } else { "" };
112 167
113 let diff = Changeset::new(actual, expected, "\n"); 168 let diff = Changeset::new(actual, expected, "\n");
114 169
115 println!( 170 println!(
116 "\n 171 "\n
117\x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m{} 172\x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m
118 \x1b[1m\x1b[34m-->\x1b[0m {}:{}:{} 173 \x1b[1m\x1b[34m-->\x1b[0m {}
119{} 174{}
120\x1b[1mExpect\x1b[0m: 175\x1b[1mExpect\x1b[0m:
121---- 176----
@@ -132,7 +187,7 @@ impl Runtime {
132{} 187{}
133---- 188----
134", 189",
135 updated, expect.file, expect.line, expect.column, help, expected, actual, diff 190 position, help, expected, actual, diff
136 ); 191 );
137 // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise. 192 // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise.
138 panic::resume_unwind(Box::new(())); 193 panic::resume_unwind(Box::new(()));
@@ -147,7 +202,7 @@ struct FileRuntime {
147 202
148impl FileRuntime { 203impl FileRuntime {
149 fn new(expect: &Expect) -> FileRuntime { 204 fn new(expect: &Expect) -> FileRuntime {
150 let path = workspace_root().join(expect.file); 205 let path = workspace_root().join(expect.position.file);
151 let original_text = fs::read_to_string(&path).unwrap(); 206 let original_text = fs::read_to_string(&path).unwrap();
152 let patchwork = Patchwork::new(original_text.clone()); 207 let patchwork = Patchwork::new(original_text.clone());
153 FileRuntime { path, original_text, patchwork } 208 FileRuntime { path, original_text, patchwork }
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 1023d3040..844b093d4 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -132,6 +132,7 @@ impl FlycheckActor {
132 self.cancel_check_process(); 132 self.cancel_check_process();
133 133
134 let mut command = self.check_command(); 134 let mut command = self.check_command();
135 log::info!("restart flycheck {:?}", command);
135 command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); 136 command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
136 if let Ok(child) = command.spawn().map(JodChild) { 137 if let Ok(child) = command.spawn().map(JodChild) {
137 self.cargo_handle = Some(CargoHandle::spawn(child)); 138 self.cargo_handle = Some(CargoHandle::spawn(child));
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index ee614de72..3640bb4d2 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -55,7 +55,6 @@ use crate::{
55pub(crate) struct AssistContext<'a> { 55pub(crate) struct AssistContext<'a> {
56 pub(crate) config: &'a AssistConfig, 56 pub(crate) config: &'a AssistConfig,
57 pub(crate) sema: Semantics<'a, RootDatabase>, 57 pub(crate) sema: Semantics<'a, RootDatabase>,
58 pub(crate) db: &'a RootDatabase,
59 pub(crate) frange: FileRange, 58 pub(crate) frange: FileRange,
60 source_file: SourceFile, 59 source_file: SourceFile,
61} 60}
@@ -67,8 +66,11 @@ impl<'a> AssistContext<'a> {
67 frange: FileRange, 66 frange: FileRange,
68 ) -> AssistContext<'a> { 67 ) -> AssistContext<'a> {
69 let source_file = sema.parse(frange.file_id); 68 let source_file = sema.parse(frange.file_id);
70 let db = sema.db; 69 AssistContext { config, sema, frange, source_file }
71 AssistContext { config, sema, db, frange, source_file } 70 }
71
72 pub(crate) fn db(&self) -> &RootDatabase {
73 self.sema.db
72 } 74 }
73 75
74 // NB, this ignores active selection. 76 // NB, this ignores active selection.
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 00fa95b6c..01adb834c 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -2,7 +2,6 @@
2use rustc_hash::FxHashMap; 2use rustc_hash::FxHashMap;
3 3
4use hir::{HirDisplay, PathResolution, SemanticsScope}; 4use hir::{HirDisplay, PathResolution, SemanticsScope};
5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 5use ra_syntax::{
7 algo::SyntaxRewriter, 6 algo::SyntaxRewriter,
8 ast::{self, AstNode}, 7 ast::{self, AstNode},
@@ -32,14 +31,14 @@ impl<'a> AstTransform<'a> for NullTransformer {
32} 31}
33 32
34pub struct SubstituteTypeParams<'a> { 33pub struct SubstituteTypeParams<'a> {
35 source_scope: &'a SemanticsScope<'a, RootDatabase>, 34 source_scope: &'a SemanticsScope<'a>,
36 substs: FxHashMap<hir::TypeParam, ast::TypeRef>, 35 substs: FxHashMap<hir::TypeParam, ast::TypeRef>,
37 previous: Box<dyn AstTransform<'a> + 'a>, 36 previous: Box<dyn AstTransform<'a> + 'a>,
38} 37}
39 38
40impl<'a> SubstituteTypeParams<'a> { 39impl<'a> SubstituteTypeParams<'a> {
41 pub fn for_trait_impl( 40 pub fn for_trait_impl(
42 source_scope: &'a SemanticsScope<'a, RootDatabase>, 41 source_scope: &'a SemanticsScope<'a>,
43 // FIXME: there's implicit invariant that `trait_` and `source_scope` match... 42 // FIXME: there's implicit invariant that `trait_` and `source_scope` match...
44 trait_: hir::Trait, 43 trait_: hir::Trait,
45 impl_def: ast::ImplDef, 44 impl_def: ast::ImplDef,
@@ -126,16 +125,13 @@ impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
126} 125}
127 126
128pub struct QualifyPaths<'a> { 127pub struct QualifyPaths<'a> {
129 target_scope: &'a SemanticsScope<'a, RootDatabase>, 128 target_scope: &'a SemanticsScope<'a>,
130 source_scope: &'a SemanticsScope<'a, RootDatabase>, 129 source_scope: &'a SemanticsScope<'a>,
131 previous: Box<dyn AstTransform<'a> + 'a>, 130 previous: Box<dyn AstTransform<'a> + 'a>,
132} 131}
133 132
134impl<'a> QualifyPaths<'a> { 133impl<'a> QualifyPaths<'a> {
135 pub fn new( 134 pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self {
136 target_scope: &'a SemanticsScope<'a, RootDatabase>,
137 source_scope: &'a SemanticsScope<'a, RootDatabase>,
138 ) -> Self {
139 Self { target_scope, source_scope, previous: Box::new(NullTransformer) } 135 Self { target_scope, source_scope, previous: Box::new(NullTransformer) }
140 } 136 }
141 137
@@ -156,7 +152,7 @@ impl<'a> QualifyPaths<'a> {
156 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; 152 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
157 match resolution { 153 match resolution {
158 PathResolution::Def(def) => { 154 PathResolution::Def(def) => {
159 let found_path = from.find_use_path(self.source_scope.db, def)?; 155 let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?;
160 let mut path = path_to_ast(found_path); 156 let mut path = path_to_ast(found_path);
161 157
162 let type_args = p 158 let type_args = p
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index 90b06a625..11df922a2 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -57,7 +57,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
57 return None; 57 return None;
58 } 58 }
59 59
60 let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; 60 let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
61 acc.add( 61 acc.add(
62 AssistId("add_explicit_type"), 62 AssistId("add_explicit_type"),
63 format!("Insert explicit type `{}`", inferred_type), 63 format!("Insert explicit type `{}`", inferred_type),
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index 1cfbd75aa..fc4e82309 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/add_function.rs
@@ -117,7 +117,7 @@ impl FunctionBuilder {
117 let mut file = ctx.frange.file_id; 117 let mut file = ctx.frange.file_id;
118 let target = match &target_module { 118 let target = match &target_module {
119 Some(target_module) => { 119 Some(target_module) => {
120 let module_source = target_module.definition_source(ctx.db); 120 let module_source = target_module.definition_source(ctx.db());
121 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; 121 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?;
122 file = in_file; 122 file = in_file;
123 target 123 target
@@ -269,7 +269,7 @@ fn fn_arg_type(
269 return None; 269 return None;
270 } 270 }
271 271
272 if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) { 272 if let Ok(rendered) = ty.display_source_code(ctx.db(), target_module.into()) {
273 Some(rendered) 273 Some(rendered)
274 } else { 274 } else {
275 None 275 None
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index abacd4065..77e092f62 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -128,9 +128,9 @@ fn add_missing_impl_members_inner(
128 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def) 128 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def)
129 .iter() 129 .iter()
130 .map(|i| match i { 130 .map(|i| match i {
131 hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db).value), 131 hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db()).value),
132 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db).value), 132 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db()).value),
133 hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db).value), 133 hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db()).value),
134 }) 134 })
135 .filter(|t| def_name(&t).is_some()) 135 .filter(|t| def_name(&t).is_some())
136 .filter(|t| match t { 136 .filter(|t| match t {
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs
index 837aa8377..e41b2aa06 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/add_new.rs
@@ -122,7 +122,7 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String {
122// FIXME: change the new fn checking to a more semantic approach when that's more 122// FIXME: change the new fn checking to a more semantic approach when that's more
123// viable (e.g. we process proc macros, etc) 123// viable (e.g. we process proc macros, etc)
124fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> { 124fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> {
125 let db = ctx.db; 125 let db = ctx.db();
126 let module = strukt.syntax().ancestors().find(|node| { 126 let module = strukt.syntax().ancestors().find(|node| {
127 ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) 127 ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
128 })?; 128 })?;
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index d1cafa7d9..7b6499a08 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -5,7 +5,7 @@ use hir::{
5 AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, 5 AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
6 Type, 6 Type,
7}; 7};
8use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; 8use ra_ide_db::{imports_locator, RootDatabase};
9use ra_prof::profile; 9use ra_prof::profile;
10use ra_syntax::{ 10use ra_syntax::{
11 ast::{self, AstNode}, 11 ast::{self, AstNode},
@@ -35,8 +35,8 @@ use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, Group
35// # pub mod std { pub mod collections { pub struct HashMap { } } } 35// # pub mod std { pub mod collections { pub struct HashMap { } } }
36// ``` 36// ```
37pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 37pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38 let auto_import_assets = AutoImportAssets::new(&ctx)?; 38 let auto_import_assets = AutoImportAssets::new(ctx)?;
39 let proposed_imports = auto_import_assets.search_for_imports(ctx.db); 39 let proposed_imports = auto_import_assets.search_for_imports(ctx);
40 if proposed_imports.is_empty() { 40 if proposed_imports.is_empty() {
41 return None; 41 return None;
42 } 42 }
@@ -127,11 +127,11 @@ impl AutoImportAssets {
127 GroupLabel(name) 127 GroupLabel(name)
128 } 128 }
129 129
130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { 130 fn search_for_imports(&self, ctx: &AssistContext) -> BTreeSet<ModPath> {
131 let _p = profile("auto_import::search_for_imports"); 131 let _p = profile("auto_import::search_for_imports");
132 let db = ctx.db();
132 let current_crate = self.module_with_name_to_import.krate(); 133 let current_crate = self.module_with_name_to_import.krate();
133 ImportsLocator::new(db, current_crate) 134 imports_locator::find_imports(&ctx.sema, current_crate, &self.get_search_query())
134 .find_imports(&self.get_search_query())
135 .into_iter() 135 .into_iter()
136 .filter_map(|candidate| match &self.import_candidate { 136 .filter_map(|candidate| match &self.import_candidate {
137 ImportCandidate::TraitAssocItem(assoc_item_type, _) => { 137 ImportCandidate::TraitAssocItem(assoc_item_type, _) => {
diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
index 43b4584b4..ca19cf198 100644
--- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -37,15 +37,15 @@ pub(crate) fn extract_struct_from_enum_variant(
37 }; 37 };
38 let variant_name = variant.name()?.to_string(); 38 let variant_name = variant.name()?.to_string();
39 let variant_hir = ctx.sema.to_def(&variant)?; 39 let variant_hir = ctx.sema.to_def(&variant)?;
40 if existing_struct_def(ctx.db, &variant_name, &variant_hir) { 40 if existing_struct_def(ctx.db(), &variant_name, &variant_hir) {
41 return None; 41 return None;
42 } 42 }
43 let enum_ast = variant.parent_enum(); 43 let enum_ast = variant.parent_enum();
44 let visibility = enum_ast.visibility(); 44 let visibility = enum_ast.visibility();
45 let enum_hir = ctx.sema.to_def(&enum_ast)?; 45 let enum_hir = ctx.sema.to_def(&enum_ast)?;
46 let variant_hir_name = variant_hir.name(ctx.db); 46 let variant_hir_name = variant_hir.name(ctx.db());
47 let enum_module_def = ModuleDef::from(enum_hir); 47 let enum_module_def = ModuleDef::from(enum_hir);
48 let current_module = enum_hir.module(ctx.db); 48 let current_module = enum_hir.module(ctx.db());
49 let target = variant.syntax().text_range(); 49 let target = variant.syntax().text_range();
50 acc.add( 50 acc.add(
51 AssistId("extract_struct_from_enum_variant"), 51 AssistId("extract_struct_from_enum_variant"),
@@ -53,7 +53,7 @@ pub(crate) fn extract_struct_from_enum_variant(
53 target, 53 target,
54 |builder| { 54 |builder| {
55 let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); 55 let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir));
56 let res = definition.find_usages(&ctx.db, None); 56 let res = definition.find_usages(&ctx.sema, None);
57 let start_offset = variant.parent_enum().syntax().text_range().start(); 57 let start_offset = variant.parent_enum().syntax().text_range().start();
58 let mut visited_modules_set = FxHashSet::default(); 58 let mut visited_modules_set = FxHashSet::default();
59 visited_modules_set.insert(current_module); 59 visited_modules_set.insert(current_module);
@@ -101,7 +101,7 @@ fn insert_import(
101 enum_module_def: &ModuleDef, 101 enum_module_def: &ModuleDef,
102 variant_hir_name: &Name, 102 variant_hir_name: &Name,
103) -> Option<()> { 103) -> Option<()> {
104 let db = ctx.db; 104 let db = ctx.db();
105 let mod_path = module.find_use_path(db, enum_module_def.clone()); 105 let mod_path = module.find_use_path(db, enum_module_def.clone());
106 if let Some(mut mod_path) = mod_path { 106 if let Some(mut mod_path) = mod_path {
107 mod_path.segments.pop(); 107 mod_path.segments.pop();
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index 64270c86f..5b1235682 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -51,11 +51,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
51 let module = ctx.sema.scope(expr.syntax()).module()?; 51 let module = ctx.sema.scope(expr.syntax()).module()?;
52 52
53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { 53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
54 let variants = enum_def.variants(ctx.db); 54 let variants = enum_def.variants(ctx.db());
55 55
56 let mut variants = variants 56 let mut variants = variants
57 .into_iter() 57 .into_iter()
58 .filter_map(|variant| build_pat(ctx.db, module, variant)) 58 .filter_map(|variant| build_pat(ctx.db(), module, variant))
59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
61 .collect::<Vec<_>>(); 61 .collect::<Vec<_>>();
@@ -84,11 +84,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
84 // where each tuple represents a proposed match arm. 84 // where each tuple represents a proposed match arm.
85 enum_defs 85 enum_defs
86 .into_iter() 86 .into_iter()
87 .map(|enum_def| enum_def.variants(ctx.db)) 87 .map(|enum_def| enum_def.variants(ctx.db()))
88 .multi_cartesian_product() 88 .multi_cartesian_product()
89 .map(|variants| { 89 .map(|variants| {
90 let patterns = 90 let patterns =
91 variants.into_iter().filter_map(|variant| build_pat(ctx.db, module, variant)); 91 variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
92 ast::Pat::from(make::tuple_pat(patterns)) 92 ast::Pat::from(make::tuple_pat(patterns))
93 }) 93 })
94 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 94 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs
index 19d4dac5e..c0f57c329 100644
--- a/crates/ra_assists/src/handlers/fix_visibility.rs
+++ b/crates/ra_assists/src/handlers/fix_visibility.rs
@@ -41,14 +41,14 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
41 }; 41 };
42 42
43 let current_module = ctx.sema.scope(&path.syntax()).module()?; 43 let current_module = ctx.sema.scope(&path.syntax()).module()?;
44 let target_module = def.module(ctx.db)?; 44 let target_module = def.module(ctx.db())?;
45 45
46 let vis = target_module.visibility_of(ctx.db, &def)?; 46 let vis = target_module.visibility_of(ctx.db(), &def)?;
47 if vis.is_visible_from(ctx.db, current_module.into()) { 47 if vis.is_visible_from(ctx.db(), current_module.into()) {
48 return None; 48 return None;
49 }; 49 };
50 50
51 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?; 51 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db(), def)?;
52 52
53 let missing_visibility = 53 let missing_visibility =
54 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; 54 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
@@ -72,16 +72,16 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
72 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; 72 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
73 73
74 let current_module = ctx.sema.scope(record_field.syntax()).module()?; 74 let current_module = ctx.sema.scope(record_field.syntax()).module()?;
75 let visibility = record_field_def.visibility(ctx.db); 75 let visibility = record_field_def.visibility(ctx.db());
76 if visibility.is_visible_from(ctx.db, current_module.into()) { 76 if visibility.is_visible_from(ctx.db(), current_module.into()) {
77 return None; 77 return None;
78 } 78 }
79 79
80 let parent = record_field_def.parent_def(ctx.db); 80 let parent = record_field_def.parent_def(ctx.db());
81 let parent_name = parent.name(ctx.db); 81 let parent_name = parent.name(ctx.db());
82 let target_module = parent.module(ctx.db); 82 let target_module = parent.module(ctx.db());
83 83
84 let in_file_source = record_field_def.source(ctx.db); 84 let in_file_source = record_field_def.source(ctx.db());
85 let (offset, target) = match in_file_source.value { 85 let (offset, target) = match in_file_source.value {
86 hir::FieldSource::Named(it) => { 86 hir::FieldSource::Named(it) => {
87 let s = it.syntax(); 87 let s = it.syntax();
@@ -95,9 +95,9 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
95 95
96 let missing_visibility = 96 let missing_visibility =
97 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; 97 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
98 let target_file = in_file_source.file_id.original_file(ctx.db); 98 let target_file = in_file_source.file_id.original_file(ctx.db());
99 99
100 let target_name = record_field_def.name(ctx.db); 100 let target_name = record_field_def.name(ctx.db());
101 let assist_label = 101 let assist_label =
102 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); 102 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
103 103
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index d26e68847..259839535 100644
--- a/crates/ra_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ra_assists/src/handlers/inline_local_variable.rs
@@ -44,7 +44,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
44 44
45 let def = ctx.sema.to_def(&bind_pat)?; 45 let def = ctx.sema.to_def(&bind_pat)?;
46 let def = Definition::Local(def); 46 let def = Definition::Local(def);
47 let refs = def.find_usages(ctx.db, None); 47 let refs = def.find_usages(&ctx.sema, None);
48 if refs.is_empty() { 48 if refs.is_empty() {
49 mark::hit!(test_not_applicable_if_variable_unused); 49 mark::hit!(test_not_applicable_if_variable_unused);
50 return None; 50 return None;
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
index bc58ce5fe..b8cf30e7f 100644
--- a/crates/ra_assists/src/handlers/reorder_fields.rs
+++ b/crates/ra_assists/src/handlers/reorder_fields.rs
@@ -90,10 +90,10 @@ fn struct_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option
90fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> { 90fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
91 Some( 91 Some(
92 struct_definition(path, &ctx.sema)? 92 struct_definition(path, &ctx.sema)?
93 .fields(ctx.db) 93 .fields(ctx.db())
94 .iter() 94 .iter()
95 .enumerate() 95 .enumerate()
96 .map(|(idx, field)| (field.name(ctx.db).to_string(), idx)) 96 .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx))
97 .collect(), 97 .collect(),
98 ) 98 )
99} 99}
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index 4f4fb4494..209713987 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -149,15 +149,17 @@ fn with_files(
149 let crate_id = crate_graph.add_crate_root( 149 let crate_id = crate_graph.add_crate_root(
150 file_id, 150 file_id,
151 meta.edition, 151 meta.edition,
152 Some(CrateName::new(&krate).unwrap()), 152 Some(krate.clone()),
153 meta.cfg, 153 meta.cfg,
154 meta.env, 154 meta.env,
155 Default::default(), 155 Default::default(),
156 ); 156 );
157 let prev = crates.insert(krate.clone(), crate_id); 157 let crate_name = CrateName::new(&krate).unwrap();
158 let prev = crates.insert(crate_name.clone(), crate_id);
158 assert!(prev.is_none()); 159 assert!(prev.is_none());
159 for dep in meta.deps { 160 for dep in meta.deps {
160 crate_deps.push((krate.clone(), dep)) 161 let dep = CrateName::new(&dep).unwrap();
162 crate_deps.push((crate_name.clone(), dep))
161 } 163 }
162 } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { 164 } else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
163 assert!(default_crate_root.is_none()); 165 assert!(default_crate_root.is_none());
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 7f3660118..aaa492759 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -67,7 +67,7 @@ pub struct CrateGraph {
67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
68pub struct CrateId(pub u32); 68pub struct CrateId(pub u32);
69 69
70#[derive(Debug, Clone, PartialEq, Eq)] 70#[derive(Debug, Clone, PartialEq, Eq, Hash)]
71pub struct CrateName(SmolStr); 71pub struct CrateName(SmolStr);
72 72
73impl CrateName { 73impl CrateName {
@@ -94,6 +94,13 @@ impl fmt::Display for CrateName {
94 } 94 }
95} 95}
96 96
97impl ops::Deref for CrateName {
98 type Target = str;
99 fn deref(&self) -> &Self::Target {
100 &*self.0
101 }
102}
103
97#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 104#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
98pub struct ProcMacroId(pub u32); 105pub struct ProcMacroId(pub u32);
99 106
@@ -117,7 +124,7 @@ pub struct CrateData {
117 /// The name to display to the end user. 124 /// The name to display to the end user.
118 /// This actual crate name can be different in a particular dependent crate 125 /// This actual crate name can be different in a particular dependent crate
119 /// or may even be missing for some cases, such as a dummy crate for the code snippet. 126 /// or may even be missing for some cases, such as a dummy crate for the code snippet.
120 pub display_name: Option<CrateName>, 127 pub display_name: Option<String>,
121 pub cfg_options: CfgOptions, 128 pub cfg_options: CfgOptions,
122 pub env: Env, 129 pub env: Env,
123 pub dependencies: Vec<Dependency>, 130 pub dependencies: Vec<Dependency>,
@@ -138,7 +145,7 @@ pub struct Env {
138#[derive(Debug, Clone, PartialEq, Eq)] 145#[derive(Debug, Clone, PartialEq, Eq)]
139pub struct Dependency { 146pub struct Dependency {
140 pub crate_id: CrateId, 147 pub crate_id: CrateId,
141 pub name: SmolStr, 148 pub name: CrateName,
142} 149}
143 150
144impl CrateGraph { 151impl CrateGraph {
@@ -146,7 +153,7 @@ impl CrateGraph {
146 &mut self, 153 &mut self,
147 file_id: FileId, 154 file_id: FileId,
148 edition: Edition, 155 edition: Edition,
149 display_name: Option<CrateName>, 156 display_name: Option<String>,
150 cfg_options: CfgOptions, 157 cfg_options: CfgOptions,
151 env: Env, 158 env: Env,
152 proc_macro: Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)>, 159 proc_macro: Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)>,
@@ -178,7 +185,7 @@ impl CrateGraph {
178 if self.dfs_find(from, to, &mut FxHashSet::default()) { 185 if self.dfs_find(from, to, &mut FxHashSet::default()) {
179 return Err(CyclicDependenciesError); 186 return Err(CyclicDependenciesError);
180 } 187 }
181 self.arena.get_mut(&from).unwrap().add_dep(name.0, to); 188 self.arena.get_mut(&from).unwrap().add_dep(name, to);
182 Ok(()) 189 Ok(())
183 } 190 }
184 191
@@ -190,6 +197,23 @@ impl CrateGraph {
190 self.arena.keys().copied() 197 self.arena.keys().copied()
191 } 198 }
192 199
200 /// Returns an iterator over all transitive dependencies of the given crate.
201 pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> + '_ {
202 let mut worklist = vec![of];
203 let mut deps = FxHashSet::default();
204
205 while let Some(krate) = worklist.pop() {
206 if !deps.insert(krate) {
207 continue;
208 }
209
210 worklist.extend(self[krate].dependencies.iter().map(|dep| dep.crate_id));
211 }
212
213 deps.remove(&of);
214 deps.into_iter()
215 }
216
193 // FIXME: this only finds one crate with the given root; we could have multiple 217 // FIXME: this only finds one crate with the given root; we could have multiple
194 pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { 218 pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
195 let (&crate_id, _) = 219 let (&crate_id, _) =
@@ -247,7 +271,7 @@ impl CrateId {
247} 271}
248 272
249impl CrateData { 273impl CrateData {
250 fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { 274 fn add_dep(&mut self, name: CrateName, crate_id: CrateId) {
251 self.dependencies.push(Dependency { name, crate_id }) 275 self.dependencies.push(Dependency { name, crate_id })
252 } 276 }
253} 277}
@@ -429,7 +453,10 @@ mod tests {
429 .is_ok()); 453 .is_ok());
430 assert_eq!( 454 assert_eq!(
431 graph[crate1].dependencies, 455 graph[crate1].dependencies,
432 vec![Dependency { crate_id: crate2, name: "crate_name_with_dashes".into() }] 456 vec![Dependency {
457 crate_id: crate2,
458 name: CrateName::new("crate_name_with_dashes").unwrap()
459 }]
433 ); 460 );
434 } 461 }
435} 462}
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 479c82fa4..1b3525011 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -31,7 +31,7 @@ use hir_ty::{
31 ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, TraitEnvironment, Ty, 31 ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, TraitEnvironment, Ty,
32 TyDefId, TypeCtor, 32 TyDefId, TypeCtor,
33}; 33};
34use ra_db::{CrateId, CrateName, Edition, FileId}; 34use ra_db::{CrateId, Edition, FileId};
35use ra_prof::profile; 35use ra_prof::profile;
36use ra_syntax::ast::{self, AttrsOwner, NameOwner}; 36use ra_syntax::ast::{self, AttrsOwner, NameOwner};
37use rustc_hash::FxHashSet; 37use rustc_hash::FxHashSet;
@@ -94,8 +94,8 @@ impl Crate {
94 db.crate_graph()[self.id].edition 94 db.crate_graph()[self.id].edition
95 } 95 }
96 96
97 pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateName> { 97 pub fn display_name(self, db: &dyn HirDatabase) -> Option<String> {
98 db.crate_graph()[self.id].display_name.as_ref().cloned() 98 db.crate_graph()[self.id].display_name.clone()
99 } 99 }
100 100
101 pub fn query_external_importables( 101 pub fn query_external_importables(
@@ -1053,12 +1053,14 @@ pub struct ImplDef {
1053 1053
1054impl ImplDef { 1054impl ImplDef {
1055 pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<ImplDef> { 1055 pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<ImplDef> {
1056 let impls = db.impls_in_crate(krate.id); 1056 let inherent = db.inherent_impls_in_crate(krate.id);
1057 impls.all_impls().map(Self::from).collect() 1057 let trait_ = db.trait_impls_in_crate(krate.id);
1058
1059 inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect()
1058 } 1060 }
1059 pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<ImplDef> { 1061 pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<ImplDef> {
1060 let impls = db.impls_in_crate(krate.id); 1062 let impls = db.trait_impls_in_crate(krate.id);
1061 impls.lookup_impl_defs_for_trait(trait_.id).map(Self::from).collect() 1063 impls.for_trait(trait_.id).map(Self::from).collect()
1062 } 1064 }
1063 1065
1064 pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> { 1066 pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> {
@@ -1303,10 +1305,10 @@ impl Type {
1303 mut callback: impl FnMut(AssocItem) -> Option<T>, 1305 mut callback: impl FnMut(AssocItem) -> Option<T>,
1304 ) -> Option<T> { 1306 ) -> Option<T> {
1305 for krate in self.ty.value.def_crates(db, krate.id)? { 1307 for krate in self.ty.value.def_crates(db, krate.id)? {
1306 let impls = db.impls_in_crate(krate); 1308 let impls = db.inherent_impls_in_crate(krate);
1307 1309
1308 for impl_def in impls.lookup_impl_defs(&self.ty.value) { 1310 for impl_def in impls.for_self_ty(&self.ty.value) {
1309 for &item in db.impl_data(impl_def).items.iter() { 1311 for &item in db.impl_data(*impl_def).items.iter() {
1310 if let Some(result) = callback(item.into()) { 1312 if let Some(result) = callback(item.into()) {
1311 return Some(result); 1313 return Some(result);
1312 } 1314 }
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index bb67952de..cb48ca065 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -16,10 +16,10 @@ pub use hir_expand::db::{
16pub use hir_ty::db::{ 16pub use hir_ty::db::{
17 AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery, 17 AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery,
18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, 18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase,
19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsFromDepsQuery, 19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, InferQueryQuery,
20 ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, 20 InherentImplsInCrateQuery, InternAssocTyValueQuery, InternChalkImplQuery, InternTypeCtorQuery,
21 InternTypeCtorQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery, 21 InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery, TraitDatumQuery,
22 TraitDatumQuery, TraitSolveQuery, TyQuery, ValueTyQuery, 22 TraitImplsInCrateQuery, TraitImplsInDepsQuery, TraitSolveQuery, TyQuery, ValueTyQuery,
23}; 23};
24 24
25#[test] 25#[test]
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 6a49c424a..3d78f71c1 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -83,6 +83,11 @@ impl PathResolution {
83/// Primary API to get semantic information, like types, from syntax trees. 83/// Primary API to get semantic information, like types, from syntax trees.
84pub struct Semantics<'db, DB> { 84pub struct Semantics<'db, DB> {
85 pub db: &'db DB, 85 pub db: &'db DB,
86 imp: SemanticsImpl<'db>,
87}
88
89pub struct SemanticsImpl<'db> {
90 pub db: &'db dyn HirDatabase,
86 s2d_cache: RefCell<SourceToDefCache>, 91 s2d_cache: RefCell<SourceToDefCache>,
87 cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>, 92 cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>,
88} 93}
@@ -95,20 +100,180 @@ impl<DB> fmt::Debug for Semantics<'_, DB> {
95 100
96impl<'db, DB: HirDatabase> Semantics<'db, DB> { 101impl<'db, DB: HirDatabase> Semantics<'db, DB> {
97 pub fn new(db: &DB) -> Semantics<DB> { 102 pub fn new(db: &DB) -> Semantics<DB> {
98 Semantics { db, s2d_cache: Default::default(), cache: Default::default() } 103 let impl_ = SemanticsImpl::new(db);
104 Semantics { db, imp: impl_ }
99 } 105 }
100 106
101 pub fn parse(&self, file_id: FileId) -> ast::SourceFile { 107 pub fn parse(&self, file_id: FileId) -> ast::SourceFile {
102 let tree = self.db.parse(file_id).tree(); 108 self.imp.parse(file_id)
103 self.cache(tree.syntax().clone(), file_id.into());
104 tree
105 } 109 }
106 110
107 pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST { 111 pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST {
108 let file_id = d.source().file_id; 112 let file_id = d.source().file_id;
109 let root = self.db.parse_or_expand(file_id).unwrap(); 113 let root = self.db.parse_or_expand(file_id).unwrap();
110 self.cache(root, file_id); 114 self.imp.cache(root, file_id);
111 d.ast(self.db) 115 d.ast(self.db.upcast())
116 }
117
118 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
119 self.imp.expand(macro_call)
120 }
121
122 pub fn expand_hypothetical(
123 &self,
124 actual_macro_call: &ast::MacroCall,
125 hypothetical_args: &ast::TokenTree,
126 token_to_map: SyntaxToken,
127 ) -> Option<(SyntaxNode, SyntaxToken)> {
128 self.imp.expand_hypothetical(actual_macro_call, hypothetical_args, token_to_map)
129 }
130
131 pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
132 self.imp.descend_into_macros(token)
133 }
134
135 pub fn descend_node_at_offset<N: ast::AstNode>(
136 &self,
137 node: &SyntaxNode,
138 offset: TextSize,
139 ) -> Option<N> {
140 self.imp.descend_node_at_offset(node, offset).find_map(N::cast)
141 }
142
143 pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
144 self.imp.original_range(node)
145 }
146
147 pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
148 self.imp.diagnostics_range(diagnostics)
149 }
150
151 pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
152 self.imp.ancestors_with_macros(node)
153 }
154
155 pub fn ancestors_at_offset_with_macros(
156 &self,
157 node: &SyntaxNode,
158 offset: TextSize,
159 ) -> impl Iterator<Item = SyntaxNode> + '_ {
160 self.imp.ancestors_at_offset_with_macros(node, offset)
161 }
162
163 /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*,
164 /// search up until it is of the target AstNode type
165 pub fn find_node_at_offset_with_macros<N: AstNode>(
166 &self,
167 node: &SyntaxNode,
168 offset: TextSize,
169 ) -> Option<N> {
170 self.imp.ancestors_at_offset_with_macros(node, offset).find_map(N::cast)
171 }
172
173 /// Find a AstNode by offset inside SyntaxNode, if it is inside *MacroCall*,
174 /// descend it and find again
175 pub fn find_node_at_offset_with_descend<N: AstNode>(
176 &self,
177 node: &SyntaxNode,
178 offset: TextSize,
179 ) -> Option<N> {
180 if let Some(it) = find_node_at_offset(&node, offset) {
181 return Some(it);
182 }
183
184 self.imp.descend_node_at_offset(node, offset).find_map(N::cast)
185 }
186
187 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
188 self.imp.type_of_expr(expr)
189 }
190
191 pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
192 self.imp.type_of_pat(pat)
193 }
194
195 pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
196 self.imp.resolve_method_call(call)
197 }
198
199 pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
200 self.imp.resolve_field(field)
201 }
202
203 pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<(Field, Option<Local>)> {
204 self.imp.resolve_record_field(field)
205 }
206
207 pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<Field> {
208 self.imp.resolve_record_field_pat(field)
209 }
210
211 pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
212 self.imp.resolve_macro_call(macro_call)
213 }
214
215 pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
216 self.imp.resolve_path(path)
217 }
218
219 pub fn resolve_variant(&self, record_lit: ast::RecordLit) -> Option<VariantId> {
220 self.imp.resolve_variant(record_lit)
221 }
222
223 pub fn lower_path(&self, path: &ast::Path) -> Option<Path> {
224 self.imp.lower_path(path)
225 }
226
227 pub fn resolve_bind_pat_to_const(&self, pat: &ast::BindPat) -> Option<ModuleDef> {
228 self.imp.resolve_bind_pat_to_const(pat)
229 }
230
231 // FIXME: use this instead?
232 // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
233
234 pub fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> {
235 self.imp.record_literal_missing_fields(literal)
236 }
237
238 pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> {
239 self.imp.record_pattern_missing_fields(pattern)
240 }
241
242 pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
243 let src = self.imp.find_file(src.syntax().clone()).with_value(src).cloned();
244 T::to_def(&self.imp, src)
245 }
246
247 pub fn to_module_def(&self, file: FileId) -> Option<Module> {
248 self.imp.to_module_def(file)
249 }
250
251 pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> {
252 self.imp.scope(node)
253 }
254
255 pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> {
256 self.imp.scope_at_offset(node, offset)
257 }
258
259 pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
260 self.imp.scope_for_def(def)
261 }
262
263 pub fn assert_contains_node(&self, node: &SyntaxNode) {
264 self.imp.assert_contains_node(node)
265 }
266}
267
268impl<'db> SemanticsImpl<'db> {
269 pub fn new(db: &'db dyn HirDatabase) -> Self {
270 Self { db, s2d_cache: Default::default(), cache: Default::default() }
271 }
272
273 pub fn parse(&self, file_id: FileId) -> ast::SourceFile {
274 let tree = self.db.parse(file_id).tree();
275 self.cache(tree.syntax().clone(), file_id.into());
276 tree
112 } 277 }
113 278
114 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { 279 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
@@ -130,9 +295,15 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
130 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); 295 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call);
131 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); 296 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
132 let krate = sa.resolver.krate()?; 297 let krate = sa.resolver.krate()?;
133 let macro_call_id = macro_call 298 let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| {
134 .as_call_id(self.db, krate, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; 299 sa.resolver.resolve_path_as_macro(self.db.upcast(), &path)
135 hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) 300 })?;
301 hir_expand::db::expand_hypothetical(
302 self.db.upcast(),
303 macro_call_id,
304 hypothetical_args,
305 token_to_map,
306 )
136 } 307 }
137 308
138 pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { 309 pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
@@ -147,7 +318,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
147 return None; 318 return None;
148 } 319 }
149 let file_id = sa.expand(self.db, token.with_value(&macro_call))?; 320 let file_id = sa.expand(self.db, token.with_value(&macro_call))?;
150 let token = file_id.expansion_info(self.db)?.map_token_down(token.as_ref())?; 321 let token = file_id.expansion_info(self.db.upcast())?.map_token_down(token.as_ref())?;
151 322
152 self.cache(find_root(&token.value.parent()), token.file_id); 323 self.cache(find_root(&token.value.parent()), token.file_id);
153 324
@@ -159,15 +330,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
159 token.value 330 token.value
160 } 331 }
161 332
162 pub fn descend_node_at_offset<N: ast::AstNode>( 333 pub fn descend_node_at_offset(
163 &self, 334 &self,
164 node: &SyntaxNode, 335 node: &SyntaxNode,
165 offset: TextSize, 336 offset: TextSize,
166 ) -> Option<N> { 337 ) -> impl Iterator<Item = SyntaxNode> + '_ {
167 // Handle macro token cases 338 // Handle macro token cases
168 node.token_at_offset(offset) 339 node.token_at_offset(offset)
169 .map(|token| self.descend_into_macros(token)) 340 .map(|token| self.descend_into_macros(token))
170 .find_map(|it| self.ancestors_with_macros(it.parent()).find_map(N::cast)) 341 .map(|it| self.ancestors_with_macros(it.parent()))
342 .flatten()
171 } 343 }
172 344
173 pub fn original_range(&self, node: &SyntaxNode) -> FileRange { 345 pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
@@ -184,7 +356,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
184 356
185 pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { 357 pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
186 let node = self.find_file(node); 358 let node = self.find_file(node);
187 node.ancestors_with_macros(self.db).map(|it| it.value) 359 node.ancestors_with_macros(self.db.upcast()).map(|it| it.value)
188 } 360 }
189 361
190 pub fn ancestors_at_offset_with_macros( 362 pub fn ancestors_at_offset_with_macros(
@@ -197,29 +369,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
197 .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) 369 .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len())
198 } 370 }
199 371
200 /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*,
201 /// search up until it is of the target AstNode type
202 pub fn find_node_at_offset_with_macros<N: AstNode>(
203 &self,
204 node: &SyntaxNode,
205 offset: TextSize,
206 ) -> Option<N> {
207 self.ancestors_at_offset_with_macros(node, offset).find_map(N::cast)
208 }
209
210 /// Find a AstNode by offset inside SyntaxNode, if it is inside *MacroCall*,
211 /// descend it and find again
212 pub fn find_node_at_offset_with_descend<N: AstNode>(
213 &self,
214 node: &SyntaxNode,
215 offset: TextSize,
216 ) -> Option<N> {
217 if let Some(it) = find_node_at_offset(&node, offset) {
218 return Some(it);
219 }
220 self.descend_node_at_offset(&node, offset)
221 }
222
223 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { 372 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
224 self.analyze(expr.syntax()).type_of(self.db, &expr) 373 self.analyze(expr.syntax()).type_of(self.db, &expr)
225 } 374 }
@@ -267,9 +416,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
267 self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat) 416 self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
268 } 417 }
269 418
270 // FIXME: use this instead?
271 // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
272
273 pub fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> { 419 pub fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> {
274 self.analyze(literal.syntax()) 420 self.analyze(literal.syntax())
275 .record_literal_missing_fields(self.db, literal) 421 .record_literal_missing_fields(self.db, literal)
@@ -282,11 +428,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
282 .unwrap_or_default() 428 .unwrap_or_default()
283 } 429 }
284 430
285 pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
286 let src = self.find_file(src.syntax().clone()).with_value(src).cloned();
287 T::to_def(self, src)
288 }
289
290 fn with_ctx<F: FnOnce(&mut SourceToDefCtx) -> T, T>(&self, f: F) -> T { 431 fn with_ctx<F: FnOnce(&mut SourceToDefCtx) -> T, T>(&self, f: F) -> T {
291 let mut cache = self.s2d_cache.borrow_mut(); 432 let mut cache = self.s2d_cache.borrow_mut();
292 let mut ctx = SourceToDefCtx { db: self.db, cache: &mut *cache }; 433 let mut ctx = SourceToDefCtx { db: self.db, cache: &mut *cache };
@@ -297,20 +438,20 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
297 self.with_ctx(|ctx| ctx.file_to_def(file)).map(Module::from) 438 self.with_ctx(|ctx| ctx.file_to_def(file)).map(Module::from)
298 } 439 }
299 440
300 pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db, DB> { 441 pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> {
301 let node = self.find_file(node.clone()); 442 let node = self.find_file(node.clone());
302 let resolver = self.analyze2(node.as_ref(), None).resolver; 443 let resolver = self.analyze2(node.as_ref(), None).resolver;
303 SemanticsScope { db: self.db, resolver } 444 SemanticsScope { db: self.db, resolver }
304 } 445 }
305 446
306 pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db, DB> { 447 pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> {
307 let node = self.find_file(node.clone()); 448 let node = self.find_file(node.clone());
308 let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver; 449 let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver;
309 SemanticsScope { db: self.db, resolver } 450 SemanticsScope { db: self.db, resolver }
310 } 451 }
311 452
312 pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db, DB> { 453 pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
313 let resolver = def.id.resolver(self.db); 454 let resolver = def.id.resolver(self.db.upcast());
314 SemanticsScope { db: self.db, resolver } 455 SemanticsScope { db: self.db, resolver }
315 } 456 }
316 457
@@ -331,17 +472,17 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
331 ChildContainer::DefWithBodyId(def) => { 472 ChildContainer::DefWithBodyId(def) => {
332 return SourceAnalyzer::new_for_body(self.db, def, src, offset) 473 return SourceAnalyzer::new_for_body(self.db, def, src, offset)
333 } 474 }
334 ChildContainer::TraitId(it) => it.resolver(self.db), 475 ChildContainer::TraitId(it) => it.resolver(self.db.upcast()),
335 ChildContainer::ImplId(it) => it.resolver(self.db), 476 ChildContainer::ImplId(it) => it.resolver(self.db.upcast()),
336 ChildContainer::ModuleId(it) => it.resolver(self.db), 477 ChildContainer::ModuleId(it) => it.resolver(self.db.upcast()),
337 ChildContainer::EnumId(it) => it.resolver(self.db), 478 ChildContainer::EnumId(it) => it.resolver(self.db.upcast()),
338 ChildContainer::VariantId(it) => it.resolver(self.db), 479 ChildContainer::VariantId(it) => it.resolver(self.db.upcast()),
339 ChildContainer::GenericDefId(it) => it.resolver(self.db), 480 ChildContainer::GenericDefId(it) => it.resolver(self.db.upcast()),
340 }; 481 };
341 SourceAnalyzer::new_for_resolver(resolver, src) 482 SourceAnalyzer::new_for_resolver(resolver, src)
342 } 483 }
343 484
344 fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) { 485 pub fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) {
345 assert!(root_node.parent().is_none()); 486 assert!(root_node.parent().is_none());
346 let mut cache = self.cache.borrow_mut(); 487 let mut cache = self.cache.borrow_mut();
347 let prev = cache.insert(root_node, file_id); 488 let prev = cache.insert(root_node, file_id);
@@ -357,7 +498,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
357 cache.get(root_node).copied() 498 cache.get(root_node).copied()
358 } 499 }
359 500
360 fn find_file(&self, node: SyntaxNode) -> InFile<SyntaxNode> { 501 pub fn find_file(&self, node: SyntaxNode) -> InFile<SyntaxNode> {
361 let root_node = find_root(&node); 502 let root_node = find_root(&node);
362 let file_id = self.lookup(&root_node).unwrap_or_else(|| { 503 let file_id = self.lookup(&root_node).unwrap_or_else(|| {
363 panic!( 504 panic!(
@@ -382,14 +523,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
382pub trait ToDef: AstNode + Clone { 523pub trait ToDef: AstNode + Clone {
383 type Def; 524 type Def;
384 525
385 fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: InFile<Self>) -> Option<Self::Def>; 526 fn to_def(sema: &SemanticsImpl, src: InFile<Self>) -> Option<Self::Def>;
386} 527}
387 528
388macro_rules! to_def_impls { 529macro_rules! to_def_impls {
389 ($(($def:path, $ast:path, $meth:ident)),* ,) => {$( 530 ($(($def:path, $ast:path, $meth:ident)),* ,) => {$(
390 impl ToDef for $ast { 531 impl ToDef for $ast {
391 type Def = $def; 532 type Def = $def;
392 fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: InFile<Self>) -> Option<Self::Def> { 533 fn to_def(sema: &SemanticsImpl, src: InFile<Self>) -> Option<Self::Def> {
393 sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from) 534 sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from)
394 } 535 }
395 } 536 }
@@ -419,12 +560,12 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
419 node.ancestors().last().unwrap() 560 node.ancestors().last().unwrap()
420} 561}
421 562
422pub struct SemanticsScope<'a, DB> { 563pub struct SemanticsScope<'a> {
423 pub db: &'a DB, 564 pub db: &'a dyn HirDatabase,
424 resolver: Resolver, 565 resolver: Resolver,
425} 566}
426 567
427impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> { 568impl<'a> SemanticsScope<'a> {
428 pub fn module(&self) -> Option<Module> { 569 pub fn module(&self) -> Option<Module> {
429 Some(Module { id: self.resolver.module()? }) 570 Some(Module { id: self.resolver.module()? })
430 } 571 }
@@ -433,13 +574,13 @@ impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> {
433 // FIXME: rename to visible_traits to not repeat scope? 574 // FIXME: rename to visible_traits to not repeat scope?
434 pub fn traits_in_scope(&self) -> FxHashSet<TraitId> { 575 pub fn traits_in_scope(&self) -> FxHashSet<TraitId> {
435 let resolver = &self.resolver; 576 let resolver = &self.resolver;
436 resolver.traits_in_scope(self.db) 577 resolver.traits_in_scope(self.db.upcast())
437 } 578 }
438 579
439 pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { 580 pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
440 let resolver = &self.resolver; 581 let resolver = &self.resolver;
441 582
442 resolver.process_all_names(self.db, &mut |name, def| { 583 resolver.process_all_names(self.db.upcast(), &mut |name, def| {
443 let def = match def { 584 let def = match def {
444 resolver::ScopeDef::PerNs(it) => { 585 resolver::ScopeDef::PerNs(it) => {
445 let items = ScopeDef::all_items(it); 586 let items = ScopeDef::all_items(it);
diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs
index 5149dd141..06743d7fc 100644
--- a/crates/ra_hir_def/src/item_tree/lower.rs
+++ b/crates/ra_hir_def/src/item_tree/lower.rs
@@ -211,7 +211,7 @@ impl Ctx {
211 fn lower_record_field(&mut self, field: &ast::RecordFieldDef) -> Option<Field> { 211 fn lower_record_field(&mut self, field: &ast::RecordFieldDef) -> Option<Field> {
212 let name = field.name()?.as_name(); 212 let name = field.name()?.as_name();
213 let visibility = self.lower_visibility(field); 213 let visibility = self.lower_visibility(field);
214 let type_ref = self.lower_type_ref(&field.ascribed_type()?); 214 let type_ref = self.lower_type_ref_opt(field.ascribed_type());
215 let res = Field { name, type_ref, visibility }; 215 let res = Field { name, type_ref, visibility };
216 Some(res) 216 Some(res)
217 } 217 }
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index 26b667b55..f2d664863 100644
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -161,7 +161,7 @@ fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree {
161 // XXX 161 // XXX
162 // All crates except core itself should have a dependency on core, 162 // All crates except core itself should have a dependency on core,
163 // We detect `core` by seeing whether it doesn't have such a dependency. 163 // We detect `core` by seeing whether it doesn't have such a dependency.
164 let tt = if cg[krate].dependencies.iter().any(|dep| dep.name == "core") { 164 let tt = if cg[krate].dependencies.iter().any(|dep| &*dep.name == "core") {
165 quote! { core } 165 quote! { core }
166 } else { 166 } else {
167 quote! { crate } 167 quote! { crate }
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index 99209c6e8..545cff9bd 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -28,7 +28,7 @@ pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
28 28
29pub trait AstDiagnostic { 29pub trait AstDiagnostic {
30 type AST; 30 type AST;
31 fn ast(&self, db: &impl AstDatabase) -> Self::AST; 31 fn ast(&self, db: &dyn AstDatabase) -> Self::AST;
32} 32}
33 33
34impl dyn Diagnostic { 34impl dyn Diagnostic {
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index 1b0303685..969a2e5b8 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -117,7 +117,7 @@ impl AsName for ast::FieldKind {
117 117
118impl AsName for ra_db::Dependency { 118impl AsName for ra_db::Dependency {
119 fn as_name(&self) -> Name { 119 fn as_name(&self) -> Name {
120 Name::new_text(self.name.clone()) 120 Name::new_text(SmolStr::new(&*self.name))
121 } 121 }
122} 122}
123 123
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index cad553273..dc06c0ee7 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -11,7 +11,7 @@ use ra_db::{impl_intern_key, salsa, CrateId, Upcast};
11use ra_prof::profile; 11use ra_prof::profile;
12 12
13use crate::{ 13use crate::{
14 method_resolution::CrateImplDefs, 14 method_resolution::{InherentImpls, TraitImpls},
15 traits::{chalk, AssocTyValue, Impl}, 15 traits::{chalk, AssocTyValue, Impl},
16 Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig, 16 Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig,
17 ReturnTypeImplTraits, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId, 17 ReturnTypeImplTraits, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId,
@@ -67,11 +67,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
67 #[salsa::invoke(crate::lower::generic_defaults_query)] 67 #[salsa::invoke(crate::lower::generic_defaults_query)]
68 fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders<Ty>]>; 68 fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders<Ty>]>;
69 69
70 #[salsa::invoke(crate::method_resolution::CrateImplDefs::impls_in_crate_query)] 70 #[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]
71 fn impls_in_crate(&self, krate: CrateId) -> Arc<CrateImplDefs>; 71 fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>;
72 72
73 #[salsa::invoke(crate::method_resolution::CrateImplDefs::impls_from_deps_query)] 73 #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
74 fn impls_from_deps(&self, krate: CrateId) -> Arc<CrateImplDefs>; 74 fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
75
76 #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
77 fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>;
75 78
76 // Interned IDs for Chalk integration 79 // Interned IDs for Chalk integration
77 #[salsa::interned] 80 #[salsa::interned]
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index a59efb347..0289911de 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -32,7 +32,7 @@ impl Diagnostic for NoSuchField {
32impl AstDiagnostic for NoSuchField { 32impl AstDiagnostic for NoSuchField {
33 type AST = ast::RecordField; 33 type AST = ast::RecordField;
34 34
35 fn ast(&self, db: &impl AstDatabase) -> Self::AST { 35 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
36 let root = db.parse_or_expand(self.source().file_id).unwrap(); 36 let root = db.parse_or_expand(self.source().file_id).unwrap();
37 let node = self.source().value.to_node(&root); 37 let node = self.source().value.to_node(&root);
38 ast::RecordField::cast(node).unwrap() 38 ast::RecordField::cast(node).unwrap()
@@ -65,7 +65,7 @@ impl Diagnostic for MissingFields {
65impl AstDiagnostic for MissingFields { 65impl AstDiagnostic for MissingFields {
66 type AST = ast::RecordFieldList; 66 type AST = ast::RecordFieldList;
67 67
68 fn ast(&self, db: &impl AstDatabase) -> Self::AST { 68 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
69 let root = db.parse_or_expand(self.source().file_id).unwrap(); 69 let root = db.parse_or_expand(self.source().file_id).unwrap();
70 let node = self.source().value.to_node(&root); 70 let node = self.source().value.to_node(&root);
71 ast::RecordFieldList::cast(node).unwrap() 71 ast::RecordFieldList::cast(node).unwrap()
@@ -135,7 +135,7 @@ impl Diagnostic for MissingOkInTailExpr {
135impl AstDiagnostic for MissingOkInTailExpr { 135impl AstDiagnostic for MissingOkInTailExpr {
136 type AST = ast::Expr; 136 type AST = ast::Expr;
137 137
138 fn ast(&self, db: &impl AstDatabase) -> Self::AST { 138 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
139 let root = db.parse_or_expand(self.file).unwrap(); 139 let root = db.parse_or_expand(self.file).unwrap();
140 let node = self.source().value.to_node(&root); 140 let node = self.source().value.to_node(&root);
141 ast::Expr::cast(node).unwrap() 141 ast::Expr::cast(node).unwrap()
@@ -163,7 +163,7 @@ impl Diagnostic for BreakOutsideOfLoop {
163impl AstDiagnostic for BreakOutsideOfLoop { 163impl AstDiagnostic for BreakOutsideOfLoop {
164 type AST = ast::Expr; 164 type AST = ast::Expr;
165 165
166 fn ast(&self, db: &impl AstDatabase) -> Self::AST { 166 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
167 let root = db.parse_or_expand(self.file).unwrap(); 167 let root = db.parse_or_expand(self.file).unwrap();
168 let node = self.source().value.to_node(&root); 168 let node = self.source().value.to_node(&root);
169 ast::Expr::cast(node).unwrap() 169 ast::Expr::cast(node).unwrap()
@@ -191,7 +191,7 @@ impl Diagnostic for MissingUnsafe {
191impl AstDiagnostic for MissingUnsafe { 191impl AstDiagnostic for MissingUnsafe {
192 type AST = ast::Expr; 192 type AST = ast::Expr;
193 193
194 fn ast(&self, db: &impl AstDatabase) -> Self::AST { 194 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
195 let root = db.parse_or_expand(self.source().file_id).unwrap(); 195 let root = db.parse_or_expand(self.source().file_id).unwrap();
196 let node = self.source().value.to_node(&root); 196 let node = self.source().value.to_node(&root);
197 ast::Expr::cast(node).unwrap() 197 ast::Expr::cast(node).unwrap()
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index c3edd6885..a45febbf7 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -39,136 +39,131 @@ impl TyFingerprint {
39 } 39 }
40} 40}
41 41
42/// A queryable and mergeable collection of impls. 42/// Trait impls defined or available in some crate.
43#[derive(Debug, PartialEq, Eq)] 43#[derive(Debug, Eq, PartialEq)]
44pub struct CrateImplDefs { 44pub struct TraitImpls {
45 inherent_impls: FxHashMap<TyFingerprint, Vec<ImplId>>, 45 // If the `Option<TyFingerprint>` is `None`, the impl may apply to any self type.
46 impls_by_trait: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>, 46 map: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>,
47} 47}
48 48
49impl CrateImplDefs { 49impl TraitImpls {
50 pub(crate) fn impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<CrateImplDefs> { 50 pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
51 let _p = profile("impls_in_crate_query"); 51 let _p = profile("trait_impls_in_crate_query");
52 let mut res = CrateImplDefs { 52 let mut impls = Self { map: FxHashMap::default() };
53 inherent_impls: FxHashMap::default(),
54 impls_by_trait: FxHashMap::default(),
55 };
56 res.fill(db, krate);
57 53
58 Arc::new(res) 54 let crate_def_map = db.crate_def_map(krate);
55 for (_module_id, module_data) in crate_def_map.modules.iter() {
56 for impl_id in module_data.scope.impls() {
57 let target_trait = match db.impl_trait(impl_id) {
58 Some(tr) => tr.value.trait_,
59 None => continue,
60 };
61 let self_ty = db.impl_self_ty(impl_id);
62 let self_ty_fp = TyFingerprint::for_impl(&self_ty.value);
63 impls
64 .map
65 .entry(target_trait)
66 .or_default()
67 .entry(self_ty_fp)
68 .or_default()
69 .push(impl_id);
70 }
71 }
72
73 Arc::new(impls)
59 } 74 }
60 75
61 /// Collects all impls from transitive dependencies of `krate` that may be used by `krate`. 76 pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
62 /// 77 let _p = profile("trait_impls_in_deps_query");
63 /// The full set of impls that can be used by `krate` is the returned map plus all the impls
64 /// from `krate` itself.
65 pub(crate) fn impls_from_deps_query(
66 db: &dyn HirDatabase,
67 krate: CrateId,
68 ) -> Arc<CrateImplDefs> {
69 let _p = profile("impls_from_deps_query");
70 let crate_graph = db.crate_graph(); 78 let crate_graph = db.crate_graph();
71 let mut res = CrateImplDefs { 79 let mut res = Self { map: FxHashMap::default() };
72 inherent_impls: FxHashMap::default(),
73 impls_by_trait: FxHashMap::default(),
74 };
75 80
76 // For each dependency, calculate `impls_from_deps` recursively, then add its own 81 for krate in crate_graph.transitive_deps(krate) {
77 // `impls_in_crate`. 82 res.merge(&db.trait_impls_in_crate(krate));
78 // As we might visit crates multiple times, `merge` has to deduplicate impls to avoid
79 // wasting memory.
80 for dep in &crate_graph[krate].dependencies {
81 res.merge(&db.impls_from_deps(dep.crate_id));
82 res.merge(&db.impls_in_crate(dep.crate_id));
83 } 83 }
84 84
85 Arc::new(res) 85 Arc::new(res)
86 } 86 }
87 87
88 fn fill(&mut self, db: &dyn HirDatabase, krate: CrateId) {
89 let crate_def_map = db.crate_def_map(krate);
90 for (_module_id, module_data) in crate_def_map.modules.iter() {
91 for impl_id in module_data.scope.impls() {
92 match db.impl_trait(impl_id) {
93 Some(tr) => {
94 let self_ty = db.impl_self_ty(impl_id);
95 let self_ty_fp = TyFingerprint::for_impl(&self_ty.value);
96 self.impls_by_trait
97 .entry(tr.value.trait_)
98 .or_default()
99 .entry(self_ty_fp)
100 .or_default()
101 .push(impl_id);
102 }
103 None => {
104 let self_ty = db.impl_self_ty(impl_id);
105 if let Some(self_ty_fp) = TyFingerprint::for_impl(&self_ty.value) {
106 self.inherent_impls.entry(self_ty_fp).or_default().push(impl_id);
107 }
108 }
109 }
110 }
111 }
112 }
113
114 fn merge(&mut self, other: &Self) { 88 fn merge(&mut self, other: &Self) {
115 for (fp, impls) in &other.inherent_impls { 89 for (trait_, other_map) in &other.map {
116 let vec = self.inherent_impls.entry(*fp).or_default(); 90 let map = self.map.entry(*trait_).or_default();
117 vec.extend(impls);
118 vec.sort();
119 vec.dedup();
120 }
121
122 for (trait_, other_map) in &other.impls_by_trait {
123 let map = self.impls_by_trait.entry(*trait_).or_default();
124 for (fp, impls) in other_map { 91 for (fp, impls) in other_map {
125 let vec = map.entry(*fp).or_default(); 92 let vec = map.entry(*fp).or_default();
126 vec.extend(impls); 93 vec.extend(impls);
127 vec.sort();
128 vec.dedup();
129 } 94 }
130 } 95 }
131 } 96 }
132 97
133 pub fn lookup_impl_defs(&self, ty: &Ty) -> impl Iterator<Item = ImplId> + '_ { 98 /// Queries all impls of the given trait.
134 let fingerprint = TyFingerprint::for_impl(ty); 99 pub fn for_trait(&self, trait_: TraitId) -> impl Iterator<Item = ImplId> + '_ {
135 fingerprint.and_then(|f| self.inherent_impls.get(&f)).into_iter().flatten().copied() 100 self.map
136 } 101 .get(&trait_)
137
138 pub fn lookup_impl_defs_for_trait(&self, tr: TraitId) -> impl Iterator<Item = ImplId> + '_ {
139 self.impls_by_trait
140 .get(&tr)
141 .into_iter() 102 .into_iter()
142 .flat_map(|m| m.values().flat_map(|v| v.iter().copied())) 103 .flat_map(|map| map.values().flat_map(|v| v.iter().copied()))
143 } 104 }
144 105
145 pub fn lookup_impl_defs_for_trait_and_ty( 106 /// Queries all impls of `trait_` that may apply to `self_ty`.
107 pub fn for_trait_and_self_ty(
146 &self, 108 &self,
147 tr: TraitId, 109 trait_: TraitId,
148 fp: TyFingerprint, 110 self_ty: TyFingerprint,
149 ) -> impl Iterator<Item = ImplId> + '_ { 111 ) -> impl Iterator<Item = ImplId> + '_ {
150 self.impls_by_trait 112 self.map
151 .get(&tr) 113 .get(&trait_)
152 .and_then(|m| m.get(&Some(fp)))
153 .into_iter() 114 .into_iter()
154 .flatten() 115 .flat_map(move |map| map.get(&None).into_iter().chain(map.get(&Some(self_ty))))
155 .copied() 116 .flat_map(|v| v.iter().copied())
156 .chain( 117 }
157 self.impls_by_trait 118
158 .get(&tr) 119 pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
159 .and_then(|m| m.get(&None)) 120 self.map.values().flat_map(|map| map.values().flat_map(|v| v.iter().copied()))
160 .into_iter() 121 }
161 .flatten() 122}
162 .copied(), 123
163 ) 124/// Inherent impls defined in some crate.
125///
126/// Inherent impls can only be defined in the crate that also defines the self type of the impl
127/// (note that some primitives are considered to be defined by both libcore and liballoc).
128///
129/// This makes inherent impl lookup easier than trait impl lookup since we only have to consider a
130/// single crate.
131#[derive(Debug, Eq, PartialEq)]
132pub struct InherentImpls {
133 map: FxHashMap<TyFingerprint, Vec<ImplId>>,
134}
135
136impl InherentImpls {
137 pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
138 let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default();
139
140 let crate_def_map = db.crate_def_map(krate);
141 for (_module_id, module_data) in crate_def_map.modules.iter() {
142 for impl_id in module_data.scope.impls() {
143 let data = db.impl_data(impl_id);
144 if data.target_trait.is_some() {
145 continue;
146 }
147
148 let self_ty = db.impl_self_ty(impl_id);
149 if let Some(fp) = TyFingerprint::for_impl(&self_ty.value) {
150 map.entry(fp).or_default().push(impl_id);
151 }
152 }
153 }
154
155 Arc::new(Self { map })
156 }
157
158 pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] {
159 match TyFingerprint::for_impl(self_ty) {
160 Some(fp) => self.map.get(&fp).map(|vec| vec.as_ref()).unwrap_or(&[]),
161 None => &[],
162 }
164 } 163 }
165 164
166 pub fn all_impls<'a>(&'a self) -> impl Iterator<Item = ImplId> + 'a { 165 pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
167 self.inherent_impls 166 self.map.values().flat_map(|v| v.iter().copied())
168 .values()
169 .chain(self.impls_by_trait.values().flat_map(|m| m.values()))
170 .flatten()
171 .copied()
172 } 167 }
173} 168}
174 169
@@ -525,9 +520,9 @@ fn iterate_inherent_methods(
525 None => return false, 520 None => return false,
526 }; 521 };
527 for krate in def_crates { 522 for krate in def_crates {
528 let impls = db.impls_in_crate(krate); 523 let impls = db.inherent_impls_in_crate(krate);
529 524
530 for impl_def in impls.lookup_impl_defs(&self_ty.value) { 525 for &impl_def in impls.for_self_ty(&self_ty.value) {
531 for &item in db.impl_data(impl_def).items.iter() { 526 for &item in db.impl_data(impl_def).items.iter() {
532 if !is_valid_candidate(db, name, receiver_ty, item, self_ty) { 527 if !is_valid_candidate(db, name, receiver_ty, item, self_ty) {
533 continue; 528 continue;
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 9084c3bed..eeac34d14 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -508,6 +508,30 @@ fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() {
508} 508}
509 509
510#[test] 510#[test]
511fn no_such_field_with_type_macro() {
512 let diagnostics = TestDB::with_files(
513 r"
514 macro_rules! Type {
515 () => { u32 };
516 }
517
518 struct Foo {
519 bar: Type![],
520 }
521 impl Foo {
522 fn new() -> Self {
523 Foo { bar: 0 }
524 }
525 }
526 ",
527 )
528 .diagnostics()
529 .0;
530
531 assert_snapshot!(diagnostics, @r###""###);
532}
533
534#[test]
511fn missing_record_pat_field_diagnostic() { 535fn missing_record_pat_field_diagnostic() {
512 let diagnostics = TestDB::with_files( 536 let diagnostics = TestDB::with_files(
513 r" 537 r"
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index 8ef4941c0..c97b81d57 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -77,8 +77,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
77 // Note: Since we're using impls_for_trait, only impls where the trait 77 // Note: Since we're using impls_for_trait, only impls where the trait
78 // can be resolved should ever reach Chalk. `impl_datum` relies on that 78 // can be resolved should ever reach Chalk. `impl_datum` relies on that
79 // and will panic if the trait can't be resolved. 79 // and will panic if the trait can't be resolved.
80 let in_deps = self.db.impls_from_deps(self.krate); 80 let in_deps = self.db.trait_impls_in_deps(self.krate);
81 let in_self = self.db.impls_in_crate(self.krate); 81 let in_self = self.db.trait_impls_in_crate(self.krate);
82 let impl_maps = [in_deps, in_self]; 82 let impl_maps = [in_deps, in_self];
83 83
84 let id_to_chalk = |id: hir_def::ImplId| Impl::ImplDef(id).to_chalk(self.db); 84 let id_to_chalk = |id: hir_def::ImplId| Impl::ImplDef(id).to_chalk(self.db);
@@ -87,14 +87,12 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
87 Some(fp) => impl_maps 87 Some(fp) => impl_maps
88 .iter() 88 .iter()
89 .flat_map(|crate_impl_defs| { 89 .flat_map(|crate_impl_defs| {
90 crate_impl_defs.lookup_impl_defs_for_trait_and_ty(trait_, fp).map(id_to_chalk) 90 crate_impl_defs.for_trait_and_self_ty(trait_, fp).map(id_to_chalk)
91 }) 91 })
92 .collect(), 92 .collect(),
93 None => impl_maps 93 None => impl_maps
94 .iter() 94 .iter()
95 .flat_map(|crate_impl_defs| { 95 .flat_map(|crate_impl_defs| crate_impl_defs.for_trait(trait_).map(id_to_chalk))
96 crate_impl_defs.lookup_impl_defs_for_trait(trait_).map(id_to_chalk)
97 })
98 .collect(), 96 .collect(),
99 }; 97 };
100 98
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
index 1e3a31602..884353808 100644
--- a/crates/ra_ide/src/call_hierarchy.rs
+++ b/crates/ra_ide/src/call_hierarchy.rs
@@ -39,10 +39,11 @@ pub(crate) fn call_hierarchy(
39 39
40pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { 40pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
41 let sema = Semantics::new(db); 41 let sema = Semantics::new(db);
42
42 // 1. Find all refs 43 // 1. Find all refs
43 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. 44 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply.
44 // 3. Add ranges relative to the start of the fndef. 45 // 3. Add ranges relative to the start of the fndef.
45 let refs = references::find_all_refs(db, position, None)?; 46 let refs = references::find_all_refs(&sema, position, None)?;
46 47
47 let mut calls = CallLocations::default(); 48 let mut calls = CallLocations::default();
48 49
@@ -355,4 +356,41 @@ fn caller3() {
355 &["caller3 FN_DEF FileId(1) 66..83 69..76 : [52..59]"], 356 &["caller3 FN_DEF FileId(1) 66..83 69..76 : [52..59]"],
356 ); 357 );
357 } 358 }
359
360 #[test]
361 fn test_call_hierarchy_issue_5103() {
362 check_hierarchy(
363 r#"
364fn a() {
365 b()
366}
367
368fn b() {}
369
370fn main() {
371 a<|>()
372}
373"#,
374 "a FN_DEF FileId(1) 0..18 3..4",
375 &["main FN_DEF FileId(1) 31..52 34..38 : [47..48]"],
376 &["b FN_DEF FileId(1) 20..29 23..24 : [13..14]"],
377 );
378
379 check_hierarchy(
380 r#"
381fn a() {
382 b<|>()
383}
384
385fn b() {}
386
387fn main() {
388 a()
389}
390"#,
391 "b FN_DEF FileId(1) 20..29 23..24",
392 &["a FN_DEF FileId(1) 0..18 3..4 : [13..14]"],
393 &[],
394 );
395 }
358} 396}
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index e1fcf379d..69ea754b3 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -137,8 +137,8 @@ mod tests {
137 documentation: &'a str, 137 documentation: &'a str,
138 } 138 }
139 139
140 fn check_detail_and_documentation(fixture: &str, expected: DetailAndDocumentation) { 140 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
141 let (analysis, position) = analysis_and_position(fixture); 141 let (analysis, position) = analysis_and_position(ra_fixture);
142 let config = CompletionConfig::default(); 142 let config = CompletionConfig::default();
143 let completions = analysis.completions(&config, position).unwrap().unwrap(); 143 let completions = analysis.completions(&config, position).unwrap().unwrap();
144 for item in completions { 144 for item in completions {
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 560fb19e6..ef22ea54d 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -213,7 +213,7 @@ impl<'a> CompletionContext<'a> {
213 } 213 }
214 } 214 }
215 215
216 pub(crate) fn scope(&self) -> SemanticsScope<'_, RootDatabase> { 216 pub(crate) fn scope(&self) -> SemanticsScope<'_> {
217 self.sema.scope_at_offset(&self.token.parent(), self.offset) 217 self.sema.scope_at_offset(&self.token.parent(), self.offset)
218 } 218 }
219 219
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 05fb799d6..46f8c31c7 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -324,10 +324,10 @@ mod tests {
324 /// * a diagnostic is produced 324 /// * a diagnostic is produced
325 /// * this diagnostic touches the input cursor position 325 /// * this diagnostic touches the input cursor position
326 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 326 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
327 fn check_apply_diagnostic_fix_from_position(fixture: &str, after: &str) { 327 fn check_apply_diagnostic_fix_from_position(ra_fixture: &str, after: &str) {
328 let after = trim_indent(after); 328 let after = trim_indent(after);
329 329
330 let (analysis, file_position) = analysis_and_position(fixture); 330 let (analysis, file_position) = analysis_and_position(ra_fixture);
331 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 331 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap();
332 let mut fix = diagnostic.fix.unwrap(); 332 let mut fix = diagnostic.fix.unwrap();
333 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 333 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
@@ -365,14 +365,14 @@ mod tests {
365 365
366 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 366 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
367 /// apply to the file containing the cursor. 367 /// apply to the file containing the cursor.
368 fn check_no_diagnostic_for_target_file(fixture: &str) { 368 fn check_no_diagnostic_for_target_file(ra_fixture: &str) {
369 let (analysis, file_position) = analysis_and_position(fixture); 369 let (analysis, file_position) = analysis_and_position(ra_fixture);
370 let diagnostics = analysis.diagnostics(file_position.file_id).unwrap(); 370 let diagnostics = analysis.diagnostics(file_position.file_id).unwrap();
371 assert_eq!(diagnostics.len(), 0); 371 assert_eq!(diagnostics.len(), 0);
372 } 372 }
373 373
374 fn check_no_diagnostic(content: &str) { 374 fn check_no_diagnostic(ra_fixture: &str) {
375 let (analysis, file_id) = single_file(content); 375 let (analysis, file_id) = single_file(ra_fixture);
376 let diagnostics = analysis.diagnostics(file_id).unwrap(); 376 let diagnostics = analysis.diagnostics(file_id).unwrap();
377 assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one"); 377 assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one");
378 } 378 }
@@ -473,7 +473,8 @@ mod tests {
473 473
474 #[test] 474 #[test]
475 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 475 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
476 let content = r#" 476 check_no_diagnostic_for_target_file(
477 r"
477 //- /main.rs 478 //- /main.rs
478 use core::result::Result::{self, Ok, Err}; 479 use core::result::Result::{self, Ok, Err};
479 480
@@ -485,13 +486,14 @@ mod tests {
485 pub mod result { 486 pub mod result {
486 pub enum Result<T, E> { Ok(T), Err(E) } 487 pub enum Result<T, E> { Ok(T), Err(E) }
487 } 488 }
488 "#; 489 ",
489 check_no_diagnostic_for_target_file(content); 490 );
490 } 491 }
491 492
492 #[test] 493 #[test]
493 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 494 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
494 let content = r#" 495 check_no_diagnostic_for_target_file(
496 r"
495 //- /main.rs 497 //- /main.rs
496 use core::result::Result::{self, Ok, Err}; 498 use core::result::Result::{self, Ok, Err};
497 499
@@ -508,8 +510,8 @@ mod tests {
508 pub mod result { 510 pub mod result {
509 pub enum Result<T, E> { Ok(T), Err(E) } 511 pub enum Result<T, E> { Ok(T), Err(E) }
510 } 512 }
511 "#; 513 ",
512 check_no_diagnostic_for_target_file(content); 514 );
513 } 515 }
514 516
515 #[test] 517 #[test]
@@ -618,7 +620,8 @@ mod tests {
618 620
619 #[test] 621 #[test]
620 fn test_fill_struct_fields_no_diagnostic() { 622 fn test_fill_struct_fields_no_diagnostic() {
621 let content = r" 623 check_no_diagnostic(
624 r"
622 struct TestStruct { 625 struct TestStruct {
623 one: i32, 626 one: i32,
624 two: i64, 627 two: i64,
@@ -628,14 +631,14 @@ mod tests {
628 let one = 1; 631 let one = 1;
629 let s = TestStruct{ one, two: 2 }; 632 let s = TestStruct{ one, two: 2 };
630 } 633 }
631 "; 634 ",
632 635 );
633 check_no_diagnostic(content);
634 } 636 }
635 637
636 #[test] 638 #[test]
637 fn test_fill_struct_fields_no_diagnostic_on_spread() { 639 fn test_fill_struct_fields_no_diagnostic_on_spread() {
638 let content = r" 640 check_no_diagnostic(
641 r"
639 struct TestStruct { 642 struct TestStruct {
640 one: i32, 643 one: i32,
641 two: i64, 644 two: i64,
@@ -645,9 +648,8 @@ mod tests {
645 let one = 1; 648 let one = 1;
646 let s = TestStruct{ ..a }; 649 let s = TestStruct{ ..a };
647 } 650 }
648 "; 651 ",
649 652 );
650 check_no_diagnostic(content);
651 } 653 }
652 654
653 #[test] 655 #[test]
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
index aad5a8e4d..c22a5d17b 100644
--- a/crates/ra_ide/src/display/structure.rs
+++ b/crates/ra_ide/src/display/structure.rs
@@ -173,12 +173,19 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
173 173
174#[cfg(test)] 174#[cfg(test)]
175mod tests { 175mod tests {
176 use expect::{expect, Expect};
177
176 use super::*; 178 use super::*;
177 use insta::assert_debug_snapshot; 179
180 fn check(ra_fixture: &str, expect: Expect) {
181 let file = SourceFile::parse(ra_fixture).ok().unwrap();
182 let structure = file_structure(&file);
183 expect.assert_debug_eq(&structure)
184 }
178 185
179 #[test] 186 #[test]
180 fn test_file_structure() { 187 fn test_file_structure() {
181 let file = SourceFile::parse( 188 check(
182 r#" 189 r#"
183struct Foo { 190struct Foo {
184 x: i32 191 x: i32
@@ -223,216 +230,211 @@ fn obsolete() {}
223#[deprecated(note = "for awhile")] 230#[deprecated(note = "for awhile")]
224fn very_obsolete() {} 231fn very_obsolete() {}
225"#, 232"#,
226 ) 233 expect![[r#"
227 .ok() 234 [
228 .unwrap(); 235 StructureNode {
229 let structure = file_structure(&file); 236 parent: None,
230 assert_debug_snapshot!(structure, 237 label: "Foo",
231 @r###" 238 navigation_range: 8..11,
232 [ 239 node_range: 1..26,
233 StructureNode { 240 kind: STRUCT_DEF,
234 parent: None, 241 detail: None,
235 label: "Foo", 242 deprecated: false,
236 navigation_range: 8..11, 243 },
237 node_range: 1..26, 244 StructureNode {
238 kind: STRUCT_DEF, 245 parent: Some(
239 detail: None, 246 0,
240 deprecated: false, 247 ),
241 }, 248 label: "x",
242 StructureNode { 249 navigation_range: 18..19,
243 parent: Some( 250 node_range: 18..24,
244 0, 251 kind: RECORD_FIELD_DEF,
245 ), 252 detail: Some(
246 label: "x", 253 "i32",
247 navigation_range: 18..19, 254 ),
248 node_range: 18..24, 255 deprecated: false,
249 kind: RECORD_FIELD_DEF, 256 },
250 detail: Some( 257 StructureNode {
251 "i32", 258 parent: None,
252 ), 259 label: "m",
253 deprecated: false, 260 navigation_range: 32..33,
254 }, 261 node_range: 28..158,
255 StructureNode { 262 kind: MODULE,
256 parent: None, 263 detail: None,
257 label: "m", 264 deprecated: false,
258 navigation_range: 32..33, 265 },
259 node_range: 28..158, 266 StructureNode {
260 kind: MODULE, 267 parent: Some(
261 detail: None, 268 2,
262 deprecated: false, 269 ),
263 }, 270 label: "bar1",
264 StructureNode { 271 navigation_range: 43..47,
265 parent: Some( 272 node_range: 40..52,
266 2, 273 kind: FN_DEF,
267 ), 274 detail: Some(
268 label: "bar1", 275 "fn()",
269 navigation_range: 43..47, 276 ),
270 node_range: 40..52, 277 deprecated: false,
271 kind: FN_DEF, 278 },
272 detail: Some( 279 StructureNode {
273 "fn()", 280 parent: Some(
274 ), 281 2,
275 deprecated: false, 282 ),
276 }, 283 label: "bar2",
277 StructureNode { 284 navigation_range: 60..64,
278 parent: Some( 285 node_range: 57..81,
279 2, 286 kind: FN_DEF,
280 ), 287 detail: Some(
281 label: "bar2", 288 "fn<T>(t: T) -> T",
282 navigation_range: 60..64, 289 ),
283 node_range: 57..81, 290 deprecated: false,
284 kind: FN_DEF, 291 },
285 detail: Some( 292 StructureNode {
286 "fn<T>(t: T) -> T", 293 parent: Some(
287 ), 294 2,
288 deprecated: false, 295 ),
289 }, 296 label: "bar3",
290 StructureNode { 297 navigation_range: 89..93,
291 parent: Some( 298 node_range: 86..156,
292 2, 299 kind: FN_DEF,
293 ), 300 detail: Some(
294 label: "bar3", 301 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
295 navigation_range: 89..93, 302 ),
296 node_range: 86..156, 303 deprecated: false,
297 kind: FN_DEF, 304 },
298 detail: Some( 305 StructureNode {
299 "fn<A, B>(a: A, b: B) -> Vec< u32 >", 306 parent: None,
300 ), 307 label: "E",
301 deprecated: false, 308 navigation_range: 165..166,
302 }, 309 node_range: 160..180,
303 StructureNode { 310 kind: ENUM_DEF,
304 parent: None, 311 detail: None,
305 label: "E", 312 deprecated: false,
306 navigation_range: 165..166, 313 },
307 node_range: 160..180, 314 StructureNode {
308 kind: ENUM_DEF, 315 parent: Some(
309 detail: None, 316 6,
310 deprecated: false, 317 ),
311 }, 318 label: "X",
312 StructureNode { 319 navigation_range: 169..170,
313 parent: Some( 320 node_range: 169..170,
314 6, 321 kind: ENUM_VARIANT,
315 ), 322 detail: None,
316 label: "X", 323 deprecated: false,
317 navigation_range: 169..170, 324 },
318 node_range: 169..170, 325 StructureNode {
319 kind: ENUM_VARIANT, 326 parent: Some(
320 detail: None, 327 6,
321 deprecated: false, 328 ),
322 }, 329 label: "Y",
323 StructureNode { 330 navigation_range: 172..173,
324 parent: Some( 331 node_range: 172..178,
325 6, 332 kind: ENUM_VARIANT,
326 ), 333 detail: None,
327 label: "Y", 334 deprecated: false,
328 navigation_range: 172..173, 335 },
329 node_range: 172..178, 336 StructureNode {
330 kind: ENUM_VARIANT, 337 parent: None,
331 detail: None, 338 label: "T",
332 deprecated: false, 339 navigation_range: 186..187,
333 }, 340 node_range: 181..193,
334 StructureNode { 341 kind: TYPE_ALIAS_DEF,
335 parent: None, 342 detail: Some(
336 label: "T", 343 "()",
337 navigation_range: 186..187, 344 ),
338 node_range: 181..193, 345 deprecated: false,
339 kind: TYPE_ALIAS_DEF, 346 },
340 detail: Some( 347 StructureNode {
341 "()", 348 parent: None,
342 ), 349 label: "S",
343 deprecated: false, 350 navigation_range: 201..202,
344 }, 351 node_range: 194..213,
345 StructureNode { 352 kind: STATIC_DEF,
346 parent: None, 353 detail: Some(
347 label: "S", 354 "i32",
348 navigation_range: 201..202, 355 ),
349 node_range: 194..213, 356 deprecated: false,
350 kind: STATIC_DEF, 357 },
351 detail: Some( 358 StructureNode {
352 "i32", 359 parent: None,
353 ), 360 label: "C",
354 deprecated: false, 361 navigation_range: 220..221,
355 }, 362 node_range: 214..232,
356 StructureNode { 363 kind: CONST_DEF,
357 parent: None, 364 detail: Some(
358 label: "C", 365 "i32",
359 navigation_range: 220..221, 366 ),
360 node_range: 214..232, 367 deprecated: false,
361 kind: CONST_DEF, 368 },
362 detail: Some( 369 StructureNode {
363 "i32", 370 parent: None,
364 ), 371 label: "impl E",
365 deprecated: false, 372 navigation_range: 239..240,
366 }, 373 node_range: 234..243,
367 StructureNode { 374 kind: IMPL_DEF,
368 parent: None, 375 detail: None,
369 label: "impl E", 376 deprecated: false,
370 navigation_range: 239..240, 377 },
371 node_range: 234..243, 378 StructureNode {
372 kind: IMPL_DEF, 379 parent: None,
373 detail: None, 380 label: "impl fmt::Debug for E",
374 deprecated: false, 381 navigation_range: 265..266,
375 }, 382 node_range: 245..269,
376 StructureNode { 383 kind: IMPL_DEF,
377 parent: None, 384 detail: None,
378 label: "impl fmt::Debug for E", 385 deprecated: false,
379 navigation_range: 265..266, 386 },
380 node_range: 245..269, 387 StructureNode {
381 kind: IMPL_DEF, 388 parent: None,
382 detail: None, 389 label: "mc",
383 deprecated: false, 390 navigation_range: 284..286,
384 }, 391 node_range: 271..303,
385 StructureNode { 392 kind: MACRO_CALL,
386 parent: None, 393 detail: None,
387 label: "mc", 394 deprecated: false,
388 navigation_range: 284..286, 395 },
389 node_range: 271..303, 396 StructureNode {
390 kind: MACRO_CALL, 397 parent: None,
391 detail: None, 398 label: "mcexp",
392 deprecated: false, 399 navigation_range: 334..339,
393 }, 400 node_range: 305..356,
394 StructureNode { 401 kind: MACRO_CALL,
395 parent: None, 402 detail: None,
396 label: "mcexp", 403 deprecated: false,
397 navigation_range: 334..339, 404 },
398 node_range: 305..356, 405 StructureNode {
399 kind: MACRO_CALL, 406 parent: None,
400 detail: None, 407 label: "mcexp",
401 deprecated: false, 408 navigation_range: 387..392,
402 }, 409 node_range: 358..409,
403 StructureNode { 410 kind: MACRO_CALL,
404 parent: None, 411 detail: None,
405 label: "mcexp", 412 deprecated: false,
406 navigation_range: 387..392, 413 },
407 node_range: 358..409, 414 StructureNode {
408 kind: MACRO_CALL, 415 parent: None,
409 detail: None, 416 label: "obsolete",
410 deprecated: false, 417 navigation_range: 428..436,
411 }, 418 node_range: 411..441,
412 StructureNode { 419 kind: FN_DEF,
413 parent: None, 420 detail: Some(
414 label: "obsolete", 421 "fn()",
415 navigation_range: 428..436, 422 ),
416 node_range: 411..441, 423 deprecated: true,
417 kind: FN_DEF, 424 },
418 detail: Some( 425 StructureNode {
419 "fn()", 426 parent: None,
420 ), 427 label: "very_obsolete",
421 deprecated: true, 428 navigation_range: 481..494,
422 }, 429 node_range: 443..499,
423 StructureNode { 430 kind: FN_DEF,
424 parent: None, 431 detail: Some(
425 label: "very_obsolete", 432 "fn()",
426 navigation_range: 481..494, 433 ),
427 node_range: 443..499, 434 deprecated: true,
428 kind: FN_DEF, 435 },
429 detail: Some( 436 ]
430 "fn()", 437 "#]],
431 ), 438 );
432 deprecated: true,
433 },
434 ]
435 "###
436 );
437 } 439 }
438} 440}
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
index 54a47aac0..043515f54 100644
--- a/crates/ra_ide/src/expand_macro.rs
+++ b/crates/ra_ide/src/expand_macro.rs
@@ -2,7 +2,9 @@ use hir::Semantics;
2use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
3use ra_syntax::{ 3use ra_syntax::{
4 algo::{find_node_at_offset, SyntaxRewriter}, 4 algo::{find_node_at_offset, SyntaxRewriter},
5 ast, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T, 5 ast, AstNode, NodeOrToken, SyntaxKind,
6 SyntaxKind::*,
7 SyntaxNode, WalkEvent, T,
6}; 8};
7 9
8use crate::FilePosition; 10use crate::FilePosition;
@@ -65,8 +67,6 @@ fn expand_macro_recur(
65// FIXME: It would also be cool to share logic here and in the mbe tests, 67// FIXME: It would also be cool to share logic here and in the mbe tests,
66// which are pretty unreadable at the moment. 68// which are pretty unreadable at the moment.
67fn insert_whitespaces(syn: SyntaxNode) -> String { 69fn insert_whitespaces(syn: SyntaxNode) -> String {
68 use SyntaxKind::*;
69
70 let mut res = String::new(); 70 let mut res = String::new();
71 let mut token_iter = syn 71 let mut token_iter = syn
72 .preorder_with_tokens() 72 .preorder_with_tokens()
@@ -120,175 +120,164 @@ fn insert_whitespaces(syn: SyntaxNode) -> String {
120 120
121#[cfg(test)] 121#[cfg(test)]
122mod tests { 122mod tests {
123 use insta::assert_snapshot; 123 use expect::{expect, Expect};
124 124
125 use crate::mock_analysis::analysis_and_position; 125 use crate::mock_analysis::analysis_and_position;
126 126
127 use super::*; 127 fn check(ra_fixture: &str, expect: Expect) {
128 128 let (analysis, pos) = analysis_and_position(ra_fixture);
129 fn check_expand_macro(fixture: &str) -> ExpandedMacro { 129 let expansion = analysis.expand_macro(pos).unwrap().unwrap();
130 let (analysis, pos) = analysis_and_position(fixture); 130 let actual = format!("{}\n{}", expansion.name, expansion.expansion);
131 analysis.expand_macro(pos).unwrap().unwrap() 131 expect.assert_eq(&actual);
132 } 132 }
133 133
134 #[test] 134 #[test]
135 fn macro_expand_recursive_expansion() { 135 fn macro_expand_recursive_expansion() {
136 let res = check_expand_macro( 136 check(
137 r#" 137 r#"
138 //- /lib.rs 138macro_rules! bar {
139 macro_rules! bar { 139 () => { fn b() {} }
140 () => { fn b() {} } 140}
141 } 141macro_rules! foo {
142 macro_rules! foo { 142 () => { bar!(); }
143 () => { bar!(); } 143}
144 } 144macro_rules! baz {
145 macro_rules! baz { 145 () => { foo!(); }
146 () => { foo!(); } 146}
147 } 147f<|>oo!();
148 f<|>oo!(); 148"#,
149 "#, 149 expect![[r#"
150 foo
151 fn b(){}
152 "#]],
150 ); 153 );
151
152 assert_eq!(res.name, "foo");
153 assert_snapshot!(res.expansion, @r###"
154fn b(){}
155"###);
156 } 154 }
157 155
158 #[test] 156 #[test]
159 fn macro_expand_multiple_lines() { 157 fn macro_expand_multiple_lines() {
160 let res = check_expand_macro( 158 check(
161 r#" 159 r#"
162 //- /lib.rs 160macro_rules! foo {
163 macro_rules! foo { 161 () => {
164 () => { 162 fn some_thing() -> u32 {
165 fn some_thing() -> u32 { 163 let a = 0;
166 let a = 0; 164 a + 10
167 a + 10
168 }
169 }
170 } 165 }
171 f<|>oo!(); 166 }
167}
168f<|>oo!();
172 "#, 169 "#,
170 expect![[r#"
171 foo
172 fn some_thing() -> u32 {
173 let a = 0;
174 a+10
175 }"#]],
173 ); 176 );
174
175 assert_eq!(res.name, "foo");
176 assert_snapshot!(res.expansion, @r###"
177fn some_thing() -> u32 {
178 let a = 0;
179 a+10
180}
181"###);
182 } 177 }
183 178
184 #[test] 179 #[test]
185 fn macro_expand_match_ast() { 180 fn macro_expand_match_ast() {
186 let res = check_expand_macro( 181 check(
187 r#" 182 r#"
188 //- /lib.rs 183macro_rules! match_ast {
189 macro_rules! match_ast { 184 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
190 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; 185 (match ($node:expr) {
186 $( ast::$ast:ident($it:ident) => $res:block, )*
187 _ => $catch_all:expr $(,)?
188 }) => {{
189 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
190 { $catch_all }
191 }};
192}
191 193
192 (match ($node:expr) { 194fn main() {
193 $( ast::$ast:ident($it:ident) => $res:block, )* 195 mat<|>ch_ast! {
194 _ => $catch_all:expr $(,)? 196 match container {
195 }) => {{ 197 ast::TraitDef(it) => {},
196 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* 198 ast::ImplDef(it) => {},
197 { $catch_all } 199 _ => { continue },
198 }};
199 } 200 }
200
201 fn main() {
202 mat<|>ch_ast! {
203 match container {
204 ast::TraitDef(it) => {},
205 ast::ImplDef(it) => {},
206 _ => { continue },
207 }
208 }
209 }
210 "#,
211 );
212
213 assert_eq!(res.name, "match_ast");
214 assert_snapshot!(res.expansion, @r###"
215{
216 if let Some(it) = ast::TraitDef::cast(container.clone()){}
217 else if let Some(it) = ast::ImplDef::cast(container.clone()){}
218 else {
219 {
220 continue
221 } 201 }
222 }
223} 202}
224"###); 203"#,
204 expect![[r#"
205 match_ast
206 {
207 if let Some(it) = ast::TraitDef::cast(container.clone()){}
208 else if let Some(it) = ast::ImplDef::cast(container.clone()){}
209 else {
210 {
211 continue
212 }
213 }
214 }"#]],
215 );
225 } 216 }
226 217
227 #[test] 218 #[test]
228 fn macro_expand_match_ast_inside_let_statement() { 219 fn macro_expand_match_ast_inside_let_statement() {
229 let res = check_expand_macro( 220 check(
230 r#" 221 r#"
231 //- /lib.rs 222macro_rules! match_ast {
232 macro_rules! match_ast { 223 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
233 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; 224 (match ($node:expr) {}) => {{}};
234 (match ($node:expr) {}) => {{}}; 225}
235 }
236 226
237 fn main() { 227fn main() {
238 let p = f(|it| { 228 let p = f(|it| {
239 let res = mat<|>ch_ast! { match c {}}; 229 let res = mat<|>ch_ast! { match c {}};
240 Some(res) 230 Some(res)
241 })?; 231 })?;
242 } 232}
243 "#, 233"#,
234 expect![[r#"
235 match_ast
236 {}
237 "#]],
244 ); 238 );
245
246 assert_eq!(res.name, "match_ast");
247 assert_snapshot!(res.expansion, @r###"{}"###);
248 } 239 }
249 240
250 #[test] 241 #[test]
251 fn macro_expand_inner_macro_fail_to_expand() { 242 fn macro_expand_inner_macro_fail_to_expand() {
252 let res = check_expand_macro( 243 check(
253 r#" 244 r#"
254 //- /lib.rs 245macro_rules! bar {
255 macro_rules! bar { 246 (BAD) => {};
256 (BAD) => {}; 247}
257 } 248macro_rules! foo {
258 macro_rules! foo { 249 () => {bar!()};
259 () => {bar!()}; 250}
260 }
261 251
262 fn main() { 252fn main() {
263 let res = fo<|>o!(); 253 let res = fo<|>o!();
264 } 254}
265 "#, 255"#,
256 expect![[r#"
257 foo
258 "#]],
266 ); 259 );
267
268 assert_eq!(res.name, "foo");
269 assert_snapshot!(res.expansion, @r###""###);
270 } 260 }
271 261
272 #[test] 262 #[test]
273 fn macro_expand_with_dollar_crate() { 263 fn macro_expand_with_dollar_crate() {
274 let res = check_expand_macro( 264 check(
275 r#" 265 r#"
276 //- /lib.rs 266#[macro_export]
277 #[macro_export] 267macro_rules! bar {
278 macro_rules! bar { 268 () => {0};
279 () => {0}; 269}
280 } 270macro_rules! foo {
281 macro_rules! foo { 271 () => {$crate::bar!()};
282 () => {$crate::bar!()}; 272}
283 }
284 273
285 fn main() { 274fn main() {
286 let res = fo<|>o!(); 275 let res = fo<|>o!();
287 } 276}
288 "#, 277"#,
278 expect![[r#"
279 foo
280 0 "#]],
289 ); 281 );
290
291 assert_eq!(res.name, "foo");
292 assert_snapshot!(res.expansion, @r###"0"###);
293 } 282 }
294} 283}
diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs
index 8657377de..5cec689f8 100644
--- a/crates/ra_ide/src/folding_ranges.rs
+++ b/crates/ra_ide/src/folding_ranges.rs
@@ -15,6 +15,7 @@ pub enum FoldKind {
15 Imports, 15 Imports,
16 Mods, 16 Mods,
17 Block, 17 Block,
18 ArgList,
18} 19}
19 20
20#[derive(Debug)] 21#[derive(Debug)]
@@ -83,6 +84,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
83 match kind { 84 match kind {
84 COMMENT => Some(FoldKind::Comment), 85 COMMENT => Some(FoldKind::Comment),
85 USE_ITEM => Some(FoldKind::Imports), 86 USE_ITEM => Some(FoldKind::Imports),
87 ARG_LIST => Some(FoldKind::ArgList),
86 RECORD_FIELD_DEF_LIST 88 RECORD_FIELD_DEF_LIST
87 | RECORD_FIELD_PAT_LIST 89 | RECORD_FIELD_PAT_LIST
88 | ITEM_LIST 90 | ITEM_LIST
@@ -196,89 +198,85 @@ fn contiguous_range_for_comment(
196 198
197#[cfg(test)] 199#[cfg(test)]
198mod tests { 200mod tests {
201 use test_utils::extract_tags;
202
199 use super::*; 203 use super::*;
200 use test_utils::extract_ranges;
201 204
202 fn do_check(text: &str, fold_kinds: &[FoldKind]) { 205 fn check(ra_fixture: &str) {
203 let (ranges, text) = extract_ranges(text, "fold"); 206 let (ranges, text) = extract_tags(ra_fixture, "fold");
207
204 let parse = SourceFile::parse(&text); 208 let parse = SourceFile::parse(&text);
205 let folds = folding_ranges(&parse.tree()); 209 let folds = folding_ranges(&parse.tree());
206
207 assert_eq!( 210 assert_eq!(
208 folds.len(), 211 folds.len(),
209 ranges.len(), 212 ranges.len(),
210 "The amount of folds is different than the expected amount" 213 "The amount of folds is different than the expected amount"
211 ); 214 );
212 assert_eq!( 215
213 folds.len(), 216 for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
214 fold_kinds.len(),
215 "The amount of fold kinds is different than the expected amount"
216 );
217 for ((fold, range), fold_kind) in
218 folds.iter().zip(ranges.into_iter()).zip(fold_kinds.iter())
219 {
220 assert_eq!(fold.range.start(), range.start()); 217 assert_eq!(fold.range.start(), range.start());
221 assert_eq!(fold.range.end(), range.end()); 218 assert_eq!(fold.range.end(), range.end());
222 assert_eq!(&fold.kind, fold_kind); 219
220 let kind = match fold.kind {
221 FoldKind::Comment => "comment",
222 FoldKind::Imports => "imports",
223 FoldKind::Mods => "mods",
224 FoldKind::Block => "block",
225 FoldKind::ArgList => "arglist",
226 };
227 assert_eq!(kind, &attr.unwrap());
223 } 228 }
224 } 229 }
225 230
226 #[test] 231 #[test]
227 fn test_fold_comments() { 232 fn test_fold_comments() {
228 let text = r#" 233 check(
229<fold>// Hello 234 r#"
235<fold comment>// Hello
230// this is a multiline 236// this is a multiline
231// comment 237// comment
232//</fold> 238//</fold>
233 239
234// But this is not 240// But this is not
235 241
236fn main() <fold>{ 242fn main() <fold block>{
237 <fold>// We should 243 <fold comment>// We should
238 // also 244 // also
239 // fold 245 // fold
240 // this one.</fold> 246 // this one.</fold>
241 <fold>//! But this one is different 247 <fold comment>//! But this one is different
242 //! because it has another flavor</fold> 248 //! because it has another flavor</fold>
243 <fold>/* As does this 249 <fold comment>/* As does this
244 multiline comment */</fold> 250 multiline comment */</fold>
245}</fold>"#; 251}</fold>"#,
246 252 );
247 let fold_kinds = &[
248 FoldKind::Comment,
249 FoldKind::Block,
250 FoldKind::Comment,
251 FoldKind::Comment,
252 FoldKind::Comment,
253 ];
254 do_check(text, fold_kinds);
255 } 253 }
256 254
257 #[test] 255 #[test]
258 fn test_fold_imports() { 256 fn test_fold_imports() {
259 let text = r#" 257 check(
260<fold>use std::<fold>{ 258 r#"
259<fold imports>use std::<fold block>{
261 str, 260 str,
262 vec, 261 vec,
263 io as iop 262 io as iop
264}</fold>;</fold> 263}</fold>;</fold>
265 264
266fn main() <fold>{ 265fn main() <fold block>{
267}</fold>"#; 266}</fold>"#,
268 267 );
269 let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block];
270 do_check(text, folds);
271 } 268 }
272 269
273 #[test] 270 #[test]
274 fn test_fold_mods() { 271 fn test_fold_mods() {
275 let text = r#" 272 check(
273 r#"
276 274
277pub mod foo; 275pub mod foo;
278<fold>mod after_pub; 276<fold mods>mod after_pub;
279mod after_pub_next;</fold> 277mod after_pub_next;</fold>
280 278
281<fold>mod before_pub; 279<fold mods>mod before_pub;
282mod before_pub_next;</fold> 280mod before_pub_next;</fold>
283pub mod bar; 281pub mod bar;
284 282
@@ -286,90 +284,93 @@ mod not_folding_single;
286pub mod foobar; 284pub mod foobar;
287pub not_folding_single_next; 285pub not_folding_single_next;
288 286
289<fold>#[cfg(test)] 287<fold mods>#[cfg(test)]
290mod with_attribute; 288mod with_attribute;
291mod with_attribute_next;</fold> 289mod with_attribute_next;</fold>
292 290
293fn main() <fold>{ 291fn main() <fold block>{
294}</fold>"#; 292}</fold>"#,
295 293 );
296 let folds = &[FoldKind::Mods, FoldKind::Mods, FoldKind::Mods, FoldKind::Block];
297 do_check(text, folds);
298 } 294 }
299 295
300 #[test] 296 #[test]
301 fn test_fold_import_groups() { 297 fn test_fold_import_groups() {
302 let text = r#" 298 check(
303<fold>use std::str; 299 r#"
300<fold imports>use std::str;
304use std::vec; 301use std::vec;
305use std::io as iop;</fold> 302use std::io as iop;</fold>
306 303
307<fold>use std::mem; 304<fold imports>use std::mem;
308use std::f64;</fold> 305use std::f64;</fold>
309 306
310use std::collections::HashMap; 307use std::collections::HashMap;
311// Some random comment 308// Some random comment
312use std::collections::VecDeque; 309use std::collections::VecDeque;
313 310
314fn main() <fold>{ 311fn main() <fold block>{
315}</fold>"#; 312}</fold>"#,
316 313 );
317 let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block];
318 do_check(text, folds);
319 } 314 }
320 315
321 #[test] 316 #[test]
322 fn test_fold_import_and_groups() { 317 fn test_fold_import_and_groups() {
323 let text = r#" 318 check(
324<fold>use std::str; 319 r#"
320<fold imports>use std::str;
325use std::vec; 321use std::vec;
326use std::io as iop;</fold> 322use std::io as iop;</fold>
327 323
328<fold>use std::mem; 324<fold imports>use std::mem;
329use std::f64;</fold> 325use std::f64;</fold>
330 326
331<fold>use std::collections::<fold>{ 327<fold imports>use std::collections::<fold block>{
332 HashMap, 328 HashMap,
333 VecDeque, 329 VecDeque,
334}</fold>;</fold> 330}</fold>;</fold>
335// Some random comment 331// Some random comment
336 332
337fn main() <fold>{ 333fn main() <fold block>{
338}</fold>"#; 334}</fold>"#,
339 335 );
340 let folds = &[
341 FoldKind::Imports,
342 FoldKind::Imports,
343 FoldKind::Imports,
344 FoldKind::Block,
345 FoldKind::Block,
346 ];
347 do_check(text, folds);
348 } 336 }
349 337
350 #[test] 338 #[test]
351 fn test_folds_macros() { 339 fn test_folds_macros() {
352 let text = r#" 340 check(
353macro_rules! foo <fold>{ 341 r#"
342macro_rules! foo <fold block>{
354 ($($tt:tt)*) => { $($tt)* } 343 ($($tt:tt)*) => { $($tt)* }
355}</fold> 344}</fold>
356"#; 345"#,
357 346 );
358 let folds = &[FoldKind::Block];
359 do_check(text, folds);
360 } 347 }
361 348
362 #[test] 349 #[test]
363 fn test_fold_match_arms() { 350 fn test_fold_match_arms() {
364 let text = r#" 351 check(
365fn main() <fold>{ 352 r#"
366 match 0 <fold>{ 353fn main() <fold block>{
354 match 0 <fold block>{
367 0 => 0, 355 0 => 0,
368 _ => 1, 356 _ => 1,
369 }</fold> 357 }</fold>
370}</fold>"#; 358}</fold>"#,
359 );
360 }
371 361
372 let folds = &[FoldKind::Block, FoldKind::Block]; 362 #[test]
373 do_check(text, folds); 363 fn fold_big_calls() {
364 check(
365 r#"
366fn main() <fold block>{
367 frobnicate<fold arglist>(
368 1,
369 2,
370 3,
371 )</fold>
372}</fold>
373 "#,
374 )
374 } 375 }
375} 376}
diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs
index 99a7022a4..9acc960fc 100644
--- a/crates/ra_ide/src/goto_implementation.rs
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -219,6 +219,10 @@ impl T for &Foo {}
219 #[derive(Copy)] 219 #[derive(Copy)]
220//^^^^^^^^^^^^^^^ 220//^^^^^^^^^^^^^^^
221struct Foo<|>; 221struct Foo<|>;
222
223mod marker {
224 trait Copy {}
225}
222"#, 226"#,
223 ); 227 );
224 } 228 }
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index 91a3097fb..7eb40d637 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -55,8 +55,8 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
55mod tests { 55mod tests {
56 use crate::mock_analysis::analysis_and_position; 56 use crate::mock_analysis::analysis_and_position;
57 57
58 fn check_goto(fixture: &str, expected: &str) { 58 fn check_goto(ra_fixture: &str, expected: &str) {
59 let (analysis, pos) = analysis_and_position(fixture); 59 let (analysis, pos) = analysis_and_position(ra_fixture);
60 60
61 let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info; 61 let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info;
62 assert_eq!(navs.len(), 1); 62 assert_eq!(navs.len(), 1);
@@ -67,7 +67,7 @@ mod tests {
67 #[test] 67 #[test]
68 fn goto_type_definition_works_simple() { 68 fn goto_type_definition_works_simple() {
69 check_goto( 69 check_goto(
70 " 70 r"
71 //- /lib.rs 71 //- /lib.rs
72 struct Foo; 72 struct Foo;
73 fn foo() { 73 fn foo() {
@@ -82,7 +82,7 @@ mod tests {
82 #[test] 82 #[test]
83 fn goto_type_definition_works_simple_ref() { 83 fn goto_type_definition_works_simple_ref() {
84 check_goto( 84 check_goto(
85 " 85 r"
86 //- /lib.rs 86 //- /lib.rs
87 struct Foo; 87 struct Foo;
88 fn foo() { 88 fn foo() {
@@ -97,7 +97,7 @@ mod tests {
97 #[test] 97 #[test]
98 fn goto_type_definition_works_through_macro() { 98 fn goto_type_definition_works_through_macro() {
99 check_goto( 99 check_goto(
100 " 100 r"
101 //- /lib.rs 101 //- /lib.rs
102 macro_rules! id { 102 macro_rules! id {
103 ($($tt:tt)*) => { $($tt)* } 103 ($($tt:tt)*) => { $($tt)* }
@@ -116,7 +116,7 @@ mod tests {
116 #[test] 116 #[test]
117 fn goto_type_definition_for_param() { 117 fn goto_type_definition_for_param() {
118 check_goto( 118 check_goto(
119 " 119 r"
120 //- /lib.rs 120 //- /lib.rs
121 struct Foo; 121 struct Foo;
122 fn foo(<|>f: Foo) {} 122 fn foo(<|>f: Foo) {}
@@ -128,7 +128,7 @@ mod tests {
128 #[test] 128 #[test]
129 fn goto_type_definition_for_tuple_field() { 129 fn goto_type_definition_for_tuple_field() {
130 check_goto( 130 check_goto(
131 " 131 r"
132 //- /lib.rs 132 //- /lib.rs
133 struct Foo; 133 struct Foo;
134 struct Bar(Foo); 134 struct Bar(Foo);
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index c3e36a387..eaba2b61e 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -417,8 +417,8 @@ mod tests {
417 assert_eq!(offset, position.into()); 417 assert_eq!(offset, position.into());
418 } 418 }
419 419
420 fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) { 420 fn check_hover_result(ra_fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) {
421 let (analysis, position) = analysis_and_position(fixture); 421 let (analysis, position) = analysis_and_position(ra_fixture);
422 let hover = analysis.hover(position).unwrap().unwrap(); 422 let hover = analysis.hover(position).unwrap().unwrap();
423 let mut results = Vec::from(hover.info.results()); 423 let mut results = Vec::from(hover.info.results());
424 results.sort(); 424 results.sort();
@@ -435,8 +435,8 @@ mod tests {
435 (content[hover.range].to_string(), hover.info.actions().to_vec()) 435 (content[hover.range].to_string(), hover.info.actions().to_vec())
436 } 436 }
437 437
438 fn check_hover_no_result(fixture: &str) { 438 fn check_hover_no_result(ra_fixture: &str) {
439 let (analysis, position) = analysis_and_position(fixture); 439 let (analysis, position) = analysis_and_position(ra_fixture);
440 assert!(analysis.hover(position).unwrap().is_none()); 440 assert!(analysis.hover(position).unwrap().is_none());
441 } 441 }
442 442
@@ -923,7 +923,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
923 #[test] 923 #[test]
924 fn test_hover_through_macro() { 924 fn test_hover_through_macro() {
925 let (hover_on, _) = check_hover_result( 925 let (hover_on, _) = check_hover_result(
926 " 926 r"
927 //- /lib.rs 927 //- /lib.rs
928 macro_rules! id { 928 macro_rules! id {
929 ($($tt:tt)*) => { $($tt)* } 929 ($($tt:tt)*) => { $($tt)* }
@@ -944,7 +944,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
944 #[test] 944 #[test]
945 fn test_hover_through_expr_in_macro() { 945 fn test_hover_through_expr_in_macro() {
946 let (hover_on, _) = check_hover_result( 946 let (hover_on, _) = check_hover_result(
947 " 947 r"
948 //- /lib.rs 948 //- /lib.rs
949 macro_rules! id { 949 macro_rules! id {
950 ($($tt:tt)*) => { $($tt)* } 950 ($($tt:tt)*) => { $($tt)* }
@@ -962,7 +962,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
962 #[test] 962 #[test]
963 fn test_hover_through_expr_in_macro_recursive() { 963 fn test_hover_through_expr_in_macro_recursive() {
964 let (hover_on, _) = check_hover_result( 964 let (hover_on, _) = check_hover_result(
965 " 965 r"
966 //- /lib.rs 966 //- /lib.rs
967 macro_rules! id_deep { 967 macro_rules! id_deep {
968 ($($tt:tt)*) => { $($tt)* } 968 ($($tt:tt)*) => { $($tt)* }
@@ -983,7 +983,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
983 #[test] 983 #[test]
984 fn test_hover_through_func_in_macro_recursive() { 984 fn test_hover_through_func_in_macro_recursive() {
985 let (hover_on, _) = check_hover_result( 985 let (hover_on, _) = check_hover_result(
986 " 986 r"
987 //- /lib.rs 987 //- /lib.rs
988 macro_rules! id_deep { 988 macro_rules! id_deep {
989 ($($tt:tt)*) => { $($tt)* } 989 ($($tt:tt)*) => { $($tt)* }
@@ -1026,7 +1026,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1026 #[test] 1026 #[test]
1027 fn test_hover_through_assert_macro() { 1027 fn test_hover_through_assert_macro() {
1028 let (hover_on, _) = check_hover_result( 1028 let (hover_on, _) = check_hover_result(
1029 r#" 1029 r"
1030 //- /lib.rs 1030 //- /lib.rs
1031 #[rustc_builtin_macro] 1031 #[rustc_builtin_macro]
1032 macro_rules! assert {} 1032 macro_rules! assert {}
@@ -1035,7 +1035,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1035 fn foo() { 1035 fn foo() {
1036 assert!(ba<|>r()); 1036 assert!(ba<|>r());
1037 } 1037 }
1038 "#, 1038 ",
1039 &["fn bar() -> bool"], 1039 &["fn bar() -> bool"],
1040 ); 1040 );
1041 1041
@@ -1077,14 +1077,14 @@ fn func(foo: i32) { if true { <|>foo; }; }
1077 #[test] 1077 #[test]
1078 fn test_hover_function_show_qualifiers() { 1078 fn test_hover_function_show_qualifiers() {
1079 check_hover_result( 1079 check_hover_result(
1080 " 1080 r"
1081 //- /lib.rs 1081 //- /lib.rs
1082 async fn foo<|>() {} 1082 async fn foo<|>() {}
1083 ", 1083 ",
1084 &["async fn foo()"], 1084 &["async fn foo()"],
1085 ); 1085 );
1086 check_hover_result( 1086 check_hover_result(
1087 " 1087 r"
1088 //- /lib.rs 1088 //- /lib.rs
1089 pub const unsafe fn foo<|>() {} 1089 pub const unsafe fn foo<|>() {}
1090 ", 1090 ",
@@ -1102,7 +1102,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1102 #[test] 1102 #[test]
1103 fn test_hover_trait_show_qualifiers() { 1103 fn test_hover_trait_show_qualifiers() {
1104 let (_, actions) = check_hover_result( 1104 let (_, actions) = check_hover_result(
1105 " 1105 r"
1106 //- /lib.rs 1106 //- /lib.rs
1107 unsafe trait foo<|>() {} 1107 unsafe trait foo<|>() {}
1108 ", 1108 ",
@@ -1114,7 +1114,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1114 #[test] 1114 #[test]
1115 fn test_hover_mod_with_same_name_as_function() { 1115 fn test_hover_mod_with_same_name_as_function() {
1116 check_hover_result( 1116 check_hover_result(
1117 " 1117 r"
1118 //- /lib.rs 1118 //- /lib.rs
1119 use self::m<|>y::Bar; 1119 use self::m<|>y::Bar;
1120 1120
@@ -1237,7 +1237,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1237 #[test] 1237 #[test]
1238 fn test_hover_trait_has_impl_action() { 1238 fn test_hover_trait_has_impl_action() {
1239 let (_, actions) = check_hover_result( 1239 let (_, actions) = check_hover_result(
1240 " 1240 r"
1241 //- /lib.rs 1241 //- /lib.rs
1242 trait foo<|>() {} 1242 trait foo<|>() {}
1243 ", 1243 ",
@@ -1249,7 +1249,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1249 #[test] 1249 #[test]
1250 fn test_hover_struct_has_impl_action() { 1250 fn test_hover_struct_has_impl_action() {
1251 let (_, actions) = check_hover_result( 1251 let (_, actions) = check_hover_result(
1252 " 1252 r"
1253 //- /lib.rs 1253 //- /lib.rs
1254 struct foo<|>() {} 1254 struct foo<|>() {}
1255 ", 1255 ",
@@ -1261,7 +1261,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1261 #[test] 1261 #[test]
1262 fn test_hover_union_has_impl_action() { 1262 fn test_hover_union_has_impl_action() {
1263 let (_, actions) = check_hover_result( 1263 let (_, actions) = check_hover_result(
1264 " 1264 r"
1265 //- /lib.rs 1265 //- /lib.rs
1266 union foo<|>() {} 1266 union foo<|>() {}
1267 ", 1267 ",
@@ -1273,7 +1273,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1273 #[test] 1273 #[test]
1274 fn test_hover_enum_has_impl_action() { 1274 fn test_hover_enum_has_impl_action() {
1275 let (_, actions) = check_hover_result( 1275 let (_, actions) = check_hover_result(
1276 " 1276 r"
1277 //- /lib.rs 1277 //- /lib.rs
1278 enum foo<|>() { 1278 enum foo<|>() {
1279 A, 1279 A,
@@ -1288,7 +1288,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1288 #[test] 1288 #[test]
1289 fn test_hover_test_has_action() { 1289 fn test_hover_test_has_action() {
1290 let (_, actions) = check_hover_result( 1290 let (_, actions) = check_hover_result(
1291 " 1291 r"
1292 //- /lib.rs 1292 //- /lib.rs
1293 #[test] 1293 #[test]
1294 fn foo_<|>test() {} 1294 fn foo_<|>test() {}
@@ -1332,7 +1332,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1332 #[test] 1332 #[test]
1333 fn test_hover_test_mod_has_action() { 1333 fn test_hover_test_mod_has_action() {
1334 let (_, actions) = check_hover_result( 1334 let (_, actions) = check_hover_result(
1335 " 1335 r"
1336 //- /lib.rs 1336 //- /lib.rs
1337 mod tests<|> { 1337 mod tests<|> {
1338 #[test] 1338 #[test]
@@ -1373,7 +1373,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1373 #[test] 1373 #[test]
1374 fn test_hover_struct_has_goto_type_action() { 1374 fn test_hover_struct_has_goto_type_action() {
1375 let (_, actions) = check_hover_result( 1375 let (_, actions) = check_hover_result(
1376 " 1376 r"
1377 //- /main.rs 1377 //- /main.rs
1378 struct S{ f1: u32 } 1378 struct S{ f1: u32 }
1379 1379
@@ -1416,7 +1416,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1416 #[test] 1416 #[test]
1417 fn test_hover_generic_struct_has_goto_type_actions() { 1417 fn test_hover_generic_struct_has_goto_type_actions() {
1418 let (_, actions) = check_hover_result( 1418 let (_, actions) = check_hover_result(
1419 " 1419 r"
1420 //- /main.rs 1420 //- /main.rs
1421 struct Arg(u32); 1421 struct Arg(u32);
1422 struct S<T>{ f1: T } 1422 struct S<T>{ f1: T }
@@ -1479,7 +1479,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1479 #[test] 1479 #[test]
1480 fn test_hover_generic_struct_has_flattened_goto_type_actions() { 1480 fn test_hover_generic_struct_has_flattened_goto_type_actions() {
1481 let (_, actions) = check_hover_result( 1481 let (_, actions) = check_hover_result(
1482 " 1482 r"
1483 //- /main.rs 1483 //- /main.rs
1484 struct Arg(u32); 1484 struct Arg(u32);
1485 struct S<T>{ f1: T } 1485 struct S<T>{ f1: T }
@@ -1542,7 +1542,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1542 #[test] 1542 #[test]
1543 fn test_hover_tuple_has_goto_type_actions() { 1543 fn test_hover_tuple_has_goto_type_actions() {
1544 let (_, actions) = check_hover_result( 1544 let (_, actions) = check_hover_result(
1545 " 1545 r"
1546 //- /main.rs 1546 //- /main.rs
1547 struct A(u32); 1547 struct A(u32);
1548 struct B(u32); 1548 struct B(u32);
@@ -1627,7 +1627,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1627 #[test] 1627 #[test]
1628 fn test_hover_return_impl_trait_has_goto_type_action() { 1628 fn test_hover_return_impl_trait_has_goto_type_action() {
1629 let (_, actions) = check_hover_result( 1629 let (_, actions) = check_hover_result(
1630 " 1630 r"
1631 //- /main.rs 1631 //- /main.rs
1632 trait Foo {} 1632 trait Foo {}
1633 1633
@@ -1672,7 +1672,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1672 #[test] 1672 #[test]
1673 fn test_hover_generic_return_impl_trait_has_goto_type_action() { 1673 fn test_hover_generic_return_impl_trait_has_goto_type_action() {
1674 let (_, actions) = check_hover_result( 1674 let (_, actions) = check_hover_result(
1675 " 1675 r"
1676 //- /main.rs 1676 //- /main.rs
1677 trait Foo<T> {} 1677 trait Foo<T> {}
1678 struct S; 1678 struct S;
@@ -1737,7 +1737,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1737 #[test] 1737 #[test]
1738 fn test_hover_return_impl_traits_has_goto_type_action() { 1738 fn test_hover_return_impl_traits_has_goto_type_action() {
1739 let (_, actions) = check_hover_result( 1739 let (_, actions) = check_hover_result(
1740 " 1740 r"
1741 //- /main.rs 1741 //- /main.rs
1742 trait Foo {} 1742 trait Foo {}
1743 trait Bar {} 1743 trait Bar {}
@@ -1802,7 +1802,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1802 #[test] 1802 #[test]
1803 fn test_hover_generic_return_impl_traits_has_goto_type_action() { 1803 fn test_hover_generic_return_impl_traits_has_goto_type_action() {
1804 let (_, actions) = check_hover_result( 1804 let (_, actions) = check_hover_result(
1805 " 1805 r"
1806 //- /main.rs 1806 //- /main.rs
1807 trait Foo<T> {} 1807 trait Foo<T> {}
1808 trait Bar<T> {} 1808 trait Bar<T> {}
@@ -1907,7 +1907,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1907 #[test] 1907 #[test]
1908 fn test_hover_arg_impl_trait_has_goto_type_action() { 1908 fn test_hover_arg_impl_trait_has_goto_type_action() {
1909 let (_, actions) = check_hover_result( 1909 let (_, actions) = check_hover_result(
1910 " 1910 r"
1911 //- /lib.rs 1911 //- /lib.rs
1912 trait Foo {} 1912 trait Foo {}
1913 fn foo(ar<|>g: &impl Foo) {} 1913 fn foo(ar<|>g: &impl Foo) {}
@@ -1947,7 +1947,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
1947 #[test] 1947 #[test]
1948 fn test_hover_arg_impl_traits_has_goto_type_action() { 1948 fn test_hover_arg_impl_traits_has_goto_type_action() {
1949 let (_, actions) = check_hover_result( 1949 let (_, actions) = check_hover_result(
1950 " 1950 r"
1951 //- /lib.rs 1951 //- /lib.rs
1952 trait Foo {} 1952 trait Foo {}
1953 trait Bar<T> {} 1953 trait Bar<T> {}
@@ -2028,7 +2028,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
2028 #[test] 2028 #[test]
2029 fn test_hover_arg_generic_impl_trait_has_goto_type_action() { 2029 fn test_hover_arg_generic_impl_trait_has_goto_type_action() {
2030 let (_, actions) = check_hover_result( 2030 let (_, actions) = check_hover_result(
2031 " 2031 r"
2032 //- /lib.rs 2032 //- /lib.rs
2033 trait Foo<T> {} 2033 trait Foo<T> {}
2034 struct S {} 2034 struct S {}
@@ -2088,7 +2088,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
2088 #[test] 2088 #[test]
2089 fn test_hover_dyn_return_has_goto_type_action() { 2089 fn test_hover_dyn_return_has_goto_type_action() {
2090 let (_, actions) = check_hover_result( 2090 let (_, actions) = check_hover_result(
2091 " 2091 r"
2092 //- /main.rs 2092 //- /main.rs
2093 trait Foo {} 2093 trait Foo {}
2094 struct S; 2094 struct S;
@@ -2156,7 +2156,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
2156 #[test] 2156 #[test]
2157 fn test_hover_dyn_arg_has_goto_type_action() { 2157 fn test_hover_dyn_arg_has_goto_type_action() {
2158 let (_, actions) = check_hover_result( 2158 let (_, actions) = check_hover_result(
2159 " 2159 r"
2160 //- /lib.rs 2160 //- /lib.rs
2161 trait Foo {} 2161 trait Foo {}
2162 fn foo(ar<|>g: &dyn Foo) {} 2162 fn foo(ar<|>g: &dyn Foo) {}
@@ -2196,7 +2196,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
2196 #[test] 2196 #[test]
2197 fn test_hover_generic_dyn_arg_has_goto_type_action() { 2197 fn test_hover_generic_dyn_arg_has_goto_type_action() {
2198 let (_, actions) = check_hover_result( 2198 let (_, actions) = check_hover_result(
2199 " 2199 r"
2200 //- /lib.rs 2200 //- /lib.rs
2201 trait Foo<T> {} 2201 trait Foo<T> {}
2202 struct S {} 2202 struct S {}
@@ -2256,7 +2256,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
2256 #[test] 2256 #[test]
2257 fn test_hover_goto_type_action_links_order() { 2257 fn test_hover_goto_type_action_links_order() {
2258 let (_, actions) = check_hover_result( 2258 let (_, actions) = check_hover_result(
2259 " 2259 r"
2260 //- /lib.rs 2260 //- /lib.rs
2261 trait ImplTrait<T> {} 2261 trait ImplTrait<T> {}
2262 trait DynTrait<T> {} 2262 trait DynTrait<T> {}
@@ -2357,7 +2357,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
2357 #[test] 2357 #[test]
2358 fn test_hover_associated_type_has_goto_type_action() { 2358 fn test_hover_associated_type_has_goto_type_action() {
2359 let (_, actions) = check_hover_result( 2359 let (_, actions) = check_hover_result(
2360 " 2360 r"
2361 //- /main.rs 2361 //- /main.rs
2362 trait Foo { 2362 trait Foo {
2363 type Item; 2363 type Item;
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 64980a832..62d364bfa 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -345,7 +345,7 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
345 345
346#[cfg(test)] 346#[cfg(test)]
347mod tests { 347mod tests {
348 use insta::assert_debug_snapshot; 348 use expect::{expect, Expect};
349 use test_utils::extract_annotations; 349 use test_utils::extract_annotations;
350 350
351 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file}; 351 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file};
@@ -363,6 +363,12 @@ mod tests {
363 assert_eq!(expected, actual); 363 assert_eq!(expected, actual);
364 } 364 }
365 365
366 fn check_expect(ra_fixture: &str, config: InlayHintsConfig, expect: Expect) {
367 let (analysis, file_id) = single_file(ra_fixture);
368 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
369 expect.assert_debug_eq(&inlay_hints)
370 }
371
366 #[test] 372 #[test]
367 fn param_hints_only() { 373 fn param_hints_only() {
368 check_with_config( 374 check_with_config(
@@ -772,34 +778,41 @@ fn main() {
772 778
773 #[test] 779 #[test]
774 fn chaining_hints_ignore_comments() { 780 fn chaining_hints_ignore_comments() {
775 let (analysis, file_id) = single_file( 781 check_expect(
776 r#" 782 r#"
777 struct A(B); 783struct A(B);
778 impl A { fn into_b(self) -> B { self.0 } } 784impl A { fn into_b(self) -> B { self.0 } }
779 struct B(C); 785struct B(C);
780 impl B { fn into_c(self) -> C { self.0 } } 786impl B { fn into_c(self) -> C { self.0 } }
781 struct C; 787struct C;
782 788
783 fn main() { 789fn main() {
784 let c = A(B(C)) 790 let c = A(B(C))
785 .into_b() // This is a comment 791 .into_b() // This is a comment
786 .into_c(); 792 .into_c();
787 }"#, 793}
788 ); 794"#,
789 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 795 InlayHintsConfig {
790 [ 796 parameter_hints: false,
791 InlayHint { 797 type_hints: false,
792 range: 147..172, 798 chaining_hints: true,
793 kind: ChainingHint, 799 max_length: None,
794 label: "B",
795 },
796 InlayHint {
797 range: 147..154,
798 kind: ChainingHint,
799 label: "A",
800 }, 800 },
801 ] 801 expect![[r#"
802 "###); 802 [
803 InlayHint {
804 range: 147..172,
805 kind: ChainingHint,
806 label: "B",
807 },
808 InlayHint {
809 range: 147..154,
810 kind: ChainingHint,
811 label: "A",
812 },
813 ]
814 "#]],
815 );
803 } 816 }
804 817
805 #[test] 818 #[test]
@@ -826,7 +839,7 @@ fn main() {
826 839
827 #[test] 840 #[test]
828 fn struct_access_chaining_hints() { 841 fn struct_access_chaining_hints() {
829 let (analysis, file_id) = single_file( 842 check_expect(
830 r#" 843 r#"
831struct A { pub b: B } 844struct A { pub b: B }
832struct B { pub c: C } 845struct B { pub c: C }
@@ -845,58 +858,71 @@ fn main() {
845 let x = D 858 let x = D
846 .foo(); 859 .foo();
847}"#, 860}"#,
848 ); 861 InlayHintsConfig {
849 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 862 parameter_hints: false,
850 [ 863 type_hints: false,
851 InlayHint { 864 chaining_hints: true,
852 range: 143..190, 865 max_length: None,
853 kind: ChainingHint,
854 label: "C",
855 },
856 InlayHint {
857 range: 143..179,
858 kind: ChainingHint,
859 label: "B",
860 }, 866 },
861 ] 867 expect![[r#"
862 "###); 868 [
869 InlayHint {
870 range: 143..190,
871 kind: ChainingHint,
872 label: "C",
873 },
874 InlayHint {
875 range: 143..179,
876 kind: ChainingHint,
877 label: "B",
878 },
879 ]
880 "#]],
881 );
863 } 882 }
864 883
865 #[test] 884 #[test]
866 fn generic_chaining_hints() { 885 fn generic_chaining_hints() {
867 let (analysis, file_id) = single_file( 886 check_expect(
868 r#" 887 r#"
869 struct A<T>(T); 888struct A<T>(T);
870 struct B<T>(T); 889struct B<T>(T);
871 struct C<T>(T); 890struct C<T>(T);
872 struct X<T,R>(T, R); 891struct X<T,R>(T, R);
873 892
874 impl<T> A<T> { 893impl<T> A<T> {
875 fn new(t: T) -> Self { A(t) } 894 fn new(t: T) -> Self { A(t) }
876 fn into_b(self) -> B<T> { B(self.0) } 895 fn into_b(self) -> B<T> { B(self.0) }
877 } 896}
878 impl<T> B<T> { 897impl<T> B<T> {
879 fn into_c(self) -> C<T> { C(self.0) } 898 fn into_c(self) -> C<T> { C(self.0) }
880 } 899}
881 fn main() { 900fn main() {
882 let c = A::new(X(42, true)) 901 let c = A::new(X(42, true))
883 .into_b() 902 .into_b()
884 .into_c(); 903 .into_c();
885 }"#, 904}
886 ); 905"#,
887 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 906 InlayHintsConfig {
888 [ 907 parameter_hints: false,
889 InlayHint { 908 type_hints: false,
890 range: 246..283, 909 chaining_hints: true,
891 kind: ChainingHint, 910 max_length: None,
892 label: "B<X<i32, bool>>",
893 },
894 InlayHint {
895 range: 246..265,
896 kind: ChainingHint,
897 label: "A<X<i32, bool>>",
898 }, 911 },
899 ] 912 expect![[r#"
900 "###); 913 [
914 InlayHint {
915 range: 246..283,
916 kind: ChainingHint,
917 label: "B<X<i32, bool>>",
918 },
919 InlayHint {
920 range: 246..265,
921 kind: ChainingHint,
922 label: "A<X<i32, bool>>",
923 },
924 ]
925 "#]],
926 );
901 } 927 }
902} 928}
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index ecac5134e..8660278f1 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -75,7 +75,7 @@ pub use crate::{
75 }, 75 },
76}; 76};
77 77
78pub use hir::Documentation; 78pub use hir::{Documentation, Semantics};
79pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist}; 79pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist};
80pub use ra_db::{ 80pub use ra_db::{
81 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, 81 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot,
@@ -385,7 +385,9 @@ impl Analysis {
385 position: FilePosition, 385 position: FilePosition,
386 search_scope: Option<SearchScope>, 386 search_scope: Option<SearchScope>,
387 ) -> Cancelable<Option<ReferenceSearchResult>> { 387 ) -> Cancelable<Option<ReferenceSearchResult>> {
388 self.with_db(|db| references::find_all_refs(db, position, search_scope).map(|it| it.info)) 388 self.with_db(|db| {
389 references::find_all_refs(&Semantics::new(db), position, search_scope).map(|it| it.info)
390 })
389 } 391 }
390 392
391 /// Returns a short text describing element at position. 393 /// Returns a short text describing element at position.
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index db6d50694..a393d3dba 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -130,7 +130,7 @@ impl MockAnalysis {
130 let other_crate = crate_graph.add_crate_root( 130 let other_crate = crate_graph.add_crate_root(
131 file_id, 131 file_id,
132 edition, 132 edition,
133 Some(CrateName::new(crate_name).unwrap()), 133 Some(crate_name.to_string()),
134 cfg, 134 cfg,
135 env, 135 env,
136 Default::default(), 136 Default::default(),
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 3433fdae3..c2b0d5efe 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -86,12 +86,11 @@ impl IntoIterator for ReferenceSearchResult {
86} 86}
87 87
88pub(crate) fn find_all_refs( 88pub(crate) fn find_all_refs(
89 db: &RootDatabase, 89 sema: &Semantics<RootDatabase>,
90 position: FilePosition, 90 position: FilePosition,
91 search_scope: Option<SearchScope>, 91 search_scope: Option<SearchScope>,
92) -> Option<RangeInfo<ReferenceSearchResult>> { 92) -> Option<RangeInfo<ReferenceSearchResult>> {
93 let _p = profile("find_all_refs"); 93 let _p = profile("find_all_refs");
94 let sema = Semantics::new(db);
95 let syntax = sema.parse(position.file_id).syntax().clone(); 94 let syntax = sema.parse(position.file_id).syntax().clone();
96 95
97 let (opt_name, search_kind) = if let Some(name) = 96 let (opt_name, search_kind) = if let Some(name) =
@@ -108,15 +107,15 @@ pub(crate) fn find_all_refs(
108 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; 107 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?;
109 108
110 let references = def 109 let references = def
111 .find_usages(db, search_scope) 110 .find_usages(sema, search_scope)
112 .into_iter() 111 .into_iter()
113 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) 112 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)
114 .collect(); 113 .collect();
115 114
116 let decl_range = def.try_to_nav(db)?.range(); 115 let decl_range = def.try_to_nav(sema.db)?.range();
117 116
118 let declaration = Declaration { 117 let declaration = Declaration {
119 nav: def.try_to_nav(db)?, 118 nav: def.try_to_nav(sema.db)?,
120 kind: ReferenceKind::Other, 119 kind: ReferenceKind::Other,
121 access: decl_access(&def, &syntax, decl_range), 120 access: decl_access(&def, &syntax, decl_range),
122 }; 121 };
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 7ebc0adcf..b6a2266b4 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -24,23 +24,24 @@ pub(crate) fn rename(
24 position: FilePosition, 24 position: FilePosition,
25 new_name: &str, 25 new_name: &str,
26) -> Option<RangeInfo<SourceChange>> { 26) -> Option<RangeInfo<SourceChange>> {
27 let sema = Semantics::new(db);
28
27 match lex_single_valid_syntax_kind(new_name)? { 29 match lex_single_valid_syntax_kind(new_name)? {
28 SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), 30 SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (),
29 SyntaxKind::SELF_KW => return rename_to_self(db, position), 31 SyntaxKind::SELF_KW => return rename_to_self(&sema, position),
30 _ => return None, 32 _ => return None,
31 } 33 }
32 34
33 let sema = Semantics::new(db);
34 let source_file = sema.parse(position.file_id); 35 let source_file = sema.parse(position.file_id);
35 let syntax = source_file.syntax(); 36 let syntax = source_file.syntax();
36 if let Some(module) = find_module_at_offset(&sema, position, syntax) { 37 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
37 rename_mod(db, position, module, new_name) 38 rename_mod(&sema, position, module, new_name)
38 } else if let Some(self_token) = 39 } else if let Some(self_token) =
39 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 40 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
40 { 41 {
41 rename_self_to_param(db, position, self_token, new_name) 42 rename_self_to_param(&sema, position, self_token, new_name)
42 } else { 43 } else {
43 rename_reference(sema.db, position, new_name) 44 rename_reference(&sema, position, new_name)
44 } 45 }
45} 46}
46 47
@@ -97,7 +98,7 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
97} 98}
98 99
99fn rename_mod( 100fn rename_mod(
100 db: &RootDatabase, 101 sema: &Semantics<RootDatabase>,
101 position: FilePosition, 102 position: FilePosition,
102 module: Module, 103 module: Module,
103 new_name: &str, 104 new_name: &str,
@@ -105,12 +106,12 @@ fn rename_mod(
105 let mut source_file_edits = Vec::new(); 106 let mut source_file_edits = Vec::new();
106 let mut file_system_edits = Vec::new(); 107 let mut file_system_edits = Vec::new();
107 108
108 let src = module.definition_source(db); 109 let src = module.definition_source(sema.db);
109 let file_id = src.file_id.original_file(db); 110 let file_id = src.file_id.original_file(sema.db);
110 match src.value { 111 match src.value {
111 ModuleSource::SourceFile(..) => { 112 ModuleSource::SourceFile(..) => {
112 // mod is defined in path/to/dir/mod.rs 113 // mod is defined in path/to/dir/mod.rs
113 let dst = if module.is_mod_rs(db) { 114 let dst = if module.is_mod_rs(sema.db) {
114 format!("../{}/mod.rs", new_name) 115 format!("../{}/mod.rs", new_name)
115 } else { 116 } else {
116 format!("{}.rs", new_name) 117 format!("{}.rs", new_name)
@@ -122,17 +123,17 @@ fn rename_mod(
122 ModuleSource::Module(..) => {} 123 ModuleSource::Module(..) => {}
123 } 124 }
124 125
125 if let Some(src) = module.declaration_source(db) { 126 if let Some(src) = module.declaration_source(sema.db) {
126 let file_id = src.file_id.original_file(db); 127 let file_id = src.file_id.original_file(sema.db);
127 let name = src.value.name()?; 128 let name = src.value.name()?;
128 let edit = SourceFileEdit { 129 let edit = SourceFileEdit {
129 file_id: file_id, 130 file_id,
130 edit: TextEdit::replace(name.syntax().text_range(), new_name.into()), 131 edit: TextEdit::replace(name.syntax().text_range(), new_name.into()),
131 }; 132 };
132 source_file_edits.push(edit); 133 source_file_edits.push(edit);
133 } 134 }
134 135
135 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 136 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
136 let ref_edits = refs 137 let ref_edits = refs
137 .references 138 .references
138 .into_iter() 139 .into_iter()
@@ -142,8 +143,10 @@ fn rename_mod(
142 Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) 143 Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
143} 144}
144 145
145fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { 146fn rename_to_self(
146 let sema = Semantics::new(db); 147 sema: &Semantics<RootDatabase>,
148 position: FilePosition,
149) -> Option<RangeInfo<SourceChange>> {
147 let source_file = sema.parse(position.file_id); 150 let source_file = sema.parse(position.file_id);
148 let syn = source_file.syntax(); 151 let syn = source_file.syntax();
149 152
@@ -158,7 +161,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo
158 _ => return None, // not renaming other types 161 _ => return None, // not renaming other types
159 }; 162 };
160 163
161 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 164 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
162 165
163 let param_range = first_param.syntax().text_range(); 166 let param_range = first_param.syntax().text_range();
164 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs 167 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
@@ -210,16 +213,15 @@ fn text_edit_from_self_param(
210} 213}
211 214
212fn rename_self_to_param( 215fn rename_self_to_param(
213 db: &RootDatabase, 216 sema: &Semantics<RootDatabase>,
214 position: FilePosition, 217 position: FilePosition,
215 self_token: SyntaxToken, 218 self_token: SyntaxToken,
216 new_name: &str, 219 new_name: &str,
217) -> Option<RangeInfo<SourceChange>> { 220) -> Option<RangeInfo<SourceChange>> {
218 let sema = Semantics::new(db);
219 let source_file = sema.parse(position.file_id); 221 let source_file = sema.parse(position.file_id);
220 let syn = source_file.syntax(); 222 let syn = source_file.syntax();
221 223
222 let text = db.file_text(position.file_id); 224 let text = sema.db.file_text(position.file_id);
223 let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; 225 let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?;
224 let search_range = fn_def.syntax().text_range(); 226 let search_range = fn_def.syntax().text_range();
225 227
@@ -249,11 +251,11 @@ fn rename_self_to_param(
249} 251}
250 252
251fn rename_reference( 253fn rename_reference(
252 db: &RootDatabase, 254 sema: &Semantics<RootDatabase>,
253 position: FilePosition, 255 position: FilePosition,
254 new_name: &str, 256 new_name: &str,
255) -> Option<RangeInfo<SourceChange>> { 257) -> Option<RangeInfo<SourceChange>> {
256 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 258 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
257 259
258 let edit = refs 260 let edit = refs
259 .into_iter() 261 .into_iter()
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index b7fad9719..aa7c887d6 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -1,6 +1,7 @@
1use std::fs; 1use std::fs;
2 2
3use test_utils::{assert_eq_text, project_dir, read_text}; 3use expect::{expect_file, ExpectFile};
4use test_utils::project_dir;
4 5
5use crate::{mock_analysis::single_file, FileRange, TextRange}; 6use crate::{mock_analysis::single_file, FileRange, TextRange};
6 7
@@ -91,7 +92,7 @@ impl<T> Option<T> {
91} 92}
92"# 93"#
93 .trim(), 94 .trim(),
94 "crates/ra_ide/src/snapshots/highlighting.html", 95 expect_file!["crates/ra_ide/test_data/highlighting.html"],
95 false, 96 false,
96 ); 97 );
97} 98}
@@ -114,7 +115,7 @@ fn bar() {
114} 115}
115"# 116"#
116 .trim(), 117 .trim(),
117 "crates/ra_ide/src/snapshots/rainbow_highlighting.html", 118 expect_file!["crates/ra_ide/test_data/rainbow_highlighting.html"],
118 true, 119 true,
119 ); 120 );
120} 121}
@@ -167,7 +168,7 @@ fn main() {
167 ); 168 );
168}"## 169}"##
169 .trim(), 170 .trim(),
170 "crates/ra_ide/src/snapshots/highlight_injection.html", 171 expect_file!["crates/ra_ide/test_data/highlight_injection.html"],
171 false, 172 false,
172 ); 173 );
173} 174}
@@ -250,7 +251,7 @@ fn main() {
250 println!("{ничоси}", ничоси = 92); 251 println!("{ничоси}", ничоси = 92);
251}"# 252}"#
252 .trim(), 253 .trim(),
253 "crates/ra_ide/src/snapshots/highlight_strings.html", 254 expect_file!["crates/ra_ide/test_data/highlight_strings.html"],
254 false, 255 false,
255 ); 256 );
256} 257}
@@ -278,7 +279,7 @@ fn main() {
278} 279}
279"# 280"#
280 .trim(), 281 .trim(),
281 "crates/ra_ide/src/snapshots/highlight_unsafe.html", 282 expect_file!["crates/ra_ide/test_data/highlight_unsafe.html"],
282 false, 283 false,
283 ); 284 );
284} 285}
@@ -354,7 +355,7 @@ macro_rules! noop {
354} 355}
355"# 356"#
356 .trim(), 357 .trim(),
357 "crates/ra_ide/src/snapshots/highlight_doctest.html", 358 expect_file!["crates/ra_ide/test_data/highlight_doctest.html"],
358 false, 359 false,
359 ); 360 );
360} 361}
@@ -362,11 +363,8 @@ macro_rules! noop {
362/// Highlights the code given by the `ra_fixture` argument, renders the 363/// Highlights the code given by the `ra_fixture` argument, renders the
363/// result as HTML, and compares it with the HTML file given as `snapshot`. 364/// result as HTML, and compares it with the HTML file given as `snapshot`.
364/// Note that the `snapshot` file is overwritten by the rendered HTML. 365/// Note that the `snapshot` file is overwritten by the rendered HTML.
365fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) { 366fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) {
366 let (analysis, file_id) = single_file(ra_fixture); 367 let (analysis, file_id) = single_file(ra_fixture);
367 let dst_file = project_dir().join(snapshot);
368 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); 368 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
369 let expected_html = &read_text(&dst_file); 369 expect.assert_eq(actual_html)
370 fs::write(dst_file, &actual_html).unwrap();
371 assert_eq_text!(expected_html, actual_html);
372} 370}
diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/test_data/highlight_doctest.html
index e8155def7..e8155def7 100644
--- a/crates/ra_ide/src/snapshots/highlight_doctest.html
+++ b/crates/ra_ide/test_data/highlight_doctest.html
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/test_data/highlight_injection.html
index 1b0349bae..1b0349bae 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/test_data/highlight_injection.html
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/test_data/highlight_strings.html
index d184b5691..d184b5691 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/test_data/highlight_strings.html
diff --git a/crates/ra_ide/src/snapshots/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index 6936e949f..6936e949f 100644
--- a/crates/ra_ide/src/snapshots/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/test_data/highlighting.html
index 8d0b38f95..8d0b38f95 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/test_data/highlighting.html
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/test_data/rainbow_highlighting.html
index 9516c7441..9516c7441 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/test_data/rainbow_highlighting.html
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs
index b507000f2..dbe6eacc5 100644
--- a/crates/ra_ide_db/src/change.rs
+++ b/crates/ra_ide_db/src/change.rs
@@ -243,8 +243,9 @@ impl RootDatabase {
243 hir::db::GenericPredicatesForParamQuery 243 hir::db::GenericPredicatesForParamQuery
244 hir::db::GenericPredicatesQuery 244 hir::db::GenericPredicatesQuery
245 hir::db::GenericDefaultsQuery 245 hir::db::GenericDefaultsQuery
246 hir::db::ImplsInCrateQuery 246 hir::db::InherentImplsInCrateQuery
247 hir::db::ImplsFromDepsQuery 247 hir::db::TraitImplsInCrateQuery
248 hir::db::TraitImplsInDepsQuery
248 hir::db::InternTypeCtorQuery 249 hir::db::InternTypeCtorQuery
249 hir::db::InternTypeParamIdQuery 250 hir::db::InternTypeParamIdQuery
250 hir::db::InternChalkImplQuery 251 hir::db::InternChalkImplQuery
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs
index fff112e66..1fba71ff8 100644
--- a/crates/ra_ide_db/src/imports_locator.rs
+++ b/crates/ra_ide_db/src/imports_locator.rs
@@ -13,57 +13,53 @@ use crate::{
13use either::Either; 13use either::Either;
14use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15 15
16pub struct ImportsLocator<'a> { 16pub fn find_imports<'a>(
17 sema: Semantics<'a, RootDatabase>, 17 sema: &Semantics<'a, RootDatabase>,
18 krate: Crate, 18 krate: Crate,
19} 19 name_to_import: &str,
20 20) -> Vec<Either<ModuleDef, MacroDef>> {
21impl<'a> ImportsLocator<'a> { 21 let _p = profile("search_for_imports");
22 pub fn new(db: &'a RootDatabase, krate: Crate) -> Self { 22 let db = sema.db;
23 Self { sema: Semantics::new(db), krate }
24 }
25 23
26 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> { 24 // Query dependencies first.
27 let _p = profile("search_for_imports"); 25 let mut candidates: FxHashSet<_> =
28 let db = self.sema.db; 26 krate.query_external_importables(db, name_to_import).collect();
29 27
30 // Query dependencies first. 28 // Query the local crate using the symbol index.
31 let mut candidates: FxHashSet<_> = 29 let local_results = {
32 self.krate.query_external_importables(db, name_to_import).collect(); 30 let mut query = Query::new(name_to_import.to_string());
31 query.exact();
32 query.limit(40);
33 symbol_index::crate_symbols(db, krate.into(), query)
34 };
33 35
34 // Query the local crate using the symbol index. 36 candidates.extend(
35 let local_results = { 37 local_results
36 let mut query = Query::new(name_to_import.to_string()); 38 .into_iter()
37 query.exact(); 39 .filter_map(|import_candidate| get_name_definition(sema, &import_candidate))
38 query.limit(40); 40 .filter_map(|name_definition_to_import| match name_definition_to_import {
39 symbol_index::crate_symbols(db, self.krate.into(), query) 41 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
40 }; 42 Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
43 _ => None,
44 }),
45 );
41 46
42 candidates.extend( 47 candidates.into_iter().collect()
43 local_results 48}
44 .into_iter()
45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate))
46 .filter_map(|name_definition_to_import| match name_definition_to_import {
47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
49 _ => None,
50 }),
51 );
52
53 candidates.into_iter().collect()
54 }
55 49
56 fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> { 50fn get_name_definition<'a>(
57 let _p = profile("get_name_definition"); 51 sema: &Semantics<'a, RootDatabase>,
58 let file_id = import_candidate.file_id; 52 import_candidate: &FileSymbol,
53) -> Option<Definition> {
54 let _p = profile("get_name_definition");
55 let file_id = import_candidate.file_id;
59 56
60 let candidate_node = import_candidate.ptr.to_node(self.sema.parse(file_id).syntax()); 57 let candidate_node = import_candidate.ptr.to_node(sema.parse(file_id).syntax());
61 let candidate_name_node = if candidate_node.kind() != NAME { 58 let candidate_name_node = if candidate_node.kind() != NAME {
62 candidate_node.children().find(|it| it.kind() == NAME)? 59 candidate_node.children().find(|it| it.kind() == NAME)?
63 } else { 60 } else {
64 candidate_node 61 candidate_node
65 }; 62 };
66 let name = ast::Name::cast(candidate_name_node)?; 63 let name = ast::Name::cast(candidate_name_node)?;
67 classify_name(&self.sema, &name)?.into_definition() 64 classify_name(sema, &name)?.into_definition()
68 }
69} 65}
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index a808de4f1..c78071ad6 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -13,7 +13,7 @@ mod wasm_shims;
13 13
14use std::sync::Arc; 14use std::sync::Arc;
15 15
16use hir::db::{AstDatabase, DefDatabase}; 16use hir::db::{AstDatabase, DefDatabase, HirDatabase};
17use ra_db::{ 17use ra_db::{
18 salsa::{self, Database, Durability}, 18 salsa::{self, Database, Durability},
19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, 19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase,
@@ -52,6 +52,12 @@ impl Upcast<dyn DefDatabase> for RootDatabase {
52 } 52 }
53} 53}
54 54
55impl Upcast<dyn HirDatabase> for RootDatabase {
56 fn upcast(&self) -> &(dyn HirDatabase + 'static) {
57 &*self
58 }
59}
60
55impl FileLoader for RootDatabase { 61impl FileLoader for RootDatabase {
56 fn file_text(&self, file_id: FileId) -> Arc<String> { 62 fn file_text(&self, file_id: FileId) -> Arc<String> {
57 FileLoaderDelegate(self).file_text(file_id) 63 FileLoaderDelegate(self).file_text(file_id)
diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs
index 44d5c35e6..81553150b 100644
--- a/crates/ra_ide_db/src/search.rs
+++ b/crates/ra_ide_db/src/search.rs
@@ -180,20 +180,20 @@ impl Definition {
180 180
181 pub fn find_usages( 181 pub fn find_usages(
182 &self, 182 &self,
183 db: &RootDatabase, 183 sema: &Semantics<RootDatabase>,
184 search_scope: Option<SearchScope>, 184 search_scope: Option<SearchScope>,
185 ) -> Vec<Reference> { 185 ) -> Vec<Reference> {
186 let _p = profile("Definition::find_usages"); 186 let _p = profile("Definition::find_usages");
187 187
188 let search_scope = { 188 let search_scope = {
189 let base = self.search_scope(db); 189 let base = self.search_scope(sema.db);
190 match search_scope { 190 match search_scope {
191 None => base, 191 None => base,
192 Some(scope) => base.intersection(&scope), 192 Some(scope) => base.intersection(&scope),
193 } 193 }
194 }; 194 };
195 195
196 let name = match self.name(db) { 196 let name = match self.name(sema.db) {
197 None => return Vec::new(), 197 None => return Vec::new(),
198 Some(it) => it.to_string(), 198 Some(it) => it.to_string(),
199 }; 199 };
@@ -202,11 +202,10 @@ impl Definition {
202 let mut refs = vec![]; 202 let mut refs = vec![];
203 203
204 for (file_id, search_range) in search_scope { 204 for (file_id, search_range) in search_scope {
205 let text = db.file_text(file_id); 205 let text = sema.db.file_text(file_id);
206 let search_range = 206 let search_range =
207 search_range.unwrap_or(TextRange::up_to(TextSize::of(text.as_str()))); 207 search_range.unwrap_or(TextRange::up_to(TextSize::of(text.as_str())));
208 208
209 let sema = Semantics::new(db);
210 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); 209 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
211 210
212 for (idx, _) in text.match_indices(pat) { 211 for (idx, _) in text.match_indices(pat) {
@@ -222,9 +221,6 @@ impl Definition {
222 continue; 221 continue;
223 }; 222 };
224 223
225 // FIXME: reuse sb
226 // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098
227
228 match classify_name_ref(&sema, &name_ref) { 224 match classify_name_ref(&sema, &name_ref) {
229 Some(NameRefClass::Definition(def)) if &def == self => { 225 Some(NameRefClass::Definition(def)) if &def == self => {
230 let kind = if is_record_lit_name_ref(&name_ref) 226 let kind = if is_record_lit_name_ref(&name_ref)
diff --git a/crates/ra_proc_macro_srv/src/tests/mod.rs b/crates/ra_proc_macro_srv/src/tests/mod.rs
index 82cefbb29..8e6f28abd 100644
--- a/crates/ra_proc_macro_srv/src/tests/mod.rs
+++ b/crates/ra_proc_macro_srv/src/tests/mod.rs
@@ -11,7 +11,7 @@ fn test_derive_serialize_proc_macro() {
11 "serde_derive", 11 "serde_derive",
12 "Serialize", 12 "Serialize",
13 "1.0", 13 "1.0",
14 r##"struct Foo {}"##, 14 r"struct Foo {}",
15 include_str!("fixtures/test_serialize_proc_macro.txt"), 15 include_str!("fixtures/test_serialize_proc_macro.txt"),
16 ); 16 );
17} 17}
@@ -22,9 +22,7 @@ fn test_derive_serialize_proc_macro_failed() {
22 "serde_derive", 22 "serde_derive",
23 "Serialize", 23 "Serialize",
24 "1.0", 24 "1.0",
25 r##" 25 r"struct {}",
26 struct {}
27"##,
28 r##" 26 r##"
29SUBTREE $ 27SUBTREE $
30 IDENT compile_error 4294967295 28 IDENT compile_error 4294967295
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs
index 8d85f2d8a..dcb00671f 100644
--- a/crates/ra_proc_macro_srv/src/tests/utils.rs
+++ b/crates/ra_proc_macro_srv/src/tests/utils.rs
@@ -44,12 +44,12 @@ pub fn assert_expand(
44 crate_name: &str, 44 crate_name: &str,
45 macro_name: &str, 45 macro_name: &str,
46 version: &str, 46 version: &str,
47 fixture: &str, 47 ra_fixture: &str,
48 expect: &str, 48 expect: &str,
49) { 49) {
50 let path = fixtures::dylib_path(crate_name, version); 50 let path = fixtures::dylib_path(crate_name, version);
51 let expander = dylib::Expander::new(&path).unwrap(); 51 let expander = dylib::Expander::new(&path).unwrap();
52 let fixture = parse_string(fixture).unwrap(); 52 let fixture = parse_string(ra_fixture).unwrap();
53 53
54 let res = expander.expand(macro_name, &fixture.subtree, None).unwrap(); 54 let res = expander.expand(macro_name, &fixture.subtree, None).unwrap();
55 assert_eq_text!(&format!("{:?}", res), &expect.trim()); 55 assert_eq_text!(&format!("{:?}", res), &expect.trim());
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 3b124020d..47a1d393d 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -1,6 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ffi::OsStr, ops, path::Path, process::Command}; 3use std::{
4 ffi::OsStr,
5 ops,
6 path::{Path, PathBuf},
7 process::Command,
8};
4 9
5use anyhow::{Context, Result}; 10use anyhow::{Context, Result};
6use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; 11use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
@@ -308,9 +313,13 @@ pub fn load_extern_resources(
308 if let Ok(message) = message { 313 if let Ok(message) = message {
309 match message { 314 match message {
310 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { 315 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
311 let out_dir = AbsPathBuf::assert(out_dir); 316 // cargo_metadata crate returns default (empty) path for
312 res.out_dirs.insert(package_id.clone(), out_dir); 317 // older cargos, which is not absolute, so work around that.
313 res.cfgs.insert(package_id, cfgs); 318 if out_dir != PathBuf::default() {
319 let out_dir = AbsPathBuf::assert(out_dir);
320 res.out_dirs.insert(package_id.clone(), out_dir);
321 res.cfgs.insert(package_id, cfgs);
322 }
314 } 323 }
315 Message::CompilerArtifact(message) => { 324 Message::CompilerArtifact(message) => {
316 if message.target.kind.contains(&"proc-macro".to_string()) { 325 if message.target.kind.contains(&"proc-macro".to_string()) {
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 8b85b4831..8dbf4e6ea 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -288,10 +288,7 @@ impl ProjectWorkspace {
288 if let (Some(&from), Some(&to)) = 288 if let (Some(&from), Some(&to)) =
289 (crates.get(&from_crate_id), crates.get(&to_crate_id)) 289 (crates.get(&from_crate_id), crates.get(&to_crate_id))
290 { 290 {
291 if crate_graph 291 if crate_graph.add_dep(from, dep.name.clone(), to).is_err() {
292 .add_dep(from, CrateName::new(&dep.name).unwrap(), to)
293 .is_err()
294 {
295 log::error!( 292 log::error!(
296 "cyclic dependency {:?} -> {:?}", 293 "cyclic dependency {:?} -> {:?}",
297 from_crate_id, 294 from_crate_id,
@@ -312,13 +309,11 @@ impl ProjectWorkspace {
312 309
313 let env = Env::default(); 310 let env = Env::default();
314 let proc_macro = vec![]; 311 let proc_macro = vec![];
315 let crate_name = CrateName::new(&sysroot[krate].name) 312 let name = sysroot[krate].name.clone();
316 .expect("Sysroot crate names should not contain dashes");
317
318 let crate_id = crate_graph.add_crate_root( 313 let crate_id = crate_graph.add_crate_root(
319 file_id, 314 file_id,
320 Edition::Edition2018, 315 Edition::Edition2018,
321 Some(crate_name), 316 Some(name),
322 cfg_options.clone(), 317 cfg_options.clone(),
323 env, 318 env,
324 proc_macro, 319 proc_macro,
@@ -392,7 +387,7 @@ impl ProjectWorkspace {
392 let crate_id = crate_graph.add_crate_root( 387 let crate_id = crate_graph.add_crate_root(
393 file_id, 388 file_id,
394 edition, 389 edition,
395 Some(CrateName::normalize_dashes(&cargo[pkg].name)), 390 Some(cargo[pkg].name.clone()),
396 cfg_options, 391 cfg_options,
397 env, 392 env,
398 proc_macro.clone(), 393 proc_macro.clone(),
diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs
index 4b5dcd634..b0fe09333 100644
--- a/crates/ra_project_model/src/project_json.rs
+++ b/crates/ra_project_model/src/project_json.rs
@@ -4,13 +4,13 @@ use std::path::PathBuf;
4 4
5use paths::{AbsPath, AbsPathBuf}; 5use paths::{AbsPath, AbsPathBuf};
6use ra_cfg::CfgOptions; 6use ra_cfg::CfgOptions;
7use ra_db::{CrateId, Dependency, Edition}; 7use ra_db::{CrateId, CrateName, Dependency, Edition};
8use rustc_hash::FxHashSet; 8use rustc_hash::FxHashSet;
9use serde::Deserialize; 9use serde::{de, Deserialize};
10use stdx::split_delim; 10use stdx::split_delim;
11 11
12/// Roots and crates that compose this Rust project. 12/// Roots and crates that compose this Rust project.
13#[derive(Clone, Debug)] 13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct ProjectJson { 14pub struct ProjectJson {
15 pub(crate) roots: Vec<Root>, 15 pub(crate) roots: Vec<Root>,
16 pub(crate) crates: Vec<Crate>, 16 pub(crate) crates: Vec<Crate>,
@@ -18,14 +18,14 @@ pub struct ProjectJson {
18 18
19/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in 19/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in
20/// all roots. Roots might be nested. 20/// all roots. Roots might be nested.
21#[derive(Clone, Debug)] 21#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct Root { 22pub struct Root {
23 pub(crate) path: AbsPathBuf, 23 pub(crate) path: AbsPathBuf,
24} 24}
25 25
26/// A crate points to the root module of a crate and lists the dependencies of the crate. This is 26/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
27/// useful in creating the crate graph. 27/// useful in creating the crate graph.
28#[derive(Clone, Debug)] 28#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct Crate { 29pub struct Crate {
30 pub(crate) root_module: AbsPathBuf, 30 pub(crate) root_module: AbsPathBuf,
31 pub(crate) edition: Edition, 31 pub(crate) edition: Edition,
@@ -50,7 +50,7 @@ impl ProjectJson {
50 .into_iter() 50 .into_iter()
51 .map(|dep_data| Dependency { 51 .map(|dep_data| Dependency {
52 crate_id: CrateId(dep_data.krate as u32), 52 crate_id: CrateId(dep_data.krate as u32),
53 name: dep_data.name.into(), 53 name: dep_data.name,
54 }) 54 })
55 .collect::<Vec<_>>(), 55 .collect::<Vec<_>>(),
56 cfg: { 56 cfg: {
@@ -113,5 +113,14 @@ struct DepData {
113 /// Identifies a crate by position in the crates array. 113 /// Identifies a crate by position in the crates array.
114 #[serde(rename = "crate")] 114 #[serde(rename = "crate")]
115 krate: usize, 115 krate: usize,
116 name: String, 116 #[serde(deserialize_with = "deserialize_crate_name")]
117 name: CrateName,
118}
119
120fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error>
121where
122 D: de::Deserializer<'de>,
123{
124 let name = String::deserialize(de)?;
125 CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {:?}", err)))
117} 126}
diff --git a/crates/ra_ssr/src/lib.rs b/crates/ra_ssr/src/lib.rs
index e148f4564..422e15ee6 100644
--- a/crates/ra_ssr/src/lib.rs
+++ b/crates/ra_ssr/src/lib.rs
@@ -9,10 +9,11 @@ mod replacing;
9#[cfg(test)] 9#[cfg(test)]
10mod tests; 10mod tests;
11 11
12use crate::matching::Match; 12pub use crate::matching::Match;
13use crate::matching::{record_match_fails_reasons_scope, MatchFailureReason};
13use hir::Semantics; 14use hir::Semantics;
14use ra_db::{FileId, FileRange}; 15use ra_db::{FileId, FileRange};
15use ra_syntax::{ast, AstNode, SmolStr, SyntaxNode}; 16use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, TextRange};
16use ra_text_edit::TextEdit; 17use ra_text_edit::TextEdit;
17use rustc_hash::FxHashMap; 18use rustc_hash::FxHashMap;
18 19
@@ -26,7 +27,7 @@ pub struct SsrRule {
26} 27}
27 28
28#[derive(Debug)] 29#[derive(Debug)]
29struct SsrPattern { 30pub struct SsrPattern {
30 raw: parsing::RawSearchPattern, 31 raw: parsing::RawSearchPattern,
31 /// Placeholders keyed by the stand-in ident that we use in Rust source code. 32 /// Placeholders keyed by the stand-in ident that we use in Rust source code.
32 placeholders_by_stand_in: FxHashMap<SmolStr, parsing::Placeholder>, 33 placeholders_by_stand_in: FxHashMap<SmolStr, parsing::Placeholder>,
@@ -45,7 +46,7 @@ pub struct SsrError(String);
45 46
46#[derive(Debug, Default)] 47#[derive(Debug, Default)]
47pub struct SsrMatches { 48pub struct SsrMatches {
48 matches: Vec<Match>, 49 pub matches: Vec<Match>,
49} 50}
50 51
51/// Searches a crate for pattern matches and possibly replaces them with something else. 52/// Searches a crate for pattern matches and possibly replaces them with something else.
@@ -64,6 +65,12 @@ impl<'db> MatchFinder<'db> {
64 self.rules.push(rule); 65 self.rules.push(rule);
65 } 66 }
66 67
68 /// Adds a search pattern. For use if you intend to only call `find_matches_in_file`. If you
69 /// intend to do replacement, use `add_rule` instead.
70 pub fn add_search_pattern(&mut self, pattern: SsrPattern) {
71 self.add_rule(SsrRule { pattern, template: "()".parse().unwrap() })
72 }
73
67 pub fn edits_for_file(&self, file_id: FileId) -> Option<TextEdit> { 74 pub fn edits_for_file(&self, file_id: FileId) -> Option<TextEdit> {
68 let matches = self.find_matches_in_file(file_id); 75 let matches = self.find_matches_in_file(file_id);
69 if matches.matches.is_empty() { 76 if matches.matches.is_empty() {
@@ -74,7 +81,7 @@ impl<'db> MatchFinder<'db> {
74 } 81 }
75 } 82 }
76 83
77 fn find_matches_in_file(&self, file_id: FileId) -> SsrMatches { 84 pub fn find_matches_in_file(&self, file_id: FileId) -> SsrMatches {
78 let file = self.sema.parse(file_id); 85 let file = self.sema.parse(file_id);
79 let code = file.syntax(); 86 let code = file.syntax();
80 let mut matches = SsrMatches::default(); 87 let mut matches = SsrMatches::default();
@@ -82,6 +89,32 @@ impl<'db> MatchFinder<'db> {
82 matches 89 matches
83 } 90 }
84 91
92 /// Finds all nodes in `file_id` whose text is exactly equal to `snippet` and attempts to match
93 /// them, while recording reasons why they don't match. This API is useful for command
94 /// line-based debugging where providing a range is difficult.
95 pub fn debug_where_text_equal(&self, file_id: FileId, snippet: &str) -> Vec<MatchDebugInfo> {
96 use ra_db::SourceDatabaseExt;
97 let file = self.sema.parse(file_id);
98 let mut res = Vec::new();
99 let file_text = self.sema.db.file_text(file_id);
100 let mut remaining_text = file_text.as_str();
101 let mut base = 0;
102 let len = snippet.len() as u32;
103 while let Some(offset) = remaining_text.find(snippet) {
104 let start = base + offset as u32;
105 let end = start + len;
106 self.output_debug_for_nodes_at_range(
107 file.syntax(),
108 FileRange { file_id, range: TextRange::new(start.into(), end.into()) },
109 &None,
110 &mut res,
111 );
112 remaining_text = &remaining_text[offset + snippet.len()..];
113 base = end;
114 }
115 res
116 }
117
85 fn find_matches( 118 fn find_matches(
86 &self, 119 &self,
87 code: &SyntaxNode, 120 code: &SyntaxNode,
@@ -128,6 +161,59 @@ impl<'db> MatchFinder<'db> {
128 self.find_matches(&child, restrict_range, matches_out); 161 self.find_matches(&child, restrict_range, matches_out);
129 } 162 }
130 } 163 }
164
165 fn output_debug_for_nodes_at_range(
166 &self,
167 node: &SyntaxNode,
168 range: FileRange,
169 restrict_range: &Option<FileRange>,
170 out: &mut Vec<MatchDebugInfo>,
171 ) {
172 for node in node.children() {
173 let node_range = self.sema.original_range(&node);
174 if node_range.file_id != range.file_id || !node_range.range.contains_range(range.range)
175 {
176 continue;
177 }
178 if node_range.range == range.range {
179 for rule in &self.rules {
180 let pattern =
181 rule.pattern.tree_for_kind_with_reason(node.kind()).map(|p| p.clone());
182 out.push(MatchDebugInfo {
183 matched: matching::get_match(true, rule, &node, restrict_range, &self.sema)
184 .map_err(|e| MatchFailureReason {
185 reason: e.reason.unwrap_or_else(|| {
186 "Match failed, but no reason was given".to_owned()
187 }),
188 }),
189 pattern,
190 node: node.clone(),
191 });
192 }
193 } else if let Some(macro_call) = ast::MacroCall::cast(node.clone()) {
194 if let Some(expanded) = self.sema.expand(&macro_call) {
195 if let Some(tt) = macro_call.token_tree() {
196 self.output_debug_for_nodes_at_range(
197 &expanded,
198 range,
199 &Some(self.sema.original_range(tt.syntax())),
200 out,
201 );
202 }
203 }
204 } else {
205 self.output_debug_for_nodes_at_range(&node, range, restrict_range, out);
206 }
207 }
208 }
209}
210
211pub struct MatchDebugInfo {
212 node: SyntaxNode,
213 /// Our search pattern parsed as the same kind of syntax node as `node`. e.g. expression, item,
214 /// etc. Will be absent if the pattern can't be parsed as that kind.
215 pattern: Result<SyntaxNode, MatchFailureReason>,
216 matched: Result<Match, MatchFailureReason>,
131} 217}
132 218
133impl std::fmt::Display for SsrError { 219impl std::fmt::Display for SsrError {
@@ -136,4 +222,70 @@ impl std::fmt::Display for SsrError {
136 } 222 }
137} 223}
138 224
225impl std::fmt::Debug for MatchDebugInfo {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 write!(f, "========= PATTERN ==========\n")?;
228 match &self.pattern {
229 Ok(pattern) => {
230 write!(f, "{:#?}", pattern)?;
231 }
232 Err(err) => {
233 write!(f, "{}", err.reason)?;
234 }
235 }
236 write!(
237 f,
238 "\n============ AST ===========\n\
239 {:#?}\n============================\n",
240 self.node
241 )?;
242 match &self.matched {
243 Ok(_) => write!(f, "Node matched")?,
244 Err(reason) => write!(f, "Node failed to match because: {}", reason.reason)?,
245 }
246 Ok(())
247 }
248}
249
250impl SsrPattern {
251 fn tree_for_kind_with_reason(
252 &self,
253 kind: SyntaxKind,
254 ) -> Result<&SyntaxNode, MatchFailureReason> {
255 record_match_fails_reasons_scope(true, || self.tree_for_kind(kind))
256 .map_err(|e| MatchFailureReason { reason: e.reason.unwrap() })
257 }
258}
259
260impl SsrMatches {
261 /// Returns `self` with any nested matches removed and made into top-level matches.
262 pub fn flattened(self) -> SsrMatches {
263 let mut out = SsrMatches::default();
264 self.flatten_into(&mut out);
265 out
266 }
267
268 fn flatten_into(self, out: &mut SsrMatches) {
269 for mut m in self.matches {
270 for p in m.placeholder_values.values_mut() {
271 std::mem::replace(&mut p.inner_matches, SsrMatches::default()).flatten_into(out);
272 }
273 out.matches.push(m);
274 }
275 }
276}
277
278impl Match {
279 pub fn matched_text(&self) -> String {
280 self.matched_node.text().to_string()
281 }
282}
283
139impl std::error::Error for SsrError {} 284impl std::error::Error for SsrError {}
285
286#[cfg(test)]
287impl MatchDebugInfo {
288 pub(crate) fn match_failure_reason(&self) -> Option<&str> {
289 self.matched.as_ref().err().map(|r| r.reason.as_str())
290 }
291}
diff --git a/crates/ra_ssr/src/matching.rs b/crates/ra_ssr/src/matching.rs
index 54413a151..53d802e77 100644
--- a/crates/ra_ssr/src/matching.rs
+++ b/crates/ra_ssr/src/matching.rs
@@ -8,9 +8,7 @@ use crate::{
8use hir::Semantics; 8use hir::Semantics;
9use ra_db::FileRange; 9use ra_db::FileRange;
10use ra_syntax::ast::{AstNode, AstToken}; 10use ra_syntax::ast::{AstNode, AstToken};
11use ra_syntax::{ 11use ra_syntax::{ast, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken};
12 ast, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken, TextRange,
13};
14use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
15use std::{cell::Cell, iter::Peekable}; 13use std::{cell::Cell, iter::Peekable};
16 14
@@ -44,8 +42,8 @@ macro_rules! fail_match {
44 42
45/// Information about a match that was found. 43/// Information about a match that was found.
46#[derive(Debug)] 44#[derive(Debug)]
47pub(crate) struct Match { 45pub struct Match {
48 pub(crate) range: TextRange, 46 pub(crate) range: FileRange,
49 pub(crate) matched_node: SyntaxNode, 47 pub(crate) matched_node: SyntaxNode,
50 pub(crate) placeholder_values: FxHashMap<Var, PlaceholderMatch>, 48 pub(crate) placeholder_values: FxHashMap<Var, PlaceholderMatch>,
51 pub(crate) ignored_comments: Vec<ast::Comment>, 49 pub(crate) ignored_comments: Vec<ast::Comment>,
@@ -135,7 +133,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
135 match_state.attempt_match_node(&match_inputs, &pattern_tree, code)?; 133 match_state.attempt_match_node(&match_inputs, &pattern_tree, code)?;
136 match_state.validate_range(&sema.original_range(code))?; 134 match_state.validate_range(&sema.original_range(code))?;
137 match_state.match_out = Some(Match { 135 match_state.match_out = Some(Match {
138 range: sema.original_range(code).range, 136 range: sema.original_range(code),
139 matched_node: code.clone(), 137 matched_node: code.clone(),
140 placeholder_values: FxHashMap::default(), 138 placeholder_values: FxHashMap::default(),
141 ignored_comments: Vec::new(), 139 ignored_comments: Vec::new(),
diff --git a/crates/ra_ssr/src/replacing.rs b/crates/ra_ssr/src/replacing.rs
index 70ce1c185..e43cc5167 100644
--- a/crates/ra_ssr/src/replacing.rs
+++ b/crates/ra_ssr/src/replacing.rs
@@ -21,8 +21,10 @@ fn matches_to_edit_at_offset(
21) -> TextEdit { 21) -> TextEdit {
22 let mut edit_builder = ra_text_edit::TextEditBuilder::default(); 22 let mut edit_builder = ra_text_edit::TextEditBuilder::default();
23 for m in &matches.matches { 23 for m in &matches.matches {
24 edit_builder 24 edit_builder.replace(
25 .replace(m.range.checked_sub(relative_start).unwrap(), render_replace(m, file_src)); 25 m.range.range.checked_sub(relative_start).unwrap(),
26 render_replace(m, file_src),
27 );
26 } 28 }
27 edit_builder.finish() 29 edit_builder.finish()
28} 30}
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs
index 57b2f50b2..c692c97e2 100644
--- a/crates/ra_ssr/src/tests.rs
+++ b/crates/ra_ssr/src/tests.rs
@@ -1,150 +1,5 @@
1use crate::matching::MatchFailureReason; 1use crate::{MatchFinder, SsrRule};
2use crate::{matching, Match, MatchFinder, SsrMatches, SsrPattern, SsrRule}; 2use ra_db::{FileId, SourceDatabaseExt};
3use matching::record_match_fails_reasons_scope;
4use ra_db::{FileId, FileRange, SourceDatabaseExt};
5use ra_syntax::ast::AstNode;
6use ra_syntax::{ast, SyntaxKind, SyntaxNode, TextRange};
7
8struct MatchDebugInfo {
9 node: SyntaxNode,
10 /// Our search pattern parsed as the same kind of syntax node as `node`. e.g. expression, item,
11 /// etc. Will be absent if the pattern can't be parsed as that kind.
12 pattern: Result<SyntaxNode, MatchFailureReason>,
13 matched: Result<Match, MatchFailureReason>,
14}
15
16impl SsrPattern {
17 pub(crate) fn tree_for_kind_with_reason(
18 &self,
19 kind: SyntaxKind,
20 ) -> Result<&SyntaxNode, MatchFailureReason> {
21 record_match_fails_reasons_scope(true, || self.tree_for_kind(kind))
22 .map_err(|e| MatchFailureReason { reason: e.reason.unwrap() })
23 }
24}
25
26impl std::fmt::Debug for MatchDebugInfo {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 write!(f, "========= PATTERN ==========\n")?;
29 match &self.pattern {
30 Ok(pattern) => {
31 write!(f, "{:#?}", pattern)?;
32 }
33 Err(err) => {
34 write!(f, "{}", err.reason)?;
35 }
36 }
37 write!(
38 f,
39 "\n============ AST ===========\n\
40 {:#?}\n============================",
41 self.node
42 )?;
43 match &self.matched {
44 Ok(_) => write!(f, "Node matched")?,
45 Err(reason) => write!(f, "Node failed to match because: {}", reason.reason)?,
46 }
47 Ok(())
48 }
49}
50
51impl SsrMatches {
52 /// Returns `self` with any nested matches removed and made into top-level matches.
53 pub(crate) fn flattened(self) -> SsrMatches {
54 let mut out = SsrMatches::default();
55 self.flatten_into(&mut out);
56 out
57 }
58
59 fn flatten_into(self, out: &mut SsrMatches) {
60 for mut m in self.matches {
61 for p in m.placeholder_values.values_mut() {
62 std::mem::replace(&mut p.inner_matches, SsrMatches::default()).flatten_into(out);
63 }
64 out.matches.push(m);
65 }
66 }
67}
68
69impl Match {
70 pub(crate) fn matched_text(&self) -> String {
71 self.matched_node.text().to_string()
72 }
73}
74
75impl<'db> MatchFinder<'db> {
76 /// Adds a search pattern. For use if you intend to only call `find_matches_in_file`. If you
77 /// intend to do replacement, use `add_rule` instead.
78 fn add_search_pattern(&mut self, pattern: SsrPattern) {
79 self.add_rule(SsrRule { pattern, template: "()".parse().unwrap() })
80 }
81
82 /// Finds all nodes in `file_id` whose text is exactly equal to `snippet` and attempts to match
83 /// them, while recording reasons why they don't match. This API is useful for command
84 /// line-based debugging where providing a range is difficult.
85 fn debug_where_text_equal(&self, file_id: FileId, snippet: &str) -> Vec<MatchDebugInfo> {
86 let file = self.sema.parse(file_id);
87 let mut res = Vec::new();
88 let file_text = self.sema.db.file_text(file_id);
89 let mut remaining_text = file_text.as_str();
90 let mut base = 0;
91 let len = snippet.len() as u32;
92 while let Some(offset) = remaining_text.find(snippet) {
93 let start = base + offset as u32;
94 let end = start + len;
95 self.output_debug_for_nodes_at_range(
96 file.syntax(),
97 TextRange::new(start.into(), end.into()),
98 &None,
99 &mut res,
100 );
101 remaining_text = &remaining_text[offset + snippet.len()..];
102 base = end;
103 }
104 res
105 }
106
107 fn output_debug_for_nodes_at_range(
108 &self,
109 node: &SyntaxNode,
110 range: TextRange,
111 restrict_range: &Option<FileRange>,
112 out: &mut Vec<MatchDebugInfo>,
113 ) {
114 for node in node.children() {
115 if !node.text_range().contains_range(range) {
116 continue;
117 }
118 if node.text_range() == range {
119 for rule in &self.rules {
120 let pattern =
121 rule.pattern.tree_for_kind_with_reason(node.kind()).map(|p| p.clone());
122 out.push(MatchDebugInfo {
123 matched: matching::get_match(true, rule, &node, restrict_range, &self.sema)
124 .map_err(|e| MatchFailureReason {
125 reason: e.reason.unwrap_or_else(|| {
126 "Match failed, but no reason was given".to_owned()
127 }),
128 }),
129 pattern,
130 node: node.clone(),
131 });
132 }
133 } else if let Some(macro_call) = ast::MacroCall::cast(node.clone()) {
134 if let Some(expanded) = self.sema.expand(&macro_call) {
135 if let Some(tt) = macro_call.token_tree() {
136 self.output_debug_for_nodes_at_range(
137 &expanded,
138 range,
139 &Some(self.sema.original_range(tt.syntax())),
140 out,
141 );
142 }
143 }
144 }
145 }
146 }
147}
148 3
149fn parse_error_text(query: &str) -> String { 4fn parse_error_text(query: &str) -> String {
150 format!("{}", query.parse::<SsrRule>().unwrap_err()) 5 format!("{}", query.parse::<SsrRule>().unwrap_err())
@@ -260,6 +115,19 @@ fn assert_no_match(pattern: &str, code: &str) {
260 assert_matches(pattern, code, &[]); 115 assert_matches(pattern, code, &[]);
261} 116}
262 117
118fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) {
119 let (db, file_id) = single_file(code);
120 let mut match_finder = MatchFinder::new(&db);
121 match_finder.add_search_pattern(pattern.parse().unwrap());
122 let mut reasons = Vec::new();
123 for d in match_finder.debug_where_text_equal(file_id, snippet) {
124 if let Some(reason) = d.match_failure_reason() {
125 reasons.push(reason.to_owned());
126 }
127 }
128 assert_eq!(reasons, vec![expected_reason]);
129}
130
263#[test] 131#[test]
264fn ssr_function_to_method() { 132fn ssr_function_to_method() {
265 assert_ssr_transform( 133 assert_ssr_transform(
@@ -623,3 +491,30 @@ fn preserves_whitespace_within_macro_expansion() {
623 fn f() {macro1!(4 - 3 - 1 * 2}"#, 491 fn f() {macro1!(4 - 3 - 1 * 2}"#,
624 ) 492 )
625} 493}
494
495#[test]
496fn match_failure_reasons() {
497 let code = r#"
498 macro_rules! foo {
499 ($a:expr) => {
500 1 + $a + 2
501 };
502 }
503 fn f1() {
504 bar(1, 2);
505 foo!(5 + 43.to_string() + 5);
506 }
507 "#;
508 assert_match_failure_reason(
509 "bar($a, 3)",
510 code,
511 "bar(1, 2)",
512 r#"Pattern wanted token '3' (INT_NUMBER), but code had token '2' (INT_NUMBER)"#,
513 );
514 assert_match_failure_reason(
515 "42.to_string()",
516 code,
517 "43.to_string()",
518 r#"Pattern wanted token '42' (INT_NUMBER), but code had token '43' (INT_NUMBER)"#,
519 );
520}
diff --git a/crates/ra_syntax/src/tests.rs b/crates/ra_syntax/src/tests.rs
index 959967b79..7b4232497 100644
--- a/crates/ra_syntax/src/tests.rs
+++ b/crates/ra_syntax/src/tests.rs
@@ -1,9 +1,11 @@
1use std::{ 1use std::{
2 env,
2 fmt::Write, 3 fmt::Write,
4 fs,
3 path::{Component, Path, PathBuf}, 5 path::{Component, Path, PathBuf},
4}; 6};
5 7
6use test_utils::{collect_rust_files, dir_tests, project_dir, read_text}; 8use test_utils::{assert_eq_text, project_dir};
7 9
8use crate::{fuzz, tokenize, SourceFile, SyntaxError, TextRange, TextSize, Token}; 10use crate::{fuzz, tokenize, SourceFile, SyntaxError, TextRange, TextSize, Token};
9 11
@@ -200,3 +202,99 @@ where
200 } 202 }
201 }); 203 });
202} 204}
205
206/// Calls callback `f` with input code and file paths for each `.rs` file in `test_data_dir`
207/// subdirectories defined by `paths`.
208///
209/// If the content of the matching output file differs from the output of `f()`
210/// the test will fail.
211///
212/// If there is no matching output file it will be created and filled with the
213/// output of `f()`, but the test will fail.
214fn dir_tests<F>(test_data_dir: &Path, paths: &[&str], outfile_extension: &str, f: F)
215where
216 F: Fn(&str, &Path) -> String,
217{
218 for (path, input_code) in collect_rust_files(test_data_dir, paths) {
219 let actual = f(&input_code, &path);
220 let path = path.with_extension(outfile_extension);
221 if !path.exists() {
222 println!("\nfile: {}", path.display());
223 println!("No .txt file with expected result, creating...\n");
224 println!("{}\n{}", input_code, actual);
225 fs::write(&path, &actual).unwrap();
226 panic!("No expected result");
227 }
228 let expected = read_text(&path);
229 assert_equal_text(&expected, &actual, &path);
230 }
231}
232
233/// Collects all `.rs` files from `dir` subdirectories defined by `paths`.
234fn collect_rust_files(root_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)> {
235 paths
236 .iter()
237 .flat_map(|path| {
238 let path = root_dir.to_owned().join(path);
239 rust_files_in_dir(&path).into_iter()
240 })
241 .map(|path| {
242 let text = read_text(&path);
243 (path, text)
244 })
245 .collect()
246}
247
248/// Collects paths to all `.rs` files from `dir` in a sorted `Vec<PathBuf>`.
249fn rust_files_in_dir(dir: &Path) -> Vec<PathBuf> {
250 let mut acc = Vec::new();
251 for file in fs::read_dir(&dir).unwrap() {
252 let file = file.unwrap();
253 let path = file.path();
254 if path.extension().unwrap_or_default() == "rs" {
255 acc.push(path);
256 }
257 }
258 acc.sort();
259 acc
260}
261
262/// Asserts that `expected` and `actual` strings are equal. If they differ only
263/// in trailing or leading whitespace the test won't fail and
264/// the contents of `actual` will be written to the file located at `path`.
265fn assert_equal_text(expected: &str, actual: &str, path: &Path) {
266 if expected == actual {
267 return;
268 }
269 let dir = project_dir();
270 let pretty_path = path.strip_prefix(&dir).unwrap_or_else(|_| path);
271 if expected.trim() == actual.trim() {
272 println!("whitespace difference, rewriting");
273 println!("file: {}\n", pretty_path.display());
274 fs::write(path, actual).unwrap();
275 return;
276 }
277 if env::var("UPDATE_EXPECT").is_ok() {
278 println!("rewriting {}", pretty_path.display());
279 fs::write(path, actual).unwrap();
280 return;
281 }
282 assert_eq_text!(expected, actual, "file: {}", pretty_path.display());
283}
284
285/// Read file and normalize newlines.
286///
287/// `rustc` seems to always normalize `\r\n` newlines to `\n`:
288///
289/// ```
290/// let s = "
291/// ";
292/// assert_eq!(s.as_bytes(), &[10]);
293/// ```
294///
295/// so this should always be correct.
296fn read_text(path: &Path) -> String {
297 fs::read_to_string(path)
298 .unwrap_or_else(|_| panic!("File at {:?} should be valid", path))
299 .replace("\r\n", "\n")
300}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 803755106..837b6714d 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -28,6 +28,7 @@ rustc-hash = "1.1.0"
28serde = { version = "1.0.106", features = ["derive"] } 28serde = { version = "1.0.106", features = ["derive"] }
29serde_json = "1.0.48" 29serde_json = "1.0.48"
30threadpool = "1.7.1" 30threadpool = "1.7.1"
31rayon = "1.3.1"
31 32
32stdx = { path = "../stdx" } 33stdx = { path = "../stdx" }
33 34
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs
index cf1108e12..8c0f4df8b 100644
--- a/crates/rust-analyzer/src/bin/args.rs
+++ b/crates/rust-analyzer/src/bin/args.rs
@@ -5,7 +5,7 @@
5 5
6use anyhow::{bail, Result}; 6use anyhow::{bail, Result};
7use pico_args::Arguments; 7use pico_args::Arguments;
8use ra_ssr::SsrRule; 8use ra_ssr::{SsrPattern, SsrRule};
9use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; 9use rust_analyzer::cli::{BenchWhat, Position, Verbosity};
10 10
11use std::{fmt::Write, path::PathBuf}; 11use std::{fmt::Write, path::PathBuf};
@@ -25,6 +25,7 @@ pub(crate) enum Command {
25 }, 25 },
26 Stats { 26 Stats {
27 randomize: bool, 27 randomize: bool,
28 parallel: bool,
28 memory_usage: bool, 29 memory_usage: bool,
29 only: Option<String>, 30 only: Option<String>,
30 with_deps: bool, 31 with_deps: bool,
@@ -49,6 +50,10 @@ pub(crate) enum Command {
49 Ssr { 50 Ssr {
50 rules: Vec<SsrRule>, 51 rules: Vec<SsrRule>,
51 }, 52 },
53 StructuredSearch {
54 debug_snippet: Option<String>,
55 patterns: Vec<SsrPattern>,
56 },
52 ProcMacro, 57 ProcMacro,
53 RunServer, 58 RunServer,
54 Version, 59 Version,
@@ -157,10 +162,14 @@ USAGE:
157 rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH] 162 rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH]
158 163
159FLAGS: 164FLAGS:
165 -o, --only Only analyze items matching this path
160 -h, --help Prints help information 166 -h, --help Prints help information
161 --memory-usage 167 --memory-usage Collect memory usage statistics (requires `--feature jemalloc`)
168 --randomize Randomize order in which crates, modules, and items are processed
169 --parallel Run type inference in parallel
162 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis 170 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
163 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding 171 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding
172 --with-deps Also analyze all dependencies
164 -v, --verbose 173 -v, --verbose
165 -q, --quiet 174 -q, --quiet
166 175
@@ -174,6 +183,7 @@ ARGS:
174 } 183 }
175 184
176 let randomize = matches.contains("--randomize"); 185 let randomize = matches.contains("--randomize");
186 let parallel = matches.contains("--parallel");
177 let memory_usage = matches.contains("--memory-usage"); 187 let memory_usage = matches.contains("--memory-usage");
178 let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; 188 let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
179 let with_deps: bool = matches.contains("--with-deps"); 189 let with_deps: bool = matches.contains("--with-deps");
@@ -189,6 +199,7 @@ ARGS:
189 199
190 Command::Stats { 200 Command::Stats {
191 randomize, 201 randomize,
202 parallel,
192 memory_usage, 203 memory_usage,
193 only, 204 only,
194 with_deps, 205 with_deps,
@@ -209,7 +220,7 @@ USAGE:
209FLAGS: 220FLAGS:
210 -h, --help Prints help information 221 -h, --help Prints help information
211 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis 222 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
212 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding 223 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding
213 -v, --verbose 224 -v, --verbose
214 225
215OPTIONS: 226OPTIONS:
@@ -287,6 +298,7 @@ EXAMPLE:
287 rust-analyzer ssr '$a.foo($b) ==> bar($a, $b)' 298 rust-analyzer ssr '$a.foo($b) ==> bar($a, $b)'
288 299
289FLAGS: 300FLAGS:
301 --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet>
290 -h, --help Prints help information 302 -h, --help Prints help information
291 303
292ARGS: 304ARGS:
@@ -300,6 +312,34 @@ ARGS:
300 } 312 }
301 Command::Ssr { rules } 313 Command::Ssr { rules }
302 } 314 }
315 "search" => {
316 if matches.contains(["-h", "--help"]) {
317 eprintln!(
318 "\
319rust-analyzer search
320
321USAGE:
322 rust-analyzer search [FLAGS] [PATTERN...]
323
324EXAMPLE:
325 rust-analyzer search '$a.foo($b)'
326
327FLAGS:
328 --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet>
329 -h, --help Prints help information
330
331ARGS:
332 <PATTERN> A structured search pattern"
333 );
334 return Ok(Err(HelpPrinted));
335 }
336 let debug_snippet = matches.opt_value_from_str("--debug")?;
337 let mut patterns = Vec::new();
338 while let Some(rule) = matches.free_from_str()? {
339 patterns.push(rule);
340 }
341 Command::StructuredSearch { patterns, debug_snippet }
342 }
303 _ => { 343 _ => {
304 print_subcommands(); 344 print_subcommands();
305 return Ok(Err(HelpPrinted)); 345 return Ok(Err(HelpPrinted));
@@ -327,6 +367,7 @@ SUBCOMMANDS:
327 diagnostics 367 diagnostics
328 proc-macro 368 proc-macro
329 parse 369 parse
370 search
330 ssr 371 ssr
331 symbols" 372 symbols"
332 ) 373 )
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 16882fc13..eec76d415 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -32,6 +32,7 @@ fn main() -> Result<()> {
32 args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, 32 args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
33 args::Command::Stats { 33 args::Command::Stats {
34 randomize, 34 randomize,
35 parallel,
35 memory_usage, 36 memory_usage,
36 only, 37 only,
37 with_deps, 38 with_deps,
@@ -45,6 +46,7 @@ fn main() -> Result<()> {
45 only.as_ref().map(String::as_ref), 46 only.as_ref().map(String::as_ref),
46 with_deps, 47 with_deps,
47 randomize, 48 randomize,
49 parallel,
48 load_output_dirs, 50 load_output_dirs,
49 with_proc_macro, 51 with_proc_macro,
50 )?, 52 )?,
@@ -63,6 +65,9 @@ fn main() -> Result<()> {
63 args::Command::Ssr { rules } => { 65 args::Command::Ssr { rules } => {
64 cli::apply_ssr_rules(rules)?; 66 cli::apply_ssr_rules(rules)?;
65 } 67 }
68 args::Command::StructuredSearch { patterns, debug_snippet } => {
69 cli::search_for_patterns(patterns, debug_snippet)?;
70 }
66 args::Command::Version => println!("rust-analyzer {}", env!("REV")), 71 args::Command::Version => println!("rust-analyzer {}", env!("REV")),
67 } 72 }
68 Ok(()) 73 Ok(())
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs
index 13e3d75be..6863f100b 100644
--- a/crates/rust-analyzer/src/cli.rs
+++ b/crates/rust-analyzer/src/cli.rs
@@ -18,7 +18,7 @@ pub use analysis_bench::{analysis_bench, BenchWhat, Position};
18pub use analysis_stats::analysis_stats; 18pub use analysis_stats::analysis_stats;
19pub use diagnostics::diagnostics; 19pub use diagnostics::diagnostics;
20pub use load_cargo::load_cargo; 20pub use load_cargo::load_cargo;
21pub use ssr::apply_ssr_rules; 21pub use ssr::{apply_ssr_rules, search_for_patterns};
22 22
23#[derive(Clone, Copy)] 23#[derive(Clone, Copy)]
24pub enum Verbosity { 24pub enum Verbosity {
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 9d09501cd..14982919d 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -5,6 +5,7 @@ use std::{path::Path, time::Instant};
5 5
6use itertools::Itertools; 6use itertools::Itertools;
7use rand::{seq::SliceRandom, thread_rng}; 7use rand::{seq::SliceRandom, thread_rng};
8use rayon::prelude::*;
8use rustc_hash::FxHashSet; 9use rustc_hash::FxHashSet;
9 10
10use hir::{ 11use hir::{
@@ -13,12 +14,23 @@ use hir::{
13}; 14};
14use hir_def::FunctionId; 15use hir_def::FunctionId;
15use hir_ty::{Ty, TypeWalk}; 16use hir_ty::{Ty, TypeWalk};
16use ra_db::SourceDatabaseExt; 17use ra_db::{
18 salsa::{self, ParallelDatabase},
19 SourceDatabaseExt,
20};
17use ra_syntax::AstNode; 21use ra_syntax::AstNode;
18use stdx::format_to; 22use stdx::format_to;
19 23
20use crate::cli::{load_cargo::load_cargo, progress_report::ProgressReport, Result, Verbosity}; 24use crate::cli::{load_cargo::load_cargo, progress_report::ProgressReport, Result, Verbosity};
21 25
26/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
27struct Snap<DB>(DB);
28impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
29 fn clone(&self) -> Snap<salsa::Snapshot<DB>> {
30 Snap(self.0.snapshot())
31 }
32}
33
22pub fn analysis_stats( 34pub fn analysis_stats(
23 verbosity: Verbosity, 35 verbosity: Verbosity,
24 memory_usage: bool, 36 memory_usage: bool,
@@ -26,6 +38,7 @@ pub fn analysis_stats(
26 only: Option<&str>, 38 only: Option<&str>,
27 with_deps: bool, 39 with_deps: bool,
28 randomize: bool, 40 randomize: bool,
41 parallel: bool,
29 load_output_dirs: bool, 42 load_output_dirs: bool,
30 with_proc_macro: bool, 43 with_proc_macro: bool,
31) -> Result<()> { 44) -> Result<()> {
@@ -91,12 +104,26 @@ pub fn analysis_stats(
91 funcs.shuffle(&mut thread_rng()); 104 funcs.shuffle(&mut thread_rng());
92 } 105 }
93 106
94 let inference_time = Instant::now();
95 let mut bar = match verbosity { 107 let mut bar = match verbosity {
96 Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), 108 Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
97 _ => ProgressReport::new(funcs.len() as u64), 109 _ => ProgressReport::new(funcs.len() as u64),
98 }; 110 };
99 111
112 if parallel {
113 let inference_time = Instant::now();
114 let snap = Snap(db.snapshot());
115 funcs
116 .par_iter()
117 .map_with(snap, |snap, &f| {
118 let f_id = FunctionId::from(f);
119 snap.0.body(f_id.into());
120 snap.0.infer(f_id.into());
121 })
122 .count();
123 println!("Parallel Inference: {:?}, {}", inference_time.elapsed(), ra_prof::memory_usage());
124 }
125
126 let inference_time = Instant::now();
100 bar.tick(); 127 bar.tick();
101 let mut num_exprs = 0; 128 let mut num_exprs = 0;
102 let mut num_exprs_unknown = 0; 129 let mut num_exprs_unknown = 0;
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs
index a5265ac15..4fb829ea5 100644
--- a/crates/rust-analyzer/src/cli/ssr.rs
+++ b/crates/rust-analyzer/src/cli/ssr.rs
@@ -2,7 +2,7 @@
2 2
3use crate::cli::{load_cargo::load_cargo, Result}; 3use crate::cli::{load_cargo::load_cargo, Result};
4use ra_ide::SourceFileEdit; 4use ra_ide::SourceFileEdit;
5use ra_ssr::{MatchFinder, SsrRule}; 5use ra_ssr::{MatchFinder, SsrPattern, SsrRule};
6 6
7pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> { 7pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> {
8 use ra_db::SourceDatabaseExt; 8 use ra_db::SourceDatabaseExt;
@@ -31,3 +31,41 @@ pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> {
31 } 31 }
32 Ok(()) 32 Ok(())
33} 33}
34
35/// Searches for `patterns`, printing debug information for any nodes whose text exactly matches
36/// `debug_snippet`. This is intended for debugging and probably isn't in it's current form useful
37/// for much else.
38pub fn search_for_patterns(patterns: Vec<SsrPattern>, debug_snippet: Option<String>) -> Result<()> {
39 use ra_db::SourceDatabaseExt;
40 use ra_ide_db::symbol_index::SymbolsDatabase;
41 let (host, vfs) = load_cargo(&std::env::current_dir()?, true, true)?;
42 let db = host.raw_database();
43 let mut match_finder = MatchFinder::new(db);
44 for pattern in patterns {
45 match_finder.add_search_pattern(pattern);
46 }
47 for &root in db.local_roots().iter() {
48 let sr = db.source_root(root);
49 for file_id in sr.iter() {
50 if let Some(debug_snippet) = &debug_snippet {
51 for debug_info in match_finder.debug_where_text_equal(file_id, debug_snippet) {
52 println!("{:#?}", debug_info);
53 }
54 } else {
55 let matches = match_finder.find_matches_in_file(file_id);
56 if !matches.matches.is_empty() {
57 let matches = matches.flattened().matches;
58 if let Some(path) = vfs.file_path(file_id).as_path() {
59 println!("{} matches in '{}'", matches.len(), path.to_string_lossy());
60 }
61 // We could possibly at some point do something more useful than just printing
62 // the matched text. For now though, that's the easiest thing to do.
63 for m in matches {
64 println!("{}", m.matched_text());
65 }
66 }
67 }
68 }
69 }
70 Ok(())
71}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 6b17ce18b..6c311648a 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -44,7 +44,7 @@ pub struct Config {
44 pub root_path: AbsPathBuf, 44 pub root_path: AbsPathBuf,
45} 45}
46 46
47#[derive(Debug, Clone)] 47#[derive(Debug, Clone, Eq, PartialEq)]
48pub enum LinkedProject { 48pub enum LinkedProject {
49 ProjectManifest(ProjectManifest), 49 ProjectManifest(ProjectManifest),
50 InlineJsonProject(ProjectJson), 50 InlineJsonProject(ProjectJson),
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index b8aa1e5b5..b7b4edf66 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -27,7 +27,7 @@ use crate::{
27 Result, 27 Result,
28}; 28};
29 29
30#[derive(Eq, PartialEq)] 30#[derive(Eq, PartialEq, Copy, Clone)]
31pub(crate) enum Status { 31pub(crate) enum Status {
32 Loading, 32 Loading,
33 Ready, 33 Ready,
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 25bcd80af..3cb532b62 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -415,7 +415,7 @@ pub(crate) fn handle_runnables(
415 let source_file = snap.analysis.parse(file_id)?; 415 let source_file = snap.analysis.parse(file_id)?;
416 algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset) 416 algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset)
417 .and_then(|it| it.path()?.segment()?.name_ref()) 417 .and_then(|it| it.path()?.segment()?.name_ref())
418 .map_or(false, |it| it.text() == "expect") 418 .map_or(false, |it| it.text() == "expect" || it.text() == "expect_file")
419 } 419 }
420 None => false, 420 None => false,
421 }; 421 };
@@ -1045,7 +1045,7 @@ pub(crate) fn handle_call_hierarchy_incoming(
1045 let item = params.item; 1045 let item = params.item;
1046 1046
1047 let doc = TextDocumentIdentifier::new(item.uri); 1047 let doc = TextDocumentIdentifier::new(item.uri);
1048 let frange = from_proto::file_range(&snap, doc, item.range)?; 1048 let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
1049 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 1049 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1050 1050
1051 let call_items = match snap.analysis.incoming_calls(fpos)? { 1051 let call_items = match snap.analysis.incoming_calls(fpos)? {
@@ -1080,7 +1080,7 @@ pub(crate) fn handle_call_hierarchy_outgoing(
1080 let item = params.item; 1080 let item = params.item;
1081 1081
1082 let doc = TextDocumentIdentifier::new(item.uri); 1082 let doc = TextDocumentIdentifier::new(item.uri);
1083 let frange = from_proto::file_range(&snap, doc, item.range)?; 1083 let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
1084 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 1084 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1085 1085
1086 let call_items = match snap.analysis.outgoing_calls(fpos)? { 1086 let call_items = match snap.analysis.outgoing_calls(fpos)? {
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 1befe678c..82207bbb8 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -14,12 +14,12 @@ impl Request for AnalyzerStatus {
14 const METHOD: &'static str = "rust-analyzer/analyzerStatus"; 14 const METHOD: &'static str = "rust-analyzer/analyzerStatus";
15} 15}
16 16
17pub enum CollectGarbage {} 17pub enum ReloadWorkspace {}
18 18
19impl Request for CollectGarbage { 19impl Request for ReloadWorkspace {
20 type Params = (); 20 type Params = ();
21 type Result = (); 21 type Result = ();
22 const METHOD: &'static str = "rust-analyzer/collectGarbage"; 22 const METHOD: &'static str = "rust-analyzer/reloadWorkspace";
23} 23}
24 24
25pub enum SyntaxTree {} 25pub enum SyntaxTree {}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 9fd16ef3b..e03038b25 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -136,7 +136,7 @@ impl GlobalState {
136 log::info!("queued count = {}", queue_count); 136 log::info!("queued count = {}", queue_count);
137 } 137 }
138 138
139 let mut became_ready = false; 139 let prev_status = self.status;
140 match event { 140 match event {
141 Event::Lsp(msg) => match msg { 141 Event::Lsp(msg) => match msg {
142 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, 142 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?,
@@ -168,22 +168,26 @@ impl GlobalState {
168 } 168 }
169 } 169 }
170 vfs::loader::Message::Progress { n_total, n_done } => { 170 vfs::loader::Message::Progress { n_total, n_done } => {
171 let state = if n_done == 0 { 171 if n_total == 0 {
172 Progress::Begin
173 } else if n_done < n_total {
174 Progress::Report
175 } else {
176 assert_eq!(n_done, n_total);
177 self.status = Status::Ready; 172 self.status = Status::Ready;
178 became_ready = true; 173 } else {
179 Progress::End 174 let state = if n_done == 0 {
180 }; 175 self.status = Status::Loading;
181 self.report_progress( 176 Progress::Begin
182 "roots scanned", 177 } else if n_done < n_total {
183 state, 178 Progress::Report
184 Some(format!("{}/{}", n_done, n_total)), 179 } else {
185 Some(Progress::percentage(n_done, n_total)), 180 assert_eq!(n_done, n_total);
186 ) 181 self.status = Status::Ready;
182 Progress::End
183 };
184 self.report_progress(
185 "roots scanned",
186 state,
187 Some(format!("{}/{}", n_done, n_total)),
188 Some(Progress::percentage(n_done, n_total)),
189 )
190 }
187 } 191 }
188 }, 192 },
189 Event::Flycheck(task) => match task { 193 Event::Flycheck(task) => match task {
@@ -231,13 +235,13 @@ impl GlobalState {
231 } 235 }
232 236
233 let state_changed = self.process_changes(); 237 let state_changed = self.process_changes();
234 if became_ready { 238 if prev_status == Status::Loading && self.status == Status::Ready {
235 if let Some(flycheck) = &self.flycheck { 239 if let Some(flycheck) = &self.flycheck {
236 flycheck.handle.update(); 240 flycheck.handle.update();
237 } 241 }
238 } 242 }
239 243
240 if self.status == Status::Ready && (state_changed || became_ready) { 244 if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) {
241 let subscriptions = self 245 let subscriptions = self
242 .mem_docs 246 .mem_docs
243 .iter() 247 .iter()
@@ -274,7 +278,7 @@ impl GlobalState {
274 self.register_request(&req, request_received); 278 self.register_request(&req, request_received);
275 279
276 RequestDispatcher { req: Some(req), global_state: self } 280 RequestDispatcher { req: Some(req), global_state: self }
277 .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))? 281 .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.reload()))?
278 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? 282 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
279 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? 283 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
280 .on_sync::<lsp_types::request::Shutdown>(|_, ()| Ok(()))? 284 .on_sync::<lsp_types::request::Shutdown>(|_, ()| Ok(()))?
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index ec71f8b29..0c1fd1b8b 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -19,11 +19,14 @@ impl GlobalState {
19 if self.config.lru_capacity != old_config.lru_capacity { 19 if self.config.lru_capacity != old_config.lru_capacity {
20 self.analysis_host.update_lru_capacity(old_config.lru_capacity); 20 self.analysis_host.update_lru_capacity(old_config.lru_capacity);
21 } 21 }
22 if self.config.flycheck != old_config.flycheck { 22 if self.config.linked_projects != old_config.linked_projects {
23 self.reload()
24 } else if self.config.flycheck != old_config.flycheck {
23 self.reload_flycheck(); 25 self.reload_flycheck();
24 } 26 }
25 } 27 }
26 pub(crate) fn reload(&mut self) { 28 pub(crate) fn reload(&mut self) {
29 log::info!("reloading projects: {:?}", self.config.linked_projects);
27 let workspaces = { 30 let workspaces = {
28 if self.config.linked_projects.is_empty() 31 if self.config.linked_projects.is_empty()
29 && self.config.notifications.cargo_toml_not_found 32 && self.config.notifications.cargo_toml_not_found
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index a0a58f689..95dd8e408 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -352,7 +352,7 @@ pub(crate) fn folding_range(
352 let kind = match fold.kind { 352 let kind = match fold.kind {
353 FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), 353 FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
354 FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), 354 FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
355 FoldKind::Mods | FoldKind::Block => None, 355 FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None,
356 }; 356 };
357 357
358 let range = range(line_index, fold.range); 358 let range = range(line_index, fold.range);
@@ -685,32 +685,27 @@ pub(crate) fn runnable(
685 685
686#[cfg(test)] 686#[cfg(test)]
687mod tests { 687mod tests {
688 use test_utils::extract_ranges; 688 use ra_ide::Analysis;
689 689
690 use super::*; 690 use super::*;
691 691
692 #[test] 692 #[test]
693 fn conv_fold_line_folding_only_fixup() { 693 fn conv_fold_line_folding_only_fixup() {
694 let text = r#"<fold>mod a; 694 let text = r#"mod a;
695mod b; 695mod b;
696mod c;</fold> 696mod c;
697 697
698fn main() <fold>{ 698fn main() {
699 if cond <fold>{ 699 if cond {
700 a::do_a(); 700 a::do_a();
701 }</fold> else <fold>{ 701 } else {
702 b::do_b(); 702 b::do_b();
703 }</fold> 703 }
704}</fold>"#; 704}"#;
705 705
706 let (ranges, text) = extract_ranges(text, "fold"); 706 let (analysis, file_id) = Analysis::from_single_file(text.to_string());
707 assert_eq!(ranges.len(), 4); 707 let folds = analysis.folding_ranges(file_id).unwrap();
708 let folds = vec![ 708 assert_eq!(folds.len(), 4);
709 Fold { range: ranges[0], kind: FoldKind::Mods },
710 Fold { range: ranges[1], kind: FoldKind::Block },
711 Fold { range: ranges[2], kind: FoldKind::Block },
712 Fold { range: ranges[3], kind: FoldKind::Block },
713 ];
714 709
715 let line_index = LineIndex::new(&text); 710 let line_index = LineIndex::new(&text);
716 let converted: Vec<lsp_types::FoldingRange> = 711 let converted: Vec<lsp_types::FoldingRange> =
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index e32a0a0c3..e4aa894ac 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -13,7 +13,7 @@ mod fixture;
13use std::{ 13use std::{
14 convert::{TryFrom, TryInto}, 14 convert::{TryFrom, TryInto},
15 env, fs, 15 env, fs,
16 path::{Path, PathBuf}, 16 path::PathBuf,
17}; 17};
18 18
19use serde_json::Value; 19use serde_json::Value;
@@ -118,8 +118,8 @@ pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) {
118} 118}
119 119
120/// Extracts ranges, marked with `<tag> </tag>` pairs from the `text` 120/// Extracts ranges, marked with `<tag> </tag>` pairs from the `text`
121pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) { 121pub fn extract_tags(mut text: &str, tag: &str) -> (Vec<(TextRange, Option<String>)>, String) {
122 let open = format!("<{}>", tag); 122 let open = format!("<{}", tag);
123 let close = format!("</{}>", tag); 123 let close = format!("</{}>", tag);
124 let mut ranges = Vec::new(); 124 let mut ranges = Vec::new();
125 let mut res = String::new(); 125 let mut res = String::new();
@@ -134,22 +134,35 @@ pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) {
134 res.push_str(&text[..i]); 134 res.push_str(&text[..i]);
135 text = &text[i..]; 135 text = &text[i..];
136 if text.starts_with(&open) { 136 if text.starts_with(&open) {
137 text = &text[open.len()..]; 137 let close_open = text.find('>').unwrap();
138 let attr = text[open.len()..close_open].trim();
139 let attr = if attr.is_empty() { None } else { Some(attr.to_string()) };
140 text = &text[close_open + '>'.len_utf8()..];
138 let from = TextSize::of(&res); 141 let from = TextSize::of(&res);
139 stack.push(from); 142 stack.push((from, attr));
140 } else if text.starts_with(&close) { 143 } else if text.starts_with(&close) {
141 text = &text[close.len()..]; 144 text = &text[close.len()..];
142 let from = stack.pop().unwrap_or_else(|| panic!("unmatched </{}>", tag)); 145 let (from, attr) =
146 stack.pop().unwrap_or_else(|| panic!("unmatched </{}>", tag));
143 let to = TextSize::of(&res); 147 let to = TextSize::of(&res);
144 ranges.push(TextRange::new(from, to)); 148 ranges.push((TextRange::new(from, to), attr));
149 } else {
150 res.push('<');
151 text = &text['<'.len_utf8()..];
145 } 152 }
146 } 153 }
147 } 154 }
148 } 155 }
149 assert!(stack.is_empty(), "unmatched <{}>", tag); 156 assert!(stack.is_empty(), "unmatched <{}>", tag);
150 ranges.sort_by_key(|r| (r.start(), r.end())); 157 ranges.sort_by_key(|r| (r.0.start(), r.0.end()));
151 (ranges, res) 158 (ranges, res)
152} 159}
160#[test]
161fn test_extract_tags() {
162 let (tags, text) = extract_tags(r#"<tag fn>fn <tag>main</tag>() {}</tag>"#, "tag");
163 let actual = tags.into_iter().map(|(range, attr)| (&text[range], attr)).collect::<Vec<_>>();
164 assert_eq!(actual, vec![("fn main() {}", Some("fn".into())), ("main", None),]);
165}
153 166
154/// Inserts `<|>` marker into the `text` at `offset`. 167/// Inserts `<|>` marker into the `text` at `offset`.
155pub fn add_cursor(text: &str, offset: TextSize) -> String { 168pub fn add_cursor(text: &str, offset: TextSize) -> String {
@@ -299,85 +312,6 @@ pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a
299 } 312 }
300} 313}
301 314
302/// Calls callback `f` with input code and file paths for each `.rs` file in `test_data_dir`
303/// subdirectories defined by `paths`.
304///
305/// If the content of the matching output file differs from the output of `f()`
306/// the test will fail.
307///
308/// If there is no matching output file it will be created and filled with the
309/// output of `f()`, but the test will fail.
310pub fn dir_tests<F>(test_data_dir: &Path, paths: &[&str], outfile_extension: &str, f: F)
311where
312 F: Fn(&str, &Path) -> String,
313{
314 for (path, input_code) in collect_rust_files(test_data_dir, paths) {
315 let actual = f(&input_code, &path);
316 let path = path.with_extension(outfile_extension);
317 if !path.exists() {
318 println!("\nfile: {}", path.display());
319 println!("No .txt file with expected result, creating...\n");
320 println!("{}\n{}", input_code, actual);
321 fs::write(&path, &actual).unwrap();
322 panic!("No expected result");
323 }
324 let expected = read_text(&path);
325 assert_equal_text(&expected, &actual, &path);
326 }
327}
328
329/// Collects all `.rs` files from `dir` subdirectories defined by `paths`.
330pub fn collect_rust_files(root_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)> {
331 paths
332 .iter()
333 .flat_map(|path| {
334 let path = root_dir.to_owned().join(path);
335 rust_files_in_dir(&path).into_iter()
336 })
337 .map(|path| {
338 let text = read_text(&path);
339 (path, text)
340 })
341 .collect()
342}
343
344/// Collects paths to all `.rs` files from `dir` in a sorted `Vec<PathBuf>`.
345fn rust_files_in_dir(dir: &Path) -> Vec<PathBuf> {
346 let mut acc = Vec::new();
347 for file in fs::read_dir(&dir).unwrap() {
348 let file = file.unwrap();
349 let path = file.path();
350 if path.extension().unwrap_or_default() == "rs" {
351 acc.push(path);
352 }
353 }
354 acc.sort();
355 acc
356}
357
358/// Returns the path to the root directory of `rust-analyzer` project.
359pub fn project_dir() -> PathBuf {
360 let dir = env!("CARGO_MANIFEST_DIR");
361 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned()
362}
363
364/// Read file and normalize newlines.
365///
366/// `rustc` seems to always normalize `\r\n` newlines to `\n`:
367///
368/// ```
369/// let s = "
370/// ";
371/// assert_eq!(s.as_bytes(), &[10]);
372/// ```
373///
374/// so this should always be correct.
375pub fn read_text(path: &Path) -> String {
376 fs::read_to_string(path)
377 .unwrap_or_else(|_| panic!("File at {:?} should be valid", path))
378 .replace("\r\n", "\n")
379}
380
381/// Returns `false` if slow tests should not run, otherwise returns `true` and 315/// Returns `false` if slow tests should not run, otherwise returns `true` and
382/// also creates a file at `./target/.slow_tests_cookie` which serves as a flag 316/// also creates a file at `./target/.slow_tests_cookie` which serves as a flag
383/// that slow tests did run. 317/// that slow tests did run.
@@ -392,25 +326,8 @@ pub fn skip_slow_tests() -> bool {
392 should_skip 326 should_skip
393} 327}
394 328
395/// Asserts that `expected` and `actual` strings are equal. If they differ only 329/// Returns the path to the root directory of `rust-analyzer` project.
396/// in trailing or leading whitespace the test won't fail and 330pub fn project_dir() -> PathBuf {
397/// the contents of `actual` will be written to the file located at `path`. 331 let dir = env!("CARGO_MANIFEST_DIR");
398fn assert_equal_text(expected: &str, actual: &str, path: &Path) { 332 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned()
399 if expected == actual {
400 return;
401 }
402 let dir = project_dir();
403 let pretty_path = path.strip_prefix(&dir).unwrap_or_else(|_| path);
404 if expected.trim() == actual.trim() {
405 println!("whitespace difference, rewriting");
406 println!("file: {}\n", pretty_path.display());
407 fs::write(path, actual).unwrap();
408 return;
409 }
410 if env::var("UPDATE_EXPECTATIONS").is_ok() {
411 println!("rewriting {}", pretty_path.display());
412 fs::write(path, actual).unwrap();
413 return;
414 }
415 assert_eq_text!(expected, actual, "file: {}", pretty_path.display());
416} 333}
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index d0ddeafe7..977ba3010 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -69,7 +69,7 @@ impl FileSetConfig {
69 Ok(it) => it, 69 Ok(it) => it,
70 Err(it) => it.saturating_sub(1), 70 Err(it) => it.saturating_sub(1),
71 }; 71 };
72 if path.starts_with(&self.roots[idx].0) { 72 if !self.roots.is_empty() && path.starts_with(&self.roots[idx].0) {
73 self.roots[idx].1 73 self.roots[idx].1
74 } else { 74 } else {
75 self.len() - 1 75 self.len() - 1