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