aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src/completions
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src/completions')
-rw-r--r--crates/ide_completion/src/completions/attribute.rs557
-rw-r--r--crates/ide_completion/src/completions/dot.rs431
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs688
-rw-r--r--crates/ide_completion/src/completions/fn_param.rs135
-rw-r--r--crates/ide_completion/src/completions/keyword.rs668
-rw-r--r--crates/ide_completion/src/completions/macro_in_item_position.rs41
-rw-r--r--crates/ide_completion/src/completions/mod_.rs315
-rw-r--r--crates/ide_completion/src/completions/pattern.rs317
-rw-r--r--crates/ide_completion/src/completions/postfix.rs565
-rw-r--r--crates/ide_completion/src/completions/postfix/format_like.rs287
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs815
-rw-r--r--crates/ide_completion/src/completions/record.rs390
-rw-r--r--crates/ide_completion/src/completions/snippet.rs116
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs736
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs755
15 files changed, 6816 insertions, 0 deletions
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
new file mode 100644
index 000000000..ab25a8c58
--- /dev/null
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -0,0 +1,557 @@
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(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
103 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
104 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
105 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
106 // FIXME: resolve through macro resolution?
107 attr("global_allocator", None, None).prefer_inner(),
108 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
109 attr("inline", Some("inline"), Some("inline")),
110 attr("link", None, None),
111 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
112 attr(
113 r#"link_section = "…""#,
114 Some("link_section"),
115 Some(r#"link_section = "${0:section_name}""#),
116 ),
117 attr("macro_export", None, None),
118 attr("macro_use", None, None),
119 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)),
120 attr("no_link", None, None).prefer_inner(),
121 attr("no_implicit_prelude", None, None).prefer_inner(),
122 attr("no_main", None, None).prefer_inner(),
123 attr("no_mangle", None, None),
124 attr("no_std", None, None).prefer_inner(),
125 attr("non_exhaustive", None, None),
126 attr("panic_handler", None, None).prefer_inner(),
127 attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)),
128 attr("proc_macro", None, None),
129 attr("proc_macro_attribute", None, None),
130 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
131 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
132 .prefer_inner(),
133 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
134 attr("should_panic", Some("should_panic"), Some(r#"should_panic"#)),
135 attr(
136 r#"target_feature = "…""#,
137 Some("target_feature"),
138 Some(r#"target_feature = "${0:feature}""#),
139 ),
140 attr("test", None, None),
141 attr("track_caller", None, None),
142 attr("type_length_limit = …", Some("type_length_limit"), Some("type_length_limit = ${0:128}"))
143 .prefer_inner(),
144 attr("used", None, None),
145 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
146 attr(
147 r#"windows_subsystem = "…""#,
148 Some("windows_subsystem"),
149 Some(r#"windows_subsystem = "${0:subsystem}""#),
150 )
151 .prefer_inner(),
152];
153
154fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
155 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
156 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
157 .iter()
158 .filter(|completion| !existing_derives.contains(completion.label))
159 {
160 let mut components = vec![derive_completion.label];
161 components.extend(
162 derive_completion
163 .dependencies
164 .iter()
165 .filter(|&&dependency| !existing_derives.contains(dependency)),
166 );
167 let lookup = components.join(", ");
168 let label = components.iter().rev().join(", ");
169 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
170 .lookup_by(lookup)
171 .kind(CompletionItemKind::Attribute)
172 .add_to(acc)
173 }
174
175 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
176 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), custom_derive_name)
177 .kind(CompletionItemKind::Attribute)
178 .add_to(acc)
179 }
180 }
181}
182
183fn complete_lint(
184 acc: &mut Completions,
185 ctx: &CompletionContext,
186 derive_input: ast::TokenTree,
187 lints_completions: &[LintCompletion],
188) {
189 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
190 for lint_completion in lints_completions
191 .into_iter()
192 .filter(|completion| !existing_lints.contains(completion.label))
193 {
194 CompletionItem::new(
195 CompletionKind::Attribute,
196 ctx.source_range(),
197 lint_completion.label,
198 )
199 .kind(CompletionItemKind::Attribute)
200 .detail(lint_completion.description)
201 .add_to(acc)
202 }
203 }
204}
205
206fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
207 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
208 (Some(left_paren), Some(right_paren))
209 if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] =>
210 {
211 let mut input_derives = FxHashSet::default();
212 let mut current_derive = String::new();
213 for token in derive_input
214 .syntax()
215 .children_with_tokens()
216 .filter_map(|token| token.into_token())
217 .skip_while(|token| token != &left_paren)
218 .skip(1)
219 .take_while(|token| token != &right_paren)
220 {
221 if T![,] == token.kind() {
222 if !current_derive.is_empty() {
223 input_derives.insert(current_derive);
224 current_derive = String::new();
225 }
226 } else {
227 current_derive.push_str(token.text().trim());
228 }
229 }
230
231 if !current_derive.is_empty() {
232 input_derives.insert(current_derive);
233 }
234 Ok(input_derives)
235 }
236 _ => Err(()),
237 }
238}
239
240fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
241 let mut result = FxHashSet::default();
242 ctx.scope.process_all_names(&mut |name, scope_def| {
243 if let hir::ScopeDef::MacroDef(mac) = scope_def {
244 if mac.is_derive_macro() {
245 result.insert(name.to_string());
246 }
247 }
248 });
249 result
250}
251
252struct DeriveCompletion {
253 label: &'static str,
254 dependencies: &'static [&'static str],
255}
256
257/// Standard Rust derives and the information about their dependencies
258/// (the dependencies are needed so that the main derive don't break the compilation when added)
259const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
260 DeriveCompletion { label: "Clone", dependencies: &[] },
261 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
262 DeriveCompletion { label: "Debug", dependencies: &[] },
263 DeriveCompletion { label: "Default", dependencies: &[] },
264 DeriveCompletion { label: "Hash", dependencies: &[] },
265 DeriveCompletion { label: "PartialEq", dependencies: &[] },
266 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
267 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
268 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
269];
270
271pub(crate) struct LintCompletion {
272 pub(crate) label: &'static str,
273 pub(crate) description: &'static str,
274}
275
276#[rustfmt::skip]
277const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
278 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"# },
279 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
280 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
281 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
282 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
283 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
284 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
285 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
286 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
287 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
288 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
289 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
290 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
291 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
292 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
293 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
294 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
295 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
296 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
297 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
298 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
299 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
300 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
301 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
302 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
303 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
304 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
305 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
306 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
307 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
308 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
309 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
310 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
311 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
312 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
313 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
314 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
315 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
316 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
317 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
318 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
319 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
320 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
321 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
322 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
323 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
324 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
325 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
326 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
327 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
328 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
329 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
330 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
331 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
332 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
333 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
334 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
335 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
336 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
337 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
338 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
339 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
340 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
341 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
342 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
343 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
344 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
345 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
346 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
347 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
348 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
349 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
350 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
351 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
352 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
353 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
354 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
355 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
356 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
357 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
358 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
359 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
360 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
361 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
362 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
363 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
364 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
365 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
366 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
367 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
368 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
369 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
370 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
371 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
372 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
373 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
374 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
375 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
376 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
377 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
378 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
379 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
380 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
381 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
382 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"# },
383 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
384 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
385 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
386 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
387 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
388 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
389 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
390 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
391 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
392 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
393];
394
395#[cfg(test)]
396mod tests {
397 use expect_test::{expect, Expect};
398
399 use crate::{test_utils::completion_list, CompletionKind};
400
401 fn check(ra_fixture: &str, expect: Expect) {
402 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
403 expect.assert_eq(&actual);
404 }
405
406 #[test]
407 fn empty_derive_completion() {
408 check(
409 r#"
410#[derive($0)]
411struct Test {}
412 "#,
413 expect![[r#"
414 at Clone
415 at Clone, Copy
416 at Debug
417 at Default
418 at Hash
419 at PartialEq
420 at PartialEq, Eq
421 at PartialEq, PartialOrd
422 at PartialEq, Eq, PartialOrd, Ord
423 "#]],
424 );
425 }
426
427 #[test]
428 fn no_completion_for_incorrect_derive() {
429 check(
430 r#"
431#[derive{$0)]
432struct Test {}
433"#,
434 expect![[r#""#]],
435 )
436 }
437
438 #[test]
439 fn derive_with_input_completion() {
440 check(
441 r#"
442#[derive(serde::Serialize, PartialEq, $0)]
443struct Test {}
444"#,
445 expect![[r#"
446 at Clone
447 at Clone, Copy
448 at Debug
449 at Default
450 at Hash
451 at Eq
452 at PartialOrd
453 at Eq, PartialOrd, Ord
454 "#]],
455 )
456 }
457
458 #[test]
459 fn test_attribute_completion() {
460 check(
461 r#"#[$0]"#,
462 expect![[r#"
463 at allow(…)
464 at automatically_derived
465 at cfg_attr(…)
466 at cfg(…)
467 at cold
468 at deny(…)
469 at deprecated
470 at derive(…)
471 at export_name = "…"
472 at doc(alias = "…")
473 at doc = "…"
474 at forbid(…)
475 at ignore = "…"
476 at inline
477 at link
478 at link_name = "…"
479 at link_section = "…"
480 at macro_export
481 at macro_use
482 at must_use
483 at no_mangle
484 at non_exhaustive
485 at path = "…"
486 at proc_macro
487 at proc_macro_attribute
488 at proc_macro_derive(…)
489 at repr(…)
490 at should_panic
491 at target_feature = "…"
492 at test
493 at track_caller
494 at used
495 at warn(…)
496 "#]],
497 )
498 }
499
500 #[test]
501 fn test_attribute_completion_inside_nested_attr() {
502 check(r#"#[cfg($0)]"#, expect![[]])
503 }
504
505 #[test]
506 fn test_inner_attribute_completion() {
507 check(
508 r"#![$0]",
509 expect![[r#"
510 at allow(…)
511 at automatically_derived
512 at cfg_attr(…)
513 at cfg(…)
514 at cold
515 at crate_name = ""
516 at deny(…)
517 at deprecated
518 at derive(…)
519 at export_name = "…"
520 at doc(alias = "…")
521 at doc = "…"
522 at feature(…)
523 at forbid(…)
524 at global_allocator
525 at ignore = "…"
526 at inline
527 at link
528 at link_name = "…"
529 at link_section = "…"
530 at macro_export
531 at macro_use
532 at must_use
533 at no_link
534 at no_implicit_prelude
535 at no_main
536 at no_mangle
537 at no_std
538 at non_exhaustive
539 at panic_handler
540 at path = "…"
541 at proc_macro
542 at proc_macro_attribute
543 at proc_macro_derive(…)
544 at recursion_limit = …
545 at repr(…)
546 at should_panic
547 at target_feature = "…"
548 at test
549 at track_caller
550 at type_length_limit = …
551 at used
552 at warn(…)
553 at windows_subsystem = "…"
554 "#]],
555 );
556 }
557}
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
new file mode 100644
index 000000000..0880a3830
--- /dev/null
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -0,0 +1,431 @@
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() -> ()
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() -> ()
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() -> ()
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() -> ()
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() -> ()
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() -> ()
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() -> ()
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() -> ()
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() -> ()
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() -> ()
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() -> ()
428 "#]],
429 )
430 }
431}
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
new file mode 100644
index 000000000..c9f928483
--- /dev/null
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -0,0 +1,688 @@
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//! Also completes associated items, that require trait imports.
24//!
25//! .Fuzzy search details
26//!
27//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
28//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
29//! For the same reasons, avoids searching for any path imports for inputs with their length less that 2 symbols
30//! (but shows all associated items for any input length).
31//!
32//! .Import configuration
33//!
34//! It is possible to configure how use-trees are merged with the `importMergeBehavior` setting.
35//! Mimics the corresponding behavior of the `Auto Import` feature.
36//!
37//! .LSP and performance implications
38//!
39//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
40//! (case sensitive) resolve client capability in its client capabilities.
41//! This way the server is able to defer the costly computations, doing them for a selected completion item only.
42//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
43//! which might be slow ergo the feature is automatically disabled.
44//!
45//! .Feature toggle
46//!
47//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag.
48//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
49//! capability enabled.
50
51use hir::{AsAssocItem, ModPath, ScopeDef};
52use ide_db::helpers::{
53 import_assets::{ImportAssets, ImportCandidate},
54 insert_use::ImportScope,
55};
56use syntax::{AstNode, SyntaxNode, T};
57use test_utils::mark;
58
59use crate::{
60 context::CompletionContext,
61 render::{render_resolution_with_import, RenderContext},
62 ImportEdit,
63};
64
65use super::Completions;
66
67pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
68 if !ctx.config.enable_imports_on_the_fly {
69 return None;
70 }
71 if ctx.use_item_syntax.is_some()
72 || ctx.attribute_under_caret.is_some()
73 || ctx.mod_declaration_under_caret.is_some()
74 {
75 return None;
76 }
77 let potential_import_name = {
78 let token_kind = ctx.token.kind();
79 if matches!(token_kind, T![.] | T![::]) {
80 String::new()
81 } else {
82 ctx.token.to_string()
83 }
84 };
85
86 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
87
88 let user_input_lowercased = potential_import_name.to_lowercase();
89 let import_assets = import_assets(ctx, potential_import_name)?;
90 let import_scope = ImportScope::find_insert_use_container(
91 position_for_import(ctx, Some(import_assets.import_candidate()))?,
92 &ctx.sema,
93 )?;
94 let mut all_mod_paths = import_assets
95 .search_for_relative_paths(&ctx.sema)
96 .into_iter()
97 .map(|(mod_path, item_in_ns)| {
98 let scope_item = match item_in_ns {
99 hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
100 hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
101 hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
102 };
103 (mod_path, scope_item)
104 })
105 .collect::<Vec<_>>();
106 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
107 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
108 });
109
110 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
111 let import_for_trait_assoc_item = match definition {
112 ScopeDef::ModuleDef(module_def) => module_def
113 .as_assoc_item(ctx.db)
114 .and_then(|assoc| assoc.containing_trait(ctx.db))
115 .is_some(),
116 _ => false,
117 };
118 let import_edit = ImportEdit {
119 import_path,
120 import_scope: import_scope.clone(),
121 import_for_trait_assoc_item,
122 };
123 render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
124 }));
125 Some(())
126}
127
128pub(crate) fn position_for_import<'a>(
129 ctx: &'a CompletionContext,
130 import_candidate: Option<&ImportCandidate>,
131) -> Option<&'a SyntaxNode> {
132 Some(match import_candidate {
133 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
134 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(),
135 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver.as_ref()?.syntax(),
136 None => ctx
137 .name_ref_syntax
138 .as_ref()
139 .map(|name_ref| name_ref.syntax())
140 .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax()))
141 .or_else(|| ctx.dot_receiver.as_ref().map(|expr| expr.syntax()))?,
142 })
143}
144
145fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
146 let current_module = ctx.scope.module()?;
147 if let Some(dot_receiver) = &ctx.dot_receiver {
148 ImportAssets::for_fuzzy_method_call(
149 current_module,
150 ctx.sema.type_of_expr(dot_receiver)?,
151 fuzzy_name,
152 )
153 } else {
154 let fuzzy_name_length = fuzzy_name.len();
155 let assets_for_path = ImportAssets::for_fuzzy_path(
156 current_module,
157 ctx.path_qual.clone(),
158 fuzzy_name,
159 &ctx.sema,
160 );
161
162 if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_))
163 && fuzzy_name_length < 2
164 {
165 mark::hit!(ignore_short_input_for_path);
166 None
167 } else {
168 assets_for_path
169 }
170 }
171}
172
173fn compute_fuzzy_completion_order_key(
174 proposed_mod_path: &ModPath,
175 user_input_lowercased: &str,
176) -> usize {
177 mark::hit!(certain_fuzzy_order_test);
178 let proposed_import_name = match proposed_mod_path.segments().last() {
179 Some(name) => name.to_string().to_lowercase(),
180 None => return usize::MAX,
181 };
182 match proposed_import_name.match_indices(user_input_lowercased).next() {
183 Some((first_matching_index, _)) => first_matching_index,
184 None => usize::MAX,
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use expect_test::{expect, Expect};
191 use test_utils::mark;
192
193 use crate::{
194 item::CompletionKind,
195 test_utils::{check_edit, completion_list},
196 };
197
198 fn check(ra_fixture: &str, expect: Expect) {
199 let actual = completion_list(ra_fixture, CompletionKind::Magic);
200 expect.assert_eq(&actual);
201 }
202
203 #[test]
204 fn function_fuzzy_completion() {
205 check_edit(
206 "stdin",
207 r#"
208//- /lib.rs crate:dep
209pub mod io {
210 pub fn stdin() {}
211};
212
213//- /main.rs crate:main deps:dep
214fn main() {
215 stdi$0
216}
217"#,
218 r#"
219use dep::io::stdin;
220
221fn main() {
222 stdin()$0
223}
224"#,
225 );
226 }
227
228 #[test]
229 fn macro_fuzzy_completion() {
230 check_edit(
231 "macro_with_curlies!",
232 r#"
233//- /lib.rs crate:dep
234/// Please call me as macro_with_curlies! {}
235#[macro_export]
236macro_rules! macro_with_curlies {
237 () => {}
238}
239
240//- /main.rs crate:main deps:dep
241fn main() {
242 curli$0
243}
244"#,
245 r#"
246use dep::macro_with_curlies;
247
248fn main() {
249 macro_with_curlies! {$0}
250}
251"#,
252 );
253 }
254
255 #[test]
256 fn struct_fuzzy_completion() {
257 check_edit(
258 "ThirdStruct",
259 r#"
260//- /lib.rs crate:dep
261pub struct FirstStruct;
262pub mod some_module {
263 pub struct SecondStruct;
264 pub struct ThirdStruct;
265}
266
267//- /main.rs crate:main deps:dep
268use dep::{FirstStruct, some_module::SecondStruct};
269
270fn main() {
271 this$0
272}
273"#,
274 r#"
275use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
276
277fn main() {
278 ThirdStruct
279}
280"#,
281 );
282 }
283
284 #[test]
285 fn short_paths_are_ignored() {
286 mark::check!(ignore_short_input_for_path);
287
288 check(
289 r#"
290//- /lib.rs crate:dep
291pub struct FirstStruct;
292pub mod some_module {
293 pub struct SecondStruct;
294 pub struct ThirdStruct;
295}
296
297//- /main.rs crate:main deps:dep
298use dep::{FirstStruct, some_module::SecondStruct};
299
300fn main() {
301 t$0
302}
303"#,
304 expect![[r#""#]],
305 );
306 }
307
308 #[test]
309 fn fuzzy_completions_come_in_specific_order() {
310 mark::check!(certain_fuzzy_order_test);
311 check(
312 r#"
313//- /lib.rs crate:dep
314pub struct FirstStruct;
315pub mod some_module {
316 // already imported, omitted
317 pub struct SecondStruct;
318 // does not contain all letters from the query, omitted
319 pub struct UnrelatedOne;
320 // contains all letters from the query, but not in sequence, displayed last
321 pub struct ThiiiiiirdStruct;
322 // contains all letters from the query, but not in the beginning, displayed second
323 pub struct AfterThirdStruct;
324 // contains all letters from the query in the begginning, displayed first
325 pub struct ThirdStruct;
326}
327
328//- /main.rs crate:main deps:dep
329use dep::{FirstStruct, some_module::SecondStruct};
330
331fn main() {
332 hir$0
333}
334"#,
335 expect![[r#"
336 st dep::some_module::ThirdStruct
337 st dep::some_module::AfterThirdStruct
338 st dep::some_module::ThiiiiiirdStruct
339 "#]],
340 );
341 }
342
343 #[test]
344 fn trait_function_fuzzy_completion() {
345 let fixture = r#"
346 //- /lib.rs crate:dep
347 pub mod test_mod {
348 pub trait TestTrait {
349 const SPECIAL_CONST: u8;
350 type HumbleType;
351 fn weird_function();
352 fn random_method(&self);
353 }
354 pub struct TestStruct {}
355 impl TestTrait for TestStruct {
356 const SPECIAL_CONST: u8 = 42;
357 type HumbleType = ();
358 fn weird_function() {}
359 fn random_method(&self) {}
360 }
361 }
362
363 //- /main.rs crate:main deps:dep
364 fn main() {
365 dep::test_mod::TestStruct::wei$0
366 }
367 "#;
368
369 check(
370 fixture,
371 expect![[r#"
372 fn weird_function() (dep::test_mod::TestTrait) -> ()
373 "#]],
374 );
375
376 check_edit(
377 "weird_function",
378 fixture,
379 r#"
380use dep::test_mod::TestTrait;
381
382fn main() {
383 dep::test_mod::TestStruct::weird_function()$0
384}
385"#,
386 );
387 }
388
389 #[test]
390 fn trait_const_fuzzy_completion() {
391 let fixture = r#"
392 //- /lib.rs crate:dep
393 pub mod test_mod {
394 pub trait TestTrait {
395 const SPECIAL_CONST: u8;
396 type HumbleType;
397 fn weird_function();
398 fn random_method(&self);
399 }
400 pub struct TestStruct {}
401 impl TestTrait for TestStruct {
402 const SPECIAL_CONST: u8 = 42;
403 type HumbleType = ();
404 fn weird_function() {}
405 fn random_method(&self) {}
406 }
407 }
408
409 //- /main.rs crate:main deps:dep
410 fn main() {
411 dep::test_mod::TestStruct::spe$0
412 }
413 "#;
414
415 check(
416 fixture,
417 expect![[r#"
418 ct SPECIAL_CONST (dep::test_mod::TestTrait)
419 "#]],
420 );
421
422 check_edit(
423 "SPECIAL_CONST",
424 fixture,
425 r#"
426use dep::test_mod::TestTrait;
427
428fn main() {
429 dep::test_mod::TestStruct::SPECIAL_CONST
430}
431"#,
432 );
433 }
434
435 #[test]
436 fn trait_method_fuzzy_completion() {
437 let fixture = r#"
438 //- /lib.rs crate:dep
439 pub mod test_mod {
440 pub trait TestTrait {
441 const SPECIAL_CONST: u8;
442 type HumbleType;
443 fn weird_function();
444 fn random_method(&self);
445 }
446 pub struct TestStruct {}
447 impl TestTrait for TestStruct {
448 const SPECIAL_CONST: u8 = 42;
449 type HumbleType = ();
450 fn weird_function() {}
451 fn random_method(&self) {}
452 }
453 }
454
455 //- /main.rs crate:main deps:dep
456 fn main() {
457 let test_struct = dep::test_mod::TestStruct {};
458 test_struct.ran$0
459 }
460 "#;
461
462 check(
463 fixture,
464 expect![[r#"
465 me random_method() (dep::test_mod::TestTrait) -> ()
466 "#]],
467 );
468
469 check_edit(
470 "random_method",
471 fixture,
472 r#"
473use dep::test_mod::TestTrait;
474
475fn main() {
476 let test_struct = dep::test_mod::TestStruct {};
477 test_struct.random_method()$0
478}
479"#,
480 );
481 }
482
483 #[test]
484 fn no_trait_type_fuzzy_completion() {
485 check(
486 r#"
487//- /lib.rs crate:dep
488pub mod test_mod {
489 pub trait TestTrait {
490 const SPECIAL_CONST: u8;
491 type HumbleType;
492 fn weird_function();
493 fn random_method(&self);
494 }
495 pub struct TestStruct {}
496 impl TestTrait for TestStruct {
497 const SPECIAL_CONST: u8 = 42;
498 type HumbleType = ();
499 fn weird_function() {}
500 fn random_method(&self) {}
501 }
502}
503
504//- /main.rs crate:main deps:dep
505fn main() {
506 dep::test_mod::TestStruct::hum$0
507}
508"#,
509 expect![[r#""#]],
510 );
511 }
512
513 #[test]
514 fn does_not_propose_names_in_scope() {
515 check(
516 r#"
517//- /lib.rs crate:dep
518pub mod test_mod {
519 pub trait TestTrait {
520 const SPECIAL_CONST: u8;
521 type HumbleType;
522 fn weird_function();
523 fn random_method(&self);
524 }
525 pub struct TestStruct {}
526 impl TestTrait for TestStruct {
527 const SPECIAL_CONST: u8 = 42;
528 type HumbleType = ();
529 fn weird_function() {}
530 fn random_method(&self) {}
531 }
532}
533
534//- /main.rs crate:main deps:dep
535use dep::test_mod::TestStruct;
536fn main() {
537 TestSt$0
538}
539"#,
540 expect![[r#""#]],
541 );
542 }
543
544 #[test]
545 fn does_not_propose_traits_in_scope() {
546 check(
547 r#"
548//- /lib.rs crate:dep
549pub mod test_mod {
550 pub trait TestTrait {
551 const SPECIAL_CONST: u8;
552 type HumbleType;
553 fn weird_function();
554 fn random_method(&self);
555 }
556 pub struct TestStruct {}
557 impl TestTrait for TestStruct {
558 const SPECIAL_CONST: u8 = 42;
559 type HumbleType = ();
560 fn weird_function() {}
561 fn random_method(&self) {}
562 }
563}
564
565//- /main.rs crate:main deps:dep
566use dep::test_mod::{TestStruct, TestTrait};
567fn main() {
568 dep::test_mod::TestStruct::hum$0
569}
570"#,
571 expect![[r#""#]],
572 );
573 }
574
575 #[test]
576 fn blanket_trait_impl_import() {
577 check_edit(
578 "another_function",
579 r#"
580//- /lib.rs crate:dep
581pub mod test_mod {
582 pub struct TestStruct {}
583 pub trait TestTrait {
584 fn another_function();
585 }
586 impl<T> TestTrait for T {
587 fn another_function() {}
588 }
589}
590
591//- /main.rs crate:main deps:dep
592fn main() {
593 dep::test_mod::TestStruct::ano$0
594}
595"#,
596 r#"
597use dep::test_mod::TestTrait;
598
599fn main() {
600 dep::test_mod::TestStruct::another_function()$0
601}
602"#,
603 );
604 }
605
606 #[test]
607 fn zero_input_deprecated_assoc_item_completion() {
608 check(
609 r#"
610//- /lib.rs crate:dep
611pub mod test_mod {
612 #[deprecated]
613 pub trait TestTrait {
614 const SPECIAL_CONST: u8;
615 type HumbleType;
616 fn weird_function();
617 fn random_method(&self);
618 }
619 pub struct TestStruct {}
620 impl TestTrait for TestStruct {
621 const SPECIAL_CONST: u8 = 42;
622 type HumbleType = ();
623 fn weird_function() {}
624 fn random_method(&self) {}
625 }
626}
627
628//- /main.rs crate:main deps:dep
629fn main() {
630 let test_struct = dep::test_mod::TestStruct {};
631 test_struct.$0
632}
633 "#,
634 expect![[r#"
635 me random_method() (dep::test_mod::TestTrait) -> () DEPRECATED
636 "#]],
637 );
638
639 check(
640 r#"
641//- /lib.rs crate:dep
642pub mod test_mod {
643 #[deprecated]
644 pub trait TestTrait {
645 const SPECIAL_CONST: u8;
646 type HumbleType;
647 fn weird_function();
648 fn random_method(&self);
649 }
650 pub struct TestStruct {}
651 impl TestTrait for TestStruct {
652 const SPECIAL_CONST: u8 = 42;
653 type HumbleType = ();
654 fn weird_function() {}
655 fn random_method(&self) {}
656 }
657}
658
659//- /main.rs crate:main deps:dep
660fn main() {
661 dep::test_mod::TestStruct::$0
662}
663"#,
664 expect![[r#"
665 ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED
666 fn weird_function() (dep::test_mod::TestTrait) -> () DEPRECATED
667 "#]],
668 );
669 }
670
671 #[test]
672 fn no_completions_in_use_statements() {
673 check(
674 r#"
675//- /lib.rs crate:dep
676pub mod io {
677 pub fn stdin() {}
678};
679
680//- /main.rs crate:main deps:dep
681use stdi$0
682
683fn main() {}
684"#,
685 expect![[]],
686 );
687 }
688}
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
new file mode 100644
index 000000000..38e33a93e
--- /dev/null
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -0,0 +1,135 @@
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, CompletionItemKind, 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(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/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
new file mode 100644
index 000000000..eb81f9765
--- /dev/null
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -0,0 +1,668 @@
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 add_keyword(ctx, acc, "for", "for $1 in $0 {}");
92 }
93
94 if ctx.if_is_prev || ctx.block_expr_parent {
95 add_keyword(ctx, acc, "let", "let ");
96 }
97
98 if ctx.after_if {
99 add_keyword(ctx, acc, "else", "else {$0}");
100 add_keyword(ctx, acc, "else if", "else if $0 {}");
101 }
102 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
103 add_keyword(ctx, acc, "mod", "mod $0");
104 }
105 if ctx.bind_pat_parent || ctx.ref_pat_parent {
106 add_keyword(ctx, acc, "mut", "mut ");
107 }
108 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
109 {
110 add_keyword(ctx, acc, "const", "const ");
111 add_keyword(ctx, acc, "type", "type ");
112 }
113 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
114 add_keyword(ctx, acc, "static", "static ");
115 };
116 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
117 add_keyword(ctx, acc, "extern", "extern ");
118 }
119 if ctx.has_item_list_or_source_file_parent
120 || has_trait_or_impl_parent
121 || ctx.block_expr_parent
122 || ctx.is_match_arm
123 {
124 add_keyword(ctx, acc, "unsafe", "unsafe ");
125 }
126 if ctx.in_loop_body {
127 if ctx.can_be_stmt {
128 add_keyword(ctx, acc, "continue", "continue;");
129 add_keyword(ctx, acc, "break", "break;");
130 } else {
131 add_keyword(ctx, acc, "continue", "continue");
132 add_keyword(ctx, acc, "break", "break");
133 }
134 }
135 if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent | ctx.has_field_list_parent {
136 add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
137 add_keyword(ctx, acc, "pub", "pub ");
138 }
139
140 if !ctx.is_trivial_path {
141 return;
142 }
143 let fn_def = match &ctx.function_syntax {
144 Some(it) => it,
145 None => return,
146 };
147
148 add_keyword(
149 ctx,
150 acc,
151 "return",
152 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
153 (true, true) => "return $0;",
154 (true, false) => "return;",
155 (false, true) => "return $0",
156 (false, false) => "return",
157 },
158 )
159}
160
161fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
162 let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
163 .kind(CompletionItemKind::Keyword);
164 let builder = match ctx.config.snippet_cap {
165 Some(cap) => {
166 let tmp;
167 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
168 mark::hit!(let_semi);
169 tmp = format!("{};", snippet);
170 &tmp
171 } else {
172 snippet
173 };
174 builder.insert_snippet(cap, snippet)
175 }
176 None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }),
177 };
178 acc.add(builder.build());
179}
180
181#[cfg(test)]
182mod tests {
183 use expect_test::{expect, Expect};
184 use test_utils::mark;
185
186 use crate::{
187 test_utils::{check_edit, completion_list},
188 CompletionKind,
189 };
190
191 fn check(ra_fixture: &str, expect: Expect) {
192 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
193 expect.assert_eq(&actual)
194 }
195
196 #[test]
197 fn test_keywords_in_use_stmt() {
198 check(
199 r"use $0",
200 expect![[r#"
201 kw crate::
202 kw self
203 kw super::
204 "#]],
205 );
206
207 check(
208 r"use a::$0",
209 expect![[r#"
210 kw self
211 kw super::
212 "#]],
213 );
214
215 check(
216 r"use a::{b, $0}",
217 expect![[r#"
218 kw self
219 kw super::
220 "#]],
221 );
222 }
223
224 #[test]
225 fn test_keywords_at_source_file_level() {
226 check(
227 r"m$0",
228 expect![[r#"
229 kw fn
230 kw use
231 kw impl
232 kw trait
233 kw enum
234 kw struct
235 kw union
236 kw mod
237 kw const
238 kw type
239 kw static
240 kw extern
241 kw unsafe
242 kw pub(crate)
243 kw pub
244 "#]],
245 );
246 }
247
248 #[test]
249 fn test_keywords_in_function() {
250 check(
251 r"fn quux() { $0 }",
252 expect![[r#"
253 kw fn
254 kw use
255 kw impl
256 kw trait
257 kw match
258 kw while
259 kw loop
260 kw if
261 kw if let
262 kw for
263 kw let
264 kw mod
265 kw const
266 kw type
267 kw static
268 kw extern
269 kw unsafe
270 kw return
271 "#]],
272 );
273 }
274
275 #[test]
276 fn test_keywords_inside_block() {
277 check(
278 r"fn quux() { if true { $0 } }",
279 expect![[r#"
280 kw fn
281 kw use
282 kw impl
283 kw trait
284 kw match
285 kw while
286 kw loop
287 kw if
288 kw if let
289 kw for
290 kw let
291 kw mod
292 kw const
293 kw type
294 kw static
295 kw extern
296 kw unsafe
297 kw return
298 "#]],
299 );
300 }
301
302 #[test]
303 fn test_keywords_after_if() {
304 check(
305 r#"fn quux() { if true { () } $0 }"#,
306 expect![[r#"
307 kw fn
308 kw use
309 kw impl
310 kw trait
311 kw match
312 kw while
313 kw loop
314 kw if
315 kw if let
316 kw for
317 kw let
318 kw else
319 kw else if
320 kw mod
321 kw const
322 kw type
323 kw static
324 kw extern
325 kw unsafe
326 kw return
327 "#]],
328 );
329 check_edit(
330 "else",
331 r#"fn quux() { if true { () } $0 }"#,
332 r#"fn quux() { if true { () } else {$0} }"#,
333 );
334 }
335
336 #[test]
337 fn test_keywords_in_match_arm() {
338 check(
339 r#"
340fn quux() -> i32 {
341 match () { () => $0 }
342}
343"#,
344 expect![[r#"
345 kw match
346 kw while
347 kw loop
348 kw if
349 kw if let
350 kw for
351 kw unsafe
352 kw return
353 "#]],
354 );
355 }
356
357 #[test]
358 fn test_keywords_in_trait_def() {
359 check(
360 r"trait My { $0 }",
361 expect![[r#"
362 kw fn
363 kw const
364 kw type
365 kw unsafe
366 "#]],
367 );
368 }
369
370 #[test]
371 fn test_keywords_in_impl_def() {
372 check(
373 r"impl My { $0 }",
374 expect![[r#"
375 kw fn
376 kw const
377 kw type
378 kw unsafe
379 kw pub(crate)
380 kw pub
381 "#]],
382 );
383 }
384
385 #[test]
386 fn test_keywords_in_loop() {
387 check(
388 r"fn my() { loop { $0 } }",
389 expect![[r#"
390 kw fn
391 kw use
392 kw impl
393 kw trait
394 kw match
395 kw while
396 kw loop
397 kw if
398 kw if let
399 kw for
400 kw let
401 kw mod
402 kw const
403 kw type
404 kw static
405 kw extern
406 kw unsafe
407 kw continue
408 kw break
409 kw return
410 "#]],
411 );
412 }
413
414 #[test]
415 fn test_keywords_after_unsafe_in_item_list() {
416 check(
417 r"unsafe $0",
418 expect![[r#"
419 kw fn
420 kw trait
421 kw impl
422 "#]],
423 );
424 }
425
426 #[test]
427 fn test_keywords_after_unsafe_in_block_expr() {
428 check(
429 r"fn my_fn() { unsafe $0 }",
430 expect![[r#"
431 kw fn
432 kw trait
433 kw impl
434 "#]],
435 );
436 }
437
438 #[test]
439 fn test_mut_in_ref_and_in_fn_parameters_list() {
440 check(
441 r"fn my_fn(&$0) {}",
442 expect![[r#"
443 kw mut
444 "#]],
445 );
446 check(
447 r"fn my_fn($0) {}",
448 expect![[r#"
449 kw mut
450 "#]],
451 );
452 check(
453 r"fn my_fn() { let &$0 }",
454 expect![[r#"
455 kw mut
456 "#]],
457 );
458 }
459
460 #[test]
461 fn test_where_keyword() {
462 check(
463 r"trait A $0",
464 expect![[r#"
465 kw where
466 "#]],
467 );
468 check(
469 r"impl A $0",
470 expect![[r#"
471 kw where
472 "#]],
473 );
474 }
475
476 #[test]
477 fn no_keyword_completion_in_comments() {
478 mark::check!(no_keyword_completion_in_comments);
479 check(
480 r#"
481fn test() {
482 let x = 2; // A comment$0
483}
484"#,
485 expect![[""]],
486 );
487 check(
488 r#"
489/*
490Some multi-line comment$0
491*/
492"#,
493 expect![[""]],
494 );
495 check(
496 r#"
497/// Some doc comment
498/// let test$0 = 1
499"#,
500 expect![[""]],
501 );
502 }
503
504 #[test]
505 fn test_completion_await_impls_future() {
506 check(
507 r#"
508//- /main.rs crate:main deps:std
509use std::future::*;
510struct A {}
511impl Future for A {}
512fn foo(a: A) { a.$0 }
513
514//- /std/lib.rs crate:std
515pub mod future {
516 #[lang = "future_trait"]
517 pub trait Future {}
518}
519"#,
520 expect![[r#"
521 kw await expr.await
522 "#]],
523 );
524
525 check(
526 r#"
527//- /main.rs crate:main deps:std
528use std::future::*;
529fn foo() {
530 let a = async {};
531 a.$0
532}
533
534//- /std/lib.rs crate:std
535pub mod future {
536 #[lang = "future_trait"]
537 pub trait Future {
538 type Output;
539 }
540}
541"#,
542 expect![[r#"
543 kw await expr.await
544 "#]],
545 )
546 }
547
548 #[test]
549 fn after_let() {
550 check(
551 r#"fn main() { let _ = $0 }"#,
552 expect![[r#"
553 kw match
554 kw while
555 kw loop
556 kw if
557 kw if let
558 kw for
559 kw return
560 "#]],
561 )
562 }
563
564 #[test]
565 fn before_field() {
566 check(
567 r#"
568struct Foo {
569 $0
570 pub f: i32,
571}
572"#,
573 expect![[r#"
574 kw pub(crate)
575 kw pub
576 "#]],
577 )
578 }
579
580 #[test]
581 fn skip_struct_initializer() {
582 mark::check!(no_keyword_completion_in_record_lit);
583 check(
584 r#"
585struct Foo {
586 pub f: i32,
587}
588fn foo() {
589 Foo {
590 $0
591 }
592}
593"#,
594 expect![[r#""#]],
595 );
596 }
597
598 #[test]
599 fn struct_initializer_field_expr() {
600 check(
601 r#"
602struct Foo {
603 pub f: i32,
604}
605fn foo() {
606 Foo {
607 f: $0
608 }
609}
610"#,
611 expect![[r#"
612 kw match
613 kw while
614 kw loop
615 kw if
616 kw if let
617 kw for
618 kw return
619 "#]],
620 );
621 }
622
623 #[test]
624 fn let_semi() {
625 mark::check!(let_semi);
626 check_edit(
627 "match",
628 r#"
629fn main() { let x = $0 }
630"#,
631 r#"
632fn main() { let x = match $0 {}; }
633"#,
634 );
635
636 check_edit(
637 "if",
638 r#"
639fn main() {
640 let x = $0
641 let y = 92;
642}
643"#,
644 r#"
645fn main() {
646 let x = if $0 {};
647 let y = 92;
648}
649"#,
650 );
651
652 check_edit(
653 "loop",
654 r#"
655fn main() {
656 let x = $0
657 bar();
658}
659"#,
660 r#"
661fn main() {
662 let x = loop {$0};
663 bar();
664}
665"#,
666 );
667 }
668}
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs
new file mode 100644
index 000000000..2be299ac2
--- /dev/null
+++ b/crates/ide_completion/src/completions/macro_in_item_position.rs
@@ -0,0 +1,41 @@
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/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs
new file mode 100644
index 000000000..352fc7c77
--- /dev/null
+++ b/crates/ide_completion/src/completions/mod_.rs
@@ -0,0 +1,315 @@
1//! Completes mod declarations.
2
3use std::iter;
4
5use hir::{Module, ModuleSource};
6use ide_db::{
7 base_db::{SourceDatabaseExt, VfsPath},
8 RootDatabase, SymbolKind,
9};
10use rustc_hash::FxHashSet;
11
12use crate::CompletionItem;
13
14use crate::{context::CompletionContext, item::CompletionKind, Completions};
15
16/// Complete mod declaration, i.e. `mod $0 ;`
17pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
18 let mod_under_caret = match &ctx.mod_declaration_under_caret {
19 Some(mod_under_caret) if mod_under_caret.item_list().is_none() => mod_under_caret,
20 _ => return None,
21 };
22
23 let _p = profile::span("completion::complete_mod");
24
25 let current_module = ctx.scope.module()?;
26
27 let module_definition_file =
28 current_module.definition_source(ctx.db).file_id.original_file(ctx.db);
29 let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file));
30 let directory_to_look_for_submodules = directory_to_look_for_submodules(
31 current_module,
32 ctx.db,
33 source_root.path_for_file(&module_definition_file)?,
34 )?;
35
36 let existing_mod_declarations = current_module
37 .children(ctx.db)
38 .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
39 .collect::<FxHashSet<_>>();
40
41 let module_declaration_file =
42 current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
43 module_declaration_source_file.file_id.original_file(ctx.db)
44 });
45
46 source_root
47 .iter()
48 .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file)
49 .filter(|submodule_candidate_file| {
50 Some(submodule_candidate_file) != module_declaration_file.as_ref()
51 })
52 .filter_map(|submodule_file| {
53 let submodule_path = source_root.path_for_file(&submodule_file)?;
54 let directory_with_submodule = submodule_path.parent()?;
55 let (name, ext) = submodule_path.name_and_extension()?;
56 if ext != Some("rs") {
57 return None;
58 }
59 match name {
60 "lib" | "main" => None,
61 "mod" => {
62 if directory_with_submodule.parent()? == directory_to_look_for_submodules {
63 match directory_with_submodule.name_and_extension()? {
64 (directory_name, None) => Some(directory_name.to_owned()),
65 _ => None,
66 }
67 } else {
68 None
69 }
70 }
71 file_name if directory_with_submodule == directory_to_look_for_submodules => {
72 Some(file_name.to_owned())
73 }
74 _ => None,
75 }
76 })
77 .filter(|name| !existing_mod_declarations.contains(name))
78 .for_each(|submodule_name| {
79 let mut label = submodule_name;
80 if mod_under_caret.semicolon_token().is_none() {
81 label.push(';');
82 }
83 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label)
84 .kind(SymbolKind::Module)
85 .add_to(acc)
86 });
87
88 Some(())
89}
90
91fn directory_to_look_for_submodules(
92 module: Module,
93 db: &RootDatabase,
94 module_file_path: &VfsPath,
95) -> Option<VfsPath> {
96 let directory_with_module_path = module_file_path.parent()?;
97 let (name, ext) = module_file_path.name_and_extension()?;
98 if ext != Some("rs") {
99 return None;
100 }
101 let base_directory = match name {
102 "mod" | "lib" | "main" => Some(directory_with_module_path),
103 regular_rust_file_name => {
104 if matches!(
105 (
106 directory_with_module_path
107 .parent()
108 .as_ref()
109 .and_then(|path| path.name_and_extension()),
110 directory_with_module_path.name_and_extension(),
111 ),
112 (Some(("src", None)), Some(("bin", None)))
113 ) {
114 // files in /src/bin/ can import each other directly
115 Some(directory_with_module_path)
116 } else {
117 directory_with_module_path.join(regular_rust_file_name)
118 }
119 }
120 }?;
121
122 module_chain_to_containing_module_file(module, db)
123 .into_iter()
124 .filter_map(|module| module.name(db))
125 .try_fold(base_directory, |path, name| path.join(&name.to_string()))
126}
127
128fn module_chain_to_containing_module_file(
129 current_module: Module,
130 db: &RootDatabase,
131) -> Vec<Module> {
132 let mut path =
133 iter::successors(Some(current_module), |current_module| current_module.parent(db))
134 .take_while(|current_module| {
135 matches!(current_module.definition_source(db).value, ModuleSource::Module(_))
136 })
137 .collect::<Vec<_>>();
138 path.reverse();
139 path
140}
141
142#[cfg(test)]
143mod tests {
144 use crate::{test_utils::completion_list, CompletionKind};
145 use expect_test::{expect, Expect};
146
147 fn check(ra_fixture: &str, expect: Expect) {
148 let actual = completion_list(ra_fixture, CompletionKind::Magic);
149 expect.assert_eq(&actual);
150 }
151
152 #[test]
153 fn lib_module_completion() {
154 check(
155 r#"
156 //- /lib.rs
157 mod $0
158 //- /foo.rs
159 fn foo() {}
160 //- /foo/ignored_foo.rs
161 fn ignored_foo() {}
162 //- /bar/mod.rs
163 fn bar() {}
164 //- /bar/ignored_bar.rs
165 fn ignored_bar() {}
166 "#,
167 expect![[r#"
168 md foo;
169 md bar;
170 "#]],
171 );
172 }
173
174 #[test]
175 fn no_module_completion_with_module_body() {
176 check(
177 r#"
178 //- /lib.rs
179 mod $0 {
180
181 }
182 //- /foo.rs
183 fn foo() {}
184 "#,
185 expect![[r#""#]],
186 );
187 }
188
189 #[test]
190 fn main_module_completion() {
191 check(
192 r#"
193 //- /main.rs
194 mod $0
195 //- /foo.rs
196 fn foo() {}
197 //- /foo/ignored_foo.rs
198 fn ignored_foo() {}
199 //- /bar/mod.rs
200 fn bar() {}
201 //- /bar/ignored_bar.rs
202 fn ignored_bar() {}
203 "#,
204 expect![[r#"
205 md foo;
206 md bar;
207 "#]],
208 );
209 }
210
211 #[test]
212 fn main_test_module_completion() {
213 check(
214 r#"
215 //- /main.rs
216 mod tests {
217 mod $0;
218 }
219 //- /tests/foo.rs
220 fn foo() {}
221 "#,
222 expect![[r#"
223 md foo
224 "#]],
225 );
226 }
227
228 #[test]
229 fn directly_nested_module_completion() {
230 check(
231 r#"
232 //- /lib.rs
233 mod foo;
234 //- /foo.rs
235 mod $0;
236 //- /foo/bar.rs
237 fn bar() {}
238 //- /foo/bar/ignored_bar.rs
239 fn ignored_bar() {}
240 //- /foo/baz/mod.rs
241 fn baz() {}
242 //- /foo/moar/ignored_moar.rs
243 fn ignored_moar() {}
244 "#,
245 expect![[r#"
246 md bar
247 md baz
248 "#]],
249 );
250 }
251
252 #[test]
253 fn nested_in_source_module_completion() {
254 check(
255 r#"
256 //- /lib.rs
257 mod foo;
258 //- /foo.rs
259 mod bar {
260 mod $0
261 }
262 //- /foo/bar/baz.rs
263 fn baz() {}
264 "#,
265 expect![[r#"
266 md baz;
267 "#]],
268 );
269 }
270
271 // FIXME binary modules are not supported in tests properly
272 // Binary modules are a bit special, they allow importing the modules from `/src/bin`
273 // and that's why are good to test two things:
274 // * no cycles are allowed in mod declarations
275 // * no modules from the parent directory are proposed
276 // Unfortunately, binary modules support is in cargo not rustc,
277 // hence the test does not work now
278 //
279 // #[test]
280 // fn regular_bin_module_completion() {
281 // check(
282 // r#"
283 // //- /src/bin.rs
284 // fn main() {}
285 // //- /src/bin/foo.rs
286 // mod $0
287 // //- /src/bin/bar.rs
288 // fn bar() {}
289 // //- /src/bin/bar/bar_ignored.rs
290 // fn bar_ignored() {}
291 // "#,
292 // expect![[r#"
293 // md bar;
294 // "#]],foo
295 // );
296 // }
297
298 #[test]
299 fn already_declared_bin_module_completion_omitted() {
300 check(
301 r#"
302 //- /src/bin.rs crate:main
303 fn main() {}
304 //- /src/bin/foo.rs
305 mod $0
306 //- /src/bin/bar.rs
307 mod foo;
308 fn bar() {}
309 //- /src/bin/bar/bar_ignored.rs
310 fn bar_ignored() {}
311 "#,
312 expect![[r#""#]],
313 );
314 }
315}
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
new file mode 100644
index 000000000..9282c3827
--- /dev/null
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -0,0 +1,317 @@
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 if let Some(ty) = &ctx.expected_type {
15 super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| {
16 acc.add_qualified_variant_pat(ctx, variant, path)
17 });
18 }
19
20 // FIXME: ideally, we should look at the type we are matching against and
21 // suggest variants + auto-imports
22 ctx.scope.process_all_names(&mut |name, res| {
23 let add_resolution = match &res {
24 hir::ScopeDef::ModuleDef(def) => match def {
25 hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
26 acc.add_struct_pat(ctx, strukt.clone(), Some(name.clone()));
27 true
28 }
29 hir::ModuleDef::Variant(variant) if !ctx.is_irrefutable_pat_binding => {
30 acc.add_variant_pat(ctx, variant.clone(), Some(name.clone()));
31 true
32 }
33 hir::ModuleDef::Adt(hir::Adt::Enum(..))
34 | hir::ModuleDef::Variant(..)
35 | hir::ModuleDef::Const(..)
36 | hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding,
37 _ => false,
38 },
39 hir::ScopeDef::MacroDef(_) => true,
40 hir::ScopeDef::ImplSelfType(impl_) => match impl_.target_ty(ctx.db).as_adt() {
41 Some(hir::Adt::Struct(strukt)) => {
42 acc.add_struct_pat(ctx, strukt, Some(name.clone()));
43 true
44 }
45 Some(hir::Adt::Enum(_)) => !ctx.is_irrefutable_pat_binding,
46 _ => true,
47 },
48 _ => false,
49 };
50 if add_resolution {
51 acc.add_resolution(ctx, name.to_string(), &res);
52 }
53 });
54}
55
56#[cfg(test)]
57mod tests {
58 use expect_test::{expect, Expect};
59
60 use crate::{
61 test_utils::{check_edit, completion_list},
62 CompletionKind,
63 };
64
65 fn check(ra_fixture: &str, expect: Expect) {
66 let actual = completion_list(ra_fixture, CompletionKind::Reference);
67 expect.assert_eq(&actual)
68 }
69
70 fn check_snippet(ra_fixture: &str, expect: Expect) {
71 let actual = completion_list(ra_fixture, CompletionKind::Snippet);
72 expect.assert_eq(&actual)
73 }
74
75 #[test]
76 fn completes_enum_variants_and_modules() {
77 check(
78 r#"
79enum E { X }
80use self::E::X;
81const Z: E = E::X;
82mod m {}
83
84static FOO: E = E::X;
85struct Bar { f: u32 }
86
87fn foo() {
88 match E::X { $0 }
89}
90"#,
91 expect![[r#"
92 en E
93 ct Z
94 st Bar
95 ev X
96 md m
97 "#]],
98 );
99 }
100
101 #[test]
102 fn completes_in_simple_macro_call() {
103 check(
104 r#"
105macro_rules! m { ($e:expr) => { $e } }
106enum E { X }
107
108fn foo() {
109 m!(match E::X { $0 })
110}
111"#,
112 expect![[r#"
113 en E
114 ma m!(…) macro_rules! m
115 "#]],
116 );
117 }
118
119 #[test]
120 fn completes_in_irrefutable_let() {
121 check(
122 r#"
123enum E { X }
124use self::E::X;
125const Z: E = E::X;
126mod m {}
127
128static FOO: E = E::X;
129struct Bar { f: u32 }
130
131fn foo() {
132 let $0
133}
134"#,
135 expect![[r#"
136 st Bar
137 "#]],
138 );
139 }
140
141 #[test]
142 fn completes_in_param() {
143 check(
144 r#"
145enum E { X }
146
147static FOO: E = E::X;
148struct Bar { f: u32 }
149
150fn foo($0) {
151}
152"#,
153 expect![[r#"
154 st Bar
155 "#]],
156 );
157 }
158
159 #[test]
160 fn completes_pat_in_let() {
161 check_snippet(
162 r#"
163struct Bar { f: u32 }
164
165fn foo() {
166 let $0
167}
168"#,
169 expect![[r#"
170 bn Bar Bar { f$1 }$0
171 "#]],
172 );
173 }
174
175 #[test]
176 fn completes_param_pattern() {
177 check_snippet(
178 r#"
179struct Foo { bar: String, baz: String }
180struct Bar(String, String);
181struct Baz;
182fn outer($0) {}
183"#,
184 expect![[r#"
185 bn Foo Foo { bar$1, baz$2 }: Foo$0
186 bn Bar Bar($1, $2): Bar$0
187 "#]],
188 )
189 }
190
191 #[test]
192 fn completes_let_pattern() {
193 check_snippet(
194 r#"
195struct Foo { bar: String, baz: String }
196struct Bar(String, String);
197struct Baz;
198fn outer() {
199 let $0
200}
201"#,
202 expect![[r#"
203 bn Foo Foo { bar$1, baz$2 }$0
204 bn Bar Bar($1, $2)$0
205 "#]],
206 )
207 }
208
209 #[test]
210 fn completes_refutable_pattern() {
211 check_snippet(
212 r#"
213struct Foo { bar: i32, baz: i32 }
214struct Bar(String, String);
215struct Baz;
216fn outer() {
217 match () {
218 $0
219 }
220}
221"#,
222 expect![[r#"
223 bn Foo Foo { bar$1, baz$2 }$0
224 bn Bar Bar($1, $2)$0
225 "#]],
226 )
227 }
228
229 #[test]
230 fn omits_private_fields_pat() {
231 check_snippet(
232 r#"
233mod foo {
234 pub struct Foo { pub bar: i32, baz: i32 }
235 pub struct Bar(pub String, String);
236 pub struct Invisible(String, String);
237}
238use foo::*;
239
240fn outer() {
241 match () {
242 $0
243 }
244}
245"#,
246 expect![[r#"
247 bn Foo Foo { bar$1, .. }$0
248 bn Bar Bar($1, ..)$0
249 "#]],
250 )
251 }
252
253 #[test]
254 fn only_shows_ident_completion() {
255 check_edit(
256 "Foo",
257 r#"
258struct Foo(i32);
259fn main() {
260 match Foo(92) {
261 $0(92) => (),
262 }
263}
264"#,
265 r#"
266struct Foo(i32);
267fn main() {
268 match Foo(92) {
269 Foo(92) => (),
270 }
271}
272"#,
273 );
274 }
275
276 #[test]
277 fn completes_self_pats() {
278 check_snippet(
279 r#"
280struct Foo(i32);
281impl Foo {
282 fn foo() {
283 match () {
284 $0
285 }
286 }
287}
288 "#,
289 expect![[r#"
290 bn Self Self($1)$0
291 bn Foo Foo($1)$0
292 "#]],
293 )
294 }
295
296 #[test]
297 fn completes_qualified_variant() {
298 check_snippet(
299 r#"
300enum Foo {
301 Bar { baz: i32 }
302}
303impl Foo {
304 fn foo() {
305 match {Foo::Bar { baz: 0 }} {
306 B$0
307 }
308 }
309}
310 "#,
311 expect![[r#"
312 bn Self::Bar Self::Bar { baz$1 }$0
313 bn Foo::Bar Foo::Bar { baz$1 }$0
314 "#]],
315 )
316 }
317}
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
new file mode 100644
index 000000000..9c34ed0b6
--- /dev/null
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -0,0 +1,565 @@
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/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs
new file mode 100644
index 000000000..3afc63021
--- /dev/null
+++ b/crates/ide_completion/src/completions/postfix/format_like.rs
@@ -0,0 +1,287 @@
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/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
new file mode 100644
index 000000000..2afa6979e
--- /dev/null
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -0,0 +1,815 @@
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 | PathResolution::Def(def @ hir::ModuleDef::BuiltinType(_)) => {
55 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
56 for variant in e.variants(ctx.db) {
57 acc.add_enum_variant(ctx, variant, None);
58 }
59 }
60 let ty = match def {
61 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
62 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
63 hir::ModuleDef::BuiltinType(builtin) => {
64 let module = match ctx.scope.module() {
65 Some(it) => it,
66 None => return,
67 };
68 builtin.ty(ctx.db, module)
69 }
70 _ => unreachable!(),
71 };
72
73 // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
74 // (where AssocType is defined on a trait, not an inherent impl)
75
76 let krate = ctx.krate;
77 if let Some(krate) = krate {
78 let traits_in_scope = ctx.scope.traits_in_scope();
79 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
80 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
81 return None;
82 }
83 match item {
84 hir::AssocItem::Function(func) => {
85 acc.add_function(ctx, func, None);
86 }
87 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
88 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
89 }
90 None::<()>
91 });
92
93 // Iterate assoc types separately
94 ty.iterate_assoc_items(ctx.db, krate, |item| {
95 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
96 return None;
97 }
98 match item {
99 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {}
100 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
101 }
102 None::<()>
103 });
104 }
105 }
106 PathResolution::Def(hir::ModuleDef::Trait(t)) => {
107 // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
108 for item in t.items(ctx.db) {
109 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
110 continue;
111 }
112 match item {
113 hir::AssocItem::Function(func) => {
114 acc.add_function(ctx, func, None);
115 }
116 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
117 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
118 }
119 }
120 }
121 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
122 if let Some(krate) = ctx.krate {
123 let ty = match resolution {
124 PathResolution::TypeParam(param) => param.ty(ctx.db),
125 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
126 _ => return,
127 };
128
129 if let Some(Adt::Enum(e)) = ty.as_adt() {
130 for variant in e.variants(ctx.db) {
131 acc.add_enum_variant(ctx, variant, None);
132 }
133 }
134
135 let traits_in_scope = ctx.scope.traits_in_scope();
136 let mut seen = FxHashSet::default();
137 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
138 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
139 return None;
140 }
141
142 // We might iterate candidates of a trait multiple times here, so deduplicate
143 // them.
144 if seen.insert(item) {
145 match item {
146 hir::AssocItem::Function(func) => {
147 acc.add_function(ctx, func, None);
148 }
149 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
150 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
151 }
152 }
153 None::<()>
154 });
155 }
156 }
157 _ => {}
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use expect_test::{expect, Expect};
164 use test_utils::mark;
165
166 use crate::{
167 test_utils::{check_edit, completion_list},
168 CompletionKind,
169 };
170
171 fn check(ra_fixture: &str, expect: Expect) {
172 let actual = completion_list(ra_fixture, CompletionKind::Reference);
173 expect.assert_eq(&actual);
174 }
175
176 fn check_builtin(ra_fixture: &str, expect: Expect) {
177 let actual = completion_list(ra_fixture, CompletionKind::BuiltinType);
178 expect.assert_eq(&actual);
179 }
180
181 #[test]
182 fn dont_complete_current_use() {
183 mark::check!(dont_complete_current_use);
184 check(r#"use self::foo$0;"#, expect![[""]]);
185 }
186
187 #[test]
188 fn dont_complete_current_use_in_braces_with_glob() {
189 check(
190 r#"
191mod foo { pub struct S; }
192use self::{foo::*, bar$0};
193"#,
194 expect![[r#"
195 st S
196 md foo
197 "#]],
198 );
199 }
200
201 #[test]
202 fn dont_complete_primitive_in_use() {
203 check_builtin(r#"use self::$0;"#, expect![[""]]);
204 }
205
206 #[test]
207 fn dont_complete_primitive_in_module_scope() {
208 check_builtin(r#"fn foo() { self::$0 }"#, expect![[""]]);
209 }
210
211 #[test]
212 fn completes_primitives() {
213 check_builtin(
214 r#"fn main() { let _: $0 = 92; }"#,
215 expect![[r#"
216 bt u32
217 bt bool
218 bt u8
219 bt isize
220 bt u16
221 bt u64
222 bt u128
223 bt f32
224 bt i128
225 bt i16
226 bt str
227 bt i64
228 bt char
229 bt f64
230 bt i32
231 bt i8
232 bt usize
233 "#]],
234 );
235 }
236
237 #[test]
238 fn completes_mod_with_same_name_as_function() {
239 check(
240 r#"
241use self::my::$0;
242
243mod my { pub struct Bar; }
244fn my() {}
245"#,
246 expect![[r#"
247 st Bar
248 "#]],
249 );
250 }
251
252 #[test]
253 fn filters_visibility() {
254 check(
255 r#"
256use self::my::$0;
257
258mod my {
259 struct Bar;
260 pub struct Foo;
261 pub use Bar as PublicBar;
262}
263"#,
264 expect![[r#"
265 st Foo
266 st PublicBar
267 "#]],
268 );
269 }
270
271 #[test]
272 fn completes_use_item_starting_with_self() {
273 check(
274 r#"
275use self::m::$0;
276
277mod m { pub struct Bar; }
278"#,
279 expect![[r#"
280 st Bar
281 "#]],
282 );
283 }
284
285 #[test]
286 fn completes_use_item_starting_with_crate() {
287 check(
288 r#"
289//- /lib.rs
290mod foo;
291struct Spam;
292//- /foo.rs
293use crate::Sp$0
294"#,
295 expect![[r#"
296 md foo
297 st Spam
298 "#]],
299 );
300 }
301
302 #[test]
303 fn completes_nested_use_tree() {
304 check(
305 r#"
306//- /lib.rs
307mod foo;
308struct Spam;
309//- /foo.rs
310use crate::{Sp$0};
311"#,
312 expect![[r#"
313 md foo
314 st Spam
315 "#]],
316 );
317 }
318
319 #[test]
320 fn completes_deeply_nested_use_tree() {
321 check(
322 r#"
323//- /lib.rs
324mod foo;
325pub mod bar {
326 pub mod baz {
327 pub struct Spam;
328 }
329}
330//- /foo.rs
331use crate::{bar::{baz::Sp$0}};
332"#,
333 expect![[r#"
334 st Spam
335 "#]],
336 );
337 }
338
339 #[test]
340 fn completes_enum_variant() {
341 check(
342 r#"
343enum E { Foo, Bar(i32) }
344fn foo() { let _ = E::$0 }
345"#,
346 expect![[r#"
347 ev Foo ()
348 ev Bar(…) (i32)
349 "#]],
350 );
351 }
352
353 #[test]
354 fn completes_struct_associated_items() {
355 check(
356 r#"
357//- /lib.rs
358struct S;
359
360impl S {
361 fn a() {}
362 fn b(&self) {}
363 const C: i32 = 42;
364 type T = i32;
365}
366
367fn foo() { let _ = S::$0 }
368"#,
369 expect![[r#"
370 fn a() -> ()
371 me b(…) -> ()
372 ct C const C: i32 = 42;
373 ta T type T = i32;
374 "#]],
375 );
376 }
377
378 #[test]
379 fn associated_item_visibility() {
380 check(
381 r#"
382struct S;
383
384mod m {
385 impl super::S {
386 pub(crate) fn public_method() { }
387 fn private_method() { }
388 pub(crate) type PublicType = u32;
389 type PrivateType = u32;
390 pub(crate) const PUBLIC_CONST: u32 = 1;
391 const PRIVATE_CONST: u32 = 1;
392 }
393}
394
395fn foo() { let _ = S::$0 }
396"#,
397 expect![[r#"
398 fn public_method() -> ()
399 ct PUBLIC_CONST pub(crate) const PUBLIC_CONST: u32 = 1;
400 ta PublicType pub(crate) type PublicType = u32;
401 "#]],
402 );
403 }
404
405 #[test]
406 fn completes_enum_associated_method() {
407 check(
408 r#"
409enum E {};
410impl E { fn m() { } }
411
412fn foo() { let _ = E::$0 }
413 "#,
414 expect![[r#"
415 fn m() -> ()
416 "#]],
417 );
418 }
419
420 #[test]
421 fn completes_union_associated_method() {
422 check(
423 r#"
424union U {};
425impl U { fn m() { } }
426
427fn foo() { let _ = U::$0 }
428"#,
429 expect![[r#"
430 fn m() -> ()
431 "#]],
432 );
433 }
434
435 #[test]
436 fn completes_use_paths_across_crates() {
437 check(
438 r#"
439//- /main.rs crate:main deps:foo
440use foo::$0;
441
442//- /foo/lib.rs crate:foo
443pub mod bar { pub struct S; }
444"#,
445 expect![[r#"
446 md bar
447 "#]],
448 );
449 }
450
451 #[test]
452 fn completes_trait_associated_method_1() {
453 check(
454 r#"
455trait Trait { fn m(); }
456
457fn foo() { let _ = Trait::$0 }
458"#,
459 expect![[r#"
460 fn m() -> ()
461 "#]],
462 );
463 }
464
465 #[test]
466 fn completes_trait_associated_method_2() {
467 check(
468 r#"
469trait Trait { fn m(); }
470
471struct S;
472impl Trait for S {}
473
474fn foo() { let _ = S::$0 }
475"#,
476 expect![[r#"
477 fn m() -> ()
478 "#]],
479 );
480 }
481
482 #[test]
483 fn completes_trait_associated_method_3() {
484 check(
485 r#"
486trait Trait { fn m(); }
487
488struct S;
489impl Trait for S {}
490
491fn foo() { let _ = <S as Trait>::$0 }
492"#,
493 expect![[r#"
494 fn m() -> ()
495 "#]],
496 );
497 }
498
499 #[test]
500 fn completes_ty_param_assoc_ty() {
501 check(
502 r#"
503trait Super {
504 type Ty;
505 const CONST: u8;
506 fn func() {}
507 fn method(&self) {}
508}
509
510trait Sub: Super {
511 type SubTy;
512 const C2: ();
513 fn subfunc() {}
514 fn submethod(&self) {}
515}
516
517fn foo<T: Sub>() { T::$0 }
518"#,
519 expect![[r#"
520 ta SubTy type SubTy;
521 ta Ty type Ty;
522 ct C2 const C2: ();
523 fn subfunc() -> ()
524 me submethod(…) -> ()
525 ct CONST const CONST: u8;
526 fn func() -> ()
527 me method(…) -> ()
528 "#]],
529 );
530 }
531
532 #[test]
533 fn completes_self_param_assoc_ty() {
534 check(
535 r#"
536trait Super {
537 type Ty;
538 const CONST: u8 = 0;
539 fn func() {}
540 fn method(&self) {}
541}
542
543trait Sub: Super {
544 type SubTy;
545 const C2: () = ();
546 fn subfunc() {}
547 fn submethod(&self) {}
548}
549
550struct Wrap<T>(T);
551impl<T> Super for Wrap<T> {}
552impl<T> Sub for Wrap<T> {
553 fn subfunc() {
554 // Should be able to assume `Self: Sub + Super`
555 Self::$0
556 }
557}
558"#,
559 expect![[r#"
560 ta SubTy type SubTy;
561 ta Ty type Ty;
562 ct CONST const CONST: u8 = 0;
563 fn func() -> ()
564 me method(…) -> ()
565 ct C2 const C2: () = ();
566 fn subfunc() -> ()
567 me submethod(…) -> ()
568 "#]],
569 );
570 }
571
572 #[test]
573 fn completes_type_alias() {
574 check(
575 r#"
576struct S;
577impl S { fn foo() {} }
578type T = S;
579impl T { fn bar() {} }
580
581fn main() { T::$0; }
582"#,
583 expect![[r#"
584 fn foo() -> ()
585 fn bar() -> ()
586 "#]],
587 );
588 }
589
590 #[test]
591 fn completes_qualified_macros() {
592 check(
593 r#"
594#[macro_export]
595macro_rules! foo { () => {} }
596
597fn main() { let _ = crate::$0 }
598 "#,
599 expect![[r##"
600 fn main() -> ()
601 ma foo!(…) #[macro_export] macro_rules! foo
602 "##]],
603 );
604 }
605
606 #[test]
607 fn test_super_super_completion() {
608 check(
609 r#"
610mod a {
611 const A: usize = 0;
612 mod b {
613 const B: usize = 0;
614 mod c { use super::super::$0 }
615 }
616}
617"#,
618 expect![[r#"
619 md b
620 ct A
621 "#]],
622 );
623 }
624
625 #[test]
626 fn completes_reexported_items_under_correct_name() {
627 check(
628 r#"
629fn foo() { self::m::$0 }
630
631mod m {
632 pub use super::p::wrong_fn as right_fn;
633 pub use super::p::WRONG_CONST as RIGHT_CONST;
634 pub use super::p::WrongType as RightType;
635}
636mod p {
637 fn wrong_fn() {}
638 const WRONG_CONST: u32 = 1;
639 struct WrongType {};
640}
641"#,
642 expect![[r#"
643 ct RIGHT_CONST
644 fn right_fn() -> ()
645 st RightType
646 "#]],
647 );
648
649 check_edit(
650 "RightType",
651 r#"
652fn foo() { self::m::$0 }
653
654mod m {
655 pub use super::p::wrong_fn as right_fn;
656 pub use super::p::WRONG_CONST as RIGHT_CONST;
657 pub use super::p::WrongType as RightType;
658}
659mod p {
660 fn wrong_fn() {}
661 const WRONG_CONST: u32 = 1;
662 struct WrongType {};
663}
664"#,
665 r#"
666fn foo() { self::m::RightType }
667
668mod m {
669 pub use super::p::wrong_fn as right_fn;
670 pub use super::p::WRONG_CONST as RIGHT_CONST;
671 pub use super::p::WrongType as RightType;
672}
673mod p {
674 fn wrong_fn() {}
675 const WRONG_CONST: u32 = 1;
676 struct WrongType {};
677}
678"#,
679 );
680 }
681
682 #[test]
683 fn completes_in_simple_macro_call() {
684 check(
685 r#"
686macro_rules! m { ($e:expr) => { $e } }
687fn main() { m!(self::f$0); }
688fn foo() {}
689"#,
690 expect![[r#"
691 fn main() -> ()
692 fn foo() -> ()
693 "#]],
694 );
695 }
696
697 #[test]
698 fn function_mod_share_name() {
699 check(
700 r#"
701fn foo() { self::m::$0 }
702
703mod m {
704 pub mod z {}
705 pub fn z() {}
706}
707"#,
708 expect![[r#"
709 md z
710 fn z() -> ()
711 "#]],
712 );
713 }
714
715 #[test]
716 fn completes_hashmap_new() {
717 check(
718 r#"
719struct RandomState;
720struct HashMap<K, V, S = RandomState> {}
721
722impl<K, V> HashMap<K, V, RandomState> {
723 pub fn new() -> HashMap<K, V, RandomState> { }
724}
725fn foo() {
726 HashMap::$0
727}
728"#,
729 expect![[r#"
730 fn new() -> HashMap<K, V, RandomState>
731 "#]],
732 );
733 }
734
735 #[test]
736 fn dont_complete_attr() {
737 check(
738 r#"
739mod foo { pub struct Foo; }
740#[foo::$0]
741fn f() {}
742"#,
743 expect![[""]],
744 );
745 }
746
747 #[test]
748 fn completes_function() {
749 check(
750 r#"
751fn foo(
752 a: i32,
753 b: i32
754) {
755
756}
757
758fn main() {
759 fo$0
760}
761"#,
762 expect![[r#"
763 fn main() -> ()
764 fn foo(…) -> ()
765 "#]],
766 );
767 }
768
769 #[test]
770 fn completes_self_enum() {
771 check(
772 r#"
773enum Foo {
774 Bar,
775 Baz,
776}
777
778impl Foo {
779 fn foo(self) {
780 Self::$0
781 }
782}
783"#,
784 expect![[r#"
785 ev Bar ()
786 ev Baz ()
787 me foo(…) -> ()
788 "#]],
789 );
790 }
791
792 #[test]
793 fn completes_primitive_assoc_const() {
794 check(
795 r#"
796//- /lib.rs crate:lib deps:core
797fn f() {
798 u8::$0
799}
800
801//- /core.rs crate:core
802#[lang = "u8"]
803impl u8 {
804 pub const MAX: Self = 255;
805
806 pub fn func(self) {}
807}
808"#,
809 expect![[r#"
810 ct MAX pub const MAX: Self = 255;
811 me func(…) -> ()
812 "#]],
813 );
814 }
815}
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
new file mode 100644
index 000000000..0a7927eb8
--- /dev/null
+++ b/crates/ide_completion/src/completions/record.rs
@@ -0,0 +1,390 @@
1//! Complete fields in record literals and patterns.
2use ide_db::{helpers::FamousDefs, SymbolKind};
3use syntax::ast::Expr;
4
5use crate::{item::CompletionKind, CompletionContext, CompletionItem, Completions};
6
7pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
8 let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
9 (None, None) => return None,
10 (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
11 (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
12 (_, Some(record_lit)) => {
13 let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone()));
14 let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default();
15 let impl_default_trait = default_trait
16 .and_then(|default_trait| ty.map(|ty| ty.impls_trait(ctx.db, default_trait, &[])))
17 .unwrap_or(false);
18
19 let missing_fields = ctx.sema.record_literal_missing_fields(record_lit);
20 if impl_default_trait && !missing_fields.is_empty() {
21 let completion_text = "..Default::default()";
22 let completion_text = completion_text
23 .strip_prefix(ctx.token.to_string().as_str())
24 .unwrap_or(completion_text);
25 acc.add(
26 CompletionItem::new(
27 CompletionKind::Snippet,
28 ctx.source_range(),
29 "..Default::default()",
30 )
31 .insert_text(completion_text)
32 .kind(SymbolKind::Field)
33 .build(),
34 );
35 }
36
37 missing_fields
38 }
39 };
40
41 for (field, ty) in missing_fields {
42 acc.add_field(ctx, field, &ty);
43 }
44
45 Some(())
46}
47
48#[cfg(test)]
49mod tests {
50 use expect_test::{expect, Expect};
51 use ide_db::helpers::FamousDefs;
52
53 use crate::{
54 test_utils::{self, completion_list},
55 CompletionKind,
56 };
57
58 fn check(ra_fixture: &str, expect: Expect) {
59 let actual = completion_list(ra_fixture, CompletionKind::Reference);
60 expect.assert_eq(&actual);
61 }
62
63 fn check_snippet(ra_fixture: &str, expect: Expect) {
64 let actual = completion_list(
65 &format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE),
66 CompletionKind::Snippet,
67 );
68 expect.assert_eq(&actual);
69 }
70
71 fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
72 test_utils::check_edit(
73 what,
74 &format!(
75 "//- /main.rs crate:main deps:core{}\n{}",
76 ra_fixture_before,
77 FamousDefs::FIXTURE,
78 ),
79 &(ra_fixture_after.to_owned() + "\n"),
80 );
81 }
82
83 #[test]
84 fn test_record_literal_field_default() {
85 let test_code = r#"
86struct S { foo: u32, bar: usize }
87
88impl core::default::Default for S {
89 fn default() -> Self {
90 S {
91 foo: 0,
92 bar: 0,
93 }
94 }
95}
96
97fn process(f: S) {
98 let other = S {
99 foo: 5,
100 .$0
101 };
102}
103"#;
104 check(
105 test_code,
106 expect![[r#"
107 fd bar usize
108 "#]],
109 );
110
111 check_snippet(
112 test_code,
113 expect![[r#"
114 sn pd
115 sn ppd
116 fd ..Default::default()
117 "#]],
118 );
119 }
120
121 #[test]
122 fn test_record_literal_field_default_completion() {
123 check_edit(
124 "..Default::default()",
125 r#"
126struct S { foo: u32, bar: usize }
127
128impl core::default::Default for S {
129 fn default() -> Self {
130 S {
131 foo: 0,
132 bar: 0,
133 }
134 }
135}
136
137fn process(f: S) {
138 let other = S {
139 foo: 5,
140 .$0
141 };
142}
143"#,
144 r#"
145struct S { foo: u32, bar: usize }
146
147impl core::default::Default for S {
148 fn default() -> Self {
149 S {
150 foo: 0,
151 bar: 0,
152 }
153 }
154}
155
156fn process(f: S) {
157 let other = S {
158 foo: 5,
159 ..Default::default()
160 };
161}
162"#,
163 );
164 }
165
166 #[test]
167 fn test_record_literal_field_without_default() {
168 let test_code = r#"
169struct S { foo: u32, bar: usize }
170
171fn process(f: S) {
172 let other = S {
173 foo: 5,
174 .$0
175 };
176}
177"#;
178 check(
179 test_code,
180 expect![[r#"
181 fd bar usize
182 "#]],
183 );
184
185 check_snippet(
186 test_code,
187 expect![[r#"
188 sn pd
189 sn ppd
190 "#]],
191 );
192 }
193
194 #[test]
195 fn test_record_pattern_field() {
196 check(
197 r#"
198struct S { foo: u32 }
199
200fn process(f: S) {
201 match f {
202 S { f$0: 92 } => (),
203 }
204}
205"#,
206 expect![[r#"
207 fd foo u32
208 "#]],
209 );
210 }
211
212 #[test]
213 fn test_record_pattern_enum_variant() {
214 check(
215 r#"
216enum E { S { foo: u32, bar: () } }
217
218fn process(e: E) {
219 match e {
220 E::S { $0 } => (),
221 }
222}
223"#,
224 expect![[r#"
225 fd foo u32
226 fd bar ()
227 "#]],
228 );
229 }
230
231 #[test]
232 fn test_record_pattern_field_in_simple_macro() {
233 check(
234 r"
235macro_rules! m { ($e:expr) => { $e } }
236struct S { foo: u32 }
237
238fn process(f: S) {
239 m!(match f {
240 S { f$0: 92 } => (),
241 })
242}
243",
244 expect![[r#"
245 fd foo u32
246 "#]],
247 );
248 }
249
250 #[test]
251 fn only_missing_fields_are_completed_in_destruct_pats() {
252 check(
253 r#"
254struct S {
255 foo1: u32, foo2: u32,
256 bar: u32, baz: u32,
257}
258
259fn main() {
260 let s = S {
261 foo1: 1, foo2: 2,
262 bar: 3, baz: 4,
263 };
264 if let S { foo1, foo2: a, $0 } = s {}
265}
266"#,
267 expect![[r#"
268 fd bar u32
269 fd baz u32
270 "#]],
271 );
272 }
273
274 #[test]
275 fn test_record_literal_field() {
276 check(
277 r#"
278struct A { the_field: u32 }
279fn foo() {
280 A { the$0 }
281}
282"#,
283 expect![[r#"
284 fd the_field u32
285 "#]],
286 );
287 }
288
289 #[test]
290 fn test_record_literal_enum_variant() {
291 check(
292 r#"
293enum E { A { a: u32 } }
294fn foo() {
295 let _ = E::A { $0 }
296}
297"#,
298 expect![[r#"
299 fd a u32
300 "#]],
301 );
302 }
303
304 #[test]
305 fn test_record_literal_two_structs() {
306 check(
307 r#"
308struct A { a: u32 }
309struct B { b: u32 }
310
311fn foo() {
312 let _: A = B { $0 }
313}
314"#,
315 expect![[r#"
316 fd b u32
317 "#]],
318 );
319 }
320
321 #[test]
322 fn test_record_literal_generic_struct() {
323 check(
324 r#"
325struct A<T> { a: T }
326
327fn foo() {
328 let _: A<u32> = A { $0 }
329}
330"#,
331 expect![[r#"
332 fd a u32
333 "#]],
334 );
335 }
336
337 #[test]
338 fn test_record_literal_field_in_simple_macro() {
339 check(
340 r#"
341macro_rules! m { ($e:expr) => { $e } }
342struct A { the_field: u32 }
343fn foo() {
344 m!(A { the$0 })
345}
346"#,
347 expect![[r#"
348 fd the_field u32
349 "#]],
350 );
351 }
352
353 #[test]
354 fn only_missing_fields_are_completed() {
355 check(
356 r#"
357struct S {
358 foo1: u32, foo2: u32,
359 bar: u32, baz: u32,
360}
361
362fn main() {
363 let foo1 = 1;
364 let s = S { foo1, foo2: 5, $0 }
365}
366"#,
367 expect![[r#"
368 fd bar u32
369 fd baz u32
370 "#]],
371 );
372 }
373
374 #[test]
375 fn completes_functional_update() {
376 check(
377 r#"
378struct S { foo1: u32, foo2: u32 }
379
380fn main() {
381 let foo1 = 1;
382 let s = S { foo1, $0 .. loop {} }
383}
384"#,
385 expect![[r#"
386 fd foo2 u32
387 "#]],
388 );
389 }
390}
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
new file mode 100644
index 000000000..df17a15c5
--- /dev/null
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -0,0 +1,116 @@
1//! This file provides snippet completions, like `pd` => `eprintln!(...)`.
2
3use ide_db::helpers::SnippetCap;
4
5use crate::{
6 item::Builder, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
7 Completions,
8};
9
10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
11 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label)
12 .insert_snippet(cap, snippet)
13 .kind(CompletionItemKind::Snippet)
14}
15
16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
17 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
18 return;
19 }
20 let cap = match ctx.config.snippet_cap {
21 Some(it) => it,
22 None => return,
23 };
24
25 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
26 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
27}
28
29pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
30 if !ctx.is_new_item {
31 return;
32 }
33 let cap = match ctx.config.snippet_cap {
34 Some(it) => it,
35 None => return,
36 };
37
38 snippet(
39 ctx,
40 cap,
41 "tmod (Test module)",
42 "\
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 #[test]
48 fn ${1:test_name}() {
49 $0
50 }
51}",
52 )
53 .lookup_by("tmod")
54 .add_to(acc);
55
56 snippet(
57 ctx,
58 cap,
59 "tfn (Test function)",
60 "\
61#[test]
62fn ${1:feature}() {
63 $0
64}",
65 )
66 .lookup_by("tfn")
67 .add_to(acc);
68
69 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc);
70}
71
72#[cfg(test)]
73mod tests {
74 use expect_test::{expect, Expect};
75
76 use crate::{test_utils::completion_list, CompletionKind};
77
78 fn check(ra_fixture: &str, expect: Expect) {
79 let actual = completion_list(ra_fixture, CompletionKind::Snippet);
80 expect.assert_eq(&actual)
81 }
82
83 #[test]
84 fn completes_snippets_in_expressions() {
85 check(
86 r#"fn foo(x: i32) { $0 }"#,
87 expect![[r#"
88 sn pd
89 sn ppd
90 "#]],
91 );
92 }
93
94 #[test]
95 fn should_not_complete_snippets_in_path() {
96 check(r#"fn foo(x: i32) { ::foo$0 }"#, expect![[""]]);
97 check(r#"fn foo(x: i32) { ::$0 }"#, expect![[""]]);
98 }
99
100 #[test]
101 fn completes_snippets_in_items() {
102 check(
103 r#"
104#[cfg(test)]
105mod tests {
106 $0
107}
108"#,
109 expect![[r#"
110 sn tmod (Test module)
111 sn tfn (Test function)
112 sn macro_rules
113 "#]],
114 )
115 }
116}
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
new file mode 100644
index 000000000..b999540b8
--- /dev/null
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -0,0 +1,736 @@
1//! Completion for associated items in a trait implementation.
2//!
3//! This module adds the completion items related to implementing associated
4//! items within a `impl Trait for Struct` block. The current context node
5//! must be within either a `FN`, `TYPE_ALIAS`, or `CONST` node
6//! and an direct child of an `IMPL`.
7//!
8//! # Examples
9//!
10//! Considering the following trait `impl`:
11//!
12//! ```ignore
13//! trait SomeTrait {
14//! fn foo();
15//! }
16//!
17//! impl SomeTrait for () {
18//! fn f$0
19//! }
20//! ```
21//!
22//! may result in the completion of the following method:
23//!
24//! ```ignore
25//! # trait SomeTrait {
26//! # fn foo();
27//! # }
28//!
29//! impl SomeTrait for () {
30//! fn foo() {}$0
31//! }
32//! ```
33
34use hir::{self, HasAttrs, HasSource};
35use ide_db::{traits::get_missing_assoc_items, SymbolKind};
36use syntax::{
37 ast::{self, edit, Impl},
38 display::function_declaration,
39 AstNode, SyntaxKind, SyntaxNode, TextRange, T,
40};
41use text_edit::TextEdit;
42
43use crate::{
44 CompletionContext,
45 CompletionItem,
46 CompletionItemKind,
47 CompletionKind,
48 Completions,
49 // display::function_declaration,
50};
51
52#[derive(Debug, PartialEq, Eq)]
53enum ImplCompletionKind {
54 All,
55 Fn,
56 TypeAlias,
57 Const,
58}
59
60pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
61 if let Some((kind, trigger, impl_def)) = completion_match(ctx) {
62 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item {
63 hir::AssocItem::Function(fn_item)
64 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
65 {
66 add_function_impl(&trigger, acc, ctx, fn_item)
67 }
68 hir::AssocItem::TypeAlias(type_item)
69 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias =>
70 {
71 add_type_alias_impl(&trigger, acc, ctx, type_item)
72 }
73 hir::AssocItem::Const(const_item)
74 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const =>
75 {
76 add_const_impl(&trigger, acc, ctx, const_item)
77 }
78 _ => {}
79 });
80 }
81}
82
83fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> {
84 let mut token = ctx.token.clone();
85 // For keywork without name like `impl .. { fn $0 }`, the current position is inside
86 // the whitespace token, which is outside `FN` syntax node.
87 // We need to follow the previous token in this case.
88 if token.kind() == SyntaxKind::WHITESPACE {
89 token = token.prev_token()?;
90 }
91
92 let impl_item_offset = match token.kind() {
93 // `impl .. { const $0 }`
94 // ERROR 0
95 // CONST_KW <- *
96 T![const] => 0,
97 // `impl .. { fn/type $0 }`
98 // FN/TYPE_ALIAS 0
99 // FN_KW <- *
100 T![fn] | T![type] => 0,
101 // `impl .. { fn/type/const foo$0 }`
102 // FN/TYPE_ALIAS/CONST 1
103 // NAME 0
104 // IDENT <- *
105 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME => 1,
106 // `impl .. { foo$0 }`
107 // MACRO_CALL 3
108 // PATH 2
109 // PATH_SEGMENT 1
110 // NAME_REF 0
111 // IDENT <- *
112 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME_REF => 3,
113 _ => return None,
114 };
115
116 let impl_item = token.ancestors().nth(impl_item_offset)?;
117 // Must directly belong to an impl block.
118 // IMPL
119 // ASSOC_ITEM_LIST
120 // <item>
121 let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?;
122 let kind = match impl_item.kind() {
123 // `impl ... { const $0 fn/type/const }`
124 _ if token.kind() == T![const] => ImplCompletionKind::Const,
125 SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const,
126 SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias,
127 SyntaxKind::FN => ImplCompletionKind::Fn,
128 SyntaxKind::MACRO_CALL => ImplCompletionKind::All,
129 _ => return None,
130 };
131 Some((kind, impl_item, impl_def))
132}
133
134fn add_function_impl(
135 fn_def_node: &SyntaxNode,
136 acc: &mut Completions,
137 ctx: &CompletionContext,
138 func: hir::Function,
139) {
140 let fn_name = func.name(ctx.db).to_string();
141
142 let label = if func.assoc_fn_params(ctx.db).is_empty() {
143 format!("fn {}()", fn_name)
144 } else {
145 format!("fn {}(..)", fn_name)
146 };
147
148 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
149 .lookup_by(fn_name)
150 .set_documentation(func.docs(ctx.db));
151
152 let completion_kind = if func.self_param(ctx.db).is_some() {
153 CompletionItemKind::Method
154 } else {
155 CompletionItemKind::SymbolKind(SymbolKind::Function)
156 };
157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
158
159 if let Some(src) = func.source(ctx.db) {
160 let function_decl = function_declaration(&src.value);
161 match ctx.config.snippet_cap {
162 Some(cap) => {
163 let snippet = format!("{} {{\n $0\n}}", function_decl);
164 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
165 }
166 None => {
167 let header = format!("{} {{", function_decl);
168 builder.text_edit(TextEdit::replace(range, header))
169 }
170 }
171 .kind(completion_kind)
172 .add_to(acc);
173 }
174}
175
176fn add_type_alias_impl(
177 type_def_node: &SyntaxNode,
178 acc: &mut Completions,
179 ctx: &CompletionContext,
180 type_alias: hir::TypeAlias,
181) {
182 let alias_name = type_alias.name(ctx.db).to_string();
183
184 let snippet = format!("type {} = ", alias_name);
185
186 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
187
188 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
189 .text_edit(TextEdit::replace(range, snippet))
190 .lookup_by(alias_name)
191 .kind(SymbolKind::TypeAlias)
192 .set_documentation(type_alias.docs(ctx.db))
193 .add_to(acc);
194}
195
196fn add_const_impl(
197 const_def_node: &SyntaxNode,
198 acc: &mut Completions,
199 ctx: &CompletionContext,
200 const_: hir::Const,
201) {
202 let const_name = const_.name(ctx.db).map(|n| n.to_string());
203
204 if let Some(const_name) = const_name {
205 if let Some(source) = const_.source(ctx.db) {
206 let snippet = make_const_compl_syntax(&source.value);
207
208 let range =
209 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
210
211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
212 .text_edit(TextEdit::replace(range, snippet))
213 .lookup_by(const_name)
214 .kind(SymbolKind::Const)
215 .set_documentation(const_.docs(ctx.db))
216 .add_to(acc);
217 }
218 }
219}
220
221fn make_const_compl_syntax(const_: &ast::Const) -> String {
222 let const_ = edit::remove_attrs_and_docs(const_);
223
224 let const_start = const_.syntax().text_range().start();
225 let const_end = const_.syntax().text_range().end();
226
227 let start =
228 const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());
229
230 let end = const_
231 .syntax()
232 .children_with_tokens()
233 .find(|s| s.kind() == T![;] || s.kind() == T![=])
234 .map_or(const_end, |f| f.text_range().start());
235
236 let len = end - start;
237 let range = TextRange::new(0.into(), len);
238
239 let syntax = const_.syntax().text().slice(range).to_string();
240
241 format!("{} = ", syntax.trim_end())
242}
243
244#[cfg(test)]
245mod tests {
246 use expect_test::{expect, Expect};
247
248 use crate::{
249 test_utils::{check_edit, completion_list},
250 CompletionKind,
251 };
252
253 fn check(ra_fixture: &str, expect: Expect) {
254 let actual = completion_list(ra_fixture, CompletionKind::Magic);
255 expect.assert_eq(&actual)
256 }
257
258 #[test]
259 fn name_ref_function_type_const() {
260 check(
261 r#"
262trait Test {
263 type TestType;
264 const TEST_CONST: u16;
265 fn test();
266}
267struct T;
268
269impl Test for T {
270 t$0
271}
272"#,
273 expect![["
274ta type TestType = \n\
275ct const TEST_CONST: u16 = \n\
276fn fn test()
277"]],
278 );
279 }
280
281 #[test]
282 fn no_completion_inside_fn() {
283 check(
284 r"
285trait Test { fn test(); fn test2(); }
286struct T;
287
288impl Test for T {
289 fn test() {
290 t$0
291 }
292}
293",
294 expect![[""]],
295 );
296
297 check(
298 r"
299trait Test { fn test(); fn test2(); }
300struct T;
301
302impl Test for T {
303 fn test() {
304 fn t$0
305 }
306}
307",
308 expect![[""]],
309 );
310
311 check(
312 r"
313trait Test { fn test(); fn test2(); }
314struct T;
315
316impl Test for T {
317 fn test() {
318 fn $0
319 }
320}
321",
322 expect![[""]],
323 );
324
325 // https://github.com/rust-analyzer/rust-analyzer/pull/5976#issuecomment-692332191
326 check(
327 r"
328trait Test { fn test(); fn test2(); }
329struct T;
330
331impl Test for T {
332 fn test() {
333 foo.$0
334 }
335}
336",
337 expect![[""]],
338 );
339
340 check(
341 r"
342trait Test { fn test(_: i32); fn test2(); }
343struct T;
344
345impl Test for T {
346 fn test(t$0)
347}
348",
349 expect![[""]],
350 );
351
352 check(
353 r"
354trait Test { fn test(_: fn()); fn test2(); }
355struct T;
356
357impl Test for T {
358 fn test(f: fn $0)
359}
360",
361 expect![[""]],
362 );
363 }
364
365 #[test]
366 fn no_completion_inside_const() {
367 check(
368 r"
369trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
370struct T;
371
372impl Test for T {
373 const TEST: fn $0
374}
375",
376 expect![[""]],
377 );
378
379 check(
380 r"
381trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
382struct T;
383
384impl Test for T {
385 const TEST: T$0
386}
387",
388 expect![[""]],
389 );
390
391 check(
392 r"
393trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
394struct T;
395
396impl Test for T {
397 const TEST: u32 = f$0
398}
399",
400 expect![[""]],
401 );
402
403 check(
404 r"
405trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
406struct T;
407
408impl Test for T {
409 const TEST: u32 = {
410 t$0
411 };
412}
413",
414 expect![[""]],
415 );
416
417 check(
418 r"
419trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
420struct T;
421
422impl Test for T {
423 const TEST: u32 = {
424 fn $0
425 };
426}
427",
428 expect![[""]],
429 );
430
431 check(
432 r"
433trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
434struct T;
435
436impl Test for T {
437 const TEST: u32 = {
438 fn t$0
439 };
440}
441",
442 expect![[""]],
443 );
444 }
445
446 #[test]
447 fn no_completion_inside_type() {
448 check(
449 r"
450trait Test { type Test; type Test2; fn test(); }
451struct T;
452
453impl Test for T {
454 type Test = T$0;
455}
456",
457 expect![[""]],
458 );
459
460 check(
461 r"
462trait Test { type Test; type Test2; fn test(); }
463struct T;
464
465impl Test for T {
466 type Test = fn $0;
467}
468",
469 expect![[""]],
470 );
471 }
472
473 #[test]
474 fn name_ref_single_function() {
475 check_edit(
476 "test",
477 r#"
478trait Test {
479 fn test();
480}
481struct T;
482
483impl Test for T {
484 t$0
485}
486"#,
487 r#"
488trait Test {
489 fn test();
490}
491struct T;
492
493impl Test for T {
494 fn test() {
495 $0
496}
497}
498"#,
499 );
500 }
501
502 #[test]
503 fn single_function() {
504 check_edit(
505 "test",
506 r#"
507trait Test {
508 fn test();
509}
510struct T;
511
512impl Test for T {
513 fn t$0
514}
515"#,
516 r#"
517trait Test {
518 fn test();
519}
520struct T;
521
522impl Test for T {
523 fn test() {
524 $0
525}
526}
527"#,
528 );
529 }
530
531 #[test]
532 fn hide_implemented_fn() {
533 check(
534 r#"
535trait Test {
536 fn foo();
537 fn foo_bar();
538}
539struct T;
540
541impl Test for T {
542 fn foo() {}
543 fn f$0
544}
545"#,
546 expect![[r#"
547 fn fn foo_bar()
548 "#]],
549 );
550 }
551
552 #[test]
553 fn generic_fn() {
554 check_edit(
555 "foo",
556 r#"
557trait Test {
558 fn foo<T>();
559}
560struct T;
561
562impl Test for T {
563 fn f$0
564}
565"#,
566 r#"
567trait Test {
568 fn foo<T>();
569}
570struct T;
571
572impl Test for T {
573 fn foo<T>() {
574 $0
575}
576}
577"#,
578 );
579 check_edit(
580 "foo",
581 r#"
582trait Test {
583 fn foo<T>() where T: Into<String>;
584}
585struct T;
586
587impl Test for T {
588 fn f$0
589}
590"#,
591 r#"
592trait Test {
593 fn foo<T>() where T: Into<String>;
594}
595struct T;
596
597impl Test for T {
598 fn foo<T>()
599where T: Into<String> {
600 $0
601}
602}
603"#,
604 );
605 }
606
607 #[test]
608 fn associated_type() {
609 check_edit(
610 "SomeType",
611 r#"
612trait Test {
613 type SomeType;
614}
615
616impl Test for () {
617 type S$0
618}
619"#,
620 "
621trait Test {
622 type SomeType;
623}
624
625impl Test for () {
626 type SomeType = \n\
627}
628",
629 );
630 }
631
632 #[test]
633 fn associated_const() {
634 check_edit(
635 "SOME_CONST",
636 r#"
637trait Test {
638 const SOME_CONST: u16;
639}
640
641impl Test for () {
642 const S$0
643}
644"#,
645 "
646trait Test {
647 const SOME_CONST: u16;
648}
649
650impl Test for () {
651 const SOME_CONST: u16 = \n\
652}
653",
654 );
655
656 check_edit(
657 "SOME_CONST",
658 r#"
659trait Test {
660 const SOME_CONST: u16 = 92;
661}
662
663impl Test for () {
664 const S$0
665}
666"#,
667 "
668trait Test {
669 const SOME_CONST: u16 = 92;
670}
671
672impl Test for () {
673 const SOME_CONST: u16 = \n\
674}
675",
676 );
677 }
678
679 #[test]
680 fn complete_without_name() {
681 let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
682 check_edit(
683 completion,
684 &format!(
685 r#"
686trait Test {{
687 type Foo;
688 const CONST: u16;
689 fn bar();
690}}
691struct T;
692
693impl Test for T {{
694 {}
695 {}
696}}
697"#,
698 hint, next_sibling
699 ),
700 &format!(
701 r#"
702trait Test {{
703 type Foo;
704 const CONST: u16;
705 fn bar();
706}}
707struct T;
708
709impl Test for T {{
710 {}
711 {}
712}}
713"#,
714 completed, next_sibling
715 ),
716 )
717 };
718
719 // Enumerate some possible next siblings.
720 for next_sibling in &[
721 "",
722 "fn other_fn() {}", // `const $0 fn` -> `const fn`
723 "type OtherType = i32;",
724 "const OTHER_CONST: i32 = 0;",
725 "async fn other_fn() {}",
726 "unsafe fn other_fn() {}",
727 "default fn other_fn() {}",
728 "default type OtherType = i32;",
729 "default const OTHER_CONST: i32 = 0;",
730 ] {
731 test("bar", "fn $0", "fn bar() {\n $0\n}", next_sibling);
732 test("Foo", "type $0", "type Foo = ", next_sibling);
733 test("CONST", "const $0", "const CONST: u16 = ", next_sibling);
734 }
735 }
736}
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
new file mode 100644
index 000000000..e9d0ff665
--- /dev/null
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -0,0 +1,755 @@
1//! Completion of names from the current scope, e.g. locals and imported items.
2
3use hir::ScopeDef;
4use syntax::AstNode;
5use test_utils::mark;
6
7use crate::{CompletionContext, Completions};
8
9pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
11 return;
12 }
13 if ctx.record_lit_syntax.is_some()
14 || ctx.record_pat_syntax.is_some()
15 || ctx.attribute_under_caret.is_some()
16 || ctx.mod_declaration_under_caret.is_some()
17 {
18 return;
19 }
20
21 if let Some(ty) = &ctx.expected_type {
22 super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| {
23 acc.add_qualified_enum_variant(ctx, variant, path)
24 });
25 }
26
27 if ctx.is_pat_binding_or_const {
28 return;
29 }
30
31 ctx.scope.process_all_names(&mut |name, res| {
32 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
33 mark::hit!(skip_lifetime_completion);
34 return;
35 }
36 if ctx.use_item_syntax.is_some() {
37 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
38 if name_ref.syntax().text() == name.to_string().as_str() {
39 mark::hit!(self_fulfilling_completion);
40 return;
41 }
42 }
43 }
44 acc.add_resolution(ctx, name.to_string(), &res);
45 });
46}
47
48#[cfg(test)]
49mod tests {
50 use expect_test::{expect, Expect};
51 use test_utils::mark;
52
53 use crate::{
54 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG},
55 CompletionConfig, CompletionKind,
56 };
57
58 fn check(ra_fixture: &str, expect: Expect) {
59 check_with_config(TEST_CONFIG, ra_fixture, expect);
60 }
61
62 fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) {
63 let actual = completion_list_with_config(config, ra_fixture, CompletionKind::Reference);
64 expect.assert_eq(&actual)
65 }
66
67 #[test]
68 fn self_fulfilling_completion() {
69 mark::check!(self_fulfilling_completion);
70 check(
71 r#"
72use foo$0
73use std::collections;
74"#,
75 expect![[r#"
76 ?? collections
77 "#]],
78 );
79 }
80
81 #[test]
82 fn bind_pat_and_path_ignore_at() {
83 check(
84 r#"
85enum Enum { A, B }
86fn quux(x: Option<Enum>) {
87 match x {
88 None => (),
89 Some(en$0 @ Enum::A) => (),
90 }
91}
92"#,
93 expect![[""]],
94 );
95 }
96
97 #[test]
98 fn bind_pat_and_path_ignore_ref() {
99 check(
100 r#"
101enum Enum { A, B }
102fn quux(x: Option<Enum>) {
103 match x {
104 None => (),
105 Some(ref en$0) => (),
106 }
107}
108"#,
109 expect![[""]],
110 );
111 }
112
113 #[test]
114 fn bind_pat_and_path() {
115 check(
116 r#"
117enum Enum { A, B }
118fn quux(x: Option<Enum>) {
119 match x {
120 None => (),
121 Some(En$0) => (),
122 }
123}
124"#,
125 expect![[r#"
126 en Enum
127 "#]],
128 );
129 }
130
131 #[test]
132 fn completes_bindings_from_let() {
133 check(
134 r#"
135fn quux(x: i32) {
136 let y = 92;
137 1 + $0;
138 let z = ();
139}
140"#,
141 expect![[r#"
142 lc y i32
143 lc x i32
144 fn quux(…) -> ()
145 "#]],
146 );
147 }
148
149 #[test]
150 fn completes_bindings_from_if_let() {
151 check(
152 r#"
153fn quux() {
154 if let Some(x) = foo() {
155 let y = 92;
156 };
157 if let Some(a) = bar() {
158 let b = 62;
159 1 + $0
160 }
161}
162"#,
163 expect![[r#"
164 lc b i32
165 lc a
166 fn quux() -> ()
167 "#]],
168 );
169 }
170
171 #[test]
172 fn completes_bindings_from_for() {
173 check(
174 r#"
175fn quux() {
176 for x in &[1, 2, 3] { $0 }
177}
178"#,
179 expect![[r#"
180 lc x
181 fn quux() -> ()
182 "#]],
183 );
184 }
185
186 #[test]
187 fn completes_if_prefix_is_keyword() {
188 mark::check!(completes_if_prefix_is_keyword);
189 check_edit(
190 "wherewolf",
191 r#"
192fn main() {
193 let wherewolf = 92;
194 drop(where$0)
195}
196"#,
197 r#"
198fn main() {
199 let wherewolf = 92;
200 drop(wherewolf)
201}
202"#,
203 )
204 }
205
206 #[test]
207 fn completes_generic_params() {
208 check(
209 r#"fn quux<T>() { $0 }"#,
210 expect![[r#"
211 tp T
212 fn quux() -> ()
213 "#]],
214 );
215 check(
216 r#"fn quux<const C: usize>() { $0 }"#,
217 expect![[r#"
218 cp C
219 fn quux() -> ()
220 "#]],
221 );
222 }
223
224 #[test]
225 fn does_not_complete_lifetimes() {
226 mark::check!(skip_lifetime_completion);
227 check(
228 r#"fn quux<'a>() { $0 }"#,
229 expect![[r#"
230 fn quux() -> ()
231 "#]],
232 );
233 }
234
235 #[test]
236 fn completes_generic_params_in_struct() {
237 check(
238 r#"struct S<T> { x: $0}"#,
239 expect![[r#"
240 sp Self
241 tp T
242 st S<…>
243 "#]],
244 );
245 }
246
247 #[test]
248 fn completes_self_in_enum() {
249 check(
250 r#"enum X { Y($0) }"#,
251 expect![[r#"
252 sp Self
253 en X
254 "#]],
255 );
256 }
257
258 #[test]
259 fn completes_module_items() {
260 check(
261 r#"
262struct S;
263enum E {}
264fn quux() { $0 }
265"#,
266 expect![[r#"
267 st S
268 fn quux() -> ()
269 en E
270 "#]],
271 );
272 }
273
274 /// Regression test for issue #6091.
275 #[test]
276 fn correctly_completes_module_items_prefixed_with_underscore() {
277 check_edit(
278 "_alpha",
279 r#"
280fn main() {
281 _$0
282}
283fn _alpha() {}
284"#,
285 r#"
286fn main() {
287 _alpha()$0
288}
289fn _alpha() {}
290"#,
291 )
292 }
293
294 #[test]
295 fn completes_extern_prelude() {
296 check(
297 r#"
298//- /lib.rs crate:main deps:other_crate
299use $0;
300
301//- /other_crate/lib.rs crate:other_crate
302// nothing here
303"#,
304 expect![[r#"
305 md other_crate
306 "#]],
307 );
308 }
309
310 #[test]
311 fn completes_module_items_in_nested_modules() {
312 check(
313 r#"
314struct Foo;
315mod m {
316 struct Bar;
317 fn quux() { $0 }
318}
319"#,
320 expect![[r#"
321 fn quux() -> ()
322 st Bar
323 "#]],
324 );
325 }
326
327 #[test]
328 fn completes_return_type() {
329 check(
330 r#"
331struct Foo;
332fn x() -> $0
333"#,
334 expect![[r#"
335 st Foo
336 fn x() -> ()
337 "#]],
338 );
339 }
340
341 #[test]
342 fn dont_show_both_completions_for_shadowing() {
343 check(
344 r#"
345fn foo() {
346 let bar = 92;
347 {
348 let bar = 62;
349 drop($0)
350 }
351}
352"#,
353 // FIXME: should be only one bar here
354 expect![[r#"
355 lc bar i32
356 lc bar i32
357 fn foo() -> ()
358 "#]],
359 );
360 }
361
362 #[test]
363 fn completes_self_in_methods() {
364 check(
365 r#"impl S { fn foo(&self) { $0 } }"#,
366 expect![[r#"
367 lc self &{unknown}
368 sp Self
369 "#]],
370 );
371 }
372
373 #[test]
374 fn completes_prelude() {
375 check(
376 r#"
377//- /main.rs crate:main deps:std
378fn foo() { let x: $0 }
379
380//- /std/lib.rs crate:std
381#[prelude_import]
382use prelude::*;
383
384mod prelude { struct Option; }
385"#,
386 expect![[r#"
387 fn foo() -> ()
388 md std
389 st Option
390 "#]],
391 );
392 }
393
394 #[test]
395 fn completes_prelude_macros() {
396 check(
397 r#"
398//- /main.rs crate:main deps:std
399fn f() {$0}
400
401//- /std/lib.rs crate:std
402#[prelude_import]
403pub use prelude::*;
404
405#[macro_use]
406mod prelude {
407 pub use crate::concat;
408}
409
410mod macros {
411 #[rustc_builtin_macro]
412 #[macro_export]
413 macro_rules! concat { }
414}
415"#,
416 expect![[r##"
417 fn f() -> ()
418 ma concat!(…) #[macro_export] macro_rules! concat
419 md std
420 "##]],
421 );
422 }
423
424 #[test]
425 fn completes_std_prelude_if_core_is_defined() {
426 check(
427 r#"
428//- /main.rs crate:main deps:core,std
429fn foo() { let x: $0 }
430
431//- /core/lib.rs crate:core
432#[prelude_import]
433use prelude::*;
434
435mod prelude { struct Option; }
436
437//- /std/lib.rs crate:std deps:core
438#[prelude_import]
439use prelude::*;
440
441mod prelude { struct String; }
442"#,
443 expect![[r#"
444 fn foo() -> ()
445 md std
446 md core
447 st String
448 "#]],
449 );
450 }
451
452 #[test]
453 fn completes_macros_as_value() {
454 check(
455 r#"
456macro_rules! foo { () => {} }
457
458#[macro_use]
459mod m1 {
460 macro_rules! bar { () => {} }
461}
462
463mod m2 {
464 macro_rules! nope { () => {} }
465
466 #[macro_export]
467 macro_rules! baz { () => {} }
468}
469
470fn main() { let v = $0 }
471"#,
472 expect![[r##"
473 md m1
474 ma baz!(…) #[macro_export] macro_rules! baz
475 fn main() -> ()
476 md m2
477 ma bar!(…) macro_rules! bar
478 ma foo!(…) macro_rules! foo
479 "##]],
480 );
481 }
482
483 #[test]
484 fn completes_both_macro_and_value() {
485 check(
486 r#"
487macro_rules! foo { () => {} }
488fn foo() { $0 }
489"#,
490 expect![[r#"
491 fn foo() -> ()
492 ma foo!(…) macro_rules! foo
493 "#]],
494 );
495 }
496
497 #[test]
498 fn completes_macros_as_type() {
499 check(
500 r#"
501macro_rules! foo { () => {} }
502fn main() { let x: $0 }
503"#,
504 expect![[r#"
505 fn main() -> ()
506 ma foo!(…) macro_rules! foo
507 "#]],
508 );
509 }
510
511 #[test]
512 fn completes_macros_as_stmt() {
513 check(
514 r#"
515macro_rules! foo { () => {} }
516fn main() { $0 }
517"#,
518 expect![[r#"
519 fn main() -> ()
520 ma foo!(…) macro_rules! foo
521 "#]],
522 );
523 }
524
525 #[test]
526 fn completes_local_item() {
527 check(
528 r#"
529fn main() {
530 return f$0;
531 fn frobnicate() {}
532}
533"#,
534 expect![[r#"
535 fn frobnicate() -> ()
536 fn main() -> ()
537 "#]],
538 );
539 }
540
541 #[test]
542 fn completes_in_simple_macro_1() {
543 check(
544 r#"
545macro_rules! m { ($e:expr) => { $e } }
546fn quux(x: i32) {
547 let y = 92;
548 m!($0);
549}
550"#,
551 expect![[r#"
552 lc y i32
553 lc x i32
554 fn quux(…) -> ()
555 ma m!(…) macro_rules! m
556 "#]],
557 );
558 }
559
560 #[test]
561 fn completes_in_simple_macro_2() {
562 check(
563 r"
564macro_rules! m { ($e:expr) => { $e } }
565fn quux(x: i32) {
566 let y = 92;
567 m!(x$0);
568}
569",
570 expect![[r#"
571 lc y i32
572 lc x i32
573 fn quux(…) -> ()
574 ma m!(…) macro_rules! m
575 "#]],
576 );
577 }
578
579 #[test]
580 fn completes_in_simple_macro_without_closing_parens() {
581 check(
582 r#"
583macro_rules! m { ($e:expr) => { $e } }
584fn quux(x: i32) {
585 let y = 92;
586 m!(x$0
587}
588"#,
589 expect![[r#"
590 lc y i32
591 lc x i32
592 fn quux(…) -> ()
593 ma m!(…) macro_rules! m
594 "#]],
595 );
596 }
597
598 #[test]
599 fn completes_unresolved_uses() {
600 check(
601 r#"
602use spam::Quux;
603
604fn main() { $0 }
605"#,
606 expect![[r#"
607 fn main() -> ()
608 ?? Quux
609 "#]],
610 );
611 }
612
613 #[test]
614 fn completes_enum_variant_matcharm() {
615 check(
616 r#"
617enum Foo { Bar, Baz, Quux }
618
619fn main() {
620 let foo = Foo::Quux;
621 match foo { Qu$0 }
622}
623"#,
624 expect![[r#"
625 ev Foo::Bar ()
626 ev Foo::Baz ()
627 ev Foo::Quux ()
628 en Foo
629 "#]],
630 )
631 }
632
633 #[test]
634 fn completes_enum_variant_matcharm_ref() {
635 check(
636 r#"
637enum Foo { Bar, Baz, Quux }
638
639fn main() {
640 let foo = Foo::Quux;
641 match &foo { Qu$0 }
642}
643"#,
644 expect![[r#"
645 ev Foo::Bar ()
646 ev Foo::Baz ()
647 ev Foo::Quux ()
648 en Foo
649 "#]],
650 )
651 }
652
653 #[test]
654 fn completes_enum_variant_iflet() {
655 check(
656 r#"
657enum Foo { Bar, Baz, Quux }
658
659fn main() {
660 let foo = Foo::Quux;
661 if let Qu$0 = foo { }
662}
663"#,
664 expect![[r#"
665 ev Foo::Bar ()
666 ev Foo::Baz ()
667 ev Foo::Quux ()
668 en Foo
669 "#]],
670 )
671 }
672
673 #[test]
674 fn completes_enum_variant_basic_expr() {
675 check(
676 r#"
677enum Foo { Bar, Baz, Quux }
678fn main() { let foo: Foo = Q$0 }
679"#,
680 expect![[r#"
681 ev Foo::Bar ()
682 ev Foo::Baz ()
683 ev Foo::Quux ()
684 en Foo
685 fn main() -> ()
686 "#]],
687 )
688 }
689
690 #[test]
691 fn completes_enum_variant_from_module() {
692 check(
693 r#"
694mod m { pub enum E { V } }
695fn f() -> m::E { V$0 }
696"#,
697 expect![[r#"
698 ev m::E::V ()
699 md m
700 fn f() -> E
701 "#]],
702 )
703 }
704
705 #[test]
706 fn completes_enum_variant_impl() {
707 check(
708 r#"
709enum Foo { Bar, Baz, Quux }
710impl Foo {
711 fn foo() { match Foo::Bar { Q$0 } }
712}
713"#,
714 expect![[r#"
715 ev Self::Bar ()
716 ev Self::Baz ()
717 ev Self::Quux ()
718 ev Foo::Bar ()
719 ev Foo::Baz ()
720 ev Foo::Quux ()
721 sp Self
722 en Foo
723 "#]],
724 )
725 }
726
727 #[test]
728 fn dont_complete_attr() {
729 check(
730 r#"
731struct Foo;
732#[$0]
733fn f() {}
734"#,
735 expect![[""]],
736 )
737 }
738
739 #[test]
740 fn completes_type_or_trait_in_impl_block() {
741 check(
742 r#"
743trait MyTrait {}
744struct MyStruct {}
745
746impl My$0
747"#,
748 expect![[r#"
749 sp Self
750 tt MyTrait
751 st MyStruct
752 "#]],
753 )
754 }
755}