aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/completion')
-rw-r--r--crates/ide/src/completion/complete_attribute.rs653
-rw-r--r--crates/ide/src/completion/complete_dot.rs416
-rw-r--r--crates/ide/src/completion/complete_fn_param.rs135
-rw-r--r--crates/ide/src/completion/complete_keyword.rs527
-rw-r--r--crates/ide/src/completion/complete_macro_in_item_position.rs41
-rw-r--r--crates/ide/src/completion/complete_pattern.rs88
-rw-r--r--crates/ide/src/completion/complete_postfix.rs378
-rw-r--r--crates/ide/src/completion/complete_qualified_path.rs733
-rw-r--r--crates/ide/src/completion/complete_record.rs226
-rw-r--r--crates/ide/src/completion/complete_snippet.rs116
-rw-r--r--crates/ide/src/completion/complete_trait_impl.rs488
-rw-r--r--crates/ide/src/completion/complete_unqualified_path.rs658
-rw-r--r--crates/ide/src/completion/completion_config.rs35
-rw-r--r--crates/ide/src/completion/completion_context.rs486
-rw-r--r--crates/ide/src/completion/completion_item.rs384
-rw-r--r--crates/ide/src/completion/generated_features.rs4
-rw-r--r--crates/ide/src/completion/patterns.rs194
-rw-r--r--crates/ide/src/completion/presentation.rs1227
-rw-r--r--crates/ide/src/completion/test_utils.rs114
19 files changed, 6903 insertions, 0 deletions
diff --git a/crates/ide/src/completion/complete_attribute.rs b/crates/ide/src/completion/complete_attribute.rs
new file mode 100644
index 000000000..0abfaebcb
--- /dev/null
+++ b/crates/ide/src/completion/complete_attribute.rs
@@ -0,0 +1,653 @@
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::completion::{
10 completion_context::CompletionContext,
11 completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions},
12 generated_features::FEATURES,
13};
14
15pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
16 let attribute = ctx.attribute_under_caret.as_ref()?;
17 match (attribute.path(), attribute.token_tree()) {
18 (Some(path), Some(token_tree)) if path.to_string() == "derive" => {
19 complete_derive(acc, ctx, token_tree)
20 }
21 (Some(path), Some(token_tree)) if path.to_string() == "feature" => {
22 complete_lint(acc, ctx, token_tree, FEATURES)
23 }
24 (Some(path), Some(token_tree))
25 if ["allow", "warn", "deny", "forbid"]
26 .iter()
27 .any(|lint_level| lint_level == &path.to_string()) =>
28 {
29 complete_lint(acc, ctx, token_tree, DEFAULT_LINT_COMPLETIONS)
30 }
31 (_, Some(_token_tree)) => {}
32 _ => complete_attribute_start(acc, ctx, attribute),
33 }
34 Some(())
35}
36
37fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
38 for attr_completion in ATTRIBUTES {
39 let mut item = CompletionItem::new(
40 CompletionKind::Attribute,
41 ctx.source_range(),
42 attr_completion.label,
43 )
44 .kind(CompletionItemKind::Attribute);
45
46 if let Some(lookup) = attr_completion.lookup {
47 item = item.lookup_by(lookup);
48 }
49
50 match (attr_completion.snippet, ctx.config.snippet_cap) {
51 (Some(snippet), Some(cap)) => {
52 item = item.insert_snippet(cap, snippet);
53 }
54 _ => {}
55 }
56
57 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
58 acc.add(item);
59 }
60 }
61}
62
63struct AttrCompletion {
64 label: &'static str,
65 lookup: Option<&'static str>,
66 snippet: Option<&'static str>,
67 prefer_inner: bool,
68}
69
70impl AttrCompletion {
71 const fn prefer_inner(self) -> AttrCompletion {
72 AttrCompletion { prefer_inner: true, ..self }
73 }
74}
75
76const fn attr(
77 label: &'static str,
78 lookup: Option<&'static str>,
79 snippet: Option<&'static str>,
80) -> AttrCompletion {
81 AttrCompletion { label, lookup, snippet, prefer_inner: false }
82}
83
84const ATTRIBUTES: &[AttrCompletion] = &[
85 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
86 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
87 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
88 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
89 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
90 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
91 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
92 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
93 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
94 // FIXME: resolve through macro resolution?
95 attr("global_allocator", None, None).prefer_inner(),
96 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
97 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
98 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
99 attr("link", None, None),
100 attr("macro_export", None, None),
101 attr("macro_use", None, None),
102 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
103 attr("no_mangle", None, None),
104 attr("no_std", None, None).prefer_inner(),
105 attr("non_exhaustive", None, None),
106 attr("panic_handler", None, None).prefer_inner(),
107 attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")),
108 attr("proc_macro", None, None),
109 attr("proc_macro_attribute", None, None),
110 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
111 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
112 .prefer_inner(),
113 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
114 attr(
115 "should_panic(…)",
116 Some("should_panic"),
117 Some(r#"should_panic(expected = "${0:reason}")"#),
118 ),
119 attr(
120 r#"target_feature = "…""#,
121 Some("target_feature"),
122 Some("target_feature = \"${0:feature}\""),
123 ),
124 attr("test", None, None),
125 attr("used", None, None),
126 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
127 attr(
128 r#"windows_subsystem = "…""#,
129 Some("windows_subsystem"),
130 Some(r#"windows_subsystem = "${0:subsystem}""#),
131 )
132 .prefer_inner(),
133];
134
135fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
136 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
137 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
138 .into_iter()
139 .filter(|completion| !existing_derives.contains(completion.label))
140 {
141 let mut label = derive_completion.label.to_owned();
142 for dependency in derive_completion
143 .dependencies
144 .into_iter()
145 .filter(|&&dependency| !existing_derives.contains(dependency))
146 {
147 label.push_str(", ");
148 label.push_str(dependency);
149 }
150 acc.add(
151 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
152 .kind(CompletionItemKind::Attribute),
153 );
154 }
155
156 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
157 acc.add(
158 CompletionItem::new(
159 CompletionKind::Attribute,
160 ctx.source_range(),
161 custom_derive_name,
162 )
163 .kind(CompletionItemKind::Attribute),
164 );
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 acc.add(
181 CompletionItem::new(
182 CompletionKind::Attribute,
183 ctx.source_range(),
184 lint_completion.label,
185 )
186 .kind(CompletionItemKind::Attribute)
187 .detail(lint_completion.description),
188 );
189 }
190 }
191}
192
193fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
194 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
195 (Some(left_paren), Some(right_paren))
196 if left_paren.kind() == SyntaxKind::L_PAREN
197 && right_paren.kind() == SyntaxKind::R_PAREN =>
198 {
199 let mut input_derives = FxHashSet::default();
200 let mut current_derive = String::new();
201 for token in derive_input
202 .syntax()
203 .children_with_tokens()
204 .filter_map(|token| token.into_token())
205 .skip_while(|token| token != &left_paren)
206 .skip(1)
207 .take_while(|token| token != &right_paren)
208 {
209 if SyntaxKind::COMMA == token.kind() {
210 if !current_derive.is_empty() {
211 input_derives.insert(current_derive);
212 current_derive = String::new();
213 }
214 } else {
215 current_derive.push_str(token.to_string().trim());
216 }
217 }
218
219 if !current_derive.is_empty() {
220 input_derives.insert(current_derive);
221 }
222 Ok(input_derives)
223 }
224 _ => Err(()),
225 }
226}
227
228fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
229 let mut result = FxHashSet::default();
230 ctx.scope.process_all_names(&mut |name, scope_def| {
231 if let hir::ScopeDef::MacroDef(mac) = scope_def {
232 if mac.is_derive_macro() {
233 result.insert(name.to_string());
234 }
235 }
236 });
237 result
238}
239
240struct DeriveCompletion {
241 label: &'static str,
242 dependencies: &'static [&'static str],
243}
244
245/// Standard Rust derives and the information about their dependencies
246/// (the dependencies are needed so that the main derive don't break the compilation when added)
247#[rustfmt::skip]
248const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
249 DeriveCompletion { label: "Clone", dependencies: &[] },
250 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
251 DeriveCompletion { label: "Debug", dependencies: &[] },
252 DeriveCompletion { label: "Default", dependencies: &[] },
253 DeriveCompletion { label: "Hash", dependencies: &[] },
254 DeriveCompletion { label: "PartialEq", dependencies: &[] },
255 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
256 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
257 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
258];
259
260pub(super) struct LintCompletion {
261 pub(super) label: &'static str,
262 pub(super) description: &'static str,
263}
264
265#[rustfmt::skip]
266const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
267 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"# },
268 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
269 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
270 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
271 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
272 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
273 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
274 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
275 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
276 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
277 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
278 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
279 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
280 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
281 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
282 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
283 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
284 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
285 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
286 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
287 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
288 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
289 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
290 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
291 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
292 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
293 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
294 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
295 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
296 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
297 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
298 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
299 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
300 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
301 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
302 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
303 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
304 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
305 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
306 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
307 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
308 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
309 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
310 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
311 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
312 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
313 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
314 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
315 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
316 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
317 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
318 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
319 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
320 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
321 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
322 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
323 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
324 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
325 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
326 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
327 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
328 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
329 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
330 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
331 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
332 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
333 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
334 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
335 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
336 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
337 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
338 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
339 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
340 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
341 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
342 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
343 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
344 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
345 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
346 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
347 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
348 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
349 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
350 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
351 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
352 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
353 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
354 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
355 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
356 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
357 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
358 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
359 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
360 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
361 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
362 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
363 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
364 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
365 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
366 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
367 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
368 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
369 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
370 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
371 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"# },
372 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
373 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
374 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
375 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
376 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
377 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
378 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
379 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
380 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
381 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
382];
383
384#[cfg(test)]
385mod tests {
386 use expect_test::{expect, Expect};
387
388 use crate::completion::{test_utils::completion_list, CompletionKind};
389
390 fn check(ra_fixture: &str, expect: Expect) {
391 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
392 expect.assert_eq(&actual);
393 }
394
395 #[test]
396 fn empty_derive_completion() {
397 check(
398 r#"
399#[derive(<|>)]
400struct Test {}
401 "#,
402 expect![[r#"
403 at Clone
404 at Copy, Clone
405 at Debug
406 at Default
407 at Eq, PartialEq
408 at Hash
409 at Ord, PartialOrd, Eq, PartialEq
410 at PartialEq
411 at PartialOrd, PartialEq
412 "#]],
413 );
414 }
415
416 #[test]
417 fn empty_lint_completion() {
418 check(
419 r#"#[allow(<|>)]"#,
420 expect![[r#"
421 at absolute_paths_not_starting_with_crate fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name
422 at ambiguous_associated_items ambiguous associated items
423 at anonymous_parameters detects anonymous parameters
424 at arithmetic_overflow arithmetic operation overflows
425 at array_into_iter detects calling `into_iter` on arrays
426 at asm_sub_register using only a subset of a register for inline asm inputs
427 at bare_trait_objects suggest using `dyn Trait` for trait objects
428 at bindings_with_variant_name detects pattern bindings with the same name as one of the matched variants
429 at box_pointers use of owned (Box type) heap memory
430 at cenum_impl_drop_cast a C-like enum implementing Drop is cast
431 at clashing_extern_declarations detects when an extern fn has been declared with the same name but different types
432 at coherence_leak_check distinct impls distinguished only by the leak-check code
433 at conflicting_repr_hints conflicts between `#[repr(..)]` hints that were previously accepted and used in practice
434 at confusable_idents detects visually confusable pairs between identifiers
435 at const_err constant evaluation detected erroneous expression
436 at dead_code detect unused, unexported items
437 at deprecated detects use of deprecated items
438 at deprecated_in_future detects use of items that will be deprecated in a future version
439 at elided_lifetimes_in_paths hidden lifetime parameters in types are deprecated
440 at ellipsis_inclusive_range_patterns `...` range patterns are deprecated
441 at explicit_outlives_requirements outlives requirements can be inferred
442 at exported_private_dependencies public interface leaks type from a private dependency
443 at ill_formed_attribute_input ill-formed attribute inputs that were previously accepted and used in practice
444 at illegal_floating_point_literal_pattern floating-point literals cannot be used in patterns
445 at improper_ctypes proper use of libc types in foreign modules
446 at improper_ctypes_definitions proper use of libc types in foreign item definitions
447 at incomplete_features incomplete features that may function improperly in some or all cases
448 at incomplete_include trailing content in included file
449 at indirect_structural_match pattern with const indirectly referencing non-structural-match type
450 at inline_no_sanitize detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`
451 at intra_doc_link_resolution_failure failures in resolving intra-doc link targets
452 at invalid_codeblock_attributes codeblock attribute looks a lot like a known one
453 at invalid_type_param_default type parameter default erroneously allowed in invalid location
454 at invalid_value an invalid value is being created (such as a NULL reference)
455 at irrefutable_let_patterns detects irrefutable patterns in if-let and while-let statements
456 at keyword_idents detects edition keywords being used as an identifier
457 at late_bound_lifetime_arguments detects generic lifetime arguments in path segments with late bound lifetime parameters
458 at macro_expanded_macro_exports_accessed_by_absolute_paths macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
459 at macro_use_extern_crate the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system
460 at meta_variable_misuse possible meta-variable misuse at macro definition
461 at missing_copy_implementations detects potentially-forgotten implementations of `Copy`
462 at missing_crate_level_docs detects crates with no crate-level documentation
463 at missing_debug_implementations detects missing implementations of Debug
464 at missing_doc_code_examples detects publicly-exported items without code samples in their documentation
465 at missing_docs detects missing documentation for public members
466 at missing_fragment_specifier detects missing fragment specifiers in unused `macro_rules!` patterns
467 at mixed_script_confusables detects Unicode scripts whose mixed script confusables codepoints are solely used
468 at mutable_borrow_reservation_conflict reservation of a two-phased borrow conflicts with other shared borrows
469 at mutable_transmutes mutating transmuted &mut T from &T may cause undefined behavior
470 at no_mangle_const_items const items will not have their symbols exported
471 at no_mangle_generic_items generic items must be mangled
472 at non_ascii_idents detects non-ASCII identifiers
473 at non_camel_case_types types, variants, traits and type parameters should have camel case names
474 at non_shorthand_field_patterns using `Struct { x: x }` instead of `Struct { x }` in a pattern
475 at non_snake_case variables, methods, functions, lifetime parameters and modules should have snake case names
476 at non_upper_case_globals static constants should have uppercase identifiers
477 at order_dependent_trait_objects trait-object types were treated as different depending on marker-trait order
478 at overflowing_literals literal out of range for its type
479 at overlapping_patterns detects overlapping patterns
480 at path_statements path statements with no effect
481 at patterns_in_fns_without_body patterns in functions without body were erroneously allowed
482 at private_doc_tests detects code samples in docs of private items not documented by rustdoc
483 at private_in_public detect private items in public interfaces not caught by the old implementation
484 at proc_macro_derive_resolution_fallback detects proc macro derives using inaccessible names from parent modules
485 at pub_use_of_private_extern_crate detect public re-exports of private extern crates
486 at redundant_semicolons detects unnecessary trailing semicolons
487 at renamed_and_removed_lints lints that have been renamed or removed
488 at safe_packed_borrows safe borrows of fields of packed structs were erroneously allowed
489 at single_use_lifetimes detects lifetime parameters that are only used once
490 at soft_unstable a feature gate that doesn't break dependent crates
491 at stable_features stable features found in `#[feature]` directive
492 at trivial_bounds these bounds don't depend on an type parameters
493 at trivial_casts detects trivial casts which could be removed
494 at trivial_numeric_casts detects trivial casts of numeric types which could be removed
495 at type_alias_bounds bounds in type aliases are not enforced
496 at tyvar_behind_raw_pointer raw pointer to an inference variable
497 at unaligned_references detects unaligned references to fields of packed structs
498 at uncommon_codepoints detects uncommon Unicode codepoints in identifiers
499 at unconditional_panic operation will cause a panic at runtime
500 at unconditional_recursion functions that cannot return without calling themselves
501 at unknown_crate_types unknown crate type found in `#[crate_type]` directive
502 at unknown_lints unrecognized lint attribute
503 at unnameable_test_items detects an item that cannot be named being marked as `#[test_case]`
504 at unreachable_code detects unreachable code paths
505 at unreachable_patterns detects unreachable patterns
506 at unreachable_pub `pub` items not reachable from crate root
507 at unsafe_code usage of `unsafe` code
508 at unsafe_op_in_unsafe_fn unsafe operations in unsafe functions without an explicit unsafe block are deprecated
509 at unstable_features enabling unstable features (deprecated. do not use)
510 at unstable_name_collisions detects name collision with an existing but unstable method
511 at unused_allocation detects unnecessary allocations that can be eliminated
512 at unused_assignments detect assignments that will never be read
513 at unused_attributes detects attributes that were not used by the compiler
514 at unused_braces unnecessary braces around an expression
515 at unused_comparisons comparisons made useless by limits of the types involved
516 at unused_crate_dependencies crate dependencies that are never used
517 at unused_doc_comments detects doc comments that aren't used by rustdoc
518 at unused_extern_crates extern crates that are never used
519 at unused_features unused features found in crate-level `#[feature]` directives
520 at unused_import_braces unnecessary braces around an imported item
521 at unused_imports imports that are never used
522 at unused_labels detects labels that are never used
523 at unused_lifetimes detects lifetime parameters that are never used
524 at unused_macros detects macros that were not used
525 at unused_must_use unused result of a type flagged as `#[must_use]`
526 at unused_mut detect mut variables which don't need to be mutable
527 at unused_parens `if`, `match`, `while` and `return` do not need parentheses
528 at unused_qualifications detects unnecessarily qualified names
529 at unused_results unused result of an expression in a statement
530 at unused_unsafe unnecessary use of an `unsafe` block
531 at unused_variables detect variables which are not used in any way
532 at variant_size_differences detects enums with widely varying variant sizes
533 at warnings mass-change the level for lints which produce warnings
534 at where_clauses_object_safety checks the object safety of where clauses
535 at while_true suggest using `loop { }` instead of `while true { }`
536 "#]],
537 )
538 }
539
540 #[test]
541 fn no_completion_for_incorrect_derive() {
542 check(
543 r#"
544#[derive{<|>)]
545struct Test {}
546"#,
547 expect![[r#""#]],
548 )
549 }
550
551 #[test]
552 fn derive_with_input_completion() {
553 check(
554 r#"
555#[derive(serde::Serialize, PartialEq, <|>)]
556struct Test {}
557"#,
558 expect![[r#"
559 at Clone
560 at Copy, Clone
561 at Debug
562 at Default
563 at Eq
564 at Hash
565 at Ord, PartialOrd, Eq
566 at PartialOrd
567 "#]],
568 )
569 }
570
571 #[test]
572 fn test_attribute_completion() {
573 check(
574 r#"#[<|>]"#,
575 expect![[r#"
576 at allow(…)
577 at cfg(…)
578 at cfg_attr(…)
579 at deny(…)
580 at deprecated = "…"
581 at derive(…)
582 at doc = "…"
583 at forbid(…)
584 at ignore = "…"
585 at inline(…)
586 at link
587 at link_name = "…"
588 at macro_export
589 at macro_use
590 at must_use = "…"
591 at no_mangle
592 at non_exhaustive
593 at path = "…"
594 at proc_macro
595 at proc_macro_attribute
596 at proc_macro_derive(…)
597 at repr(…)
598 at should_panic(…)
599 at target_feature = "…"
600 at test
601 at used
602 at warn(…)
603 "#]],
604 )
605 }
606
607 #[test]
608 fn test_attribute_completion_inside_nested_attr() {
609 check(r#"#[cfg(<|>)]"#, expect![[]])
610 }
611
612 #[test]
613 fn test_inner_attribute_completion() {
614 check(
615 r"#![<|>]",
616 expect![[r#"
617 at allow(…)
618 at cfg(…)
619 at cfg_attr(…)
620 at deny(…)
621 at deprecated = "…"
622 at derive(…)
623 at doc = "…"
624 at feature(…)
625 at forbid(…)
626 at global_allocator
627 at ignore = "…"
628 at inline(…)
629 at link
630 at link_name = "…"
631 at macro_export
632 at macro_use
633 at must_use = "…"
634 at no_mangle
635 at no_std
636 at non_exhaustive
637 at panic_handler
638 at path = "…"
639 at proc_macro
640 at proc_macro_attribute
641 at proc_macro_derive(…)
642 at recursion_limit = …
643 at repr(…)
644 at should_panic(…)
645 at target_feature = "…"
646 at test
647 at used
648 at warn(…)
649 at windows_subsystem = "…"
650 "#]],
651 );
652 }
653}
diff --git a/crates/ide/src/completion/complete_dot.rs b/crates/ide/src/completion/complete_dot.rs
new file mode 100644
index 000000000..0b9f1798a
--- /dev/null
+++ b/crates/ide/src/completion/complete_dot.rs
@@ -0,0 +1,416 @@
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::completion::{completion_context::CompletionContext, completion_item::Completions};
8
9/// Complete dot accesses, i.e. fields or methods.
10pub(super) 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::completion::{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(super) 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(super) fn the_method(&self) {}
163 }
164}
165fn foo(a: A) { a.<|> }
166"#,
167 expect![[r#"
168 me the_method() pub(super) 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}
diff --git a/crates/ide/src/completion/complete_fn_param.rs b/crates/ide/src/completion/complete_fn_param.rs
new file mode 100644
index 000000000..9efe25461
--- /dev/null
+++ b/crates/ide/src/completion/complete_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::completion::{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(super) 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::completion::{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/ide/src/completion/complete_keyword.rs b/crates/ide/src/completion/complete_keyword.rs
new file mode 100644
index 000000000..95e4ff1ac
--- /dev/null
+++ b/crates/ide/src/completion/complete_keyword.rs
@@ -0,0 +1,527 @@
1//! FIXME: write short doc here
2
3use syntax::{ast, SyntaxKind};
4use test_utils::mark;
5
6use crate::completion::{
7 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
8};
9
10pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
11 // complete keyword "crate" in use stmt
12 let source_range = ctx.source_range();
13
14 if ctx.use_item_syntax.is_some() {
15 if ctx.path_qual.is_none() {
16 CompletionItem::new(CompletionKind::Keyword, source_range, "crate::")
17 .kind(CompletionItemKind::Keyword)
18 .insert_text("crate::")
19 .add_to(acc);
20 }
21 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
22 .kind(CompletionItemKind::Keyword)
23 .add_to(acc);
24 CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
25 .kind(CompletionItemKind::Keyword)
26 .insert_text("super::")
27 .add_to(acc);
28 }
29
30 // Suggest .await syntax for types that implement Future trait
31 if let Some(receiver) = &ctx.dot_receiver {
32 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
33 if ty.impls_future(ctx.db) {
34 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
35 .kind(CompletionItemKind::Keyword)
36 .detail("expr.await")
37 .insert_text("await")
38 .add_to(acc);
39 }
40 };
41 }
42}
43
44pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
45 if ctx.token.kind() == SyntaxKind::COMMENT {
46 mark::hit!(no_keyword_completion_in_comments);
47 return;
48 }
49
50 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
51 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
52 add_keyword(ctx, acc, "where", "where ");
53 return;
54 }
55 if ctx.unsafe_is_prev {
56 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
57 add_keyword(ctx, acc, "fn", "fn $0() {}")
58 }
59
60 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
61 add_keyword(ctx, acc, "trait", "trait $0 {}");
62 add_keyword(ctx, acc, "impl", "impl $0 {}");
63 }
64
65 return;
66 }
67 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
68 {
69 add_keyword(ctx, acc, "fn", "fn $0() {}");
70 }
71 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
72 add_keyword(ctx, acc, "use", "use ");
73 add_keyword(ctx, acc, "impl", "impl $0 {}");
74 add_keyword(ctx, acc, "trait", "trait $0 {}");
75 }
76
77 if ctx.has_item_list_or_source_file_parent {
78 add_keyword(ctx, acc, "enum", "enum $0 {}");
79 add_keyword(ctx, acc, "struct", "struct $0");
80 add_keyword(ctx, acc, "union", "union $0 {}");
81 }
82
83 if ctx.is_expr {
84 add_keyword(ctx, acc, "match", "match $0 {}");
85 add_keyword(ctx, acc, "while", "while $0 {}");
86 add_keyword(ctx, acc, "loop", "loop {$0}");
87 add_keyword(ctx, acc, "if", "if ");
88 add_keyword(ctx, acc, "if let", "if let ");
89 }
90
91 if ctx.if_is_prev || ctx.block_expr_parent {
92 add_keyword(ctx, acc, "let", "let ");
93 }
94
95 if ctx.after_if {
96 add_keyword(ctx, acc, "else", "else {$0}");
97 add_keyword(ctx, acc, "else if", "else if $0 {}");
98 }
99 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
100 add_keyword(ctx, acc, "mod", "mod $0 {}");
101 }
102 if ctx.bind_pat_parent || ctx.ref_pat_parent {
103 add_keyword(ctx, acc, "mut", "mut ");
104 }
105 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
106 {
107 add_keyword(ctx, acc, "const", "const ");
108 add_keyword(ctx, acc, "type", "type ");
109 }
110 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
111 add_keyword(ctx, acc, "static", "static ");
112 };
113 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
114 add_keyword(ctx, acc, "extern", "extern ");
115 }
116 if ctx.has_item_list_or_source_file_parent
117 || has_trait_or_impl_parent
118 || ctx.block_expr_parent
119 || ctx.is_match_arm
120 {
121 add_keyword(ctx, acc, "unsafe", "unsafe ");
122 }
123 if ctx.in_loop_body {
124 if ctx.can_be_stmt {
125 add_keyword(ctx, acc, "continue", "continue;");
126 add_keyword(ctx, acc, "break", "break;");
127 } else {
128 add_keyword(ctx, acc, "continue", "continue");
129 add_keyword(ctx, acc, "break", "break");
130 }
131 }
132 if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent {
133 add_keyword(ctx, acc, "pub", "pub ")
134 }
135
136 if !ctx.is_trivial_path {
137 return;
138 }
139 let fn_def = match &ctx.function_syntax {
140 Some(it) => it,
141 None => return,
142 };
143 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
144}
145
146fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
147 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
148 .kind(CompletionItemKind::Keyword);
149
150 match ctx.config.snippet_cap {
151 Some(cap) => res.insert_snippet(cap, snippet),
152 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
153 }
154 .build()
155}
156
157fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
158 acc.add(keyword(ctx, kw, snippet));
159}
160
161fn complete_return(
162 ctx: &CompletionContext,
163 fn_def: &ast::Fn,
164 can_be_stmt: bool,
165) -> Option<CompletionItem> {
166 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
167 (true, true) => "return $0;",
168 (true, false) => "return;",
169 (false, true) => "return $0",
170 (false, false) => "return",
171 };
172 Some(keyword(ctx, "return", snip))
173}
174
175#[cfg(test)]
176mod tests {
177 use expect_test::{expect, Expect};
178
179 use crate::completion::{
180 test_utils::{check_edit, completion_list},
181 CompletionKind,
182 };
183 use test_utils::mark;
184
185 fn check(ra_fixture: &str, expect: Expect) {
186 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
187 expect.assert_eq(&actual)
188 }
189
190 #[test]
191 fn test_keywords_in_use_stmt() {
192 check(
193 r"use <|>",
194 expect![[r#"
195 kw crate::
196 kw self
197 kw super::
198 "#]],
199 );
200
201 check(
202 r"use a::<|>",
203 expect![[r#"
204 kw self
205 kw super::
206 "#]],
207 );
208
209 check(
210 r"use a::{b, <|>}",
211 expect![[r#"
212 kw self
213 kw super::
214 "#]],
215 );
216 }
217
218 #[test]
219 fn test_keywords_at_source_file_level() {
220 check(
221 r"m<|>",
222 expect![[r#"
223 kw const
224 kw enum
225 kw extern
226 kw fn
227 kw impl
228 kw mod
229 kw pub
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 type
368 kw unsafe
369 "#]],
370 );
371 }
372
373 #[test]
374 fn test_keywords_in_loop() {
375 check(
376 r"fn my() { loop { <|> } }",
377 expect![[r#"
378 kw break
379 kw const
380 kw continue
381 kw extern
382 kw fn
383 kw if
384 kw if let
385 kw impl
386 kw let
387 kw loop
388 kw match
389 kw mod
390 kw return
391 kw static
392 kw trait
393 kw type
394 kw unsafe
395 kw use
396 kw while
397 "#]],
398 );
399 }
400
401 #[test]
402 fn test_keywords_after_unsafe_in_item_list() {
403 check(
404 r"unsafe <|>",
405 expect![[r#"
406 kw fn
407 kw impl
408 kw trait
409 "#]],
410 );
411 }
412
413 #[test]
414 fn test_keywords_after_unsafe_in_block_expr() {
415 check(
416 r"fn my_fn() { unsafe <|> }",
417 expect![[r#"
418 kw fn
419 kw impl
420 kw trait
421 "#]],
422 );
423 }
424
425 #[test]
426 fn test_mut_in_ref_and_in_fn_parameters_list() {
427 check(
428 r"fn my_fn(&<|>) {}",
429 expect![[r#"
430 kw mut
431 "#]],
432 );
433 check(
434 r"fn my_fn(<|>) {}",
435 expect![[r#"
436 kw mut
437 "#]],
438 );
439 check(
440 r"fn my_fn() { let &<|> }",
441 expect![[r#"
442 kw mut
443 "#]],
444 );
445 }
446
447 #[test]
448 fn test_where_keyword() {
449 check(
450 r"trait A <|>",
451 expect![[r#"
452 kw where
453 "#]],
454 );
455 check(
456 r"impl A <|>",
457 expect![[r#"
458 kw where
459 "#]],
460 );
461 }
462
463 #[test]
464 fn no_keyword_completion_in_comments() {
465 mark::check!(no_keyword_completion_in_comments);
466 check(
467 r#"
468fn test() {
469 let x = 2; // A comment<|>
470}
471"#,
472 expect![[""]],
473 );
474 check(
475 r#"
476/*
477Some multi-line comment<|>
478*/
479"#,
480 expect![[""]],
481 );
482 check(
483 r#"
484/// Some doc comment
485/// let test<|> = 1
486"#,
487 expect![[""]],
488 );
489 }
490
491 #[test]
492 fn test_completion_await_impls_future() {
493 check(
494 r#"
495//- /main.rs
496use std::future::*;
497struct A {}
498impl Future for A {}
499fn foo(a: A) { a.<|> }
500
501//- /std/lib.rs
502pub mod future {
503 #[lang = "future_trait"]
504 pub trait Future {}
505}
506"#,
507 expect![[r#"
508 kw await expr.await
509 "#]],
510 )
511 }
512
513 #[test]
514 fn after_let() {
515 check(
516 r#"fn main() { let _ = <|> }"#,
517 expect![[r#"
518 kw if
519 kw if let
520 kw loop
521 kw match
522 kw return
523 kw while
524 "#]],
525 )
526 }
527}
diff --git a/crates/ide/src/completion/complete_macro_in_item_position.rs b/crates/ide/src/completion/complete_macro_in_item_position.rs
new file mode 100644
index 000000000..fc8625d8e
--- /dev/null
+++ b/crates/ide/src/completion/complete_macro_in_item_position.rs
@@ -0,0 +1,41 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5pub(super) 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::completion::{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/ide/src/completion/complete_pattern.rs b/crates/ide/src/completion/complete_pattern.rs
new file mode 100644
index 000000000..5a13574d4
--- /dev/null
+++ b/crates/ide/src/completion/complete_pattern.rs
@@ -0,0 +1,88 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5/// Completes constats and paths in patterns.
6pub(super) 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::completion::{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/ide/src/completion/complete_postfix.rs b/crates/ide/src/completion/complete_postfix.rs
new file mode 100644
index 000000000..84c4e129d
--- /dev/null
+++ b/crates/ide/src/completion/complete_postfix.rs
@@ -0,0 +1,378 @@
1//! FIXME: write short doc here
2use assists::utils::TryEnum;
3use syntax::{
4 ast::{self, AstNode},
5 TextRange, TextSize,
6};
7use text_edit::TextEdit;
8
9use crate::{
10 completion::{
11 completion_config::SnippetCap,
12 completion_context::CompletionContext,
13 completion_item::{Builder, CompletionKind, Completions},
14 },
15 CompletionItem, CompletionItemKind,
16};
17
18pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
19 if !ctx.config.enable_postfix_completions {
20 return;
21 }
22
23 let dot_receiver = match &ctx.dot_receiver {
24 Some(it) => it,
25 None => return,
26 };
27
28 let receiver_text =
29 get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
30
31 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
32 Some(it) => it,
33 None => return,
34 };
35
36 let cap = match ctx.config.snippet_cap {
37 Some(it) => it,
38 None => return,
39 };
40 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty);
41 if let Some(try_enum) = &try_enum {
42 match try_enum {
43 TryEnum::Result => {
44 postfix_snippet(
45 ctx,
46 cap,
47 &dot_receiver,
48 "ifl",
49 "if let Ok {}",
50 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text),
51 )
52 .add_to(acc);
53
54 postfix_snippet(
55 ctx,
56 cap,
57 &dot_receiver,
58 "while",
59 "while let Ok {}",
60 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text),
61 )
62 .add_to(acc);
63 }
64 TryEnum::Option => {
65 postfix_snippet(
66 ctx,
67 cap,
68 &dot_receiver,
69 "ifl",
70 "if let Some {}",
71 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text),
72 )
73 .add_to(acc);
74
75 postfix_snippet(
76 ctx,
77 cap,
78 &dot_receiver,
79 "while",
80 "while let Some {}",
81 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text),
82 )
83 .add_to(acc);
84 }
85 }
86 } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
87 postfix_snippet(
88 ctx,
89 cap,
90 &dot_receiver,
91 "if",
92 "if expr {}",
93 &format!("if {} {{\n $0\n}}", receiver_text),
94 )
95 .add_to(acc);
96 postfix_snippet(
97 ctx,
98 cap,
99 &dot_receiver,
100 "while",
101 "while expr {}",
102 &format!("while {} {{\n $0\n}}", receiver_text),
103 )
104 .add_to(acc);
105 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
106 .add_to(acc);
107 }
108
109 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
110 .add_to(acc);
111 postfix_snippet(
112 ctx,
113 cap,
114 &dot_receiver,
115 "refm",
116 "&mut expr",
117 &format!("&mut {}", receiver_text),
118 )
119 .add_to(acc);
120
121 // The rest of the postfix completions create an expression that moves an argument,
122 // so it's better to consider references now to avoid breaking the compilation
123 let dot_receiver = include_references(dot_receiver);
124 let receiver_text =
125 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
126
127 match try_enum {
128 Some(try_enum) => match try_enum {
129 TryEnum::Result => {
130 postfix_snippet(
131 ctx,
132 cap,
133 &dot_receiver,
134 "match",
135 "match expr {}",
136 &format!("match {} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
137 )
138 .add_to(acc);
139 }
140 TryEnum::Option => {
141 postfix_snippet(
142 ctx,
143 cap,
144 &dot_receiver,
145 "match",
146 "match expr {}",
147 &format!(
148 "match {} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}",
149 receiver_text
150 ),
151 )
152 .add_to(acc);
153 }
154 },
155 None => {
156 postfix_snippet(
157 ctx,
158 cap,
159 &dot_receiver,
160 "match",
161 "match expr {}",
162 &format!("match {} {{\n ${{1:_}} => {{$0}},\n}}", receiver_text),
163 )
164 .add_to(acc);
165 }
166 }
167
168 postfix_snippet(
169 ctx,
170 cap,
171 &dot_receiver,
172 "box",
173 "Box::new(expr)",
174 &format!("Box::new({})", receiver_text),
175 )
176 .add_to(acc);
177
178 postfix_snippet(
179 ctx,
180 cap,
181 &dot_receiver,
182 "dbg",
183 "dbg!(expr)",
184 &format!("dbg!({})", receiver_text),
185 )
186 .add_to(acc);
187
188 postfix_snippet(
189 ctx,
190 cap,
191 &dot_receiver,
192 "call",
193 "function(expr)",
194 &format!("${{1}}({})", receiver_text),
195 )
196 .add_to(acc);
197}
198
199fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
200 if receiver_is_ambiguous_float_literal {
201 let text = receiver.syntax().text();
202 let without_dot = ..text.len() - TextSize::of('.');
203 text.slice(without_dot).to_string()
204 } else {
205 receiver.to_string()
206 }
207}
208
209fn include_references(initial_element: &ast::Expr) -> ast::Expr {
210 let mut resulting_element = initial_element.clone();
211 while let Some(parent_ref_element) =
212 resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
213 {
214 resulting_element = ast::Expr::from(parent_ref_element);
215 }
216 resulting_element
217}
218
219fn postfix_snippet(
220 ctx: &CompletionContext,
221 cap: SnippetCap,
222 receiver: &ast::Expr,
223 label: &str,
224 detail: &str,
225 snippet: &str,
226) -> Builder {
227 let edit = {
228 let receiver_syntax = receiver.syntax();
229 let receiver_range = ctx.sema.original_range(receiver_syntax).range;
230 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
231 TextEdit::replace(delete_range, snippet.to_string())
232 };
233 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
234 .detail(detail)
235 .kind(CompletionItemKind::Snippet)
236 .snippet_edit(cap, edit)
237}
238
239#[cfg(test)]
240mod tests {
241 use expect_test::{expect, Expect};
242
243 use crate::completion::{
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::Postfix);
250 expect.assert_eq(&actual)
251 }
252
253 #[test]
254 fn postfix_completion_works_for_trivial_path_expression() {
255 check(
256 r#"
257fn main() {
258 let bar = true;
259 bar.<|>
260}
261"#,
262 expect![[r#"
263 sn box Box::new(expr)
264 sn call function(expr)
265 sn dbg dbg!(expr)
266 sn if if expr {}
267 sn match match expr {}
268 sn not !expr
269 sn ref &expr
270 sn refm &mut expr
271 sn while while expr {}
272 "#]],
273 );
274 }
275
276 #[test]
277 fn postfix_type_filtering() {
278 check(
279 r#"
280fn main() {
281 let bar: u8 = 12;
282 bar.<|>
283}
284"#,
285 expect![[r#"
286 sn box Box::new(expr)
287 sn call function(expr)
288 sn dbg dbg!(expr)
289 sn match match expr {}
290 sn ref &expr
291 sn refm &mut expr
292 "#]],
293 )
294 }
295
296 #[test]
297 fn option_iflet() {
298 check_edit(
299 "ifl",
300 r#"
301enum Option<T> { Some(T), None }
302
303fn main() {
304 let bar = Option::Some(true);
305 bar.<|>
306}
307"#,
308 r#"
309enum Option<T> { Some(T), None }
310
311fn main() {
312 let bar = Option::Some(true);
313 if let Some($1) = bar {
314 $0
315}
316}
317"#,
318 );
319 }
320
321 #[test]
322 fn result_match() {
323 check_edit(
324 "match",
325 r#"
326enum Result<T, E> { Ok(T), Err(E) }
327
328fn main() {
329 let bar = Result::Ok(true);
330 bar.<|>
331}
332"#,
333 r#"
334enum Result<T, E> { Ok(T), Err(E) }
335
336fn main() {
337 let bar = Result::Ok(true);
338 match bar {
339 Ok(${1:_}) => {$2},
340 Err(${3:_}) => {$0},
341}
342}
343"#,
344 );
345 }
346
347 #[test]
348 fn postfix_completion_works_for_ambiguous_float_literal() {
349 check_edit("refm", r#"fn main() { 42.<|> }"#, r#"fn main() { &mut 42 }"#)
350 }
351
352 #[test]
353 fn works_in_simple_macro() {
354 check_edit(
355 "dbg",
356 r#"
357macro_rules! m { ($e:expr) => { $e } }
358fn main() {
359 let bar: u8 = 12;
360 m!(bar.d<|>)
361}
362"#,
363 r#"
364macro_rules! m { ($e:expr) => { $e } }
365fn main() {
366 let bar: u8 = 12;
367 m!(dbg!(bar))
368}
369"#,
370 );
371 }
372
373 #[test]
374 fn postfix_completion_for_references() {
375 check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#);
376 check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#);
377 }
378}
diff --git a/crates/ide/src/completion/complete_qualified_path.rs b/crates/ide/src/completion/complete_qualified_path.rs
new file mode 100644
index 000000000..accb09f7e
--- /dev/null
+++ b/crates/ide/src/completion/complete_qualified_path.rs
@@ -0,0 +1,733 @@
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::completion::{CompletionContext, Completions};
9
10pub(super) 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() {
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::completion::{
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(super) fn public_method() { }
373 fn private_method() { }
374 pub(super) type PublicType = u32;
375 type PrivateType = u32;
376 pub(super) 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(super) const PUBLIC_CONST: u32 = 1;
385 ta PublicType pub(super) type PublicType = u32;
386 fn public_method() pub(super) 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
426use foo::<|>;
427
428//- /foo/lib.rs
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}
diff --git a/crates/ide/src/completion/complete_record.rs b/crates/ide/src/completion/complete_record.rs
new file mode 100644
index 000000000..ceb8d16c1
--- /dev/null
+++ b/crates/ide/src/completion/complete_record.rs
@@ -0,0 +1,226 @@
1//! Complete fields in record literals and patterns.
2use crate::completion::{CompletionContext, Completions};
3
4pub(super) 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::completion::{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/ide/src/completion/complete_snippet.rs b/crates/ide/src/completion/complete_snippet.rs
new file mode 100644
index 000000000..c3b03b199
--- /dev/null
+++ b/crates/ide/src/completion/complete_snippet.rs
@@ -0,0 +1,116 @@
1//! FIXME: write short doc here
2
3use crate::completion::{
4 completion_config::SnippetCap, completion_item::Builder, CompletionContext, CompletionItem,
5 CompletionItemKind, 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(super) 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(super) 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 snippet(ctx, cap, "pub(crate)", "pub(crate) $0").add_to(acc);
69}
70
71#[cfg(test)]
72mod tests {
73 use expect_test::{expect, Expect};
74
75 use crate::completion::{test_utils::completion_list, CompletionKind};
76
77 fn check(ra_fixture: &str, expect: Expect) {
78 let actual = completion_list(ra_fixture, CompletionKind::Snippet);
79 expect.assert_eq(&actual)
80 }
81
82 #[test]
83 fn completes_snippets_in_expressions() {
84 check(
85 r#"fn foo(x: i32) { <|> }"#,
86 expect![[r#"
87 sn pd
88 sn ppd
89 "#]],
90 );
91 }
92
93 #[test]
94 fn should_not_complete_snippets_in_path() {
95 check(r#"fn foo(x: i32) { ::foo<|> }"#, expect![[""]]);
96 check(r#"fn foo(x: i32) { ::<|> }"#, expect![[""]]);
97 }
98
99 #[test]
100 fn completes_snippets_in_items() {
101 check(
102 r#"
103#[cfg(test)]
104mod tests {
105 <|>
106}
107"#,
108 expect![[r#"
109 sn macro_rules
110 sn pub(crate)
111 sn tfn (Test function)
112 sn tmod (Test module)
113 "#]],
114 )
115 }
116}
diff --git a/crates/ide/src/completion/complete_trait_impl.rs b/crates/ide/src/completion/complete_trait_impl.rs
new file mode 100644
index 000000000..1a2b1e8a5
--- /dev/null
+++ b/crates/ide/src/completion/complete_trait_impl.rs
@@ -0,0 +1,488 @@
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 assists::utils::get_missing_assoc_items;
35use hir::{self, Docs, HasSource};
36use syntax::{
37 ast::{self, edit, Impl},
38 AstNode, SyntaxKind, SyntaxNode, TextRange, T,
39};
40use text_edit::TextEdit;
41
42use crate::{
43 completion::{
44 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
45 },
46 display::function_declaration,
47};
48
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
50 if let Some((trigger, impl_def)) = completion_match(ctx) {
51 match trigger.kind() {
52 SyntaxKind::NAME_REF => get_missing_assoc_items(&ctx.sema, &impl_def)
53 .into_iter()
54 .for_each(|item| match item {
55 hir::AssocItem::Function(fn_item) => {
56 add_function_impl(&trigger, acc, ctx, fn_item)
57 }
58 hir::AssocItem::TypeAlias(type_item) => {
59 add_type_alias_impl(&trigger, acc, ctx, type_item)
60 }
61 hir::AssocItem::Const(const_item) => {
62 add_const_impl(&trigger, acc, ctx, const_item)
63 }
64 }),
65
66 SyntaxKind::FN => {
67 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
68 .into_iter()
69 .filter_map(|item| match item {
70 hir::AssocItem::Function(fn_item) => Some(fn_item),
71 _ => None,
72 })
73 {
74 add_function_impl(&trigger, acc, ctx, missing_fn);
75 }
76 }
77
78 SyntaxKind::TYPE_ALIAS => {
79 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
80 .into_iter()
81 .filter_map(|item| match item {
82 hir::AssocItem::TypeAlias(type_item) => Some(type_item),
83 _ => None,
84 })
85 {
86 add_type_alias_impl(&trigger, acc, ctx, missing_fn);
87 }
88 }
89
90 SyntaxKind::CONST => {
91 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
92 .into_iter()
93 .filter_map(|item| match item {
94 hir::AssocItem::Const(const_item) => Some(const_item),
95 _ => None,
96 })
97 {
98 add_const_impl(&trigger, acc, ctx, missing_fn);
99 }
100 }
101
102 _ => {}
103 }
104 }
105}
106
107fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, Impl)> {
108 let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() {
109 SyntaxKind::FN | SyntaxKind::TYPE_ALIAS | SyntaxKind::CONST | SyntaxKind::BLOCK_EXPR => {
110 Some((p, 2))
111 }
112 SyntaxKind::NAME_REF => Some((p, 5)),
113 _ => None,
114 })?;
115 let impl_def = (0..impl_def_offset - 1)
116 .try_fold(trigger.parent()?, |t, _| t.parent())
117 .and_then(ast::Impl::cast)?;
118 Some((trigger, impl_def))
119}
120
121fn add_function_impl(
122 fn_def_node: &SyntaxNode,
123 acc: &mut Completions,
124 ctx: &CompletionContext,
125 func: hir::Function,
126) {
127 let fn_name = func.name(ctx.db).to_string();
128
129 let label = if func.params(ctx.db).is_empty() {
130 format!("fn {}()", fn_name)
131 } else {
132 format!("fn {}(..)", fn_name)
133 };
134
135 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
136 .lookup_by(fn_name)
137 .set_documentation(func.docs(ctx.db));
138
139 let completion_kind = if func.self_param(ctx.db).is_some() {
140 CompletionItemKind::Method
141 } else {
142 CompletionItemKind::Function
143 };
144 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
145
146 let function_decl = function_declaration(&func.source(ctx.db).value);
147 match ctx.config.snippet_cap {
148 Some(cap) => {
149 let snippet = format!("{} {{\n $0\n}}", function_decl);
150 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
151 }
152 None => {
153 let header = format!("{} {{", function_decl);
154 builder.text_edit(TextEdit::replace(range, header))
155 }
156 }
157 .kind(completion_kind)
158 .add_to(acc);
159}
160
161fn add_type_alias_impl(
162 type_def_node: &SyntaxNode,
163 acc: &mut Completions,
164 ctx: &CompletionContext,
165 type_alias: hir::TypeAlias,
166) {
167 let alias_name = type_alias.name(ctx.db).to_string();
168
169 let snippet = format!("type {} = ", alias_name);
170
171 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
172
173 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
174 .text_edit(TextEdit::replace(range, snippet))
175 .lookup_by(alias_name)
176 .kind(CompletionItemKind::TypeAlias)
177 .set_documentation(type_alias.docs(ctx.db))
178 .add_to(acc);
179}
180
181fn add_const_impl(
182 const_def_node: &SyntaxNode,
183 acc: &mut Completions,
184 ctx: &CompletionContext,
185 const_: hir::Const,
186) {
187 let const_name = const_.name(ctx.db).map(|n| n.to_string());
188
189 if let Some(const_name) = const_name {
190 let snippet = make_const_compl_syntax(&const_.source(ctx.db).value);
191
192 let range = TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
193
194 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
195 .text_edit(TextEdit::replace(range, snippet))
196 .lookup_by(const_name)
197 .kind(CompletionItemKind::Const)
198 .set_documentation(const_.docs(ctx.db))
199 .add_to(acc);
200 }
201}
202
203fn make_const_compl_syntax(const_: &ast::Const) -> String {
204 let const_ = edit::remove_attrs_and_docs(const_);
205
206 let const_start = const_.syntax().text_range().start();
207 let const_end = const_.syntax().text_range().end();
208
209 let start =
210 const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());
211
212 let end = const_
213 .syntax()
214 .children_with_tokens()
215 .find(|s| s.kind() == T![;] || s.kind() == T![=])
216 .map_or(const_end, |f| f.text_range().start());
217
218 let len = end - start;
219 let range = TextRange::new(0.into(), len);
220
221 let syntax = const_.syntax().text().slice(range).to_string();
222
223 format!("{} = ", syntax.trim_end())
224}
225
226#[cfg(test)]
227mod tests {
228 use expect_test::{expect, Expect};
229
230 use crate::completion::{
231 test_utils::{check_edit, completion_list},
232 CompletionKind,
233 };
234
235 fn check(ra_fixture: &str, expect: Expect) {
236 let actual = completion_list(ra_fixture, CompletionKind::Magic);
237 expect.assert_eq(&actual)
238 }
239
240 #[test]
241 fn name_ref_function_type_const() {
242 check(
243 r#"
244trait Test {
245 type TestType;
246 const TEST_CONST: u16;
247 fn test();
248}
249struct T;
250
251impl Test for T {
252 t<|>
253}
254"#,
255 expect![["
256ct const TEST_CONST: u16 = \n\
257fn fn test()
258ta type TestType = \n\
259 "]],
260 );
261 }
262
263 #[test]
264 fn no_nested_fn_completions() {
265 check(
266 r"
267trait Test {
268 fn test();
269 fn test2();
270}
271struct T;
272
273impl Test for T {
274 fn test() {
275 t<|>
276 }
277}
278",
279 expect![[""]],
280 );
281 }
282
283 #[test]
284 fn name_ref_single_function() {
285 check_edit(
286 "test",
287 r#"
288trait Test {
289 fn test();
290}
291struct T;
292
293impl Test for T {
294 t<|>
295}
296"#,
297 r#"
298trait Test {
299 fn test();
300}
301struct T;
302
303impl Test for T {
304 fn test() {
305 $0
306}
307}
308"#,
309 );
310 }
311
312 #[test]
313 fn single_function() {
314 check_edit(
315 "test",
316 r#"
317trait Test {
318 fn test();
319}
320struct T;
321
322impl Test for T {
323 fn t<|>
324}
325"#,
326 r#"
327trait Test {
328 fn test();
329}
330struct T;
331
332impl Test for T {
333 fn test() {
334 $0
335}
336}
337"#,
338 );
339 }
340
341 #[test]
342 fn hide_implemented_fn() {
343 check(
344 r#"
345trait Test {
346 fn foo();
347 fn foo_bar();
348}
349struct T;
350
351impl Test for T {
352 fn foo() {}
353 fn f<|>
354}
355"#,
356 expect![[r#"
357 fn fn foo_bar()
358 "#]],
359 );
360 }
361
362 #[test]
363 fn generic_fn() {
364 check_edit(
365 "foo",
366 r#"
367trait Test {
368 fn foo<T>();
369}
370struct T;
371
372impl Test for T {
373 fn f<|>
374}
375"#,
376 r#"
377trait Test {
378 fn foo<T>();
379}
380struct T;
381
382impl Test for T {
383 fn foo<T>() {
384 $0
385}
386}
387"#,
388 );
389 check_edit(
390 "foo",
391 r#"
392trait Test {
393 fn foo<T>() where T: Into<String>;
394}
395struct T;
396
397impl Test for T {
398 fn f<|>
399}
400"#,
401 r#"
402trait Test {
403 fn foo<T>() where T: Into<String>;
404}
405struct T;
406
407impl Test for T {
408 fn foo<T>()
409where T: Into<String> {
410 $0
411}
412}
413"#,
414 );
415 }
416
417 #[test]
418 fn associated_type() {
419 check_edit(
420 "SomeType",
421 r#"
422trait Test {
423 type SomeType;
424}
425
426impl Test for () {
427 type S<|>
428}
429"#,
430 "
431trait Test {
432 type SomeType;
433}
434
435impl Test for () {
436 type SomeType = \n\
437}
438",
439 );
440 }
441
442 #[test]
443 fn associated_const() {
444 check_edit(
445 "SOME_CONST",
446 r#"
447trait Test {
448 const SOME_CONST: u16;
449}
450
451impl Test for () {
452 const S<|>
453}
454"#,
455 "
456trait Test {
457 const SOME_CONST: u16;
458}
459
460impl Test for () {
461 const SOME_CONST: u16 = \n\
462}
463",
464 );
465
466 check_edit(
467 "SOME_CONST",
468 r#"
469trait Test {
470 const SOME_CONST: u16 = 92;
471}
472
473impl Test for () {
474 const S<|>
475}
476"#,
477 "
478trait Test {
479 const SOME_CONST: u16 = 92;
480}
481
482impl Test for () {
483 const SOME_CONST: u16 = \n\
484}
485",
486 );
487 }
488}
diff --git a/crates/ide/src/completion/complete_unqualified_path.rs b/crates/ide/src/completion/complete_unqualified_path.rs
new file mode 100644
index 000000000..1f1b682a7
--- /dev/null
+++ b/crates/ide/src/completion/complete_unqualified_path.rs
@@ -0,0 +1,658 @@
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::completion::{CompletionContext, Completions};
8
9pub(super) 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 {
17 return;
18 }
19
20 if let Some(ty) = &ctx.expected_type {
21 complete_enum_variants(acc, ctx, ty);
22 }
23
24 if ctx.is_pat_binding_or_const {
25 return;
26 }
27
28 ctx.scope.process_all_names(&mut |name, res| {
29 if ctx.use_item_syntax.is_some() {
30 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
31 if name_ref.syntax().text() == name.to_string().as_str() {
32 mark::hit!(self_fulfilling_completion);
33 return;
34 }
35 }
36 }
37 acc.add_resolution(ctx, name.to_string(), &res)
38 });
39}
40
41fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
42 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
43 let variants = enum_data.variants(ctx.db);
44
45 let module = if let Some(module) = ctx.scope.module() {
46 // Compute path from the completion site if available.
47 module
48 } else {
49 // Otherwise fall back to the enum's definition site.
50 enum_data.module(ctx.db)
51 };
52
53 for variant in variants {
54 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
55 // Variants with trivial paths are already added by the existing completion logic,
56 // so we should avoid adding these twice
57 if path.segments.len() > 1 {
58 acc.add_qualified_enum_variant(ctx, variant, path);
59 }
60 }
61 }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use expect_test::{expect, Expect};
68 use test_utils::mark;
69
70 use crate::completion::{
71 test_utils::{check_edit, completion_list},
72 CompletionKind,
73 };
74
75 fn check(ra_fixture: &str, expect: Expect) {
76 let actual = completion_list(ra_fixture, CompletionKind::Reference);
77 expect.assert_eq(&actual)
78 }
79
80 #[test]
81 fn self_fulfilling_completion() {
82 mark::check!(self_fulfilling_completion);
83 check(
84 r#"
85use foo<|>
86use std::collections;
87"#,
88 expect![[r#"
89 ?? collections
90 "#]],
91 );
92 }
93
94 #[test]
95 fn bind_pat_and_path_ignore_at() {
96 check(
97 r#"
98enum Enum { A, B }
99fn quux(x: Option<Enum>) {
100 match x {
101 None => (),
102 Some(en<|> @ Enum::A) => (),
103 }
104}
105"#,
106 expect![[""]],
107 );
108 }
109
110 #[test]
111 fn bind_pat_and_path_ignore_ref() {
112 check(
113 r#"
114enum Enum { A, B }
115fn quux(x: Option<Enum>) {
116 match x {
117 None => (),
118 Some(ref en<|>) => (),
119 }
120}
121"#,
122 expect![[""]],
123 );
124 }
125
126 #[test]
127 fn bind_pat_and_path() {
128 check(
129 r#"
130enum Enum { A, B }
131fn quux(x: Option<Enum>) {
132 match x {
133 None => (),
134 Some(En<|>) => (),
135 }
136}
137"#,
138 expect![[r#"
139 en Enum
140 "#]],
141 );
142 }
143
144 #[test]
145 fn completes_bindings_from_let() {
146 check(
147 r#"
148fn quux(x: i32) {
149 let y = 92;
150 1 + <|>;
151 let z = ();
152}
153"#,
154 expect![[r#"
155 fn quux(…) fn quux(x: i32)
156 bn x i32
157 bn y i32
158 "#]],
159 );
160 }
161
162 #[test]
163 fn completes_bindings_from_if_let() {
164 check(
165 r#"
166fn quux() {
167 if let Some(x) = foo() {
168 let y = 92;
169 };
170 if let Some(a) = bar() {
171 let b = 62;
172 1 + <|>
173 }
174}
175"#,
176 expect![[r#"
177 bn a
178 bn b i32
179 fn quux() fn quux()
180 "#]],
181 );
182 }
183
184 #[test]
185 fn completes_bindings_from_for() {
186 check(
187 r#"
188fn quux() {
189 for x in &[1, 2, 3] { <|> }
190}
191"#,
192 expect![[r#"
193 fn quux() fn quux()
194 bn x
195 "#]],
196 );
197 }
198
199 #[test]
200 fn completes_if_prefix_is_keyword() {
201 mark::check!(completes_if_prefix_is_keyword);
202 check_edit(
203 "wherewolf",
204 r#"
205fn main() {
206 let wherewolf = 92;
207 drop(where<|>)
208}
209"#,
210 r#"
211fn main() {
212 let wherewolf = 92;
213 drop(wherewolf)
214}
215"#,
216 )
217 }
218
219 #[test]
220 fn completes_generic_params() {
221 check(
222 r#"fn quux<T>() { <|> }"#,
223 expect![[r#"
224 tp T
225 fn quux() fn quux<T>()
226 "#]],
227 );
228 }
229
230 #[test]
231 fn completes_generic_params_in_struct() {
232 check(
233 r#"struct S<T> { x: <|>}"#,
234 expect![[r#"
235 st S<…>
236 tp Self
237 tp T
238 "#]],
239 );
240 }
241
242 #[test]
243 fn completes_self_in_enum() {
244 check(
245 r#"enum X { Y(<|>) }"#,
246 expect![[r#"
247 tp Self
248 en X
249 "#]],
250 );
251 }
252
253 #[test]
254 fn completes_module_items() {
255 check(
256 r#"
257struct S;
258enum E {}
259fn quux() { <|> }
260"#,
261 expect![[r#"
262 en E
263 st S
264 fn quux() fn quux()
265 "#]],
266 );
267 }
268
269 #[test]
270 fn completes_extern_prelude() {
271 check(
272 r#"
273//- /lib.rs
274use <|>;
275
276//- /other_crate/lib.rs
277// nothing here
278"#,
279 expect![[r#"
280 md other_crate
281 "#]],
282 );
283 }
284
285 #[test]
286 fn completes_module_items_in_nested_modules() {
287 check(
288 r#"
289struct Foo;
290mod m {
291 struct Bar;
292 fn quux() { <|> }
293}
294"#,
295 expect![[r#"
296 st Bar
297 fn quux() fn quux()
298 "#]],
299 );
300 }
301
302 #[test]
303 fn completes_return_type() {
304 check(
305 r#"
306struct Foo;
307fn x() -> <|>
308"#,
309 expect![[r#"
310 st Foo
311 fn x() fn x()
312 "#]],
313 );
314 }
315
316 #[test]
317 fn dont_show_both_completions_for_shadowing() {
318 check(
319 r#"
320fn foo() {
321 let bar = 92;
322 {
323 let bar = 62;
324 drop(<|>)
325 }
326}
327"#,
328 // FIXME: should be only one bar here
329 expect![[r#"
330 bn bar i32
331 bn bar i32
332 fn foo() fn foo()
333 "#]],
334 );
335 }
336
337 #[test]
338 fn completes_self_in_methods() {
339 check(
340 r#"impl S { fn foo(&self) { <|> } }"#,
341 expect![[r#"
342 tp Self
343 bn self &{unknown}
344 "#]],
345 );
346 }
347
348 #[test]
349 fn completes_prelude() {
350 check(
351 r#"
352//- /main.rs
353fn foo() { let x: <|> }
354
355//- /std/lib.rs
356#[prelude_import]
357use prelude::*;
358
359mod prelude { struct Option; }
360"#,
361 expect![[r#"
362 st Option
363 fn foo() fn foo()
364 md std
365 "#]],
366 );
367 }
368
369 #[test]
370 fn completes_std_prelude_if_core_is_defined() {
371 check(
372 r#"
373//- /main.rs
374fn foo() { let x: <|> }
375
376//- /core/lib.rs
377#[prelude_import]
378use prelude::*;
379
380mod prelude { struct Option; }
381
382//- /std/lib.rs
383#[prelude_import]
384use prelude::*;
385
386mod prelude { struct String; }
387"#,
388 expect![[r#"
389 st String
390 md core
391 fn foo() fn foo()
392 md std
393 "#]],
394 );
395 }
396
397 #[test]
398 fn completes_macros_as_value() {
399 check(
400 r#"
401macro_rules! foo { () => {} }
402
403#[macro_use]
404mod m1 {
405 macro_rules! bar { () => {} }
406}
407
408mod m2 {
409 macro_rules! nope { () => {} }
410
411 #[macro_export]
412 macro_rules! baz { () => {} }
413}
414
415fn main() { let v = <|> }
416"#,
417 expect![[r##"
418 ma bar!(…) macro_rules! bar
419 ma baz!(…) #[macro_export]
420 macro_rules! baz
421 ma foo!(…) macro_rules! foo
422 md m1
423 md m2
424 fn main() fn main()
425 "##]],
426 );
427 }
428
429 #[test]
430 fn completes_both_macro_and_value() {
431 check(
432 r#"
433macro_rules! foo { () => {} }
434fn foo() { <|> }
435"#,
436 expect![[r#"
437 ma foo!(…) macro_rules! foo
438 fn foo() fn foo()
439 "#]],
440 );
441 }
442
443 #[test]
444 fn completes_macros_as_type() {
445 check(
446 r#"
447macro_rules! foo { () => {} }
448fn main() { let x: <|> }
449"#,
450 expect![[r#"
451 ma foo!(…) macro_rules! foo
452 fn main() fn main()
453 "#]],
454 );
455 }
456
457 #[test]
458 fn completes_macros_as_stmt() {
459 check(
460 r#"
461macro_rules! foo { () => {} }
462fn main() { <|> }
463"#,
464 expect![[r#"
465 ma foo!(…) macro_rules! foo
466 fn main() fn main()
467 "#]],
468 );
469 }
470
471 #[test]
472 fn completes_local_item() {
473 check(
474 r#"
475fn main() {
476 return f<|>;
477 fn frobnicate() {}
478}
479"#,
480 expect![[r#"
481 fn frobnicate() fn frobnicate()
482 fn main() fn main()
483 "#]],
484 );
485 }
486
487 #[test]
488 fn completes_in_simple_macro_1() {
489 check(
490 r#"
491macro_rules! m { ($e:expr) => { $e } }
492fn quux(x: i32) {
493 let y = 92;
494 m!(<|>);
495}
496"#,
497 expect![[r#"
498 ma m!(…) macro_rules! m
499 fn quux(…) fn quux(x: i32)
500 bn x i32
501 bn y i32
502 "#]],
503 );
504 }
505
506 #[test]
507 fn completes_in_simple_macro_2() {
508 check(
509 r"
510macro_rules! m { ($e:expr) => { $e } }
511fn quux(x: i32) {
512 let y = 92;
513 m!(x<|>);
514}
515",
516 expect![[r#"
517 ma m!(…) macro_rules! m
518 fn quux(…) fn quux(x: i32)
519 bn x i32
520 bn y i32
521 "#]],
522 );
523 }
524
525 #[test]
526 fn completes_in_simple_macro_without_closing_parens() {
527 check(
528 r#"
529macro_rules! m { ($e:expr) => { $e } }
530fn quux(x: i32) {
531 let y = 92;
532 m!(x<|>
533}
534"#,
535 expect![[r#"
536 ma m!(…) macro_rules! m
537 fn quux(…) fn quux(x: i32)
538 bn x i32
539 bn y i32
540 "#]],
541 );
542 }
543
544 #[test]
545 fn completes_unresolved_uses() {
546 check(
547 r#"
548use spam::Quux;
549
550fn main() { <|> }
551"#,
552 expect![[r#"
553 ?? Quux
554 fn main() fn main()
555 "#]],
556 );
557 }
558 #[test]
559 fn completes_enum_variant_matcharm() {
560 check(
561 r#"
562enum Foo { Bar, Baz, Quux }
563
564fn main() {
565 let foo = Foo::Quux;
566 match foo { Qu<|> }
567}
568"#,
569 expect![[r#"
570 en Foo
571 ev Foo::Bar ()
572 ev Foo::Baz ()
573 ev Foo::Quux ()
574 "#]],
575 )
576 }
577
578 #[test]
579 fn completes_enum_variant_iflet() {
580 check(
581 r#"
582enum Foo { Bar, Baz, Quux }
583
584fn main() {
585 let foo = Foo::Quux;
586 if let Qu<|> = foo { }
587}
588"#,
589 expect![[r#"
590 en Foo
591 ev Foo::Bar ()
592 ev Foo::Baz ()
593 ev Foo::Quux ()
594 "#]],
595 )
596 }
597
598 #[test]
599 fn completes_enum_variant_basic_expr() {
600 check(
601 r#"
602enum Foo { Bar, Baz, Quux }
603fn main() { let foo: Foo = Q<|> }
604"#,
605 expect![[r#"
606 en Foo
607 ev Foo::Bar ()
608 ev Foo::Baz ()
609 ev Foo::Quux ()
610 fn main() fn main()
611 "#]],
612 )
613 }
614
615 #[test]
616 fn completes_enum_variant_from_module() {
617 check(
618 r#"
619mod m { pub enum E { V } }
620fn f() -> m::E { V<|> }
621"#,
622 expect![[r#"
623 fn f() fn f() -> m::E
624 md m
625 ev m::E::V ()
626 "#]],
627 )
628 }
629
630 #[test]
631 fn dont_complete_attr() {
632 check(
633 r#"
634struct Foo;
635#[<|>]
636fn f() {}
637"#,
638 expect![[""]],
639 )
640 }
641
642 #[test]
643 fn completes_type_or_trait_in_impl_block() {
644 check(
645 r#"
646trait MyTrait {}
647struct MyStruct {}
648
649impl My<|>
650"#,
651 expect![[r#"
652 st MyStruct
653 tt MyTrait
654 tp Self
655 "#]],
656 )
657 }
658}
diff --git a/crates/ide/src/completion/completion_config.rs b/crates/ide/src/completion/completion_config.rs
new file mode 100644
index 000000000..71b49ace8
--- /dev/null
+++ b/crates/ide/src/completion/completion_config.rs
@@ -0,0 +1,35 @@
1//! Settings for tweaking completion.
2//!
3//! The fun thing here is `SnippetCap` -- this type can only be created in this
4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to.
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct CompletionConfig {
9 pub enable_postfix_completions: bool,
10 pub add_call_parenthesis: bool,
11 pub add_call_argument_snippets: bool,
12 pub snippet_cap: Option<SnippetCap>,
13}
14
15impl CompletionConfig {
16 pub fn allow_snippets(&mut self, yes: bool) {
17 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
18 }
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct SnippetCap {
23 _private: (),
24}
25
26impl Default for CompletionConfig {
27 fn default() -> Self {
28 CompletionConfig {
29 enable_postfix_completions: true,
30 add_call_parenthesis: true,
31 add_call_argument_snippets: true,
32 snippet_cap: Some(SnippetCap { _private: () }),
33 }
34 }
35}
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs
new file mode 100644
index 000000000..5adac7ebc
--- /dev/null
+++ b/crates/ide/src/completion/completion_context.rs
@@ -0,0 +1,486 @@
1//! FIXME: write short doc here
2
3use base_db::SourceDatabase;
4use hir::{Semantics, SemanticsScope, Type};
5use ide_db::RootDatabase;
6use syntax::{
7 algo::{find_covering_element, find_node_at_offset},
8 ast, match_ast, AstNode, NodeOrToken,
9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11};
12use test_utils::mark;
13use text_edit::Indel;
14
15use crate::{
16 call_info::ActiveParameter,
17 completion::{
18 patterns::{
19 has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent,
20 has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling,
21 has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, unsafe_is_prev,
22 },
23 CompletionConfig,
24 },
25 FilePosition,
26};
27
28/// `CompletionContext` is created early during completion to figure out, where
29/// exactly is the cursor, syntax-wise.
30#[derive(Debug)]
31pub(crate) struct CompletionContext<'a> {
32 pub(super) sema: Semantics<'a, RootDatabase>,
33 pub(super) scope: SemanticsScope<'a>,
34 pub(super) db: &'a RootDatabase,
35 pub(super) config: &'a CompletionConfig,
36 pub(super) position: FilePosition,
37 /// The token before the cursor, in the original file.
38 pub(super) original_token: SyntaxToken,
39 /// The token before the cursor, in the macro-expanded file.
40 pub(super) token: SyntaxToken,
41 pub(super) krate: Option<hir::Crate>,
42 pub(super) expected_type: Option<Type>,
43 pub(super) name_ref_syntax: Option<ast::NameRef>,
44 pub(super) function_syntax: Option<ast::Fn>,
45 pub(super) use_item_syntax: Option<ast::Use>,
46 pub(super) record_lit_syntax: Option<ast::RecordExpr>,
47 pub(super) record_pat_syntax: Option<ast::RecordPat>,
48 pub(super) record_field_syntax: Option<ast::RecordExprField>,
49 pub(super) impl_def: Option<ast::Impl>,
50 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
51 pub(super) active_parameter: Option<ActiveParameter>,
52 pub(super) is_param: bool,
53 /// If a name-binding or reference to a const in a pattern.
54 /// Irrefutable patterns (like let) are excluded.
55 pub(super) is_pat_binding_or_const: bool,
56 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
57 pub(super) is_trivial_path: bool,
58 /// If not a trivial path, the prefix (qualifier).
59 pub(super) path_qual: Option<ast::Path>,
60 pub(super) after_if: bool,
61 /// `true` if we are a statement or a last expr in the block.
62 pub(super) can_be_stmt: bool,
63 /// `true` if we expect an expression at the cursor position.
64 pub(super) is_expr: bool,
65 /// Something is typed at the "top" level, in module or impl/trait.
66 pub(super) is_new_item: bool,
67 /// The receiver if this is a field or method access, i.e. writing something.<|>
68 pub(super) dot_receiver: Option<ast::Expr>,
69 pub(super) dot_receiver_is_ambiguous_float_literal: bool,
70 /// If this is a call (method or function) in particular, i.e. the () are already there.
71 pub(super) is_call: bool,
72 /// Like `is_call`, but for tuple patterns.
73 pub(super) is_pattern_call: bool,
74 /// If this is a macro call, i.e. the () are already there.
75 pub(super) is_macro_call: bool,
76 pub(super) is_path_type: bool,
77 pub(super) has_type_args: bool,
78 pub(super) attribute_under_caret: Option<ast::Attr>,
79 pub(super) unsafe_is_prev: bool,
80 pub(super) if_is_prev: bool,
81 pub(super) block_expr_parent: bool,
82 pub(super) bind_pat_parent: bool,
83 pub(super) ref_pat_parent: bool,
84 pub(super) in_loop_body: bool,
85 pub(super) has_trait_parent: bool,
86 pub(super) has_impl_parent: bool,
87 pub(super) trait_as_prev_sibling: bool,
88 pub(super) impl_as_prev_sibling: bool,
89 pub(super) is_match_arm: bool,
90 pub(super) has_item_list_or_source_file_parent: bool,
91}
92
93impl<'a> CompletionContext<'a> {
94 pub(super) fn new(
95 db: &'a RootDatabase,
96 position: FilePosition,
97 config: &'a CompletionConfig,
98 ) -> Option<CompletionContext<'a>> {
99 let sema = Semantics::new(db);
100
101 let original_file = sema.parse(position.file_id);
102
103 // Insert a fake ident to get a valid parse tree. We will use this file
104 // to determine context, though the original_file will be used for
105 // actual completion.
106 let file_with_fake_ident = {
107 let parse = db.parse(position.file_id);
108 let edit = Indel::insert(position.offset, "intellijRulezz".to_string());
109 parse.reparse(&edit).tree()
110 };
111 let fake_ident_token =
112 file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap();
113
114 let krate = sema.to_module_def(position.file_id).map(|m| m.krate());
115 let original_token =
116 original_file.syntax().token_at_offset(position.offset).left_biased()?;
117 let token = sema.descend_into_macros(original_token.clone());
118 let scope = sema.scope_at_offset(&token.parent(), position.offset);
119 let mut ctx = CompletionContext {
120 sema,
121 scope,
122 db,
123 config,
124 original_token,
125 token,
126 position,
127 krate,
128 expected_type: None,
129 name_ref_syntax: None,
130 function_syntax: None,
131 use_item_syntax: None,
132 record_lit_syntax: None,
133 record_pat_syntax: None,
134 record_field_syntax: None,
135 impl_def: None,
136 active_parameter: ActiveParameter::at(db, position),
137 is_param: false,
138 is_pat_binding_or_const: false,
139 is_trivial_path: false,
140 path_qual: None,
141 after_if: false,
142 can_be_stmt: false,
143 is_expr: false,
144 is_new_item: false,
145 dot_receiver: None,
146 is_call: false,
147 is_pattern_call: false,
148 is_macro_call: false,
149 is_path_type: false,
150 has_type_args: false,
151 dot_receiver_is_ambiguous_float_literal: false,
152 attribute_under_caret: None,
153 unsafe_is_prev: false,
154 in_loop_body: false,
155 ref_pat_parent: false,
156 bind_pat_parent: false,
157 block_expr_parent: false,
158 has_trait_parent: false,
159 has_impl_parent: false,
160 trait_as_prev_sibling: false,
161 impl_as_prev_sibling: false,
162 if_is_prev: false,
163 is_match_arm: false,
164 has_item_list_or_source_file_parent: false,
165 };
166
167 let mut original_file = original_file.syntax().clone();
168 let mut hypothetical_file = file_with_fake_ident.syntax().clone();
169 let mut offset = position.offset;
170 let mut fake_ident_token = fake_ident_token;
171
172 // Are we inside a macro call?
173 while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
174 find_node_at_offset::<ast::MacroCall>(&original_file, offset),
175 find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset),
176 ) {
177 if actual_macro_call.path().as_ref().map(|s| s.syntax().text())
178 != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text())
179 {
180 break;
181 }
182 let hypothetical_args = match macro_call_with_fake_ident.token_tree() {
183 Some(tt) => tt,
184 None => break,
185 };
186 if let (Some(actual_expansion), Some(hypothetical_expansion)) = (
187 ctx.sema.expand(&actual_macro_call),
188 ctx.sema.speculative_expand(
189 &actual_macro_call,
190 &hypothetical_args,
191 fake_ident_token,
192 ),
193 ) {
194 let new_offset = hypothetical_expansion.1.text_range().start();
195 if new_offset > actual_expansion.text_range().end() {
196 break;
197 }
198 original_file = actual_expansion;
199 hypothetical_file = hypothetical_expansion.0;
200 fake_ident_token = hypothetical_expansion.1;
201 offset = new_offset;
202 } else {
203 break;
204 }
205 }
206 ctx.fill_keyword_patterns(&hypothetical_file, offset);
207 ctx.fill(&original_file, hypothetical_file, offset);
208 Some(ctx)
209 }
210
211 // The range of the identifier that is being completed.
212 pub(crate) fn source_range(&self) -> TextRange {
213 // check kind of macro-expanded token, but use range of original token
214 if self.token.kind() == IDENT || self.token.kind().is_keyword() {
215 mark::hit!(completes_if_prefix_is_keyword);
216 self.original_token.text_range()
217 } else {
218 TextRange::empty(self.position.offset)
219 }
220 }
221
222 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
223 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
224 let syntax_element = NodeOrToken::Token(fake_ident_token);
225 self.block_expr_parent = has_block_expr_parent(syntax_element.clone());
226 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone());
227 self.if_is_prev = if_is_prev(syntax_element.clone());
228 self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone());
229 self.ref_pat_parent = has_ref_parent(syntax_element.clone());
230 self.in_loop_body = is_in_loop_body(syntax_element.clone());
231 self.has_trait_parent = has_trait_parent(syntax_element.clone());
232 self.has_impl_parent = has_impl_parent(syntax_element.clone());
233 self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone());
234 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
235 self.is_match_arm = is_match_arm(syntax_element.clone());
236 self.has_item_list_or_source_file_parent =
237 has_item_list_or_source_file_parent(syntax_element);
238 }
239
240 fn fill(
241 &mut self,
242 original_file: &SyntaxNode,
243 file_with_fake_ident: SyntaxNode,
244 offset: TextSize,
245 ) {
246 // FIXME: this is wrong in at least two cases:
247 // * when there's no token `foo(<|>)`
248 // * when there is a token, but it happens to have type of it's own
249 self.expected_type = self
250 .token
251 .ancestors()
252 .find_map(|node| {
253 let ty = match_ast! {
254 match node {
255 ast::Pat(it) => self.sema.type_of_pat(&it),
256 ast::Expr(it) => self.sema.type_of_expr(&it),
257 _ => return None,
258 }
259 };
260 Some(ty)
261 })
262 .flatten();
263 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
264
265 // First, let's try to complete a reference to some declaration.
266 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
267 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
268 // See RFC#1685.
269 if is_node::<ast::Param>(name_ref.syntax()) {
270 self.is_param = true;
271 return;
272 }
273 // FIXME: remove this (V) duplication and make the check more precise
274 if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() {
275 self.record_pat_syntax =
276 self.sema.find_node_at_offset_with_macros(&original_file, offset);
277 }
278 self.classify_name_ref(original_file, name_ref, offset);
279 }
280
281 // Otherwise, see if this is a declaration. We can use heuristics to
282 // suggest declaration names, see `CompletionKind::Magic`.
283 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) {
284 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::IdentPat::cast) {
285 self.is_pat_binding_or_const = true;
286 if bind_pat.at_token().is_some()
287 || bind_pat.ref_token().is_some()
288 || bind_pat.mut_token().is_some()
289 {
290 self.is_pat_binding_or_const = false;
291 }
292 if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() {
293 self.is_pat_binding_or_const = false;
294 }
295 if let Some(let_stmt) = bind_pat.syntax().ancestors().find_map(ast::LetStmt::cast) {
296 if let Some(pat) = let_stmt.pat() {
297 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range())
298 {
299 self.is_pat_binding_or_const = false;
300 }
301 }
302 }
303 }
304 if is_node::<ast::Param>(name.syntax()) {
305 self.is_param = true;
306 return;
307 }
308 // FIXME: remove this (^) duplication and make the check more precise
309 if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() {
310 self.record_pat_syntax =
311 self.sema.find_node_at_offset_with_macros(&original_file, offset);
312 }
313 }
314 }
315
316 fn classify_name_ref(
317 &mut self,
318 original_file: &SyntaxNode,
319 name_ref: ast::NameRef,
320 offset: TextSize,
321 ) {
322 self.name_ref_syntax =
323 find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
324 let name_range = name_ref.syntax().text_range();
325 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
326 self.record_lit_syntax =
327 self.sema.find_node_at_offset_with_macros(&original_file, offset);
328 }
329
330 self.impl_def = self
331 .sema
332 .ancestors_with_macros(self.token.parent())
333 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
334 .find_map(ast::Impl::cast);
335
336 let top_node = name_ref
337 .syntax()
338 .ancestors()
339 .take_while(|it| it.text_range() == name_range)
340 .last()
341 .unwrap();
342
343 match top_node.parent().map(|it| it.kind()) {
344 Some(SOURCE_FILE) | Some(ITEM_LIST) => {
345 self.is_new_item = true;
346 return;
347 }
348 _ => (),
349 }
350
351 self.use_item_syntax =
352 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::Use::cast);
353
354 self.function_syntax = self
355 .sema
356 .ancestors_with_macros(self.token.parent())
357 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
358 .find_map(ast::Fn::cast);
359
360 self.record_field_syntax = self
361 .sema
362 .ancestors_with_macros(self.token.parent())
363 .take_while(|it| {
364 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
365 })
366 .find_map(ast::RecordExprField::cast);
367
368 let parent = match name_ref.syntax().parent() {
369 Some(it) => it,
370 None => return,
371 };
372
373 if let Some(segment) = ast::PathSegment::cast(parent.clone()) {
374 let path = segment.parent_path();
375 self.is_call = path
376 .syntax()
377 .parent()
378 .and_then(ast::PathExpr::cast)
379 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
380 .is_some();
381 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
382 self.is_pattern_call =
383 path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
384
385 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
386 self.has_type_args = segment.generic_arg_list().is_some();
387
388 if let Some(path) = path_or_use_tree_qualifier(&path) {
389 self.path_qual = path
390 .segment()
391 .and_then(|it| {
392 find_node_with_range::<ast::PathSegment>(
393 original_file,
394 it.syntax().text_range(),
395 )
396 })
397 .map(|it| it.parent_path());
398 return;
399 }
400
401 if let Some(segment) = path.segment() {
402 if segment.coloncolon_token().is_some() {
403 return;
404 }
405 }
406
407 self.is_trivial_path = true;
408
409 // Find either enclosing expr statement (thing with `;`) or a
410 // block. If block, check that we are the last expr.
411 self.can_be_stmt = name_ref
412 .syntax()
413 .ancestors()
414 .find_map(|node| {
415 if let Some(stmt) = ast::ExprStmt::cast(node.clone()) {
416 return Some(stmt.syntax().text_range() == name_ref.syntax().text_range());
417 }
418 if let Some(block) = ast::BlockExpr::cast(node) {
419 return Some(
420 block.expr().map(|e| e.syntax().text_range())
421 == Some(name_ref.syntax().text_range()),
422 );
423 }
424 None
425 })
426 .unwrap_or(false);
427 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
428
429 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
430 if let Some(if_expr) =
431 self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
432 {
433 if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start()
434 {
435 self.after_if = true;
436 }
437 }
438 }
439 }
440 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
441 // The receiver comes before the point of insertion of the fake
442 // ident, so it should have the same range in the non-modified file
443 self.dot_receiver = field_expr
444 .expr()
445 .map(|e| e.syntax().text_range())
446 .and_then(|r| find_node_with_range(original_file, r));
447 self.dot_receiver_is_ambiguous_float_literal =
448 if let Some(ast::Expr::Literal(l)) = &self.dot_receiver {
449 match l.kind() {
450 ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
451 _ => false,
452 }
453 } else {
454 false
455 }
456 }
457 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
458 // As above
459 self.dot_receiver = method_call_expr
460 .receiver()
461 .map(|e| e.syntax().text_range())
462 .and_then(|r| find_node_with_range(original_file, r));
463 self.is_call = true;
464 }
465 }
466}
467
468fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
469 find_covering_element(syntax, range).ancestors().find_map(N::cast)
470}
471
472fn is_node<N: AstNode>(node: &SyntaxNode) -> bool {
473 match node.ancestors().find_map(N::cast) {
474 None => false,
475 Some(n) => n.syntax().text_range() == node.text_range(),
476 }
477}
478
479fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<ast::Path> {
480 if let Some(qual) = path.qualifier() {
481 return Some(qual);
482 }
483 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
484 let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?;
485 use_tree.path()
486}
diff --git a/crates/ide/src/completion/completion_item.rs b/crates/ide/src/completion/completion_item.rs
new file mode 100644
index 000000000..9377cdc57
--- /dev/null
+++ b/crates/ide/src/completion/completion_item.rs
@@ -0,0 +1,384 @@
1//! FIXME: write short doc here
2
3use std::fmt;
4
5use hir::Documentation;
6use syntax::TextRange;
7use text_edit::TextEdit;
8
9use crate::completion::completion_config::SnippetCap;
10
11/// `CompletionItem` describes a single completion variant in the editor pop-up.
12/// It is basically a POD with various properties. To construct a
13/// `CompletionItem`, use `new` method and the `Builder` struct.
14pub struct CompletionItem {
15 /// Used only internally in tests, to check only specific kind of
16 /// completion (postfix, keyword, reference, etc).
17 #[allow(unused)]
18 pub(crate) completion_kind: CompletionKind,
19 /// Label in the completion pop up which identifies completion.
20 label: String,
21 /// Range of identifier that is being completed.
22 ///
23 /// It should be used primarily for UI, but we also use this to convert
24 /// genetic TextEdit into LSP's completion edit (see conv.rs).
25 ///
26 /// `source_range` must contain the completion offset. `insert_text` should
27 /// start with what `source_range` points to, or VSCode will filter out the
28 /// completion silently.
29 source_range: TextRange,
30 /// What happens when user selects this item.
31 ///
32 /// Typically, replaces `source_range` with new identifier.
33 text_edit: TextEdit,
34 insert_text_format: InsertTextFormat,
35
36 /// What item (struct, function, etc) are we completing.
37 kind: Option<CompletionItemKind>,
38
39 /// Lookup is used to check if completion item indeed can complete current
40 /// ident.
41 ///
42 /// That is, in `foo.bar<|>` lookup of `abracadabra` will be accepted (it
43 /// contains `bar` sub sequence), and `quux` will rejected.
44 lookup: Option<String>,
45
46 /// Additional info to show in the UI pop up.
47 detail: Option<String>,
48 documentation: Option<Documentation>,
49
50 /// Whether this item is marked as deprecated
51 deprecated: bool,
52
53 /// If completing a function call, ask the editor to show parameter popup
54 /// after completion.
55 trigger_call_info: bool,
56
57 /// Score is useful to pre select or display in better order completion items
58 score: Option<CompletionScore>,
59}
60
61// We use custom debug for CompletionItem to make snapshot tests more readable.
62impl fmt::Debug for CompletionItem {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let mut s = f.debug_struct("CompletionItem");
65 s.field("label", &self.label()).field("source_range", &self.source_range());
66 if self.text_edit().len() == 1 {
67 let atom = &self.text_edit().iter().next().unwrap();
68 s.field("delete", &atom.delete);
69 s.field("insert", &atom.insert);
70 } else {
71 s.field("text_edit", &self.text_edit);
72 }
73 if let Some(kind) = self.kind().as_ref() {
74 s.field("kind", kind);
75 }
76 if self.lookup() != self.label() {
77 s.field("lookup", &self.lookup());
78 }
79 if let Some(detail) = self.detail() {
80 s.field("detail", &detail);
81 }
82 if let Some(documentation) = self.documentation() {
83 s.field("documentation", &documentation);
84 }
85 if self.deprecated {
86 s.field("deprecated", &true);
87 }
88 if let Some(score) = &self.score {
89 s.field("score", score);
90 }
91 if self.trigger_call_info {
92 s.field("trigger_call_info", &true);
93 }
94 s.finish()
95 }
96}
97
98#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
99pub enum CompletionScore {
100 /// If only type match
101 TypeMatch,
102 /// If type and name match
103 TypeAndNameMatch,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum CompletionItemKind {
108 Snippet,
109 Keyword,
110 Module,
111 Function,
112 BuiltinType,
113 Struct,
114 Enum,
115 EnumVariant,
116 Binding,
117 Field,
118 Static,
119 Const,
120 Trait,
121 TypeAlias,
122 Method,
123 TypeParam,
124 Macro,
125 Attribute,
126 UnresolvedReference,
127}
128
129impl CompletionItemKind {
130 #[cfg(test)]
131 pub(crate) fn tag(&self) -> &'static str {
132 match self {
133 CompletionItemKind::Attribute => "at",
134 CompletionItemKind::Binding => "bn",
135 CompletionItemKind::BuiltinType => "bt",
136 CompletionItemKind::Const => "ct",
137 CompletionItemKind::Enum => "en",
138 CompletionItemKind::EnumVariant => "ev",
139 CompletionItemKind::Field => "fd",
140 CompletionItemKind::Function => "fn",
141 CompletionItemKind::Keyword => "kw",
142 CompletionItemKind::Macro => "ma",
143 CompletionItemKind::Method => "me",
144 CompletionItemKind::Module => "md",
145 CompletionItemKind::Snippet => "sn",
146 CompletionItemKind::Static => "sc",
147 CompletionItemKind::Struct => "st",
148 CompletionItemKind::Trait => "tt",
149 CompletionItemKind::TypeAlias => "ta",
150 CompletionItemKind::TypeParam => "tp",
151 CompletionItemKind::UnresolvedReference => "??",
152 }
153 }
154}
155
156#[derive(Debug, PartialEq, Eq, Copy, Clone)]
157pub(crate) enum CompletionKind {
158 /// Parser-based keyword completion.
159 Keyword,
160 /// Your usual "complete all valid identifiers".
161 Reference,
162 /// "Secret sauce" completions.
163 Magic,
164 Snippet,
165 Postfix,
166 BuiltinType,
167 Attribute,
168}
169
170#[derive(Debug, PartialEq, Eq, Copy, Clone)]
171pub enum InsertTextFormat {
172 PlainText,
173 Snippet,
174}
175
176impl CompletionItem {
177 pub(crate) fn new(
178 completion_kind: CompletionKind,
179 source_range: TextRange,
180 label: impl Into<String>,
181 ) -> Builder {
182 let label = label.into();
183 Builder {
184 source_range,
185 completion_kind,
186 label,
187 insert_text: None,
188 insert_text_format: InsertTextFormat::PlainText,
189 detail: None,
190 documentation: None,
191 lookup: None,
192 kind: None,
193 text_edit: None,
194 deprecated: None,
195 trigger_call_info: None,
196 score: None,
197 }
198 }
199 /// What user sees in pop-up in the UI.
200 pub fn label(&self) -> &str {
201 &self.label
202 }
203 pub fn source_range(&self) -> TextRange {
204 self.source_range
205 }
206
207 pub fn insert_text_format(&self) -> InsertTextFormat {
208 self.insert_text_format
209 }
210
211 pub fn text_edit(&self) -> &TextEdit {
212 &self.text_edit
213 }
214
215 /// Short one-line additional information, like a type
216 pub fn detail(&self) -> Option<&str> {
217 self.detail.as_deref()
218 }
219 /// A doc-comment
220 pub fn documentation(&self) -> Option<Documentation> {
221 self.documentation.clone()
222 }
223 /// What string is used for filtering.
224 pub fn lookup(&self) -> &str {
225 self.lookup.as_deref().unwrap_or(&self.label)
226 }
227
228 pub fn kind(&self) -> Option<CompletionItemKind> {
229 self.kind
230 }
231
232 pub fn deprecated(&self) -> bool {
233 self.deprecated
234 }
235
236 pub fn score(&self) -> Option<CompletionScore> {
237 self.score
238 }
239
240 pub fn trigger_call_info(&self) -> bool {
241 self.trigger_call_info
242 }
243}
244
245/// A helper to make `CompletionItem`s.
246#[must_use]
247pub(crate) struct Builder {
248 source_range: TextRange,
249 completion_kind: CompletionKind,
250 label: String,
251 insert_text: Option<String>,
252 insert_text_format: InsertTextFormat,
253 detail: Option<String>,
254 documentation: Option<Documentation>,
255 lookup: Option<String>,
256 kind: Option<CompletionItemKind>,
257 text_edit: Option<TextEdit>,
258 deprecated: Option<bool>,
259 trigger_call_info: Option<bool>,
260 score: Option<CompletionScore>,
261}
262
263impl Builder {
264 pub(crate) fn add_to(self, acc: &mut Completions) {
265 acc.add(self.build())
266 }
267
268 pub(crate) fn build(self) -> CompletionItem {
269 let label = self.label;
270 let text_edit = match self.text_edit {
271 Some(it) => it,
272 None => TextEdit::replace(
273 self.source_range,
274 self.insert_text.unwrap_or_else(|| label.clone()),
275 ),
276 };
277
278 CompletionItem {
279 source_range: self.source_range,
280 label,
281 insert_text_format: self.insert_text_format,
282 text_edit,
283 detail: self.detail,
284 documentation: self.documentation,
285 lookup: self.lookup,
286 kind: self.kind,
287 completion_kind: self.completion_kind,
288 deprecated: self.deprecated.unwrap_or(false),
289 trigger_call_info: self.trigger_call_info.unwrap_or(false),
290 score: self.score,
291 }
292 }
293 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
294 self.lookup = Some(lookup.into());
295 self
296 }
297 pub(crate) fn label(mut self, label: impl Into<String>) -> Builder {
298 self.label = label.into();
299 self
300 }
301 pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder {
302 self.insert_text = Some(insert_text.into());
303 self
304 }
305 pub(crate) fn insert_snippet(
306 mut self,
307 _cap: SnippetCap,
308 snippet: impl Into<String>,
309 ) -> Builder {
310 self.insert_text_format = InsertTextFormat::Snippet;
311 self.insert_text(snippet)
312 }
313 pub(crate) fn kind(mut self, kind: CompletionItemKind) -> Builder {
314 self.kind = Some(kind);
315 self
316 }
317 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder {
318 self.text_edit = Some(edit);
319 self
320 }
321 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
322 self.insert_text_format = InsertTextFormat::Snippet;
323 self.text_edit(edit)
324 }
325 #[allow(unused)]
326 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder {
327 self.set_detail(Some(detail))
328 }
329 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
330 self.detail = detail.map(Into::into);
331 self
332 }
333 #[allow(unused)]
334 pub(crate) fn documentation(self, docs: Documentation) -> Builder {
335 self.set_documentation(Some(docs))
336 }
337 pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder {
338 self.documentation = docs.map(Into::into);
339 self
340 }
341 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder {
342 self.deprecated = Some(deprecated);
343 self
344 }
345 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder {
346 self.score = Some(score);
347 self
348 }
349 pub(crate) fn trigger_call_info(mut self) -> Builder {
350 self.trigger_call_info = Some(true);
351 self
352 }
353}
354
355impl<'a> Into<CompletionItem> for Builder {
356 fn into(self) -> CompletionItem {
357 self.build()
358 }
359}
360
361/// Represents an in-progress set of completions being built.
362#[derive(Debug, Default)]
363pub(crate) struct Completions {
364 buf: Vec<CompletionItem>,
365}
366
367impl Completions {
368 pub(crate) fn add(&mut self, item: impl Into<CompletionItem>) {
369 self.buf.push(item.into())
370 }
371 pub(crate) fn add_all<I>(&mut self, items: I)
372 where
373 I: IntoIterator,
374 I::Item: Into<CompletionItem>,
375 {
376 items.into_iter().for_each(|item| self.add(item.into()))
377 }
378}
379
380impl Into<Vec<CompletionItem>> for Completions {
381 fn into(self) -> Vec<CompletionItem> {
382 self.buf
383 }
384}
diff --git a/crates/ide/src/completion/generated_features.rs b/crates/ide/src/completion/generated_features.rs
new file mode 100644
index 000000000..24754a8cf
--- /dev/null
+++ b/crates/ide/src/completion/generated_features.rs
@@ -0,0 +1,4 @@
1//! Generated file, do not edit by hand, see `xtask/src/codegen`
2
3use crate::completion::complete_attribute::LintCompletion;
4pub ( super ) const FEATURES : & [ LintCompletion ] = & [ LintCompletion { label : "doc_cfg" , description : "# `doc_cfg`\n\nThe tracking issue for this feature is: [#43781]\n\n------\n\nThe `doc_cfg` feature allows an API be documented as only available in some specific platforms.\nThis attribute has two effects:\n\n1. In the annotated item's documentation, there will be a message saying \"This is supported on\n (platform) only\".\n\n2. The item's doc-tests will only run on the specific platform.\n\nIn addition to allowing the use of the `#[doc(cfg)]` attribute, this feature enables the use of a\nspecial conditional compilation flag, `#[cfg(doc)]`, set whenever building documentation on your\ncrate.\n\nThis feature was introduced as part of PR [#43348] to allow the platform-specific parts of the\nstandard library be documented.\n\n```rust\n#![feature(doc_cfg)]\n\n#[cfg(any(windows, doc))]\n#[doc(cfg(windows))]\n/// The application's icon in the notification area (a.k.a. system tray).\n///\n/// # Examples\n///\n/// ```no_run\n/// extern crate my_awesome_ui_library;\n/// use my_awesome_ui_library::current_app;\n/// use my_awesome_ui_library::windows::notification;\n///\n/// let icon = current_app().get::<notification::Icon>();\n/// icon.show();\n/// icon.show_message(\"Hello\");\n/// ```\npub struct Icon {\n // ...\n}\n```\n\n[#43781]: https://github.com/rust-lang/rust/issues/43781\n[#43348]: https://github.com/rust-lang/rust/issues/43348\n" } , LintCompletion { label : "impl_trait_in_bindings" , description : "# `impl_trait_in_bindings`\n\nThe tracking issue for this feature is: [#63065]\n\n[#63065]: https://github.com/rust-lang/rust/issues/63065\n\n------------------------\n\nThe `impl_trait_in_bindings` feature gate lets you use `impl Trait` syntax in\n`let`, `static`, and `const` bindings.\n\nA simple example is:\n\n```rust\n#![feature(impl_trait_in_bindings)]\n\nuse std::fmt::Debug;\n\nfn main() {\n let a: impl Debug + Clone = 42;\n let b = a.clone();\n println!(\"{:?}\", b); // prints `42`\n}\n```\n\nNote however that because the types of `a` and `b` are opaque in the above\nexample, calling inherent methods or methods outside of the specified traits\n(e.g., `a.abs()` or `b.abs()`) is not allowed, and yields an error.\n" } , LintCompletion { label : "plugin" , description : "# `plugin`\n\nThe tracking issue for this feature is: [#29597]\n\n[#29597]: https://github.com/rust-lang/rust/issues/29597\n\n\nThis feature is part of \"compiler plugins.\" It will often be used with the\n[`plugin_registrar`] and `rustc_private` features.\n\n[`plugin_registrar`]: plugin-registrar.md\n\n------------------------\n\n`rustc` can load compiler plugins, which are user-provided libraries that\nextend the compiler's behavior with new lint checks, etc.\n\nA plugin is a dynamic library crate with a designated *registrar* function that\nregisters extensions with `rustc`. Other crates can load these extensions using\nthe crate attribute `#![plugin(...)]`. See the\n`rustc_driver::plugin` documentation for more about the\nmechanics of defining and loading a plugin.\n\nIn the vast majority of cases, a plugin should *only* be used through\n`#![plugin]` and not through an `extern crate` item. Linking a plugin would\npull in all of librustc_ast and librustc as dependencies of your crate. This is\ngenerally unwanted unless you are building another plugin.\n\nThe usual practice is to put compiler plugins in their own crate, separate from\nany `macro_rules!` macros or ordinary Rust code meant to be used by consumers\nof a library.\n\n# Lint plugins\n\nPlugins can extend [Rust's lint\ninfrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with\nadditional checks for code style, safety, etc. Now let's write a plugin\n[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs)\nthat warns about any item named `lintme`.\n\n```rust,ignore\n#![feature(plugin_registrar)]\n#![feature(box_syntax, rustc_private)]\n\nextern crate rustc_ast;\n\n// Load rustc as a plugin to get macros\nextern crate rustc_driver;\n#[macro_use]\nextern crate rustc_lint;\n#[macro_use]\nextern crate rustc_session;\n\nuse rustc_driver::plugin::Registry;\nuse rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};\nuse rustc_ast::ast;\ndeclare_lint!(TEST_LINT, Warn, \"Warn about items named 'lintme'\");\n\ndeclare_lint_pass!(Pass => [TEST_LINT]);\n\nimpl EarlyLintPass for Pass {\n fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {\n if it.ident.name.as_str() == \"lintme\" {\n cx.lint(TEST_LINT, |lint| {\n lint.build(\"item is named 'lintme'\").set_span(it.span).emit()\n });\n }\n }\n}\n\n#[plugin_registrar]\npub fn plugin_registrar(reg: &mut Registry) {\n reg.lint_store.register_lints(&[&TEST_LINT]);\n reg.lint_store.register_early_pass(|| box Pass);\n}\n```\n\nThen code like\n\n```rust,ignore\n#![feature(plugin)]\n#![plugin(lint_plugin_test)]\n\nfn lintme() { }\n```\n\nwill produce a compiler warning:\n\n```txt\nfoo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default\nfoo.rs:4 fn lintme() { }\n ^~~~~~~~~~~~~~~\n```\n\nThe components of a lint plugin are:\n\n* one or more `declare_lint!` invocations, which define static `Lint` structs;\n\n* a struct holding any state needed by the lint pass (here, none);\n\n* a `LintPass`\n implementation defining how to check each syntax element. A single\n `LintPass` may call `span_lint` for several different `Lint`s, but should\n register them all through the `get_lints` method.\n\nLint passes are syntax traversals, but they run at a late stage of compilation\nwhere type information is available. `rustc`'s [built-in\nlints](https://github.com/rust-lang/rust/blob/master/src/librustc_session/lint/builtin.rs)\nmostly use the same infrastructure as lint plugins, and provide examples of how\nto access type information.\n\nLints defined by plugins are controlled by the usual [attributes and compiler\nflags](../../reference/attributes/diagnostics.md#lint-check-attributes), e.g.\n`#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the\nfirst argument to `declare_lint!`, with appropriate case and punctuation\nconversion.\n\nYou can run `rustc -W help foo.rs` to see a list of lints known to `rustc`,\nincluding those provided by plugins loaded by `foo.rs`.\n" } , LintCompletion { label : "infer_static_outlives_requirements" , description : "# `infer_static_outlives_requirements`\n\nThe tracking issue for this feature is: [#54185]\n\n[#54185]: https://github.com/rust-lang/rust/issues/54185\n\n------------------------\nThe `infer_static_outlives_requirements` feature indicates that certain\n`'static` outlives requirements can be inferred by the compiler rather than\nstating them explicitly.\n\nNote: It is an accompanying feature to `infer_outlives_requirements`,\nwhich must be enabled to infer outlives requirements.\n\nFor example, currently generic struct definitions that contain\nreferences, require where-clauses of the form T: 'static. By using\nthis feature the outlives predicates will be inferred, although\nthey may still be written explicitly.\n\n```rust,ignore (pseudo-Rust)\nstruct Foo<U> where U: 'static { // <-- currently required\n bar: Bar<U>\n}\nstruct Bar<T: 'static> {\n x: T,\n}\n```\n\n\n## Examples:\n\n```rust,ignore (pseudo-Rust)\n#![feature(infer_outlives_requirements)]\n#![feature(infer_static_outlives_requirements)]\n\n#[rustc_outlives]\n// Implicitly infer U: 'static\nstruct Foo<U> {\n bar: Bar<U>\n}\nstruct Bar<T: 'static> {\n x: T,\n}\n```\n\n" } , LintCompletion { label : "doc_alias" , description : "# `doc_alias`\n\nThe tracking issue for this feature is: [#50146]\n\n[#50146]: https://github.com/rust-lang/rust/issues/50146\n\n------------------------\n\nYou can add alias(es) to an item when using the `rustdoc` search through the\n`doc(alias)` attribute. Example:\n\n```rust,no_run\n#![feature(doc_alias)]\n\n#[doc(alias = \"x\")]\n#[doc(alias = \"big\")]\npub struct BigX;\n```\n\nThen, when looking for it through the `rustdoc` search, if you enter \"x\" or\n\"big\", search will show the `BigX` struct first.\n\nNote that this feature is currently hidden behind the `feature(doc_alias)` gate.\n" } , LintCompletion { label : "optin_builtin_traits" , description : "# `optin_builtin_traits`\n\nThe tracking issue for this feature is [#13231] \n\n[#13231]: https://github.com/rust-lang/rust/issues/13231\n\n----\n\nThe `optin_builtin_traits` feature gate allows you to define auto traits.\n\nAuto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits\nthat are automatically implemented for every type, unless the type, or a type it contains, \nhas explicitly opted out via a negative impl. (Negative impls are separately controlled\nby the `negative_impls` feature.)\n\n[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html\n[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html\n\n```rust,ignore\nimpl !Trait for Type\n```\n\nExample:\n\n```rust\n#![feature(negative_impls)]\n#![feature(optin_builtin_traits)]\n\nauto trait Valid {}\n\nstruct True;\nstruct False;\n\nimpl !Valid for False {}\n\nstruct MaybeValid<T>(T);\n\nfn must_be_valid<T: Valid>(_t: T) { }\n\nfn main() {\n // works\n must_be_valid( MaybeValid(True) );\n \n // compiler error - trait bound not satisfied\n // must_be_valid( MaybeValid(False) );\n}\n```\n\n## Automatic trait implementations\n\nWhen a type is declared as an `auto trait`, we will automatically\ncreate impls for every struct/enum/union, unless an explicit impl is\nprovided. These automatic impls contain a where clause for each field\nof the form `T: AutoTrait`, where `T` is the type of the field and\n`AutoTrait` is the auto trait in question. As an example, consider the\nstruct `List` and the auto trait `Send`:\n\n```rust\nstruct List<T> {\n data: T,\n next: Option<Box<List<T>>>,\n}\n```\n\nPresuming that there is no explicit impl of `Send` for `List`, the\ncompiler will supply an automatic impl of the form:\n\n```rust\nstruct List<T> {\n data: T,\n next: Option<Box<List<T>>>,\n}\n\nunsafe impl<T> Send for List<T>\nwhere\n T: Send, // from the field `data`\n Option<Box<List<T>>>: Send, // from the field `next`\n{ }\n```\n\nExplicit impls may be either positive or negative. They take the form:\n\n```rust,ignore\nimpl<...> AutoTrait for StructName<..> { }\nimpl<...> !AutoTrait for StructName<..> { }\n```\n\n## Coinduction: Auto traits permit cyclic matching\n\nUnlike ordinary trait matching, auto traits are **coinductive**. This\nmeans, in short, that cycles which occur in trait matching are\nconsidered ok. As an example, consider the recursive struct `List`\nintroduced in the previous section. In attempting to determine whether\n`List: Send`, we would wind up in a cycle: to apply the impl, we must\nshow that `Option<Box<List>>: Send`, which will in turn require\n`Box<List>: Send` and then finally `List: Send` again. Under ordinary\ntrait matching, this cycle would be an error, but for an auto trait it\nis considered a successful match.\n\n## Items\n\nAuto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations.\n\n## Supertraits\n\nAuto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile.\n\n" } , LintCompletion { label : "const_in_array_repeat_expressions" , description : "# `const_in_array_repeat_expressions`\n\nThe tracking issue for this feature is: [#49147]\n\n[#49147]: https://github.com/rust-lang/rust/issues/49147\n\n------------------------\n\nRelaxes the rules for repeat expressions, `[x; N]` such that `x` may also be `const` (strictly\nspeaking rvalue promotable), in addition to `typeof(x): Copy`. The result of `[x; N]` where `x` is\n`const` is itself also `const`.\n" } , LintCompletion { label : "generators" , description : "# `generators`\n\nThe tracking issue for this feature is: [#43122]\n\n[#43122]: https://github.com/rust-lang/rust/issues/43122\n\n------------------------\n\nThe `generators` feature gate in Rust allows you to define generator or\ncoroutine literals. A generator is a \"resumable function\" that syntactically\nresembles a closure but compiles to much different semantics in the compiler\nitself. The primary feature of a generator is that it can be suspended during\nexecution to be resumed at a later date. Generators use the `yield` keyword to\n\"return\", and then the caller can `resume` a generator to resume execution just\nafter the `yield` keyword.\n\nGenerators are an extra-unstable feature in the compiler right now. Added in\n[RFC 2033] they're mostly intended right now as a information/constraint\ngathering phase. The intent is that experimentation can happen on the nightly\ncompiler before actual stabilization. A further RFC will be required to\nstabilize generators/coroutines and will likely contain at least a few small\ntweaks to the overall design.\n\n[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033\n\nA syntactical example of a generator is:\n\n```rust\n#![feature(generators, generator_trait)]\n\nuse std::ops::{Generator, GeneratorState};\nuse std::pin::Pin;\n\nfn main() {\n let mut generator = || {\n yield 1;\n return \"foo\"\n };\n\n match Pin::new(&mut generator).resume(()) {\n GeneratorState::Yielded(1) => {}\n _ => panic!(\"unexpected value from resume\"),\n }\n match Pin::new(&mut generator).resume(()) {\n GeneratorState::Complete(\"foo\") => {}\n _ => panic!(\"unexpected value from resume\"),\n }\n}\n```\n\nGenerators are closure-like literals which can contain a `yield` statement. The\n`yield` statement takes an optional expression of a value to yield out of the\ngenerator. All generator literals implement the `Generator` trait in the\n`std::ops` module. The `Generator` trait has one main method, `resume`, which\nresumes execution of the generator at the previous suspension point.\n\nAn example of the control flow of generators is that the following example\nprints all numbers in order:\n\n```rust\n#![feature(generators, generator_trait)]\n\nuse std::ops::Generator;\nuse std::pin::Pin;\n\nfn main() {\n let mut generator = || {\n println!(\"2\");\n yield;\n println!(\"4\");\n };\n\n println!(\"1\");\n Pin::new(&mut generator).resume(());\n println!(\"3\");\n Pin::new(&mut generator).resume(());\n println!(\"5\");\n}\n```\n\nAt this time the main intended use case of generators is an implementation\nprimitive for async/await syntax, but generators will likely be extended to\nergonomic implementations of iterators and other primitives in the future.\nFeedback on the design and usage is always appreciated!\n\n### The `Generator` trait\n\nThe `Generator` trait in `std::ops` currently looks like:\n\n```rust\n# #![feature(arbitrary_self_types, generator_trait)]\n# use std::ops::GeneratorState;\n# use std::pin::Pin;\n\npub trait Generator<R = ()> {\n type Yield;\n type Return;\n fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState<Self::Yield, Self::Return>;\n}\n```\n\nThe `Generator::Yield` type is the type of values that can be yielded with the\n`yield` statement. The `Generator::Return` type is the returned type of the\ngenerator. This is typically the last expression in a generator's definition or\nany value passed to `return` in a generator. The `resume` function is the entry\npoint for executing the `Generator` itself.\n\nThe return value of `resume`, `GeneratorState`, looks like:\n\n```rust\npub enum GeneratorState<Y, R> {\n Yielded(Y),\n Complete(R),\n}\n```\n\nThe `Yielded` variant indicates that the generator can later be resumed. This\ncorresponds to a `yield` point in a generator. The `Complete` variant indicates\nthat the generator is complete and cannot be resumed again. Calling `resume`\nafter a generator has returned `Complete` will likely result in a panic of the\nprogram.\n\n### Closure-like semantics\n\nThe closure-like syntax for generators alludes to the fact that they also have\nclosure-like semantics. Namely:\n\n* When created, a generator executes no code. A closure literal does not\n actually execute any of the closure's code on construction, and similarly a\n generator literal does not execute any code inside the generator when\n constructed.\n\n* Generators can capture outer variables by reference or by move, and this can\n be tweaked with the `move` keyword at the beginning of the closure. Like\n closures all generators will have an implicit environment which is inferred by\n the compiler. Outer variables can be moved into a generator for use as the\n generator progresses.\n\n* Generator literals produce a value with a unique type which implements the\n `std::ops::Generator` trait. This allows actual execution of the generator\n through the `Generator::resume` method as well as also naming it in return\n types and such.\n\n* Traits like `Send` and `Sync` are automatically implemented for a `Generator`\n depending on the captured variables of the environment. Unlike closures,\n generators also depend on variables live across suspension points. This means\n that although the ambient environment may be `Send` or `Sync`, the generator\n itself may not be due to internal variables live across `yield` points being\n not-`Send` or not-`Sync`. Note that generators do\n not implement traits like `Copy` or `Clone` automatically.\n\n* Whenever a generator is dropped it will drop all captured environment\n variables.\n\n### Generators as state machines\n\nIn the compiler, generators are currently compiled as state machines. Each\n`yield` expression will correspond to a different state that stores all live\nvariables over that suspension point. Resumption of a generator will dispatch on\nthe current state and then execute internally until a `yield` is reached, at\nwhich point all state is saved off in the generator and a value is returned.\n\nLet's take a look at an example to see what's going on here:\n\n```rust\n#![feature(generators, generator_trait)]\n\nuse std::ops::Generator;\nuse std::pin::Pin;\n\nfn main() {\n let ret = \"foo\";\n let mut generator = move || {\n yield 1;\n return ret\n };\n\n Pin::new(&mut generator).resume(());\n Pin::new(&mut generator).resume(());\n}\n```\n\nThis generator literal will compile down to something similar to:\n\n```rust\n#![feature(arbitrary_self_types, generators, generator_trait)]\n\nuse std::ops::{Generator, GeneratorState};\nuse std::pin::Pin;\n\nfn main() {\n let ret = \"foo\";\n let mut generator = {\n enum __Generator {\n Start(&'static str),\n Yield1(&'static str),\n Done,\n }\n\n impl Generator for __Generator {\n type Yield = i32;\n type Return = &'static str;\n\n fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState<i32, &'static str> {\n use std::mem;\n match mem::replace(&mut *self, __Generator::Done) {\n __Generator::Start(s) => {\n *self = __Generator::Yield1(s);\n GeneratorState::Yielded(1)\n }\n\n __Generator::Yield1(s) => {\n *self = __Generator::Done;\n GeneratorState::Complete(s)\n }\n\n __Generator::Done => {\n panic!(\"generator resumed after completion\")\n }\n }\n }\n }\n\n __Generator::Start(ret)\n };\n\n Pin::new(&mut generator).resume(());\n Pin::new(&mut generator).resume(());\n}\n```\n\nNotably here we can see that the compiler is generating a fresh type,\n`__Generator` in this case. This type has a number of states (represented here\nas an `enum`) corresponding to each of the conceptual states of the generator.\nAt the beginning we're closing over our outer variable `foo` and then that\nvariable is also live over the `yield` point, so it's stored in both states.\n\nWhen the generator starts it'll immediately yield 1, but it saves off its state\njust before it does so indicating that it has reached the yield point. Upon\nresuming again we'll execute the `return ret` which returns the `Complete`\nstate.\n\nHere we can also note that the `Done` state, if resumed, panics immediately as\nit's invalid to resume a completed generator. It's also worth noting that this\nis just a rough desugaring, not a normative specification for what the compiler\ndoes.\n" } , LintCompletion { label : "unsized_tuple_coercion" , description : "# `unsized_tuple_coercion`\n\nThe tracking issue for this feature is: [#42877]\n\n[#42877]: https://github.com/rust-lang/rust/issues/42877\n\n------------------------\n\nThis is a part of [RFC0401]. According to the RFC, there should be an implementation like this:\n\n```rust,ignore\nimpl<..., T, U: ?Sized> Unsized<(..., U)> for (..., T) where T: Unsized<U> {}\n```\n\nThis implementation is currently gated behind `#[feature(unsized_tuple_coercion)]` to avoid insta-stability. Therefore you can use it like this:\n\n```rust\n#![feature(unsized_tuple_coercion)]\n\nfn main() {\n let x : ([i32; 3], [i32; 3]) = ([1, 2, 3], [4, 5, 6]);\n let y : &([i32; 3], [i32]) = &x;\n assert_eq!(y.1[0], 4);\n}\n```\n\n[RFC0401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md\n" } , LintCompletion { label : "cfg_version" , description : "# `cfg_version`\n\nThe tracking issue for this feature is: [#64796]\n\n[#64796]: https://github.com/rust-lang/rust/issues/64796\n\n------------------------\n\nThe `cfg_version` feature makes it possible to execute different code\ndepending on the compiler version.\n\n## Examples\n\n```rust\n#![feature(cfg_version)]\n\n#[cfg(version(\"1.42\"))]\nfn a() {\n // ...\n}\n\n#[cfg(not(version(\"1.42\")))]\nfn a() {\n // ...\n}\n\nfn b() {\n if cfg!(version(\"1.42\")) {\n // ...\n } else {\n // ...\n }\n}\n```\n" } , LintCompletion { label : "ffi_const" , description : "# `ffi_const`\n\nThe `#[ffi_const]` attribute applies clang's `const` attribute to foreign\nfunctions declarations.\n\nThat is, `#[ffi_const]` functions shall have