diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir/src/lib.rs | 19 | ||||
-rw-r--r-- | crates/hir/src/semantics.rs | 3 | ||||
-rw-r--r-- | crates/hir/src/semantics/source_to_def.rs | 106 | ||||
-rw-r--r-- | crates/hir_expand/src/db.rs | 2 | ||||
-rw-r--r-- | crates/ide/src/expand_macro.rs | 34 | ||||
-rw-r--r-- | crates/ide_completion/src/completions.rs | 51 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/attribute.rs | 4 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/flyimport.rs | 3 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/pattern.rs | 24 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/qualified_path.rs | 41 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/unqualified_path.rs | 76 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 42 | ||||
-rw-r--r-- | crates/ide_completion/src/render.rs | 15 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/insert_use.rs | 18 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/insert_use/tests.rs | 15 | ||||
-rw-r--r-- | crates/syntax/src/ast/node_ext.rs | 5 |
16 files changed, 320 insertions, 138 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index c2b68a853..dba46df04 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -1351,6 +1351,13 @@ impl MacroDef { | |||
1351 | MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::FuncLike, _) => MacroKind::ProcMacro, | 1351 | MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::FuncLike, _) => MacroKind::ProcMacro, |
1352 | } | 1352 | } |
1353 | } | 1353 | } |
1354 | |||
1355 | pub fn is_fn_like(&self) -> bool { | ||
1356 | match self.kind() { | ||
1357 | MacroKind::Declarative | MacroKind::BuiltIn | MacroKind::ProcMacro => true, | ||
1358 | MacroKind::Attr | MacroKind::Derive => false, | ||
1359 | } | ||
1360 | } | ||
1354 | } | 1361 | } |
1355 | 1362 | ||
1356 | /// Invariant: `inner.as_assoc_item(db).is_some()` | 1363 | /// Invariant: `inner.as_assoc_item(db).is_some()` |
@@ -2496,6 +2503,18 @@ impl ScopeDef { | |||
2496 | 2503 | ||
2497 | items | 2504 | items |
2498 | } | 2505 | } |
2506 | |||
2507 | pub fn is_value_def(&self) -> bool { | ||
2508 | matches!( | ||
2509 | self, | ||
2510 | ScopeDef::ModuleDef(ModuleDef::Function(_)) | ||
2511 | | ScopeDef::ModuleDef(ModuleDef::Variant(_)) | ||
2512 | | ScopeDef::ModuleDef(ModuleDef::Const(_)) | ||
2513 | | ScopeDef::ModuleDef(ModuleDef::Static(_)) | ||
2514 | | ScopeDef::GenericParam(GenericParam::ConstParam(_)) | ||
2515 | | ScopeDef::Local(_) | ||
2516 | ) | ||
2517 | } | ||
2499 | } | 2518 | } |
2500 | 2519 | ||
2501 | impl From<ItemInNs> for ScopeDef { | 2520 | impl From<ItemInNs> for ScopeDef { |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index c437dbdc6..d522d5245 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -35,8 +35,9 @@ pub enum PathResolution { | |||
35 | Def(ModuleDef), | 35 | Def(ModuleDef), |
36 | /// A local binding (only value namespace) | 36 | /// A local binding (only value namespace) |
37 | Local(Local), | 37 | Local(Local), |
38 | /// A generic parameter | 38 | /// A type parameter |
39 | TypeParam(TypeParam), | 39 | TypeParam(TypeParam), |
40 | /// A const parameter | ||
40 | ConstParam(ConstParam), | 41 | ConstParam(ConstParam), |
41 | SelfType(Impl), | 42 | SelfType(Impl), |
42 | Macro(MacroDef), | 43 | Macro(MacroDef), |
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 22e196196..e8c2ed48e 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs | |||
@@ -211,62 +211,68 @@ impl SourceToDefCtx<'_, '_> { | |||
211 | 211 | ||
212 | pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { | 212 | pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { |
213 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { | 213 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { |
214 | let res: ChildContainer = match_ast! { | 214 | if let Some(res) = self.container_to_def(container) { |
215 | match (container.value) { | 215 | return Some(res); |
216 | ast::Module(it) => { | 216 | } |
217 | let def = self.module_to_def(container.with_value(it))?; | ||
218 | def.into() | ||
219 | }, | ||
220 | ast::Trait(it) => { | ||
221 | let def = self.trait_to_def(container.with_value(it))?; | ||
222 | def.into() | ||
223 | }, | ||
224 | ast::Impl(it) => { | ||
225 | let def = self.impl_to_def(container.with_value(it))?; | ||
226 | def.into() | ||
227 | }, | ||
228 | ast::Fn(it) => { | ||
229 | let def = self.fn_to_def(container.with_value(it))?; | ||
230 | DefWithBodyId::from(def).into() | ||
231 | }, | ||
232 | ast::Struct(it) => { | ||
233 | let def = self.struct_to_def(container.with_value(it))?; | ||
234 | VariantId::from(def).into() | ||
235 | }, | ||
236 | ast::Enum(it) => { | ||
237 | let def = self.enum_to_def(container.with_value(it))?; | ||
238 | def.into() | ||
239 | }, | ||
240 | ast::Union(it) => { | ||
241 | let def = self.union_to_def(container.with_value(it))?; | ||
242 | VariantId::from(def).into() | ||
243 | }, | ||
244 | ast::Static(it) => { | ||
245 | let def = self.static_to_def(container.with_value(it))?; | ||
246 | DefWithBodyId::from(def).into() | ||
247 | }, | ||
248 | ast::Const(it) => { | ||
249 | let def = self.const_to_def(container.with_value(it))?; | ||
250 | DefWithBodyId::from(def).into() | ||
251 | }, | ||
252 | ast::TypeAlias(it) => { | ||
253 | let def = self.type_alias_to_def(container.with_value(it))?; | ||
254 | def.into() | ||
255 | }, | ||
256 | ast::Variant(it) => { | ||
257 | let def = self.enum_variant_to_def(container.with_value(it))?; | ||
258 | VariantId::from(def).into() | ||
259 | }, | ||
260 | _ => continue, | ||
261 | } | ||
262 | }; | ||
263 | return Some(res); | ||
264 | } | 217 | } |
265 | 218 | ||
266 | let def = self.file_to_def(src.file_id.original_file(self.db.upcast())).get(0).copied()?; | 219 | let def = self.file_to_def(src.file_id.original_file(self.db.upcast())).get(0).copied()?; |
267 | Some(def.into()) | 220 | Some(def.into()) |
268 | } | 221 | } |
269 | 222 | ||
223 | fn container_to_def(&mut self, container: InFile<SyntaxNode>) -> Option<ChildContainer> { | ||
224 | let cont = match_ast! { | ||
225 | match (container.value) { | ||
226 | ast::Module(it) => { | ||
227 | let def = self.module_to_def(container.with_value(it))?; | ||
228 | def.into() | ||
229 | }, | ||
230 | ast::Trait(it) => { | ||
231 | let def = self.trait_to_def(container.with_value(it))?; | ||
232 | def.into() | ||
233 | }, | ||
234 | ast::Impl(it) => { | ||
235 | let def = self.impl_to_def(container.with_value(it))?; | ||
236 | def.into() | ||
237 | }, | ||
238 | ast::Fn(it) => { | ||
239 | let def = self.fn_to_def(container.with_value(it))?; | ||
240 | DefWithBodyId::from(def).into() | ||
241 | }, | ||
242 | ast::Struct(it) => { | ||
243 | let def = self.struct_to_def(container.with_value(it))?; | ||
244 | VariantId::from(def).into() | ||
245 | }, | ||
246 | ast::Enum(it) => { | ||
247 | let def = self.enum_to_def(container.with_value(it))?; | ||
248 | def.into() | ||
249 | }, | ||
250 | ast::Union(it) => { | ||
251 | let def = self.union_to_def(container.with_value(it))?; | ||
252 | VariantId::from(def).into() | ||
253 | }, | ||
254 | ast::Static(it) => { | ||
255 | let def = self.static_to_def(container.with_value(it))?; | ||
256 | DefWithBodyId::from(def).into() | ||
257 | }, | ||
258 | ast::Const(it) => { | ||
259 | let def = self.const_to_def(container.with_value(it))?; | ||
260 | DefWithBodyId::from(def).into() | ||
261 | }, | ||
262 | ast::TypeAlias(it) => { | ||
263 | let def = self.type_alias_to_def(container.with_value(it))?; | ||
264 | def.into() | ||
265 | }, | ||
266 | ast::Variant(it) => { | ||
267 | let def = self.enum_variant_to_def(container.with_value(it))?; | ||
268 | VariantId::from(def).into() | ||
269 | }, | ||
270 | _ => return None, | ||
271 | } | ||
272 | }; | ||
273 | Some(cont) | ||
274 | } | ||
275 | |||
270 | fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> { | 276 | fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> { |
271 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { | 277 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { |
272 | let res: GenericDefId = match_ast! { | 278 | let res: GenericDefId = match_ast! { |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 3ebe194e4..18a05579f 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -85,7 +85,7 @@ impl TokenExpander { | |||
85 | pub trait AstDatabase: SourceDatabase { | 85 | pub trait AstDatabase: SourceDatabase { |
86 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; | 86 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; |
87 | 87 | ||
88 | /// Main public API -- parsis a hir file, not caring whether it's a real | 88 | /// Main public API -- parses a hir file, not caring whether it's a real |
89 | /// file or a macro expansion. | 89 | /// file or a macro expansion. |
90 | #[salsa::transparent] | 90 | #[salsa::transparent] |
91 | fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; | 91 | fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; |
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 3f38e2145..cc43c5772 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs | |||
@@ -2,9 +2,7 @@ use std::iter; | |||
2 | 2 | ||
3 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use ide_db::RootDatabase; | 4 | use ide_db::RootDatabase; |
5 | use syntax::{ | 5 | use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T}; |
6 | ast, match_ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T, | ||
7 | }; | ||
8 | 6 | ||
9 | use crate::FilePosition; | 7 | use crate::FilePosition; |
10 | 8 | ||
@@ -32,25 +30,21 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< | |||
32 | let mut expanded = None; | 30 | let mut expanded = None; |
33 | let mut name = None; | 31 | let mut name = None; |
34 | for node in tok.ancestors() { | 32 | for node in tok.ancestors() { |
35 | match_ast! { | 33 | if let Some(item) = ast::Item::cast(node.clone()) { |
36 | match node { | 34 | expanded = sema.expand_attr_macro(&item); |
37 | ast::MacroCall(mac) => { | 35 | if expanded.is_some() { |
38 | name = Some(mac.path()?.segment()?.name_ref()?.to_string()); | 36 | // FIXME: add the macro name |
39 | expanded = expand_macro_recur(&sema, &mac); | 37 | // FIXME: make this recursive too |
40 | break; | 38 | name = Some("?".to_string()); |
41 | }, | 39 | break; |
42 | ast::Item(item) => { | ||
43 | // FIXME: add the macro name | ||
44 | // FIXME: make this recursive too | ||
45 | name = Some("?".to_string()); | ||
46 | expanded = sema.expand_attr_macro(&item); | ||
47 | if expanded.is_some() { | ||
48 | break; | ||
49 | } | ||
50 | }, | ||
51 | _ => {} | ||
52 | } | 40 | } |
53 | } | 41 | } |
42 | |||
43 | if let Some(mac) = ast::MacroCall::cast(node) { | ||
44 | name = Some(mac.path()?.segment()?.name_ref()?.to_string()); | ||
45 | expanded = expand_macro_recur(&sema, &mac); | ||
46 | break; | ||
47 | } | ||
54 | } | 48 | } |
55 | 49 | ||
56 | // FIXME: | 50 | // FIXME: |
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index 7a4d71e91..fbd499900 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs | |||
@@ -56,10 +56,16 @@ impl Builder { | |||
56 | } | 56 | } |
57 | 57 | ||
58 | impl Completions { | 58 | impl Completions { |
59 | pub(crate) fn add(&mut self, item: CompletionItem) { | 59 | fn add(&mut self, item: CompletionItem) { |
60 | self.buf.push(item) | 60 | self.buf.push(item) |
61 | } | 61 | } |
62 | 62 | ||
63 | fn add_opt(&mut self, item: Option<CompletionItem>) { | ||
64 | if let Some(item) = item { | ||
65 | self.buf.push(item) | ||
66 | } | ||
67 | } | ||
68 | |||
63 | pub(crate) fn add_all<I>(&mut self, items: I) | 69 | pub(crate) fn add_all<I>(&mut self, items: I) |
64 | where | 70 | where |
65 | I: IntoIterator, | 71 | I: IntoIterator, |
@@ -103,9 +109,10 @@ impl Completions { | |||
103 | local_name: hir::Name, | 109 | local_name: hir::Name, |
104 | resolution: &hir::ScopeDef, | 110 | resolution: &hir::ScopeDef, |
105 | ) { | 111 | ) { |
106 | if let Some(item) = render_resolution(RenderContext::new(ctx), local_name, resolution) { | 112 | if ctx.expects_type() && resolution.is_value_def() { |
107 | self.add(item); | 113 | return; |
108 | } | 114 | } |
115 | self.add_opt(render_resolution(RenderContext::new(ctx), local_name, resolution)); | ||
109 | } | 116 | } |
110 | 117 | ||
111 | pub(crate) fn add_macro( | 118 | pub(crate) fn add_macro( |
@@ -118,9 +125,7 @@ impl Completions { | |||
118 | Some(it) => it, | 125 | Some(it) => it, |
119 | None => return, | 126 | None => return, |
120 | }; | 127 | }; |
121 | if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) { | 128 | self.add_opt(render_macro(RenderContext::new(ctx), None, name, macro_)); |
122 | self.add(item); | ||
123 | } | ||
124 | } | 129 | } |
125 | 130 | ||
126 | pub(crate) fn add_function( | 131 | pub(crate) fn add_function( |
@@ -129,9 +134,10 @@ impl Completions { | |||
129 | func: hir::Function, | 134 | func: hir::Function, |
130 | local_name: Option<hir::Name>, | 135 | local_name: Option<hir::Name>, |
131 | ) { | 136 | ) { |
132 | if let Some(item) = render_fn(RenderContext::new(ctx), None, local_name, func) { | 137 | if ctx.expects_type() { |
133 | self.add(item) | 138 | return; |
134 | } | 139 | } |
140 | self.add_opt(render_fn(RenderContext::new(ctx), None, local_name, func)); | ||
135 | } | 141 | } |
136 | 142 | ||
137 | pub(crate) fn add_method( | 143 | pub(crate) fn add_method( |
@@ -141,10 +147,7 @@ impl Completions { | |||
141 | receiver: Option<hir::Name>, | 147 | receiver: Option<hir::Name>, |
142 | local_name: Option<hir::Name>, | 148 | local_name: Option<hir::Name>, |
143 | ) { | 149 | ) { |
144 | if let Some(item) = render_method(RenderContext::new(ctx), None, receiver, local_name, func) | 150 | self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func)); |
145 | { | ||
146 | self.add(item) | ||
147 | } | ||
148 | } | 151 | } |
149 | 152 | ||
150 | pub(crate) fn add_variant_pat( | 153 | pub(crate) fn add_variant_pat( |
@@ -153,9 +156,7 @@ impl Completions { | |||
153 | variant: hir::Variant, | 156 | variant: hir::Variant, |
154 | local_name: Option<hir::Name>, | 157 | local_name: Option<hir::Name>, |
155 | ) { | 158 | ) { |
156 | if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name, None) { | 159 | self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None)); |
157 | self.add(item); | ||
158 | } | ||
159 | } | 160 | } |
160 | 161 | ||
161 | pub(crate) fn add_qualified_variant_pat( | 162 | pub(crate) fn add_qualified_variant_pat( |
@@ -164,9 +165,7 @@ impl Completions { | |||
164 | variant: hir::Variant, | 165 | variant: hir::Variant, |
165 | path: hir::ModPath, | 166 | path: hir::ModPath, |
166 | ) { | 167 | ) { |
167 | if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)) { | 168 | self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path))); |
168 | self.add(item); | ||
169 | } | ||
170 | } | 169 | } |
171 | 170 | ||
172 | pub(crate) fn add_struct_pat( | 171 | pub(crate) fn add_struct_pat( |
@@ -175,21 +174,18 @@ impl Completions { | |||
175 | strukt: hir::Struct, | 174 | strukt: hir::Struct, |
176 | local_name: Option<hir::Name>, | 175 | local_name: Option<hir::Name>, |
177 | ) { | 176 | ) { |
178 | if let Some(item) = render_struct_pat(RenderContext::new(ctx), strukt, local_name) { | 177 | self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name)); |
179 | self.add(item); | ||
180 | } | ||
181 | } | 178 | } |
182 | 179 | ||
183 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { | 180 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { |
184 | if let Some(item) = render_const(RenderContext::new(ctx), constant) { | 181 | if ctx.expects_type() { |
185 | self.add(item); | 182 | return; |
186 | } | 183 | } |
184 | self.add_opt(render_const(RenderContext::new(ctx), constant)); | ||
187 | } | 185 | } |
188 | 186 | ||
189 | pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { | 187 | pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { |
190 | if let Some(item) = render_type_alias(RenderContext::new(ctx), type_alias) { | 188 | self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias)); |
191 | self.add(item) | ||
192 | } | ||
193 | } | 189 | } |
194 | 190 | ||
195 | pub(crate) fn add_qualified_enum_variant( | 191 | pub(crate) fn add_qualified_enum_variant( |
@@ -208,6 +204,9 @@ impl Completions { | |||
208 | variant: hir::Variant, | 204 | variant: hir::Variant, |
209 | local_name: Option<hir::Name>, | 205 | local_name: Option<hir::Name>, |
210 | ) { | 206 | ) { |
207 | if ctx.expects_type() { | ||
208 | return; | ||
209 | } | ||
211 | let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); | 210 | let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); |
212 | self.add(item); | 211 | self.add(item); |
213 | } | 212 | } |
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs index d3392100d..7f76e357e 100644 --- a/crates/ide_completion/src/completions/attribute.rs +++ b/crates/ide_completion/src/completions/attribute.rs | |||
@@ -69,7 +69,7 @@ fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attrib | |||
69 | } | 69 | } |
70 | 70 | ||
71 | if is_inner || !attr_completion.prefer_inner { | 71 | if is_inner || !attr_completion.prefer_inner { |
72 | acc.add(item.build()); | 72 | item.add_to(acc); |
73 | } | 73 | } |
74 | }; | 74 | }; |
75 | 75 | ||
@@ -96,7 +96,7 @@ fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attrib | |||
96 | if let Some(docs) = mac.docs(ctx.sema.db) { | 96 | if let Some(docs) = mac.docs(ctx.sema.db) { |
97 | item.documentation(docs); | 97 | item.documentation(docs); |
98 | } | 98 | } |
99 | acc.add(item.build()); | 99 | item.add_to(acc); |
100 | } | 100 | } |
101 | } | 101 | } |
102 | }); | 102 | }); |
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index 7bf47bf75..c010cbbca 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -90,7 +90,6 @@ | |||
90 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding | 90 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding |
91 | //! capability enabled. | 91 | //! capability enabled. |
92 | 92 | ||
93 | use hir::ModPath; | ||
94 | use ide_db::helpers::{ | 93 | use ide_db::helpers::{ |
95 | import_assets::{ImportAssets, ImportCandidate}, | 94 | import_assets::{ImportAssets, ImportCandidate}, |
96 | insert_use::ImportScope, | 95 | insert_use::ImportScope, |
@@ -208,7 +207,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs | |||
208 | } | 207 | } |
209 | 208 | ||
210 | fn compute_fuzzy_completion_order_key( | 209 | fn compute_fuzzy_completion_order_key( |
211 | proposed_mod_path: &ModPath, | 210 | proposed_mod_path: &hir::ModPath, |
212 | user_input_lowercased: &str, | 211 | user_input_lowercased: &str, |
213 | ) -> usize { | 212 | ) -> usize { |
214 | cov_mark::hit!(certain_fuzzy_order_test); | 213 | cov_mark::hit!(certain_fuzzy_order_test); |
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index 8a728c67e..1daa8595a 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs | |||
@@ -39,7 +39,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
39 | | hir::ModuleDef::Module(..) => refutable, | 39 | | hir::ModuleDef::Module(..) => refutable, |
40 | _ => false, | 40 | _ => false, |
41 | }, | 41 | }, |
42 | hir::ScopeDef::MacroDef(_) => true, | 42 | hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(), |
43 | hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() { | 43 | hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() { |
44 | Some(hir::Adt::Struct(strukt)) => { | 44 | Some(hir::Adt::Struct(strukt)) => { |
45 | acc.add_struct_pat(ctx, strukt, Some(name.clone())); | 45 | acc.add_struct_pat(ctx, strukt, Some(name.clone())); |
@@ -102,6 +102,28 @@ fn foo() { | |||
102 | } | 102 | } |
103 | 103 | ||
104 | #[test] | 104 | #[test] |
105 | fn does_not_complete_non_fn_macros() { | ||
106 | check( | ||
107 | r#" | ||
108 | macro_rules! m { ($e:expr) => { $e } } | ||
109 | enum E { X } | ||
110 | |||
111 | #[rustc_builtin_macro] | ||
112 | macro Clone {} | ||
113 | |||
114 | fn foo() { | ||
115 | match E::X { $0 } | ||
116 | } | ||
117 | "#, | ||
118 | expect![[r#" | ||
119 | ev E::X () | ||
120 | en E | ||
121 | ma m!(…) macro_rules! m | ||
122 | "#]], | ||
123 | ); | ||
124 | } | ||
125 | |||
126 | #[test] | ||
105 | fn completes_in_simple_macro_call() { | 127 | fn completes_in_simple_macro_call() { |
106 | check( | 128 | check( |
107 | r#" | 129 | r#" |
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index d58745fb4..0b0a81410 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs | |||
@@ -26,7 +26,9 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
26 | let module_scope = module.scope(ctx.db, context_module); | 26 | let module_scope = module.scope(ctx.db, context_module); |
27 | for (name, def) in module_scope { | 27 | for (name, def) in module_scope { |
28 | if let hir::ScopeDef::MacroDef(macro_def) = def { | 28 | if let hir::ScopeDef::MacroDef(macro_def) = def { |
29 | acc.add_macro(ctx, Some(name.clone()), macro_def); | 29 | if macro_def.is_fn_like() { |
30 | acc.add_macro(ctx, Some(name.clone()), macro_def); | ||
31 | } | ||
30 | } | 32 | } |
31 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { | 33 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { |
32 | acc.add_resolution(ctx, name, &def); | 34 | acc.add_resolution(ctx, name, &def); |
@@ -58,6 +60,13 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
58 | } | 60 | } |
59 | } | 61 | } |
60 | 62 | ||
63 | if let hir::ScopeDef::MacroDef(macro_def) = def { | ||
64 | if !macro_def.is_fn_like() { | ||
65 | // Don't suggest attribute macros and derives. | ||
66 | continue; | ||
67 | } | ||
68 | } | ||
69 | |||
61 | acc.add_resolution(ctx, name, &def); | 70 | acc.add_resolution(ctx, name, &def); |
62 | } | 71 | } |
63 | } | 72 | } |
@@ -199,6 +208,36 @@ mod tests { | |||
199 | } | 208 | } |
200 | 209 | ||
201 | #[test] | 210 | #[test] |
211 | fn dont_complete_values_in_type_pos() { | ||
212 | check( | ||
213 | r#" | ||
214 | const FOO: () = (); | ||
215 | static BAR: () = (); | ||
216 | struct Baz; | ||
217 | fn foo() { | ||
218 | let _: self::$0; | ||
219 | } | ||
220 | "#, | ||
221 | expect![[r#" | ||
222 | st Baz | ||
223 | "#]], | ||
224 | ); | ||
225 | } | ||
226 | |||
227 | #[test] | ||
228 | fn dont_complete_enum_variants_in_type_pos() { | ||
229 | check( | ||
230 | r#" | ||
231 | enum Foo { Bar } | ||
232 | fn foo() { | ||
233 | let _: Foo::$0; | ||
234 | } | ||
235 | "#, | ||
236 | expect![[r#""#]], | ||
237 | ); | ||
238 | } | ||
239 | |||
240 | #[test] | ||
202 | fn dont_complete_current_use_in_braces_with_glob() { | 241 | fn dont_complete_current_use_in_braces_with_glob() { |
203 | check( | 242 | check( |
204 | r#" | 243 | r#" |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 8b22933e0..1f6c4069f 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -13,7 +13,9 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
13 | // only show macros in {Assoc}ItemList | 13 | // only show macros in {Assoc}ItemList |
14 | ctx.scope.process_all_names(&mut |name, res| { | 14 | ctx.scope.process_all_names(&mut |name, res| { |
15 | if let hir::ScopeDef::MacroDef(mac) = res { | 15 | if let hir::ScopeDef::MacroDef(mac) = res { |
16 | acc.add_macro(ctx, Some(name.clone()), mac); | 16 | if mac.is_fn_like() { |
17 | acc.add_macro(ctx, Some(name.clone()), mac); | ||
18 | } | ||
17 | } | 19 | } |
18 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { | 20 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { |
19 | acc.add_resolution(ctx, name, &res); | 21 | acc.add_resolution(ctx, name, &res); |
@@ -46,7 +48,13 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
46 | cov_mark::hit!(skip_lifetime_completion); | 48 | cov_mark::hit!(skip_lifetime_completion); |
47 | return; | 49 | return; |
48 | } | 50 | } |
49 | acc.add_resolution(ctx, name, &res); | 51 | let add_resolution = match res { |
52 | ScopeDef::MacroDef(mac) => mac.is_fn_like(), | ||
53 | _ => true, | ||
54 | }; | ||
55 | if add_resolution { | ||
56 | acc.add_resolution(ctx, name, &res); | ||
57 | } | ||
50 | }); | 58 | }); |
51 | } | 59 | } |
52 | 60 | ||
@@ -69,6 +77,28 @@ mod tests { | |||
69 | } | 77 | } |
70 | 78 | ||
71 | #[test] | 79 | #[test] |
80 | fn dont_complete_values_in_type_pos() { | ||
81 | check( | ||
82 | r#" | ||
83 | const FOO: () = (); | ||
84 | static BAR: () = (); | ||
85 | enum Foo { | ||
86 | Bar | ||
87 | } | ||
88 | struct Baz; | ||
89 | fn foo() { | ||
90 | let local = (); | ||
91 | let _: $0; | ||
92 | } | ||
93 | "#, | ||
94 | expect![[r#" | ||
95 | en Foo | ||
96 | st Baz | ||
97 | "#]], | ||
98 | ); | ||
99 | } | ||
100 | |||
101 | #[test] | ||
72 | fn only_completes_modules_in_import() { | 102 | fn only_completes_modules_in_import() { |
73 | cov_mark::check!(only_completes_modules_in_import); | 103 | cov_mark::check!(only_completes_modules_in_import); |
74 | check( | 104 | check( |
@@ -339,7 +369,6 @@ fn x() -> $0 | |||
339 | "#, | 369 | "#, |
340 | expect![[r#" | 370 | expect![[r#" |
341 | st Foo | 371 | st Foo |
342 | fn x() fn() | ||
343 | "#]], | 372 | "#]], |
344 | ); | 373 | ); |
345 | } | 374 | } |
@@ -391,7 +420,6 @@ pub mod prelude { | |||
391 | } | 420 | } |
392 | "#, | 421 | "#, |
393 | expect![[r#" | 422 | expect![[r#" |
394 | fn foo() fn() | ||
395 | md std | 423 | md std |
396 | st Option | 424 | st Option |
397 | "#]], | 425 | "#]], |
@@ -427,6 +455,44 @@ mod macros { | |||
427 | } | 455 | } |
428 | 456 | ||
429 | #[test] | 457 | #[test] |
458 | fn does_not_complete_non_fn_macros() { | ||
459 | check( | ||
460 | r#" | ||
461 | #[rustc_builtin_macro] | ||
462 | pub macro Clone {} | ||
463 | |||
464 | fn f() {$0} | ||
465 | "#, | ||
466 | expect![[r#" | ||
467 | fn f() fn() | ||
468 | "#]], | ||
469 | ); | ||
470 | check( | ||
471 | r#" | ||
472 | #[rustc_builtin_macro] | ||
473 | pub macro Clone {} | ||
474 | |||
475 | struct S; | ||
476 | impl S { | ||
477 | $0 | ||
478 | } | ||
479 | "#, | ||
480 | expect![[r#""#]], | ||
481 | ); | ||
482 | check( | ||
483 | r#" | ||
484 | mod m { | ||
485 | #[rustc_builtin_macro] | ||
486 | pub macro Clone {} | ||
487 | } | ||
488 | |||
489 | fn f() {m::$0} | ||
490 | "#, | ||
491 | expect![[r#""#]], | ||
492 | ); | ||
493 | } | ||
494 | |||
495 | #[test] | ||
430 | fn completes_std_prelude_if_core_is_defined() { | 496 | fn completes_std_prelude_if_core_is_defined() { |
431 | check( | 497 | check( |
432 | r#" | 498 | r#" |
@@ -448,7 +514,6 @@ pub mod prelude { | |||
448 | } | 514 | } |
449 | "#, | 515 | "#, |
450 | expect![[r#" | 516 | expect![[r#" |
451 | fn foo() fn() | ||
452 | md std | 517 | md std |
453 | md core | 518 | md core |
454 | st String | 519 | st String |
@@ -509,7 +574,6 @@ macro_rules! foo { () => {} } | |||
509 | fn main() { let x: $0 } | 574 | fn main() { let x: $0 } |
510 | "#, | 575 | "#, |
511 | expect![[r#" | 576 | expect![[r#" |
512 | fn main() fn() | ||
513 | ma foo!(…) macro_rules! foo | 577 | ma foo!(…) macro_rules! foo |
514 | "#]], | 578 | "#]], |
515 | ); | 579 | ); |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 6177caa12..2c2a4aa6b 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -30,19 +30,24 @@ pub(crate) enum PatternRefutability { | |||
30 | } | 30 | } |
31 | 31 | ||
32 | #[derive(Debug)] | 32 | #[derive(Debug)] |
33 | pub(super) enum PathKind { | ||
34 | Expr, | ||
35 | Type, | ||
36 | } | ||
37 | |||
38 | #[derive(Debug)] | ||
33 | pub(crate) struct PathCompletionContext { | 39 | pub(crate) struct PathCompletionContext { |
34 | /// If this is a call with () already there | 40 | /// If this is a call with () already there |
35 | call_kind: Option<CallKind>, | 41 | call_kind: Option<CallKind>, |
36 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | 42 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. |
37 | pub(super) is_trivial_path: bool, | 43 | pub(super) is_trivial_path: bool, |
38 | /// If not a trivial path, the prefix (qualifier). | 44 | /// If not a trivial path, the prefix (qualifier). |
39 | pub(super) path_qual: Option<ast::Path>, | 45 | pub(super) qualifier: Option<ast::Path>, |
40 | pub(super) is_path_type: bool, | 46 | pub(super) kind: Option<PathKind>, |
47 | /// Whether the path segment has type args or not. | ||
41 | pub(super) has_type_args: bool, | 48 | pub(super) has_type_args: bool, |
42 | /// `true` if we are a statement or a last expr in the block. | 49 | /// `true` if we are a statement or a last expr in the block. |
43 | pub(super) can_be_stmt: bool, | 50 | pub(super) can_be_stmt: bool, |
44 | /// `true` if we expect an expression at the cursor position. | ||
45 | pub(super) is_expr: bool, | ||
46 | pub(super) in_loop_body: bool, | 51 | pub(super) in_loop_body: bool, |
47 | } | 52 | } |
48 | 53 | ||
@@ -308,7 +313,11 @@ impl<'a> CompletionContext<'a> { | |||
308 | } | 313 | } |
309 | 314 | ||
310 | pub(crate) fn expects_expression(&self) -> bool { | 315 | pub(crate) fn expects_expression(&self) -> bool { |
311 | self.path_context.as_ref().map_or(false, |it| it.is_expr) | 316 | matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Expr), .. })) |
317 | } | ||
318 | |||
319 | pub(crate) fn expects_type(&self) -> bool { | ||
320 | matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), .. })) | ||
312 | } | 321 | } |
313 | 322 | ||
314 | pub(crate) fn path_call_kind(&self) -> Option<CallKind> { | 323 | pub(crate) fn path_call_kind(&self) -> Option<CallKind> { |
@@ -316,11 +325,11 @@ impl<'a> CompletionContext<'a> { | |||
316 | } | 325 | } |
317 | 326 | ||
318 | pub(crate) fn is_trivial_path(&self) -> bool { | 327 | pub(crate) fn is_trivial_path(&self) -> bool { |
319 | self.path_context.as_ref().map_or(false, |it| it.is_trivial_path) | 328 | matches!(self.path_context, Some(PathCompletionContext { is_trivial_path: true, .. })) |
320 | } | 329 | } |
321 | 330 | ||
322 | pub(crate) fn path_qual(&self) -> Option<&ast::Path> { | 331 | pub(crate) fn path_qual(&self) -> Option<&ast::Path> { |
323 | self.path_context.as_ref().and_then(|it| it.path_qual.as_ref()) | 332 | self.path_context.as_ref().and_then(|it| it.qualifier.as_ref()) |
324 | } | 333 | } |
325 | 334 | ||
326 | fn fill_impl_def(&mut self) { | 335 | fn fill_impl_def(&mut self) { |
@@ -573,12 +582,11 @@ impl<'a> CompletionContext<'a> { | |||
573 | let path_ctx = self.path_context.get_or_insert(PathCompletionContext { | 582 | let path_ctx = self.path_context.get_or_insert(PathCompletionContext { |
574 | call_kind: None, | 583 | call_kind: None, |
575 | is_trivial_path: false, | 584 | is_trivial_path: false, |
576 | path_qual: None, | 585 | qualifier: None, |
577 | has_type_args: false, | 586 | has_type_args: false, |
578 | is_path_type: false, | ||
579 | can_be_stmt: false, | 587 | can_be_stmt: false, |
580 | is_expr: false, | ||
581 | in_loop_body: false, | 588 | in_loop_body: false, |
589 | kind: None, | ||
582 | }); | 590 | }); |
583 | path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax()); | 591 | path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax()); |
584 | let path = segment.parent_path(); | 592 | let path = segment.parent_path(); |
@@ -593,11 +601,20 @@ impl<'a> CompletionContext<'a> { | |||
593 | } | 601 | } |
594 | }; | 602 | }; |
595 | } | 603 | } |
596 | path_ctx.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | 604 | |
605 | if let Some(parent) = path.syntax().parent() { | ||
606 | path_ctx.kind = match_ast! { | ||
607 | match parent { | ||
608 | ast::PathType(_it) => Some(PathKind::Type), | ||
609 | ast::PathExpr(_it) => Some(PathKind::Expr), | ||
610 | _ => None, | ||
611 | } | ||
612 | }; | ||
613 | } | ||
597 | path_ctx.has_type_args = segment.generic_arg_list().is_some(); | 614 | path_ctx.has_type_args = segment.generic_arg_list().is_some(); |
598 | 615 | ||
599 | if let Some(path) = path_or_use_tree_qualifier(&path) { | 616 | if let Some(path) = path_or_use_tree_qualifier(&path) { |
600 | path_ctx.path_qual = path | 617 | path_ctx.qualifier = path |
601 | .segment() | 618 | .segment() |
602 | .and_then(|it| { | 619 | .and_then(|it| { |
603 | find_node_with_range::<ast::PathSegment>( | 620 | find_node_with_range::<ast::PathSegment>( |
@@ -635,7 +652,6 @@ impl<'a> CompletionContext<'a> { | |||
635 | None | 652 | None |
636 | }) | 653 | }) |
637 | .unwrap_or(false); | 654 | .unwrap_or(false); |
638 | path_ctx.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); | ||
639 | } | 655 | } |
640 | } | 656 | } |
641 | } | 657 | } |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 750694432..7118183fe 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -18,6 +18,7 @@ use ide_db::{ | |||
18 | use syntax::TextRange; | 18 | use syntax::TextRange; |
19 | 19 | ||
20 | use crate::{ | 20 | use crate::{ |
21 | context::{PathCompletionContext, PathKind}, | ||
21 | item::{CompletionRelevanceTypeMatch, ImportEdit}, | 22 | item::{CompletionRelevanceTypeMatch, ImportEdit}, |
22 | render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}, | 23 | render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}, |
23 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, | 24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, |
@@ -54,6 +55,9 @@ pub(crate) fn render_resolution_with_import<'a>( | |||
54 | import_edit: ImportEdit, | 55 | import_edit: ImportEdit, |
55 | ) -> Option<CompletionItem> { | 56 | ) -> Option<CompletionItem> { |
56 | let resolution = hir::ScopeDef::from(import_edit.import.original_item); | 57 | let resolution = hir::ScopeDef::from(import_edit.import.original_item); |
58 | if ctx.completion.expects_type() && resolution.is_value_def() { | ||
59 | return None; | ||
60 | } | ||
57 | let local_name = match resolution { | 61 | let local_name = match resolution { |
58 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db), | 62 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db), |
59 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?, | 63 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?, |
@@ -275,13 +279,10 @@ impl<'a> Render<'a> { | |||
275 | }; | 279 | }; |
276 | 280 | ||
277 | // Add `<>` for generic types | 281 | // Add `<>` for generic types |
278 | if self | 282 | if matches!( |
279 | .ctx | 283 | self.ctx.completion.path_context, |
280 | .completion | 284 | Some(PathCompletionContext { kind: Some(PathKind::Type), has_type_args: false, .. }) |
281 | .path_context | 285 | ) && self.ctx.completion.config.add_call_parenthesis |
282 | .as_ref() | ||
283 | .map_or(false, |it| it.is_path_type && !it.has_type_args) | ||
284 | && self.ctx.completion.config.add_call_parenthesis | ||
285 | { | 286 | { |
286 | if let Some(cap) = self.ctx.snippet_cap() { | 287 | if let Some(cap) = self.ctx.snippet_cap() { |
287 | let has_non_default_type_params = match resolution { | 288 | let has_non_default_type_params = match resolution { |
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index aa61c5bcb..10bbafe77 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs | |||
@@ -120,15 +120,19 @@ impl ImportScope { | |||
120 | if eq_visibility(prev_vis, curr_vis.clone()) && eq_attrs(prev_attrs, curr_attrs.clone()) | 120 | if eq_visibility(prev_vis, curr_vis.clone()) && eq_attrs(prev_attrs, curr_attrs.clone()) |
121 | { | 121 | { |
122 | if let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) { | 122 | if let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) { |
123 | if let Some(_) = common_prefix(&prev_path, &curr_path) { | 123 | if let Some((prev_prefix, _)) = common_prefix(&prev_path, &curr_path) { |
124 | if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() { | 124 | if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() { |
125 | // Same prefix but no use tree lists so this has to be of item style. | 125 | let prefix_c = prev_prefix.qualifiers().count(); |
126 | break ImportGranularityGuess::Item; // this overwrites CrateOrModule, technically the file doesn't adhere to anything here. | 126 | let curr_c = curr_path.qualifiers().count() - prefix_c; |
127 | } else { | 127 | let prev_c = prev_path.qualifiers().count() - prefix_c; |
128 | // Same prefix with item tree lists, has to be module style as it | 128 | if curr_c <= 1 || prev_c <= 1 { |
129 | // can't be crate style since the trees wouldn't share a prefix then. | 129 | // Same prefix but no use tree lists so this has to be of item style. |
130 | break ImportGranularityGuess::Module; | 130 | break ImportGranularityGuess::Item; // this overwrites CrateOrModule, technically the file doesn't adhere to anything here. |
131 | } | ||
131 | } | 132 | } |
133 | // Same prefix with item tree lists, has to be module style as it | ||
134 | // can't be crate style since the trees wouldn't share a prefix then. | ||
135 | break ImportGranularityGuess::Module; | ||
132 | } | 136 | } |
133 | } | 137 | } |
134 | } | 138 | } |
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs index 78a2a87b3..70b11bf81 100644 --- a/crates/ide_db/src/helpers/insert_use/tests.rs +++ b/crates/ide_db/src/helpers/insert_use/tests.rs | |||
@@ -663,6 +663,13 @@ use foo::bar::qux; | |||
663 | ", | 663 | ", |
664 | ImportGranularityGuess::Item, | 664 | ImportGranularityGuess::Item, |
665 | ); | 665 | ); |
666 | check_guess( | ||
667 | r" | ||
668 | use foo::bar::Bar; | ||
669 | use foo::baz; | ||
670 | ", | ||
671 | ImportGranularityGuess::Item, | ||
672 | ); | ||
666 | } | 673 | } |
667 | 674 | ||
668 | #[test] | 675 | #[test] |
@@ -682,6 +689,14 @@ use foo::{baz::{qux, quux}, bar}; | |||
682 | ", | 689 | ", |
683 | ImportGranularityGuess::Module, | 690 | ImportGranularityGuess::Module, |
684 | ); | 691 | ); |
692 | check_guess( | ||
693 | r" | ||
694 | use foo::bar::Bar; | ||
695 | use foo::baz::Baz; | ||
696 | use foo::{Foo, Qux}; | ||
697 | ", | ||
698 | ImportGranularityGuess::Module, | ||
699 | ); | ||
685 | } | 700 | } |
686 | 701 | ||
687 | #[test] | 702 | #[test] |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 884fe0739..a60bc5ad9 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -259,11 +259,14 @@ impl ast::Path { | |||
259 | } | 259 | } |
260 | 260 | ||
261 | pub fn segments(&self) -> impl Iterator<Item = ast::PathSegment> + Clone { | 261 | pub fn segments(&self) -> impl Iterator<Item = ast::PathSegment> + Clone { |
262 | // cant make use of SyntaxNode::siblings, because the returned Iterator is not clone | ||
263 | successors(self.first_segment(), |p| { | 262 | successors(self.first_segment(), |p| { |
264 | p.parent_path().parent_path().and_then(|p| p.segment()) | 263 | p.parent_path().parent_path().and_then(|p| p.segment()) |
265 | }) | 264 | }) |
266 | } | 265 | } |
266 | |||
267 | pub fn qualifiers(&self) -> impl Iterator<Item = ast::Path> + Clone { | ||
268 | successors(self.qualifier(), |p| p.qualifier()) | ||
269 | } | ||
267 | } | 270 | } |
268 | impl ast::UseTree { | 271 | impl ast::UseTree { |
269 | pub fn is_simple_path(&self) -> bool { | 272 | pub fn is_simple_path(&self) -> bool { |