diff options
Diffstat (limited to 'crates/ide_db/src')
-rw-r--r-- | crates/ide_db/src/apply_change.rs | 5 | ||||
-rw-r--r-- | crates/ide_db/src/defs.rs | 16 | ||||
-rw-r--r-- | crates/ide_db/src/helpers.rs | 6 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/famous_defs_fixture.rs | 11 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 356 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/insert_use.rs | 2 | ||||
-rw-r--r-- | crates/ide_db/src/imports_locator.rs | 76 | ||||
-rw-r--r-- | crates/ide_db/src/lib.rs | 24 | ||||
-rw-r--r-- | crates/ide_db/src/line_index.rs | 29 | ||||
-rw-r--r-- | crates/ide_db/src/line_index/tests.rs | 33 | ||||
-rw-r--r-- | crates/ide_db/src/search.rs | 152 | ||||
-rw-r--r-- | crates/ide_db/src/source_change.rs | 9 | ||||
-rw-r--r-- | crates/ide_db/src/symbol_index.rs | 20 | ||||
-rw-r--r-- | crates/ide_db/src/ty_filter.rs | 15 |
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 | ||
33 | impl fmt::Debug for RootChange { | 33 | impl 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 | ||
8 | use hir::{ | 8 | use 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 | }; |
12 | use syntax::{ | 12 | use 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. |
3 | pub 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 | |||
3 | pub mod convert { | 13 | pub 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 | ||
110 | pub mod prelude { | 120 | pub 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. |
2 | use either::Either; | 2 | use either::Either; |
3 | use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; | 3 | use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics}; |
4 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
5 | use syntax::{ast, AstNode, SyntaxNode}; | 5 | use syntax::{ast, AstNode}; |
6 | 6 | ||
7 | use crate::{imports_locator, RootDatabase}; | 7 | use crate::{ |
8 | 8 | imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, | |
9 | use super::insert_use::InsertUseConfig; | 9 | RootDatabase, |
10 | }; | ||
10 | 11 | ||
11 | #[derive(Debug)] | 12 | #[derive(Debug)] |
12 | pub enum ImportCandidate { | 13 | pub enum ImportCandidate { |
@@ -24,86 +25,141 @@ pub enum ImportCandidate { | |||
24 | 25 | ||
25 | #[derive(Debug)] | 26 | #[derive(Debug)] |
26 | pub struct TraitImportCandidate { | 27 | pub 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)] |
32 | pub struct PathImportCandidate { | 33 | pub 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)] | ||
39 | pub enum NameToImport { | ||
40 | Exact(String), | ||
41 | Fuzzy(String), | ||
42 | } | ||
43 | |||
44 | impl 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)] |
38 | pub struct ImportAssets { | 54 | pub 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 | ||
44 | impl ImportAssets { | 59 | impl 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 | |||
135 | impl 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> { | 236 | fn 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 | |||
302 | fn 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 | ||
4 | use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics}; | 4 | use hir::{ |
5 | import_map::{self, ImportKind}, | ||
6 | AsAssocItem, Crate, MacroDef, ModuleDef, Semantics, | ||
7 | }; | ||
5 | use syntax::{ast, AstNode, SyntaxKind::NAME}; | 8 | use syntax::{ast, AstNode, SyntaxKind::NAME}; |
6 | 9 | ||
7 | use crate::{ | 10 | use crate::{ |
@@ -12,69 +15,84 @@ use crate::{ | |||
12 | use either::Either; | 15 | use either::Either; |
13 | use rustc_hash::FxHashSet; | 16 | use rustc_hash::FxHashSet; |
14 | 17 | ||
15 | const QUERY_SEARCH_LIMIT: usize = 40; | 18 | pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; |
16 | 19 | ||
17 | pub fn find_exact_imports<'a>( | 20 | pub 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 | |||
43 | pub enum AssocItemSearch { | ||
44 | Include, | ||
45 | Exclude, | ||
46 | AssocItemsOnly, | ||
38 | } | 47 | } |
39 | 48 | ||
40 | pub fn find_similar_imports<'a>( | 49 | pub 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 | } | 89 | fn 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 | ||
80 | fn find_imports<'a>( | 98 | fn 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)] | ||
139 | pub 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)] |
18 | pub struct LineCol { | 18 | pub 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)] | ||
26 | pub 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] |
4 | fn test_line_index() { | 4 | fn 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)] |
56 | pub struct FileReference { | 56 | pub 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)] | ||
63 | pub 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)] |
75 | pub enum ReferenceAccess { | 63 | pub 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 | |||
467 | fn 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 | |||
481 | fn 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 | |||
492 | fn 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 | |||
506 | fn 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 | ||
6 | use std::{ | 6 | use std::{ |
7 | collections::hash_map::Entry, | 7 | collections::hash_map::Entry, |
@@ -10,7 +10,7 @@ use std::{ | |||
10 | 10 | ||
11 | use base_db::{AnchoredPathBuf, FileId}; | 11 | use base_db::{AnchoredPathBuf, FileId}; |
12 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
13 | use stdx::assert_never; | 13 | use stdx::never; |
14 | use text_edit::TextEdit; | 14 | use 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 | ||
212 | pub fn index_resolve(db: &RootDatabase, name: &SmolStr) -> Vec<FileSymbol> { | 212 | pub 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)] |
358 | pub enum FileSymbolKind { | 358 | pub 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 | ||
370 | impl FileSymbolKind { | 371 | impl 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", |