aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db/src')
-rw-r--r--crates/ide_db/src/apply_change.rs5
-rw-r--r--crates/ide_db/src/defs.rs16
-rw-r--r--crates/ide_db/src/helpers.rs6
-rw-r--r--crates/ide_db/src/helpers/famous_defs_fixture.rs11
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs356
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs2
-rw-r--r--crates/ide_db/src/imports_locator.rs76
-rw-r--r--crates/ide_db/src/lib.rs24
-rw-r--r--crates/ide_db/src/line_index.rs29
-rw-r--r--crates/ide_db/src/line_index/tests.rs33
-rw-r--r--crates/ide_db/src/search.rs152
-rw-r--r--crates/ide_db/src/source_change.rs9
-rw-r--r--crates/ide_db/src/symbol_index.rs20
-rw-r--r--crates/ide_db/src/ty_filter.rs15
14 files changed, 455 insertions, 299 deletions
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index c770a236b..104ee113f 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -32,7 +32,7 @@ struct RootChange {
32 32
33impl fmt::Debug for RootChange { 33impl fmt::Debug for RootChange {
34 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 34 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
35 fmt.debug_struct("AnalysisChange") 35 fmt.debug_struct("RootChange")
36 .field("added", &self.added.len()) 36 .field("added", &self.added.len())
37 .field("removed", &self.removed.len()) 37 .field("removed", &self.removed.len())
38 .finish() 38 .finish()
@@ -67,7 +67,7 @@ impl RootDatabase {
67 } 67 }
68 68
69 pub fn collect_garbage(&mut self) { 69 pub fn collect_garbage(&mut self) {
70 if cfg!(feature = "wasm") { 70 if cfg!(target_arch = "wasm32") {
71 return; 71 return;
72 } 72 }
73 73
@@ -149,6 +149,7 @@ impl RootDatabase {
149 149
150 // DefDatabase 150 // DefDatabase
151 hir::db::ItemTreeQuery 151 hir::db::ItemTreeQuery
152 hir::db::BlockDefMapQuery
152 hir::db::CrateDefMapQueryQuery 153 hir::db::CrateDefMapQueryQuery
153 hir::db::StructDataQuery 154 hir::db::StructDataQuery
154 hir::db::UnionDataQuery 155 hir::db::UnionDataQuery
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index d9875ffef..ff612b7d0 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -6,8 +6,8 @@
6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). 6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
7 7
8use hir::{ 8use hir::{
9 db::HirDatabase, Crate, Field, GenericParam, HasVisibility, Impl, Label, Local, MacroDef, 9 db::HirDatabase, Crate, Field, GenericParam, HasAttrs, HasVisibility, Impl, Label, Local,
10 Module, ModuleDef, Name, PathResolution, Semantics, Visibility, 10 MacroDef, Module, ModuleDef, Name, PathResolution, Semantics, Visibility,
11}; 11};
12use syntax::{ 12use syntax::{
13 ast::{self, AstNode, PathSegmentKind}, 13 ast::{self, AstNode, PathSegmentKind},
@@ -343,7 +343,7 @@ impl NameRefClass {
343 hir::AssocItem::TypeAlias(it) => Some(*it), 343 hir::AssocItem::TypeAlias(it) => Some(*it),
344 _ => None, 344 _ => None,
345 }) 345 })
346 .find(|alias| alias.name(sema.db).to_string() == **name_ref.text()) 346 .find(|alias| &alias.name(sema.db).to_string() == name_ref.text())
347 { 347 {
348 return Some(NameRefClass::Definition(Definition::ModuleDef( 348 return Some(NameRefClass::Definition(Definition::ModuleDef(
349 ModuleDef::TypeAlias(ty), 349 ModuleDef::TypeAlias(ty),
@@ -366,7 +366,15 @@ impl NameRefClass {
366 366
367 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { 367 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
368 if let Some(resolved) = sema.resolve_path(&path) { 368 if let Some(resolved) = sema.resolve_path(&path) {
369 return Some(NameRefClass::Definition(resolved.into())); 369 if path.syntax().parent().and_then(ast::Attr::cast).is_some() {
370 if let PathResolution::Def(ModuleDef::Function(func)) = resolved {
371 if func.attrs(sema.db).by_key("proc_macro_attribute").exists() {
372 return Some(NameRefClass::Definition(resolved.into()));
373 }
374 }
375 } else {
376 return Some(NameRefClass::Definition(resolved.into()));
377 }
370 } 378 }
371 } 379 }
372 380
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 0dcc4dd29..f9de8ce0e 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -24,7 +24,7 @@ pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
24 } 24 }
25 25
26 segments.extend( 26 segments.extend(
27 path.segments 27 path.segments()
28 .iter() 28 .iter()
29 .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))), 29 .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))),
30 ); 30 );
@@ -45,6 +45,10 @@ impl FamousDefs<'_, '_> {
45 self.find_crate("core") 45 self.find_crate("core")
46 } 46 }
47 47
48 pub fn core_cmp_Ord(&self) -> Option<Trait> {
49 self.find_trait("core:cmp:Ord")
50 }
51
48 pub fn core_convert_From(&self) -> Option<Trait> { 52 pub fn core_convert_From(&self) -> Option<Trait> {
49 self.find_trait("core:convert:From") 53 self.find_trait("core:convert:From")
50 } 54 }
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs
index 5e88de64d..bb4e9666b 100644
--- a/crates/ide_db/src/helpers/famous_defs_fixture.rs
+++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs
@@ -1,5 +1,15 @@
1//- /libcore.rs crate:core 1//- /libcore.rs crate:core
2//! Signatures of traits, types and functions from the core lib for use in tests. 2//! Signatures of traits, types and functions from the core lib for use in tests.
3pub mod cmp {
4
5 pub trait Ord {
6 fn cmp(&self, other: &Self) -> Ordering;
7 fn max(self, other: Self) -> Self;
8 fn min(self, other: Self) -> Self;
9 fn clamp(self, min: Self, max: Self) -> Self;
10 }
11}
12
3pub mod convert { 13pub mod convert {
4 pub trait From<T> { 14 pub trait From<T> {
5 fn from(t: T) -> Self; 15 fn from(t: T) -> Self;
@@ -109,6 +119,7 @@ pub mod option {
109 119
110pub mod prelude { 120pub mod prelude {
111 pub use crate::{ 121 pub use crate::{
122 cmp::Ord,
112 convert::From, 123 convert::From,
113 default::Default, 124 default::Default,
114 iter::{IntoIterator, Iterator}, 125 iter::{IntoIterator, Iterator},
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index edc3da318..517abbb4b 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,12 +1,13 @@
1//! Look up accessible paths for items. 1//! Look up accessible paths for items.
2use either::Either; 2use either::Either;
3use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; 3use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use syntax::{ast, AstNode, SyntaxNode}; 5use syntax::{ast, AstNode};
6 6
7use crate::{imports_locator, RootDatabase}; 7use crate::{
8 8 imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
9use super::insert_use::InsertUseConfig; 9 RootDatabase,
10};
10 11
11#[derive(Debug)] 12#[derive(Debug)]
12pub enum ImportCandidate { 13pub enum ImportCandidate {
@@ -24,86 +25,141 @@ pub enum ImportCandidate {
24 25
25#[derive(Debug)] 26#[derive(Debug)]
26pub struct TraitImportCandidate { 27pub struct TraitImportCandidate {
27 pub ty: hir::Type, 28 pub receiver_ty: hir::Type,
28 pub name: ast::NameRef, 29 pub name: NameToImport,
29} 30}
30 31
31#[derive(Debug)] 32#[derive(Debug)]
32pub struct PathImportCandidate { 33pub struct PathImportCandidate {
33 pub qualifier: Option<ast::Path>, 34 pub qualifier: Option<ast::Path>,
34 pub name: ast::NameRef, 35 pub name: NameToImport,
36}
37
38#[derive(Debug)]
39pub enum NameToImport {
40 Exact(String),
41 Fuzzy(String),
42}
43
44impl NameToImport {
45 pub fn text(&self) -> &str {
46 match self {
47 NameToImport::Exact(text) => text.as_str(),
48 NameToImport::Fuzzy(text) => text.as_str(),
49 }
50 }
35} 51}
36 52
37#[derive(Debug)] 53#[derive(Debug)]
38pub struct ImportAssets { 54pub struct ImportAssets {
39 import_candidate: ImportCandidate, 55 import_candidate: ImportCandidate,
40 module_with_name_to_import: hir::Module, 56 module_with_candidate: hir::Module,
41 syntax_under_caret: SyntaxNode,
42} 57}
43 58
44impl ImportAssets { 59impl ImportAssets {
45 pub fn for_method_call( 60 pub fn for_method_call(
46 method_call: ast::MethodCallExpr, 61 method_call: &ast::MethodCallExpr,
47 sema: &Semantics<RootDatabase>, 62 sema: &Semantics<RootDatabase>,
48 ) -> Option<Self> { 63 ) -> Option<Self> {
49 let syntax_under_caret = method_call.syntax().to_owned();
50 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
51 Some(Self { 64 Some(Self {
52 import_candidate: ImportCandidate::for_method_call(sema, &method_call)?, 65 import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
53 module_with_name_to_import, 66 module_with_candidate: sema.scope(method_call.syntax()).module()?,
54 syntax_under_caret,
55 }) 67 })
56 } 68 }
57 69
58 pub fn for_regular_path( 70 pub fn for_exact_path(
59 path_under_caret: ast::Path, 71 fully_qualified_path: &ast::Path,
60 sema: &Semantics<RootDatabase>, 72 sema: &Semantics<RootDatabase>,
61 ) -> Option<Self> { 73 ) -> Option<Self> {
62 let syntax_under_caret = path_under_caret.syntax().to_owned(); 74 let syntax_under_caret = fully_qualified_path.syntax();
63 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { 75 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() {
64 return None; 76 return None;
65 } 77 }
66
67 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
68 Some(Self { 78 Some(Self {
69 import_candidate: ImportCandidate::for_regular_path(sema, &path_under_caret)?, 79 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
70 module_with_name_to_import, 80 module_with_candidate: sema.scope(syntax_under_caret).module()?,
71 syntax_under_caret,
72 }) 81 })
73 } 82 }
74 83
75 pub fn syntax_under_caret(&self) -> &SyntaxNode { 84 pub fn for_fuzzy_path(
76 &self.syntax_under_caret 85 module_with_path: Module,
86 qualifier: Option<ast::Path>,
87 fuzzy_name: String,
88 sema: &Semantics<RootDatabase>,
89 ) -> Option<Self> {
90 Some(match qualifier {
91 Some(qualifier) => {
92 let qualifier_resolution = sema.resolve_path(&qualifier)?;
93 match qualifier_resolution {
94 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => Self {
95 import_candidate: ImportCandidate::TraitAssocItem(TraitImportCandidate {
96 receiver_ty: assoc_item_path.ty(sema.db),
97 name: NameToImport::Fuzzy(fuzzy_name),
98 }),
99 module_with_candidate: module_with_path,
100 },
101 _ => Self {
102 import_candidate: ImportCandidate::Path(PathImportCandidate {
103 qualifier: Some(qualifier),
104 name: NameToImport::Fuzzy(fuzzy_name),
105 }),
106 module_with_candidate: module_with_path,
107 },
108 }
109 }
110 None => Self {
111 import_candidate: ImportCandidate::Path(PathImportCandidate {
112 qualifier: None,
113 name: NameToImport::Fuzzy(fuzzy_name),
114 }),
115 module_with_candidate: module_with_path,
116 },
117 })
77 } 118 }
78 119
120 pub fn for_fuzzy_method_call(
121 module_with_method_call: Module,
122 receiver_ty: hir::Type,
123 fuzzy_method_name: String,
124 ) -> Option<Self> {
125 Some(Self {
126 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
127 receiver_ty,
128 name: NameToImport::Fuzzy(fuzzy_method_name),
129 }),
130 module_with_candidate: module_with_method_call,
131 })
132 }
133}
134
135impl ImportAssets {
79 pub fn import_candidate(&self) -> &ImportCandidate { 136 pub fn import_candidate(&self) -> &ImportCandidate {
80 &self.import_candidate 137 &self.import_candidate
81 } 138 }
82 139
83 fn get_search_query(&self) -> &str { 140 fn name_to_import(&self) -> &NameToImport {
84 match &self.import_candidate { 141 match &self.import_candidate {
85 ImportCandidate::Path(candidate) => candidate.name.text(), 142 ImportCandidate::Path(candidate) => &candidate.name,
86 ImportCandidate::TraitAssocItem(candidate) 143 ImportCandidate::TraitAssocItem(candidate)
87 | ImportCandidate::TraitMethod(candidate) => candidate.name.text(), 144 | ImportCandidate::TraitMethod(candidate) => &candidate.name,
88 } 145 }
89 } 146 }
90 147
91 pub fn search_for_imports( 148 pub fn search_for_imports(
92 &self, 149 &self,
93 sema: &Semantics<RootDatabase>, 150 sema: &Semantics<RootDatabase>,
94 config: &InsertUseConfig, 151 prefix_kind: PrefixKind,
95 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 152 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
96 let _p = profile::span("import_assists::search_for_imports"); 153 let _p = profile::span("import_assets::search_for_imports");
97 self.search_for(sema, Some(config.prefix_kind)) 154 self.search_for(sema, Some(prefix_kind))
98 } 155 }
99 156
100 /// This may return non-absolute paths if a part of the returned path is already imported into scope. 157 /// This may return non-absolute paths if a part of the returned path is already imported into scope.
101 #[allow(dead_code)]
102 pub fn search_for_relative_paths( 158 pub fn search_for_relative_paths(
103 &self, 159 &self,
104 sema: &Semantics<RootDatabase>, 160 sema: &Semantics<RootDatabase>,
105 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 161 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
106 let _p = profile::span("import_assists::search_for_relative_paths"); 162 let _p = profile::span("import_assets::search_for_relative_paths");
107 self.search_for(sema, None) 163 self.search_for(sema, None)
108 } 164 }
109 165
@@ -112,99 +168,142 @@ impl ImportAssets {
112 sema: &Semantics<RootDatabase>, 168 sema: &Semantics<RootDatabase>,
113 prefixed: Option<hir::PrefixKind>, 169 prefixed: Option<hir::PrefixKind>,
114 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 170 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
115 let db = sema.db; 171 let current_crate = self.module_with_candidate.krate();
116 let mut trait_candidates = FxHashSet::default();
117 let current_crate = self.module_with_name_to_import.krate();
118 172
119 let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| { 173 let unfiltered_imports = match self.name_to_import() {
120 trait_candidates.clear(); 174 NameToImport::Exact(exact_name) => {
121 match &self.import_candidate { 175 imports_locator::find_exact_imports(sema, current_crate, exact_name.clone())
122 ImportCandidate::TraitAssocItem(trait_candidate) => { 176 }
123 let located_assoc_item = match candidate { 177 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
124 Either::Left(ModuleDef::Function(located_function)) => { 178 // instead, we need to look up all trait impls for a certain struct and search through them only
125 located_function.as_assoc_item(db) 179 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
126 } 180 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
127 Either::Left(ModuleDef::Const(located_const)) => { 181 // for the details
128 located_const.as_assoc_item(db) 182 NameToImport::Fuzzy(fuzzy_name) => {
129 } 183 let (assoc_item_search, limit) = match self.import_candidate {
130 _ => None, 184 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
185 (AssocItemSearch::AssocItemsOnly, None)
131 } 186 }
132 .map(|assoc| assoc.container(db)) 187 _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)),
133 .and_then(Self::assoc_to_trait)?; 188 };
134 189 imports_locator::find_similar_imports(
135 trait_candidates.insert(located_assoc_item.into()); 190 sema,
191 current_crate,
192 fuzzy_name.clone(),
193 assoc_item_search,
194 limit,
195 )
196 }
197 };
136 198
137 trait_candidate 199 let db = sema.db;
138 .ty 200 let mut res =
139 .iterate_path_candidates( 201 applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports)
140 db, 202 .filter_map(|candidate| {
141 current_crate, 203 let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into);
142 &trait_candidates,
143 None,
144 |_, assoc| Self::assoc_to_trait(assoc.container(db)),
145 )
146 .map(ModuleDef::from)
147 .map(Either::Left)
148 }
149 ImportCandidate::TraitMethod(trait_candidate) => {
150 let located_assoc_item =
151 if let Either::Left(ModuleDef::Function(located_function)) = candidate {
152 located_function
153 .as_assoc_item(db)
154 .map(|assoc| assoc.container(db))
155 .and_then(Self::assoc_to_trait)
156 } else {
157 None
158 }?;
159 204
160 trait_candidates.insert(located_assoc_item.into()); 205 let item_to_search = match self.import_candidate {
206 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
207 let canidate_trait = match candidate {
208 Either::Left(module_def) => {
209 module_def.as_assoc_item(db)?.containing_trait(db)
210 }
211 _ => None,
212 }?;
213 ModuleDef::from(canidate_trait).into()
214 }
215 _ => item,
216 };
161 217
162 trait_candidate 218 if let Some(prefix_kind) = prefixed {
163 .ty 219 self.module_with_candidate.find_use_path_prefixed(
164 .iterate_method_candidates(
165 db, 220 db,
166 current_crate, 221 item_to_search,
167 &trait_candidates, 222 prefix_kind,
168 None,
169 |_, function| {
170 Self::assoc_to_trait(function.as_assoc_item(db)?.container(db))
171 },
172 ) 223 )
173 .map(ModuleDef::from) 224 } else {
174 .map(Either::Left) 225 self.module_with_candidate.find_use_path(db, item_to_search)
175 } 226 }
176 _ => Some(candidate), 227 .map(|path| (path, item))
177 } 228 })
178 }; 229 .filter(|(use_path, _)| use_path.len() > 1)
179 230 .collect::<Vec<_>>();
180 let mut res = imports_locator::find_exact_imports( 231 res.sort_by_cached_key(|(path, _)| path.clone());
181 sema,
182 current_crate,
183 self.get_search_query().to_string(),
184 )
185 .filter_map(filter)
186 .filter_map(|candidate| {
187 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
188 if let Some(prefix_kind) = prefixed {
189 self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind)
190 } else {
191 self.module_with_name_to_import.find_use_path(db, item)
192 }
193 .map(|path| (path, item))
194 })
195 .filter(|(use_path, _)| use_path.len() > 1)
196 .take(20)
197 .collect::<Vec<_>>();
198 res.sort_by_key(|(path, _)| path.clone());
199 res 232 res
200 } 233 }
234}
201 235
202 fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> { 236fn applicable_defs<'a>(
203 if let AssocItemContainer::Trait(extracted_trait) = assoc { 237 import_candidate: &ImportCandidate,
204 Some(extracted_trait) 238 current_crate: Crate,
205 } else { 239 db: &RootDatabase,
206 None 240 unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>,
241) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
242 let receiver_ty = match import_candidate {
243 ImportCandidate::Path(_) => return unfiltered_imports,
244 ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => {
245 &candidate.receiver_ty
207 } 246 }
247 };
248
249 let mut required_assoc_items = FxHashSet::default();
250
251 let trait_candidates = unfiltered_imports
252 .filter_map(|input| match input {
253 Either::Left(module_def) => module_def.as_assoc_item(db),
254 _ => None,
255 })
256 .filter_map(|assoc| {
257 let assoc_item_trait = assoc.containing_trait(db)?;
258 required_assoc_items.insert(assoc);
259 Some(assoc_item_trait.into())
260 })
261 .collect();
262
263 let mut applicable_defs = FxHashSet::default();
264
265 match import_candidate {
266 ImportCandidate::Path(_) => unreachable!(),
267 ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates(
268 db,
269 current_crate,
270 &trait_candidates,
271 None,
272 |_, assoc| {
273 if required_assoc_items.contains(&assoc) {
274 if let AssocItem::Function(f) = assoc {
275 if f.self_param(db).is_some() {
276 return None;
277 }
278 }
279 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc)));
280 }
281 None::<()>
282 },
283 ),
284 ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates(
285 db,
286 current_crate,
287 &trait_candidates,
288 None,
289 |_, function| {
290 let assoc = function.as_assoc_item(db)?;
291 if required_assoc_items.contains(&assoc) {
292 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc)));
293 }
294 None::<()>
295 },
296 ),
297 };
298
299 Box::new(applicable_defs.into_iter())
300}
301
302fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef {
303 match assoc {
304 AssocItem::Function(f) => f.into(),
305 AssocItem::Const(c) => c.into(),
306 AssocItem::TypeAlias(t) => t.into(),
208 } 307 }
209} 308}
210 309
@@ -216,22 +315,19 @@ impl ImportCandidate {
216 match sema.resolve_method_call(method_call) { 315 match sema.resolve_method_call(method_call) {
217 Some(_) => None, 316 Some(_) => None,
218 None => Some(Self::TraitMethod(TraitImportCandidate { 317 None => Some(Self::TraitMethod(TraitImportCandidate {
219 ty: sema.type_of_expr(&method_call.receiver()?)?, 318 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?,
220 name: method_call.name_ref()?, 319 name: NameToImport::Exact(method_call.name_ref()?.to_string()),
221 })), 320 })),
222 } 321 }
223 } 322 }
224 323
225 fn for_regular_path( 324 fn for_regular_path(sema: &Semantics<RootDatabase>, path: &ast::Path) -> Option<Self> {
226 sema: &Semantics<RootDatabase>, 325 if sema.resolve_path(path).is_some() {
227 path_under_caret: &ast::Path,
228 ) -> Option<Self> {
229 if sema.resolve_path(path_under_caret).is_some() {
230 return None; 326 return None;
231 } 327 }
232 328
233 let segment = path_under_caret.segment()?; 329 let segment = path.segment()?;
234 let candidate = if let Some(qualifier) = path_under_caret.qualifier() { 330 let candidate = if let Some(qualifier) = path.qualifier() {
235 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; 331 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
236 let qualifier_start_path = 332 let qualifier_start_path =
237 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; 333 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
@@ -244,8 +340,8 @@ impl ImportCandidate {
244 match qualifier_resolution { 340 match qualifier_resolution {
245 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { 341 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
246 ImportCandidate::TraitAssocItem(TraitImportCandidate { 342 ImportCandidate::TraitAssocItem(TraitImportCandidate {
247 ty: assoc_item_path.ty(sema.db), 343 receiver_ty: assoc_item_path.ty(sema.db),
248 name: segment.name_ref()?, 344 name: NameToImport::Exact(segment.name_ref()?.to_string()),
249 }) 345 })
250 } 346 }
251 _ => return None, 347 _ => return None,
@@ -253,13 +349,15 @@ impl ImportCandidate {
253 } else { 349 } else {
254 ImportCandidate::Path(PathImportCandidate { 350 ImportCandidate::Path(PathImportCandidate {
255 qualifier: Some(qualifier), 351 qualifier: Some(qualifier),
256 name: qualifier_start, 352 name: NameToImport::Exact(qualifier_start.to_string()),
257 }) 353 })
258 } 354 }
259 } else { 355 } else {
260 ImportCandidate::Path(PathImportCandidate { 356 ImportCandidate::Path(PathImportCandidate {
261 qualifier: None, 357 qualifier: None,
262 name: segment.syntax().descendants().find_map(ast::NameRef::cast)?, 358 name: NameToImport::Exact(
359 segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(),
360 ),
263 }) 361 })
264 }; 362 };
265 Some(candidate) 363 Some(candidate)
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 877d4f1c7..fd4035198 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -507,7 +507,7 @@ impl ImportGroup {
507 PathSegmentKind::SelfKw => ImportGroup::ThisModule, 507 PathSegmentKind::SelfKw => ImportGroup::ThisModule,
508 PathSegmentKind::SuperKw => ImportGroup::SuperModule, 508 PathSegmentKind::SuperKw => ImportGroup::SuperModule,
509 PathSegmentKind::CrateKw => ImportGroup::ThisCrate, 509 PathSegmentKind::CrateKw => ImportGroup::ThisCrate,
510 PathSegmentKind::Name(name) => match name.text().as_str() { 510 PathSegmentKind::Name(name) => match name.text() {
511 "std" => ImportGroup::Std, 511 "std" => ImportGroup::Std,
512 "core" => ImportGroup::Std, 512 "core" => ImportGroup::Std,
513 _ => ImportGroup::ExternCrate, 513 _ => ImportGroup::ExternCrate,
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index d111fba92..502e8281a 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -1,7 +1,10 @@
1//! This module contains an import search functionality that is provided to the assists module. 1//! This module contains an import search functionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3 3
4use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics}; 4use hir::{
5 import_map::{self, ImportKind},
6 AsAssocItem, Crate, MacroDef, ModuleDef, Semantics,
7};
5use syntax::{ast, AstNode, SyntaxKind::NAME}; 8use syntax::{ast, AstNode, SyntaxKind::NAME};
6 9
7use crate::{ 10use crate::{
@@ -12,69 +15,84 @@ use crate::{
12use either::Either; 15use either::Either;
13use rustc_hash::FxHashSet; 16use rustc_hash::FxHashSet;
14 17
15const QUERY_SEARCH_LIMIT: usize = 40; 18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
16 19
17pub fn find_exact_imports<'a>( 20pub fn find_exact_imports<'a>(
18 sema: &Semantics<'a, RootDatabase>, 21 sema: &Semantics<'a, RootDatabase>,
19 krate: Crate, 22 krate: Crate,
20 name_to_import: String, 23 name_to_import: String,
21) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 24) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> {
22 let _p = profile::span("find_exact_imports"); 25 let _p = profile::span("find_exact_imports");
23 find_imports( 26 Box::new(find_imports(
24 sema, 27 sema,
25 krate, 28 krate,
26 { 29 {
27 let mut local_query = symbol_index::Query::new(name_to_import.clone()); 30 let mut local_query = symbol_index::Query::new(name_to_import.clone());
28 local_query.exact(); 31 local_query.exact();
29 local_query.limit(QUERY_SEARCH_LIMIT); 32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
30 local_query 33 local_query
31 }, 34 },
32 import_map::Query::new(name_to_import) 35 import_map::Query::new(name_to_import)
33 .limit(QUERY_SEARCH_LIMIT) 36 .limit(DEFAULT_QUERY_SEARCH_LIMIT)
34 .name_only() 37 .name_only()
35 .search_mode(import_map::SearchMode::Equals) 38 .search_mode(import_map::SearchMode::Equals)
36 .case_sensitive(), 39 .case_sensitive(),
37 ) 40 ))
41}
42
43pub enum AssocItemSearch {
44 Include,
45 Exclude,
46 AssocItemsOnly,
38} 47}
39 48
40pub fn find_similar_imports<'a>( 49pub fn find_similar_imports<'a>(
41 sema: &Semantics<'a, RootDatabase>, 50 sema: &Semantics<'a, RootDatabase>,
42 krate: Crate, 51 krate: Crate,
43 limit: Option<usize>,
44 fuzzy_search_string: String, 52 fuzzy_search_string: String,
45 ignore_assoc_items: bool, 53 assoc_item_search: AssocItemSearch,
46 name_only: bool, 54 limit: Option<usize>,
47) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a { 55) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
48 let _p = profile::span("find_similar_imports"); 56 let _p = profile::span("find_similar_imports");
49 57
50 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 58 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
51 .search_mode(import_map::SearchMode::Fuzzy); 59 .search_mode(import_map::SearchMode::Fuzzy)
52 if name_only { 60 .name_only();
53 external_query = external_query.name_only(); 61
62 match assoc_item_search {
63 AssocItemSearch::Include => {}
64 AssocItemSearch::Exclude => {
65 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
66 }
67 AssocItemSearch::AssocItemsOnly => {
68 external_query = external_query.assoc_items_only();
69 }
54 } 70 }
55 71
56 let mut local_query = symbol_index::Query::new(fuzzy_search_string); 72 let mut local_query = symbol_index::Query::new(fuzzy_search_string);
57 73
58 if let Some(limit) = limit { 74 if let Some(limit) = limit {
59 local_query.limit(limit);
60 external_query = external_query.limit(limit); 75 external_query = external_query.limit(limit);
76 local_query.limit(limit);
61 } 77 }
62 78
63 let db = sema.db; 79 let db = sema.db;
64 find_imports(sema, krate, local_query, external_query).filter(move |import_candidate| { 80 Box::new(find_imports(sema, krate, local_query, external_query).filter(
65 if ignore_assoc_items { 81 move |import_candidate| match assoc_item_search {
66 match import_candidate { 82 AssocItemSearch::Include => true,
67 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_none(), 83 AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db),
68 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_none(), 84 AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db),
69 Either::Left(ModuleDef::TypeAlias(type_alias)) => { 85 },
70 type_alias.as_assoc_item(db).is_none() 86 ))
71 } 87}
72 _ => true, 88
73 } 89fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool {
74 } else { 90 match import_candidate {
75 true 91 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(),
76 } 92 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(),
77 }) 93 Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(),
94 _ => false,
95 }
78} 96}
79 97
80fn find_imports<'a>( 98fn find_imports<'a>(
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 118c090d7..6eb34b06b 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -134,3 +134,27 @@ fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
134 let text = db.file_text(file_id); 134 let text = db.file_text(file_id);
135 Arc::new(LineIndex::new(&*text)) 135 Arc::new(LineIndex::new(&*text))
136} 136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
139pub enum SymbolKind {
140 Const,
141 ConstParam,
142 Enum,
143 Field,
144 Function,
145 Impl,
146 Label,
147 LifetimeParam,
148 Local,
149 Macro,
150 Module,
151 SelfParam,
152 Static,
153 Struct,
154 Trait,
155 TypeAlias,
156 TypeParam,
157 Union,
158 ValueParam,
159 Variant,
160}
diff --git a/crates/ide_db/src/line_index.rs b/crates/ide_db/src/line_index.rs
index 41226305e..8e9d8cca2 100644
--- a/crates/ide_db/src/line_index.rs
+++ b/crates/ide_db/src/line_index.rs
@@ -15,11 +15,19 @@ pub struct LineIndex {
15} 15}
16 16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
18pub struct LineCol { 18pub struct LineColUtf16 {
19 /// Zero-based 19 /// Zero-based
20 pub line: u32, 20 pub line: u32,
21 /// Zero-based 21 /// Zero-based
22 pub col_utf16: u32, 22 pub col: u32,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26pub struct LineCol {
27 /// Zero-based
28 pub line: u32,
29 /// Zero-based utf8 offset
30 pub col: u32,
23} 31}
24 32
25#[derive(Clone, Debug, Hash, PartialEq, Eq)] 33#[derive(Clone, Debug, Hash, PartialEq, Eq)]
@@ -92,14 +100,21 @@ impl LineIndex {
92 let line = partition_point(&self.newlines, |&it| it <= offset) - 1; 100 let line = partition_point(&self.newlines, |&it| it <= offset) - 1;
93 let line_start_offset = self.newlines[line]; 101 let line_start_offset = self.newlines[line];
94 let col = offset - line_start_offset; 102 let col = offset - line_start_offset;
95 103 LineCol { line: line as u32, col: col.into() }
96 LineCol { line: line as u32, col_utf16: self.utf8_to_utf16_col(line as u32, col) as u32 }
97 } 104 }
98 105
99 pub fn offset(&self, line_col: LineCol) -> TextSize { 106 pub fn offset(&self, line_col: LineCol) -> TextSize {
100 //FIXME: return Result 107 self.newlines[line_col.line as usize] + TextSize::from(line_col.col)
101 let col = self.utf16_to_utf8_col(line_col.line, line_col.col_utf16); 108 }
102 self.newlines[line_col.line as usize] + col 109
110 pub fn to_utf16(&self, line_col: LineCol) -> LineColUtf16 {
111 let col = self.utf8_to_utf16_col(line_col.line, line_col.col.into());
112 LineColUtf16 { line: line_col.line, col: col as u32 }
113 }
114
115 pub fn to_utf8(&self, line_col: LineColUtf16) -> LineCol {
116 let col = self.utf16_to_utf8_col(line_col.line, line_col.col);
117 LineCol { line: line_col.line, col: col.into() }
103 } 118 }
104 119
105 pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ { 120 pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
diff --git a/crates/ide_db/src/line_index/tests.rs b/crates/ide_db/src/line_index/tests.rs
index 05f7484e8..09f3bca62 100644
--- a/crates/ide_db/src/line_index/tests.rs
+++ b/crates/ide_db/src/line_index/tests.rs
@@ -3,24 +3,29 @@ use super::*;
3#[test] 3#[test]
4fn test_line_index() { 4fn test_line_index() {
5 let text = "hello\nworld"; 5 let text = "hello\nworld";
6 let table = [
7 (00, 0, 0),
8 (01, 0, 1),
9 (05, 0, 5),
10 (06, 1, 0),
11 (07, 1, 1),
12 (08, 1, 2),
13 (10, 1, 4),
14 (11, 1, 5),
15 (12, 1, 6),
16 ];
17
6 let index = LineIndex::new(text); 18 let index = LineIndex::new(text);
7 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); 19 for &(offset, line, col) in &table {
8 assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 }); 20 assert_eq!(index.line_col(offset.into()), LineCol { line, col });
9 assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 }); 21 }
10 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 });
11 assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 });
12 assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 });
13 assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 });
14 assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 });
15 assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 });
16 22
17 let text = "\nhello\nworld"; 23 let text = "\nhello\nworld";
24 let table = [(0, 0, 0), (1, 1, 0), (2, 1, 1), (6, 1, 5), (7, 2, 0)];
18 let index = LineIndex::new(text); 25 let index = LineIndex::new(text);
19 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); 26 for &(offset, line, col) in &table {
20 assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 }); 27 assert_eq!(index.line_col(offset.into()), LineCol { line, col });
21 assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 }); 28 }
22 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 });
23 assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 });
24} 29}
25 30
26#[test] 31#[test]
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 0ecb13a64..ddcfbd3f3 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -55,22 +55,10 @@ impl IntoIterator for UsageSearchResult {
55#[derive(Debug, Clone)] 55#[derive(Debug, Clone)]
56pub struct FileReference { 56pub struct FileReference {
57 pub range: TextRange, 57 pub range: TextRange,
58 pub kind: ReferenceKind, 58 pub name: ast::NameLike,
59 pub access: Option<ReferenceAccess>, 59 pub access: Option<ReferenceAccess>,
60} 60}
61 61
62#[derive(Debug, Clone, PartialEq)]
63pub enum ReferenceKind {
64 FieldShorthandForField,
65 FieldShorthandForLocal,
66 StructLiteral,
67 RecordFieldExprOrPat,
68 SelfParam,
69 EnumLiteral,
70 Lifetime,
71 Other,
72}
73
74#[derive(Debug, Copy, Clone, PartialEq)] 62#[derive(Debug, Copy, Clone, PartialEq)]
75pub enum ReferenceAccess { 63pub enum ReferenceAccess {
76 Read, 64 Read,
@@ -228,6 +216,15 @@ impl Definition {
228 // so do nothing. 216 // so do nothing.
229 } 217 }
230 } 218 }
219 ModuleSource::BlockExpr(b) => {
220 if is_first {
221 let range = Some(b.syntax().text_range());
222 res.insert(file_id, range);
223 } else {
224 // We have already added the enclosing file to the search scope,
225 // so do nothing.
226 }
227 }
231 ModuleSource::SourceFile(_) => { 228 ModuleSource::SourceFile(_) => {
232 res.insert(file_id, None); 229 res.insert(file_id, None);
233 } 230 }
@@ -257,6 +254,7 @@ impl Definition {
257 let mut res = FxHashMap::default(); 254 let mut res = FxHashMap::default();
258 let range = match module_src.value { 255 let range = match module_src.value {
259 ModuleSource::Module(m) => Some(m.syntax().text_range()), 256 ModuleSource::Module(m) => Some(m.syntax().text_range()),
257 ModuleSource::BlockExpr(b) => Some(b.syntax().text_range()),
260 ModuleSource::SourceFile(_) => None, 258 ModuleSource::SourceFile(_) => None,
261 }; 259 };
262 res.insert(file_id, range); 260 res.insert(file_id, range);
@@ -278,6 +276,7 @@ impl<'a> FindUsages<'a> {
278 pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> { 276 pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> {
279 self.set_scope(Some(scope)) 277 self.set_scope(Some(scope))
280 } 278 }
279
281 pub fn set_scope(mut self, scope: Option<SearchScope>) -> FindUsages<'a> { 280 pub fn set_scope(mut self, scope: Option<SearchScope>) -> FindUsages<'a> {
282 assert!(self.scope.is_none()); 281 assert!(self.scope.is_none());
283 self.scope = scope; 282 self.scope = scope;
@@ -323,7 +322,7 @@ impl<'a> FindUsages<'a> {
323 for (file_id, search_range) in search_scope { 322 for (file_id, search_range) in search_scope {
324 let text = sema.db.file_text(file_id); 323 let text = sema.db.file_text(file_id);
325 let search_range = 324 let search_range =
326 search_range.unwrap_or(TextRange::up_to(TextSize::of(text.as_str()))); 325 search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
327 326
328 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); 327 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
329 328
@@ -333,18 +332,23 @@ impl<'a> FindUsages<'a> {
333 continue; 332 continue;
334 } 333 }
335 334
336 if let Some(name_ref) = sema.find_node_at_offset_with_descend(&tree, offset) { 335 if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) {
337 if self.found_name_ref(&name_ref, sink) { 336 match name {
338 return; 337 ast::NameLike::NameRef(name_ref) => {
339 } 338 if self.found_name_ref(&name_ref, sink) {
340 } else if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) { 339 return;
341 if self.found_name(&name, sink) { 340 }
342 return; 341 }
343 } 342 ast::NameLike::Name(name) => {
344 } else if let Some(lifetime) = sema.find_node_at_offset_with_descend(&tree, offset) 343 if self.found_name(&name, sink) {
345 { 344 return;
346 if self.found_lifetime(&lifetime, sink) { 345 }
347 return; 346 }
347 ast::NameLike::Lifetime(lifetime) => {
348 if self.found_lifetime(&lifetime, sink) {
349 return;
350 }
351 }
348 } 352 }
349 } 353 }
350 } 354 }
@@ -359,8 +363,11 @@ impl<'a> FindUsages<'a> {
359 match NameRefClass::classify_lifetime(self.sema, lifetime) { 363 match NameRefClass::classify_lifetime(self.sema, lifetime) {
360 Some(NameRefClass::Definition(def)) if &def == self.def => { 364 Some(NameRefClass::Definition(def)) if &def == self.def => {
361 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); 365 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
362 let reference = 366 let reference = FileReference {
363 FileReference { range, kind: ReferenceKind::Lifetime, access: None }; 367 range,
368 name: ast::NameLike::Lifetime(lifetime.clone()),
369 access: None,
370 };
364 sink(file_id, reference) 371 sink(file_id, reference)
365 } 372 }
366 _ => false, // not a usage 373 _ => false, // not a usage
@@ -374,19 +381,12 @@ impl<'a> FindUsages<'a> {
374 ) -> bool { 381 ) -> bool {
375 match NameRefClass::classify(self.sema, &name_ref) { 382 match NameRefClass::classify(self.sema, &name_ref) {
376 Some(NameRefClass::Definition(def)) if &def == self.def => { 383 Some(NameRefClass::Definition(def)) if &def == self.def => {
377 let kind = if is_record_field_expr_or_pat(&name_ref) {
378 ReferenceKind::RecordFieldExprOrPat
379 } else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
380 ReferenceKind::StructLiteral
381 } else if is_enum_lit_name_ref(&name_ref) {
382 ReferenceKind::EnumLiteral
383 } else {
384 ReferenceKind::Other
385 };
386
387 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); 384 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
388 let reference = 385 let reference = FileReference {
389 FileReference { range, kind, access: reference_access(&def, &name_ref) }; 386 range,
387 name: ast::NameLike::NameRef(name_ref.clone()),
388 access: reference_access(&def, &name_ref),
389 };
390 sink(file_id, reference) 390 sink(file_id, reference)
391 } 391 }
392 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { 392 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
@@ -394,12 +394,12 @@ impl<'a> FindUsages<'a> {
394 let reference = match self.def { 394 let reference = match self.def {
395 Definition::Field(_) if &field == self.def => FileReference { 395 Definition::Field(_) if &field == self.def => FileReference {
396 range, 396 range,
397 kind: ReferenceKind::FieldShorthandForField, 397 name: ast::NameLike::NameRef(name_ref.clone()),
398 access: reference_access(&field, &name_ref), 398 access: reference_access(&field, &name_ref),
399 }, 399 },
400 Definition::Local(l) if &local == l => FileReference { 400 Definition::Local(l) if &local == l => FileReference {
401 range, 401 range,
402 kind: ReferenceKind::FieldShorthandForLocal, 402 name: ast::NameLike::NameRef(name_ref.clone()),
403 access: reference_access(&Definition::Local(local), &name_ref), 403 access: reference_access(&Definition::Local(local), &name_ref),
404 }, 404 },
405 _ => return false, // not a usage 405 _ => return false, // not a usage
@@ -416,19 +416,26 @@ impl<'a> FindUsages<'a> {
416 sink: &mut dyn FnMut(FileId, FileReference) -> bool, 416 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
417 ) -> bool { 417 ) -> bool {
418 match NameClass::classify(self.sema, name) { 418 match NameClass::classify(self.sema, name) {
419 Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => { 419 Some(NameClass::PatFieldShorthand { local_def: _, field_ref })
420 if !matches!(self.def, Definition::Field(_) if &field_ref == self.def) { 420 if matches!(
421 return false; 421 self.def, Definition::Field(_) if &field_ref == self.def
422 } 422 ) =>
423 {
423 let FileRange { file_id, range } = self.sema.original_range(name.syntax()); 424 let FileRange { file_id, range } = self.sema.original_range(name.syntax());
424 let reference = FileReference { 425 let reference = FileReference {
425 range, 426 range,
426 kind: ReferenceKind::FieldShorthandForField, 427 name: ast::NameLike::Name(name.clone()),
427 // FIXME: mutable patterns should have `Write` access 428 // FIXME: mutable patterns should have `Write` access
428 access: Some(ReferenceAccess::Read), 429 access: Some(ReferenceAccess::Read),
429 }; 430 };
430 sink(file_id, reference) 431 sink(file_id, reference)
431 } 432 }
433 Some(NameClass::ConstReference(def)) if *self.def == def => {
434 let FileRange { file_id, range } = self.sema.original_range(name.syntax());
435 let reference =
436 FileReference { range, name: ast::NameLike::Name(name.clone()), access: None };
437 sink(file_id, reference)
438 }
432 _ => false, // not a usage 439 _ => false, // not a usage
433 } 440 }
434 } 441 }
@@ -463,54 +470,3 @@ fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<Referen
463 // Default Locals and Fields to read 470 // Default Locals and Fields to read
464 mode.or(Some(ReferenceAccess::Read)) 471 mode.or(Some(ReferenceAccess::Read))
465} 472}
466
467fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool {
468 name_ref
469 .syntax()
470 .ancestors()
471 .find_map(ast::CallExpr::cast)
472 .and_then(|c| match c.expr()? {
473 ast::Expr::PathExpr(p) => {
474 Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref))
475 }
476 _ => None,
477 })
478 .unwrap_or(false)
479}
480
481fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
482 name_ref
483 .syntax()
484 .ancestors()
485 .find_map(ast::RecordExpr::cast)
486 .and_then(|l| l.path())
487 .and_then(|p| p.segment())
488 .map(|p| p.name_ref().as_ref() == Some(name_ref))
489 .unwrap_or(false)
490}
491
492fn is_record_field_expr_or_pat(name_ref: &ast::NameRef) -> bool {
493 if let Some(parent) = name_ref.syntax().parent() {
494 match_ast! {
495 match parent {
496 ast::RecordExprField(it) => true,
497 ast::RecordPatField(_it) => true,
498 _ => false,
499 }
500 }
501 } else {
502 false
503 }
504}
505
506fn is_enum_lit_name_ref(name_ref: &ast::NameRef) -> bool {
507 name_ref
508 .syntax()
509 .ancestors()
510 .find_map(ast::PathExpr::cast)
511 .and_then(|p| p.path())
512 .and_then(|p| p.qualifier())
513 .and_then(|p| p.segment())
514 .map(|p| p.name_ref().as_ref() == Some(name_ref))
515 .unwrap_or(false)
516}
diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs
index b1f87731b..b36455d49 100644
--- a/crates/ide_db/src/source_change.rs
+++ b/crates/ide_db/src/source_change.rs
@@ -1,7 +1,7 @@
1//! This modules defines type to represent changes to the source code, that flow 1//! This modules defines type to represent changes to the source code, that flow
2//! from the server to the client. 2//! from the server to the client.
3//! 3//!
4//! It can be viewed as a dual for `AnalysisChange`. 4//! It can be viewed as a dual for `Change`.
5 5
6use std::{ 6use std::{
7 collections::hash_map::Entry, 7 collections::hash_map::Entry,
@@ -10,7 +10,7 @@ use std::{
10 10
11use base_db::{AnchoredPathBuf, FileId}; 11use base_db::{AnchoredPathBuf, FileId};
12use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
13use stdx::assert_never; 13use stdx::never;
14use text_edit::TextEdit; 14use text_edit::TextEdit;
15 15
16#[derive(Default, Debug, Clone)] 16#[derive(Default, Debug, Clone)]
@@ -40,10 +40,7 @@ impl SourceChange {
40 pub fn insert_source_edit(&mut self, file_id: FileId, edit: TextEdit) { 40 pub fn insert_source_edit(&mut self, file_id: FileId, edit: TextEdit) {
41 match self.source_file_edits.entry(file_id) { 41 match self.source_file_edits.entry(file_id) {
42 Entry::Occupied(mut entry) => { 42 Entry::Occupied(mut entry) => {
43 assert_never!( 43 never!(entry.get_mut().union(edit).is_err(), "overlapping edits for same file");
44 entry.get_mut().union(edit).is_err(),
45 "overlapping edits for same file"
46 );
47 } 44 }
48 Entry::Vacant(entry) => { 45 Entry::Vacant(entry) => {
49 entry.insert(edit); 46 entry.insert(edit);
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs
index 0aa6a0765..9ed9568ce 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -191,7 +191,7 @@ pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<Fil
191 191
192 let def_map = db.crate_def_map(krate); 192 let def_map = db.crate_def_map(krate);
193 let mut files = Vec::new(); 193 let mut files = Vec::new();
194 let mut modules = vec![def_map.root]; 194 let mut modules = vec![def_map.root()];
195 while let Some(module) = modules.pop() { 195 while let Some(module) = modules.pop() {
196 let data = &def_map[module]; 196 let data = &def_map[module];
197 files.extend(data.origin.file_id()); 197 files.extend(data.origin.file_id());
@@ -209,7 +209,7 @@ pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<Fil
209 query.search(&buf) 209 query.search(&buf)
210} 210}
211 211
212pub fn index_resolve(db: &RootDatabase, name: &SmolStr) -> Vec<FileSymbol> { 212pub fn index_resolve(db: &RootDatabase, name: &str) -> Vec<FileSymbol> {
213 let mut query = Query::new(name.to_string()); 213 let mut query = Query::new(name.to_string());
214 query.exact(); 214 query.exact();
215 query.limit(4); 215 query.limit(4);
@@ -356,15 +356,16 @@ pub struct FileSymbol {
356 356
357#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] 357#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
358pub enum FileSymbolKind { 358pub enum FileSymbolKind {
359 Const,
360 Enum,
359 Function, 361 Function,
362 Macro,
363 Module,
364 Static,
360 Struct, 365 Struct,
361 Enum,
362 Trait, 366 Trait,
363 Module,
364 TypeAlias, 367 TypeAlias,
365 Const, 368 Union,
366 Static,
367 Macro,
368} 369}
369 370
370impl FileSymbolKind { 371impl FileSymbolKind {
@@ -375,6 +376,7 @@ impl FileSymbolKind {
375 | FileSymbolKind::Enum 376 | FileSymbolKind::Enum
376 | FileSymbolKind::Trait 377 | FileSymbolKind::Trait
377 | FileSymbolKind::TypeAlias 378 | FileSymbolKind::TypeAlias
379 | FileSymbolKind::Union
378 ) 380 )
379 } 381 }
380} 382}
@@ -409,7 +411,7 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
409 fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { 411 fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
410 let name = node.name()?; 412 let name = node.name()?;
411 let name_range = name.syntax().text_range(); 413 let name_range = name.syntax().text_range();
412 let name = name.text().clone(); 414 let name = name.text().into();
413 let ptr = SyntaxNodePtr::new(node.syntax()); 415 let ptr = SyntaxNodePtr::new(node.syntax());
414 416
415 Some((name, ptr, name_range)) 417 Some((name, ptr, name_range))
@@ -425,6 +427,7 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
425 ast::Const(it) => decl(it), 427 ast::Const(it) => decl(it),
426 ast::Static(it) => decl(it), 428 ast::Static(it) => decl(it),
427 ast::MacroRules(it) => decl(it), 429 ast::MacroRules(it) => decl(it),
430 ast::Union(it) => decl(it),
428 _ => None, 431 _ => None,
429 } 432 }
430 } 433 }
@@ -443,6 +446,7 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
443 CONST => FileSymbolKind::Const, 446 CONST => FileSymbolKind::Const,
444 STATIC => FileSymbolKind::Static, 447 STATIC => FileSymbolKind::Static,
445 MACRO_RULES => FileSymbolKind::Macro, 448 MACRO_RULES => FileSymbolKind::Macro,
449 UNION => FileSymbolKind::Union,
446 kind => unreachable!("{:?}", kind), 450 kind => unreachable!("{:?}", kind),
447 }, 451 },
448 range: node.text_range(), 452 range: node.text_range(),
diff --git a/crates/ide_db/src/ty_filter.rs b/crates/ide_db/src/ty_filter.rs
index 63a945282..f8406851b 100644
--- a/crates/ide_db/src/ty_filter.rs
+++ b/crates/ide_db/src/ty_filter.rs
@@ -49,6 +49,21 @@ impl TryEnum {
49 } 49 }
50 } 50 }
51 51
52 pub fn happy_pattern(self) -> ast::Pat {
53 match self {
54 TryEnum::Result => make::tuple_struct_pat(
55 make::path_unqualified(make::path_segment(make::name_ref("Ok"))),
56 iter::once(make::wildcard_pat().into()),
57 )
58 .into(),
59 TryEnum::Option => make::tuple_struct_pat(
60 make::path_unqualified(make::path_segment(make::name_ref("Some"))),
61 iter::once(make::wildcard_pat().into()),
62 )
63 .into(),
64 }
65 }
66
52 fn type_name(self) -> &'static str { 67 fn type_name(self) -> &'static str {
53 match self { 68 match self {
54 TryEnum::Result => "Result", 69 TryEnum::Result => "Result",