aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src')
-rw-r--r--crates/completion/src/completions.rs168
-rw-r--r--crates/completion/src/completions/attribute.rs554
-rw-r--r--crates/completion/src/completions/dot.rs431
-rw-r--r--crates/completion/src/completions/flyimport.rs291
-rw-r--r--crates/completion/src/completions/fn_param.rs135
-rw-r--r--crates/completion/src/completions/keyword.rs660
-rw-r--r--crates/completion/src/completions/macro_in_item_position.rs41
-rw-r--r--crates/completion/src/completions/mod_.rs313
-rw-r--r--crates/completion/src/completions/pattern.rs261
-rw-r--r--crates/completion/src/completions/postfix.rs565
-rw-r--r--crates/completion/src/completions/postfix/format_like.rs287
-rw-r--r--crates/completion/src/completions/qualified_path.rs783
-rw-r--r--crates/completion/src/completions/record.rs392
-rw-r--r--crates/completion/src/completions/snippet.rs116
-rw-r--r--crates/completion/src/completions/trait_impl.rs741
-rw-r--r--crates/completion/src/completions/unqualified_path.rs737
-rw-r--r--crates/completion/src/config.rs17
-rw-r--r--crates/completion/src/context.rs531
-rw-r--r--crates/completion/src/generated_lint_completions.rs5
-rw-r--r--crates/completion/src/item.rs444
-rw-r--r--crates/completion/src/lib.rs285
-rw-r--r--crates/completion/src/patterns.rs249
-rw-r--r--crates/completion/src/render.rs879
-rw-r--r--crates/completion/src/render/builder_ext.rs94
-rw-r--r--crates/completion/src/render/const_.rs55
-rw-r--r--crates/completion/src/render/enum_variant.rs130
-rw-r--r--crates/completion/src/render/function.rs341
-rw-r--r--crates/completion/src/render/macro_.rs213
-rw-r--r--crates/completion/src/render/pattern.rs146
-rw-r--r--crates/completion/src/render/type_alias.rs55
-rw-r--r--crates/completion/src/test_utils.rs150
31 files changed, 0 insertions, 10069 deletions
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
deleted file mode 100644
index c3ce6e51d..000000000
--- a/crates/completion/src/completions.rs
+++ /dev/null
@@ -1,168 +0,0 @@
1//! This module defines an accumulator for completions which are going to be presented to user.
2
3pub(crate) mod attribute;
4pub(crate) mod dot;
5pub(crate) mod record;
6pub(crate) mod pattern;
7pub(crate) mod fn_param;
8pub(crate) mod keyword;
9pub(crate) mod snippet;
10pub(crate) mod qualified_path;
11pub(crate) mod unqualified_path;
12pub(crate) mod postfix;
13pub(crate) mod macro_in_item_position;
14pub(crate) mod trait_impl;
15pub(crate) mod mod_;
16pub(crate) mod flyimport;
17
18use hir::{ModPath, ScopeDef, Type};
19
20use crate::{
21 item::Builder,
22 render::{
23 const_::render_const,
24 enum_variant::render_variant,
25 function::render_fn,
26 macro_::render_macro,
27 pattern::{render_struct_pat, render_variant_pat},
28 render_field, render_resolution, render_tuple_field,
29 type_alias::render_type_alias,
30 RenderContext,
31 },
32 CompletionContext, CompletionItem,
33};
34
35/// Represents an in-progress set of completions being built.
36#[derive(Debug, Default)]
37pub struct Completions {
38 buf: Vec<CompletionItem>,
39}
40
41impl Into<Vec<CompletionItem>> for Completions {
42 fn into(self) -> Vec<CompletionItem> {
43 self.buf
44 }
45}
46
47impl Builder {
48 /// Convenience method, which allows to add a freshly created completion into accumulator
49 /// without binding it to the variable.
50 pub(crate) fn add_to(self, acc: &mut Completions) {
51 acc.add(self.build())
52 }
53}
54
55impl Completions {
56 pub(crate) fn add(&mut self, item: CompletionItem) {
57 self.buf.push(item.into())
58 }
59
60 pub(crate) fn add_all<I>(&mut self, items: I)
61 where
62 I: IntoIterator,
63 I::Item: Into<CompletionItem>,
64 {
65 items.into_iter().for_each(|item| self.add(item.into()))
66 }
67
68 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) {
69 let item = render_field(RenderContext::new(ctx), field, ty);
70 self.add(item);
71 }
72
73 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) {
74 let item = render_tuple_field(RenderContext::new(ctx), field, ty);
75 self.add(item);
76 }
77
78 pub(crate) fn add_resolution(
79 &mut self,
80 ctx: &CompletionContext,
81 local_name: String,
82 resolution: &ScopeDef,
83 ) {
84 if let Some(item) = render_resolution(RenderContext::new(ctx), local_name, resolution) {
85 self.add(item);
86 }
87 }
88
89 pub(crate) fn add_macro(
90 &mut self,
91 ctx: &CompletionContext,
92 name: Option<String>,
93 macro_: hir::MacroDef,
94 ) {
95 let name = match name {
96 Some(it) => it,
97 None => return,
98 };
99 if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) {
100 self.add(item);
101 }
102 }
103
104 pub(crate) fn add_function(
105 &mut self,
106 ctx: &CompletionContext,
107 func: hir::Function,
108 local_name: Option<String>,
109 ) {
110 if let Some(item) = render_fn(RenderContext::new(ctx), None, local_name, func) {
111 self.add(item)
112 }
113 }
114
115 pub(crate) fn add_variant_pat(
116 &mut self,
117 ctx: &CompletionContext,
118 variant: hir::Variant,
119 local_name: Option<hir::Name>,
120 ) {
121 if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name) {
122 self.add(item);
123 }
124 }
125
126 pub(crate) fn add_struct_pat(
127 &mut self,
128 ctx: &CompletionContext,
129 strukt: hir::Struct,
130 local_name: Option<hir::Name>,
131 ) {
132 if let Some(item) = render_struct_pat(RenderContext::new(ctx), strukt, local_name) {
133 self.add(item);
134 }
135 }
136
137 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
138 if let Some(item) = render_const(RenderContext::new(ctx), constant) {
139 self.add(item);
140 }
141 }
142
143 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
144 if let Some(item) = render_type_alias(RenderContext::new(ctx), type_alias) {
145 self.add(item)
146 }
147 }
148
149 pub(crate) fn add_qualified_enum_variant(
150 &mut self,
151 ctx: &CompletionContext,
152 variant: hir::Variant,
153 path: ModPath,
154 ) {
155 let item = render_variant(RenderContext::new(ctx), None, None, variant, Some(path));
156 self.add(item);
157 }
158
159 pub(crate) fn add_enum_variant(
160 &mut self,
161 ctx: &CompletionContext,
162 variant: hir::Variant,
163 local_name: Option<String>,
164 ) {
165 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None);
166 self.add(item);
167 }
168}
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs
deleted file mode 100644
index e5522980d..000000000
--- a/crates/completion/src/completions/attribute.rs
+++ /dev/null
@@ -1,554 +0,0 @@
1//! Completion for attributes
2//!
3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes.
5
6use itertools::Itertools;
7use rustc_hash::FxHashSet;
8use syntax::{ast, AstNode, T};
9
10use crate::{
11 context::CompletionContext,
12 generated_lint_completions::{CLIPPY_LINTS, FEATURES},
13 item::{CompletionItem, CompletionItemKind, CompletionKind},
14 Completions,
15};
16
17pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
18 if ctx.mod_declaration_under_caret.is_some() {
19 return None;
20 }
21
22 let attribute = ctx.attribute_under_caret.as_ref()?;
23 match (attribute.path(), attribute.token_tree()) {
24 (Some(path), Some(token_tree)) => {
25 let path = path.syntax().text();
26 if path == "derive" {
27 complete_derive(acc, ctx, token_tree)
28 } else if path == "feature" {
29 complete_lint(acc, ctx, token_tree, FEATURES)
30 } else if path == "allow" || path == "warn" || path == "deny" || path == "forbid" {
31 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS);
32 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
33 }
34 }
35 (_, Some(_token_tree)) => {}
36 _ => complete_attribute_start(acc, ctx, attribute),
37 }
38 Some(())
39}
40
41fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
42 for attr_completion in ATTRIBUTES {
43 let mut item = CompletionItem::new(
44 CompletionKind::Attribute,
45 ctx.source_range(),
46 attr_completion.label,
47 )
48 .kind(CompletionItemKind::Attribute);
49
50 if let Some(lookup) = attr_completion.lookup {
51 item = item.lookup_by(lookup);
52 }
53
54 if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) {
55 item = item.insert_snippet(cap, snippet);
56 }
57
58 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
59 acc.add(item.build());
60 }
61 }
62}
63
64struct AttrCompletion {
65 label: &'static str,
66 lookup: Option<&'static str>,
67 snippet: Option<&'static str>,
68 prefer_inner: bool,
69}
70
71impl AttrCompletion {
72 const fn prefer_inner(self) -> AttrCompletion {
73 AttrCompletion { prefer_inner: true, ..self }
74 }
75}
76
77const fn attr(
78 label: &'static str,
79 lookup: Option<&'static str>,
80 snippet: Option<&'static str>,
81) -> AttrCompletion {
82 AttrCompletion { label, lookup, snippet, prefer_inner: false }
83}
84
85/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index
86const ATTRIBUTES: &[AttrCompletion] = &[
87 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
88 attr("automatically_derived", None, None),
89 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
90 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
91 attr("cold", None, None),
92 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#))
93 .prefer_inner(),
94 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
95 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)),
96 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
97 attr(
98 r#"export_name = "…""#,
99 Some("export_name"),
100 Some(r#"export_name = "${0:exported_symbol_name}""#),
101 ),
102 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
103 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
104 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
105 // FIXME: resolve through macro resolution?
106 attr("global_allocator", None, None).prefer_inner(),
107 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
108 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
109 attr("link", None, None),
110 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
111 attr(
112 r#"link_section = "…""#,
113 Some("link_section"),
114 Some(r#"link_section = "${0:section_name}""#),
115 ),
116 attr("macro_export", None, None),
117 attr("macro_use", None, None),
118 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)),
119 attr("no_link", None, None).prefer_inner(),
120 attr("no_implicit_prelude", None, None).prefer_inner(),
121 attr("no_main", None, None).prefer_inner(),
122 attr("no_mangle", None, None),
123 attr("no_std", None, None).prefer_inner(),
124 attr("non_exhaustive", None, None),
125 attr("panic_handler", None, None).prefer_inner(),
126 attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)),
127 attr("proc_macro", None, None),
128 attr("proc_macro_attribute", None, None),
129 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
130 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
131 .prefer_inner(),
132 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
133 attr("should_panic", Some("should_panic"), Some(r#"should_panic"#)),
134 attr(
135 r#"target_feature = "…""#,
136 Some("target_feature"),
137 Some(r#"target_feature = "${0:feature}""#),
138 ),
139 attr("test", None, None),
140 attr("track_caller", None, None),
141 attr("type_length_limit = …", Some("type_length_limit"), Some("type_length_limit = ${0:128}"))
142 .prefer_inner(),
143 attr("used", None, None),
144 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
145 attr(
146 r#"windows_subsystem = "…""#,
147 Some("windows_subsystem"),
148 Some(r#"windows_subsystem = "${0:subsystem}""#),
149 )
150 .prefer_inner(),
151];
152
153fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
154 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
155 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
156 .iter()
157 .filter(|completion| !existing_derives.contains(completion.label))
158 {
159 let mut components = vec![derive_completion.label];
160 components.extend(
161 derive_completion
162 .dependencies
163 .iter()
164 .filter(|&&dependency| !existing_derives.contains(dependency)),
165 );
166 let lookup = components.join(", ");
167 let label = components.iter().rev().join(", ");
168 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
169 .lookup_by(lookup)
170 .kind(CompletionItemKind::Attribute)
171 .add_to(acc)
172 }
173
174 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
175 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), custom_derive_name)
176 .kind(CompletionItemKind::Attribute)
177 .add_to(acc)
178 }
179 }
180}
181
182fn complete_lint(
183 acc: &mut Completions,
184 ctx: &CompletionContext,
185 derive_input: ast::TokenTree,
186 lints_completions: &[LintCompletion],
187) {
188 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
189 for lint_completion in lints_completions
190 .into_iter()
191 .filter(|completion| !existing_lints.contains(completion.label))
192 {
193 CompletionItem::new(
194 CompletionKind::Attribute,
195 ctx.source_range(),
196 lint_completion.label,
197 )
198 .kind(CompletionItemKind::Attribute)
199 .detail(lint_completion.description)
200 .add_to(acc)
201 }
202 }
203}
204
205fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
206 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
207 (Some(left_paren), Some(right_paren))
208 if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] =>
209 {
210 let mut input_derives = FxHashSet::default();
211 let mut current_derive = String::new();
212 for token in derive_input
213 .syntax()
214 .children_with_tokens()
215 .filter_map(|token| token.into_token())
216 .skip_while(|token| token != &left_paren)
217 .skip(1)
218 .take_while(|token| token != &right_paren)
219 {
220 if T![,] == token.kind() {
221 if !current_derive.is_empty() {
222 input_derives.insert(current_derive);
223 current_derive = String::new();
224 }
225 } else {
226 current_derive.push_str(token.text().trim());
227 }
228 }
229
230 if !current_derive.is_empty() {
231 input_derives.insert(current_derive);
232 }
233 Ok(input_derives)
234 }
235 _ => Err(()),
236 }
237}
238
239fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
240 let mut result = FxHashSet::default();
241 ctx.scope.process_all_names(&mut |name, scope_def| {
242 if let hir::ScopeDef::MacroDef(mac) = scope_def {
243 if mac.is_derive_macro() {
244 result.insert(name.to_string());
245 }
246 }
247 });
248 result
249}
250
251struct DeriveCompletion {
252 label: &'static str,
253 dependencies: &'static [&'static str],
254}
255
256/// Standard Rust derives and the information about their dependencies
257/// (the dependencies are needed so that the main derive don't break the compilation when added)
258const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
259 DeriveCompletion { label: "Clone", dependencies: &[] },
260 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
261 DeriveCompletion { label: "Debug", dependencies: &[] },
262 DeriveCompletion { label: "Default", dependencies: &[] },
263 DeriveCompletion { label: "Hash", dependencies: &[] },
264 DeriveCompletion { label: "PartialEq", dependencies: &[] },
265 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
266 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
267 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
268];
269
270pub(crate) struct LintCompletion {
271 pub(crate) label: &'static str,
272 pub(crate) description: &'static str,
273}
274
275#[rustfmt::skip]
276const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
277 LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# },
278 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
279 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
280 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
281 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
282 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
283 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
284 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
285 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
286 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
287 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
288 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
289 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
290 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
291 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
292 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
293 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
294 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
295 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
296 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
297 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
298 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
299 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
300 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
301 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
302 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
303 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
304 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
305 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
306 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
307 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
308 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
309 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
310 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
311 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
312 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
313 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
314 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
315 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
316 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
317 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
318 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
319 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
320 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
321 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
322 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
323 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
324 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
325 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
326 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
327 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
328 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
329 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
330 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
331 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
332 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
333 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
334 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
335 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
336 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
337 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
338 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
339 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
340 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
341 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
342 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
343 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
344 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
345 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
346 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
347 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
348 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
349 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
350 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
351 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
352 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
353 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
354 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
355 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
356 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
357 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
358 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
359 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
360 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
361 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
362 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
363 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
364 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
365 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
366 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
367 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
368 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
369 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
370 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
371 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
372 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
373 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
374 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
375 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
376 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
377 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
378 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
379 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
380 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
381 LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# },
382 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
383 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
384 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
385 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
386 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
387 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
388 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
389 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
390 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
391 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
392];
393
394#[cfg(test)]
395mod tests {
396 use expect_test::{expect, Expect};
397
398 use crate::{test_utils::completion_list, CompletionKind};
399
400 fn check(ra_fixture: &str, expect: Expect) {
401 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
402 expect.assert_eq(&actual);
403 }
404
405 #[test]
406 fn empty_derive_completion() {
407 check(
408 r#"
409#[derive($0)]
410struct Test {}
411 "#,
412 expect![[r#"
413 at Clone
414 at Clone, Copy
415 at Debug
416 at Default
417 at Hash
418 at PartialEq
419 at PartialEq, Eq
420 at PartialEq, PartialOrd
421 at PartialEq, Eq, PartialOrd, Ord
422 "#]],
423 );
424 }
425
426 #[test]
427 fn no_completion_for_incorrect_derive() {
428 check(
429 r#"
430#[derive{$0)]
431struct Test {}
432"#,
433 expect![[r#""#]],
434 )
435 }
436
437 #[test]
438 fn derive_with_input_completion() {
439 check(
440 r#"
441#[derive(serde::Serialize, PartialEq, $0)]
442struct Test {}
443"#,
444 expect![[r#"
445 at Clone
446 at Clone, Copy
447 at Debug
448 at Default
449 at Hash
450 at Eq
451 at PartialOrd
452 at Eq, PartialOrd, Ord
453 "#]],
454 )
455 }
456
457 #[test]
458 fn test_attribute_completion() {
459 check(
460 r#"#[$0]"#,
461 expect![[r#"
462 at allow(…)
463 at automatically_derived
464 at cfg_attr(…)
465 at cfg(…)
466 at cold
467 at deny(…)
468 at deprecated
469 at derive(…)
470 at export_name = "…"
471 at doc = "…"
472 at forbid(…)
473 at ignore = "…"
474 at inline(…)
475 at link
476 at link_name = "…"
477 at link_section = "…"
478 at macro_export
479 at macro_use
480 at must_use
481 at no_mangle
482 at non_exhaustive
483 at path = "…"
484 at proc_macro
485 at proc_macro_attribute
486 at proc_macro_derive(…)
487 at repr(…)
488 at should_panic
489 at target_feature = "…"
490 at test
491 at track_caller
492 at used
493 at warn(…)
494 "#]],
495 )
496 }
497
498 #[test]
499 fn test_attribute_completion_inside_nested_attr() {
500 check(r#"#[cfg($0)]"#, expect![[]])
501 }
502
503 #[test]
504 fn test_inner_attribute_completion() {
505 check(
506 r"#![$0]",
507 expect![[r#"
508 at allow(…)
509 at automatically_derived
510 at cfg_attr(…)
511 at cfg(…)
512 at cold
513 at crate_name = ""
514 at deny(…)
515 at deprecated
516 at derive(…)
517 at export_name = "…"
518 at doc = "…"
519 at feature(…)
520 at forbid(…)
521 at global_allocator
522 at ignore = "…"
523 at inline(…)
524 at link
525 at link_name = "…"
526 at link_section = "…"
527 at macro_export
528 at macro_use
529 at must_use
530 at no_link
531 at no_implicit_prelude
532 at no_main
533 at no_mangle
534 at no_std
535 at non_exhaustive
536 at panic_handler
537 at path = "…"
538 at proc_macro
539 at proc_macro_attribute
540 at proc_macro_derive(…)
541 at recursion_limit = …
542 at repr(…)
543 at should_panic
544 at target_feature = "…"
545 at test
546 at track_caller
547 at type_length_limit = …
548 at used
549 at warn(…)
550 at windows_subsystem = "…"
551 "#]],
552 );
553 }
554}
diff --git a/crates/completion/src/completions/dot.rs b/crates/completion/src/completions/dot.rs
deleted file mode 100644
index d04eef65a..000000000
--- a/crates/completion/src/completions/dot.rs
+++ /dev/null
@@ -1,431 +0,0 @@
1//! Completes references after dot (fields and method calls).
2
3use hir::{HasVisibility, Type};
4use rustc_hash::FxHashSet;
5use test_utils::mark;
6
7use crate::{context::CompletionContext, Completions};
8
9/// Complete dot accesses, i.e. fields or methods.
10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
11 let dot_receiver = match &ctx.dot_receiver {
12 Some(expr) => expr,
13 _ => return,
14 };
15
16 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
17 Some(ty) => ty,
18 _ => return,
19 };
20
21 if ctx.is_call {
22 mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else {
24 complete_fields(acc, ctx, &receiver_ty);
25 }
26 complete_methods(acc, ctx, &receiver_ty);
27}
28
29fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
30 for receiver in receiver.autoderef(ctx.db) {
31 for (field, ty) in receiver.fields(ctx.db) {
32 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
33 // Skip private field. FIXME: If the definition location of the
34 // field is editable, we should show the completion
35 continue;
36 }
37 acc.add_field(ctx, field, &ty);
38 }
39 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
40 // FIXME: Handle visibility
41 acc.add_tuple_field(ctx, i, &ty);
42 }
43 }
44}
45
46fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
47 if let Some(krate) = ctx.krate {
48 let mut seen_methods = FxHashSet::default();
49 let traits_in_scope = ctx.scope.traits_in_scope();
50 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
51 if func.self_param(ctx.db).is_some()
52 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
53 && seen_methods.insert(func.name(ctx.db))
54 {
55 acc.add_function(ctx, func, None);
56 }
57 None::<()>
58 });
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use expect_test::{expect, Expect};
65 use test_utils::mark;
66
67 use crate::{test_utils::completion_list, CompletionKind};
68
69 fn check(ra_fixture: &str, expect: Expect) {
70 let actual = completion_list(ra_fixture, CompletionKind::Reference);
71 expect.assert_eq(&actual);
72 }
73
74 #[test]
75 fn test_struct_field_and_method_completion() {
76 check(
77 r#"
78struct S { foo: u32 }
79impl S {
80 fn bar(&self) {}
81}
82fn foo(s: S) { s.$0 }
83"#,
84 expect![[r#"
85 fd foo u32
86 me bar() fn bar(&self)
87 "#]],
88 );
89 }
90
91 #[test]
92 fn test_struct_field_completion_self() {
93 check(
94 r#"
95struct S { the_field: (u32,) }
96impl S {
97 fn foo(self) { self.$0 }
98}
99"#,
100 expect![[r#"
101 fd the_field (u32,)
102 me foo() fn foo(self)
103 "#]],
104 )
105 }
106
107 #[test]
108 fn test_struct_field_completion_autoderef() {
109 check(
110 r#"
111struct A { the_field: (u32, i32) }
112impl A {
113 fn foo(&self) { self.$0 }
114}
115"#,
116 expect![[r#"
117 fd the_field (u32, i32)
118 me foo() fn foo(&self)
119 "#]],
120 )
121 }
122
123 #[test]
124 fn test_no_struct_field_completion_for_method_call() {
125 mark::check!(test_no_struct_field_completion_for_method_call);
126 check(
127 r#"
128struct A { the_field: u32 }
129fn foo(a: A) { a.$0() }
130"#,
131 expect![[""]],
132 );
133 }
134
135 #[test]
136 fn test_visibility_filtering() {
137 check(
138 r#"
139mod inner {
140 pub struct A {
141 private_field: u32,
142 pub pub_field: u32,
143 pub(crate) crate_field: u32,
144 pub(crate) super_field: u32,
145 }
146}
147fn foo(a: inner::A) { a.$0 }
148"#,
149 expect![[r#"
150 fd pub_field u32
151 fd crate_field u32
152 fd super_field u32
153 "#]],
154 );
155
156 check(
157 r#"
158struct A {}
159mod m {
160 impl super::A {
161 fn private_method(&self) {}
162 pub(crate) fn the_method(&self) {}
163 }
164}
165fn foo(a: A) { a.$0 }
166"#,
167 expect![[r#"
168 me the_method() pub(crate) fn the_method(&self)
169 "#]],
170 );
171 }
172
173 #[test]
174 fn test_union_field_completion() {
175 check(
176 r#"
177union U { field: u8, other: u16 }
178fn foo(u: U) { u.$0 }
179"#,
180 expect![[r#"
181 fd field u8
182 fd other u16
183 "#]],
184 );
185 }
186
187 #[test]
188 fn test_method_completion_only_fitting_impls() {
189 check(
190 r#"
191struct A<T> {}
192impl A<u32> {
193 fn the_method(&self) {}
194}
195impl A<i32> {
196 fn the_other_method(&self) {}
197}
198fn foo(a: A<u32>) { a.$0 }
199"#,
200 expect![[r#"
201 me the_method() fn the_method(&self)
202 "#]],
203 )
204 }
205
206 #[test]
207 fn test_trait_method_completion() {
208 check(
209 r#"
210struct A {}
211trait Trait { fn the_method(&self); }
212impl Trait for A {}
213fn foo(a: A) { a.$0 }
214"#,
215 expect![[r#"
216 me the_method() fn the_method(&self)
217 "#]],
218 );
219 }
220
221 #[test]
222 fn test_trait_method_completion_deduplicated() {
223 check(
224 r"
225struct A {}
226trait Trait { fn the_method(&self); }
227impl<T> Trait for T {}
228fn foo(a: &A) { a.$0 }
229",
230 expect![[r#"
231 me the_method() fn the_method(&self)
232 "#]],
233 );
234 }
235
236 #[test]
237 fn completes_trait_method_from_other_module() {
238 check(
239 r"
240struct A {}
241mod m {
242 pub trait Trait { fn the_method(&self); }
243}
244use m::Trait;
245impl Trait for A {}
246fn foo(a: A) { a.$0 }
247",
248 expect![[r#"
249 me the_method() fn the_method(&self)
250 "#]],
251 );
252 }
253
254 #[test]
255 fn test_no_non_self_method() {
256 check(
257 r#"
258struct A {}
259impl A {
260 fn the_method() {}
261}
262fn foo(a: A) {
263 a.$0
264}
265"#,
266 expect![[""]],
267 );
268 }
269
270 #[test]
271 fn test_tuple_field_completion() {
272 check(
273 r#"
274fn foo() {
275 let b = (0, 3.14);
276 b.$0
277}
278"#,
279 expect![[r#"
280 fd 0 i32
281 fd 1 f64
282 "#]],
283 )
284 }
285
286 #[test]
287 fn test_tuple_field_inference() {
288 check(
289 r#"
290pub struct S;
291impl S { pub fn blah(&self) {} }
292
293struct T(S);
294
295impl T {
296 fn foo(&self) {
297 // FIXME: This doesn't work without the trailing `a` as `0.` is a float
298 self.0.a$0
299 }
300}
301"#,
302 expect![[r#"
303 me blah() pub fn blah(&self)
304 "#]],
305 );
306 }
307
308 #[test]
309 fn test_completion_works_in_consts() {
310 check(
311 r#"
312struct A { the_field: u32 }
313const X: u32 = {
314 A { the_field: 92 }.$0
315};
316"#,
317 expect![[r#"
318 fd the_field u32
319 "#]],
320 );
321 }
322
323 #[test]
324 fn works_in_simple_macro_1() {
325 check(
326 r#"
327macro_rules! m { ($e:expr) => { $e } }
328struct A { the_field: u32 }
329fn foo(a: A) {
330 m!(a.x$0)
331}
332"#,
333 expect![[r#"
334 fd the_field u32
335 "#]],
336 );
337 }
338
339 #[test]
340 fn works_in_simple_macro_2() {
341 // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery
342 check(
343 r#"
344macro_rules! m { ($e:expr) => { $e } }
345struct A { the_field: u32 }
346fn foo(a: A) {
347 m!(a.$0)
348}
349"#,
350 expect![[r#"
351 fd the_field u32
352 "#]],
353 );
354 }
355
356 #[test]
357 fn works_in_simple_macro_recursive_1() {
358 check(
359 r#"
360macro_rules! m { ($e:expr) => { $e } }
361struct A { the_field: u32 }
362fn foo(a: A) {
363 m!(m!(m!(a.x$0)))
364}
365"#,
366 expect![[r#"
367 fd the_field u32
368 "#]],
369 );
370 }
371
372 #[test]
373 fn macro_expansion_resilient() {
374 check(
375 r#"
376macro_rules! d {
377 () => {};
378 ($val:expr) => {
379 match $val { tmp => { tmp } }
380 };
381 // Trailing comma with single argument is ignored
382 ($val:expr,) => { $crate::d!($val) };
383 ($($val:expr),+ $(,)?) => {
384 ($($crate::d!($val)),+,)
385 };
386}
387struct A { the_field: u32 }
388fn foo(a: A) {
389 d!(a.$0)
390}
391"#,
392 expect![[r#"
393 fd the_field u32
394 "#]],
395 );
396 }
397
398 #[test]
399 fn test_method_completion_issue_3547() {
400 check(
401 r#"
402struct HashSet<T> {}
403impl<T> HashSet<T> {
404 pub fn the_method(&self) {}
405}
406fn foo() {
407 let s: HashSet<_>;
408 s.$0
409}
410"#,
411 expect![[r#"
412 me the_method() pub fn the_method(&self)
413 "#]],
414 );
415 }
416
417 #[test]
418 fn completes_method_call_when_receiver_is_a_macro_call() {
419 check(
420 r#"
421struct S;
422impl S { fn foo(&self) {} }
423macro_rules! make_s { () => { S }; }
424fn main() { make_s!().f$0; }
425"#,
426 expect![[r#"
427 me foo() fn foo(&self)
428 "#]],
429 )
430 }
431}
diff --git a/crates/completion/src/completions/flyimport.rs b/crates/completion/src/completions/flyimport.rs
deleted file mode 100644
index 222809638..000000000
--- a/crates/completion/src/completions/flyimport.rs
+++ /dev/null
@@ -1,291 +0,0 @@
1//! Feature: completion with imports-on-the-fly
2//!
3//! When completing names in the current scope, proposes additional imports from other modules or crates,
4//! if they can be qualified in the scope and their name contains all symbols from the completion input
5//! (case-insensitive, in any order or places).
6//!
7//! ```
8//! fn main() {
9//! pda$0
10//! }
11//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
12//! ```
13//! ->
14//! ```
15//! use std::marker::PhantomData;
16//!
17//! fn main() {
18//! PhantomData
19//! }
20//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
21//! ```
22//!
23//! .Fuzzy search details
24//!
25//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
26//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
27//! For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols.
28//!
29//! .Import configuration
30//!
31//! It is possible to configure how use-trees are merged with the `importMergeBehavior` setting.
32//! Mimics the corresponding behavior of the `Auto Import` feature.
33//!
34//! .LSP and performance implications
35//!
36//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
37//! (case sensitive) resolve client capability in its client capabilities.
38//! This way the server is able to defer the costly computations, doing them for a selected completion item only.
39//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
40//! which might be slow ergo the feature is automatically disabled.
41//!
42//! .Feature toggle
43//!
44//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag.
45//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
46//! capability enabled.
47
48use either::Either;
49use hir::{ModPath, ScopeDef};
50use ide_db::{helpers::insert_use::ImportScope, imports_locator};
51use syntax::AstNode;
52use test_utils::mark;
53
54use crate::{
55 context::CompletionContext,
56 render::{render_resolution_with_import, RenderContext},
57 ImportEdit,
58};
59
60use super::Completions;
61
62pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
63 if !ctx.config.enable_autoimport_completions {
64 return None;
65 }
66 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
67 return None;
68 }
69 let potential_import_name = ctx.token.to_string();
70 if potential_import_name.len() < 2 {
71 return None;
72 }
73 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
74
75 let current_module = ctx.scope.module()?;
76 let anchor = ctx.name_ref_syntax.as_ref()?;
77 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
78
79 let user_input_lowercased = potential_import_name.to_lowercase();
80 let mut all_mod_paths = imports_locator::find_similar_imports(
81 &ctx.sema,
82 ctx.krate?,
83 Some(40),
84 potential_import_name,
85 true,
86 true,
87 )
88 .filter_map(|import_candidate| {
89 Some(match import_candidate {
90 Either::Left(module_def) => {
91 (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def))
92 }
93 Either::Right(macro_def) => {
94 (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def))
95 }
96 })
97 })
98 .filter(|(mod_path, _)| mod_path.len() > 1)
99 .collect::<Vec<_>>();
100
101 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
102 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
103 });
104
105 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
106 render_resolution_with_import(
107 RenderContext::new(ctx),
108 ImportEdit { import_path, import_scope: import_scope.clone() },
109 &definition,
110 )
111 }));
112 Some(())
113}
114
115fn compute_fuzzy_completion_order_key(
116 proposed_mod_path: &ModPath,
117 user_input_lowercased: &str,
118) -> usize {
119 mark::hit!(certain_fuzzy_order_test);
120 let proposed_import_name = match proposed_mod_path.segments.last() {
121 Some(name) => name.to_string().to_lowercase(),
122 None => return usize::MAX,
123 };
124 match proposed_import_name.match_indices(user_input_lowercased).next() {
125 Some((first_matching_index, _)) => first_matching_index,
126 None => usize::MAX,
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use expect_test::{expect, Expect};
133 use test_utils::mark;
134
135 use crate::{
136 item::CompletionKind,
137 test_utils::{check_edit, completion_list},
138 };
139
140 fn check(ra_fixture: &str, expect: Expect) {
141 let actual = completion_list(ra_fixture, CompletionKind::Magic);
142 expect.assert_eq(&actual);
143 }
144
145 #[test]
146 fn function_fuzzy_completion() {
147 check_edit(
148 "stdin",
149 r#"
150//- /lib.rs crate:dep
151pub mod io {
152 pub fn stdin() {}
153};
154
155//- /main.rs crate:main deps:dep
156fn main() {
157 stdi$0
158}
159"#,
160 r#"
161use dep::io::stdin;
162
163fn main() {
164 stdin()$0
165}
166"#,
167 );
168 }
169
170 #[test]
171 fn macro_fuzzy_completion() {
172 check_edit(
173 "macro_with_curlies!",
174 r#"
175//- /lib.rs crate:dep
176/// Please call me as macro_with_curlies! {}
177#[macro_export]
178macro_rules! macro_with_curlies {
179 () => {}
180}
181
182//- /main.rs crate:main deps:dep
183fn main() {
184 curli$0
185}
186"#,
187 r#"
188use dep::macro_with_curlies;
189
190fn main() {
191 macro_with_curlies! {$0}
192}
193"#,
194 );
195 }
196
197 #[test]
198 fn struct_fuzzy_completion() {
199 check_edit(
200 "ThirdStruct",
201 r#"
202//- /lib.rs crate:dep
203pub struct FirstStruct;
204pub mod some_module {
205 pub struct SecondStruct;
206 pub struct ThirdStruct;
207}
208
209//- /main.rs crate:main deps:dep
210use dep::{FirstStruct, some_module::SecondStruct};
211
212fn main() {
213 this$0
214}
215"#,
216 r#"
217use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
218
219fn main() {
220 ThirdStruct
221}
222"#,
223 );
224 }
225
226 #[test]
227 fn fuzzy_completions_come_in_specific_order() {
228 mark::check!(certain_fuzzy_order_test);
229 check(
230 r#"
231//- /lib.rs crate:dep
232pub struct FirstStruct;
233pub mod some_module {
234 // already imported, omitted
235 pub struct SecondStruct;
236 // does not contain all letters from the query, omitted
237 pub struct UnrelatedOne;
238 // contains all letters from the query, but not in sequence, displayed last
239 pub struct ThiiiiiirdStruct;
240 // contains all letters from the query, but not in the beginning, displayed second
241 pub struct AfterThirdStruct;
242 // contains all letters from the query in the begginning, displayed first
243 pub struct ThirdStruct;
244}
245
246//- /main.rs crate:main deps:dep
247use dep::{FirstStruct, some_module::SecondStruct};
248
249fn main() {
250 hir$0
251}
252"#,
253 expect![[r#"
254 st dep::some_module::ThirdStruct
255 st dep::some_module::AfterThirdStruct
256 st dep::some_module::ThiiiiiirdStruct
257 "#]],
258 );
259 }
260
261 #[test]
262 fn does_not_propose_names_in_scope() {
263 check(
264 r#"
265//- /lib.rs crate:dep
266pub mod test_mod {
267 pub trait TestTrait {
268 const SPECIAL_CONST: u8;
269 type HumbleType;
270 fn weird_function();
271 fn random_method(&self);
272 }
273 pub struct TestStruct {}
274 impl TestTrait for TestStruct {
275 const SPECIAL_CONST: u8 = 42;
276 type HumbleType = ();
277 fn weird_function() {}
278 fn random_method(&self) {}
279 }
280}
281
282//- /main.rs crate:main deps:dep
283use dep::test_mod::TestStruct;
284fn main() {
285 TestSt$0
286}
287"#,
288 expect![[r#""#]],
289 );
290 }
291}
diff --git a/crates/completion/src/completions/fn_param.rs b/crates/completion/src/completions/fn_param.rs
deleted file mode 100644
index 5505c3559..000000000
--- a/crates/completion/src/completions/fn_param.rs
+++ /dev/null
@@ -1,135 +0,0 @@
1//! See `complete_fn_param`.
2
3use rustc_hash::FxHashMap;
4use syntax::{
5 ast::{self, ModuleItemOwner},
6 match_ast, AstNode,
7};
8
9use crate::{CompletionContext, CompletionItem, CompletionKind, Completions};
10
11/// Complete repeated parameters, both name and type. For example, if all
12/// functions in a file have a `spam: &mut Spam` parameter, a completion with
13/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
14/// suggested.
15pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) {
16 if !ctx.is_param {
17 return;
18 }
19
20 let mut params = FxHashMap::default();
21
22 let me = ctx.token.ancestors().find_map(ast::Fn::cast);
23 let mut process_fn = |func: ast::Fn| {
24 if Some(&func) == me.as_ref() {
25 return;
26 }
27 func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
28 let text = param.syntax().text().to_string();
29 params.entry(text).or_insert(param);
30 })
31 };
32
33 for node in ctx.token.parent().ancestors() {
34 match_ast! {
35 match node {
36 ast::SourceFile(it) => it.items().filter_map(|item| match item {
37 ast::Item::Fn(it) => Some(it),
38 _ => None,
39 }).for_each(&mut process_fn),
40 ast::ItemList(it) => it.items().filter_map(|item| match item {
41 ast::Item::Fn(it) => Some(it),
42 _ => None,
43 }).for_each(&mut process_fn),
44 ast::AssocItemList(it) => it.assoc_items().filter_map(|item| match item {
45 ast::AssocItem::Fn(it) => Some(it),
46 _ => None,
47 }).for_each(&mut process_fn),
48 _ => continue,
49 }
50 };
51 }
52
53 params
54 .into_iter()
55 .filter_map(|(label, param)| {
56 let lookup = param.pat()?.syntax().text().to_string();
57 Some((label, lookup))
58 })
59 .for_each(|(label, lookup)| {
60 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
61 .kind(crate::CompletionItemKind::Binding)
62 .lookup_by(lookup)
63 .add_to(acc)
64 });
65}
66
67#[cfg(test)]
68mod tests {
69 use expect_test::{expect, Expect};
70
71 use crate::{test_utils::completion_list, CompletionKind};
72
73 fn check(ra_fixture: &str, expect: Expect) {
74 let actual = completion_list(ra_fixture, CompletionKind::Magic);
75 expect.assert_eq(&actual);
76 }
77
78 #[test]
79 fn test_param_completion_last_param() {
80 check(
81 r#"
82fn foo(file_id: FileId) {}
83fn bar(file_id: FileId) {}
84fn baz(file$0) {}
85"#,
86 expect![[r#"
87 bn file_id: FileId
88 "#]],
89 );
90 }
91
92 #[test]
93 fn test_param_completion_nth_param() {
94 check(
95 r#"
96fn foo(file_id: FileId) {}
97fn baz(file$0, x: i32) {}
98"#,
99 expect![[r#"
100 bn file_id: FileId
101 "#]],
102 );
103 }
104
105 #[test]
106 fn test_param_completion_trait_param() {
107 check(
108 r#"
109pub(crate) trait SourceRoot {
110 pub fn contains(&self, file_id: FileId) -> bool;
111 pub fn module_map(&self) -> &ModuleMap;
112 pub fn lines(&self, file_id: FileId) -> &LineIndex;
113 pub fn syntax(&self, file$0)
114}
115"#,
116 expect![[r#"
117 bn file_id: FileId
118 "#]],
119 );
120 }
121
122 #[test]
123 fn completes_param_in_inner_function() {
124 check(
125 r#"
126fn outer(text: String) {
127 fn inner($0)
128}
129"#,
130 expect![[r#"
131 bn text: String
132 "#]],
133 )
134 }
135}
diff --git a/crates/completion/src/completions/keyword.rs b/crates/completion/src/completions/keyword.rs
deleted file mode 100644
index 47e146128..000000000
--- a/crates/completion/src/completions/keyword.rs
+++ /dev/null
@@ -1,660 +0,0 @@
1//! Completes keywords.
2
3use syntax::SyntaxKind;
4use test_utils::mark;
5
6use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
7
8pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
9 // complete keyword "crate" in use stmt
10 let source_range = ctx.source_range();
11
12 if ctx.use_item_syntax.is_some() {
13 if ctx.path_qual.is_none() {
14 CompletionItem::new(CompletionKind::Keyword, source_range, "crate::")
15 .kind(CompletionItemKind::Keyword)
16 .insert_text("crate::")
17 .add_to(acc);
18 }
19 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
20 .kind(CompletionItemKind::Keyword)
21 .add_to(acc);
22 CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
23 .kind(CompletionItemKind::Keyword)
24 .insert_text("super::")
25 .add_to(acc);
26 }
27
28 // Suggest .await syntax for types that implement Future trait
29 if let Some(receiver) = &ctx.dot_receiver {
30 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
31 if ty.impls_future(ctx.db) {
32 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
33 .kind(CompletionItemKind::Keyword)
34 .detail("expr.await")
35 .insert_text("await")
36 .add_to(acc);
37 }
38 };
39 }
40}
41
42pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
43 if ctx.token.kind() == SyntaxKind::COMMENT {
44 mark::hit!(no_keyword_completion_in_comments);
45 return;
46 }
47 if ctx.record_lit_syntax.is_some() {
48 mark::hit!(no_keyword_completion_in_record_lit);
49 return;
50 }
51
52 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
53 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
54 add_keyword(ctx, acc, "where", "where ");
55 return;
56 }
57 if ctx.unsafe_is_prev {
58 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
59 add_keyword(ctx, acc, "fn", "fn $0() {}")
60 }
61
62 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
63 add_keyword(ctx, acc, "trait", "trait $0 {}");
64 add_keyword(ctx, acc, "impl", "impl $0 {}");
65 }
66
67 return;
68 }
69 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
70 {
71 add_keyword(ctx, acc, "fn", "fn $0() {}");
72 }
73 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
74 add_keyword(ctx, acc, "use", "use ");
75 add_keyword(ctx, acc, "impl", "impl $0 {}");
76 add_keyword(ctx, acc, "trait", "trait $0 {}");
77 }
78
79 if ctx.has_item_list_or_source_file_parent {
80 add_keyword(ctx, acc, "enum", "enum $0 {}");
81 add_keyword(ctx, acc, "struct", "struct $0");
82 add_keyword(ctx, acc, "union", "union $0 {}");
83 }
84
85 if ctx.is_expr {
86 add_keyword(ctx, acc, "match", "match $0 {}");
87 add_keyword(ctx, acc, "while", "while $0 {}");
88 add_keyword(ctx, acc, "loop", "loop {$0}");
89 add_keyword(ctx, acc, "if", "if $0 {}");
90 add_keyword(ctx, acc, "if let", "if let $1 = $0 {}");
91 }
92
93 if ctx.if_is_prev || ctx.block_expr_parent {
94 add_keyword(ctx, acc, "let", "let ");
95 }
96
97 if ctx.after_if {
98 add_keyword(ctx, acc, "else", "else {$0}");
99 add_keyword(ctx, acc, "else if", "else if $0 {}");
100 }
101 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
102 add_keyword(ctx, acc, "mod", "mod $0");
103 }
104 if ctx.bind_pat_parent || ctx.ref_pat_parent {
105 add_keyword(ctx, acc, "mut", "mut ");
106 }
107 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
108 {
109 add_keyword(ctx, acc, "const", "const ");
110 add_keyword(ctx, acc, "type", "type ");
111 }
112 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
113 add_keyword(ctx, acc, "static", "static ");
114 };
115 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
116 add_keyword(ctx, acc, "extern", "extern ");
117 }
118 if ctx.has_item_list_or_source_file_parent
119 || has_trait_or_impl_parent
120 || ctx.block_expr_parent
121 || ctx.is_match_arm
122 {
123 add_keyword(ctx, acc, "unsafe", "unsafe ");
124 }
125 if ctx.in_loop_body {
126 if ctx.can_be_stmt {
127 add_keyword(ctx, acc, "continue", "continue;");
128 add_keyword(ctx, acc, "break", "break;");
129 } else {
130 add_keyword(ctx, acc, "continue", "continue");
131 add_keyword(ctx, acc, "break", "break");
132 }
133 }
134 if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent | ctx.has_field_list_parent {
135 add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
136 add_keyword(ctx, acc, "pub", "pub ");
137 }
138
139 if !ctx.is_trivial_path {
140 return;
141 }
142 let fn_def = match &ctx.function_syntax {
143 Some(it) => it,
144 None => return,
145 };
146
147 add_keyword(
148 ctx,
149 acc,
150 "return",
151 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
152 (true, true) => "return $0;",
153 (true, false) => "return;",
154 (false, true) => "return $0",
155 (false, false) => "return",
156 },
157 )
158}
159
160fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
161 let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
162 .kind(CompletionItemKind::Keyword);
163 let builder = match ctx.config.snippet_cap {
164 Some(cap) => {
165 let tmp;
166 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
167 mark::hit!(let_semi);
168 tmp = format!("{};", snippet);
169 &tmp
170 } else {
171 snippet
172 };
173 builder.insert_snippet(cap, snippet)
174 }
175 None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }),
176 };
177 acc.add(builder.build());
178}
179
180#[cfg(test)]
181mod tests {
182 use expect_test::{expect, Expect};
183 use test_utils::mark;
184
185 use crate::{
186 test_utils::{check_edit, completion_list},
187 CompletionKind,
188 };
189
190 fn check(ra_fixture: &str, expect: Expect) {
191 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
192 expect.assert_eq(&actual)
193 }
194
195 #[test]
196 fn test_keywords_in_use_stmt() {
197 check(
198 r"use $0",
199 expect![[r#"
200 kw crate::
201 kw self
202 kw super::
203 "#]],
204 );
205
206 check(
207 r"use a::$0",
208 expect![[r#"
209 kw self
210 kw super::
211 "#]],
212 );
213
214 check(
215 r"use a::{b, $0}",
216 expect![[r#"
217 kw self
218 kw super::
219 "#]],
220 );
221 }
222
223 #[test]
224 fn test_keywords_at_source_file_level() {
225 check(
226 r"m$0",
227 expect![[r#"
228 kw fn
229 kw use
230 kw impl
231 kw trait
232 kw enum
233 kw struct
234 kw union
235 kw mod
236 kw const
237 kw type
238 kw static
239 kw extern
240 kw unsafe
241 kw pub(crate)
242 kw pub
243 "#]],
244 );
245 }
246
247 #[test]
248 fn test_keywords_in_function() {
249 check(
250 r"fn quux() { $0 }",
251 expect![[r#"
252 kw fn
253 kw use
254 kw impl
255 kw trait
256 kw match
257 kw while
258 kw loop
259 kw if
260 kw if let
261 kw let
262 kw mod
263 kw const
264 kw type
265 kw static
266 kw extern
267 kw unsafe
268 kw return
269 "#]],
270 );
271 }
272
273 #[test]
274 fn test_keywords_inside_block() {
275 check(
276 r"fn quux() { if true { $0 } }",
277 expect![[r#"
278 kw fn
279 kw use
280 kw impl
281 kw trait
282 kw match
283 kw while
284 kw loop
285 kw if
286 kw if let
287 kw let
288 kw mod
289 kw const
290 kw type
291 kw static
292 kw extern
293 kw unsafe
294 kw return
295 "#]],
296 );
297 }
298
299 #[test]
300 fn test_keywords_after_if() {
301 check(
302 r#"fn quux() { if true { () } $0 }"#,
303 expect![[r#"
304 kw fn
305 kw use
306 kw impl
307 kw trait
308 kw match
309 kw while
310 kw loop
311 kw if
312 kw if let
313 kw let
314 kw else
315 kw else if
316 kw mod
317 kw const
318 kw type
319 kw static
320 kw extern
321 kw unsafe
322 kw return
323 "#]],
324 );
325 check_edit(
326 "else",
327 r#"fn quux() { if true { () } $0 }"#,
328 r#"fn quux() { if true { () } else {$0} }"#,
329 );
330 }
331
332 #[test]
333 fn test_keywords_in_match_arm() {
334 check(
335 r#"
336fn quux() -> i32 {
337 match () { () => $0 }
338}
339"#,
340 expect![[r#"
341 kw match
342 kw while
343 kw loop
344 kw if
345 kw if let
346 kw unsafe
347 kw return
348 "#]],
349 );
350 }
351
352 #[test]
353 fn test_keywords_in_trait_def() {
354 check(
355 r"trait My { $0 }",
356 expect![[r#"
357 kw fn
358 kw const
359 kw type
360 kw unsafe
361 "#]],
362 );
363 }
364
365 #[test]
366 fn test_keywords_in_impl_def() {
367 check(
368 r"impl My { $0 }",
369 expect![[r#"
370 kw fn
371 kw const
372 kw type
373 kw unsafe
374 kw pub(crate)
375 kw pub
376 "#]],
377 );
378 }
379
380 #[test]
381 fn test_keywords_in_loop() {
382 check(
383 r"fn my() { loop { $0 } }",
384 expect![[r#"
385 kw fn
386 kw use
387 kw impl
388 kw trait
389 kw match
390 kw while
391 kw loop
392 kw if
393 kw if let
394 kw let
395 kw mod
396 kw const
397 kw type
398 kw static
399 kw extern
400 kw unsafe
401 kw continue
402 kw break
403 kw return
404 "#]],
405 );
406 }
407
408 #[test]
409 fn test_keywords_after_unsafe_in_item_list() {
410 check(
411 r"unsafe $0",
412 expect![[r#"
413 kw fn
414 kw trait
415 kw impl
416 "#]],
417 );
418 }
419
420 #[test]
421 fn test_keywords_after_unsafe_in_block_expr() {
422 check(
423 r"fn my_fn() { unsafe $0 }",
424 expect![[r#"
425 kw fn
426 kw trait
427 kw impl
428 "#]],
429 );
430 }
431
432 #[test]
433 fn test_mut_in_ref_and_in_fn_parameters_list() {
434 check(
435 r"fn my_fn(&$0) {}",
436 expect![[r#"
437 kw mut
438 "#]],
439 );
440 check(
441 r"fn my_fn($0) {}",
442 expect![[r#"
443 kw mut
444 "#]],
445 );
446 check(
447 r"fn my_fn() { let &$0 }",
448 expect![[r#"
449 kw mut
450 "#]],
451 );
452 }
453
454 #[test]
455 fn test_where_keyword() {
456 check(
457 r"trait A $0",
458 expect![[r#"
459 kw where
460 "#]],
461 );
462 check(
463 r"impl A $0",
464 expect![[r#"
465 kw where
466 "#]],
467 );
468 }
469
470 #[test]
471 fn no_keyword_completion_in_comments() {
472 mark::check!(no_keyword_completion_in_comments);
473 check(
474 r#"
475fn test() {
476 let x = 2; // A comment$0
477}
478"#,
479 expect![[""]],
480 );
481 check(
482 r#"
483/*
484Some multi-line comment$0
485*/
486"#,
487 expect![[""]],
488 );
489 check(
490 r#"
491/// Some doc comment
492/// let test$0 = 1
493"#,
494 expect![[""]],
495 );
496 }
497
498 #[test]
499 fn test_completion_await_impls_future() {
500 check(
501 r#"
502//- /main.rs crate:main deps:std
503use std::future::*;
504struct A {}
505impl Future for A {}
506fn foo(a: A) { a.$0 }
507
508//- /std/lib.rs crate:std
509pub mod future {
510 #[lang = "future_trait"]
511 pub trait Future {}
512}
513"#,
514 expect![[r#"
515 kw await expr.await
516 "#]],
517 );
518
519 check(
520 r#"
521//- /main.rs crate:main deps:std
522use std::future::*;
523fn foo() {
524 let a = async {};
525 a.$0
526}
527
528//- /std/lib.rs crate:std
529pub mod future {
530 #[lang = "future_trait"]
531 pub trait Future {
532 type Output;
533 }
534}
535"#,
536 expect![[r#"
537 kw await expr.await
538 "#]],
539 )
540 }
541
542 #[test]
543 fn after_let() {
544 check(
545 r#"fn main() { let _ = $0 }"#,
546 expect![[r#"
547 kw match
548 kw while
549 kw loop
550 kw if
551 kw if let
552 kw return
553 "#]],
554 )
555 }
556
557 #[test]
558 fn before_field() {
559 check(
560 r#"
561struct Foo {
562 $0
563 pub f: i32,
564}
565"#,
566 expect![[r#"
567 kw pub(crate)
568 kw pub
569 "#]],
570 )
571 }
572
573 #[test]
574 fn skip_struct_initializer() {
575 mark::check!(no_keyword_completion_in_record_lit);
576 check(
577 r#"
578struct Foo {
579 pub f: i32,
580}
581fn foo() {
582 Foo {
583 $0
584 }
585}
586"#,
587 expect![[r#""#]],
588 );
589 }
590
591 #[test]
592 fn struct_initializer_field_expr() {
593 check(
594 r#"
595struct Foo {
596 pub f: i32,
597}
598fn foo() {
599 Foo {
600 f: $0
601 }
602}
603"#,
604 expect![[r#"
605 kw match
606 kw while
607 kw loop
608 kw if
609 kw if let
610 kw return
611 "#]],
612 );
613 }
614
615 #[test]
616 fn let_semi() {
617 mark::check!(let_semi);
618 check_edit(
619 "match",
620 r#"
621fn main() { let x = $0 }
622"#,
623 r#"
624fn main() { let x = match $0 {}; }
625"#,
626 );
627
628 check_edit(
629 "if",
630 r#"
631fn main() {
632 let x = $0
633 let y = 92;
634}
635"#,
636 r#"
637fn main() {
638 let x = if $0 {};
639 let y = 92;
640}
641"#,
642 );
643
644 check_edit(
645 "loop",
646 r#"
647fn main() {
648 let x = $0
649 bar();
650}
651"#,
652 r#"
653fn main() {
654 let x = loop {$0};
655 bar();
656}
657"#,
658 );
659 }
660}
diff --git a/crates/completion/src/completions/macro_in_item_position.rs b/crates/completion/src/completions/macro_in_item_position.rs
deleted file mode 100644
index 2be299ac2..000000000
--- a/crates/completion/src/completions/macro_in_item_position.rs
+++ /dev/null
@@ -1,41 +0,0 @@
1//! Completes macro invocations used in item position.
2
3use crate::{CompletionContext, Completions};
4
5pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
6 // Show only macros in top level.
7 if ctx.is_new_item {
8 ctx.scope.process_all_names(&mut |name, res| {
9 if let hir::ScopeDef::MacroDef(mac) = res {
10 acc.add_macro(ctx, Some(name.to_string()), mac);
11 }
12 })
13 }
14}
15
16#[cfg(test)]
17mod tests {
18 use expect_test::{expect, Expect};
19
20 use crate::{test_utils::completion_list, CompletionKind};
21
22 fn check(ra_fixture: &str, expect: Expect) {
23 let actual = completion_list(ra_fixture, CompletionKind::Reference);
24 expect.assert_eq(&actual)
25 }
26
27 #[test]
28 fn completes_macros_as_item() {
29 check(
30 r#"
31macro_rules! foo { () => {} }
32fn foo() {}
33
34$0
35"#,
36 expect![[r#"
37 ma foo!(…) macro_rules! foo
38 "#]],
39 )
40 }
41}
diff --git a/crates/completion/src/completions/mod_.rs b/crates/completion/src/completions/mod_.rs
deleted file mode 100644
index 00e951ca9..000000000
--- a/crates/completion/src/completions/mod_.rs
+++ /dev/null
@@ -1,313 +0,0 @@
1//! Completes mod declarations.
2
3use std::iter;
4
5use hir::{Module, ModuleSource};
6use ide_db::base_db::{SourceDatabaseExt, VfsPath};
7use ide_db::RootDatabase;
8use rustc_hash::FxHashSet;
9
10use crate::{CompletionItem, CompletionItemKind};
11
12use crate::{context::CompletionContext, item::CompletionKind, Completions};
13
14/// Complete mod declaration, i.e. `mod $0 ;`
15pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
16 let mod_under_caret = match &ctx.mod_declaration_under_caret {
17 Some(mod_under_caret) if mod_under_caret.item_list().is_none() => mod_under_caret,
18 _ => return None,
19 };
20
21 let _p = profile::span("completion::complete_mod");
22
23 let current_module = ctx.scope.module()?;
24
25 let module_definition_file =
26 current_module.definition_source(ctx.db).file_id.original_file(ctx.db);
27 let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file));
28 let directory_to_look_for_submodules = directory_to_look_for_submodules(
29 current_module,
30 ctx.db,
31 source_root.path_for_file(&module_definition_file)?,
32 )?;
33
34 let existing_mod_declarations = current_module
35 .children(ctx.db)
36 .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
37 .collect::<FxHashSet<_>>();
38
39 let module_declaration_file =
40 current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
41 module_declaration_source_file.file_id.original_file(ctx.db)
42 });
43
44 source_root
45 .iter()
46 .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file)
47 .filter(|submodule_candidate_file| {
48 Some(submodule_candidate_file) != module_declaration_file.as_ref()
49 })
50 .filter_map(|submodule_file| {
51 let submodule_path = source_root.path_for_file(&submodule_file)?;
52 let directory_with_submodule = submodule_path.parent()?;
53 let (name, ext) = submodule_path.name_and_extension()?;
54 if ext != Some("rs") {
55 return None;
56 }
57 match name {
58 "lib" | "main" => None,
59 "mod" => {
60 if directory_with_submodule.parent()? == directory_to_look_for_submodules {
61 match directory_with_submodule.name_and_extension()? {
62 (directory_name, None) => Some(directory_name.to_owned()),
63 _ => None,
64 }
65 } else {
66 None
67 }
68 }
69 file_name if directory_with_submodule == directory_to_look_for_submodules => {
70 Some(file_name.to_owned())
71 }
72 _ => None,
73 }
74 })
75 .filter(|name| !existing_mod_declarations.contains(name))
76 .for_each(|submodule_name| {
77 let mut label = submodule_name;
78 if mod_under_caret.semicolon_token().is_none() {
79 label.push(';');
80 }
81 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label)
82 .kind(CompletionItemKind::Module)
83 .add_to(acc)
84 });
85
86 Some(())
87}
88
89fn directory_to_look_for_submodules(
90 module: Module,
91 db: &RootDatabase,
92 module_file_path: &VfsPath,
93) -> Option<VfsPath> {
94 let directory_with_module_path = module_file_path.parent()?;
95 let (name, ext) = module_file_path.name_and_extension()?;
96 if ext != Some("rs") {
97 return None;
98 }
99 let base_directory = match name {
100 "mod" | "lib" | "main" => Some(directory_with_module_path),
101 regular_rust_file_name => {
102 if matches!(
103 (
104 directory_with_module_path
105 .parent()
106 .as_ref()
107 .and_then(|path| path.name_and_extension()),
108 directory_with_module_path.name_and_extension(),
109 ),
110 (Some(("src", None)), Some(("bin", None)))
111 ) {
112 // files in /src/bin/ can import each other directly
113 Some(directory_with_module_path)
114 } else {
115 directory_with_module_path.join(regular_rust_file_name)
116 }
117 }
118 }?;
119
120 module_chain_to_containing_module_file(module, db)
121 .into_iter()
122 .filter_map(|module| module.name(db))
123 .try_fold(base_directory, |path, name| path.join(&name.to_string()))
124}
125
126fn module_chain_to_containing_module_file(
127 current_module: Module,
128 db: &RootDatabase,
129) -> Vec<Module> {
130 let mut path =
131 iter::successors(Some(current_module), |current_module| current_module.parent(db))
132 .take_while(|current_module| {
133 matches!(current_module.definition_source(db).value, ModuleSource::Module(_))
134 })
135 .collect::<Vec<_>>();
136 path.reverse();
137 path
138}
139
140#[cfg(test)]
141mod tests {
142 use crate::{test_utils::completion_list, CompletionKind};
143 use expect_test::{expect, Expect};
144
145 fn check(ra_fixture: &str, expect: Expect) {
146 let actual = completion_list(ra_fixture, CompletionKind::Magic);
147 expect.assert_eq(&actual);
148 }
149
150 #[test]
151 fn lib_module_completion() {
152 check(
153 r#"
154 //- /lib.rs
155 mod $0
156 //- /foo.rs
157 fn foo() {}
158 //- /foo/ignored_foo.rs
159 fn ignored_foo() {}
160 //- /bar/mod.rs
161 fn bar() {}
162 //- /bar/ignored_bar.rs
163 fn ignored_bar() {}
164 "#,
165 expect![[r#"
166 md foo;
167 md bar;
168 "#]],
169 );
170 }
171
172 #[test]
173 fn no_module_completion_with_module_body() {
174 check(
175 r#"
176 //- /lib.rs
177 mod $0 {
178
179 }
180 //- /foo.rs
181 fn foo() {}
182 "#,
183 expect![[r#""#]],
184 );
185 }
186
187 #[test]
188 fn main_module_completion() {
189 check(
190 r#"
191 //- /main.rs
192 mod $0
193 //- /foo.rs
194 fn foo() {}
195 //- /foo/ignored_foo.rs
196 fn ignored_foo() {}
197 //- /bar/mod.rs
198 fn bar() {}
199 //- /bar/ignored_bar.rs
200 fn ignored_bar() {}
201 "#,
202 expect![[r#"
203 md foo;
204 md bar;
205 "#]],
206 );
207 }
208
209 #[test]
210 fn main_test_module_completion() {
211 check(
212 r#"
213 //- /main.rs
214 mod tests {
215 mod $0;
216 }
217 //- /tests/foo.rs
218 fn foo() {}
219 "#,
220 expect![[r#"
221 md foo
222 "#]],
223 );
224 }
225
226 #[test]
227 fn directly_nested_module_completion() {
228 check(
229 r#"
230 //- /lib.rs
231 mod foo;
232 //- /foo.rs
233 mod $0;
234 //- /foo/bar.rs
235 fn bar() {}
236 //- /foo/bar/ignored_bar.rs
237 fn ignored_bar() {}
238 //- /foo/baz/mod.rs
239 fn baz() {}
240 //- /foo/moar/ignored_moar.rs
241 fn ignored_moar() {}
242 "#,
243 expect![[r#"
244 md bar
245 md baz
246 "#]],
247 );
248 }
249
250 #[test]
251 fn nested_in_source_module_completion() {
252 check(
253 r#"
254 //- /lib.rs
255 mod foo;
256 //- /foo.rs
257 mod bar {
258 mod $0
259 }
260 //- /foo/bar/baz.rs
261 fn baz() {}
262 "#,
263 expect![[r#"
264 md baz;
265 "#]],
266 );
267 }
268
269 // FIXME binary modules are not supported in tests properly
270 // Binary modules are a bit special, they allow importing the modules from `/src/bin`
271 // and that's why are good to test two things:
272 // * no cycles are allowed in mod declarations
273 // * no modules from the parent directory are proposed
274 // Unfortunately, binary modules support is in cargo not rustc,
275 // hence the test does not work now
276 //
277 // #[test]
278 // fn regular_bin_module_completion() {
279 // check(
280 // r#"
281 // //- /src/bin.rs
282 // fn main() {}
283 // //- /src/bin/foo.rs
284 // mod $0
285 // //- /src/bin/bar.rs
286 // fn bar() {}
287 // //- /src/bin/bar/bar_ignored.rs
288 // fn bar_ignored() {}
289 // "#,
290 // expect![[r#"
291 // md bar;
292 // "#]],foo
293 // );
294 // }
295
296 #[test]
297 fn already_declared_bin_module_completion_omitted() {
298 check(
299 r#"
300 //- /src/bin.rs crate:main
301 fn main() {}
302 //- /src/bin/foo.rs
303 mod $0
304 //- /src/bin/bar.rs
305 mod foo;
306 fn bar() {}
307 //- /src/bin/bar/bar_ignored.rs
308 fn bar_ignored() {}
309 "#,
310 expect![[r#""#]],
311 );
312 }
313}
diff --git a/crates/completion/src/completions/pattern.rs b/crates/completion/src/completions/pattern.rs
deleted file mode 100644
index 595160ff5..000000000
--- a/crates/completion/src/completions/pattern.rs
+++ /dev/null
@@ -1,261 +0,0 @@
1//! Completes constats and paths in patterns.
2
3use crate::{CompletionContext, Completions};
4
5/// Completes constants and paths in patterns.
6pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7 if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_pat_binding) {
8 return;
9 }
10 if ctx.record_pat_syntax.is_some() {
11 return;
12 }
13
14 // FIXME: ideally, we should look at the type we are matching against and
15 // suggest variants + auto-imports
16 ctx.scope.process_all_names(&mut |name, res| {
17 let add_resolution = match &res {
18 hir::ScopeDef::ModuleDef(def) => match def {
19 hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
20 acc.add_struct_pat(ctx, strukt.clone(), Some(name.clone()));
21 true
22 }
23 hir::ModuleDef::Variant(variant) if !ctx.is_irrefutable_pat_binding => {
24 acc.add_variant_pat(ctx, variant.clone(), Some(name.clone()));
25 true
26 }
27 hir::ModuleDef::Adt(hir::Adt::Enum(..))
28 | hir::ModuleDef::Variant(..)
29 | hir::ModuleDef::Const(..)
30 | hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding,
31 _ => false,
32 },
33 hir::ScopeDef::MacroDef(_) => true,
34 _ => false,
35 };
36 if add_resolution {
37 acc.add_resolution(ctx, name.to_string(), &res);
38 }
39 });
40}
41
42#[cfg(test)]
43mod tests {
44 use expect_test::{expect, Expect};
45
46 use crate::{
47 test_utils::{check_edit, completion_list},
48 CompletionKind,
49 };
50
51 fn check(ra_fixture: &str, expect: Expect) {
52 let actual = completion_list(ra_fixture, CompletionKind::Reference);
53 expect.assert_eq(&actual)
54 }
55
56 fn check_snippet(ra_fixture: &str, expect: Expect) {
57 let actual = completion_list(ra_fixture, CompletionKind::Snippet);
58 expect.assert_eq(&actual)
59 }
60
61 #[test]
62 fn completes_enum_variants_and_modules() {
63 check(
64 r#"
65enum E { X }
66use self::E::X;
67const Z: E = E::X;
68mod m {}
69
70static FOO: E = E::X;
71struct Bar { f: u32 }
72
73fn foo() {
74 match E::X { $0 }
75}
76"#,
77 expect![[r#"
78 en E
79 ct Z
80 st Bar
81 ev X
82 md m
83 "#]],
84 );
85 }
86
87 #[test]
88 fn completes_in_simple_macro_call() {
89 check(
90 r#"
91macro_rules! m { ($e:expr) => { $e } }
92enum E { X }
93
94fn foo() {
95 m!(match E::X { $0 })
96}
97"#,
98 expect![[r#"
99 en E
100 ma m!(…) macro_rules! m
101 "#]],
102 );
103 }
104
105 #[test]
106 fn completes_in_irrefutable_let() {
107 check(
108 r#"
109enum E { X }
110use self::E::X;
111const Z: E = E::X;
112mod m {}
113
114static FOO: E = E::X;
115struct Bar { f: u32 }
116
117fn foo() {
118 let $0
119}
120"#,
121 expect![[r#"
122 st Bar
123 "#]],
124 );
125 }
126
127 #[test]
128 fn completes_in_param() {
129 check(
130 r#"
131enum E { X }
132
133static FOO: E = E::X;
134struct Bar { f: u32 }
135
136fn foo($0) {
137}
138"#,
139 expect![[r#"
140 st Bar
141 "#]],
142 );
143 }
144
145 #[test]
146 fn completes_pat_in_let() {
147 check_snippet(
148 r#"
149struct Bar { f: u32 }
150
151fn foo() {
152 let $0
153}
154"#,
155 expect![[r#"
156 bn Bar Bar { f$1 }$0
157 "#]],
158 );
159 }
160
161 #[test]
162 fn completes_param_pattern() {
163 check_snippet(
164 r#"
165struct Foo { bar: String, baz: String }
166struct Bar(String, String);
167struct Baz;
168fn outer($0) {}
169"#,
170 expect![[r#"
171 bn Foo Foo { bar$1, baz$2 }: Foo$0
172 bn Bar Bar($1, $2): Bar$0
173 "#]],
174 )
175 }
176
177 #[test]
178 fn completes_let_pattern() {
179 check_snippet(
180 r#"
181struct Foo { bar: String, baz: String }
182struct Bar(String, String);
183struct Baz;
184fn outer() {
185 let $0
186}
187"#,
188 expect![[r#"
189 bn Foo Foo { bar$1, baz$2 }$0
190 bn Bar Bar($1, $2)$0
191 "#]],
192 )
193 }
194
195 #[test]
196 fn completes_refutable_pattern() {
197 check_snippet(
198 r#"
199struct Foo { bar: i32, baz: i32 }
200struct Bar(String, String);
201struct Baz;
202fn outer() {
203 match () {
204 $0
205 }
206}
207"#,
208 expect![[r#"
209 bn Foo Foo { bar$1, baz$2 }$0
210 bn Bar Bar($1, $2)$0
211 "#]],
212 )
213 }
214
215 #[test]
216 fn omits_private_fields_pat() {
217 check_snippet(
218 r#"
219mod foo {
220 pub struct Foo { pub bar: i32, baz: i32 }
221 pub struct Bar(pub String, String);
222 pub struct Invisible(String, String);
223}
224use foo::*;
225
226fn outer() {
227 match () {
228 $0
229 }
230}
231"#,
232 expect![[r#"
233 bn Foo Foo { bar$1, .. }$0
234 bn Bar Bar($1, ..)$0
235 "#]],
236 )
237 }
238
239 #[test]
240 fn only_shows_ident_completion() {
241 check_edit(
242 "Foo",
243 r#"
244struct Foo(i32);
245fn main() {
246 match Foo(92) {
247 $0(92) => (),
248 }
249}
250"#,
251 r#"
252struct Foo(i32);
253fn main() {
254 match Foo(92) {
255 Foo(92) => (),
256 }
257}
258"#,
259 );
260 }
261}
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs
deleted file mode 100644
index 9c34ed0b6..000000000
--- a/crates/completion/src/completions/postfix.rs
+++ /dev/null
@@ -1,565 +0,0 @@
1//! Postfix completions, like `Ok(10).ifl$0` => `if let Ok() = Ok(10) { $0 }`.
2
3mod format_like;
4
5use ide_db::{helpers::SnippetCap, ty_filter::TryEnum};
6use syntax::{
7 ast::{self, AstNode, AstToken},
8 SyntaxKind::{BLOCK_EXPR, EXPR_STMT},
9 TextRange, TextSize,
10};
11use text_edit::TextEdit;
12
13use crate::{
14 completions::postfix::format_like::add_format_like_completions,
15 context::CompletionContext,
16 item::{Builder, CompletionKind},
17 CompletionItem, CompletionItemKind, Completions,
18};
19
20pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
21 if !ctx.config.enable_postfix_completions {
22 return;
23 }
24
25 let dot_receiver = match &ctx.dot_receiver {
26 Some(it) => it,
27 None => return,
28 };
29
30 let receiver_text =
31 get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
32
33 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
34 Some(it) => it,
35 None => return,
36 };
37
38 let ref_removed_ty =
39 std::iter::successors(Some(receiver_ty.clone()), |ty| ty.remove_ref()).last().unwrap();
40
41 let cap = match ctx.config.snippet_cap {
42 Some(it) => it,
43 None => return,
44 };
45 let try_enum = TryEnum::from_ty(&ctx.sema, &ref_removed_ty);
46 if let Some(try_enum) = &try_enum {
47 match try_enum {
48 TryEnum::Result => {
49 postfix_snippet(
50 ctx,
51 cap,
52 &dot_receiver,
53 "ifl",
54 "if let Ok {}",
55 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text),
56 )
57 .add_to(acc);
58
59 postfix_snippet(
60 ctx,
61 cap,
62 &dot_receiver,
63 "while",
64 "while let Ok {}",
65 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text),
66 )
67 .add_to(acc);
68 }
69 TryEnum::Option => {
70 postfix_snippet(
71 ctx,
72 cap,
73 &dot_receiver,
74 "ifl",
75 "if let Some {}",
76 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text),
77 )
78 .add_to(acc);
79
80 postfix_snippet(
81 ctx,
82 cap,
83 &dot_receiver,
84 "while",
85 "while let Some {}",
86 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text),
87 )
88 .add_to(acc);
89 }
90 }
91 } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
92 postfix_snippet(
93 ctx,
94 cap,
95 &dot_receiver,
96 "if",
97 "if expr {}",
98 &format!("if {} {{\n $0\n}}", receiver_text),
99 )
100 .add_to(acc);
101 postfix_snippet(
102 ctx,
103 cap,
104 &dot_receiver,
105 "while",
106 "while expr {}",
107 &format!("while {} {{\n $0\n}}", receiver_text),
108 )
109 .add_to(acc);
110 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
111 .add_to(acc);
112 }
113
114 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
115 .add_to(acc);
116 postfix_snippet(
117 ctx,
118 cap,
119 &dot_receiver,
120 "refm",
121 "&mut expr",
122 &format!("&mut {}", receiver_text),
123 )
124 .add_to(acc);
125
126 // The rest of the postfix completions create an expression that moves an argument,
127 // so it's better to consider references now to avoid breaking the compilation
128 let dot_receiver = include_references(dot_receiver);
129 let receiver_text =
130 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
131
132 match try_enum {
133 Some(try_enum) => match try_enum {
134 TryEnum::Result => {
135 postfix_snippet(
136 ctx,
137 cap,
138 &dot_receiver,
139 "match",
140 "match expr {}",
141 &format!("match {} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
142 )
143 .add_to(acc);
144 }
145 TryEnum::Option => {
146 postfix_snippet(
147 ctx,
148 cap,
149 &dot_receiver,
150 "match",
151 "match expr {}",
152 &format!(
153 "match {} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}",
154 receiver_text
155 ),
156 )
157 .add_to(acc);
158 }
159 },
160 None => {
161 postfix_snippet(
162 ctx,
163 cap,
164 &dot_receiver,
165 "match",
166 "match expr {}",
167 &format!("match {} {{\n ${{1:_}} => {{$0}},\n}}", receiver_text),
168 )
169 .add_to(acc);
170 }
171 }
172
173 postfix_snippet(
174 ctx,
175 cap,
176 &dot_receiver,
177 "box",
178 "Box::new(expr)",
179 &format!("Box::new({})", receiver_text),
180 )
181 .add_to(acc);
182
183 postfix_snippet(ctx, cap, &dot_receiver, "ok", "Ok(expr)", &format!("Ok({})", receiver_text))
184 .add_to(acc);
185
186 postfix_snippet(
187 ctx,
188 cap,
189 &dot_receiver,
190 "some",
191 "Some(expr)",
192 &format!("Some({})", receiver_text),
193 )
194 .add_to(acc);
195
196 postfix_snippet(
197 ctx,
198 cap,
199 &dot_receiver,
200 "dbg",
201 "dbg!(expr)",
202 &format!("dbg!({})", receiver_text),
203 )
204 .add_to(acc);
205
206 postfix_snippet(
207 ctx,
208 cap,
209 &dot_receiver,
210 "dbgr",
211 "dbg!(&expr)",
212 &format!("dbg!(&{})", receiver_text),
213 )
214 .add_to(acc);
215
216 postfix_snippet(
217 ctx,
218 cap,
219 &dot_receiver,
220 "call",
221 "function(expr)",
222 &format!("${{1}}({})", receiver_text),
223 )
224 .add_to(acc);
225
226 if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) {
227 if matches!(parent.kind(), BLOCK_EXPR | EXPR_STMT) {
228 postfix_snippet(
229 ctx,
230 cap,
231 &dot_receiver,
232 "let",
233 "let",
234 &format!("let $0 = {};", receiver_text),
235 )
236 .add_to(acc);
237 postfix_snippet(
238 ctx,
239 cap,
240 &dot_receiver,
241 "letm",
242 "let mut",
243 &format!("let mut $0 = {};", receiver_text),
244 )
245 .add_to(acc);
246 }
247 }
248
249 if let ast::Expr::Literal(literal) = dot_receiver.clone() {
250 if let Some(literal_text) = ast::String::cast(literal.token()) {
251 add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text);
252 }
253 }
254}
255
256fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
257 if receiver_is_ambiguous_float_literal {
258 let text = receiver.syntax().text();
259 let without_dot = ..text.len() - TextSize::of('.');
260 text.slice(without_dot).to_string()
261 } else {
262 receiver.to_string()
263 }
264}
265
266fn include_references(initial_element: &ast::Expr) -> ast::Expr {
267 let mut resulting_element = initial_element.clone();
268 while let Some(parent_ref_element) =
269 resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
270 {
271 resulting_element = ast::Expr::from(parent_ref_element);
272 }
273 resulting_element
274}
275
276fn postfix_snippet(
277 ctx: &CompletionContext,
278 cap: SnippetCap,
279 receiver: &ast::Expr,
280 label: &str,
281 detail: &str,
282 snippet: &str,
283) -> Builder {
284 let edit = {
285 let receiver_syntax = receiver.syntax();
286 let receiver_range = ctx.sema.original_range(receiver_syntax).range;
287 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
288 TextEdit::replace(delete_range, snippet.to_string())
289 };
290 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
291 .detail(detail)
292 .kind(CompletionItemKind::Snippet)
293 .snippet_edit(cap, edit)
294}
295
296#[cfg(test)]
297mod tests {
298 use expect_test::{expect, Expect};
299
300 use crate::{
301 test_utils::{check_edit, completion_list},
302 CompletionKind,
303 };
304
305 fn check(ra_fixture: &str, expect: Expect) {
306 let actual = completion_list(ra_fixture, CompletionKind::Postfix);
307 expect.assert_eq(&actual)
308 }
309
310 #[test]
311 fn postfix_completion_works_for_trivial_path_expression() {
312 check(
313 r#"
314fn main() {
315 let bar = true;
316 bar.$0
317}
318"#,
319 expect![[r#"
320 sn if if expr {}
321 sn while while expr {}
322 sn not !expr
323 sn ref &expr
324 sn refm &mut expr
325 sn match match expr {}
326 sn box Box::new(expr)
327 sn ok Ok(expr)
328 sn some Some(expr)
329 sn dbg dbg!(expr)
330 sn dbgr dbg!(&expr)
331 sn call function(expr)
332 sn let let
333 sn letm let mut
334 "#]],
335 );
336 }
337
338 #[test]
339 fn postfix_completion_works_for_function_calln() {
340 check(
341 r#"
342fn foo(elt: bool) -> bool {
343 !elt
344}
345
346fn main() {
347 let bar = true;
348 foo(bar.$0)
349}
350"#,
351 expect![[r#"
352 sn if if expr {}
353 sn while while expr {}
354 sn not !expr
355 sn ref &expr
356 sn refm &mut expr
357 sn match match expr {}
358 sn box Box::new(expr)
359 sn ok Ok(expr)
360 sn some Some(expr)
361 sn dbg dbg!(expr)
362 sn dbgr dbg!(&expr)
363 sn call function(expr)
364 "#]],
365 );
366 }
367
368 #[test]
369 fn postfix_type_filtering() {
370 check(
371 r#"
372fn main() {
373 let bar: u8 = 12;
374 bar.$0
375}
376"#,
377 expect![[r#"
378 sn ref &expr
379 sn refm &mut expr
380 sn match match expr {}
381 sn box Box::new(expr)
382 sn ok Ok(expr)
383 sn some Some(expr)
384 sn dbg dbg!(expr)
385 sn dbgr dbg!(&expr)
386 sn call function(expr)
387 sn let let
388 sn letm let mut
389 "#]],
390 )
391 }
392
393 #[test]
394 fn let_middle_block() {
395 check(
396 r#"
397fn main() {
398 baz.l$0
399 res
400}
401"#,
402 expect![[r#"
403 sn if if expr {}
404 sn while while expr {}
405 sn not !expr
406 sn ref &expr
407 sn refm &mut expr
408 sn match match expr {}
409 sn box Box::new(expr)
410 sn ok Ok(expr)
411 sn some Some(expr)
412 sn dbg dbg!(expr)
413 sn dbgr dbg!(&expr)
414 sn call function(expr)
415 sn let let
416 sn letm let mut
417 "#]],
418 );
419 }
420
421 #[test]
422 fn option_iflet() {
423 check_edit(
424 "ifl",
425 r#"
426enum Option<T> { Some(T), None }
427
428fn main() {
429 let bar = Option::Some(true);
430 bar.$0
431}
432"#,
433 r#"
434enum Option<T> { Some(T), None }
435
436fn main() {
437 let bar = Option::Some(true);
438 if let Some($1) = bar {
439 $0
440}
441}
442"#,
443 );
444 }
445
446 #[test]
447 fn result_match() {
448 check_edit(
449 "match",
450 r#"
451enum Result<T, E> { Ok(T), Err(E) }
452
453fn main() {
454 let bar = Result::Ok(true);
455 bar.$0
456}
457"#,
458 r#"
459enum Result<T, E> { Ok(T), Err(E) }
460
461fn main() {
462 let bar = Result::Ok(true);
463 match bar {
464 Ok(${1:_}) => {$2},
465 Err(${3:_}) => {$0},
466}
467}
468"#,
469 );
470 }
471
472 #[test]
473 fn postfix_completion_works_for_ambiguous_float_literal() {
474 check_edit("refm", r#"fn main() { 42.$0 }"#, r#"fn main() { &mut 42 }"#)
475 }
476
477 #[test]
478 fn works_in_simple_macro() {
479 check_edit(
480 "dbg",
481 r#"
482macro_rules! m { ($e:expr) => { $e } }
483fn main() {
484 let bar: u8 = 12;
485 m!(bar.d$0)
486}
487"#,
488 r#"
489macro_rules! m { ($e:expr) => { $e } }
490fn main() {
491 let bar: u8 = 12;
492 m!(dbg!(bar))
493}
494"#,
495 );
496 }
497
498 #[test]
499 fn postfix_completion_for_references() {
500 check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#);
501 check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#);
502 check_edit(
503 "ifl",
504 r#"
505enum Option<T> { Some(T), None }
506
507fn main() {
508 let bar = &Option::Some(true);
509 bar.$0
510}
511"#,
512 r#"
513enum Option<T> { Some(T), None }
514
515fn main() {
516 let bar = &Option::Some(true);
517 if let Some($1) = bar {
518 $0
519}
520}
521"#,
522 )
523 }
524
525 #[test]
526 fn postfix_completion_for_format_like_strings() {
527 check_edit(
528 "format",
529 r#"fn main() { "{some_var:?}".$0 }"#,
530 r#"fn main() { format!("{:?}", some_var) }"#,
531 );
532 check_edit(
533 "panic",
534 r#"fn main() { "Panic with {a}".$0 }"#,
535 r#"fn main() { panic!("Panic with {}", a) }"#,
536 );
537 check_edit(
538 "println",
539 r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".$0 }"#,
540 r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }) }"#,
541 );
542 check_edit(
543 "loge",
544 r#"fn main() { "{2+2}".$0 }"#,
545 r#"fn main() { log::error!("{}", 2+2) }"#,
546 );
547 check_edit(
548 "logt",
549 r#"fn main() { "{2+2}".$0 }"#,
550 r#"fn main() { log::trace!("{}", 2+2) }"#,
551 );
552 check_edit(
553 "logd",
554 r#"fn main() { "{2+2}".$0 }"#,
555 r#"fn main() { log::debug!("{}", 2+2) }"#,
556 );
557 check_edit("logi", r#"fn main() { "{2+2}".$0 }"#, r#"fn main() { log::info!("{}", 2+2) }"#);
558 check_edit("logw", r#"fn main() { "{2+2}".$0 }"#, r#"fn main() { log::warn!("{}", 2+2) }"#);
559 check_edit(
560 "loge",
561 r#"fn main() { "{2+2}".$0 }"#,
562 r#"fn main() { log::error!("{}", 2+2) }"#,
563 );
564 }
565}
diff --git a/crates/completion/src/completions/postfix/format_like.rs b/crates/completion/src/completions/postfix/format_like.rs
deleted file mode 100644
index 3afc63021..000000000
--- a/crates/completion/src/completions/postfix/format_like.rs
+++ /dev/null
@@ -1,287 +0,0 @@
1// Feature: Format String Completion.
2//
3// `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`.
4//
5// The following postfix snippets are available:
6//
7// - `format` -> `format!(...)`
8// - `panic` -> `panic!(...)`
9// - `println` -> `println!(...)`
10// - `log`:
11// + `logd` -> `log::debug!(...)`
12// + `logt` -> `log::trace!(...)`
13// + `logi` -> `log::info!(...)`
14// + `logw` -> `log::warn!(...)`
15// + `loge` -> `log::error!(...)`
16
17use ide_db::helpers::SnippetCap;
18use syntax::ast::{self, AstToken};
19
20use crate::{completions::postfix::postfix_snippet, context::CompletionContext, Completions};
21
22/// Mapping ("postfix completion item" => "macro to use")
23static KINDS: &[(&str, &str)] = &[
24 ("format", "format!"),
25 ("panic", "panic!"),
26 ("println", "println!"),
27 ("eprintln", "eprintln!"),
28 ("logd", "log::debug!"),
29 ("logt", "log::trace!"),
30 ("logi", "log::info!"),
31 ("logw", "log::warn!"),
32 ("loge", "log::error!"),
33];
34
35pub(crate) fn add_format_like_completions(
36 acc: &mut Completions,
37 ctx: &CompletionContext,
38 dot_receiver: &ast::Expr,
39 cap: SnippetCap,
40 receiver_text: &ast::String,
41) {
42 let input = match string_literal_contents(receiver_text) {
43 // It's not a string literal, do not parse input.
44 Some(input) => input,
45 None => return,
46 };
47
48 let mut parser = FormatStrParser::new(input);
49
50 if parser.parse().is_ok() {
51 for (label, macro_name) in KINDS {
52 let snippet = parser.into_suggestion(macro_name);
53
54 postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc);
55 }
56 }
57}
58
59/// Checks whether provided item is a string literal.
60fn string_literal_contents(item: &ast::String) -> Option<String> {
61 let item = item.text();
62 if item.len() >= 2 && item.starts_with("\"") && item.ends_with("\"") {
63 return Some(item[1..item.len() - 1].to_owned());
64 }
65
66 None
67}
68
69/// Parser for a format-like string. It is more allowing in terms of string contents,
70/// as we expect variable placeholders to be filled with expressions.
71#[derive(Debug)]
72pub(crate) struct FormatStrParser {
73 input: String,
74 output: String,
75 extracted_expressions: Vec<String>,
76 state: State,
77 parsed: bool,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq)]
81enum State {
82 NotExpr,
83 MaybeExpr,
84 Expr,
85 MaybeIncorrect,
86 FormatOpts,
87}
88
89impl FormatStrParser {
90 pub(crate) fn new(input: String) -> Self {
91 Self {
92 input: input.into(),
93 output: String::new(),
94 extracted_expressions: Vec::new(),
95 state: State::NotExpr,
96 parsed: false,
97 }
98 }
99
100 pub(crate) fn parse(&mut self) -> Result<(), ()> {
101 let mut current_expr = String::new();
102
103 let mut placeholder_id = 1;
104
105 // Count of open braces inside of an expression.
106 // We assume that user knows what they're doing, thus we treat it like a correct pattern, e.g.
107 // "{MyStruct { val_a: 0, val_b: 1 }}".
108 let mut inexpr_open_count = 0;
109
110 let mut chars = self.input.chars().peekable();
111 while let Some(chr) = chars.next() {
112 match (self.state, chr) {
113 (State::NotExpr, '{') => {
114 self.output.push(chr);
115 self.state = State::MaybeExpr;
116 }
117 (State::NotExpr, '}') => {
118 self.output.push(chr);
119 self.state = State::MaybeIncorrect;
120 }
121 (State::NotExpr, _) => {
122 self.output.push(chr);
123 }
124 (State::MaybeIncorrect, '}') => {
125 // It's okay, we met "}}".
126 self.output.push(chr);
127 self.state = State::NotExpr;
128 }
129 (State::MaybeIncorrect, _) => {
130 // Error in the string.
131 return Err(());
132 }
133 (State::MaybeExpr, '{') => {
134 self.output.push(chr);
135 self.state = State::NotExpr;
136 }
137 (State::MaybeExpr, '}') => {
138 // This is an empty sequence '{}'. Replace it with placeholder.
139 self.output.push(chr);
140 self.extracted_expressions.push(format!("${}", placeholder_id));
141 placeholder_id += 1;
142 self.state = State::NotExpr;
143 }
144 (State::MaybeExpr, _) => {
145 current_expr.push(chr);
146 self.state = State::Expr;
147 }
148 (State::Expr, '}') => {
149 if inexpr_open_count == 0 {
150 self.output.push(chr);
151 self.extracted_expressions.push(current_expr.trim().into());
152 current_expr = String::new();
153 self.state = State::NotExpr;
154 } else {
155 // We're closing one brace met before inside of the expression.
156 current_expr.push(chr);
157 inexpr_open_count -= 1;
158 }
159 }
160 (State::Expr, ':') if chars.peek().copied() == Some(':') => {
161 // path seperator
162 current_expr.push_str("::");
163 chars.next();
164 }
165 (State::Expr, ':') => {
166 if inexpr_open_count == 0 {
167 // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
168 self.output.push(chr);
169 self.extracted_expressions.push(current_expr.trim().into());
170 current_expr = String::new();
171 self.state = State::FormatOpts;
172 } else {
173 // We're inside of braced expression, assume that it's a struct field name/value delimeter.
174 current_expr.push(chr);
175 }
176 }
177 (State::Expr, '{') => {
178 current_expr.push(chr);
179 inexpr_open_count += 1;
180 }
181 (State::Expr, _) => {
182 current_expr.push(chr);
183 }
184 (State::FormatOpts, '}') => {
185 self.output.push(chr);
186 self.state = State::NotExpr;
187 }
188 (State::FormatOpts, _) => {
189 self.output.push(chr);
190 }
191 }
192 }
193
194 if self.state != State::NotExpr {
195 return Err(());
196 }
197
198 self.parsed = true;
199 Ok(())
200 }
201
202 pub(crate) fn into_suggestion(&self, macro_name: &str) -> String {
203 assert!(self.parsed, "Attempt to get a suggestion from not parsed expression");
204
205 let expressions_as_string = self.extracted_expressions.join(", ");
206 format!(r#"{}("{}", {})"#, macro_name, self.output, expressions_as_string)
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use expect_test::{expect, Expect};
214
215 fn check(input: &str, expect: &Expect) {
216 let mut parser = FormatStrParser::new((*input).to_owned());
217 let outcome_repr = if parser.parse().is_ok() {
218 // Parsing should be OK, expected repr is "string; expr_1, expr_2".
219 if parser.extracted_expressions.is_empty() {
220 parser.output
221 } else {
222 format!("{}; {}", parser.output, parser.extracted_expressions.join(", "))
223 }
224 } else {
225 // Parsing should fail, expected repr is "-".
226 "-".to_owned()
227 };
228
229 expect.assert_eq(&outcome_repr);
230 }
231
232 #[test]
233 fn format_str_parser() {
234 let test_vector = &[
235 ("no expressions", expect![["no expressions"]]),
236 ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
237 ("{expr:?}", expect![["{:?}; expr"]]),
238 ("{malformed", expect![["-"]]),
239 ("malformed}", expect![["-"]]),
240 ("{{correct", expect![["{{correct"]]),
241 ("correct}}", expect![["correct}}"]]),
242 ("{correct}}}", expect![["{}}}; correct"]]),
243 ("{correct}}}}}", expect![["{}}}}}; correct"]]),
244 ("{incorrect}}", expect![["-"]]),
245 ("placeholders {} {}", expect![["placeholders {} {}; $1, $2"]]),
246 ("mixed {} {2 + 2} {}", expect![["mixed {} {} {}; $1, 2 + 2, $2"]]),
247 (
248 "{SomeStruct { val_a: 0, val_b: 1 }}",
249 expect![["{}; SomeStruct { val_a: 0, val_b: 1 }"]],
250 ),
251 ("{expr:?} is {2.32f64:.5}", expect![["{:?} is {:.5}; expr, 2.32f64"]]),
252 (
253 "{SomeStruct { val_a: 0, val_b: 1 }:?}",
254 expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]],
255 ),
256 ("{ 2 + 2 }", expect![["{}; 2 + 2"]]),
257 ("{strsim::jaro_winkle(a)}", expect![["{}; strsim::jaro_winkle(a)"]]),
258 ("{foo::bar::baz()}", expect![["{}; foo::bar::baz()"]]),
259 ("{foo::bar():?}", expect![["{:?}; foo::bar()"]]),
260 ];
261
262 for (input, output) in test_vector {
263 check(input, output)
264 }
265 }
266
267 #[test]
268 fn test_into_suggestion() {
269 let test_vector = &[
270 ("println!", "{}", r#"println!("{}", $1)"#),
271 ("eprintln!", "{}", r#"eprintln!("{}", $1)"#),
272 (
273 "log::info!",
274 "{} {expr} {} {2 + 2}",
275 r#"log::info!("{} {} {} {}", $1, expr, $2, 2 + 2)"#,
276 ),
277 ("format!", "{expr:?}", r#"format!("{:?}", expr)"#),
278 ];
279
280 for (kind, input, output) in test_vector {
281 let mut parser = FormatStrParser::new((*input).to_owned());
282 parser.parse().expect("Parsing must succeed");
283
284 assert_eq!(&parser.into_suggestion(*kind), output);
285 }
286 }
287}
diff --git a/crates/completion/src/completions/qualified_path.rs b/crates/completion/src/completions/qualified_path.rs
deleted file mode 100644
index 33df26761..000000000
--- a/crates/completion/src/completions/qualified_path.rs
+++ /dev/null
@@ -1,783 +0,0 @@
1//! Completion of paths, i.e. `some::prefix::$0`.
2
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use rustc_hash::FxHashSet;
5use syntax::AstNode;
6use test_utils::mark;
7
8use crate::{CompletionContext, Completions};
9
10pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 let path = match &ctx.path_qual {
12 Some(path) => path.clone(),
13 None => return,
14 };
15
16 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
17 return;
18 }
19
20 let context_module = ctx.scope.module();
21
22 let resolution = match ctx.sema.resolve_path(&path) {
23 Some(res) => res,
24 None => return,
25 };
26
27 // Add associated types on type parameters and `Self`.
28 resolution.assoc_type_shorthand_candidates(ctx.db, |alias| {
29 acc.add_type_alias(ctx, alias);
30 None::<()>
31 });
32
33 match resolution {
34 PathResolution::Def(hir::ModuleDef::Module(module)) => {
35 let module_scope = module.scope(ctx.db, context_module);
36 for (name, def) in module_scope {
37 if ctx.use_item_syntax.is_some() {
38 if let ScopeDef::Unknown = def {
39 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
40 if name_ref.syntax().text() == name.to_string().as_str() {
41 // for `use self::foo$0`, don't suggest `foo` as a completion
42 mark::hit!(dont_complete_current_use);
43 continue;
44 }
45 }
46 }
47 }
48
49 acc.add_resolution(ctx, name.to_string(), &def);
50 }
51 }
52 PathResolution::Def(def @ hir::ModuleDef::Adt(_))
53 | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => {
54 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
55 for variant in e.variants(ctx.db) {
56 acc.add_enum_variant(ctx, variant, None);
57 }
58 }
59 let ty = match def {
60 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
61 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
62 _ => unreachable!(),
63 };
64
65 // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
66 // (where AssocType is defined on a trait, not an inherent impl)
67
68 let krate = ctx.krate;
69 if let Some(krate) = krate {
70 let traits_in_scope = ctx.scope.traits_in_scope();
71 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
72 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
73 return None;
74 }
75 match item {
76 hir::AssocItem::Function(func) => {
77 acc.add_function(ctx, func, None);
78 }
79 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
80 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
81 }
82 None::<()>
83 });
84
85 // Iterate assoc types separately
86 ty.iterate_assoc_items(ctx.db, krate, |item| {
87 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
88 return None;
89 }
90 match item {
91 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {}
92 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
93 }
94 None::<()>
95 });
96 }
97 }
98 PathResolution::Def(hir::ModuleDef::Trait(t)) => {
99 // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
100 for item in t.items(ctx.db) {
101 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
102 continue;
103 }
104 match item {
105 hir::AssocItem::Function(func) => {
106 acc.add_function(ctx, func, None);
107 }
108 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
109 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
110 }
111 }
112 }
113 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
114 if let Some(krate) = ctx.krate {
115 let ty = match resolution {
116 PathResolution::TypeParam(param) => param.ty(ctx.db),
117 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
118 _ => return,
119 };
120
121 if let Some(Adt::Enum(e)) = ty.as_adt() {
122 for variant in e.variants(ctx.db) {
123 acc.add_enum_variant(ctx, variant, None);
124 }
125 }
126
127 let traits_in_scope = ctx.scope.traits_in_scope();
128 let mut seen = FxHashSet::default();
129 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
130 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
131 return None;
132 }
133
134 // We might iterate candidates of a trait multiple times here, so deduplicate
135 // them.
136 if seen.insert(item) {
137 match item {
138 hir::AssocItem::Function(func) => {
139 acc.add_function(ctx, func, None);
140 }
141 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
142 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
143 }
144 }
145 None::<()>
146 });
147 }
148 }
149 _ => {}
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use expect_test::{expect, Expect};
156 use test_utils::mark;
157
158 use crate::{
159 test_utils::{check_edit, completion_list},
160 CompletionKind,
161 };
162
163 fn check(ra_fixture: &str, expect: Expect) {
164 let actual = completion_list(ra_fixture, CompletionKind::Reference);
165 expect.assert_eq(&actual);
166 }
167
168 fn check_builtin(ra_fixture: &str, expect: Expect) {
169 let actual = completion_list(ra_fixture, CompletionKind::BuiltinType);
170 expect.assert_eq(&actual);
171 }
172
173 #[test]
174 fn dont_complete_current_use() {
175 mark::check!(dont_complete_current_use);
176 check(r#"use self::foo$0;"#, expect![[""]]);
177 }
178
179 #[test]
180 fn dont_complete_current_use_in_braces_with_glob() {
181 check(
182 r#"
183mod foo { pub struct S; }
184use self::{foo::*, bar$0};
185"#,
186 expect![[r#"
187 st S
188 md foo
189 "#]],
190 );
191 }
192
193 #[test]
194 fn dont_complete_primitive_in_use() {
195 check_builtin(r#"use self::$0;"#, expect![[""]]);
196 }
197
198 #[test]
199 fn dont_complete_primitive_in_module_scope() {
200 check_builtin(r#"fn foo() { self::$0 }"#, expect![[""]]);
201 }
202
203 #[test]
204 fn completes_primitives() {
205 check_builtin(
206 r#"fn main() { let _: $0 = 92; }"#,
207 expect![[r#"
208 bt u32
209 bt bool
210 bt u8
211 bt isize
212 bt u16
213 bt u64
214 bt u128
215 bt f32
216 bt i128
217 bt i16
218 bt str
219 bt i64
220 bt char
221 bt f64
222 bt i32
223 bt i8
224 bt usize
225 "#]],
226 );
227 }
228
229 #[test]
230 fn completes_mod_with_same_name_as_function() {
231 check(
232 r#"
233use self::my::$0;
234
235mod my { pub struct Bar; }
236fn my() {}
237"#,
238 expect![[r#"
239 st Bar
240 "#]],
241 );
242 }
243
244 #[test]
245 fn filters_visibility() {
246 check(
247 r#"
248use self::my::$0;
249
250mod my {
251 struct Bar;
252 pub struct Foo;
253 pub use Bar as PublicBar;
254}
255"#,
256 expect![[r#"
257 st Foo
258 st PublicBar
259 "#]],
260 );
261 }
262
263 #[test]
264 fn completes_use_item_starting_with_self() {
265 check(
266 r#"
267use self::m::$0;
268
269mod m { pub struct Bar; }
270"#,
271 expect![[r#"
272 st Bar
273 "#]],
274 );
275 }
276
277 #[test]
278 fn completes_use_item_starting_with_crate() {
279 check(
280 r#"
281//- /lib.rs
282mod foo;
283struct Spam;
284//- /foo.rs
285use crate::Sp$0
286"#,
287 expect![[r#"
288 md foo
289 st Spam
290 "#]],
291 );
292 }
293
294 #[test]
295 fn completes_nested_use_tree() {
296 check(
297 r#"
298//- /lib.rs
299mod foo;
300struct Spam;
301//- /foo.rs
302use crate::{Sp$0};
303"#,
304 expect![[r#"
305 md foo
306 st Spam
307 "#]],
308 );
309 }
310
311 #[test]
312 fn completes_deeply_nested_use_tree() {
313 check(
314 r#"
315//- /lib.rs
316mod foo;
317pub mod bar {
318 pub mod baz {
319 pub struct Spam;
320 }
321}
322//- /foo.rs
323use crate::{bar::{baz::Sp$0}};
324"#,
325 expect![[r#"
326 st Spam
327 "#]],
328 );
329 }
330
331 #[test]
332 fn completes_enum_variant() {
333 check(
334 r#"
335enum E { Foo, Bar(i32) }
336fn foo() { let _ = E::$0 }
337"#,
338 expect![[r#"
339 ev Foo ()
340 ev Bar(…) (i32)
341 "#]],
342 );
343 }
344
345 #[test]
346 fn completes_struct_associated_items() {
347 check(
348 r#"
349//- /lib.rs
350struct S;
351
352impl S {
353 fn a() {}
354 fn b(&self) {}
355 const C: i32 = 42;
356 type T = i32;
357}
358
359fn foo() { let _ = S::$0 }
360"#,
361 expect![[r#"
362 fn a() fn a()
363 me b(…) fn b(&self)
364 ct C const C: i32 = 42;
365 ta T type T = i32;
366 "#]],
367 );
368 }
369
370 #[test]
371 fn associated_item_visibility() {
372 check(
373 r#"
374struct S;
375
376mod m {
377 impl super::S {
378 pub(crate) fn public_method() { }
379 fn private_method() { }
380 pub(crate) type PublicType = u32;
381 type PrivateType = u32;
382 pub(crate) const PUBLIC_CONST: u32 = 1;
383 const PRIVATE_CONST: u32 = 1;
384 }
385}
386
387fn foo() { let _ = S::$0 }
388"#,
389 expect![[r#"
390 fn public_method() pub(crate) fn public_method()
391 ct PUBLIC_CONST pub(crate) const PUBLIC_CONST: u32 = 1;
392 ta PublicType pub(crate) type PublicType = u32;
393 "#]],
394 );
395 }
396
397 #[test]
398 fn completes_enum_associated_method() {
399 check(
400 r#"
401enum E {};
402impl E { fn m() { } }
403
404fn foo() { let _ = E::$0 }
405 "#,
406 expect![[r#"
407 fn m() fn m()
408 "#]],
409 );
410 }
411
412 #[test]
413 fn completes_union_associated_method() {
414 check(
415 r#"
416union U {};
417impl U { fn m() { } }
418
419fn foo() { let _ = U::$0 }
420"#,
421 expect![[r#"
422 fn m() fn m()
423 "#]],
424 );
425 }
426
427 #[test]
428 fn completes_use_paths_across_crates() {
429 check(
430 r#"
431//- /main.rs crate:main deps:foo
432use foo::$0;
433
434//- /foo/lib.rs crate:foo
435pub mod bar { pub struct S; }
436"#,
437 expect![[r#"
438 md bar
439 "#]],
440 );
441 }
442
443 #[test]
444 fn completes_trait_associated_method_1() {
445 check(
446 r#"
447trait Trait { fn m(); }
448
449fn foo() { let _ = Trait::$0 }
450"#,
451 expect![[r#"
452 fn m() fn m()
453 "#]],
454 );
455 }
456
457 #[test]
458 fn completes_trait_associated_method_2() {
459 check(
460 r#"
461trait Trait { fn m(); }
462
463struct S;
464impl Trait for S {}
465
466fn foo() { let _ = S::$0 }
467"#,
468 expect![[r#"
469 fn m() fn m()
470 "#]],
471 );
472 }
473
474 #[test]
475 fn completes_trait_associated_method_3() {
476 check(
477 r#"
478trait Trait { fn m(); }
479
480struct S;
481impl Trait for S {}
482
483fn foo() { let _ = <S as Trait>::$0 }
484"#,
485 expect![[r#"
486 fn m() fn m()
487 "#]],
488 );
489 }
490
491 #[test]
492 fn completes_ty_param_assoc_ty() {
493 check(
494 r#"
495trait Super {
496 type Ty;
497 const CONST: u8;
498 fn func() {}
499 fn method(&self) {}
500}
501
502trait Sub: Super {
503 type SubTy;
504 const C2: ();
505 fn subfunc() {}
506 fn submethod(&self) {}
507}
508
509fn foo<T: Sub>() { T::$0 }
510"#,
511 expect![[r#"
512 ta SubTy type SubTy;
513 ta Ty type Ty;
514 ct C2 const C2: ();
515 fn subfunc() fn subfunc()
516 me submethod(…) fn submethod(&self)
517 ct CONST const CONST: u8;
518 fn func() fn func()
519 me method(…) fn method(&self)
520 "#]],
521 );
522 }
523
524 #[test]
525 fn completes_self_param_assoc_ty() {
526 check(
527 r#"
528trait Super {
529 type Ty;
530 const CONST: u8 = 0;
531 fn func() {}
532 fn method(&self) {}
533}
534
535trait Sub: Super {
536 type SubTy;
537 const C2: () = ();
538 fn subfunc() {}
539 fn submethod(&self) {}
540}
541
542struct Wrap<T>(T);
543impl<T> Super for Wrap<T> {}
544impl<T> Sub for Wrap<T> {
545 fn subfunc() {
546 // Should be able to assume `Self: Sub + Super`
547 Self::$0
548 }
549}
550"#,
551 expect![[r#"
552 ta SubTy type SubTy;
553 ta Ty type Ty;
554 ct CONST const CONST: u8 = 0;
555 fn func() fn func()
556 me method(…) fn method(&self)
557 ct C2 const C2: () = ();
558 fn subfunc() fn subfunc()
559 me submethod(…) fn submethod(&self)
560 "#]],
561 );
562 }
563
564 #[test]
565 fn completes_type_alias() {
566 check(
567 r#"
568struct S;
569impl S { fn foo() {} }
570type T = S;
571impl T { fn bar() {} }
572
573fn main() { T::$0; }
574"#,
575 expect![[r#"
576 fn foo() fn foo()
577 fn bar() fn bar()
578 "#]],
579 );
580 }
581
582 #[test]
583 fn completes_qualified_macros() {
584 check(
585 r#"
586#[macro_export]
587macro_rules! foo { () => {} }
588
589fn main() { let _ = crate::$0 }
590 "#,
591 expect![[r##"
592 fn main() fn main()
593 ma foo!(…) #[macro_export] macro_rules! foo
594