aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/completions
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/completions')
-rw-r--r--crates/completion/src/completions/attribute.rs528
-rw-r--r--crates/completion/src/completions/dot.rs431
-rw-r--r--crates/completion/src/completions/fn_param.rs135
-rw-r--r--crates/completion/src/completions/keyword.rs566
-rw-r--r--crates/completion/src/completions/macro_in_item_position.rs41
-rw-r--r--crates/completion/src/completions/mod_.rs320
-rw-r--r--crates/completion/src/completions/pattern.rs88
-rw-r--r--crates/completion/src/completions/postfix.rs452
-rw-r--r--crates/completion/src/completions/postfix/format_like.rs279
-rw-r--r--crates/completion/src/completions/qualified_path.rs755
-rw-r--r--crates/completion/src/completions/record.rs226
-rw-r--r--crates/completion/src/completions/snippet.rs114
-rw-r--r--crates/completion/src/completions/trait_impl.rs736
-rw-r--r--crates/completion/src/completions/unqualified_path.rs679
14 files changed, 5350 insertions, 0 deletions
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs
new file mode 100644
index 000000000..f3d669458
--- /dev/null
+++ b/crates/completion/src/completions/attribute.rs
@@ -0,0 +1,528 @@
1//! Completion for attributes
2//!
3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes.
5
6use rustc_hash::FxHashSet;
7use syntax::{ast, AstNode, SyntaxKind};
8
9use crate::{
10 context::CompletionContext,
11 generated_lint_completions::{CLIPPY_LINTS, FEATURES},
12 item::{CompletionItem, CompletionItemKind, CompletionKind},
13 Completions,
14};
15
16pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
17 if ctx.mod_declaration_under_caret.is_some() {
18 return None;
19 }
20
21 let attribute = ctx.attribute_under_caret.as_ref()?;
22 match (attribute.path(), attribute.token_tree()) {
23 (Some(path), Some(token_tree)) if path.to_string() == "derive" => {
24 complete_derive(acc, ctx, token_tree)
25 }
26 (Some(path), Some(token_tree)) if path.to_string() == "feature" => {
27 complete_lint(acc, ctx, token_tree, FEATURES);
28 }
29 (Some(path), Some(token_tree))
30 if ["allow", "warn", "deny", "forbid"]
31 .iter()
32 .any(|lint_level| lint_level == &path.to_string()) =>
33 {
34 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS);
35 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
36 }
37 (_, Some(_token_tree)) => {}
38 _ => complete_attribute_start(acc, ctx, attribute),
39 }
40 Some(())
41}
42
43fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
44 for attr_completion in ATTRIBUTES {
45 let mut item = CompletionItem::new(
46 CompletionKind::Attribute,
47 ctx.source_range(),
48 attr_completion.label,
49 )
50 .kind(CompletionItemKind::Attribute);
51
52 if let Some(lookup) = attr_completion.lookup {
53 item = item.lookup_by(lookup);
54 }
55
56 match (attr_completion.snippet, ctx.config.snippet_cap) {
57 (Some(snippet), Some(cap)) => {
58 item = item.insert_snippet(cap, snippet);
59 }
60 _ => {}
61 }
62
63 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
64 acc.add(item.build());
65 }
66 }
67}
68
69struct AttrCompletion {
70 label: &'static str,
71 lookup: Option<&'static str>,
72 snippet: Option<&'static str>,
73 prefer_inner: bool,
74}
75
76impl AttrCompletion {
77 const fn prefer_inner(self) -> AttrCompletion {
78 AttrCompletion { prefer_inner: true, ..self }
79 }
80}
81
82const fn attr(
83 label: &'static str,
84 lookup: Option<&'static str>,
85 snippet: Option<&'static str>,
86) -> AttrCompletion {
87 AttrCompletion { label, lookup, snippet, prefer_inner: false }
88}
89
90const ATTRIBUTES: &[AttrCompletion] = &[
91 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
92 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
93 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
94 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
95 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
96 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
97 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
98 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
99 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
100 // FIXME: resolve through macro resolution?
101 attr("global_allocator", None, None).prefer_inner(),
102 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
103 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
104 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
105 attr("link", None, None),
106 attr("macro_export", None, None),
107 attr("macro_use", None, None),
108 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
109 attr("no_mangle", None, None),
110 attr("no_std", None, None).prefer_inner(),
111 attr("non_exhaustive", None, None),
112 attr("panic_handler", None, None).prefer_inner(),
113 attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")),
114 attr("proc_macro", None, None),
115 attr("proc_macro_attribute", None, None),
116 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
117 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
118 .prefer_inner(),
119 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
120 attr(
121 "should_panic(…)",
122 Some("should_panic"),
123 Some(r#"should_panic(expected = "${0:reason}")"#),
124 ),
125 attr(
126 r#"target_feature = "…""#,
127 Some("target_feature"),
128 Some("target_feature = \"${0:feature}\""),
129 ),
130 attr("test", None, None),
131 attr("used", None, None),
132 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
133 attr(
134 r#"windows_subsystem = "…""#,
135 Some("windows_subsystem"),
136 Some(r#"windows_subsystem = "${0:subsystem}""#),
137 )
138 .prefer_inner(),
139];
140
141fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
142 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
143 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
144 .into_iter()
145 .filter(|completion| !existing_derives.contains(completion.label))
146 {
147 let mut label = derive_completion.label.to_owned();
148 for dependency in derive_completion
149 .dependencies
150 .into_iter()
151 .filter(|&&dependency| !existing_derives.contains(dependency))
152 {
153 label.push_str(", ");
154 label.push_str(dependency);
155 }
156 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
157 .kind(CompletionItemKind::Attribute)
158 .add_to(acc)
159 }
160
161 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
162 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), custom_derive_name)
163 .kind(CompletionItemKind::Attribute)
164 .add_to(acc)
165 }
166 }
167}
168
169fn complete_lint(
170 acc: &mut Completions,
171 ctx: &CompletionContext,
172 derive_input: ast::TokenTree,
173 lints_completions: &[LintCompletion],
174) {
175 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
176 for lint_completion in lints_completions
177 .into_iter()
178 .filter(|completion| !existing_lints.contains(completion.label))
179 {
180 CompletionItem::new(
181 CompletionKind::Attribute,
182 ctx.source_range(),
183 lint_completion.label,
184 )
185 .kind(CompletionItemKind::Attribute)
186 .detail(lint_completion.description)
187 .add_to(acc)
188 }
189 }
190}
191
192fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
193 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
194 (Some(left_paren), Some(right_paren))
195 if left_paren.kind() == SyntaxKind::L_PAREN
196 && right_paren.kind() == SyntaxKind::R_PAREN =>
197 {
198 let mut input_derives = FxHashSet::default();
199 let mut current_derive = String::new();
200 for token in derive_input
201 .syntax()
202 .children_with_tokens()
203 .filter_map(|token| token.into_token())
204 .skip_while(|token| token != &left_paren)
205 .skip(1)
206 .take_while(|token| token != &right_paren)
207 {
208 if SyntaxKind::COMMA == token.kind() {
209 if !current_derive.is_empty() {
210 input_derives.insert(current_derive);
211 current_derive = String::new();
212 }
213 } else {
214 current_derive.push_str(token.to_string().trim());
215 }
216 }
217
218 if !current_derive.is_empty() {
219 input_derives.insert(current_derive);
220 }
221 Ok(input_derives)
222 }
223 _ => Err(()),
224 }
225}
226
227fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
228 let mut result = FxHashSet::default();
229 ctx.scope.process_all_names(&mut |name, scope_def| {
230 if let hir::ScopeDef::MacroDef(mac) = scope_def {
231 if mac.is_derive_macro() {
232 result.insert(name.to_string());
233 }
234 }
235 });
236 result
237}
238
239struct DeriveCompletion {
240 label: &'static str,
241 dependencies: &'static [&'static str],
242}
243
244/// Standard Rust derives and the information about their dependencies
245/// (the dependencies are needed so that the main derive don't break the compilation when added)
246#[rustfmt::skip]
247const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
248 DeriveCompletion { label: "Clone", dependencies: &[] },
249 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
250 DeriveCompletion { label: "Debug", dependencies: &[] },
251 DeriveCompletion { label: "Default", dependencies: &[] },
252 DeriveCompletion { label: "Hash", dependencies: &[] },
253 DeriveCompletion { label: "PartialEq", dependencies: &[] },
254 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
255 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
256 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
257];
258
259pub(crate) struct LintCompletion {
260 pub(crate) label: &'static str,
261 pub(crate) description: &'static str,
262}
263
264#[rustfmt::skip]
265const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
266 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"# },
267 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
268 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
269 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
270 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
271 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
272 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
273 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
274 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
275 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
276 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
277 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
278 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
279 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
280 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
281 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
282 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
283 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
284 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
285 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
286 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
287 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
288 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
289 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
290 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
291 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
292 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
293 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
294 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
295 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
296 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
297 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
298 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
299 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
300 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
301 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
302 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
303 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
304 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
305 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
306 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
307 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
308 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
309 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
310 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
311 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
312 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
313 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
314 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
315 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
316 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
317 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
318 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
319 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
320 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
321 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
322 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
323 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
324 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
325 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
326 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
327 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
328 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
329 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
330 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
331 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
332 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
333 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
334 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
335 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
336 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
337 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
338 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
339 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
340 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
341 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
342 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
343 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
344 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
345 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
346 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
347 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
348 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
349 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
350 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
351 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
352 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
353 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
354 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
355 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
356 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
357 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
358 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
359 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
360 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
361 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
362 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
363 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
364 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
365 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
366 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
367 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
368 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
369 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
370 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"# },
371 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
372 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
373 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
374 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
375 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
376 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
377 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
378 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
379 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
380 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
381];
382
383#[cfg(test)]
384mod tests {
385 use expect_test::{expect, Expect};
386
387 use crate::{test_utils::completion_list, CompletionKind};
388
389 fn check(ra_fixture: &str, expect: Expect) {
390 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
391 expect.assert_eq(&actual);
392 }
393
394 #[test]
395 fn empty_derive_completion() {
396 check(
397 r#"
398#[derive(<|>)]
399struct Test {}
400 "#,
401 expect![[r#"
402 at Clone
403 at Copy, Clone
404 at Debug
405 at Default
406 at Eq, PartialEq
407 at Hash
408 at Ord, PartialOrd, Eq, PartialEq
409 at PartialEq
410 at PartialOrd, PartialEq
411 "#]],
412 );
413 }
414
415 #[test]
416 fn no_completion_for_incorrect_derive() {
417 check(
418 r#"
419#[derive{<|>)]
420struct Test {}
421"#,
422 expect![[r#""#]],
423 )
424 }
425
426 #[test]
427 fn derive_with_input_completion() {
428 check(
429 r#"
430#[derive(serde::Serialize, PartialEq, <|>)]
431struct Test {}
432"#,
433 expect![[r#"
434 at Clone
435 at Copy, Clone
436 at Debug
437 at Default
438 at Eq
439 at Hash
440 at Ord, PartialOrd, Eq
441 at PartialOrd
442 "#]],
443 )
444 }
445
446 #[test]
447 fn test_attribute_completion() {
448 check(
449 r#"#[<|>]"#,
450 expect![[r#"
451 at allow(…)
452 at cfg(…)
453 at cfg_attr(…)
454 at deny(…)
455 at deprecated = "…"
456 at derive(…)
457 at doc = "…"
458 at forbid(…)
459 at ignore = "…"
460 at inline(…)
461 at link
462 at link_name = "…"
463 at macro_export
464 at macro_use
465 at must_use = "…"
466 at no_mangle
467 at non_exhaustive
468 at path = "…"
469 at proc_macro
470 at proc_macro_attribute
471 at proc_macro_derive(…)
472 at repr(…)
473 at should_panic(…)
474 at target_feature = "…"
475 at test
476 at used
477 at warn(…)
478 "#]],
479 )
480 }
481
482 #[test]
483 fn test_attribute_completion_inside_nested_attr() {
484 check(r#"#[cfg(<|>)]"#, expect![[]])
485 }
486
487 #[test]
488 fn test_inner_attribute_completion() {
489 check(
490 r"#![<|>]",
491 expect![[r#"
492 at allow(…)
493 at cfg(…)
494 at cfg_attr(…)
495 at deny(…)
496 at deprecated = "…"
497 at derive(…)
498 at doc = "…"
499 at feature(…)
500 at forbid(…)
501 at global_allocator
502 at ignore = "…"
503 at inline(…)
504 at link
505 at link_name = "…"
506 at macro_export
507 at macro_use
508 at must_use = "…"
509 at no_mangle
510 at no_std
511 at non_exhaustive
512 at panic_handler
513 at path = "…"
514 at proc_macro
515 at proc_macro_attribute
516 at proc_macro_derive(…)
517 at recursion_limit = …
518 at repr(…)
519 at should_panic(…)
520 at target_feature = "…"
521 at test
522 at used
523 at warn(…)
524 at windows_subsystem = "…"
525 "#]],
526 );
527 }
528}
diff --git a/crates/completion/src/completions/dot.rs b/crates/completion/src/completions/dot.rs
new file mode 100644
index 000000000..c9875045a
--- /dev/null
+++ b/crates/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.<|> }
83"#,
84 expect![[r#"
85 me bar() fn bar(&self)
86 fd foo u32
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.<|> }
98}
99"#,
100 expect![[r#"
101 me foo() fn foo(self)
102 fd the_field (u32,)
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.<|> }
114}
115"#,
116 expect![[r#"
117 me foo() fn foo(&self)
118 fd the_field (u32, i32)
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.<|>() }
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.<|> }
148"#,
149 expect![[r#"
150 fd crate_field u32
151 fd pub_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.<|> }
166"#,
167 expect![[r#"
168 me the_method() pub(crate) fn the_method(&self)
169 "#]],
170 );
171 }
172
173 #[test]
174 fn test_union_field_completion() {
175 check(
176 r#"
177union U { field: u8, other: u16 }
178fn foo(u: U) { u.<|> }
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.<|> }
199"#,
200 expect![[r#"
201 me the_method() fn the_method(&self)
202 "#]],
203 )
204 }
205
206 #[test]
207 fn test_trait_method_completion() {
208 check(
209 r#"
210struct A {}
211trait Trait { fn the_method(&self); }
212impl Trait for A {}
213fn foo(a: A) { a.<|> }
214"#,
215 expect![[r#"
216 me the_method() fn the_method(&self)
217 "#]],
218 );
219 }
220
221 #[test]
222 fn test_trait_method_completion_deduplicated() {
223 check(
224 r"
225struct A {}
226trait Trait { fn the_method(&self); }
227impl<T> Trait for T {}
228fn foo(a: &A) { a.<|> }
229",
230 expect![[r#"
231 me the_method() fn the_method(&self)
232 "#]],
233 );
234 }
235
236 #[test]
237 fn completes_trait_method_from_other_module() {
238 check(
239 r"
240struct A {}
241mod m {
242 pub trait Trait { fn the_method(&self); }
243}
244use m::Trait;
245impl Trait for A {}
246fn foo(a: A) { a.<|> }
247",
248 expect![[r#"
249 me the_method() fn the_method(&self)
250 "#]],
251 );
252 }
253
254 #[test]
255 fn test_no_non_self_method() {
256 check(
257 r#"
258struct A {}
259impl A {
260 fn the_method() {}
261}
262fn foo(a: A) {
263 a.<|>
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.<|>
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<|>
299 }
300}
301"#,
302 expect![[r#"
303 me blah() pub fn blah(&self)
304 "#]],
305 );
306 }
307
308 #[test]
309 fn test_completion_works_in_consts() {
310 check(
311 r#"
312struct A { the_field: u32 }
313const X: u32 = {
314 A { the_field: 92 }.<|>
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<|>)
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.<|>)
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<|>)))
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! dbg {
377 () => {};
378 ($val:expr) => {
379 match $val { tmp => { tmp } }
380 };
381 // Trailing comma with single argument is ignored
382 ($val:expr,) => { $crate::dbg!($val) };
383 ($($val:expr),+ $(,)?) => {
384 ($($crate::dbg!($val)),+,)
385 };
386}
387struct A { the_field: u32 }
388fn foo(a: A) {
389 dbg!(a.<|>)
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.<|>
409}
410"#,
411 expect![[r#"
412 me the_method() pub fn the_method(&self)
413 "#]],
414 );
415 }
416
417 #[test]
418 fn completes_method_call_when_receiver_is_a_macro_call() {
419 check(
420 r#"
421struct S;
422impl S { fn foo(&self) {} }
423macro_rules! make_s { () => { S }; }
424fn main() { make_s!().f<|>; }
425"#,
426 expect![[r#"
427 me foo() fn foo(&self)
428 "#]],
429 )
430 }
431}
diff --git a/crates/completion/src/completions/fn_param.rs b/crates/completion/src/completions/fn_param.rs
new file mode 100644
index 000000000..e777a53c1
--- /dev/null
+++ b/crates/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, CompletionKind, Completions};
10
11/// Complete repeated parameters, both name and type. For example, if all
12/// functions in a file have a `spam: &mut Spam` parameter, a completion with
13/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
14/// suggested.
15pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) {
16 if !ctx.is_param {
17 return;
18 }
19
20 let mut params = FxHashMap::default();
21
22 let me = ctx.token.ancestors().find_map(ast::Fn::cast);
23 let mut process_fn = |func: ast::Fn| {
24 if Some(&func) == me.as_ref() {
25 return;
26 }
27 func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
28 let text = param.syntax().text().to_string();
29 params.entry(text).or_insert(param);
30 })
31 };
32
33 for node in ctx.token.parent().ancestors() {
34 match_ast! {
35 match node {
36 ast::SourceFile(it) => it.items().filter_map(|item| match item {
37 ast::Item::Fn(it) => Some(it),
38 _ => None,
39 }).for_each(&mut process_fn),
40 ast::ItemList(it) => it.items().filter_map(|item| match item {
41 ast::Item::Fn(it) => Some(it),
42 _ => None,
43 }).for_each(&mut process_fn),
44 ast::AssocItemList(it) => it.assoc_items().filter_map(|item| match item {
45 ast::AssocItem::Fn(it) => Some(it),
46 _ => None,
47 }).for_each(&mut process_fn),
48 _ => continue,
49 }
50 };
51 }
52
53 params
54 .into_iter()
55 .filter_map(|(label, param)| {
56 let lookup = param.pat()?.syntax().text().to_string();
57 Some((label, lookup))
58 })
59 .for_each(|(label, lookup)| {
60 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
61 .kind(crate::CompletionItemKind::Binding)
62 .lookup_by(lookup)
63 .add_to(acc)
64 });
65}
66
67#[cfg(test)]
68mod tests {
69 use expect_test::{expect, Expect};
70
71 use crate::{test_utils::completion_list, CompletionKind};
72
73 fn check(ra_fixture: &str, expect: Expect) {
74 let actual = completion_list(ra_fixture, CompletionKind::Magic);
75 expect.assert_eq(&actual);
76 }
77
78 #[test]
79 fn test_param_completion_last_param() {
80 check(
81 r#"
82fn foo(file_id: FileId) {}
83fn bar(file_id: FileId) {}
84fn baz(file<|>) {}
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<|>, 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<|>)
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(<|>)
128}
129"#,
130 expect![[r#"
131 bn text: String
132 "#]],
133 )
134 }
135}
diff --git a/crates/completion/src/completions/keyword.rs b/crates/completion/src/completions/keyword.rs
new file mode 100644
index 000000000..c7df15900
--- /dev/null
+++ b/crates/completion/src/completions/keyword.rs
@@ -0,0 +1,566 @@
1//! Completes keywords.
2
3use syntax::{ast, 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
48 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
49 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
50 add_keyword(ctx, acc, "where", "where ");
51 return;
52 }
53 if ctx.unsafe_is_prev {
54 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
55 add_keyword(ctx, acc, "fn", "fn $0() {}")
56 }
57
58 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
59 add_keyword(ctx, acc, "trait", "trait $0 {}");
60 add_keyword(ctx, acc, "impl", "impl $0 {}");
61 }
62
63 return;
64 }
65 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
66 {
67 add_keyword(ctx, acc, "fn", "fn $0() {}");
68 }
69 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
70 add_keyword(ctx, acc, "use", "use ");
71 add_keyword(ctx, acc, "impl", "impl $0 {}");
72 add_keyword(ctx, acc, "trait", "trait $0 {}");
73 }
74
75 if ctx.has_item_list_or_source_file_parent {
76 add_keyword(ctx, acc, "enum", "enum $0 {}");
77 add_keyword(ctx, acc, "struct", "struct $0");
78 add_keyword(ctx, acc, "union", "union $0 {}");
79 }
80
81 if ctx.is_expr {
82 add_keyword(ctx, acc, "match", "match $0 {}");
83 add_keyword(ctx, acc, "while", "while $0 {}");
84 add_keyword(ctx, acc, "loop", "loop {$0}");
85 add_keyword(ctx, acc, "if", "if ");
86 add_keyword(ctx, acc, "if let", "if let ");
87 }
88
89 if ctx.if_is_prev || ctx.block_expr_parent {
90 add_keyword(ctx, acc, "let", "let ");
91 }
92
93 if ctx.after_if {
94 add_keyword(ctx, acc, "else", "else {$0}");
95 add_keyword(ctx, acc, "else if", "else if $0 {}");
96 }
97 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
98 add_keyword(ctx, acc, "mod", "mod $0 {}");
99 }
100 if ctx.bind_pat_parent || ctx.ref_pat_parent {
101 add_keyword(ctx, acc, "mut", "mut ");
102 }
103 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
104 {
105 add_keyword(ctx, acc, "const", "const ");
106 add_keyword(ctx, acc, "type", "type ");
107 }
108 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
109 add_keyword(ctx, acc, "static", "static ");
110 };
111 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
112 add_keyword(ctx, acc, "extern", "extern ");
113 }
114 if ctx.has_item_list_or_source_file_parent
115 || has_trait_or_impl_parent
116 || ctx.block_expr_parent
117 || ctx.is_match_arm
118 {
119 add_keyword(ctx, acc, "unsafe", "unsafe ");
120 }
121 if ctx.in_loop_body {
122 if ctx.can_be_stmt {
123 add_keyword(ctx, acc, "continue", "continue;");
124 add_keyword(ctx, acc, "break", "break;");
125 } else {
126 add_keyword(ctx, acc, "continue", "continue");
127 add_keyword(ctx, acc, "break", "break");
128 }
129 }
130 if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent | ctx.has_field_list_parent {
131 add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
132 add_keyword(ctx, acc, "pub", "pub ");
133 }
134
135 if !ctx.is_trivial_path {
136 return;
137 }
138 let fn_def = match &ctx.function_syntax {
139 Some(it) => it,
140 None => return,
141 };
142 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
143}
144
145fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
146 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
147 .kind(CompletionItemKind::Keyword);
148
149 match ctx.config.snippet_cap {
150 Some(cap) => res.insert_snippet(cap, snippet),
151 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
152 }
153 .build()
154}
155
156fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
157 acc.add(keyword(ctx, kw, snippet));
158}
159
160fn complete_return(
161 ctx: &CompletionContext,
162 fn_def: &ast::Fn,
163 can_be_stmt: bool,
164) -> Option<CompletionItem> {
165 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
166 (true, true) => "return $0;",
167 (true, false) => "return;",
168 (false, true) => "return $0",
169 (false, false) => "return",
170 };
171 Some(keyword(ctx, "return", snip))
172}
173
174#[cfg(test)]
175mod tests {
176 use expect_test::{expect, Expect};
177
178 use crate::{
179 test_utils::{check_edit, completion_list},
180 CompletionKind,
181 };
182 use test_utils::mark;
183
184 fn check(ra_fixture: &str, expect: Expect) {
185 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
186 expect.assert_eq(&actual)
187 }
188
189 #[test]
190 fn test_keywords_in_use_stmt() {
191 check(
192 r"use <|>",
193 expect![[r#"
194 kw crate::
195 kw self
196 kw super::
197 "#]],
198 );
199
200 check(
201 r"use a::<|>",
202 expect![[r#"
203 kw self
204 kw super::
205 "#]],
206 );
207
208 check(
209 r"use a::{b, <|>}",
210 expect![[r#"
211 kw self
212 kw super::
213 "#]],
214 );
215 }
216
217 #[test]
218 fn test_keywords_at_source_file_level() {
219 check(
220 r"m<|>",
221 expect![[r#"
222 kw const
223 kw enum
224 kw extern
225 kw fn
226 kw impl
227 kw mod
228 kw pub
229 kw pub(crate)
230 kw static
231 kw struct
232 kw trait
233 kw type
234 kw union
235 kw unsafe
236 kw use
237 "#]],
238 );
239 }
240
241 #[test]
242 fn test_keywords_in_function() {
243 check(
244 r"fn quux() { <|> }",
245 expect![[r#"
246 kw const
247 kw extern
248 kw fn
249 kw if
250 kw if let
251 kw impl
252 kw let
253 kw loop
254 kw match
255 kw mod
256 kw return
257 kw static
258 kw trait
259 kw type
260 kw unsafe
261 kw use
262 kw while
263 "#]],
264 );
265 }
266
267 #[test]
268 fn test_keywords_inside_block() {
269 check(
270 r"fn quux() { if true { <|> } }",
271 expect![[r#"
272 kw const
273 kw extern
274 kw fn
275 kw if
276 kw if let
277 kw impl
278 kw let
279 kw loop
280 kw match
281 kw mod
282 kw return
283 kw static
284 kw trait
285 kw type
286 kw unsafe
287 kw use
288 kw while
289 "#]],
290 );
291 }
292
293 #[test]
294 fn test_keywords_after_if() {
295 check(
296 r#"fn quux() { if true { () } <|> }"#,
297 expect![[r#"
298 kw const
299 kw else
300 kw else if
301 kw extern
302 kw fn
303 kw if
304 kw if let
305 kw impl
306 kw let
307 kw loop
308 kw match
309 kw mod
310 kw return
311 kw static
312 kw trait
313 kw type
314 kw unsafe
315 kw use
316 kw while
317 "#]],
318 );
319 check_edit(
320 "else",
321 r#"fn quux() { if true { () } <|> }"#,
322 r#"fn quux() { if true { () } else {$0} }"#,
323 );
324 }
325
326 #[test]
327 fn test_keywords_in_match_arm() {
328 check(
329 r#"
330fn quux() -> i32 {
331 match () { () => <|> }
332}
333"#,
334 expect![[r#"
335 kw if
336 kw if let
337 kw loop
338 kw match
339 kw return
340 kw unsafe
341 kw while
342 "#]],
343 );
344 }
345
346 #[test]
347 fn test_keywords_in_trait_def() {
348 check(
349 r"trait My { <|> }",
350 expect![[r#"
351 kw const
352 kw fn
353 kw type
354 kw unsafe
355 "#]],
356 );
357 }
358
359 #[test]
360 fn test_keywords_in_impl_def() {
361 check(
362 r"impl My { <|> }",
363 expect![[r#"
364 kw const
365 kw fn
366 kw pub
367 kw pub(crate)
368 kw type
369 kw unsafe
370 "#]],
371 );
372 }
373
374 #[test]
375 fn test_keywords_in_loop() {
376 check(
377 r"fn my() { loop { <|> } }",
378 expect![[r#"
379 kw break
380 kw const
381 kw continue
382 kw extern
383 kw fn
384 kw if
385 kw if let
386 kw impl
387 kw let
388 kw loop
389 kw match
390 kw mod
391 kw return
392 kw static
393 kw trait
394 kw type
395 kw unsafe
396 kw use
397 kw while
398 "#]],
399 );
400 }
401
402 #[test]
403 fn test_keywords_after_unsafe_in_item_list() {
404 check(
405 r"unsafe <|>",
406 expect![[r#"
407 kw fn
408 kw impl
409 kw trait
410 "#]],
411 );
412 }
413
414 #[test]
415 fn test_keywords_after_unsafe_in_block_expr() {
416 check(
417 r"fn my_fn() { unsafe <|> }",
418 expect![[r#"
419 kw fn
420 kw impl
421 kw trait
422 "#]],
423 );
424 }
425
426 #[test]
427 fn test_mut_in_ref_and_in_fn_parameters_list() {
428 check(
429 r"fn my_fn(&<|>) {}",
430 expect![[r#"
431 kw mut
432 "#]],
433 );
434 check(
435 r"fn my_fn(<|>) {}",
436 expect![[r#"
437 kw mut
438 "#]],
439 );
440 check(
441 r"fn my_fn() { let &<|> }",
442 expect![[r#"
443 kw mut
444 "#]],
445 );
446 }
447
448 #[test]
449 fn test_where_keyword() {
450 check(
451 r"trait A <|>",
452 expect![[r#"
453 kw where
454 "#]],
455 );
456 check(
457 r"impl A <|>",
458 expect![[r#"
459 kw where
460 "#]],
461 );
462 }
463
464 #[test]
465 fn no_keyword_completion_in_comments() {
466 mark::check!(no_keyword_completion_in_comments);
467 check(
468 r#"
469fn test() {
470 let x = 2; // A comment<|>
471}
472"#,
473 expect![[""]],
474 );
475 check(
476 r#"
477/*
478Some multi-line comment<|>
479*/
480"#,
481 expect![[""]],
482 );
483 check(
484 r#"
485/// Some doc comment
486/// let test<|> = 1
487"#,
488 expect![[""]],
489 );
490 }
491
492 #[test]
493 fn test_completion_await_impls_future() {
494 check(
495 r#"
496//- /main.rs crate:main deps:std
497use std::future::*;
498struct A {}
499impl Future for A {}
500fn foo(a: A) { a.<|> }
501
502//- /std/lib.rs crate:std
503pub mod future {
504 #[lang = "future_trait"]
505 pub trait Future {}
506}
507"#,
508 expect![[r#"
509 kw await expr.await
510 "#]],
511 );
512
513 check(
514 r#"
515//- /main.rs crate:main deps:std
516use std::future::*;
517fn foo() {
518 let a = async {};
519 a.<|>
520}
521
522//- /std/lib.rs crate:std
523pub mod future {
524 #[lang = "future_trait"]
525 pub trait Future {
526 type Output;
527 }
528}
529"#,
530 expect![[r#"
531 kw await expr.await
532 "#]],
533 )
534 }
535
536 #[test]
537 fn after_let() {
538 check(
539 r#"fn main() { let _ = <|> }"#,
540 expect![[r#"
541 kw if
542 kw if let
543 kw loop
544 kw match
545 kw return
546 kw while
547 "#]],
548 )
549 }
550
551 #[test]
552 fn before_field() {
553 check(
554 r#"
555struct Foo {
556 <|>
557 pub f: i32,
558}
559"#,
560 expect![[r#"
561 kw pub
562 kw pub(crate)
563 "#]],
564 )
565 }
566}
diff --git a/crates/completion/src/completions/macro_in_item_position.rs b/crates/completion/src/completions/macro_in_item_position.rs
new file mode 100644
index 000000000..82884a181
--- /dev/null
+++ b/crates/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<|>
35"#,
36 expect![[r#"
37 ma foo!(…) macro_rules! foo
38 "#]],
39 )
40 }
41}
diff --git a/crates/completion/src/completions/mod_.rs b/crates/completion/src/completions/mod_.rs
new file mode 100644
index 000000000..c96f84171
--- /dev/null
+++ b/crates/completion/src/completions/mod_.rs
@@ -0,0 +1,320 @@
1//! Completes mod declarations.
2
3use hir::{Module, ModuleSource};
4use ide_db::base_db::{SourceDatabaseExt, VfsPath};
5use ide_db::RootDatabase;
6use rustc_hash::FxHashSet;
7
8use crate::{CompletionItem, CompletionItemKind};
9
10use crate::{context::CompletionContext, item::CompletionKind, Completions};
11
12/// Complete mod declaration, i.e. `mod <|> ;`
13pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
14 let mod_under_caret = match &ctx.mod_declaration_under_caret {
15 Some(mod_under_caret) if mod_under_caret.item_list().is_some() => return None,
16 Some(mod_under_caret) => mod_under_caret,
17 None => return None,
18 };
19
20 let _p = profile::span("completion::complete_mod");
21
22 let current_module = ctx.scope.module()?;
23
24 let module_definition_file =
25 current_module.definition_source(ctx.db).file_id.original_file(ctx.db);
26 let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file));
27 let directory_to_look_for_submodules = directory_to_look_for_submodules(
28 current_module,
29 ctx.db,
30 source_root.path_for_file(&module_definition_file)?,
31 )?;
32
33 let existing_mod_declarations = current_module
34 .children(ctx.db)
35 .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
36 .collect::<FxHashSet<_>>();
37
38 let module_declaration_file =
39 current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
40 module_declaration_source_file.file_id.original_file(ctx.db)
41 });
42
43 source_root
44 .iter()
45 .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file)
46 .filter(|submodule_candidate_file| {
47 Some(submodule_candidate_file) != module_declaration_file.as_ref()
48 })
49 .filter_map(|submodule_file| {
50 let submodule_path = source_root.path_for_file(&submodule_file)?;
51 let directory_with_submodule = submodule_path.parent()?;
52 match submodule_path.name_and_extension()? {
53 ("lib", Some("rs")) | ("main", Some("rs")) => None,
54 ("mod", Some("rs")) => {
55 if directory_with_submodule.parent()? == directory_to_look_for_submodules {
56 match directory_with_submodule.name_and_extension()? {
57 (directory_name, None) => Some(directory_name.to_owned()),
58 _ => None,
59 }
60 } else {
61 None
62 }
63 }
64 (file_name, Some("rs"))
65 if directory_with_submodule == directory_to_look_for_submodules =>
66 {
67 Some(file_name.to_owned())
68 }
69 _ => None,
70 }
71 })
72 .filter(|name| !existing_mod_declarations.contains(name))
73 .for_each(|submodule_name| {
74 let mut label = submodule_name;
75 if mod_under_caret.semicolon_token().is_none() {
76 label.push(';')
77 }
78 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label)
79 .kind(CompletionItemKind::Module)
80 .add_to(acc)
81 });
82
83 Some(())
84}
85
86fn directory_to_look_for_submodules(
87 module: Module,
88 db: &RootDatabase,
89 module_file_path: &VfsPath,
90) -> Option<VfsPath> {
91 let directory_with_module_path = module_file_path.parent()?;
92 let base_directory = match module_file_path.name_and_extension()? {
93 ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => {
94 Some(directory_with_module_path)
95 }
96 (regular_rust_file_name, Some("rs")) => {
97 if matches!(
98 (
99 directory_with_module_path
100 .parent()
101 .as_ref()
102 .and_then(|path| path.name_and_extension()),
103 directory_with_module_path.name_and_extension(),
104 ),
105 (Some(("src", None)), Some(("bin", None)))
106 ) {
107 // files in /src/bin/ can import each other directly
108 Some(directory_with_module_path)
109 } else {
110 directory_with_module_path.join(regular_rust_file_name)
111 }
112 }
113 _ => None,
114 }?;
115
116 let mut resulting_path = base_directory;
117 for module in module_chain_to_containing_module_file(module, db) {
118 if let Some(name) = module.name(db) {
119 resulting_path = resulting_path.join(&name.to_string())?;
120 }
121 }
122
123 Some(resulting_path)
124}
125
126fn module_chain_to_containing_module_file(
127 current_module: Module,
128 db: &RootDatabase,
129) -> Vec<Module> {
130 let mut path = Vec::new();
131
132 let mut current_module = Some(current_module);
133 while let Some(ModuleSource::Module(_)) =
134 current_module.map(|module| module.definition_source(db).value)
135 {
136 if let Some(module) = current_module {
137 path.insert(0, module);
138 current_module = module.parent(db);
139 } else {
140 current_module = None;
141 }
142 }
143
144 path
145}
146
147#[cfg(test)]
148mod tests {
149 use crate::{test_utils::completion_list, CompletionKind};
150 use expect_test::{expect, Expect};
151
152 fn check(ra_fixture: &str, expect: Expect) {
153 let actual = completion_list(ra_fixture, CompletionKind::Magic);
154 expect.assert_eq(&actual);
155 }
156
157 #[test]
158 fn lib_module_completion() {
159 check(
160 r#"
161 //- /lib.rs
162 mod <|>
163 //- /foo.rs
164 fn foo() {}
165 //- /foo/ignored_foo.rs
166 fn ignored_foo() {}
167 //- /bar/mod.rs
168 fn bar() {}
169 //- /bar/ignored_bar.rs
170 fn ignored_bar() {}
171 "#,
172 expect![[r#"
173 md bar;
174 md foo;
175 "#]],
176 );
177 }
178
179 #[test]
180 fn no_module_completion_with_module_body() {
181 check(
182 r#"
183 //- /lib.rs
184 mod <|> {
185
186 }
187 //- /foo.rs
188 fn foo() {}
189 "#,
190 expect![[r#""#]],
191 );
192 }
193
194 #[test]
195 fn main_module_completion() {
196 check(
197 r#"
198 //- /main.rs
199 mod <|>
200 //- /foo.rs
201 fn foo() {}
202 //- /foo/ignored_foo.rs
203 fn ignored_foo() {}
204 //- /bar/mod.rs
205 fn bar() {}
206 //- /bar/ignored_bar.rs
207 fn ignored_bar() {}
208 "#,
209 expect![[r#"
210 md bar;
211 md foo;
212 "#]],
213 );
214 }
215
216 #[test]
217 fn main_test_module_completion() {
218 check(
219 r#"
220 //- /main.rs
221 mod tests {
222 mod <|>;
223 }
224 //- /tests/foo.rs
225 fn foo() {}
226 "#,
227 expect![[r#"
228 md foo
229 "#]],
230 );
231 }
232
233 #[test]
234 fn directly_nested_module_completion() {
235 check(
236 r#"
237 //- /lib.rs
238 mod foo;
239 //- /foo.rs
240 mod <|>;
241 //- /foo/bar.rs
242 fn bar() {}
243 //- /foo/bar/ignored_bar.rs
244 fn ignored_bar() {}
245 //- /foo/baz/mod.rs
246 fn baz() {}
247 //- /foo/moar/ignored_moar.rs
248 fn ignored_moar() {}
249 "#,
250 expect![[r#"
251 md bar
252 md baz
253 "#]],
254 );
255 }
256
257 #[test]
258 fn nested_in_source_module_completion() {
259 check(
260 r#"
261 //- /lib.rs
262 mod foo;
263 //- /foo.rs
264 mod bar {
265 mod <|>
266 }
267 //- /foo/bar/baz.rs
268 fn baz() {}
269 "#,
270 expect![[r#"
271 md baz;
272 "#]],
273 );
274 }
275
276 // FIXME binary modules are not supported in tests properly
277 // Binary modules are a bit special, they allow importing the modules from `/src/bin`
278 // and that's why are good to test two things:
279 // * no cycles are allowed in mod declarations
280 // * no modules from the parent directory are proposed
281 // Unfortunately, binary modules support is in cargo not rustc,
282 // hence the test does not work now
283 //
284 // #[test]
285 // fn regular_bin_module_completion() {
286 // check(
287 // r#"
288 // //- /src/bin.rs
289 // fn main() {}
290 // //- /src/bin/foo.rs
291 // mod <|>
292 // //- /src/bin/bar.rs
293 // fn bar() {}
294 // //- /src/bin/bar/bar_ignored.rs
295 // fn bar_ignored() {}
296 // "#,
297 // expect![[r#"
298 // md bar;
299 // "#]],foo
300 // );
301 // }
302
303 #[test]
304 fn already_declared_bin_module_completion_omitted() {
305 check(
306 r#"
307 //- /src/bin.rs crate:main
308 fn main() {}
309 //- /src/bin/foo.rs
310 mod <|>
311 //- /src/bin/bar.rs
312 mod foo;
313 fn bar() {}
314 //- /src/bin/bar/bar_ignored.rs
315 fn bar_ignored() {}
316 "#,
317 expect![[r#""#]],
318 );
319 }
320}
diff --git a/crates/completion/src/completions/pattern.rs b/crates/completion/src/completions/pattern.rs
new file mode 100644
index 000000000..7ab7f09fe
--- /dev/null
+++ b/crates/completion/src/completions/pattern.rs
@@ -0,0 +1,88 @@
1//! Completes constats and paths in patterns.
2
3use crate::{CompletionContext, Completions};
4
5/// Completes constats and paths in patterns.
6pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7 if !ctx.is_pat_binding_or_const {
8 return;
9 }
10 if ctx.record_pat_syntax.is_some() {
11 return;
12 }
13
14 // FIXME: ideally, we should look at the type we are matching against and
15 // suggest variants + auto-imports
16 ctx.scope.process_all_names(&mut |name, res| {
17 match &res {
18 hir::ScopeDef::ModuleDef(def) => match def {
19 hir::ModuleDef::Adt(hir::Adt::Enum(..))
20 | hir::ModuleDef::Adt(hir::Adt::Struct(..))
21 | hir::ModuleDef::EnumVariant(..)
22 | hir::ModuleDef::Const(..)
23 | hir::ModuleDef::Module(..) => (),
24 _ => return,
25 },
26 hir::ScopeDef::MacroDef(_) => (),
27 _ => return,
28 };
29
30 acc.add_resolution(ctx, name.to_string(), &res)
31 });
32}
33
34#[cfg(test)]
35mod tests {
36 use expect_test::{expect, Expect};
37
38 use crate::{test_utils::completion_list, CompletionKind};
39
40 fn check(ra_fixture: &str, expect: Expect) {
41 let actual = completion_list(ra_fixture, CompletionKind::Reference);
42 expect.assert_eq(&actual)
43 }
44
45 #[test]
46 fn completes_enum_variants_and_modules() {
47 check(
48 r#"
49enum E { X }
50use self::E::X;
51const Z: E = E::X;
52mod m {}
53
54static FOO: E = E::X;
55struct Bar { f: u32 }
56
57fn foo() {
58 match E::X { <|> }
59}
60"#,
61 expect![[r#"
62 st Bar
63 en E
64 ev X ()
65 ct Z
66 md m
67 "#]],
68 );
69 }
70
71 #[test]
72 fn completes_in_simple_macro_call() {
73 check(
74 r#"
75macro_rules! m { ($e:expr) => { $e } }
76enum E { X }
77
78fn foo() {
79 m!(match E::X { <|> })
80}
81"#,
82 expect![[r#"
83 en E
84 ma m!(…) macro_rules! m
85 "#]],
86 );
87 }
88}
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs
new file mode 100644
index 000000000..348f017bd
--- /dev/null
+++ b/crates/completion/src/completions/postfix.rs
@@ -0,0 +1,452 @@
1//! Postfix completions, like `Ok(10).ifl<|>` => `if let Ok() = Ok(10) { <|> }`.
2
3mod format_like;
4
5use ide_db::ty_filter::TryEnum;
6use syntax::{
7 ast::{self, AstNode, AstToken},
8 TextRange, TextSize,
9};
10use text_edit::TextEdit;
11
12use self::format_like::add_format_like_completions;
13use crate::{
14 config::SnippetCap,
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 cap = match ctx.config.snippet_cap {
39 Some(it) => it,
40 None => return,
41 };
42 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty);
43 if let Some(try_enum) = &try_enum {
44 match try_enum {
45 TryEnum::Result => {
46 postfix_snippet(
47 ctx,
48 cap,
49 &dot_receiver,
50 "ifl",
51 "if let Ok {}",
52 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text),
53 )
54 .add_to(acc);
55
56 postfix_snippet(
57 ctx,
58 cap,
59 &dot_receiver,
60 "while",
61 "while let Ok {}",
62 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text),
63 )
64 .add_to(acc);
65 }
66 TryEnum::Option => {
67 postfix_snippet(
68 ctx,
69 cap,
70 &dot_receiver,
71 "ifl",
72 "if let Some {}",
73 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text),
74 )
75 .add_to(acc);
76
77 postfix_snippet(
78 ctx,
79 cap,
80 &dot_receiver,
81 "while",
82 "while let Some {}",
83 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text),
84 )
85 .add_to(acc);
86 }
87 }
88 } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
89 postfix_snippet(
90 ctx,
91 cap,
92 &dot_receiver,
93 "if",
94 "if expr {}",
95 &format!("if {} {{\n $0\n}}", receiver_text),
96 )
97 .add_to(acc);
98 postfix_snippet(
99 ctx,
100 cap,
101 &dot_receiver,
102 "while",
103 "while expr {}",
104 &format!("while {} {{\n $0\n}}", receiver_text),
105 )
106 .add_to(acc);
107 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
108 .add_to(acc);
109 }
110
111 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
112 .add_to(acc);
113 postfix_snippet(
114 ctx,
115 cap,
116 &dot_receiver,
117 "refm",
118 "&mut expr",
119 &format!("&mut {}", receiver_text),
120 )
121 .add_to(acc);
122
123 // The rest of the postfix completions create an expression that moves an argument,
124 // so it's better to consider references now to avoid breaking the compilation
125 let dot_receiver = include_references(dot_receiver);
126 let receiver_text =
127 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
128
129 match try_enum {
130 Some(try_enum) => match try_enum {
131 TryEnum::Result => {
132 postfix_snippet(
133 ctx,
134 cap,
135 &dot_receiver,
136 "match",
137 "match expr {}",
138 &format!("match {} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
139 )
140 .add_to(acc);
141 }
142 TryEnum::Option => {
143 postfix_snippet(
144 ctx,
145 cap,
146 &dot_receiver,
147 "match",
148 "match expr {}",
149 &format!(
150 "match {} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}",
151 receiver_text
152 ),
153 )
154 .add_to(acc);
155 }
156 },
157 None => {
158 postfix_snippet(
159 ctx,
160 cap,
161 &dot_receiver,
162 "match",
163 "match expr {}",
164 &format!("match {} {{\n ${{1:_}} => {{$0}},\n}}", receiver_text),
165 )
166 .add_to(acc);
167 }
168 }
169
170 postfix_snippet(
171 ctx,
172 cap,
173 &dot_receiver,
174 "box",
175 "Box::new(expr)",
176 &format!("Box::new({})", receiver_text),
177 )
178 .add_to(acc);
179
180 postfix_snippet(ctx, cap, &dot_receiver, "ok", "Ok(expr)", &format!("Ok({})", receiver_text))
181 .add_to(acc);
182
183 postfix_snippet(
184 ctx,
185 cap,
186 &dot_receiver,
187 "dbg",
188 "dbg!(expr)",
189 &format!("dbg!({})", receiver_text),
190 )
191 .add_to(acc);
192
193 postfix_snippet(
194 ctx,
195 cap,
196 &dot_receiver,
197 "dbgr",
198 "dbg!(&expr)",
199 &format!("dbg!(&{})", receiver_text),
200 )
201 .add_to(acc);
202
203 postfix_snippet(
204 ctx,
205 cap,
206 &dot_receiver,
207 "call",
208 "function(expr)",
209 &format!("${{1}}({})", receiver_text),
210 )
211 .add_to(acc);
212
213 if let ast::Expr::Literal(literal) = dot_receiver.clone() {
214 if let Some(literal_text) = ast::String::cast(literal.token()) {
215 add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text);
216 }
217 }
218}
219
220fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
221 if receiver_is_ambiguous_float_literal {
222 let text = receiver.syntax().text();
223 let without_dot = ..text.len() - TextSize::of('.');
224 text.slice(without_dot).to_string()
225 } else {
226 receiver.to_string()
227 }
228}
229
230fn include_references(initial_element: &ast::Expr) -> ast::Expr {
231 let mut resulting_element = initial_element.clone();
232 while let Some(parent_ref_element) =
233 resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
234 {
235 resulting_element = ast::Expr::from(parent_ref_element);
236 }
237 resulting_element
238}
239
240fn postfix_snippet(
241 ctx: &CompletionContext,
242 cap: SnippetCap,
243 receiver: &ast::Expr,
244 label: &str,
245 detail: &str,
246 snippet: &str,
247) -> Builder {
248 let edit = {
249 let receiver_syntax = receiver.syntax();
250 let receiver_range = ctx.sema.original_range(receiver_syntax).range;
251 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
252 TextEdit::replace(delete_range, snippet.to_string())
253 };
254 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
255 .detail(detail)
256 .kind(CompletionItemKind::Snippet)
257 .snippet_edit(cap, edit)
258}
259
260#[cfg(test)]
261mod tests {
262 use expect_test::{expect, Expect};
263
264 use crate::{
265 test_utils::{check_edit, completion_list},
266 CompletionKind,
267 };
268
269 fn check(ra_fixture: &str, expect: Expect) {
270 let actual = completion_list(ra_fixture, CompletionKind::Postfix);
271 expect.assert_eq(&actual)
272 }
273
274 #[test]
275 fn postfix_completion_works_for_trivial_path_expression() {
276 check(
277 r#"
278fn main() {
279 let bar = true;
280 bar.<|>
281}
282"#,
283 expect![[r#"
284 sn box Box::new(expr)
285 sn call function(expr)
286 sn dbg dbg!(expr)
287 sn dbgr dbg!(&expr)
288 sn if if expr {}
289 sn match match expr {}
290 sn not !expr
291 sn ok Ok(expr)
292 sn ref &expr
293 sn refm &mut expr
294 sn while while expr {}
295 "#]],
296 );
297 }
298
299 #[test]
300 fn postfix_type_filtering() {
301 check(
302 r#"
303fn main() {
304 let bar: u8 = 12;
305 bar.<|>
306}
307"#,
308 expect![[r#"
309 sn box Box::new(expr)
310 sn call function(expr)
311 sn dbg dbg!(expr)
312 sn dbgr dbg!(&expr)
313 sn match match expr {}
314 sn ok Ok(expr)
315 sn ref &expr
316 sn refm &mut expr
317 "#]],
318 )
319 }
320
321 #[test]
322 fn option_iflet() {
323 check_edit(
324 "ifl",
325 r#"
326enum Option<T> { Some(T), None }
327
328fn main() {
329 let bar = Option::Some(true);
330 bar.<|>
331}
332"#,
333 r#"
334enum Option<T> { Some(T), None }
335
336fn main() {
337 let bar = Option::Some(true);
338 if let Some($1) = bar {
339 $0
340}
341}
342"#,
343 );
344 }
345
346 #[test]
347 fn result_match() {
348 check_edit(
349 "match",
350 r#"
351enum Result<T, E> { Ok(T), Err(E) }
352
353fn main() {
354 let bar = Result::Ok(true);
355 bar.<|>
356}
357"#,
358 r#"
359enum Result<T, E> { Ok(T), Err(E) }
360
361fn main() {
362 let bar = Result::Ok(true);
363 match bar {
364 Ok(${1:_}) => {$2},
365 Err(${3:_}) => {$0},
366}
367}
368"#,
369 );
370 }
371
372 #[test]
373 fn postfix_completion_works_for_ambiguous_float_literal() {
374 check_edit("refm", r#"fn main() { 42.<|> }"#, r#"fn main() { &mut 42 }"#)
375 }
376
377 #[test]
378 fn works_in_simple_macro() {
379 check_edit(
380 "dbg",
381 r#"
382macro_rules! m { ($e:expr) => { $e } }
383fn main() {
384 let bar: u8 = 12;
385 m!(bar.d<|>)
386}
387"#,
388 r#"
389macro_rules! m { ($e:expr) => { $e } }
390fn main() {
391 let bar: u8 = 12;
392 m!(dbg!(bar))
393}
394"#,
395 );
396 }
397
398 #[test]
399 fn postfix_completion_for_references() {
400 check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#);
401 check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#);
402 }
403
404 #[test]
405 fn postfix_completion_for_format_like_strings() {
406 check_edit(
407 "fmt",
408 r#"fn main() { "{some_var:?}".<|> }"#,
409 r#"fn main() { format!("{:?}", some_var) }"#,
410 );
411 check_edit(
412 "panic",
413 r#"fn main() { "Panic with {a}".<|> }"#,
414 r#"fn main() { panic!("Panic with {}", a) }"#,
415 );
416 check_edit(
417 "println",
418 r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".<|> }"#,
419 r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }) }"#,
420 );
421 check_edit(
422 "loge",
423 r#"fn main() { "{2+2}".<|> }"#,
424 r#"fn main() { log::error!("{}", 2+2) }"#,
425 );
426 check_edit(
427 "logt",
428 r#"fn main() { "{2+2}".<|> }"#,
429 r#"fn main() { log::trace!("{}", 2+2) }"#,
430 );
431 check_edit(
432 "logd",
433 r#"fn main() { "{2+2}".<|> }"#,
434 r#"fn main() { log::debug!("{}", 2+2) }"#,
435 );
436 check_edit(
437 "logi",
438 r#"fn main() { "{2+2}".<|> }"#,
439 r#"fn main() { log::info!("{}", 2+2) }"#,
440 );
441 check_edit(
442 "logw",
443 r#"fn main() { "{2+2}".<|> }"#,
444 r#"fn main() { log::warn!("{}", 2+2) }"#,
445 );
446 check_edit(
447 "loge",
448 r#"fn main() { "{2+2}".<|> }"#,
449 r#"fn main() { log::error!("{}", 2+2) }"#,
450 );
451 }
452}
diff --git a/crates/completion/src/completions/postfix/format_like.rs b/crates/completion/src/completions/postfix/format_like.rs
new file mode 100644
index 000000000..88ba86acb
--- /dev/null
+++ b/crates/completion/src/completions/postfix/format_like.rs
@@ -0,0 +1,279 @@
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 crate::{
18 completions::postfix::postfix_snippet, config::SnippetCap, context::CompletionContext,
19 Completions,
20};
21use syntax::ast::{self, AstToken};
22
23/// Mapping ("postfix completion item" => "macro to use")
24static KINDS: &[(&str, &str)] = &[
25 ("fmt", "format!"),
26 ("panic", "panic!"),
27 ("println", "println!"),
28 ("eprintln", "eprintln!"),
29 ("logd", "log::debug!"),
30 ("logt", "log::trace!"),
31 ("logi", "log::info!"),
32 ("logw", "log::warn!"),
33 ("loge", "log::error!"),
34];
35
36pub(crate) fn add_format_like_completions(
37 acc: &mut Completions,
38 ctx: &CompletionContext,
39 dot_receiver: &ast::Expr,
40 cap: SnippetCap,
41 receiver_text: &ast::String,
42) {
43 let input = match string_literal_contents(receiver_text) {
44 // It's not a string literal, do not parse input.
45 Some(input) => input,
46 None => return,
47 };
48
49 let mut parser = FormatStrParser::new(input);
50
51 if parser.parse().is_ok() {
52 for (label, macro_name) in KINDS {
53 let snippet = parser.into_suggestion(macro_name);
54
55 postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc);
56 }
57 }
58}
59
60/// Checks whether provided item is a string literal.
61fn string_literal_contents(item: &ast::String) -> Option<String> {
62 let item = item.text();
63 if item.len() >= 2 && item.starts_with("\"") && item.ends_with("\"") {
64 return Some(item[1..item.len() - 1].to_owned());
65 }
66
67 None
68}
69
70/// Parser for a format-like string. It is more allowing in terms of string contents,
71/// as we expect variable placeholders to be filled with expressions.
72#[derive(Debug)]
73pub(crate) struct FormatStrParser {
74 input: String,
75 output: String,
76 extracted_expressions: Vec<String>,
77 state: State,
78 parsed: bool,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq)]
82enum State {
83 NotExpr,
84 MaybeExpr,
85 Expr,
86 MaybeIncorrect,
87 FormatOpts,
88}
89
90impl FormatStrParser {
91 pub(crate) fn new(input: String) -> Self {
92 Self {
93 input: input.into(),
94 output: String::new(),
95 extracted_expressions: Vec::new(),
96 state: State::NotExpr,
97 parsed: false,
98 }
99 }
100
101 pub(crate) fn parse(&mut self) -> Result<(), ()> {
102 let mut current_expr = String::new();
103
104 let mut placeholder_id = 1;
105
106 // Count of open braces inside of an expression.
107 // We assume that user knows what they're doing, thus we treat it like a correct pattern, e.g.
108 // "{MyStruct { val_a: 0, val_b: 1 }}".
109 let mut inexpr_open_count = 0;
110
111 for chr in self.input.chars() {
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, ':') => {
161 if inexpr_open_count == 0 {
162 // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
163 self.output.push(chr);
164 self.extracted_expressions.push(current_expr.trim().into());
165 current_expr = String::new();
166 self.state = State::FormatOpts;
167 } else {
168 // We're inside of braced expression, assume that it's a struct field name/value delimeter.
169 current_expr.push(chr);
170 }
171 }
172 (State::Expr, '{') => {
173 current_expr.push(chr);
174 inexpr_open_count += 1;
175 }
176 (State::Expr, _) => {
177 current_expr.push(chr);
178 }
179 (State::FormatOpts, '}') => {
180 self.output.push(chr);
181 self.state = State::NotExpr;
182 }
183 (State::FormatOpts, _) => {
184 self.output.push(chr);
185 }
186 }
187 }
188
189 if self.state != State::NotExpr {
190 return Err(());
191 }
192
193 self.parsed = true;
194 Ok(())
195 }
196
197 pub(crate) fn into_suggestion(&self, macro_name: &str) -> String {
198 assert!(self.parsed, "Attempt to get a suggestion from not parsed expression");
199
200 let expressions_as_string = self.extracted_expressions.join(", ");
201 format!(r#"{}("{}", {})"#, macro_name, self.output, expressions_as_string)
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use expect_test::{expect, Expect};
209
210 fn check(input: &str, expect: &Expect) {
211 let mut parser = FormatStrParser::new((*input).to_owned());
212 let outcome_repr = if parser.parse().is_ok() {
213 // Parsing should be OK, expected repr is "string; expr_1, expr_2".
214 if parser.extracted_expressions.is_empty() {
215 parser.output
216 } else {
217 format!("{}; {}", parser.output, parser.extracted_expressions.join(", "))
218 }
219 } else {
220 // Parsing should fail, expected repr is "-".
221 "-".to_owned()
222 };
223
224 expect.assert_eq(&outcome_repr);
225 }
226
227 #[test]
228 fn format_str_parser() {
229 let test_vector = &[
230 ("no expressions", expect![["no expressions"]]),
231 ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
232 ("{expr:?}", expect![["{:?}; expr"]]),
233 ("{malformed", expect![["-"]]),
234 ("malformed}", expect![["-"]]),
235 ("{{correct", expect![["{{correct"]]),
236 ("correct}}", expect![["correct}}"]]),
237 ("{correct}}}", expect![["{}}}; correct"]]),
238 ("{correct}}}}}", expect![["{}}}}}; correct"]]),
239 ("{incorrect}}", expect![["-"]]),
240 ("placeholders {} {}", expect![["placeholders {} {}; $1, $2"]]),
241 ("mixed {} {2 + 2} {}", expect![["mixed {} {} {}; $1, 2 + 2, $2"]]),
242 (
243 "{SomeStruct { val_a: 0, val_b: 1 }}",
244 expect![["{}; SomeStruct { val_a: 0, val_b: 1 }"]],
245 ),
246 ("{expr:?} is {2.32f64:.5}", expect![["{:?} is {:.5}; expr, 2.32f64"]]),
247 (
248 "{SomeStruct { val_a: 0, val_b: 1 }:?}",
249 expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]],
250 ),
251 ("{ 2 + 2 }", expect![["{}; 2 + 2"]]),
252 ];
253
254 for (input, output) in test_vector {
255 check(input, output)
256 }
257 }
258
259 #[test]
260 fn test_into_suggestion() {
261 let test_vector = &[
262 ("println!", "{}", r#"println!("{}", $1)"#),
263 ("eprintln!", "{}", r#"eprintln!("{}", $1)"#),
264 (
265 "log::info!",
266 "{} {expr} {} {2 + 2}",
267 r#"log::info!("{} {} {} {}", $1, expr, $2, 2 + 2)"#,
268 ),
269 ("format!", "{expr:?}", r#"format!("{:?}", expr)"#),
270 ];
271
272 for (kind, input, output) in test_vector {
273 let mut parser = FormatStrParser::new((*input).to_owned());
274 parser.parse().expect("Parsing must succeed");
275
276 assert_eq!(&parser.into_suggestion(*kind), output);
277 }
278 }
279}
diff --git a/crates/completion/src/completions/qualified_path.rs b/crates/completion/src/completions/qualified_path.rs
new file mode 100644
index 000000000..d9387054d
--- /dev/null
+++ b/crates/completion/src/completions/qualified_path.rs
@@ -0,0 +1,755 @@
1//! Completion of paths, i.e. `some::prefix::<|>`.
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<|>`, don't suggest `foo` as a completion
42 mark::hit!(dont_complete_current_use);
43 continue;
44 }
45 }
46 }
47 }
48
49 acc.add_resolution(ctx, name.to_string(), &def);
50 }
51 }
52 PathResolution::Def(def @ hir::ModuleDef::Adt(_))
53 | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => {
54 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
55 for variant in e.variants(ctx.db) {
56 acc.add_enum_variant(ctx, variant, None);
57 }
58 }
59 let ty = match def {
60 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
61 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
62 _ => unreachable!(),
63 };
64
65 // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
66 // (where AssocType is defined on a trait, not an inherent impl)
67
68 let krate = ctx.krate;
69 if let Some(krate) = krate {
70 let traits_in_scope = ctx.scope.traits_in_scope();
71 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
72 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
73 return None;
74 }
75 match item {
76 hir::AssocItem::Function(func) => {
77 acc.add_function(ctx, func, None);
78 }
79 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
80 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
81 }
82 None::<()>
83 });
84
85 // Iterate assoc types separately
86 ty.iterate_assoc_items(ctx.db, krate, |item| {
87 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
88 return None;
89 }
90 match item {
91 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {}
92 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
93 }
94 None::<()>
95 });
96 }
97 }
98 PathResolution::Def(hir::ModuleDef::Trait(t)) => {
99 // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
100 for item in t.items(ctx.db) {
101 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
102 continue;
103 }
104 match item {
105 hir::AssocItem::Function(func) => {
106 acc.add_function(ctx, func, None);
107 }
108 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
109 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
110 }
111 }
112 }
113 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
114 if let Some(krate) = ctx.krate {
115 let ty = match resolution {
116 PathResolution::TypeParam(param) => param.ty(ctx.db),
117 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
118 _ => return,
119 };
120
121 let traits_in_scope = ctx.scope.traits_in_scope();
122 let mut seen = FxHashSet::default();
123 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
124 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
125 return None;
126 }
127
128 // We might iterate candidates of a trait multiple times here, so deduplicate
129 // them.
130 if seen.insert(item) {
131 match item {
132 hir::AssocItem::Function(func) => {
133 acc.add_function(ctx, func, None);
134 }
135 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
136 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
137 }
138 }
139 None::<()>
140 });
141 }
142 }
143 _ => {}
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use expect_test::{expect, Expect};
150 use test_utils::mark;
151
152 use crate::{
153 test_utils::{check_edit, completion_list},
154 CompletionKind,
155 };
156
157 fn check(ra_fixture: &str, expect: Expect) {
158 let actual = completion_list(ra_fixture, CompletionKind::Reference);
159 expect.assert_eq(&actual);
160 }
161
162 fn check_builtin(ra_fixture: &str, expect: Expect) {
163 let actual = completion_list(ra_fixture, CompletionKind::BuiltinType);
164 expect.assert_eq(&actual);
165 }
166
167 #[test]
168 fn dont_complete_current_use() {
169 mark::check!(dont_complete_current_use);
170 check(r#"use self::foo<|>;"#, expect![[""]]);
171 }
172
173 #[test]
174 fn dont_complete_current_use_in_braces_with_glob() {
175 check(
176 r#"
177mod foo { pub struct S; }
178use self::{foo::*, bar<|>};
179"#,
180 expect![[r#"
181 st S
182 md foo
183 "#]],
184 );
185 }
186
187 #[test]
188 fn dont_complete_primitive_in_use() {
189 check_builtin(r#"use self::<|>;"#, expect![[""]]);
190 }
191
192 #[test]
193 fn dont_complete_primitive_in_module_scope() {
194 check_builtin(r#"fn foo() { self::<|> }"#, expect![[""]]);
195 }
196
197 #[test]
198 fn completes_primitives() {
199 check_builtin(
200 r#"fn main() { let _: <|> = 92; }"#,
201 expect![[r#"
202 bt bool
203 bt char
204 bt f32
205 bt f64
206 bt i128
207 bt i16
208 bt i32
209 bt i64
210 bt i8
211 bt isize
212 bt str
213 bt u128
214 bt u16
215 bt u32
216 bt u64
217 bt u8
218 bt usize
219 "#]],
220 );
221 }
222
223 #[test]
224 fn completes_mod_with_same_name_as_function() {
225 check(
226 r#"
227use self::my::<|>;
228
229mod my { pub struct Bar; }
230fn my() {}
231"#,
232 expect![[r#"
233 st Bar
234 "#]],
235 );
236 }
237
238 #[test]
239 fn filters_visibility() {
240 check(
241 r#"
242use self::my::<|>;
243
244mod my {
245 struct Bar;
246 pub struct Foo;
247 pub use Bar as PublicBar;
248}
249"#,
250 expect![[r#"
251 st Foo
252 st PublicBar
253 "#]],
254 );
255 }
256
257 #[test]
258 fn completes_use_item_starting_with_self() {
259 check(
260 r#"
261use self::m::<|>;
262
263mod m { pub struct Bar; }
264"#,
265 expect![[r#"
266 st Bar
267 "#]],
268 );
269 }
270
271 #[test]
272 fn completes_use_item_starting_with_crate() {
273 check(
274 r#"
275//- /lib.rs
276mod foo;
277struct Spam;
278//- /foo.rs
279use crate::Sp<|>
280"#,
281 expect![[r#"
282 st Spam
283 md foo
284 "#]],
285 );
286 }
287
288 #[test]
289 fn completes_nested_use_tree() {
290 check(
291 r#"
292//- /lib.rs
293mod foo;
294struct Spam;
295//- /foo.rs
296use crate::{Sp<|>};
297"#,
298 expect![[r#"
299 st Spam
300 md foo
301 "#]],
302 );
303 }
304
305 #[test]
306 fn completes_deeply_nested_use_tree() {
307 check(
308 r#"
309//- /lib.rs
310mod foo;
311pub mod bar {
312 pub mod baz {
313 pub struct Spam;
314 }
315}
316//- /foo.rs
317use crate::{bar::{baz::Sp<|>}};
318"#,
319 expect![[r#"
320 st Spam
321 "#]],
322 );
323 }
324
325 #[test]
326 fn completes_enum_variant() {
327 check(
328 r#"
329enum E { Foo, Bar(i32) }
330fn foo() { let _ = E::<|> }
331"#,
332 expect![[r#"
333 ev Bar(…) (i32)
334 ev Foo ()
335 "#]],
336 );
337 }
338
339 #[test]
340 fn completes_struct_associated_items() {
341 check(
342 r#"
343//- /lib.rs
344struct S;
345
346impl S {
347 fn a() {}
348 fn b(&self) {}
349 const C: i32 = 42;
350 type T = i32;
351}
352
353fn foo() { let _ = S::<|> }
354"#,
355 expect![[r#"
356 ct C const C: i32 = 42;
357 ta T type T = i32;
358 fn a() fn a()
359 me b() fn b(&self)
360 "#]],
361 );
362 }
363
364 #[test]
365 fn associated_item_visibility() {
366 check(
367 r#"
368struct S;
369
370mod m {
371 impl super::S {
372 pub(crate) fn public_method() { }
373 fn private_method() { }
374 pub(crate) type PublicType = u32;
375 type PrivateType = u32;
376 pub(crate) const PUBLIC_CONST: u32 = 1;
377 const PRIVATE_CONST: u32 = 1;
378 }
379}
380
381fn foo() { let _ = S::<|> }
382"#,
383 expect![[r#"
384 ct PUBLIC_CONST pub(crate) const PUBLIC_CONST: u32 = 1;
385 ta PublicType pub(crate) type PublicType = u32;
386 fn public_method() pub(crate) fn public_method()
387 "#]],
388 );
389 }
390
391 #[test]
392 fn completes_enum_associated_method() {
393 check(
394 r#"
395enum E {};
396impl E { fn m() { } }
397
398fn foo() { let _ = E::<|> }
399 "#,
400 expect![[r#"
401 fn m() fn m()
402 "#]],
403 );
404 }
405
406 #[test]
407 fn completes_union_associated_method() {
408 check(
409 r#"
410union U {};
411impl U { fn m() { } }
412
413fn foo() { let _ = U::<|> }
414"#,
415 expect![[r#"
416 fn m() fn m()
417 "#]],
418 );
419 }
420
421 #[test]
422 fn completes_use_paths_across_crates() {
423 check(
424 r#"
425//- /main.rs crate:main deps:foo
426use foo::<|>;
427
428//- /foo/lib.rs crate:foo
429pub mod bar { pub struct S; }
430"#,
431 expect![[r#"
432 md bar
433 "#]],
434 );
435 }
436
437 #[test]
438 fn completes_trait_associated_method_1() {
439 check(
440 r#"
441trait Trait { fn m(); }
442
443fn foo() { let _ = Trait::<|> }
444"#,
445 expect![[r#"
446 fn m() fn m()
447 "#]],
448 );
449 }
450
451 #[test]
452 fn completes_trait_associated_method_2() {
453 check(
454 r#"
455trait Trait { fn m(); }
456
457struct S;
458impl Trait for S {}
459
460fn foo() { let _ = S::<|> }
461"#,
462 expect![[r#"
463 fn m() fn m()
464 "#]],
465 );
466 }
467
468 #[test]
469 fn completes_trait_associated_method_3() {
470 check(
471 r#"
472trait Trait { fn m(); }
473
474struct S;
475impl Trait for S {}
476
477fn foo() { let _ = <S as Trait>::<|> }
478"#,
479 expect![[r#"
480 fn m() fn m()
481 "#]],
482 );
483 }
484
485 #[test]
486 fn completes_ty_param_assoc_ty() {
487 check(
488 r#"
489trait Super {
490 type Ty;
491 const CONST: u8;
492 fn func() {}
493 fn method(&self) {}
494}
495
496trait Sub: Super {
497 type SubTy;
498 const C2: ();
499 fn subfunc() {}
500 fn submethod(&self) {}
501}
502
503fn foo<T: Sub>() { T::<|> }
504"#,
505 expect![[r#"
506 ct C2 const C2: ();
507 ct CONST const CONST: u8;
508 ta SubTy type SubTy;
509 ta Ty type Ty;
510 fn func() fn func()
511 me method() fn method(&self)
512 fn subfunc() fn subfunc()
513 me submethod() fn submethod(&self)
514 "#]],
515 );
516 }
517
518 #[test]
519 fn completes_self_param_assoc_ty() {
520 check(
521 r#"
522trait Super {
523 type Ty;
524 const CONST: u8 = 0;
525 fn func() {}
526 fn method(&self) {}
527}
528
529trait Sub: Super {
530 type SubTy;
531 const C2: () = ();
532 fn subfunc() {}
533 fn submethod(&self) {}
534}
535
536struct Wrap<T>(T);
537impl<T> Super for Wrap<T> {}
538impl<T> Sub for Wrap<T> {
539 fn subfunc() {
540 // Should be able to assume `Self: Sub + Super`
541 Self::<|>
542 }
543}
544"#,
545 expect![[r#"
546 ct C2 const C2: () = ();
547 ct CONST const CONST: u8 = 0;
548 ta SubTy type SubTy;
549 ta Ty type Ty;
550 fn func() fn func()
551 me method() fn method(&self)
552 fn subfunc() fn subfunc()
553 me submethod() fn submethod(&self)
554 "#]],
555 );
556 }
557
558 #[test]
559 fn completes_type_alias() {
560 check(
561 r#"
562struct S;
563impl S { fn foo() {} }
564type T = S;
565impl T { fn bar() {} }
566
567fn main() { T::<|>; }
568"#,
569 expect![[r#"
570 fn bar() fn bar()
571 fn foo() fn foo()
572 "#]],
573 );
574 }
575
576 #[test]
577 fn completes_qualified_macros() {
578 check(
579 r#"
580#[macro_export]
581macro_rules! foo { () => {} }
582
583fn main() { let _ = crate::<|> }
584 "#,
585 expect![[r##"
586 ma foo!(…) #[macro_export]
587 macro_rules! foo
588 fn main() fn main()
589 "##]],
590 );
591 }
592
593 #[test]
594 fn test_super_super_completion() {
595 check(
596 r#"
597mod a {
598 const A: usize = 0;
599 mod b {
600 const B: usize = 0;
601 mod c { use super::super::<|> }
602 }
603}
604"#,
605 expect![[r#"
606 ct A
607 md b
608 "#]],
609 );
610 }
611
612 #[test]
613 fn completes_reexported_items_under_correct_name() {
614 check(
615 r#"
616fn foo() { self::m::<|> }
617
618mod m {
619 pub use super::p::wrong_fn as right_fn;
620 pub use super::p::WRONG_CONST as RIGHT_CONST;
621 pub use super::p::WrongType as RightType;
622}
623mod p {
624 fn wrong_fn() {}
625 const WRONG_CONST: u32 = 1;
626 struct WrongType {};
627}
628"#,
629 expect![[r#"
630 ct RIGHT_CONST
631 st RightType
632 fn right_fn() fn wrong_fn()
633 "#]],
634 );
635
636 check_edit(
637 "RightType",
638 r#"
639fn foo() { self::m::<|> }
640
641mod m {
642 pub use super::p::wrong_fn as right_fn;
643 pub use super::p::WRONG_CONST as RIGHT_CONST;
644 pub use super::p::WrongType as RightType;
645}
646mod p {
647 fn wrong_fn() {}
648 const WRONG_CONST: u32 = 1;
649 struct WrongType {};
650}
651"#,
652 r#"
653fn foo() { self::m::RightType }
654
655mod m {
656 pub use super::p::wrong_fn as right_fn;
657 pub use super::p::WRONG_CONST as RIGHT_CONST;
658 pub use super::p::WrongType as RightType;
659}
660mod p {
661 fn wrong_fn() {}
662 const WRONG_CONST: u32 = 1;
663 struct WrongType {};
664}
665"#,
666 );
667 }
668
669 #[test]
670 fn completes_in_simple_macro_call() {
671 check(
672 r#"
673macro_rules! m { ($e:expr) => { $e } }
674fn main() { m!(self::f<|>); }
675fn foo() {}
676"#,
677 expect![[r#"
678 fn foo() fn foo()
679 fn main() fn main()
680 "#]],
681 );
682 }
683
684 #[test]
685 fn function_mod_share_name() {
686 check(
687 r#"
688fn foo() { self::m::<|> }
689
690mod m {
691 pub mod z {}
692 pub fn z() {}
693}
694"#,
695 expect![[r#"
696 md z
697 fn z() pub fn z()
698 "#]],
699 );
700 }
701
702 #[test]
703 fn completes_hashmap_new() {
704 check(
705 r#"
706struct RandomState;
707struct HashMap<K, V, S = RandomState> {}
708
709impl<K, V> HashMap<K, V, RandomState> {
710 pub fn new() -> HashMap<K, V, RandomState> { }
711}
712fn foo() {
713 HashMap::<|>
714}
715"#,
716 expect![[r#"
717 fn new() pub fn new() -> HashMap<K, V, RandomState>
718 "#]],
719 );
720 }
721
722 #[test]
723 fn dont_complete_attr() {
724 check(
725 r#"
726mod foo { pub struct Foo; }
727#[foo::<|>]
728fn f() {}
729"#,
730 expect![[""]],
731 );
732 }
733
734 #[test]
735 fn completes_function() {
736 check(
737 r#"
738fn foo(
739 a: i32,
740 b: i32
741) {
742
743}
744
745fn main() {
746 fo<|>
747}
748"#,
749 expect![[r#"
750 fn foo(…) fn foo(a: i32, b: i32)
751 fn main() fn main()
752 "#]],
753 );
754 }
755}
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs
new file mode 100644
index 000000000..0f611084b
--- /dev/null
+++ b/crates/completion/src/completions/record.rs
@@ -0,0 +1,226 @@
1//! Complete fields in record literals and patterns.
2use crate::{CompletionContext, Completions};
3
4pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
5 let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
6 (None, None) => return None,
7 (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
8 (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
9 (_, Some(record_lit)) => ctx.sema.record_literal_missing_fields(record_lit),
10 };
11
12 for (field, ty) in missing_fields {
13 acc.add_field(ctx, field, &ty)
14 }
15
16 Some(())
17}
18
19#[cfg(test)]
20mod tests {
21 use expect_test::{expect, Expect};
22
23 use crate::{test_utils::completion_list, CompletionKind};
24
25 fn check(ra_fixture: &str, expect: Expect) {
26 let actual = completion_list(ra_fixture, CompletionKind::Reference);
27 expect.assert_eq(&actual);
28 }
29
30 #[test]
31 fn test_record_pattern_field() {
32 check(
33 r#"
34struct S { foo: u32 }
35
36fn process(f: S) {
37 match f {
38 S { f<|>: 92 } => (),
39 }
40}
41"#,
42 expect![[r#"
43 fd foo u32
44 "#]],
45 );
46 }
47
48 #[test]
49 fn test_record_pattern_enum_variant() {
50 check(
51 r#"
52enum E { S { foo: u32, bar: () } }
53
54fn process(e: E) {
55 match e {
56 E::S { <|> } => (),
57 }
58}
59"#,
60 expect![[r#"
61 fd bar ()
62 fd foo u32
63 "#]],
64 );
65 }
66
67 #[test]
68 fn test_record_pattern_field_in_simple_macro() {
69 check(
70 r"
71macro_rules! m { ($e:expr) => { $e } }
72struct S { foo: u32 }
73
74fn process(f: S) {
75 m!(match f {
76 S { f<|>: 92 } => (),
77 })
78}
79",
80 expect![[r#"
81 fd foo u32
82 "#]],
83 );
84 }
85
86 #[test]
87 fn only_missing_fields_are_completed_in_destruct_pats() {
88 check(
89 r#"
90struct S {
91 foo1: u32, foo2: u32,
92 bar: u32, baz: u32,
93}
94
95fn main() {
96 let s = S {
97 foo1: 1, foo2: 2,
98 bar: 3, baz: 4,
99 };
100 if let S { foo1, foo2: a, <|> } = s {}
101}
102"#,
103 expect![[r#"
104 fd bar u32
105 fd baz u32
106 "#]],
107 );
108 }
109
110 #[test]
111 fn test_record_literal_field() {
112 check(
113 r#"
114struct A { the_field: u32 }
115fn foo() {
116 A { the<|> }
117}
118"#,
119 expect![[r#"
120 fd the_field u32
121 "#]],
122 );
123 }
124
125 #[test]
126 fn test_record_literal_enum_variant() {
127 check(
128 r#"
129enum E { A { a: u32 } }
130fn foo() {
131 let _ = E::A { <|> }
132}
133"#,
134 expect![[r#"
135 fd a u32
136 "#]],
137 );
138 }
139
140 #[test]
141 fn test_record_literal_two_structs() {
142 check(
143 r#"
144struct A { a: u32 }
145struct B { b: u32 }
146
147fn foo() {
148 let _: A = B { <|> }
149}
150"#,
151 expect![[r#"
152 fd b u32
153 "#]],
154 );
155 }
156
157 #[test]
158 fn test_record_literal_generic_struct() {
159 check(
160 r#"
161struct A<T> { a: T }
162
163fn foo() {
164 let _: A<u32> = A { <|> }
165}
166"#,
167 expect![[r#"
168 fd a u32
169 "#]],
170 );
171 }
172
173 #[test]
174 fn test_record_literal_field_in_simple_macro() {
175 check(
176 r#"
177macro_rules! m { ($e:expr) => { $e } }
178struct A { the_field: u32 }
179fn foo() {
180 m!(A { the<|> })
181}
182"#,
183 expect![[r#"
184 fd the_field u32
185 "#]],
186 );
187 }
188
189 #[test]
190 fn only_missing_fields_are_completed() {
191 check(
192 r#"
193struct S {
194 foo1: u32, foo2: u32,
195 bar: u32, baz: u32,
196}
197
198fn main() {
199 let foo1 = 1;
200 let s = S { foo1, foo2: 5, <|> }
201}
202"#,
203 expect![[r#"
204 fd bar u32
205 fd baz u32
206 "#]],
207 );
208 }
209
210 #[test]
211 fn completes_functional_update() {
212 check(
213 r#"
214struct S { foo1: u32, foo2: u32 }
215
216fn main() {
217 let foo1 = 1;
218 let s = S { foo1, <|> .. loop {} }
219}
220"#,
221 expect![[r#"
222 fd foo2 u32
223 "#]],
224 );
225 }
226}
diff --git a/crates/completion/src/completions/snippet.rs b/crates/completion/src/completions/snippet.rs
new file mode 100644
index 000000000..6f0c00078
--- /dev/null
+++ b/crates/completion/src/completions/snippet.rs
@@ -0,0 +1,114 @@
1//! This file provides snippet completions, like `pd` => `eprintln!(...)`.
2
3use crate::{
4 config::SnippetCap, item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
5 CompletionKind, Completions,
6};
7
8fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
9 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label)
10 .insert_snippet(cap, snippet)
11 .kind(CompletionItemKind::Snippet)
12}
13
14pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
15 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
16 return;
17 }
18 let cap = match ctx.config.snippet_cap {
19 Some(it) => it,
20 None => return,
21 };
22
23 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
24 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
25}
26
27pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
28 if !ctx.is_new_item {
29 return;
30 }
31 let cap = match ctx.config.snippet_cap {
32 Some(it) => it,
33 None => return,
34 };
35
36 snippet(
37 ctx,
38 cap,
39 "tmod (Test module)",
40 "\
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn ${1:test_name}() {
47 $0
48 }
49}",
50 )
51 .lookup_by("tmod")
52 .add_to(acc);
53
54 snippet(
55 ctx,
56 cap,
57 "tfn (Test function)",
58 "\
59#[test]
60fn ${1:feature}() {
61 $0
62}",
63 )
64 .lookup_by("tfn")
65 .add_to(acc);
66
67 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc);
68}
69
70#[cfg(test)]
71mod tests {
72 use expect_test::{expect, Expect};
73
74 use crate::{test_utils::completion_list, CompletionKind};
75
76 fn check(ra_fixture: &str, expect: Expect) {
77 let actual = completion_list(ra_fixture, CompletionKind::Snippet);
78 expect.assert_eq(&actual)
79 }
80
81 #[test]
82 fn completes_snippets_in_expressions() {
83 check(
84 r#"fn foo(x: i32) { <|> }"#,
85 expect![[r#"
86 sn pd
87 sn ppd
88 "#]],
89 );
90 }
91
92 #[test]
93 fn should_not_complete_snippets_in_path() {
94 check(r#"fn foo(x: i32) { ::foo<|> }"#, expect![[""]]);
95 check(r#"fn foo(x: i32) { ::<|> }"#, expect![[""]]);
96 }
97
98 #[test]
99 fn completes_snippets_in_items() {
100 check(
101 r#"
102#[cfg(test)]
103mod tests {
104 <|>
105}
106"#,
107 expect![[r#"
108 sn macro_rules
109 sn tfn (Test function)
110 sn tmod (Test module)
111 "#]],
112 )
113 }
114}
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs
new file mode 100644
index 000000000..a14be9c73
--- /dev/null
+++ b/crates/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<|>
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() {}<|>
31//! }
32//! ```
33
34use hir::{self, HasAttrs, HasSource};
35use ide_db::traits::get_missing_assoc_items;
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 <|> }`, 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 <|> }`
94 // ERROR 0
95 // CONST_KW <- *
96 SyntaxKind::CONST_KW => 0,
97 // `impl .. { fn/type <|> }`
98 // FN/TYPE_ALIAS 0
99 // FN_KW <- *
100 SyntaxKind::FN_KW | SyntaxKind::TYPE_KW => 0,
101 // `impl .. { fn/type/const foo<|> }`
102 // FN/TYPE_ALIAS/CONST 1
103 // NAME 0
104 // IDENT <- *
105 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME => 1,
106 // `impl .. { foo<|> }`
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 <|> fn/type/const }`
124 _ if token.kind() == SyntaxKind::CONST_KW => 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.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::Function
156 };
157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
158
159 let function_decl = function_declaration(&func.source(ctx.db).value);
160 match ctx.config.snippet_cap {
161 Some(cap) => {
162 let snippet = format!("{} {{\n $0\n}}", function_decl);
163 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
164 }
165 None => {
166 let header = format!("{} {{", function_decl);
167 builder.text_edit(TextEdit::replace(range, header))
168 }
169 }
170 .kind(completion_kind)
171 .add_to(acc);
172}
173
174fn add_type_alias_impl(
175 type_def_node: &SyntaxNode,
176 acc: &mut Completions,
177 ctx: &CompletionContext,
178 type_alias: hir::TypeAlias,
179) {
180 let alias_name = type_alias.name(ctx.db).to_string();
181
182 let snippet = format!("type {} = ", alias_name);
183
184 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
185
186 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
187 .text_edit(TextEdit::replace(range, snippet))
188 .lookup_by(alias_name)
189 .kind(CompletionItemKind::TypeAlias)
190 .set_documentation(type_alias.docs(ctx.db))
191 .add_to(acc);
192}
193
194fn add_const_impl(
195 const_def_node: &SyntaxNode,
196 acc: &mut Completions,
197 ctx: &CompletionContext,
198 const_: hir::Const,
199) {
200 let const_name = const_.name(ctx.db).map(|n| n.to_string());
201
202 if let Some(const_name) = const_name {
203 let snippet = make_const_compl_syntax(&const_.source(ctx.db).value);
204
205 let range = TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
206
207 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
208 .text_edit(TextEdit::replace(range, snippet))
209 .lookup_by(const_name)
210 .kind(CompletionItemKind::Const)
211 .set_documentation(const_.docs(ctx.db))
212 .add_to(acc);
213 }
214}
215
216fn make_const_compl_syntax(const_: &ast::Const) -> String {
217 let const_ = edit::remove_attrs_and_docs(const_);
218
219 let const_start = const_.syntax().text_range().start();
220 let const_end = const_.syntax().text_range().end();
221
222 let start =
223 const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());
224
225 let end = const_
226 .syntax()
227 .children_with_tokens()
228 .find(|s| s.kind() == T![;] || s.kind() == T![=])
229 .map_or(const_end, |f| f.text_range().start());
230
231 let len = end - start;
232 let range = TextRange::new(0.into(), len);
233
234 let syntax = const_.syntax().text().slice(range).to_string();
235
236 format!("{} = ", syntax.trim_end())
237}
238
239#[cfg(test)]
240mod tests {
241 use expect_test::{expect, Expect};
242
243 use crate::{
244 test_utils::{check_edit, completion_list},
245 CompletionKind,
246 };
247
248 fn check(ra_fixture: &str, expect: Expect) {
249 let actual = completion_list(ra_fixture, CompletionKind::Magic);
250 expect.assert_eq(&actual)
251 }
252
253 #[test]
254 fn name_ref_function_type_const() {
255 check(
256 r#"
257trait Test {
258 type TestType;
259 const TEST_CONST: u16;
260 fn test();
261}
262struct T;
263
264impl Test for T {
265 t<|>
266}
267"#,
268 expect![["
269ct const TEST_CONST: u16 = \n\
270fn fn test()
271ta type TestType = \n\
272 "]],
273 );
274 }
275
276 #[test]
277 fn no_completion_inside_fn() {
278 check(
279 r"
280trait Test { fn test(); fn test2(); }
281struct T;
282
283impl Test for T {
284 fn test() {
285 t<|>
286 }
287}
288",
289 expect![[""]],
290 );
291
292 check(
293 r"
294trait Test { fn test(); fn test2(); }
295struct T;
296
297impl Test for T {
298 fn test() {
299 fn t<|>
300 }
301}
302",
303 expect![[""]],
304 );
305
306 check(
307 r"
308trait Test { fn test(); fn test2(); }
309struct T;
310
311impl Test for T {
312 fn test() {
313 fn <|>
314 }
315}
316",
317 expect![[""]],
318 );
319
320 // https://github.com/rust-analyzer/rust-analyzer/pull/5976#issuecomment-692332191
321 check(
322 r"
323trait Test { fn test(); fn test2(); }
324struct T;
325
326impl Test for T {
327 fn test() {
328 foo.<|>
329 }
330}
331",
332 expect![[""]],
333 );
334
335 check(
336 r"
337trait Test { fn test(_: i32); fn test2(); }
338struct T;
339
340impl Test for T {
341 fn test(t<|>)
342}
343",
344 expect![[""]],
345 );
346
347 check(
348 r"
349trait Test { fn test(_: fn()); fn test2(); }
350struct T;
351
352impl Test for T {
353 fn test(f: fn <|>)
354}
355",
356 expect![[""]],
357 );
358 }
359
360 #[test]
361 fn no_completion_inside_const() {
362 check(
363 r"
364trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
365struct T;
366
367impl Test for T {
368 const TEST: fn <|>
369}
370",
371 expect![[""]],
372 );
373
374 check(
375 r"
376trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
377struct T;
378
379impl Test for T {
380 const TEST: T<|>
381}
382",
383 expect![[""]],
384 );
385
386 check(
387 r"
388trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
389struct T;
390
391impl Test for T {
392 const TEST: u32 = f<|>
393}
394",
395 expect![[""]],
396 );
397
398 check(
399 r"
400trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
401struct T;
402
403impl Test for T {
404 const TEST: u32 = {
405 t<|>
406 };
407}
408",
409 expect![[""]],
410 );
411
412 check(
413 r"
414trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
415struct T;
416
417impl Test for T {
418 const TEST: u32 = {
419 fn <|>
420 };
421}
422",
423 expect![[""]],
424 );
425
426 check(
427 r"
428trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
429struct T;
430
431impl Test for T {
432 const TEST: u32 = {
433 fn t<|>
434 };
435}
436",
437 expect![[""]],
438 );
439 }
440
441 #[test]
442 fn no_completion_inside_type() {
443 check(
444 r"
445trait Test { type Test; type Test2; fn test(); }
446struct T;
447
448impl Test for T {
449 type Test = T<|>;
450}
451",
452 expect![[""]],
453 );
454
455 check(
456 r"
457trait Test { type Test; type Test2; fn test(); }
458struct T;
459
460impl Test for T {
461 type Test = fn <|>;
462}
463",
464 expect![[""]],
465 );
466 }
467
468 #[test]
469 fn name_ref_single_function() {
470 check_edit(
471 "test",
472 r#"
473trait Test {
474 fn test();
475}
476struct T;
477
478impl Test for T {
479 t<|>
480}
481"#,
482 r#"
483trait Test {
484 fn test();
485}
486struct T;
487
488impl Test for T {
489 fn test() {
490 $0
491}
492}
493"#,
494 );
495 }
496
497 #[test]
498 fn single_function() {
499 check_edit(
500 "test",
501 r#"
502trait Test {
503 fn test();
504}
505struct T;
506
507impl Test for T {
508 fn t<|>
509}
510"#,
511 r#"
512trait Test {
513 fn test();
514}
515struct T;
516
517impl Test for T {
518 fn test() {
519 $0
520}
521}
522"#,
523 );
524 }
525
526 #[test]
527 fn hide_implemented_fn() {
528 check(
529 r#"
530trait Test {
531 fn foo();
532 fn foo_bar();
533}
534struct T;
535
536impl Test for T {
537 fn foo() {}
538 fn f<|>
539}
540"#,
541 expect![[r#"
542 fn fn foo_bar()
543 "#]],
544 );
545 }
546
547 #[test]
548 fn generic_fn() {
549 check_edit(
550 "foo",
551 r#"
552trait Test {
553 fn foo<T>();
554}
555struct T;
556
557impl Test for T {
558 fn f<|>
559}
560"#,
561 r#"
562trait Test {
563 fn foo<T>();
564}
565struct T;
566
567impl Test for T {
568 fn foo<T>() {
569 $0
570}
571}
572"#,
573 );
574 check_edit(
575 "foo",
576 r#"
577trait Test {
578 fn foo<T>() where T: Into<String>;
579}
580struct T;
581
582impl Test for T {
583 fn f<|>
584}
585"#,
586 r#"
587trait Test {
588 fn foo<T>() where T: Into<String>;
589}
590struct T;
591
592impl Test for T {
593 fn foo<T>()
594where T: Into<String> {
595 $0
596}
597}
598"#,
599 );
600 }
601
602 #[test]
603 fn associated_type() {
604 check_edit(
605 "SomeType",
606 r#"
607trait Test {
608 type SomeType;
609}
610
611impl Test for () {
612 type S<|>
613}
614"#,
615 "
616trait Test {
617 type SomeType;
618}
619
620impl Test for () {
621 type SomeType = \n\
622}
623",
624 );
625 }
626
627 #[test]
628 fn associated_const() {
629 check_edit(
630 "SOME_CONST",
631 r#"
632trait Test {
633 const SOME_CONST: u16;
634}
635
636impl Test for () {
637 const S<|>
638}
639"#,
640 "
641trait Test {
642 const SOME_CONST: u16;
643}
644
645impl Test for () {
646 const SOME_CONST: u16 = \n\
647}
648",
649 );
650
651 check_edit(
652 "SOME_CONST",
653 r#"
654trait Test {
655 const SOME_CONST: u16 = 92;
656}
657
658impl Test for () {
659 const S<|>
660}
661"#,
662 "
663trait Test {
664 const SOME_CONST: u16 = 92;
665}
666
667impl Test for () {
668 const SOME_CONST: u16 = \n\
669}
670",
671 );
672 }
673
674 #[test]
675 fn complete_without_name() {
676 let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
677 println!(
678 "completion='{}', hint='{}', next_sibling='{}'",
679 completion, hint, next_sibling
680 );
681
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 <|> 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 <|>", "fn bar() {\n $0\n}", next_sibling);
732 test("Foo", "type <|>", "type Foo = ", next_sibling);
733 test("CONST", "const <|>", "const CONST: u16 = ", next_sibling);
734 }
735 }
736}
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
new file mode 100644
index 000000000..7df58e1da
--- /dev/null
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -0,0 +1,679 @@
1//! Completion of names from the current scope, e.g. locals and imported items.
2
3use hir::{Adt, ModuleDef, ScopeDef, Type};
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 complete_enum_variants(acc, ctx, ty);
23 }
24
25 if ctx.is_pat_binding_or_const {
26 return;
27 }
28
29 ctx.scope.process_all_names(&mut |name, res| {
30 if ctx.use_item_syntax.is_some() {
31 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
32 if name_ref.syntax().text() == name.to_string().as_str() {
33 mark::hit!(self_fulfilling_completion);
34 return;
35 }
36 }
37 }
38 acc.add_resolution(ctx, name.to_string(), &res)
39 });
40}
41
42fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
43 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
44 let variants = enum_data.variants(ctx.db);
45
46 let module = if let Some(module) = ctx.scope.module() {
47 // Compute path from the completion site if available.
48 module
49 } else {
50 // Otherwise fall back to the enum's definition site.
51 enum_data.module(ctx.db)
52 };
53
54 for variant in variants {
55 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
56 // Variants with trivial paths are already added by the existing completion logic,
57 // so we should avoid adding these twice
58 if path.segments.len() > 1 {
59 acc.add_qualified_enum_variant(ctx, variant, path);
60 }
61 }
62 }
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use expect_test::{expect, Expect};
69 use test_utils::mark;
70
71 use crate::{
72 test_utils::{check_edit, completion_list},
73 CompletionKind,
74 };
75
76 fn check(ra_fixture: &str, expect: Expect) {
77 let actual = completion_list(ra_fixture, CompletionKind::Reference);
78 expect.assert_eq(&actual)
79 }
80
81 #[test]
82 fn self_fulfilling_completion() {
83 mark::check!(self_fulfilling_completion);
84 check(
85 r#"
86use foo<|>
87use std::collections;
88"#,
89 expect![[r#"
90 ?? collections
91 "#]],
92 );
93 }
94
95 #[test]
96 fn bind_pat_and_path_ignore_at() {
97 check(
98 r#"
99enum Enum { A, B }
100fn quux(x: Option<Enum>) {
101 match x {
102 None => (),
103 Some(en<|> @ Enum::A) => (),
104 }
105}
106"#,
107 expect![[""]],
108 );
109 }
110
111 #[test]
112 fn bind_pat_and_path_ignore_ref() {
113 check(
114 r#"
115enum Enum { A, B }
116fn quux(x: Option<Enum>) {
117 match x {
118 None => (),
119 Some(ref en<|>) => (),
120 }
121}
122"#,
123 expect![[""]],
124 );
125 }
126
127 #[test]
128 fn bind_pat_and_path() {
129 check(
130 r#"
131enum Enum { A, B }
132fn quux(x: Option<Enum>) {
133 match x {
134 None => (),
135 Some(En<|>) => (),
136 }
137}
138"#,
139 expect![[r#"
140 en Enum
141 "#]],
142 );
143 }
144
145 #[test]
146 fn completes_bindings_from_let() {
147 check(
148 r#"
149fn quux(x: i32) {
150 let y = 92;
151 1 + <|>;
152 let z = ();
153}
154"#,
155 expect![[r#"
156 fn quux(…) fn quux(x: i32)
157 bn x i32
158 bn y i32
159 "#]],
160 );
161 }
162
163 #[test]
164 fn completes_bindings_from_if_let() {
165 check(
166 r#"
167fn quux() {
168 if let Some(x) = foo() {
169 let y = 92;
170 };
171 if let Some(a) = bar() {
172 let b = 62;
173 1 + <|>
174 }
175}
176"#,
177 expect![[r#"
178 bn a
179 bn b i32
180 fn quux() fn quux()
181 "#]],
182 );
183 }
184
185 #[test]
186 fn completes_bindings_from_for() {
187 check(
188 r#"
189fn quux() {
190 for x in &[1, 2, 3] { <|> }
191}
192"#,
193 expect![[r#"
194 fn quux() fn quux()
195 bn x
196 "#]],
197 );
198 }
199
200 #[test]
201 fn completes_if_prefix_is_keyword() {
202 mark::check!(completes_if_prefix_is_keyword);
203 check_edit(
204 "wherewolf",
205 r#"
206fn main() {
207 let wherewolf = 92;
208 drop(where<|>)
209}
210"#,
211 r#"
212fn main() {
213 let wherewolf = 92;
214 drop(wherewolf)
215}
216"#,
217 )
218 }
219
220 #[test]
221 fn completes_generic_params() {
222 check(
223 r#"fn quux<T>() { <|> }"#,
224 expect![[r#"
225 tp T
226 fn quux() fn quux<T>()
227 "#]],
228 );
229 }
230
231 #[test]
232 fn completes_generic_params_in_struct() {
233 check(
234 r#"struct S<T> { x: <|>}"#,
235 expect![[r#"
236 st S<…>
237 tp Self
238 tp T
239 "#]],
240 );
241 }
242
243 #[test]
244 fn completes_self_in_enum() {
245 check(
246 r#"enum X { Y(<|>) }"#,
247 expect![[r#"
248 tp Self
249 en X
250 "#]],
251 );
252 }
253
254 #[test]
255 fn completes_module_items() {
256 check(
257 r#"
258struct S;
259enum E {}
260fn quux() { <|> }
261"#,
262 expect![[r#"
263 en E
264 st S
265 fn quux() fn quux()
266 "#]],
267 );
268 }
269
270 /// Regression test for issue #6091.
271 #[test]
272 fn correctly_completes_module_items_prefixed_with_underscore() {
273 check_edit(
274 "_alpha",
275 r#"
276fn main() {
277 _<|>
278}
279fn _alpha() {}
280"#,
281 r#"
282fn main() {
283 _alpha()$0
284}
285fn _alpha() {}
286"#,
287 )
288 }
289
290 #[test]
291 fn completes_extern_prelude() {
292 check(
293 r#"
294//- /lib.rs crate:main deps:other_crate
295use <|>;
296
297//- /other_crate/lib.rs crate:other_crate
298// nothing here
299"#,
300 expect![[r#"
301 md other_crate
302 "#]],
303 );
304 }
305
306 #[test]
307 fn completes_module_items_in_nested_modules() {
308 check(
309 r#"
310struct Foo;
311mod m {
312 struct Bar;
313 fn quux() { <|> }
314}
315"#,
316 expect![[r#"
317 st Bar
318 fn quux() fn quux()
319 "#]],
320 );
321 }
322
323 #[test]
324 fn completes_return_type() {
325 check(
326 r#"
327struct Foo;
328fn x() -> <|>
329"#,
330 expect![[r#"
331 st Foo
332 fn x() fn x()
333 "#]],
334 );
335 }
336
337 #[test]
338 fn dont_show_both_completions_for_shadowing() {
339 check(
340 r#"
341fn foo() {
342 let bar = 92;
343 {
344 let bar = 62;
345 drop(<|>)
346 }
347}
348"#,
349 // FIXME: should be only one bar here
350 expect![[r#"
351 bn bar i32
352 bn bar i32
353 fn foo() fn foo()
354 "#]],
355 );
356 }
357
358 #[test]
359 fn completes_self_in_methods() {
360 check(
361 r#"impl S { fn foo(&self) { <|> } }"#,
362 expect![[r#"
363 tp Self
364 bn self &{unknown}
365 "#]],
366 );
367 }
368
369 #[test]
370 fn completes_prelude() {
371 check(
372 r#"
373//- /main.rs crate:main deps:std
374fn foo() { let x: <|> }
375
376//- /std/lib.rs crate:std
377#[prelude_import]
378use prelude::*;
379
380mod prelude { struct Option; }
381"#,
382 expect![[r#"
383 st Option
384 fn foo() fn foo()
385 md std
386 "#]],
387 );
388 }
389
390 #[test]
391 fn completes_std_prelude_if_core_is_defined() {
392 check(
393 r#"
394//- /main.rs crate:main deps:core,std
395fn foo() { let x: <|> }
396
397//- /core/lib.rs crate:core
398#[prelude_import]
399use prelude::*;
400
401mod prelude { struct Option; }
402
403//- /std/lib.rs crate:std deps:core
404#[prelude_import]
405use prelude::*;
406
407mod prelude { struct String; }
408"#,
409 expect![[r#"
410 st String
411 md core
412 fn foo() fn foo()
413 md std
414 "#]],
415 );
416 }
417
418 #[test]
419 fn completes_macros_as_value() {
420 check(
421 r#"
422macro_rules! foo { () => {} }
423
424#[macro_use]
425mod m1 {
426 macro_rules! bar { () => {} }
427}
428
429mod m2 {
430 macro_rules! nope { () => {} }
431
432 #[macro_export]
433 macro_rules! baz { () => {} }
434}
435
436fn main() { let v = <|> }
437"#,
438 expect![[r##"
439 ma bar!(…) macro_rules! bar
440 ma baz!(…) #[macro_export]
441 macro_rules! baz
442 ma foo!(…) macro_rules! foo
443 md m1
444 md m2
445 fn main() fn main()
446 "##]],
447 );
448 }
449
450 #[test]
451 fn completes_both_macro_and_value() {
452 check(
453 r#"
454macro_rules! foo { () => {} }
455fn foo() { <|> }
456"#,
457 expect![[r#"
458 ma foo!(…) macro_rules! foo
459 fn foo() fn foo()
460 "#]],
461 );
462 }
463
464 #[test]
465 fn completes_macros_as_type() {
466 check(
467 r#"
468macro_rules! foo { () => {} }
469fn main() { let x: <|> }
470"#,
471 expect![[r#"
472 ma foo!(…) macro_rules! foo
473 fn main() fn main()
474 "#]],
475 );
476 }
477
478 #[test]
479 fn completes_macros_as_stmt() {
480 check(
481 r#"
482macro_rules! foo { () => {} }
483fn main() { <|> }
484"#,
485 expect![[r#"
486 ma foo!(…) macro_rules! foo
487 fn main() fn main()
488 "#]],
489 );
490 }
491
492 #[test]
493 fn completes_local_item() {
494 check(
495 r#"
496fn main() {
497 return f<|>;
498 fn frobnicate() {}
499}
500"#,
501 expect![[r#"
502 fn frobnicate() fn frobnicate()
503 fn main() fn main()
504 "#]],
505 );
506 }
507
508 #[test]
509 fn completes_in_simple_macro_1() {
510 check(
511 r#"
512macro_rules! m { ($e:expr) => { $e } }
513fn quux(x: i32) {
514 let y = 92;
515 m!(<|>);
516}
517"#,
518 expect![[r#"
519 ma m!(…) macro_rules! m
520 fn quux(…) fn quux(x: i32)
521 bn x i32
522 bn y i32
523 "#]],
524 );
525 }
526
527 #[test]
528 fn completes_in_simple_macro_2() {
529 check(
530 r"
531macro_rules! m { ($e:expr) => { $e } }
532fn quux(x: i32) {
533 let y = 92;
534 m!(x<|>);
535}
536",
537 expect![[r#"
538 ma m!(…) macro_rules! m
539 fn quux(…) fn quux(x: i32)
540 bn x i32
541 bn y i32
542 "#]],
543 );
544 }
545
546 #[test]
547 fn completes_in_simple_macro_without_closing_parens() {
548 check(
549 r#"
550macro_rules! m { ($e:expr) => { $e } }
551fn quux(x: i32) {
552 let y = 92;
553 m!(x<|>
554}
555"#,
556 expect![[r#"
557 ma m!(…) macro_rules! m
558 fn quux(…) fn quux(x: i32)
559 bn x i32
560 bn y i32
561 "#]],
562 );
563 }
564
565 #[test]
566 fn completes_unresolved_uses() {
567 check(
568 r#"
569use spam::Quux;
570
571fn main() { <|> }
572"#,
573 expect![[r#"
574 ?? Quux
575 fn main() fn main()
576 "#]],
577 );
578 }
579 #[test]
580 fn completes_enum_variant_matcharm() {
581 check(
582 r#"
583enum Foo { Bar, Baz, Quux }
584
585fn main() {
586 let foo = Foo::Quux;
587 match foo { Qu<|> }
588}
589"#,
590 expect![[r#"
591 en Foo
592 ev Foo::Bar ()
593 ev Foo::Baz ()
594 ev Foo::Quux ()
595 "#]],
596 )
597 }
598
599 #[test]
600 fn completes_enum_variant_iflet() {
601 check(
602 r#"
603enum Foo { Bar, Baz, Quux }
604
605fn main() {
606 let foo = Foo::Quux;
607 if let Qu<|> = foo { }
608}
609"#,
610 expect![[r#"
611 en Foo
612 ev Foo::Bar ()
613 ev Foo::Baz ()
614 ev Foo::Quux ()
615 "#]],
616 )
617 }
618
619 #[test]
620 fn completes_enum_variant_basic_expr() {
621 check(
622 r#"
623enum Foo { Bar, Baz, Quux }
624fn main() { let foo: Foo = Q<|> }
625"#,
626 expect![[r#"
627 en Foo
628 ev Foo::Bar ()
629 ev Foo::Baz ()
630 ev Foo::Quux ()
631 fn main() fn main()
632 "#]],
633 )
634 }
635
636 #[test]
637 fn completes_enum_variant_from_module() {
638 check(
639 r#"
640mod m { pub enum E { V } }
641fn f() -> m::E { V<|> }
642"#,
643 expect![[r#"
644 fn f() fn f() -> m::E
645 md m
646 ev m::E::V ()
647 "#]],
648 )
649 }
650
651 #[test]
652 fn dont_complete_attr() {
653 check(
654 r#"
655struct Foo;
656#[<|>]
657fn f() {}
658"#,
659 expect![[""]],
660 )
661 }
662
663 #[test]
664 fn completes_type_or_trait_in_impl_block() {
665 check(
666 r#"
667trait MyTrait {}
668struct MyStruct {}
669
670impl My<|>
671"#,
672 expect![[r#"
673 st MyStruct
674 tt MyTrait
675 tp Self
676 "#]],
677 )
678 }
679}