aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock49
-rw-r--r--crates/ra_db/src/input.rs7
-rw-r--r--crates/ra_ide/src/display.rs2
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs66
-rw-r--r--crates/ra_ide/src/goto_definition.rs149
-rw-r--r--crates/ra_ide/src/inlay_hints.rs309
-rw-r--r--crates/ra_ide/src/references.rs12
-rw-r--r--docs/user/features.md15
8 files changed, 275 insertions, 334 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a808f4743..0b4d5ebe6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -40,12 +40,6 @@ dependencies = [
40 40
41[[package]] 41[[package]]
42name = "autocfg" 42name = "autocfg"
43version = "0.1.7"
44source = "registry+https://github.com/rust-lang/crates.io-index"
45checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
46
47[[package]]
48name = "autocfg"
49version = "1.0.0" 43version = "1.0.0"
50source = "registry+https://github.com/rust-lang/crates.io-index" 44source = "registry+https://github.com/rust-lang/crates.io-index"
51checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 45checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
@@ -249,33 +243,36 @@ dependencies = [
249 243
250[[package]] 244[[package]]
251name = "crossbeam-channel" 245name = "crossbeam-channel"
252version = "0.4.0" 246version = "0.4.2"
253source = "registry+https://github.com/rust-lang/crates.io-index" 247source = "registry+https://github.com/rust-lang/crates.io-index"
254checksum = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" 248checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061"
255dependencies = [ 249dependencies = [
256 "crossbeam-utils", 250 "crossbeam-utils",
251 "maybe-uninit",
257] 252]
258 253
259[[package]] 254[[package]]
260name = "crossbeam-deque" 255name = "crossbeam-deque"
261version = "0.7.2" 256version = "0.7.3"
262source = "registry+https://github.com/rust-lang/crates.io-index" 257source = "registry+https://github.com/rust-lang/crates.io-index"
263checksum = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" 258checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
264dependencies = [ 259dependencies = [
265 "crossbeam-epoch", 260 "crossbeam-epoch",
266 "crossbeam-utils", 261 "crossbeam-utils",
262 "maybe-uninit",
267] 263]
268 264
269[[package]] 265[[package]]
270name = "crossbeam-epoch" 266name = "crossbeam-epoch"
271version = "0.8.0" 267version = "0.8.2"
272source = "registry+https://github.com/rust-lang/crates.io-index" 268source = "registry+https://github.com/rust-lang/crates.io-index"
273checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" 269checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
274dependencies = [ 270dependencies = [
275 "autocfg 0.1.7", 271 "autocfg",
276 "cfg-if", 272 "cfg-if",
277 "crossbeam-utils", 273 "crossbeam-utils",
278 "lazy_static", 274 "lazy_static",
275 "maybe-uninit",
279 "memoffset", 276 "memoffset",
280 "scopeguard", 277 "scopeguard",
281] 278]
@@ -292,11 +289,11 @@ dependencies = [
292 289
293[[package]] 290[[package]]
294name = "crossbeam-utils" 291name = "crossbeam-utils"
295version = "0.7.0" 292version = "0.7.2"
296source = "registry+https://github.com/rust-lang/crates.io-index" 293source = "registry+https://github.com/rust-lang/crates.io-index"
297checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" 294checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
298dependencies = [ 295dependencies = [
299 "autocfg 0.1.7", 296 "autocfg",
300 "cfg-if", 297 "cfg-if",
301 "lazy_static", 298 "lazy_static",
302] 299]
@@ -488,7 +485,7 @@ version = "1.3.2"
488source = "registry+https://github.com/rust-lang/crates.io-index" 485source = "registry+https://github.com/rust-lang/crates.io-index"
489checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" 486checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292"
490dependencies = [ 487dependencies = [
491 "autocfg 1.0.0", 488 "autocfg",
492] 489]
493 490
494[[package]] 491[[package]]
@@ -623,9 +620,9 @@ checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
623 620
624[[package]] 621[[package]]
625name = "libc" 622name = "libc"
626version = "0.2.66" 623version = "0.2.67"
627source = "registry+https://github.com/rust-lang/crates.io-index" 624source = "registry+https://github.com/rust-lang/crates.io-index"
628checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" 625checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
629 626
630[[package]] 627[[package]]
631name = "linked-hash-map" 628name = "linked-hash-map"
@@ -684,10 +681,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
684checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 681checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
685 682
686[[package]] 683[[package]]
684name = "maybe-uninit"
685version = "2.0.0"
686source = "registry+https://github.com/rust-lang/crates.io-index"
687checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
688
689[[package]]
687name = "memchr" 690name = "memchr"
688version = "2.3.2" 691version = "2.3.3"
689source = "registry+https://github.com/rust-lang/crates.io-index" 692source = "registry+https://github.com/rust-lang/crates.io-index"
690checksum = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978" 693checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
691 694
692[[package]] 695[[package]]
693name = "memoffset" 696name = "memoffset"
@@ -1531,9 +1534,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
1531 1534
1532[[package]] 1535[[package]]
1533name = "syn" 1536name = "syn"
1534version = "1.0.14" 1537version = "1.0.15"
1535source = "registry+https://github.com/rust-lang/crates.io-index" 1538source = "registry+https://github.com/rust-lang/crates.io-index"
1536checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" 1539checksum = "7a0294dc449adc58bb6592fff1a23d3e5e6e235afc6a0ffca2657d19e7bbffe5"
1537dependencies = [ 1540dependencies = [
1538 "proc-macro2", 1541 "proc-macro2",
1539 "quote", 1542 "quote",
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 78d121683..eaff99fd3 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -165,7 +165,7 @@ impl CrateGraph {
165 self.arena.is_empty() 165 self.arena.is_empty()
166 } 166 }
167 167
168 pub fn iter<'a>(&'a self) -> impl Iterator<Item = CrateId> + 'a { 168 pub fn iter(&self) -> impl Iterator<Item = CrateId> + '_ {
169 self.arena.keys().copied() 169 self.arena.keys().copied()
170 } 170 }
171 171
@@ -183,10 +183,7 @@ impl CrateGraph {
183 Some(crate_id) 183 Some(crate_id)
184 } 184 }
185 185
186 pub fn dependencies<'a>( 186 pub fn dependencies(&self, crate_id: CrateId) -> impl Iterator<Item = &Dependency> {
187 &'a self,
188 crate_id: CrateId,
189 ) -> impl Iterator<Item = &'a Dependency> + 'a {
190 self.arena[&crate_id].dependencies.iter() 187 self.arena[&crate_id].dependencies.iter()
191 } 188 }
192 189
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index fbe89841b..1c26a8697 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -15,7 +15,7 @@ pub use function_signature::FunctionSignature;
15pub use navigation_target::NavigationTarget; 15pub use navigation_target::NavigationTarget;
16pub use structure::{file_structure, StructureNode}; 16pub use structure::{file_structure, StructureNode};
17 17
18pub(crate) use navigation_target::ToNav; 18pub(crate) use navigation_target::{ToNav, TryToNav};
19pub(crate) use short_label::ShortLabel; 19pub(crate) use short_label::ShortLabel;
20 20
21pub(crate) fn function_label(node: &ast::FnDef) -> String { 21pub(crate) fn function_label(node: &ast::FnDef) -> String {
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 096c41c81..b42cb477e 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11 TextRange, 11 TextRange,
12}; 12};
13 13
14use crate::{expand::original_range, FileSymbol}; 14use crate::{expand::original_range, references::NameDefinition, FileSymbol};
15 15
16use super::short_label::ShortLabel; 16use super::short_label::ShortLabel;
17 17
@@ -36,6 +36,10 @@ pub(crate) trait ToNav {
36 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget; 36 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget;
37} 37}
38 38
39pub(crate) trait TryToNav {
40 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget>;
41}
42
39impl NavigationTarget { 43impl NavigationTarget {
40 /// When `focus_range` is specified, returns it. otherwise 44 /// When `focus_range` is specified, returns it. otherwise
41 /// returns `full_range` 45 /// returns `full_range`
@@ -96,26 +100,6 @@ impl NavigationTarget {
96 module.to_nav(db) 100 module.to_nav(db)
97 } 101 }
98 102
99 pub(crate) fn from_def(
100 db: &RootDatabase,
101 module_def: hir::ModuleDef,
102 ) -> Option<NavigationTarget> {
103 let nav = match module_def {
104 hir::ModuleDef::Module(module) => module.to_nav(db),
105 hir::ModuleDef::Function(it) => it.to_nav(db),
106 hir::ModuleDef::Adt(it) => it.to_nav(db),
107 hir::ModuleDef::Const(it) => it.to_nav(db),
108 hir::ModuleDef::Static(it) => it.to_nav(db),
109 hir::ModuleDef::EnumVariant(it) => it.to_nav(db),
110 hir::ModuleDef::Trait(it) => it.to_nav(db),
111 hir::ModuleDef::TypeAlias(it) => it.to_nav(db),
112 hir::ModuleDef::BuiltinType(..) => {
113 return None;
114 }
115 };
116 Some(nav)
117 }
118
119 #[cfg(test)] 103 #[cfg(test)]
120 pub(crate) fn assert_match(&self, expected: &str) { 104 pub(crate) fn assert_match(&self, expected: &str) {
121 let actual = self.debug_render(); 105 let actual = self.debug_render();
@@ -201,6 +185,36 @@ impl ToNav for FileSymbol {
201 } 185 }
202} 186}
203 187
188impl TryToNav for NameDefinition {
189 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
190 match self {
191 NameDefinition::Macro(it) => Some(it.to_nav(db)),
192 NameDefinition::StructField(it) => Some(it.to_nav(db)),
193 NameDefinition::ModuleDef(it) => it.try_to_nav(db),
194 NameDefinition::SelfType(it) => Some(it.to_nav(db)),
195 NameDefinition::Local(it) => Some(it.to_nav(db)),
196 NameDefinition::TypeParam(it) => Some(it.to_nav(db)),
197 }
198 }
199}
200
201impl TryToNav for hir::ModuleDef {
202 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
203 let res = match self {
204 hir::ModuleDef::Module(it) => it.to_nav(db),
205 hir::ModuleDef::Function(it) => it.to_nav(db),
206 hir::ModuleDef::Adt(it) => it.to_nav(db),
207 hir::ModuleDef::EnumVariant(it) => it.to_nav(db),
208 hir::ModuleDef::Const(it) => it.to_nav(db),
209 hir::ModuleDef::Static(it) => it.to_nav(db),
210 hir::ModuleDef::Trait(it) => it.to_nav(db),
211 hir::ModuleDef::TypeAlias(it) => it.to_nav(db),
212 hir::ModuleDef::BuiltinType(_) => return None,
213 };
214 Some(res)
215 }
216}
217
204pub(crate) trait ToNavFromAst {} 218pub(crate) trait ToNavFromAst {}
205impl ToNavFromAst for hir::Function {} 219impl ToNavFromAst for hir::Function {}
206impl ToNavFromAst for hir::Const {} 220impl ToNavFromAst for hir::Const {}
@@ -232,15 +246,17 @@ impl ToNav for hir::Module {
232 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 246 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
233 let src = self.definition_source(db); 247 let src = self.definition_source(db);
234 let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 248 let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default();
235 let syntax = match &src.value { 249 let (syntax, focus) = match &src.value {
236 ModuleSource::SourceFile(node) => node.syntax(), 250 ModuleSource::SourceFile(node) => (node.syntax(), None),
237 ModuleSource::Module(node) => node.syntax(), 251 ModuleSource::Module(node) => {
252 (node.syntax(), node.name().map(|it| it.syntax().text_range()))
253 }
238 }; 254 };
239 let frange = original_range(db, src.with_value(syntax)); 255 let frange = original_range(db, src.with_value(syntax));
240 NavigationTarget::from_syntax( 256 NavigationTarget::from_syntax(
241 frange.file_id, 257 frange.file_id,
242 name, 258 name,
243 None, 259 focus,
244 frange.range, 260 frange.range,
245 syntax.kind(), 261 syntax.kind(),
246 None, 262 None,
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index cce539e56..feff1ec3f 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,18 +1,18 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{db::AstDatabase, InFile, SourceBinder}; 3use hir::{db::AstDatabase, InFile, SourceBinder};
4use ra_ide_db::{defs::NameDefinition, symbol_index, RootDatabase}; 4use ra_ide_db::{symbol_index, RootDatabase};
5use ra_syntax::{ 5use ra_syntax::{
6 ast::{self, DocCommentsOwner}, 6 ast::{self},
7 match_ast, AstNode, 7 match_ast, AstNode,
8 SyntaxKind::*, 8 SyntaxKind::*,
9 SyntaxNode, SyntaxToken, TokenAtOffset, 9 SyntaxToken, TokenAtOffset,
10}; 10};
11 11
12use crate::{ 12use crate::{
13 display::{ShortLabel, ToNav}, 13 display::{ToNav, TryToNav},
14 expand::descend_into_macros, 14 expand::descend_into_macros,
15 references::classify_name_ref, 15 references::{classify_name, classify_name_ref},
16 FilePosition, NavigationTarget, RangeInfo, 16 FilePosition, NavigationTarget, RangeInfo,
17}; 17};
18 18
@@ -74,23 +74,12 @@ pub(crate) fn reference_definition(
74 use self::ReferenceResult::*; 74 use self::ReferenceResult::*;
75 75
76 let name_kind = classify_name_ref(sb, name_ref); 76 let name_kind = classify_name_ref(sb, name_ref);
77 match name_kind { 77 if let Some(def) = name_kind {
78 Some(NameDefinition::Macro(it)) => return Exact(it.to_nav(sb.db)), 78 return match def.try_to_nav(sb.db) {
79 Some(NameDefinition::StructField(it)) => return Exact(it.to_nav(sb.db)), 79 Some(nav) => ReferenceResult::Exact(nav),
80 Some(NameDefinition::TypeParam(it)) => return Exact(it.to_nav(sb.db)), 80 None => ReferenceResult::Approximate(Vec::new()),
81 Some(NameDefinition::Local(it)) => return Exact(it.to_nav(sb.db)), 81 };
82 Some(NameDefinition::ModuleDef(def)) => match NavigationTarget::from_def(sb.db, def) { 82 }
83 Some(nav) => return Exact(nav),
84 None => return Approximate(vec![]),
85 },
86 Some(NameDefinition::SelfType(imp)) => {
87 // FIXME: ideally, this should point to the type in the impl, and
88 // not at the whole impl. And goto **type** definition should bring
89 // us to the actual type
90 return Exact(imp.to_nav(sb.db));
91 }
92 None => {}
93 };
94 83
95 // Fallback index based approach: 84 // Fallback index based approach:
96 let navs = symbol_index::index_resolve(sb.db, name_ref.value) 85 let navs = symbol_index::index_resolve(sb.db, name_ref.value)
@@ -104,119 +93,9 @@ fn name_definition(
104 sb: &mut SourceBinder<RootDatabase>, 93 sb: &mut SourceBinder<RootDatabase>,
105 name: InFile<&ast::Name>, 94 name: InFile<&ast::Name>,
106) -> Option<Vec<NavigationTarget>> { 95) -> Option<Vec<NavigationTarget>> {
107 let parent = name.value.syntax().parent()?; 96 let def = classify_name(sb, name)?;
108 97 let nav = def.try_to_nav(sb.db)?;
109 if let Some(module) = ast::Module::cast(parent.clone()) { 98 Some(vec![nav])
110 if module.has_semi() {
111 let src = name.with_value(module);
112 if let Some(child_module) = sb.to_def(src) {
113 let nav = child_module.to_nav(sb.db);
114 return Some(vec![nav]);
115 }
116 }
117 }
118
119 if let Some(nav) = named_target(sb.db, name.with_value(&parent)) {
120 return Some(vec![nav]);
121 }
122
123 None
124}
125
126fn named_target(db: &RootDatabase, node: InFile<&SyntaxNode>) -> Option<NavigationTarget> {
127 match_ast! {
128 match (node.value) {
129 ast::StructDef(it) => {
130 Some(NavigationTarget::from_named(
131 db,
132 node.with_value(&it),
133 it.doc_comment_text(),
134 it.short_label(),
135 ))
136 },
137 ast::EnumDef(it) => {
138 Some(NavigationTarget::from_named(
139 db,
140 node.with_value(&it),
141 it.doc_comment_text(),
142 it.short_label(),
143 ))
144 },
145 ast::EnumVariant(it) => {
146 Some(NavigationTarget::from_named(
147 db,
148 node.with_value(&it),
149 it.doc_comment_text(),
150 it.short_label(),
151 ))
152 },
153 ast::FnDef(it) => {
154 Some(NavigationTarget::from_named(
155 db,
156 node.with_value(&it),
157 it.doc_comment_text(),
158 it.short_label(),
159 ))
160 },
161 ast::TypeAliasDef(it) => {
162 Some(NavigationTarget::from_named(
163 db,
164 node.with_value(&it),
165 it.doc_comment_text(),
166 it.short_label(),
167 ))
168 },
169 ast::ConstDef(it) => {
170 Some(NavigationTarget::from_named(
171 db,
172 node.with_value(&it),
173 it.doc_comment_text(),
174 it.short_label(),
175 ))
176 },
177 ast::StaticDef(it) => {
178 Some(NavigationTarget::from_named(
179 db,
180 node.with_value(&it),
181 it.doc_comment_text(),
182 it.short_label(),
183 ))
184 },
185 ast::TraitDef(it) => {
186 Some(NavigationTarget::from_named(
187 db,
188 node.with_value(&it),
189 it.doc_comment_text(),
190 it.short_label(),
191 ))
192 },
193 ast::RecordFieldDef(it) => {
194 Some(NavigationTarget::from_named(
195 db,
196 node.with_value(&it),
197 it.doc_comment_text(),
198 it.short_label(),
199 ))
200 },
201 ast::Module(it) => {
202 Some(NavigationTarget::from_named(
203 db,
204 node.with_value(&it),
205 it.doc_comment_text(),
206 it.short_label(),
207 ))
208 },
209 ast::MacroCall(it) => {
210 Some(NavigationTarget::from_named(
211 db,
212 node.with_value(&it),
213 it.doc_comment_text(),
214 None,
215 ))
216 },
217 _ => None,
218 }
219 }
220} 99}
221 100
222#[cfg(test)] 101#[cfg(test)]
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 2ae97e65f..8d1c447ef 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -1,12 +1,12 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{HirDisplay, SourceAnalyzer, SourceBinder}; 3use hir::{Function, HirDisplay, SourceAnalyzer, SourceBinder};
4use once_cell::unsync::Lazy; 4use once_cell::unsync::Lazy;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_prof::profile; 6use ra_prof::profile;
7use ra_syntax::{ 7use ra_syntax::{
8 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, 8 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
9 match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, 9 match_ast, SmolStr, SourceFile, SyntaxNode, TextRange,
10}; 10};
11 11
12use crate::{FileId, FunctionSignature}; 12use crate::{FileId, FunctionSignature};
@@ -50,51 +50,53 @@ fn get_inlay_hints(
50 let analyzer = Lazy::new(move || sb.analyze(hir::InFile::new(file_id.into(), node), None)); 50 let analyzer = Lazy::new(move || sb.analyze(hir::InFile::new(file_id.into(), node), None));
51 match_ast! { 51 match_ast! {
52 match node { 52 match node {
53 ast::LetStmt(it) => {
54 if it.ascribed_type().is_some() {
55 return None;
56 }
57 let pat = it.pat()?;
58 get_pat_type_hints(acc, db, &analyzer, pat, false, max_inlay_hint_length);
59 },
60 ast::LambdaExpr(it) => {
61 it.param_list().map(|param_list| {
62 param_list
63 .params()
64 .filter(|closure_param| closure_param.ascribed_type().is_none())
65 .filter_map(|closure_param| closure_param.pat())
66 .for_each(|root_pat| get_pat_type_hints(acc, db, &analyzer, root_pat, false, max_inlay_hint_length))
67 });
68 },
69 ast::ForExpr(it) => {
70 let pat = it.pat()?;
71 get_pat_type_hints(acc, db, &analyzer, pat, false, max_inlay_hint_length);
72 },
73 ast::IfExpr(it) => {
74 let pat = it.condition()?.pat()?;
75 get_pat_type_hints(acc, db, &analyzer, pat, true, max_inlay_hint_length);
76 },
77 ast::WhileExpr(it) => {
78 let pat = it.condition()?.pat()?;
79 get_pat_type_hints(acc, db, &analyzer, pat, true, max_inlay_hint_length);
80 },
81 ast::MatchArmList(it) => {
82 it.arms()
83 .filter_map(|match_arm| match_arm.pat())
84 .for_each(|root_pat| get_pat_type_hints(acc, db, &analyzer, root_pat, true, max_inlay_hint_length));
85 },
86 ast::CallExpr(it) => { 53 ast::CallExpr(it) => {
87 get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); 54 get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it));
88 }, 55 },
89 ast::MethodCallExpr(it) => { 56 ast::MethodCallExpr(it) => {
90 get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); 57 get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it));
91 }, 58 },
59 ast::BindPat(it) => {
60 if should_not_display_type_hint(&it) {
61 return None;
62 }
63 let pat = ast::Pat::from(it);
64 let ty = analyzer.type_of_pat(db, &pat)?;
65 if ty.is_unknown() {
66 return None;
67 }
68
69 acc.push(
70 InlayHint {
71 range: pat.syntax().text_range(),
72 kind: InlayKind::TypeHint,
73 label: ty.display_truncated(db, max_inlay_hint_length).to_string().into(),
74 }
75 );
76 },
92 _ => (), 77 _ => (),
93 } 78 }
94 }; 79 };
95 Some(()) 80 Some(())
96} 81}
97 82
83fn should_not_display_type_hint(bind_pat: &ast::BindPat) -> bool {
84 for node in bind_pat.syntax().ancestors() {
85 match_ast! {
86 match node {
87 ast::LetStmt(it) => {
88 return it.ascribed_type().is_some()
89 },
90 ast::Param(it) => {
91 return it.ascribed_type().is_some()
92 },
93 _ => (),
94 }
95 }
96 }
97 false
98}
99
98fn get_param_name_hints( 100fn get_param_name_hints(
99 acc: &mut Vec<InlayHint>, 101 acc: &mut Vec<InlayHint>,
100 db: &RootDatabase, 102 db: &RootDatabase,
@@ -105,28 +107,33 @@ fn get_param_name_hints(
105 ast::Expr::CallExpr(expr) => expr.arg_list()?.args(), 107 ast::Expr::CallExpr(expr) => expr.arg_list()?.args(),
106 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), 108 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(),
107 _ => return None, 109 _ => return None,
110 }
111 .into_iter()
112 // we need args len to determine whether to skip or not the &self parameter
113 .collect::<Vec<_>>();
114
115 let (has_self_param, fn_signature) = get_fn_signature(db, analyzer, &expr)?;
116 let parameters = if has_self_param && fn_signature.parameter_names.len() > args.len() {
117 fn_signature.parameter_names.into_iter().skip(1)
118 } else {
119 fn_signature.parameter_names.into_iter().skip(0)
108 }; 120 };
109 121
110 let mut parameters = get_fn_signature(db, analyzer, &expr)?.parameter_names.into_iter(); 122 let hints =
111 123 parameters
112 if let ast::Expr::MethodCallExpr(_) = &expr { 124 .zip(args)
113 parameters.next(); 125 .filter_map(|(param, arg)| {
114 }; 126 if !param.is_empty() {
115 127 Some((arg.syntax().text_range(), param))
116 let hints = parameters 128 } else {
117 .zip(args) 129 None
118 .filter_map(|(param, arg)| { 130 }
119 if arg.syntax().kind() == SyntaxKind::LITERAL && !param.is_empty() { 131 })
120 Some((arg.syntax().text_range(), param)) 132 .map(|(range, param_name)| InlayHint {
121 } else { 133 range,
122 None 134 kind: InlayKind::ParameterHint,
123 } 135 label: param_name.into(),
124 }) 136 });
125 .map(|(range, param_name)| InlayHint {
126 range,
127 kind: InlayKind::ParameterHint,
128 label: param_name.into(),
129 });
130 137
131 acc.extend(hints); 138 acc.extend(hints);
132 Some(()) 139 Some(())
@@ -136,100 +143,32 @@ fn get_fn_signature(
136 db: &RootDatabase, 143 db: &RootDatabase,
137 analyzer: &SourceAnalyzer, 144 analyzer: &SourceAnalyzer,
138 expr: &ast::Expr, 145 expr: &ast::Expr,
139) -> Option<FunctionSignature> { 146) -> Option<(bool, FunctionSignature)> {
140 match expr { 147 match expr {
141 ast::Expr::CallExpr(expr) => { 148 ast::Expr::CallExpr(expr) => {
142 // FIXME: Type::as_callable is broken for closures 149 // FIXME: Type::as_callable is broken for closures
143 let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; 150 let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
144 match callable_def { 151 match callable_def {
145 hir::CallableDef::FunctionId(it) => { 152 hir::CallableDef::FunctionId(it) => {
146 let fn_def = it.into(); 153 let fn_def: Function = it.into();
147 Some(FunctionSignature::from_hir(db, fn_def)) 154 Some((fn_def.has_self_param(db), FunctionSignature::from_hir(db, fn_def)))
148 } 155 }
149 hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()), 156 hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into())
157 .map(|signature| (false, signature)),
150 hir::CallableDef::EnumVariantId(it) => { 158 hir::CallableDef::EnumVariantId(it) => {
151 FunctionSignature::from_enum_variant(db, it.into()) 159 FunctionSignature::from_enum_variant(db, it.into())
160 .map(|signature| (false, signature))
152 } 161 }
153 } 162 }
154 } 163 }
155 ast::Expr::MethodCallExpr(expr) => { 164 ast::Expr::MethodCallExpr(expr) => {
156 let fn_def = analyzer.resolve_method_call(&expr)?; 165 let fn_def = analyzer.resolve_method_call(&expr)?;
157 Some(FunctionSignature::from_hir(db, fn_def)) 166 Some((fn_def.has_self_param(db), FunctionSignature::from_hir(db, fn_def)))
158 } 167 }
159 _ => None, 168 _ => None,
160 } 169 }
161} 170}
162 171
163fn get_pat_type_hints(
164 acc: &mut Vec<InlayHint>,
165 db: &RootDatabase,
166 analyzer: &SourceAnalyzer,
167 root_pat: ast::Pat,
168 skip_root_pat_hint: bool,
169 max_inlay_hint_length: Option<usize>,
170) {
171 let original_pat = &root_pat.clone();
172
173 let hints = get_leaf_pats(root_pat)
174 .into_iter()
175 .filter(|pat| !skip_root_pat_hint || pat != original_pat)
176 .filter_map(|pat| {
177 let ty = analyzer.type_of_pat(db, &pat)?;
178 if ty.is_unknown() {
179 return None;
180 }
181 Some((pat.syntax().text_range(), ty))
182 })
183 .map(|(range, pat_type)| InlayHint {
184 range,
185 kind: InlayKind::TypeHint,
186 label: pat_type.display_truncated(db, max_inlay_hint_length).to_string().into(),
187 });
188
189 acc.extend(hints);
190}
191
192fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> {
193 let mut pats_to_process = std::collections::VecDeque::<ast::Pat>::new();
194 pats_to_process.push_back(root_pat);
195
196 let mut leaf_pats = Vec::new();
197
198 while let Some(maybe_leaf_pat) = pats_to_process.pop_front() {
199 match &maybe_leaf_pat {
200 ast::Pat::BindPat(bind_pat) => match bind_pat.pat() {
201 Some(pat) => pats_to_process.push_back(pat),
202 _ => leaf_pats.push(maybe_leaf_pat),
203 },
204 ast::Pat::OrPat(ref_pat) => pats_to_process.extend(ref_pat.pats()),
205 ast::Pat::TuplePat(tuple_pat) => pats_to_process.extend(tuple_pat.args()),
206 ast::Pat::RecordPat(record_pat) => {
207 if let Some(pat_list) = record_pat.record_field_pat_list() {
208 pats_to_process.extend(
209 pat_list
210 .record_field_pats()
211 .filter_map(|record_field_pat| {
212 record_field_pat
213 .pat()
214 .filter(|pat| pat.syntax().kind() != SyntaxKind::BIND_PAT)
215 })
216 .chain(pat_list.bind_pats().map(|bind_pat| {
217 bind_pat.pat().unwrap_or_else(|| ast::Pat::from(bind_pat))
218 })),
219 );
220 }
221 }
222 ast::Pat::TupleStructPat(tuple_struct_pat) => {
223 pats_to_process.extend(tuple_struct_pat.args())
224 }
225 ast::Pat::ParenPat(inner_pat) => pats_to_process.extend(inner_pat.pat()),
226 ast::Pat::RefPat(ref_pat) => pats_to_process.extend(ref_pat.pat()),
227 _ => (),
228 }
229 }
230 leaf_pats
231}
232
233#[cfg(test)] 172#[cfg(test)]
234mod tests { 173mod tests {
235 use insta::assert_debug_snapshot; 174 use insta::assert_debug_snapshot;
@@ -346,11 +285,6 @@ fn main() {
346 label: "i32", 285 label: "i32",
347 }, 286 },
348 InlayHint { 287 InlayHint {
349 range: [584; 585),
350 kind: TypeHint,
351 label: "i32",
352 },
353 InlayHint {
354 range: [577; 578), 288 range: [577; 578),
355 kind: TypeHint, 289 kind: TypeHint,
356 label: "f64", 290 label: "f64",
@@ -361,6 +295,11 @@ fn main() {
361 label: "f64", 295 label: "f64",
362 }, 296 },
363 InlayHint { 297 InlayHint {
298 range: [584; 585),
299 kind: TypeHint,
300 label: "i32",
301 },
302 InlayHint {
364 range: [627; 628), 303 range: [627; 628),
365 kind: TypeHint, 304 kind: TypeHint,
366 label: "i32", 305 label: "i32",
@@ -508,6 +447,11 @@ fn main() {
508 label: "CustomOption<Test>", 447 label: "CustomOption<Test>",
509 }, 448 },
510 InlayHint { 449 InlayHint {
450 range: [287; 291),
451 kind: TypeHint,
452 label: "&CustomOption<Test>",
453 },
454 InlayHint {
511 range: [334; 338), 455 range: [334; 338),
512 kind: TypeHint, 456 kind: TypeHint,
513 label: "&Test", 457 label: "&Test",
@@ -523,10 +467,35 @@ fn main() {
523 label: "&u8", 467 label: "&u8",
524 }, 468 },
525 InlayHint { 469 InlayHint {
470 range: [449; 450),
471 kind: TypeHint,
472 label: "&CustomOption<u32>",
473 },
474 InlayHint {
475 range: [455; 456),
476 kind: TypeHint,
477 label: "&u8",
478 },
479 InlayHint {
526 range: [531; 532), 480 range: [531; 532),
527 kind: TypeHint, 481 kind: TypeHint,
528 label: "&u32", 482 label: "&u32",
529 }, 483 },
484 InlayHint {
485 range: [538; 539),
486 kind: TypeHint,
487 label: "&u8",
488 },
489 InlayHint {
490 range: [618; 619),
491 kind: TypeHint,
492 label: "&u8",
493 },
494 InlayHint {
495 range: [675; 676),
496 kind: TypeHint,
497 label: "&u8",
498 },
530 ] 499 ]
531 "### 500 "###
532 ); 501 );
@@ -571,6 +540,11 @@ fn main() {
571 label: "CustomOption<Test>", 540 label: "CustomOption<Test>",
572 }, 541 },
573 InlayHint { 542 InlayHint {
543 range: [293; 297),
544 kind: TypeHint,
545 label: "&CustomOption<Test>",
546 },
547 InlayHint {
574 range: [343; 347), 548 range: [343; 347),
575 kind: TypeHint, 549 kind: TypeHint,
576 label: "&Test", 550 label: "&Test",
@@ -586,10 +560,35 @@ fn main() {
586 label: "&u8", 560 label: "&u8",
587 }, 561 },
588 InlayHint { 562 InlayHint {
563 range: [464; 465),
564 kind: TypeHint,
565 label: "&CustomOption<u32>",
566 },
567 InlayHint {
568 range: [470; 471),
569 kind: TypeHint,
570 label: "&u8",
571 },
572 InlayHint {
589 range: [549; 550), 573 range: [549; 550),
590 kind: TypeHint, 574 kind: TypeHint,
591 label: "&u32", 575 label: "&u32",
592 }, 576 },
577 InlayHint {
578 range: [556; 557),
579 kind: TypeHint,
580 label: "&u8",
581 },
582 InlayHint {
583 range: [639; 640),
584 kind: TypeHint,
585 label: "&u8",
586 },
587 InlayHint {
588 range: [699; 700),
589 kind: TypeHint,
590 label: "&u8",
591 },
593 ] 592 ]
594 "### 593 "###
595 ); 594 );
@@ -629,6 +628,11 @@ fn main() {
629 assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" 628 assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###"
630 [ 629 [
631 InlayHint { 630 InlayHint {
631 range: [272; 276),
632 kind: TypeHint,
633 label: "CustomOption<Test>",
634 },
635 InlayHint {
632 range: [311; 315), 636 range: [311; 315),
633 kind: TypeHint, 637 kind: TypeHint,
634 label: "Test", 638 label: "Test",
@@ -644,10 +648,35 @@ fn main() {
644 label: "u8", 648 label: "u8",
645 }, 649 },
646 InlayHint { 650 InlayHint {
651 range: [410; 411),
652 kind: TypeHint,
653 label: "CustomOption<u32>",
654 },
655 InlayHint {
656 range: [416; 417),
657 kind: TypeHint,
658 label: "u8",
659 },
660 InlayHint {
647 range: [484; 485), 661 range: [484; 485),
648 kind: TypeHint, 662 kind: TypeHint,
649 label: "u32", 663 label: "u32",
650 }, 664 },
665 InlayHint {
666 range: [491; 492),
667 kind: TypeHint,
668 label: "u8",
669 },
670 InlayHint {
671 range: [563; 564),
672 kind: TypeHint,
673 label: "u8",
674 },
675 InlayHint {
676 range: [612; 613),
677 kind: TypeHint,
678 label: "u8",
679 },
651 ] 680 ]
652 "### 681 "###
653 ); 682 );
@@ -738,11 +767,21 @@ fn main() {
738 label: "msg", 767 label: "msg",
739 }, 768 },
740 InlayHint { 769 InlayHint {
770 range: [277; 288),
771 kind: ParameterHint,
772 label: "last",
773 },
774 InlayHint {
741 range: [331; 334), 775 range: [331; 334),
742 kind: ParameterHint, 776 kind: ParameterHint,
743 label: "param", 777 label: "param",
744 }, 778 },
745 InlayHint { 779 InlayHint {
780 range: [354; 356),
781 kind: ParameterHint,
782 label: "&self",
783 },
784 InlayHint {
746 range: [358; 362), 785 range: [358; 362),
747 kind: ParameterHint, 786 kind: ParameterHint,
748 label: "param", 787 label: "param",
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 7f790a62d..aadc2dbcb 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -25,7 +25,7 @@ use ra_syntax::{
25 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, 25 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset,
26}; 26};
27 27
28use crate::{display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; 28use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
29 29
30pub(crate) use self::{ 30pub(crate) use self::{
31 classify::{classify_name, classify_name_ref}, 31 classify::{classify_name, classify_name_ref},
@@ -125,15 +125,7 @@ pub(crate) fn find_all_refs(
125 }; 125 };
126 126
127 let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?; 127 let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?;
128 128 let declaration = def.try_to_nav(db)?;
129 let declaration = match def {
130 NameDefinition::Macro(mac) => mac.to_nav(db),
131 NameDefinition::StructField(field) => field.to_nav(db),
132 NameDefinition::ModuleDef(def) => NavigationTarget::from_def(db, def)?,
133 NameDefinition::SelfType(imp) => imp.to_nav(db),
134 NameDefinition::Local(local) => local.to_nav(db),
135 NameDefinition::TypeParam(_) => return None,
136 };
137 129
138 let search_scope = { 130 let search_scope = {
139 let base = SearchScope::for_def(&def, db); 131 let base = SearchScope::for_def(&def, db);
diff --git a/docs/user/features.md b/docs/user/features.md
index a9cff34d2..48c63ba7b 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -105,6 +105,21 @@ Start `cargo watch` for live error highlighting. Will prompt to install if it's
105 105
106Stop `cargo watch`. 106Stop `cargo watch`.
107 107
108#### Structural Seach and Replace
109
110Search and replace with named wildcards that will match any expression.
111The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement. Available via the command `rust-analyzer.ssr`.
112
113```rust
114// Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)]
115
116// BEFORE
117String::from(foo(y + 5, z))
118
119// AFTER
120String::from((y + 5).foo(z))
121```
122
108### Assists (Code Actions) 123### Assists (Code Actions)
109 124
110Assists, or code actions, are small local refactorings, available in a particular context. 125Assists, or code actions, are small local refactorings, available in a particular context.