aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r--crates/ide_completion/src/completions/attribute.rs910
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs147
-rw-r--r--crates/ide_completion/src/completions/attribute/lint.rs187
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs8
-rw-r--r--crates/ide_completion/src/completions/keyword.rs239
-rw-r--r--crates/ide_completion/src/completions/lifetime.rs11
-rw-r--r--crates/ide_completion/src/completions/macro_in_item_position.rs19
-rw-r--r--crates/ide_completion/src/completions/pattern.rs47
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs64
-rw-r--r--crates/ide_completion/src/completions/record.rs21
-rw-r--r--crates/ide_completion/src/completions/snippet.rs8
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs90
-rw-r--r--crates/ide_completion/src/context.rs370
-rw-r--r--crates/ide_completion/src/lib.rs4
-rw-r--r--crates/ide_completion/src/patterns.rs403
-rw-r--r--crates/ide_completion/src/render.rs5
-rw-r--r--crates/ide_completion/src/render/macro_.rs6
-rw-r--r--crates/ide_completion/src/test_utils.rs18
18 files changed, 1683 insertions, 874 deletions
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index b1505c74b..13d5b90c9 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -3,9 +3,9 @@
3//! This module uses a bit of static metadata to provide completions 3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes. 4//! for built-in attributes.
5 5
6use itertools::Itertools; 6use once_cell::sync::Lazy;
7use rustc_hash::FxHashSet; 7use rustc_hash::{FxHashMap, FxHashSet};
8use syntax::{ast, AstNode, T}; 8use syntax::{ast, AstNode, NodeOrToken, SyntaxKind, T};
9 9
10use crate::{ 10use crate::{
11 context::CompletionContext, 11 context::CompletionContext,
@@ -14,33 +14,40 @@ use crate::{
14 Completions, 14 Completions,
15}; 15};
16 16
17pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 17mod derive;
18 if ctx.mod_declaration_under_caret.is_some() { 18mod lint;
19 return None; 19pub(crate) use self::lint::LintCompletion;
20 }
21 20
21pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
22 let attribute = ctx.attribute_under_caret.as_ref()?; 22 let attribute = ctx.attribute_under_caret.as_ref()?;
23 match (attribute.path(), attribute.token_tree()) { 23 match (attribute.path().and_then(|p| p.as_single_name_ref()), attribute.token_tree()) {
24 (Some(path), Some(token_tree)) => { 24 (Some(path), Some(token_tree)) => match path.text().as_str() {
25 let path = path.syntax().text(); 25 "derive" => derive::complete_derive(acc, ctx, token_tree),
26 if path == "derive" { 26 "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES),
27 complete_derive(acc, ctx, token_tree) 27 "allow" | "warn" | "deny" | "forbid" => {
28 } else if path == "feature" { 28 lint::complete_lint(acc, ctx, token_tree.clone(), lint::DEFAULT_LINT_COMPLETIONS);
29 complete_lint(acc, ctx, token_tree, FEATURES) 29 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
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 } 30 }
34 } 31 _ => (),
35 (_, Some(_token_tree)) => {} 32 },
36 _ => complete_attribute_start(acc, ctx, attribute), 33 (None, Some(_)) => (),
34 _ => complete_new_attribute(acc, ctx, attribute),
37 } 35 }
38 Some(()) 36 Some(())
39} 37}
40 38
41fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) { 39fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
40 let attribute_annotated_item_kind = attribute.syntax().parent().map(|it| it.kind());
41 let attributes = attribute_annotated_item_kind.and_then(|kind| {
42 if ast::Expr::can_cast(kind) {
43 Some(EXPR_ATTRIBUTES)
44 } else {
45 KIND_TO_ATTRIBUTES.get(&kind).copied()
46 }
47 });
42 let is_inner = attribute.kind() == ast::AttrKind::Inner; 48 let is_inner = attribute.kind() == ast::AttrKind::Inner;
43 for attr_completion in ATTRIBUTES.iter().filter(|compl| is_inner || !compl.prefer_inner) { 49
50 let add_completion = |attr_completion: &AttrCompletion| {
44 let mut item = CompletionItem::new( 51 let mut item = CompletionItem::new(
45 CompletionKind::Attribute, 52 CompletionKind::Attribute,
46 ctx.source_range(), 53 ctx.source_range(),
@@ -56,9 +63,19 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
56 item.insert_snippet(cap, snippet); 63 item.insert_snippet(cap, snippet);
57 } 64 }
58 65
59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { 66 if is_inner || !attr_completion.prefer_inner {
60 acc.add(item.build()); 67 acc.add(item.build());
61 } 68 }
69 };
70
71 match attributes {
72 Some(applicable) => applicable
73 .iter()
74 .flat_map(|name| ATTRIBUTES.binary_search_by(|attr| attr.key().cmp(name)).ok())
75 .flat_map(|idx| ATTRIBUTES.get(idx))
76 .for_each(add_completion),
77 None if is_inner => ATTRIBUTES.iter().for_each(add_completion),
78 None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion),
62 } 79 }
63} 80}
64 81
@@ -70,6 +87,10 @@ struct AttrCompletion {
70} 87}
71 88
72impl AttrCompletion { 89impl AttrCompletion {
90 fn key(&self) -> &'static str {
91 self.lookup.unwrap_or(self.label)
92 }
93
73 const fn prefer_inner(self) -> AttrCompletion { 94 const fn prefer_inner(self) -> AttrCompletion {
74 AttrCompletion { prefer_inner: true, ..self } 95 AttrCompletion { prefer_inner: true, ..self }
75 } 96 }
@@ -83,26 +104,119 @@ const fn attr(
83 AttrCompletion { label, lookup, snippet, prefer_inner: false } 104 AttrCompletion { label, lookup, snippet, prefer_inner: false }
84} 105}
85 106
107macro_rules! attrs {
108 // attributes applicable to all items
109 [@ { item $($tt:tt)* } {$($acc:tt)*}] => {
110 attrs!(@ { $($tt)* } { $($acc)*, "deprecated", "doc", "dochidden", "docalias", "must_use", "no_mangle" })
111 };
112 // attributes applicable to all adts
113 [@ { adt $($tt:tt)* } {$($acc:tt)*}] => {
114 attrs!(@ { $($tt)* } { $($acc)*, "derive", "repr" })
115 };
116 // attributes applicable to all linkable things aka functions/statics
117 [@ { linkable $($tt:tt)* } {$($acc:tt)*}] => {
118 attrs!(@ { $($tt)* } { $($acc)*, "export_name", "link_name", "link_section" })
119 };
120 // error fallback for nicer error message
121 [@ { $ty:ident $($tt:tt)* } {$($acc:tt)*}] => {
122 compile_error!(concat!("unknown attr subtype ", stringify!($ty)))
123 };
124 // general push down accumulation
125 [@ { $lit:literal $($tt:tt)*} {$($acc:tt)*}] => {
126 attrs!(@ { $($tt)* } { $($acc)*, $lit })
127 };
128 [@ {$($tt:tt)+} {$($tt2:tt)*}] => {
129 compile_error!(concat!("Unexpected input ", stringify!($($tt)+)))
130 };
131 // final output construction
132 [@ {} {$($tt:tt)*}] => { &[$($tt)*] as _ };
133 // starting matcher
134 [$($tt:tt),*] => {
135 attrs!(@ { $($tt)* } { "allow", "cfg", "cfg_attr", "deny", "forbid", "warn" })
136 };
137}
138
139#[rustfmt::skip]
140static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| {
141 use SyntaxKind::*;
142 std::array::IntoIter::new([
143 (
144 SOURCE_FILE,
145 attrs!(
146 item,
147 "crate_name", "feature", "no_implicit_prelude", "no_main", "no_std",
148 "recursion_limit", "type_length_limit", "windows_subsystem"
149 ),
150 ),
151 (MODULE, attrs!(item, "no_implicit_prelude", "path")),
152 (ITEM_LIST, attrs!(item, "no_implicit_prelude")),
153 (MACRO_RULES, attrs!(item, "macro_export", "macro_use")),
154 (MACRO_DEF, attrs!(item)),
155 (EXTERN_CRATE, attrs!(item, "macro_use", "no_link")),
156 (USE, attrs!(item)),
157 (TYPE_ALIAS, attrs!(item)),
158 (STRUCT, attrs!(item, adt, "non_exhaustive")),
159 (ENUM, attrs!(item, adt, "non_exhaustive")),
160 (UNION, attrs!(item, adt)),
161 (CONST, attrs!(item)),
162 (
163 FN,
164 attrs!(
165 item, linkable,
166 "cold", "ignore", "inline", "must_use", "panic_handler", "proc_macro",
167 "proc_macro_derive", "proc_macro_attribute", "should_panic", "target_feature",
168 "test", "track_caller"
169 ),
170 ),
171 (STATIC, attrs!(item, linkable, "global_allocator", "used")),
172 (TRAIT, attrs!(item, "must_use")),
173 (IMPL, attrs!(item, "automatically_derived")),
174 (ASSOC_ITEM_LIST, attrs!(item)),
175 (EXTERN_BLOCK, attrs!(item, "link")),
176 (EXTERN_ITEM_LIST, attrs!(item, "link")),
177 (MACRO_CALL, attrs!()),
178 (SELF_PARAM, attrs!()),
179 (PARAM, attrs!()),
180 (RECORD_FIELD, attrs!()),
181 (VARIANT, attrs!("non_exhaustive")),
182 (TYPE_PARAM, attrs!()),
183 (CONST_PARAM, attrs!()),
184 (LIFETIME_PARAM, attrs!()),
185 (LET_STMT, attrs!()),
186 (EXPR_STMT, attrs!()),
187 (LITERAL, attrs!()),
188 (RECORD_EXPR_FIELD_LIST, attrs!()),
189 (RECORD_EXPR_FIELD, attrs!()),
190 (MATCH_ARM_LIST, attrs!()),
191 (MATCH_ARM, attrs!()),
192 (IDENT_PAT, attrs!()),
193 (RECORD_PAT_FIELD, attrs!()),
194 ])
195 .collect()
196});
197const EXPR_ATTRIBUTES: &[&str] = attrs!();
198
86/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index 199/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index
200// Keep these sorted for the binary search!
87const ATTRIBUTES: &[AttrCompletion] = &[ 201const ATTRIBUTES: &[AttrCompletion] = &[
88 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), 202 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
89 attr("automatically_derived", None, None), 203 attr("automatically_derived", None, None),
90 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
91 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")), 204 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
205 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
92 attr("cold", None, None), 206 attr("cold", None, None),
93 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#)) 207 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#))
94 .prefer_inner(), 208 .prefer_inner(),
95 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")), 209 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
96 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)), 210 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)),
97 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)), 211 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
212 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
213 attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
214 attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
98 attr( 215 attr(
99 r#"export_name = "…""#, 216 r#"export_name = "…""#,
100 Some("export_name"), 217 Some("export_name"),
101 Some(r#"export_name = "${0:exported_symbol_name}""#), 218 Some(r#"export_name = "${0:exported_symbol_name}""#),
102 ), 219 ),
103 attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
104 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
105 attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
106 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(), 220 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
107 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), 221 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
108 // FIXME: resolve through macro resolution? 222 // FIXME: resolve through macro resolution?
@@ -119,8 +233,8 @@ const ATTRIBUTES: &[AttrCompletion] = &[
119 attr("macro_export", None, None), 233 attr("macro_export", None, None),
120 attr("macro_use", None, None), 234 attr("macro_use", None, None),
121 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)), 235 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)),
122 attr("no_link", None, None).prefer_inner(),
123 attr("no_implicit_prelude", None, None).prefer_inner(), 236 attr("no_implicit_prelude", None, None).prefer_inner(),
237 attr("no_link", None, None).prefer_inner(),
124 attr("no_main", None, None).prefer_inner(), 238 attr("no_main", None, None).prefer_inner(),
125 attr("no_mangle", None, None), 239 attr("no_mangle", None, None),
126 attr("no_std", None, None).prefer_inner(), 240 attr("no_std", None, None).prefer_inner(),
@@ -153,412 +267,492 @@ const ATTRIBUTES: &[AttrCompletion] = &[
153 .prefer_inner(), 267 .prefer_inner(),
154]; 268];
155 269
156fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { 270fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Option<FxHashSet<String>> {
157 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) { 271 let (l_paren, r_paren) = derive_input.l_paren_token().zip(derive_input.r_paren_token())?;
158 for derive_completion in DEFAULT_DERIVE_COMPLETIONS 272 let mut input_derives = FxHashSet::default();
159 .iter() 273 let mut tokens = derive_input
160 .filter(|completion| !existing_derives.contains(completion.label)) 274 .syntax()
161 { 275 .children_with_tokens()
162 let mut components = vec![derive_completion.label]; 276 .filter_map(NodeOrToken::into_token)
163 components.extend( 277 .skip_while(|token| token != &l_paren)
164 derive_completion 278 .skip(1)
165 .dependencies 279 .take_while(|token| token != &r_paren)
166 .iter() 280 .peekable();
167 .filter(|&&dependency| !existing_derives.contains(dependency)), 281 let mut input = String::new();
168 ); 282 while tokens.peek().is_some() {
169 let lookup = components.join(", "); 283 for token in tokens.by_ref().take_while(|t| t.kind() != T![,]) {
170 let label = components.iter().rev().join(", "); 284 input.push_str(token.text());
171 let mut item =
172 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
173 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
174 item.add_to(acc);
175 }
176
177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
178 let mut item = CompletionItem::new(
179 CompletionKind::Attribute,
180 ctx.source_range(),
181 custom_derive_name,
182 );
183 item.kind(CompletionItemKind::Attribute);
184 item.add_to(acc);
185 } 285 }
186 }
187}
188 286
189fn complete_lint( 287 if !input.is_empty() {
190 acc: &mut Completions, 288 input_derives.insert(input.trim().to_owned());
191 ctx: &CompletionContext,
192 derive_input: ast::TokenTree,
193 lints_completions: &[LintCompletion],
194) {
195 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
196 for lint_completion in lints_completions
197 .into_iter()
198 .filter(|completion| !existing_lints.contains(completion.label))
199 {
200 let mut item = CompletionItem::new(
201 CompletionKind::Attribute,
202 ctx.source_range(),
203 lint_completion.label,
204 );
205 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description);
206 item.add_to(acc)
207 } 289 }
208 }
209}
210
211fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
212 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
213 (Some(left_paren), Some(right_paren))
214 if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] =>
215 {
216 let mut input_derives = FxHashSet::default();
217 let mut current_derive = String::new();
218 for token in derive_input
219 .syntax()
220 .children_with_tokens()
221 .filter_map(|token| token.into_token())
222 .skip_while(|token| token != &left_paren)
223 .skip(1)
224 .take_while(|token| token != &right_paren)
225 {
226 if T![,] == token.kind() {
227 if !current_derive.is_empty() {
228 input_derives.insert(current_derive);
229 current_derive = String::new();
230 }
231 } else {
232 current_derive.push_str(token.text().trim());
233 }
234 }
235 290
236 if !current_derive.is_empty() { 291 input.clear();
237 input_derives.insert(current_derive);
238 }
239 Ok(input_derives)
240 }
241 _ => Err(()),
242 } 292 }
243}
244 293
245fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { 294 Some(input_derives)
246 let mut result = FxHashSet::default();
247 ctx.scope.process_all_names(&mut |name, scope_def| {
248 if let hir::ScopeDef::MacroDef(mac) = scope_def {
249 // FIXME kind() doesn't check whether proc-macro is a derive
250 if mac.kind() == hir::MacroKind::Derive || mac.kind() == hir::MacroKind::ProcMacro {
251 result.insert(name.to_string());
252 }
253 }
254 });
255 result
256}
257
258struct DeriveCompletion {
259 label: &'static str,
260 dependencies: &'static [&'static str],
261} 295}
262 296
263/// Standard Rust derives and the information about their dependencies
264/// (the dependencies are needed so that the main derive don't break the compilation when added)
265const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
266 DeriveCompletion { label: "Clone", dependencies: &[] },
267 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
268 DeriveCompletion { label: "Debug", dependencies: &[] },
269 DeriveCompletion { label: "Default", dependencies: &[] },
270 DeriveCompletion { label: "Hash", dependencies: &[] },
271 DeriveCompletion { label: "PartialEq", dependencies: &[] },
272 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
273 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
274 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
275];
276
277pub(crate) struct LintCompletion {
278 pub(crate) label: &'static str,
279 pub(crate) description: &'static str,
280}
281
282#[rustfmt::skip]
283const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
284 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"# },
285 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
286 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
287 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
288 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
289 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
290 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
291 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
292 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
293 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
294 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
295 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
296 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
297 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
298 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
299 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
300 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
301 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
302 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
303 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
304 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
305 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
306 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
307 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
308 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
309 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
310 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
311 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
312 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
313 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
314 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
315 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
316 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
317 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
318 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
319 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
320 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
321 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
322 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
323 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
324 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
325 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
326 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
327 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
328 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
329 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
330 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
331 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
332 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
333 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
334 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
335 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
336 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
337 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
338 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
339 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
340 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
341 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
342 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
343 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
344 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
345 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
346 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
347 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
348 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
349 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
350 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
351 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
352 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
353 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
354 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
355 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
356 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
357 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
358 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
359 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
360 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
361 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
362 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
363 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
364 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
365 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
366 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
367 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
368 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
369 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
370 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
371 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
372 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
373 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
374 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
375 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
376 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
377 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
378 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
379 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
380 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
381 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
382 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
383 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
384 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
385 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
386 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
387 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
388 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"# },
389 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
390 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
391 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
392 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
393 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
394 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
395 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
396 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
397 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
398 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
399];
400
401#[cfg(test)] 297#[cfg(test)]
402mod tests { 298mod tests {
299 use super::*;
300
403 use expect_test::{expect, Expect}; 301 use expect_test::{expect, Expect};
404 302
405 use crate::{test_utils::completion_list, CompletionKind}; 303 use crate::{test_utils::completion_list, CompletionKind};
406 304
305 #[test]
306 fn attributes_are_sorted() {
307 let mut attrs = ATTRIBUTES.iter().map(|attr| attr.key());
308 let mut prev = attrs.next().unwrap();
309
310 attrs.for_each(|next| {
311 assert!(
312 prev < next,
313 r#"ATTRIBUTES array is not sorted, "{}" should come after "{}""#,
314 prev,
315 next
316 );
317 prev = next;
318 });
319 }
320
407 fn check(ra_fixture: &str, expect: Expect) { 321 fn check(ra_fixture: &str, expect: Expect) {
408 let actual = completion_list(ra_fixture, CompletionKind::Attribute); 322 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
409 expect.assert_eq(&actual); 323 expect.assert_eq(&actual);
410 } 324 }
411 325
412 #[test] 326 #[test]
413 fn empty_derive_completion() { 327 fn test_attribute_completion_inside_nested_attr() {
328 check(r#"#[cfg($0)]"#, expect![[]])
329 }
330
331 #[test]
332 fn test_attribute_completion_with_existing_attr() {
414 check( 333 check(
415 r#" 334 r#"#[no_mangle] #[$0] mcall!();"#,
416#[derive($0)]
417struct Test {}
418 "#,
419 expect![[r#" 335 expect![[r#"
420 at Clone 336 at allow(…)
421 at Clone, Copy 337 at cfg(…)
422 at Debug 338 at cfg_attr(…)
423 at Default 339 at deny(…)
424 at Hash 340 at forbid(…)
425 at PartialEq 341 at warn(…)
426 at PartialEq, Eq 342 "#]],
427 at PartialEq, PartialOrd 343 )
428 at PartialEq, Eq, PartialOrd, Ord 344 }
345
346 #[test]
347 fn complete_attribute_on_source_file() {
348 check(
349 r#"#![$0]"#,
350 expect![[r#"
351 at allow(…)
352 at cfg(…)
353 at cfg_attr(…)
354 at deny(…)
355 at forbid(…)
356 at warn(…)
357 at deprecated
358 at doc = "…"
359 at doc(hidden)
360 at doc(alias = "…")
361 at must_use
362 at no_mangle
363 at crate_name = ""
364 at feature(…)
365 at no_implicit_prelude
366 at no_main
367 at no_std
368 at recursion_limit = …
369 at type_length_limit = …
370 at windows_subsystem = "…"
429 "#]], 371 "#]],
430 ); 372 );
431 } 373 }
432 374
433 #[test] 375 #[test]
434 fn no_completion_for_incorrect_derive() { 376 fn complete_attribute_on_module() {
435 check( 377 check(
436 r#" 378 r#"#[$0] mod foo;"#,
437#[derive{$0)] 379 expect![[r#"
438struct Test {} 380 at allow(…)
439"#, 381 at cfg(…)
440 expect![[r#""#]], 382 at cfg_attr(…)
441 ) 383 at deny(…)
384 at forbid(…)
385 at warn(…)
386 at deprecated
387 at doc = "…"
388 at doc(hidden)
389 at doc(alias = "…")
390 at must_use
391 at no_mangle
392 at path = "…"
393 "#]],
394 );
395 check(
396 r#"mod foo {#![$0]}"#,
397 expect![[r#"
398 at allow(…)
399 at cfg(…)
400 at cfg_attr(…)
401 at deny(…)
402 at forbid(…)
403 at warn(…)
404 at deprecated
405 at doc = "…"
406 at doc(hidden)
407 at doc(alias = "…")
408 at must_use
409 at no_mangle
410 at no_implicit_prelude
411 "#]],
412 );
442 } 413 }
443 414
444 #[test] 415 #[test]
445 fn derive_with_input_completion() { 416 fn complete_attribute_on_macro_rules() {
446 check( 417 check(
447 r#" 418 r#"#[$0] macro_rules! foo {}"#,
448#[derive(serde::Serialize, PartialEq, $0)]
449struct Test {}
450"#,
451 expect![[r#" 419 expect![[r#"
452 at Clone 420 at allow(…)
453 at Clone, Copy 421 at cfg(…)
454 at Debug 422 at cfg_attr(…)
455 at Default 423 at deny(…)
456 at Hash 424 at forbid(…)
457 at Eq 425 at warn(…)
458 at PartialOrd 426 at deprecated
459 at Eq, PartialOrd, Ord 427 at doc = "…"
428 at doc(hidden)
429 at doc(alias = "…")
430 at must_use
431 at no_mangle
432 at macro_export
433 at macro_use
460 "#]], 434 "#]],
461 ) 435 );
462 } 436 }
463 437
464 #[test] 438 #[test]
465 fn test_attribute_completion() { 439 fn complete_attribute_on_macro_def() {
466 check( 440 check(
467 r#"#[$0]"#, 441 r#"#[$0] macro foo {}"#,
468 expect![[r#" 442 expect![[r#"
469 at allow(…) 443 at allow(…)
470 at automatically_derived 444 at cfg(…)
471 at cfg_attr(…) 445 at cfg_attr(…)
446 at deny(…)
447 at forbid(…)
448 at warn(…)
449 at deprecated
450 at doc = "…"
451 at doc(hidden)
452 at doc(alias = "…")
453 at must_use
454 at no_mangle
455 "#]],
456 );
457 }
458
459 #[test]
460 fn complete_attribute_on_extern_crate() {
461 check(
462 r#"#[$0] extern crate foo;"#,
463 expect![[r#"
464 at allow(…)
472 at cfg(…) 465 at cfg(…)
473 at cold 466 at cfg_attr(…)
474 at deny(…) 467 at deny(…)
468 at forbid(…)
469 at warn(…)
475 at deprecated 470 at deprecated
476 at derive() 471 at doc = ""
477 at export_name = "…" 472 at doc(hidden)
478 at doc(alias = "…") 473 at doc(alias = "…")
474 at must_use
475 at no_mangle
476 at macro_use
477 "#]],
478 );
479 }
480
481 #[test]
482 fn complete_attribute_on_use() {
483 check(
484 r#"#[$0] use foo;"#,
485 expect![[r#"
486 at allow(…)
487 at cfg(…)
488 at cfg_attr(…)
489 at deny(…)
490 at forbid(…)
491 at warn(…)
492 at deprecated
479 at doc = "…" 493 at doc = "…"
480 at doc(hidden) 494 at doc(hidden)
495 at doc(alias = "…")
496 at must_use
497 at no_mangle
498 "#]],
499 );
500 }
501
502 #[test]
503 fn complete_attribute_on_type_alias() {
504 check(
505 r#"#[$0] type foo = ();"#,
506 expect![[r#"
507 at allow(…)
508 at cfg(…)
509 at cfg_attr(…)
510 at deny(…)
481 at forbid(…) 511 at forbid(…)
482 at ignore = "…" 512 at warn(…)
483 at inline 513 at deprecated
484 at link 514 at doc = "…"
485 at link_name = "…" 515 at doc(hidden)
486 at link_section = "…" 516 at doc(alias = "…")
487 at macro_export
488 at macro_use
489 at must_use 517 at must_use
490 at no_mangle 518 at no_mangle
519 "#]],
520 );
521 }
522
523 #[test]
524 fn complete_attribute_on_struct() {
525 check(
526 r#"#[$0] struct Foo;"#,
527 expect![[r#"
528 at allow(…)
529 at cfg(…)
530 at cfg_attr(…)
531 at deny(…)
532 at forbid(…)
533 at warn(…)
534 at deprecated
535 at doc = "…"
536 at doc(hidden)
537 at doc(alias = "…")
538 at must_use
539 at no_mangle
540 at derive(…)
541 at repr(…)
491 at non_exhaustive 542 at non_exhaustive
492 at path = "…" 543 "#]],
493 at proc_macro 544 );
494 at proc_macro_attribute 545 }
495 at proc_macro_derive(…) 546
547 #[test]
548 fn complete_attribute_on_enum() {
549 check(
550 r#"#[$0] enum Foo {}"#,
551 expect![[r#"
552 at allow(…)
553 at cfg(…)
554 at cfg_attr(…)
555 at deny(…)
556 at forbid(…)
557 at warn(…)
558 at deprecated
559 at doc = "…"
560 at doc(hidden)
561 at doc(alias = "…")
562 at must_use
563 at no_mangle
564 at derive(…)
496 at repr(…) 565 at repr(…)
497 at should_panic 566 at non_exhaustive
498 at target_feature = "…" 567 "#]],
499 at test 568 );
500 at track_caller 569 }
501 at used 570
571 #[test]
572 fn complete_attribute_on_const() {
573 check(
574 r#"#[$0] const FOO: () = ();"#,
575 expect![[r#"
576 at allow(…)
577 at cfg(…)
578 at cfg_attr(…)
579 at deny(…)
580 at forbid(…)
502 at warn(…) 581 at warn(…)
582 at deprecated
583 at doc = "…"
584 at doc(hidden)
585 at doc(alias = "…")
586 at must_use
587 at no_mangle
503 "#]], 588 "#]],
504 ) 589 );
505 } 590 }
506 591
507 #[test] 592 #[test]
508 fn test_attribute_completion_inside_nested_attr() { 593 fn complete_attribute_on_static() {
509 check(r#"#[cfg($0)]"#, expect![[]]) 594 check(
595 r#"#[$0] static FOO: () = ()"#,
596 expect![[r#"
597 at allow(…)
598 at cfg(…)
599 at cfg_attr(…)
600 at deny(…)
601 at forbid(…)
602 at warn(…)
603 at deprecated
604 at doc = "…"
605 at doc(hidden)
606 at doc(alias = "…")
607 at must_use
608 at no_mangle
609 at export_name = "…"
610 at link_name = "…"
611 at link_section = "…"
612 at used
613 "#]],
614 );
510 } 615 }
511 616
512 #[test] 617 #[test]
513 fn test_inner_attribute_completion() { 618 fn complete_attribute_on_trait() {
514 check( 619 check(
515 r"#![$0]", 620 r#"#[$0] trait Foo {}"#,
516 expect![[r#" 621 expect![[r#"
517 at allow(…) 622 at allow(…)
518 at automatically_derived 623 at cfg(…)
519 at cfg_attr(…) 624 at cfg_attr(…)
625 at deny(…)
626 at forbid(…)
627 at warn(…)
628 at deprecated
629 at doc = "…"
630 at doc(hidden)
631 at doc(alias = "…")
632 at must_use
633 at no_mangle
634 at must_use
635 "#]],
636 );
637 }
638
639 #[test]
640 fn complete_attribute_on_impl() {
641 check(
642 r#"#[$0] impl () {}"#,
643 expect![[r#"
644 at allow(…)
520 at cfg(…) 645 at cfg(…)
521 at cold 646 at cfg_attr(…)
522 at crate_name = ""
523 at deny(…) 647 at deny(…)
648 at forbid(…)
649 at warn(…)
524 at deprecated 650 at deprecated
525 at derive() 651 at doc = ""
526 at export_name = "…" 652 at doc(hidden)
527 at doc(alias = "…") 653 at doc(alias = "…")
654 at must_use
655 at no_mangle
656 at automatically_derived
657 "#]],
658 );
659 check(
660 r#"impl () {#![$0]}"#,
661 expect![[r#"
662 at allow(…)
663 at cfg(…)
664 at cfg_attr(…)
665 at deny(…)
666 at forbid(…)
667 at warn(…)
668 at deprecated
528 at doc = "…" 669 at doc = "…"
529 at doc(hidden) 670 at doc(hidden)
530 at feature(…) 671 at doc(alias = "…")
672 at must_use
673 at no_mangle
674 "#]],
675 );
676 }
677
678 #[test]
679 fn complete_attribute_on_extern_block() {
680 check(
681 r#"#[$0] extern {}"#,
682 expect![[r#"
683 at allow(…)
684 at cfg(…)
685 at cfg_attr(…)
686 at deny(…)
531 at forbid(…) 687 at forbid(…)
532 at global_allocator 688 at warn(…)
533 at ignore = "…" 689 at deprecated
534 at inline 690 at doc = "…"
691 at doc(hidden)
692 at doc(alias = "…")
693 at must_use
694 at no_mangle
535 at link 695 at link
536 at link_name = "…" 696 "#]],
537 at link_section = "…" 697 );
538 at macro_export 698 check(
539 at macro_use 699 r#"extern {#![$0]}"#,
700 expect![[r#"
701 at allow(…)
702 at cfg(…)
703 at cfg_attr(…)
704 at deny(…)
705 at forbid(…)
706 at warn(…)
707 at deprecated
708 at doc = "…"
709 at doc(hidden)
710 at doc(alias = "…")
540 at must_use 711 at must_use
541 at no_link
542 at no_implicit_prelude
543 at no_main
544 at no_mangle 712 at no_mangle
545 at no_std 713 at link
714 "#]],
715 );
716 }
717
718 #[test]
719 fn complete_attribute_on_variant() {
720 check(
721 r#"enum Foo { #[$0] Bar }"#,
722 expect![[r#"
723 at allow(…)
724 at cfg(…)
725 at cfg_attr(…)
726 at deny(…)
727 at forbid(…)
728 at warn(…)
546 at non_exhaustive 729 at non_exhaustive
547 at panic_handler 730 "#]],
548 at path = "…" 731 );
549 at proc_macro 732 }
550 at proc_macro_attribute 733
551 at proc_macro_derive(…) 734 #[test]
552 at recursion_limit = … 735 fn complete_attribute_on_expr() {
553 at repr(…) 736 check(
554 at should_panic 737 r#"fn main() { #[$0] foo() }"#,
555 at target_feature = "…" 738 expect![[r#"
556 at test 739 at allow(…)
557 at track_caller 740 at cfg(…)
558 at type_length_limit = … 741 at cfg_attr(…)
559 at used 742 at deny(…)
743 at forbid(…)
744 at warn(…)
745 "#]],
746 );
747 check(
748 r#"fn main() { #[$0] foo(); }"#,
749 expect![[r#"
750 at allow(…)
751 at cfg(…)
752 at cfg_attr(…)
753 at deny(…)
754 at forbid(…)
560 at warn(…) 755 at warn(…)
561 at windows_subsystem = "…"
562 "#]], 756 "#]],
563 ); 757 );
564 } 758 }
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
new file mode 100644
index 000000000..0bc3eab98
--- /dev/null
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -0,0 +1,147 @@
1//! Completion for derives
2use itertools::Itertools;
3use rustc_hash::FxHashSet;
4use syntax::ast;
5
6use crate::{
7 context::CompletionContext,
8 item::{CompletionItem, CompletionItemKind, CompletionKind},
9 Completions,
10};
11
12pub(super) fn complete_derive(
13 acc: &mut Completions,
14 ctx: &CompletionContext,
15 derive_input: ast::TokenTree,
16) {
17 if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) {
18 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
19 .iter()
20 .filter(|completion| !existing_derives.contains(completion.label))
21 {
22 let mut components = vec![derive_completion.label];
23 components.extend(
24 derive_completion
25 .dependencies
26 .iter()
27 .filter(|&&dependency| !existing_derives.contains(dependency)),
28 );
29 let lookup = components.join(", ");
30 let label = components.iter().rev().join(", ");
31 let mut item =
32 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
33 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
34 item.add_to(acc);
35 }
36
37 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
38 let mut item = CompletionItem::new(
39 CompletionKind::Attribute,
40 ctx.source_range(),
41 custom_derive_name,
42 );
43 item.kind(CompletionItemKind::Attribute);
44 item.add_to(acc);
45 }
46 }
47}
48
49fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
50 let mut result = FxHashSet::default();
51 ctx.scope.process_all_names(&mut |name, scope_def| {
52 if let hir::ScopeDef::MacroDef(mac) = scope_def {
53 if mac.kind() == hir::MacroKind::Derive {
54 result.insert(name.to_string());
55 }
56 }
57 });
58 result
59}
60
61struct DeriveCompletion {
62 label: &'static str,
63 dependencies: &'static [&'static str],
64}
65
66/// Standard Rust derives and the information about their dependencies
67/// (the dependencies are needed so that the main derive don't break the compilation when added)
68const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
69 DeriveCompletion { label: "Clone", dependencies: &[] },
70 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
71 DeriveCompletion { label: "Debug", dependencies: &[] },
72 DeriveCompletion { label: "Default", dependencies: &[] },
73 DeriveCompletion { label: "Hash", dependencies: &[] },
74 DeriveCompletion { label: "PartialEq", dependencies: &[] },
75 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
76 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
77 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
78];
79
80#[cfg(test)]
81mod tests {
82 use expect_test::{expect, Expect};
83
84 use crate::{test_utils::completion_list, CompletionKind};
85
86 fn check(ra_fixture: &str, expect: Expect) {
87 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
88 expect.assert_eq(&actual);
89 }
90
91 #[test]
92 fn no_completion_for_incorrect_derive() {
93 check(r#"#[derive{$0)] struct Test;"#, expect![[]])
94 }
95
96 #[test]
97 fn empty_derive() {
98 check(
99 r#"#[derive($0)] struct Test;"#,
100 expect![[r#"
101 at Clone
102 at Clone, Copy
103 at Debug
104 at Default
105 at Hash
106 at PartialEq
107 at PartialEq, Eq
108 at PartialEq, PartialOrd
109 at PartialEq, Eq, PartialOrd, Ord
110 "#]],
111 );
112 }
113
114 #[test]
115 fn derive_with_input() {
116 check(
117 r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#,
118 expect![[r#"
119 at Clone
120 at Clone, Copy
121 at Debug
122 at Default
123 at Hash
124 at Eq
125 at PartialOrd
126 at Eq, PartialOrd, Ord
127 "#]],
128 )
129 }
130
131 #[test]
132 fn derive_with_input2() {
133 check(
134 r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#,
135 expect![[r#"
136 at Clone
137 at Clone, Copy
138 at Debug
139 at Default
140 at Hash
141 at Eq
142 at PartialOrd
143 at Eq, PartialOrd, Ord
144 "#]],
145 )
146 }
147}
diff --git a/crates/ide_completion/src/completions/attribute/lint.rs b/crates/ide_completion/src/completions/attribute/lint.rs
new file mode 100644
index 000000000..403630dce
--- /dev/null
+++ b/crates/ide_completion/src/completions/attribute/lint.rs
@@ -0,0 +1,187 @@
1//! Completion for lints
2use syntax::ast;
3
4use crate::{
5 context::CompletionContext,
6 item::{CompletionItem, CompletionItemKind, CompletionKind},
7 Completions,
8};
9
10pub(super) fn complete_lint(
11 acc: &mut Completions,
12 ctx: &CompletionContext,
13 derive_input: ast::TokenTree,
14 lints_completions: &[LintCompletion],
15) {
16 if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) {
17 for lint_completion in lints_completions
18 .into_iter()
19 .filter(|completion| !existing_lints.contains(completion.label))
20 {
21 let mut item = CompletionItem::new(
22 CompletionKind::Attribute,
23 ctx.source_range(),
24 lint_completion.label,
25 );
26 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description);
27 item.add_to(acc)
28 }
29 }
30}
31
32pub(crate) struct LintCompletion {
33 pub(crate) label: &'static str,
34 pub(crate) description: &'static str,
35}
36
37#[rustfmt::skip]
38pub(super) const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
39 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"# },
40 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
41 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
42 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
43 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
44 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
45 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
46 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
47 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
48 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
49 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
50 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
51 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
52 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
53 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
54 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
55 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
56 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
57 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
58 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
59 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
60 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
61 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
62 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
63 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
64 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
65 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
66 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
67 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
68 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
69 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
70 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
71 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
72 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
73 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
74 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
75 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
76 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
77 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
78 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
79 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
80 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
81 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
82 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
83 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
84 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
85 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
86 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
87 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
88 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
89 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
90 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
91 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
92 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
93 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
94 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
95 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
96 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
97 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
98 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
99 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
100 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
101 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
102 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
103 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
104 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
105 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
106 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
107 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
108 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
109 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
110 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
111 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
112 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
113 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
114 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
115 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
116 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
117 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
118 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
119 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
120 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
121 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
122 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
123 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
124 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
125 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
126 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
127 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
128 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
129 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
130 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
131 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
132 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
133 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
134 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
135 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
136 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
137 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
138 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
139 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
140 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
141 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
142 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
143 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"# },
144 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
145 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
146 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
147 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
148 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
149 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
150 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
151 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
152 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
153 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
154];
155
156#[cfg(test)]
157mod tests {
158
159 use crate::test_utils::check_edit;
160
161 #[test]
162 fn check_empty() {
163 check_edit(
164 "deprecated",
165 r#"#[allow($0)] struct Test;"#,
166 r#"#[allow(deprecated)] struct Test;"#,
167 )
168 }
169
170 #[test]
171 fn check_with_existing() {
172 check_edit(
173 "deprecated",
174 r#"#[allow(keyword_idents, $0)] struct Test;"#,
175 r#"#[allow(keyword_idents, deprecated)] struct Test;"#,
176 )
177 }
178
179 #[test]
180 fn check_qualified() {
181 check_edit(
182 "deprecated",
183 r#"#[allow(keyword_idents, $0)] struct Test;"#,
184 r#"#[allow(keyword_idents, deprecated)] struct Test;"#,
185 )
186 }
187}
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 9d5b61562..df27e7a84 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -111,11 +111,9 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
111 return None; 111 return None;
112 } 112 }
113 if ctx.use_item_syntax.is_some() 113 if ctx.use_item_syntax.is_some()
114 || ctx.attribute_under_caret.is_some() 114 || ctx.is_path_disallowed()
115 || ctx.mod_declaration_under_caret.is_some() 115 || ctx.expects_item()
116 || ctx.record_lit_syntax.is_some() 116 || ctx.expects_assoc_item()
117 || ctx.has_trait_parent
118 || ctx.has_impl_parent
119 { 117 {
120 return None; 118 return None;
121 } 119 }
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 61b667104..e71a04b6e 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -2,7 +2,7 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use syntax::SyntaxKind; 5use syntax::{SyntaxKind, T};
6 6
7use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; 7use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
8 8
@@ -48,107 +48,102 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
48 cov_mark::hit!(no_keyword_completion_in_record_lit); 48 cov_mark::hit!(no_keyword_completion_in_record_lit);
49 return; 49 return;
50 } 50 }
51 let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet);
51 52
52 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; 53 let expects_assoc_item = ctx.expects_assoc_item();
53 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { 54 let has_block_expr_parent = ctx.has_block_expr_parent();
54 add_keyword(ctx, acc, "where", "where "); 55 let expects_item = ctx.expects_item();
56
57 if ctx.has_impl_or_trait_prev_sibling() {
58 // FIXME this also incorrectly shows up after a complete trait/impl
59 add_keyword("where", "where ");
55 return; 60 return;
56 } 61 }
57 if ctx.unsafe_is_prev { 62 if ctx.previous_token_is(T![unsafe]) {
58 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { 63 if expects_item || expects_assoc_item || has_block_expr_parent {
59 add_keyword(ctx, acc, "fn", "fn $0() {}") 64 add_keyword("fn", "fn $1($2) {\n $0\n}")
60 } 65 }
61 66
62 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { 67 if expects_item || has_block_expr_parent {
63 add_keyword(ctx, acc, "trait", "trait $0 {}"); 68 add_keyword("trait", "trait $1 {\n $0\n}");
64 add_keyword(ctx, acc, "impl", "impl $0 {}"); 69 add_keyword("impl", "impl $1 {\n $0\n}");
65 } 70 }
66 71
67 return; 72 return;
68 } 73 }
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 74
79 if ctx.has_item_list_or_source_file_parent { 75 if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() {
80 add_keyword(ctx, acc, "enum", "enum $0 {}"); 76 add_keyword("pub(crate)", "pub(crate) ");
81 add_keyword(ctx, acc, "struct", "struct $0"); 77 add_keyword("pub", "pub ");
82 add_keyword(ctx, acc, "union", "union $0 {}");
83 } 78 }
84 79
85 if ctx.is_expr { 80 if expects_item || expects_assoc_item || has_block_expr_parent || ctx.is_match_arm {
86 add_keyword(ctx, acc, "match", "match $0 {}"); 81 add_keyword("unsafe", "unsafe ");
87 add_keyword(ctx, acc, "while", "while $0 {}");
88 add_keyword(ctx, acc, "while let", "while let $1 = $0 {}");
89 add_keyword(ctx, acc, "loop", "loop {$0}");
90 add_keyword(ctx, acc, "if", "if $0 {}");
91 add_keyword(ctx, acc, "if let", "if let $1 = $0 {}");
92 add_keyword(ctx, acc, "for", "for $1 in $0 {}");
93 } 82 }
94 83
95 if ctx.if_is_prev || ctx.block_expr_parent { 84 if expects_item || expects_assoc_item || has_block_expr_parent {
96 add_keyword(ctx, acc, "let", "let "); 85 add_keyword("fn", "fn $1($2) {\n $0\n}");
86 add_keyword("const", "const $0");
87 add_keyword("type", "type $0");
97 } 88 }
98 89
99 if ctx.after_if { 90 if expects_item || has_block_expr_parent {
100 add_keyword(ctx, acc, "else", "else {$0}"); 91 add_keyword("use", "use $0");
101 add_keyword(ctx, acc, "else if", "else if $0 {}"); 92 add_keyword("impl", "impl $1 {\n $0\n}");
93 add_keyword("trait", "trait $1 {\n $0\n}");
94 add_keyword("static", "static $0");
95 add_keyword("extern", "extern $0");
96 add_keyword("mod", "mod $0");
102 } 97 }
103 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { 98
104 add_keyword(ctx, acc, "mod", "mod $0"); 99 if expects_item {
100 add_keyword("enum", "enum $1 {\n $0\n}");
101 add_keyword("struct", "struct $0");
102 add_keyword("union", "union $1 {\n $0\n}");
105 } 103 }
106 if ctx.bind_pat_parent || ctx.ref_pat_parent { 104
107 add_keyword(ctx, acc, "mut", "mut "); 105 if ctx.expects_expression() {
106 add_keyword("match", "match $1 {\n $0\n}");
107 add_keyword("while", "while $1 {\n $0\n}");
108 add_keyword("while let", "while let $1 = $2 {\n $0\n}");
109 add_keyword("loop", "loop {\n $0\n}");
110 add_keyword("if", "if $1 {\n $0\n}");
111 add_keyword("if let", "if let $1 = $2 {\n $0\n}");
112 add_keyword("for", "for $1 in $2 {\n $0\n}");
108 } 113 }
109 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent 114
110 { 115 if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent {
111 add_keyword(ctx, acc, "const", "const "); 116 add_keyword("let", "let ");
112 add_keyword(ctx, acc, "type", "type ");
113 } 117 }
114 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { 118
115 add_keyword(ctx, acc, "static", "static "); 119 if ctx.after_if() {
116 }; 120 add_keyword("else", "else {\n $0\n}");
117 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { 121 add_keyword("else if", "else if $1 {\n $0\n}");
118 add_keyword(ctx, acc, "extern", "extern ");
119 } 122 }
120 if ctx.has_item_list_or_source_file_parent 123
121 || has_trait_or_impl_parent 124 if ctx.expects_ident_pat_or_ref_expr() {
122 || ctx.block_expr_parent 125 add_keyword("mut", "mut ");
123 || ctx.is_match_arm
124 {
125 add_keyword(ctx, acc, "unsafe", "unsafe ");
126 } 126 }
127
127 if ctx.in_loop_body { 128 if ctx.in_loop_body {
128 if ctx.can_be_stmt { 129 if ctx.can_be_stmt {
129 add_keyword(ctx, acc, "continue", "continue;"); 130 add_keyword("continue", "continue;");
130 add_keyword(ctx, acc, "break", "break;"); 131 add_keyword("break", "break;");
131 } else { 132 } else {
132 add_keyword(ctx, acc, "continue", "continue"); 133 add_keyword("continue", "continue");
133 add_keyword(ctx, acc, "break", "break"); 134 add_keyword("break", "break");
134 } 135 }
135 } 136 }
136 if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent | ctx.has_field_list_parent {
137 add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
138 add_keyword(ctx, acc, "pub", "pub ");
139 }
140 137
141 if !ctx.is_trivial_path { 138 if !ctx.is_trivial_path {
142 return; 139 return;
143 } 140 }
144 let fn_def = match &ctx.function_syntax { 141 let fn_def = match &ctx.function_def {
145 Some(it) => it, 142 Some(it) => it,
146 None => return, 143 None => return,
147 }; 144 };
148 145
149 add_keyword( 146 add_keyword(
150 ctx,
151 acc,
152 "return", 147 "return",
153 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { 148 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
154 (true, true) => "return $0;", 149 (true, true) => "return $0;",
@@ -165,15 +160,12 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
165 160
166 match ctx.config.snippet_cap { 161 match ctx.config.snippet_cap {
167 Some(cap) => { 162 Some(cap) => {
168 let tmp; 163 if snippet.ends_with('}') && ctx.incomplete_let {
169 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
170 cov_mark::hit!(let_semi); 164 cov_mark::hit!(let_semi);
171 tmp = format!("{};", snippet); 165 item.insert_snippet(cap, format!("{};", snippet));
172 &tmp
173 } else { 166 } else {
174 snippet 167 item.insert_snippet(cap, snippet);
175 }; 168 }
176 item.insert_snippet(cap, snippet);
177 } 169 }
178 None => { 170 None => {
179 item.insert_text(if snippet.contains('$') { kw } else { snippet }); 171 item.insert_text(if snippet.contains('$') { kw } else { snippet });
@@ -236,21 +228,21 @@ mod tests {
236 check( 228 check(
237 r"m$0", 229 r"m$0",
238 expect![[r#" 230 expect![[r#"
231 kw pub(crate)
232 kw pub
233 kw unsafe
239 kw fn 234 kw fn
235 kw const
236 kw type
240 kw use 237 kw use
241 kw impl 238 kw impl
242 kw trait 239 kw trait
240 kw static
241 kw extern
242 kw mod
243 kw enum 243 kw enum
244 kw struct 244 kw struct
245 kw union 245 kw union
246 kw mod
247 kw const
248 kw type
249 kw static
250 kw extern
251 kw unsafe
252 kw pub(crate)
253 kw pub
254 "#]], 246 "#]],
255 ); 247 );
256 } 248 }
@@ -260,10 +252,16 @@ mod tests {
260 check( 252 check(
261 r"fn quux() { $0 }", 253 r"fn quux() { $0 }",
262 expect![[r#" 254 expect![[r#"
255 kw unsafe
263 kw fn 256 kw fn
257 kw const
258 kw type
264 kw use 259 kw use
265 kw impl 260 kw impl
266 kw trait 261 kw trait
262 kw static
263 kw extern
264 kw mod
267 kw match 265 kw match
268 kw while 266 kw while
269 kw while let 267 kw while let
@@ -272,12 +270,6 @@ mod tests {
272 kw if let 270 kw if let
273 kw for 271 kw for
274 kw let 272 kw let
275 kw mod
276 kw const
277 kw type
278 kw static
279 kw extern
280 kw unsafe
281 kw return 273 kw return
282 "#]], 274 "#]],
283 ); 275 );
@@ -288,10 +280,16 @@ mod tests {
288 check( 280 check(
289 r"fn quux() { if true { $0 } }", 281 r"fn quux() { if true { $0 } }",
290 expect![[r#" 282 expect![[r#"
283 kw unsafe
291 kw fn 284 kw fn
285 kw const
286 kw type
292 kw use 287 kw use
293 kw impl 288 kw impl
294 kw trait 289 kw trait
290 kw static
291 kw extern
292 kw mod
295 kw match 293 kw match
296 kw while 294 kw while
297 kw while let 295 kw while let
@@ -300,12 +298,6 @@ mod tests {
300 kw if let 298 kw if let
301 kw for 299 kw for
302 kw let 300 kw let
303 kw mod
304 kw const
305 kw type
306 kw static
307 kw extern
308 kw unsafe
309 kw return 301 kw return
310 "#]], 302 "#]],
311 ); 303 );
@@ -316,10 +308,16 @@ mod tests {
316 check( 308 check(
317 r#"fn quux() { if true { () } $0 }"#, 309 r#"fn quux() { if true { () } $0 }"#,
318 expect![[r#" 310 expect![[r#"
311 kw unsafe
319 kw fn 312 kw fn
313 kw const
314 kw type
320 kw use 315 kw use
321 kw impl 316 kw impl
322 kw trait 317 kw trait
318 kw static
319 kw extern
320 kw mod
323 kw match 321 kw match
324 kw while 322 kw while
325 kw while let 323 kw while let
@@ -330,19 +328,15 @@ mod tests {
330 kw let 328 kw let
331 kw else 329 kw else
332 kw else if 330 kw else if
333 kw mod
334 kw const
335 kw type
336 kw static
337 kw extern
338 kw unsafe
339 kw return 331 kw return
340 "#]], 332 "#]],
341 ); 333 );
342 check_edit( 334 check_edit(
343 "else", 335 "else",
344 r#"fn quux() { if true { () } $0 }"#, 336 r#"fn quux() { if true { () } $0 }"#,
345 r#"fn quux() { if true { () } else {$0} }"#, 337 r#"fn quux() { if true { () } else {
338 $0
339} }"#,
346 ); 340 );
347 } 341 }
348 342
@@ -355,6 +349,7 @@ fn quux() -> i32 {
355} 349}
356"#, 350"#,
357 expect![[r#" 351 expect![[r#"
352 kw unsafe
358 kw match 353 kw match
359 kw while 354 kw while
360 kw while let 355 kw while let
@@ -362,7 +357,6 @@ fn quux() -> i32 {
362 kw if 357 kw if
363 kw if let 358 kw if let
364 kw for 359 kw for
365 kw unsafe
366 kw return 360 kw return
367 "#]], 361 "#]],
368 ); 362 );
@@ -373,10 +367,10 @@ fn quux() -> i32 {
373 check( 367 check(
374 r"trait My { $0 }", 368 r"trait My { $0 }",
375 expect![[r#" 369 expect![[r#"
370 kw unsafe
376 kw fn 371 kw fn
377 kw const 372 kw const
378 kw type 373 kw type
379 kw unsafe
380 "#]], 374 "#]],
381 ); 375 );
382 } 376 }
@@ -386,12 +380,27 @@ fn quux() -> i32 {
386 check( 380 check(
387 r"impl My { $0 }", 381 r"impl My { $0 }",
388 expect![[r#" 382 expect![[r#"
383 kw pub(crate)
384 kw pub
385 kw unsafe
389 kw fn 386 kw fn
390 kw const 387 kw const
391 kw type 388 kw type
392 kw unsafe 389 "#]],
390 );
391 }
392
393 #[test]
394 fn test_keywords_in_impl_def_with_attr() {
395 check(
396 r"impl My { #[foo] $0 }",
397 expect![[r#"
393 kw pub(crate) 398 kw pub(crate)
394 kw pub 399 kw pub
400 kw unsafe
401 kw fn
402 kw const
403 kw type
395 "#]], 404 "#]],
396 ); 405 );
397 } 406 }
@@ -401,10 +410,16 @@ fn quux() -> i32 {
401 check( 410 check(
402 r"fn my() { loop { $0 } }", 411 r"fn my() { loop { $0 } }",
403 expect![[r#" 412 expect![[r#"
413 kw unsafe
404 kw fn 414 kw fn
415 kw const
416 kw type
405 kw use 417 kw use
406 kw impl 418 kw impl
407 kw trait 419 kw trait
420 kw static
421 kw extern
422 kw mod
408 kw match 423 kw match
409 kw while 424 kw while
410 kw while let 425 kw while let
@@ -413,12 +428,6 @@ fn quux() -> i32 {
413 kw if let 428 kw if let
414 kw for 429 kw for
415 kw let 430 kw let
416 kw mod
417 kw const
418 kw type
419 kw static
420 kw extern
421 kw unsafe
422 kw continue 431 kw continue
423 kw break 432 kw break
424 kw return 433 kw return
@@ -646,7 +655,9 @@ fn foo() {
646fn main() { let x = $0 } 655fn main() { let x = $0 }
647"#, 656"#,
648 r#" 657 r#"
649fn main() { let x = match $0 {}; } 658fn main() { let x = match $1 {
659 $0
660}; }
650"#, 661"#,
651 ); 662 );
652 663
@@ -660,7 +671,9 @@ fn main() {
660"#, 671"#,
661 r#" 672 r#"
662fn main() { 673fn main() {
663 let x = if $0 {}; 674 let x = if $1 {
675 $0
676};
664 let y = 92; 677 let y = 92;
665} 678}
666"#, 679"#,
@@ -676,7 +689,9 @@ fn main() {
676"#, 689"#,
677 r#" 690 r#"
678fn main() { 691fn main() {
679 let x = loop {$0}; 692 let x = loop {
693 $0
694};
680 bar(); 695 bar();
681} 696}
682"#, 697"#,
diff --git a/crates/ide_completion/src/completions/lifetime.rs b/crates/ide_completion/src/completions/lifetime.rs
index 5eeddf7a4..5f6285b84 100644
--- a/crates/ide_completion/src/completions/lifetime.rs
+++ b/crates/ide_completion/src/completions/lifetime.rs
@@ -8,19 +8,24 @@ pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext)
8 if !ctx.lifetime_allowed { 8 if !ctx.lifetime_allowed {
9 return; 9 return;
10 } 10 }
11 let lp_string;
11 let param_lifetime = match ( 12 let param_lifetime = match (
12 &ctx.lifetime_syntax, 13 &ctx.lifetime_syntax,
13 ctx.lifetime_param_syntax.as_ref().and_then(|lp| lp.lifetime()), 14 ctx.lifetime_param_syntax.as_ref().and_then(|lp| lp.lifetime()),
14 ) { 15 ) {
15 (Some(lt), Some(lp)) if lp == lt.clone() => return, 16 (Some(lt), Some(lp)) if lp == lt.clone() => return,
16 (Some(_), Some(lp)) => Some(lp.to_string()), 17 (Some(_), Some(lp)) => {
18 lp_string = lp.to_string();
19 Some(&lp_string)
20 }
17 _ => None, 21 _ => None,
18 }; 22 };
19 23
20 ctx.scope.process_all_names(&mut |name, res| { 24 ctx.scope.process_all_names(&mut |name, res| {
21 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { 25 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
22 if param_lifetime != Some(name.to_string()) { 26 let name = name.to_string();
23 acc.add_resolution(ctx, name.to_string(), &res); 27 if param_lifetime != Some(&name) {
28 acc.add_resolution(ctx, name, &res);
24 } 29 }
25 } 30 }
26 }); 31 });
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs
index 2be299ac2..ec57aee30 100644
--- a/crates/ide_completion/src/completions/macro_in_item_position.rs
+++ b/crates/ide_completion/src/completions/macro_in_item_position.rs
@@ -2,15 +2,22 @@
2 2
3use crate::{CompletionContext, Completions}; 3use crate::{CompletionContext, Completions};
4 4
5// Ideally this should be removed and moved into `(un)qualified_path` respectively
5pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { 6pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
6 // Show only macros in top level. 7 // Show only macros in top level.
7 if ctx.is_new_item { 8 if !ctx.is_new_item {
8 ctx.scope.process_all_names(&mut |name, res| { 9 return;
9 if let hir::ScopeDef::MacroDef(mac) = res {
10 acc.add_macro(ctx, Some(name.to_string()), mac);
11 }
12 })
13 } 10 }
11
12 ctx.scope.process_all_names(&mut |name, res| {
13 if let hir::ScopeDef::MacroDef(mac) = res {
14 acc.add_macro(ctx, Some(name.to_string()), mac);
15 }
16 // FIXME: This should be done in qualified_path/unqualified_path instead?
17 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
18 acc.add_resolution(ctx, name.to_string(), &res);
19 }
20 })
14} 21}
15 22
16#[cfg(test)] 23#[cfg(test)]
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 8dc9ab73c..b84e9a967 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -1,17 +1,15 @@
1//! Completes constants and paths in patterns. 1//! Completes constants and paths in patterns.
2 2
3use crate::{CompletionContext, Completions}; 3use crate::{context::PatternRefutability, CompletionContext, Completions};
4 4
5/// Completes constants and paths in patterns. 5/// Completes constants and paths in patterns.
6pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { 6pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7 if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_pat_binding) { 7 let refutable = match ctx.is_pat_or_const {
8 return; 8 Some(it) => it == PatternRefutability::Refutable,
9 } 9 None => return,
10 if ctx.record_pat_syntax.is_some() { 10 };
11 return;
12 }
13 11
14 if !ctx.is_irrefutable_pat_binding { 12 if refutable {
15 if let Some(hir::Adt::Enum(e)) = 13 if let Some(hir::Adt::Enum(e)) =
16 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) 14 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
17 { 15 {
@@ -31,14 +29,14 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
31 acc.add_struct_pat(ctx, *strukt, Some(name.clone())); 29 acc.add_struct_pat(ctx, *strukt, Some(name.clone()));
32 true 30 true
33 } 31 }
34 hir::ModuleDef::Variant(variant) if !ctx.is_irrefutable_pat_binding => { 32 hir::ModuleDef::Variant(variant) if refutable => {
35 acc.add_variant_pat(ctx, *variant, Some(name.clone())); 33 acc.add_variant_pat(ctx, *variant, Some(name.clone()));
36 true 34 true
37 } 35 }
38 hir::ModuleDef::Adt(hir::Adt::Enum(..)) 36 hir::ModuleDef::Adt(hir::Adt::Enum(..))
39 | hir::ModuleDef::Variant(..) 37 | hir::ModuleDef::Variant(..)
40 | hir::ModuleDef::Const(..) 38 | hir::ModuleDef::Const(..)
41 | hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding, 39 | hir::ModuleDef::Module(..) => refutable,
42 _ => false, 40 _ => false,
43 }, 41 },
44 hir::ScopeDef::MacroDef(_) => true, 42 hir::ScopeDef::MacroDef(_) => true,
@@ -47,7 +45,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
47 acc.add_struct_pat(ctx, strukt, Some(name.clone())); 45 acc.add_struct_pat(ctx, strukt, Some(name.clone()));
48 true 46 true
49 } 47 }
50 Some(hir::Adt::Enum(_)) => !ctx.is_irrefutable_pat_binding, 48 Some(hir::Adt::Enum(_)) => refutable,
51 _ => true, 49 _ => true,
52 }, 50 },
53 _ => false, 51 _ => false,
@@ -402,4 +400,31 @@ impl Foo {
402 "#]], 400 "#]],
403 ) 401 )
404 } 402 }
403
404 #[test]
405 fn completes_in_record_field_pat() {
406 check_snippet(
407 r#"
408struct Foo { bar: Bar }
409struct Bar(u32);
410fn outer(Foo { bar: $0 }: Foo) {}
411"#,
412 expect![[r#"
413 bn Foo Foo { bar$1 }$0
414 bn Bar Bar($1)$0
415 "#]],
416 )
417 }
418
419 #[test]
420 fn skips_in_record_field_pat_name() {
421 check_snippet(
422 r#"
423struct Foo { bar: Bar }
424struct Bar(u32);
425fn outer(Foo { bar$0 }: Foo) {}
426"#,
427 expect![[r#""#]],
428 )
429 }
405} 430}
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index eedb44873..7a0e1ead3 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -7,21 +7,33 @@ use syntax::AstNode;
7use crate::{CompletionContext, Completions}; 7use crate::{CompletionContext, Completions};
8 8
9pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { 9pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 if ctx.is_path_disallowed() || ctx.expects_item() {
11 return;
12 }
10 let path = match &ctx.path_qual { 13 let path = match &ctx.path_qual {
11 Some(path) => path.clone(), 14 Some(path) => path.clone(),
12 None => return, 15 None => return,
13 }; 16 };
14 17
15 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
16 return;
17 }
18
19 let context_module = ctx.scope.module();
20
21 let resolution = match ctx.sema.resolve_path(&path) { 18 let resolution = match ctx.sema.resolve_path(&path) {
22 Some(res) => res, 19 Some(res) => res,
23 None => return, 20 None => return,
24 }; 21 };
22 let context_module = ctx.scope.module();
23 if ctx.expects_assoc_item() {
24 if let PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
25 let module_scope = module.scope(ctx.db, context_module);
26 for (name, def) in module_scope {
27 if let ScopeDef::MacroDef(macro_def) = def {
28 acc.add_macro(ctx, Some(name.to_string()), macro_def);
29 }
30 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
31 acc.add_resolution(ctx, name.to_string(), &def);
32 }
33 }
34 }
35 return;
36 }
25 37
26 // Add associated types on type parameters and `Self`. 38 // Add associated types on type parameters and `Self`.
27 resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| { 39 resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
@@ -596,7 +608,7 @@ fn main() { T::$0; }
596macro_rules! foo { () => {} } 608macro_rules! foo { () => {} }
597 609
598fn main() { let _ = crate::$0 } 610fn main() { let _ = crate::$0 }
599 "#, 611"#,
600 expect![[r##" 612 expect![[r##"
601 fn main() fn() 613 fn main() fn()
602 ma foo!(…) #[macro_export] macro_rules! foo 614 ma foo!(…) #[macro_export] macro_rules! foo
@@ -605,6 +617,44 @@ fn main() { let _ = crate::$0 }
605 } 617 }
606 618
607 #[test] 619 #[test]
620 fn completes_in_assoc_item_list() {
621 check(
622 r#"
623#[macro_export]
624macro_rules! foo { () => {} }
625mod bar {}
626
627struct MyStruct {}
628impl MyStruct {
629 crate::$0
630}
631"#,
632 expect![[r##"
633 md bar
634 ma foo! #[macro_export] macro_rules! foo
635 "##]],
636 );
637 }
638
639 #[test]
640 #[ignore] // FIXME doesn't complete anything atm
641 fn completes_in_item_list() {
642 check(
643 r#"
644struct MyStruct {}
645macro_rules! foo {}
646mod bar {}
647
648crate::$0
649"#,
650 expect![[r#"
651 md bar
652 ma foo! macro_rules! foo
653 "#]],
654 )
655 }
656
657 #[test]
608 fn test_super_super_completion() { 658 fn test_super_super_completion() {
609 check( 659 check(
610 r#" 660 r#"
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index 2f95b8687..e1526b70b 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -13,20 +13,19 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
13 let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone())); 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(); 14 let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default();
15 let impl_default_trait = default_trait 15 let impl_default_trait = default_trait
16 .and_then(|default_trait| ty.map(|ty| ty.impls_trait(ctx.db, default_trait, &[]))) 16 .zip(ty)
17 .unwrap_or(false); 17 .map_or(false, |(default_trait, ty)| ty.impls_trait(ctx.db, default_trait, &[]));
18 18
19 let missing_fields = ctx.sema.record_literal_missing_fields(record_lit); 19 let missing_fields = ctx.sema.record_literal_missing_fields(record_lit);
20 if impl_default_trait && !missing_fields.is_empty() { 20 if impl_default_trait && !missing_fields.is_empty() {
21 let completion_text = "..Default::default()"; 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 let mut item = CompletionItem::new( 22 let mut item = CompletionItem::new(
26 CompletionKind::Snippet, 23 CompletionKind::Snippet,
27 ctx.source_range(), 24 ctx.source_range(),
28 "..Default::default()", 25 completion_text,
29 ); 26 );
27 let completion_text =
28 completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text);
30 item.insert_text(completion_text).kind(SymbolKind::Field); 29 item.insert_text(completion_text).kind(SymbolKind::Field);
31 item.add_to(acc); 30 item.add_to(acc);
32 } 31 }
@@ -108,8 +107,6 @@ fn process(f: S) {
108 check_snippet( 107 check_snippet(
109 test_code, 108 test_code,
110 expect![[r#" 109 expect![[r#"
111 sn pd
112 sn ppd
113 fd ..Default::default() 110 fd ..Default::default()
114 "#]], 111 "#]],
115 ); 112 );
@@ -179,13 +176,7 @@ fn process(f: S) {
179 "#]], 176 "#]],
180 ); 177 );
181 178
182 check_snippet( 179 check_snippet(test_code, expect![[r#""#]]);
183 test_code,
184 expect![[r#"
185 sn pd
186 sn ppd
187 "#]],
188 );
189 } 180 }
190 181
191 #[test] 182 #[test]
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index 7f7830976..defc25b00 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -14,7 +14,7 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str)
14} 14}
15 15
16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { 16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
17 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) { 17 if !(ctx.is_trivial_path && ctx.function_def.is_some()) {
18 return; 18 return;
19 } 19 }
20 let cap = match ctx.config.snippet_cap { 20 let cap = match ctx.config.snippet_cap {
@@ -22,8 +22,10 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
22 None => return, 22 None => return,
23 }; 23 };
24 24
25 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); 25 if ctx.can_be_stmt {
26 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); 26 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
27 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
28 }
27} 29}
28 30
29pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { 31pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 7875500c1..ede07f605 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -1,7 +1,6 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef; 3use hir::ScopeDef;
4use syntax::AstNode;
5 4
6use crate::{CompletionContext, Completions}; 5use crate::{CompletionContext, Completions};
7 6
@@ -9,14 +8,30 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
9 if !ctx.is_trivial_path { 8 if !ctx.is_trivial_path {
10 return; 9 return;
11 } 10 }
12 if ctx.record_lit_syntax.is_some() 11 if ctx.is_path_disallowed() || ctx.expects_item() {
13 || ctx.record_pat_syntax.is_some() 12 return;
14 || ctx.attribute_under_caret.is_some() 13 }
15 || ctx.mod_declaration_under_caret.is_some() 14 if ctx.expects_assoc_item() {
16 { 15 ctx.scope.process_all_names(&mut |name, def| {
16 if let ScopeDef::MacroDef(macro_def) = def {
17 acc.add_macro(ctx, Some(name.to_string()), macro_def);
18 }
19 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
20 acc.add_resolution(ctx, name.to_string(), &def);
21 }
22 });
17 return; 23 return;
18 } 24 }
19 25
26 if ctx.expects_use_tree() {
27 cov_mark::hit!(only_completes_modules_in_import);
28 ctx.scope.process_all_names(&mut |name, res| {
29 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
30 acc.add_resolution(ctx, name.to_string(), &res);
31 }
32 });
33 return;
34 }
20 if let Some(hir::Adt::Enum(e)) = 35 if let Some(hir::Adt::Enum(e)) =
21 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) 36 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
22 { 37 {
@@ -30,14 +45,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
30 cov_mark::hit!(skip_lifetime_completion); 45 cov_mark::hit!(skip_lifetime_completion);
31 return; 46 return;
32 } 47 }
33 if ctx.use_item_syntax.is_some() {
34 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
35 if name_ref.syntax().text() == name.to_string().as_str() {
36 cov_mark::hit!(self_fulfilling_completion);
37 return;
38 }
39 }
40 }
41 acc.add_resolution(ctx, name.to_string(), &res); 48 acc.add_resolution(ctx, name.to_string(), &res);
42 }); 49 });
43} 50}
@@ -61,15 +68,17 @@ mod tests {
61 } 68 }
62 69
63 #[test] 70 #[test]
64 fn self_fulfilling_completion() { 71 fn only_completes_modules_in_import() {
65 cov_mark::check!(self_fulfilling_completion); 72 cov_mark::check!(only_completes_modules_in_import);
66 check( 73 check(
67 r#" 74 r#"
68use foo$0 75use f$0
69use std::collections; 76
77struct Foo;
78mod foo {}
70"#, 79"#,
71 expect![[r#" 80 expect![[r#"
72 ?? collections 81 md foo
73 "#]], 82 "#]],
74 ); 83 );
75 } 84 }
@@ -86,7 +95,7 @@ fn quux(x: Option<Enum>) {
86 } 95 }
87} 96}
88"#, 97"#,
89 expect![[""]], 98 expect![[r#""#]],
90 ); 99 );
91 } 100 }
92 101
@@ -102,7 +111,7 @@ fn quux(x: Option<Enum>) {
102 } 111 }
103} 112}
104"#, 113"#,
105 expect![[""]], 114 expect![[r#""#]],
106 ); 115 );
107 } 116 }
108 117
@@ -651,7 +660,7 @@ fn f() {}
651 } 660 }
652 661
653 #[test] 662 #[test]
654 fn completes_type_or_trait_in_impl_block() { 663 fn completes_target_type_or_trait_in_impl_block() {
655 check( 664 check(
656 r#" 665 r#"
657trait MyTrait {} 666trait MyTrait {}
@@ -666,4 +675,41 @@ impl My$0
666 "#]], 675 "#]],
667 ) 676 )
668 } 677 }
678
679 #[test]
680 fn completes_in_assoc_item_list() {
681 check(
682 r#"
683macro_rules! foo {}
684mod bar {}
685
686struct MyStruct {}
687impl MyStruct {
688 $0
689}
690"#,
691 expect![[r#"
692 md bar
693 ma foo! macro_rules! foo
694 "#]],
695 )
696 }
697
698 // FIXME: The completions here currently come from `macro_in_item_position`, but they shouldn't
699 #[test]
700 fn completes_in_item_list() {
701 check(
702 r#"
703struct MyStruct {}
704macro_rules! foo {}
705mod bar {}
706
707$0
708"#,
709 expect![[r#"
710 md bar
711 ma foo!(…) macro_rules! foo
712 "#]],
713 )
714 }
669} 715}
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 1ec59ff80..8d6440cb2 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -1,29 +1,34 @@
1//! See `CompletionContext` structure. 1//! See `CompletionContext` structure.
2 2
3use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type}; 3use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type};
4use ide_db::base_db::{FilePosition, SourceDatabase}; 4use ide_db::{
5use ide_db::{call_info::ActiveParameter, RootDatabase}; 5 base_db::{FilePosition, SourceDatabase},
6 call_info::ActiveParameter,
7 RootDatabase,
8};
6use syntax::{ 9use syntax::{
7 algo::find_node_at_offset, 10 algo::find_node_at_offset,
8 ast::{self, NameOrNameRef, NameOwner}, 11 ast::{self, NameOrNameRef, NameOwner},
9 match_ast, AstNode, NodeOrToken, 12 match_ast, AstNode, NodeOrToken,
10 SyntaxKind::*, 13 SyntaxKind::{self, *},
11 SyntaxNode, SyntaxToken, TextRange, TextSize, 14 SyntaxNode, SyntaxToken, TextRange, TextSize, T,
12}; 15};
13
14use text_edit::Indel; 16use text_edit::Indel;
15 17
16use crate::{ 18use crate::{
17 patterns::{ 19 patterns::{
18 fn_is_prev, for_is_prev2, has_bind_pat_parent, has_block_expr_parent, 20 determine_location, determine_prev_sibling, for_is_prev2, inside_impl_trait_block,
19 has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent, 21 is_in_loop_body, is_match_arm, previous_token, ImmediateLocation, ImmediatePrevSibling,
20 has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling,
21 has_trait_parent, if_is_prev, inside_impl_trait_block, is_in_loop_body, is_match_arm,
22 unsafe_is_prev,
23 }, 22 },
24 CompletionConfig, 23 CompletionConfig,
25}; 24};
26 25
26#[derive(Copy, Clone, Debug, PartialEq, Eq)]
27pub(crate) enum PatternRefutability {
28 Refutable,
29 Irrefutable,
30}
31
27/// `CompletionContext` is created early during completion to figure out, where 32/// `CompletionContext` is created early during completion to figure out, where
28/// exactly is the cursor, syntax-wise. 33/// exactly is the cursor, syntax-wise.
29#[derive(Debug)] 34#[derive(Debug)]
@@ -41,28 +46,38 @@ pub(crate) struct CompletionContext<'a> {
41 pub(super) expected_name: Option<NameOrNameRef>, 46 pub(super) expected_name: Option<NameOrNameRef>,
42 pub(super) expected_type: Option<Type>, 47 pub(super) expected_type: Option<Type>,
43 pub(super) name_ref_syntax: Option<ast::NameRef>, 48 pub(super) name_ref_syntax: Option<ast::NameRef>,
44 pub(super) lifetime_syntax: Option<ast::Lifetime>, 49
45 pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
46 pub(super) function_syntax: Option<ast::Fn>,
47 pub(super) use_item_syntax: Option<ast::Use>, 50 pub(super) use_item_syntax: Option<ast::Use>,
51
52 /// The parent function of the cursor position if it exists.
53 pub(super) function_def: Option<ast::Fn>,
54 /// The parent impl of the cursor position if it exists.
55 pub(super) impl_def: Option<ast::Impl>,
56
57 /// RecordExpr the token is a field of
48 pub(super) record_lit_syntax: Option<ast::RecordExpr>, 58 pub(super) record_lit_syntax: Option<ast::RecordExpr>,
59 /// RecordPat the token is a field of
49 pub(super) record_pat_syntax: Option<ast::RecordPat>, 60 pub(super) record_pat_syntax: Option<ast::RecordPat>,
50 pub(super) record_field_syntax: Option<ast::RecordExprField>, 61
51 pub(super) impl_def: Option<ast::Impl>, 62 // potentially set if we are completing a lifetime
63 pub(super) lifetime_syntax: Option<ast::Lifetime>,
64 pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
52 pub(super) lifetime_allowed: bool, 65 pub(super) lifetime_allowed: bool,
66 pub(super) is_label_ref: bool,
67
68 // potentially set if we are completing a name
69 pub(super) is_pat_or_const: Option<PatternRefutability>,
70 pub(super) is_param: bool,
71
72 pub(super) completion_location: Option<ImmediateLocation>,
73 pub(super) prev_sibling: Option<ImmediatePrevSibling>,
74
53 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 75 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
54 pub(super) active_parameter: Option<ActiveParameter>, 76 pub(super) active_parameter: Option<ActiveParameter>,
55 pub(super) is_param: bool,
56 pub(super) is_label_ref: bool,
57 /// If a name-binding or reference to a const in a pattern.
58 /// Irrefutable patterns (like let) are excluded.
59 pub(super) is_pat_binding_or_const: bool,
60 pub(super) is_irrefutable_pat_binding: bool,
61 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. 77 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
62 pub(super) is_trivial_path: bool, 78 pub(super) is_trivial_path: bool,
63 /// If not a trivial path, the prefix (qualifier). 79 /// If not a trivial path, the prefix (qualifier).
64 pub(super) path_qual: Option<ast::Path>, 80 pub(super) path_qual: Option<ast::Path>,
65 pub(super) after_if: bool,
66 /// `true` if we are a statement or a last expr in the block. 81 /// `true` if we are a statement or a last expr in the block.
67 pub(super) can_be_stmt: bool, 82 pub(super) can_be_stmt: bool,
68 /// `true` if we expect an expression at the cursor position. 83 /// `true` if we expect an expression at the cursor position.
@@ -82,24 +97,15 @@ pub(crate) struct CompletionContext<'a> {
82 pub(super) has_type_args: bool, 97 pub(super) has_type_args: bool,
83 pub(super) attribute_under_caret: Option<ast::Attr>, 98 pub(super) attribute_under_caret: Option<ast::Attr>,
84 pub(super) mod_declaration_under_caret: Option<ast::Module>, 99 pub(super) mod_declaration_under_caret: Option<ast::Module>,
85 pub(super) unsafe_is_prev: bool, 100 pub(super) locals: Vec<(String, Local)>,
86 pub(super) if_is_prev: bool, 101
87 pub(super) block_expr_parent: bool, 102 // keyword patterns
88 pub(super) bind_pat_parent: bool, 103 pub(super) previous_token: Option<SyntaxToken>,
89 pub(super) ref_pat_parent: bool,
90 pub(super) in_loop_body: bool, 104 pub(super) in_loop_body: bool,
91 pub(super) has_trait_parent: bool,
92 pub(super) has_impl_parent: bool,
93 pub(super) inside_impl_trait_block: bool,
94 pub(super) has_field_list_parent: bool,
95 pub(super) trait_as_prev_sibling: bool,
96 pub(super) impl_as_prev_sibling: bool,
97 pub(super) is_match_arm: bool, 105 pub(super) is_match_arm: bool,
98 pub(super) has_item_list_or_source_file_parent: bool,
99 pub(super) for_is_prev2: bool,
100 pub(super) fn_is_prev: bool,
101 pub(super) incomplete_let: bool, 106 pub(super) incomplete_let: bool,
102 pub(super) locals: Vec<(String, Local)>, 107
108 no_completion_required: bool,
103} 109}
104 110
105impl<'a> CompletionContext<'a> { 111impl<'a> CompletionContext<'a> {
@@ -149,20 +155,17 @@ impl<'a> CompletionContext<'a> {
149 name_ref_syntax: None, 155 name_ref_syntax: None,
150 lifetime_syntax: None, 156 lifetime_syntax: None,
151 lifetime_param_syntax: None, 157 lifetime_param_syntax: None,
152 function_syntax: None, 158 function_def: None,
153 use_item_syntax: None, 159 use_item_syntax: None,
154 record_lit_syntax: None, 160 record_lit_syntax: None,
155 record_pat_syntax: None, 161 record_pat_syntax: None,
156 record_field_syntax: None,
157 impl_def: None, 162 impl_def: None,
158 active_parameter: ActiveParameter::at(db, position), 163 active_parameter: ActiveParameter::at(db, position),
159 is_label_ref: false, 164 is_label_ref: false,
160 is_param: false, 165 is_param: false,
161 is_pat_binding_or_const: false, 166 is_pat_or_const: None,
162 is_irrefutable_pat_binding: false,
163 is_trivial_path: false, 167 is_trivial_path: false,
164 path_qual: None, 168 path_qual: None,
165 after_if: false,
166 can_be_stmt: false, 169 can_be_stmt: false,
167 is_expr: false, 170 is_expr: false,
168 is_new_item: false, 171 is_new_item: false,
@@ -175,67 +178,57 @@ impl<'a> CompletionContext<'a> {
175 has_type_args: false, 178 has_type_args: false,
176 attribute_under_caret: None, 179 attribute_under_caret: None,
177 mod_declaration_under_caret: None, 180 mod_declaration_under_caret: None,
178 unsafe_is_prev: false, 181 previous_token: None,
179 if_is_prev: false,
180 block_expr_parent: false,
181 bind_pat_parent: false,
182 ref_pat_parent: false,
183 in_loop_body: false, 182 in_loop_body: false,
184 has_trait_parent: false, 183 completion_location: None,
185 has_impl_parent: false, 184 prev_sibling: None,
186 inside_impl_trait_block: false,
187 has_field_list_parent: false,
188 trait_as_prev_sibling: false,
189 impl_as_prev_sibling: false,
190 is_match_arm: false, 185 is_match_arm: false,
191 has_item_list_or_source_file_parent: false, 186 no_completion_required: false,
192 for_is_prev2: false,
193 fn_is_prev: false,
194 incomplete_let: false, 187 incomplete_let: false,
195 locals, 188 locals,
196 }; 189 };
197 190
198 let mut original_file = original_file.syntax().clone(); 191 let mut original_file = original_file.syntax().clone();
199 let mut hypothetical_file = file_with_fake_ident.syntax().clone(); 192 let mut speculative_file = file_with_fake_ident.syntax().clone();
200 let mut offset = position.offset; 193 let mut offset = position.offset;
201 let mut fake_ident_token = fake_ident_token; 194 let mut fake_ident_token = fake_ident_token;
202 195
203 // Are we inside a macro call? 196 // Are we inside a macro call?
204 while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( 197 while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
205 find_node_at_offset::<ast::MacroCall>(&original_file, offset), 198 find_node_at_offset::<ast::MacroCall>(&original_file, offset),
206 find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset), 199 find_node_at_offset::<ast::MacroCall>(&speculative_file, offset),
207 ) { 200 ) {
208 if actual_macro_call.path().as_ref().map(|s| s.syntax().text()) 201 if actual_macro_call.path().as_ref().map(|s| s.syntax().text())
209 != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text()) 202 != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text())
210 { 203 {
211 break; 204 break;
212 } 205 }
213 let hypothetical_args = match macro_call_with_fake_ident.token_tree() { 206 let speculative_args = match macro_call_with_fake_ident.token_tree() {
214 Some(tt) => tt, 207 Some(tt) => tt,
215 None => break, 208 None => break,
216 }; 209 };
217 if let (Some(actual_expansion), Some(hypothetical_expansion)) = ( 210 if let (Some(actual_expansion), Some(speculative_expansion)) = (
218 ctx.sema.expand(&actual_macro_call), 211 ctx.sema.expand(&actual_macro_call),
219 ctx.sema.speculative_expand( 212 ctx.sema.speculative_expand(
220 &actual_macro_call, 213 &actual_macro_call,
221 &hypothetical_args, 214 &speculative_args,
222 fake_ident_token, 215 fake_ident_token,
223 ), 216 ),
224 ) { 217 ) {
225 let new_offset = hypothetical_expansion.1.text_range().start(); 218 let new_offset = speculative_expansion.1.text_range().start();
226 if new_offset > actual_expansion.text_range().end() { 219 if new_offset > actual_expansion.text_range().end() {
227 break; 220 break;
228 } 221 }
229 original_file = actual_expansion; 222 original_file = actual_expansion;
230 hypothetical_file = hypothetical_expansion.0; 223 speculative_file = speculative_expansion.0;
231 fake_ident_token = hypothetical_expansion.1; 224 fake_ident_token = speculative_expansion.1;
232 offset = new_offset; 225 offset = new_offset;
233 } else { 226 } else {
234 break; 227 break;
235 } 228 }
236 } 229 }
237 ctx.fill_keyword_patterns(&hypothetical_file, offset); 230 ctx.fill_keyword_patterns(&speculative_file, offset);
238 ctx.fill(&original_file, hypothetical_file, offset); 231 ctx.fill(&original_file, speculative_file, offset);
239 Some(ctx) 232 Some(ctx)
240 } 233 }
241 234
@@ -245,7 +238,7 @@ impl<'a> CompletionContext<'a> {
245 /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names. 238 /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names.
246 /// - `for _ i$0` -- obviously, it'll be "in" keyword. 239 /// - `for _ i$0` -- obviously, it'll be "in" keyword.
247 pub(crate) fn no_completion_required(&self) -> bool { 240 pub(crate) fn no_completion_required(&self) -> bool {
248 (self.fn_is_prev && !self.inside_impl_trait_block) || self.for_is_prev2 241 self.no_completion_required
249 } 242 }
250 243
251 /// The range of the identifier that is being completed. 244 /// The range of the identifier that is being completed.
@@ -264,33 +257,85 @@ impl<'a> CompletionContext<'a> {
264 } 257 }
265 } 258 }
266 259
260 pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool {
261 self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
262 }
263
264 pub(crate) fn expects_assoc_item(&self) -> bool {
265 matches!(
266 self.completion_location,
267 Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl)
268 )
269 }
270
271 pub(crate) fn expects_use_tree(&self) -> bool {
272 matches!(self.completion_location, Some(ImmediateLocation::Use))
273 }
274
275 pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
276 matches!(self.completion_location, Some(ImmediateLocation::Impl))
277 }
278
279 pub(crate) fn expects_item(&self) -> bool {
280 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
281 }
282
283 pub(crate) fn expects_expression(&self) -> bool {
284 self.is_expr
285 }
286
287 pub(crate) fn has_block_expr_parent(&self) -> bool {
288 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
289 }
290
291 pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool {
292 matches!(
293 self.completion_location,
294 Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr)
295 )
296 }
297
298 pub(crate) fn expect_record_field(&self) -> bool {
299 matches!(self.completion_location, Some(ImmediateLocation::RecordField))
300 }
301
302 pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
303 matches!(
304 self.prev_sibling,
305 Some(ImmediatePrevSibling::ImplDefType) | Some(ImmediatePrevSibling::TraitDefName)
306 )
307 }
308
309 pub(crate) fn after_if(&self) -> bool {
310 matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
311 }
312
313 pub(crate) fn is_path_disallowed(&self) -> bool {
314 self.record_lit_syntax.is_some()
315 || self.record_pat_syntax.is_some()
316 || self.attribute_under_caret.is_some()
317 || self.mod_declaration_under_caret.is_some()
318 }
319
267 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 320 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
268 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 321 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
269 let syntax_element = NodeOrToken::Token(fake_ident_token); 322 let syntax_element = NodeOrToken::Token(fake_ident_token);
270 self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); 323 self.previous_token = previous_token(syntax_element.clone());
271 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone());
272 self.if_is_prev = if_is_prev(syntax_element.clone());
273 self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone());
274 self.ref_pat_parent = has_ref_parent(syntax_element.clone());
275 self.in_loop_body = is_in_loop_body(syntax_element.clone()); 324 self.in_loop_body = is_in_loop_body(syntax_element.clone());
276 self.has_trait_parent = has_trait_parent(syntax_element.clone());
277 self.has_impl_parent = has_impl_parent(syntax_element.clone());
278 self.inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
279 self.has_field_list_parent = has_field_list_parent(syntax_element.clone());
280 self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone());
281 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
282 self.is_match_arm = is_match_arm(syntax_element.clone()); 325 self.is_match_arm = is_match_arm(syntax_element.clone());
283 self.has_item_list_or_source_file_parent = 326
284 has_item_list_or_source_file_parent(syntax_element.clone());
285 self.mod_declaration_under_caret = 327 self.mod_declaration_under_caret =
286 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) 328 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
287 .filter(|module| module.item_list().is_none()); 329 .filter(|module| module.item_list().is_none());
288 self.for_is_prev2 = for_is_prev2(syntax_element.clone());
289 self.fn_is_prev = fn_is_prev(syntax_element.clone());
290 self.incomplete_let = 330 self.incomplete_let =
291 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { 331 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
292 it.syntax().text_range().end() == syntax_element.text_range().end() 332 it.syntax().text_range().end() == syntax_element.text_range().end()
293 }); 333 });
334
335 let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
336 let fn_is_prev = self.previous_token_is(T![fn]);
337 let for_is_prev2 = for_is_prev2(syntax_element.clone());
338 self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2;
294 } 339 }
295 340
296 fn fill_impl_def(&mut self) { 341 fn fill_impl_def(&mut self) {
@@ -412,67 +457,21 @@ impl<'a> CompletionContext<'a> {
412 self.expected_type = expected_type; 457 self.expected_type = expected_type;
413 self.expected_name = expected_name; 458 self.expected_name = expected_name;
414 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); 459 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
415 460 let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) {
416 if let Some(lifetime) = find_node_at_offset::<ast::Lifetime>(&file_with_fake_ident, offset) 461 Some(it) => it,
417 { 462 None => return,
418 self.classify_lifetime(original_file, lifetime, offset); 463 };
419 } 464 self.completion_location = determine_location(&name_like);
420 465 self.prev_sibling = determine_prev_sibling(&name_like);
421 // First, let's try to complete a reference to some declaration. 466 match name_like {
422 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { 467 ast::NameLike::Lifetime(lifetime) => {
423 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. 468 self.classify_lifetime(original_file, lifetime, offset);
424 // See RFC#1685.
425 if is_node::<ast::Param>(name_ref.syntax()) {
426 self.is_param = true;
427 return;
428 }
429 // FIXME: remove this (V) duplication and make the check more precise
430 if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() {
431 self.record_pat_syntax =
432 self.sema.find_node_at_offset_with_macros(&original_file, offset);
433 }
434 self.classify_name_ref(original_file, name_ref, offset);
435 }
436
437 // Otherwise, see if this is a declaration. We can use heuristics to
438 // suggest declaration names, see `CompletionKind::Magic`.
439 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) {
440 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::IdentPat::cast) {
441 self.is_pat_binding_or_const = true;
442 if bind_pat.at_token().is_some()
443 || bind_pat.ref_token().is_some()
444 || bind_pat.mut_token().is_some()
445 {
446 self.is_pat_binding_or_const = false;
447 }
448 if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() {
449 self.is_pat_binding_or_const = false;
450 }
451 if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| {
452 match_ast! {
453 match node {
454 ast::LetStmt(it) => Some(it.pat()),
455 ast::Param(it) => Some(it.pat()),
456 _ => None,
457 }
458 }
459 }) {
460 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
461 self.is_pat_binding_or_const = false;
462 self.is_irrefutable_pat_binding = true;
463 }
464 }
465
466 self.fill_impl_def();
467 } 469 }
468 if is_node::<ast::Param>(name.syntax()) { 470 ast::NameLike::NameRef(name_ref) => {
469 self.is_param = true; 471 self.classify_name_ref(original_file, name_ref, offset);
470 return;
471 } 472 }
472 // FIXME: remove this (^) duplication and make the check more precise 473 ast::NameLike::Name(name) => {
473 if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { 474 self.classify_name(original_file, name, offset);
474 self.record_pat_syntax =
475 self.sema.find_node_at_offset_with_macros(&original_file, offset);
476 } 475 }
477 } 476 }
478 } 477 }
@@ -506,22 +505,71 @@ impl<'a> CompletionContext<'a> {
506 } 505 }
507 } 506 }
508 507
508 fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) {
509 if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
510 self.is_pat_or_const = Some(PatternRefutability::Refutable);
511 // if any of these is here our bind pat can't be a const pat anymore
512 let complex_ident_pat = bind_pat.at_token().is_some()
513 || bind_pat.ref_token().is_some()
514 || bind_pat.mut_token().is_some();
515 if complex_ident_pat {
516 self.is_pat_or_const = None;
517 } else {
518 let irrefutable_pat = bind_pat.syntax().ancestors().find_map(|node| {
519 match_ast! {
520 match node {
521 ast::LetStmt(it) => Some(it.pat()),
522 ast::Param(it) => Some(it.pat()),
523 _ => None,
524 }
525 }
526 });
527 if let Some(Some(pat)) = irrefutable_pat {
528 // This check is here since we could be inside a pattern in the initializer expression of the let statement.
529 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
530 self.is_pat_or_const = Some(PatternRefutability::Irrefutable);
531 }
532 }
533
534 let is_name_in_field_pat = bind_pat
535 .syntax()
536 .parent()
537 .and_then(ast::RecordPatField::cast)
538 .map_or(false, |pat_field| pat_field.name_ref().is_none());
539 if is_name_in_field_pat {
540 self.is_pat_or_const = None;
541 }
542 }
543
544 self.fill_impl_def();
545 }
546 self.is_param |= is_node::<ast::Param>(name.syntax());
547 if ast::RecordPatField::for_field_name(&name).is_some() {
548 self.record_pat_syntax =
549 self.sema.find_node_at_offset_with_macros(&original_file, offset);
550 }
551 }
552
509 fn classify_name_ref( 553 fn classify_name_ref(
510 &mut self, 554 &mut self,
511 original_file: &SyntaxNode, 555 original_file: &SyntaxNode,
512 name_ref: ast::NameRef, 556 name_ref: ast::NameRef,
513 offset: TextSize, 557 offset: TextSize,
514 ) { 558 ) {
515 self.name_ref_syntax = 559 self.fill_impl_def();
516 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
517 let name_range = name_ref.syntax().text_range();
518 if ast::RecordExprField::for_field_name(&name_ref).is_some() { 560 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
519 self.record_lit_syntax = 561 self.record_lit_syntax =
520 self.sema.find_node_at_offset_with_macros(original_file, offset); 562 self.sema.find_node_at_offset_with_macros(original_file, offset);
521 } 563 }
564 if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() {
565 self.record_pat_syntax =
566 self.sema.find_node_at_offset_with_macros(&original_file, offset);
567 }
522 568
523 self.fill_impl_def(); 569 self.name_ref_syntax =
570 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
524 571
572 let name_range = name_ref.syntax().text_range();
525 let top_node = name_ref 573 let top_node = name_ref
526 .syntax() 574 .syntax()
527 .ancestors() 575 .ancestors()
@@ -529,31 +577,20 @@ impl<'a> CompletionContext<'a> {
529 .last() 577 .last()
530 .unwrap(); 578 .unwrap();
531 579
532 match top_node.parent().map(|it| it.kind()) { 580 if matches!(top_node.parent().map(|it| it.kind()), Some(SOURCE_FILE) | Some(ITEM_LIST)) {
533 Some(SOURCE_FILE) | Some(ITEM_LIST) => { 581 self.is_new_item = true;
534 self.is_new_item = true; 582 return;
535 return;
536 }
537 _ => (),
538 } 583 }
539 584
540 self.use_item_syntax = 585 self.use_item_syntax =
541 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); 586 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast);
542 587
543 self.function_syntax = self 588 self.function_def = self
544 .sema 589 .sema
545 .token_ancestors_with_macros(self.token.clone()) 590 .token_ancestors_with_macros(self.token.clone())
546 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 591 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
547 .find_map(ast::Fn::cast); 592 .find_map(ast::Fn::cast);
548 593
549 self.record_field_syntax = self
550 .sema
551 .token_ancestors_with_macros(self.token.clone())
552 .take_while(|it| {
553 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
554 })
555 .find_map(ast::RecordExprField::cast);
556
557 let parent = match name_ref.syntax().parent() { 594 let parent = match name_ref.syntax().parent() {
558 Some(it) => it, 595 Some(it) => it,
559 None => return, 596 None => return,
@@ -614,18 +651,8 @@ impl<'a> CompletionContext<'a> {
614 }) 651 })
615 .unwrap_or(false); 652 .unwrap_or(false);
616 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); 653 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
617
618 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
619 if let Some(if_expr) =
620 self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
621 {
622 if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start()
623 {
624 self.after_if = true;
625 }
626 }
627 }
628 } 654 }
655
629 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { 656 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
630 // The receiver comes before the point of insertion of the fake 657 // The receiver comes before the point of insertion of the fake
631 // ident, so it should have the same range in the non-modified file 658 // ident, so it should have the same range in the non-modified file
@@ -643,6 +670,7 @@ impl<'a> CompletionContext<'a> {
643 false 670 false
644 }; 671 };
645 } 672 }
673
646 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { 674 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
647 // As above 675 // As above
648 self.dot_receiver = method_call_expr 676 self.dot_receiver = method_call_expr
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 645349215..1152a9850 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -107,7 +107,7 @@ pub use crate::{
107/// identifier prefix/fuzzy match should be done higher in the stack, together 107/// identifier prefix/fuzzy match should be done higher in the stack, together
108/// with ordering of completions (currently this is done by the client). 108/// with ordering of completions (currently this is done by the client).
109/// 109///
110/// # Hypothetical Completion Problem 110/// # Speculative Completion Problem
111/// 111///
112/// There's a curious unsolved problem in the current implementation. Often, you 112/// There's a curious unsolved problem in the current implementation. Often, you
113/// want to compute completions on a *slightly different* text document. 113/// want to compute completions on a *slightly different* text document.
@@ -121,7 +121,7 @@ pub use crate::{
121/// doesn't allow such "phantom" inputs. 121/// doesn't allow such "phantom" inputs.
122/// 122///
123/// Another case where this would be instrumental is macro expansion. We want to 123/// Another case where this would be instrumental is macro expansion. We want to
124/// insert a fake ident and re-expand code. There's `expand_hypothetical` as a 124/// insert a fake ident and re-expand code. There's `expand_speculative` as a
125/// work-around for this. 125/// work-around for this.
126/// 126///
127/// A different use-case is completion of injection (examples and links in doc 127/// A different use-case is completion of injection (examples and links in doc
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index d82564381..caf0ef39f 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -10,29 +10,143 @@ use syntax::{
10 10
11#[cfg(test)] 11#[cfg(test)]
12use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; 12use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
13/// Direct parent container of the cursor position
14#[derive(Copy, Clone, Debug, PartialEq, Eq)]
15pub(crate) enum ImmediatePrevSibling {
16 IfExpr,
17 TraitDefName,
18 ImplDefType,
19}
13 20
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { 21/// Direct parent container of the cursor position
15 not_same_range_ancestor(element) 22#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16 .filter(|it| it.kind() == ASSOC_ITEM_LIST) 23pub(crate) enum ImmediateLocation {
17 .and_then(|it| it.parent()) 24 Use,
18 .filter(|it| it.kind() == TRAIT) 25 Impl,
19 .is_some() 26 Trait,
27 RecordField,
28 RefExpr,
29 IdentPat,
30 BlockExpr,
31 ItemList,
20} 32}
21#[test] 33
22fn test_has_trait_parent() { 34pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<ImmediatePrevSibling> {
23 check_pattern_is_applicable(r"trait A { f$0 }", has_trait_parent); 35 let node = maximize_name_ref(name_like)?;
36 let node = match node.parent().and_then(ast::MacroCall::cast) {
37 // When a path is being typed after the name of a trait/type of an impl it is being
38 // parsed as a macro, so when the trait/impl has a block following it an we are between the
39 // name and block the macro will attach the block to itself so maximizing fails to take
40 // that into account
41 // FIXME path expr and statement have a similar problem with attrs
42 Some(call)
43 if call.excl_token().is_none()
44 && call.token_tree().map_or(false, |t| t.l_curly_token().is_some())
45 && call.semicolon_token().is_none() =>
46 {
47 call.syntax().clone()
48 }
49 _ => node,
50 };
51 let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
52 let res = match_ast! {
53 match prev_sibling {
54 ast::ExprStmt(it) => {
55 let node = it.expr().filter(|_| it.semicolon_token().is_none())?.syntax().clone();
56 match_ast! {
57 match node {
58 ast::IfExpr(_it) => ImmediatePrevSibling::IfExpr,
59 _ => return None,
60 }
61 }
62 },
63 ast::Trait(it) => if it.assoc_item_list().is_none() {
64 ImmediatePrevSibling::TraitDefName
65 } else {
66 return None
67 },
68 ast::Impl(it) => if it.assoc_item_list().is_none()
69 && (it.for_token().is_none() || it.self_ty().is_some()) {
70 ImmediatePrevSibling::ImplDefType
71 } else {
72 return None
73 },
74 _ => return None,
75 }
76 };
77 Some(res)
24} 78}
25 79
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { 80pub(crate) fn determine_location(name_like: &ast::NameLike) -> Option<ImmediateLocation> {
27 not_same_range_ancestor(element) 81 let node = maximize_name_ref(name_like)?;
28 .filter(|it| it.kind() == ASSOC_ITEM_LIST) 82 let parent = match node.parent() {
29 .and_then(|it| it.parent()) 83 Some(parent) => match ast::MacroCall::cast(parent.clone()) {
30 .filter(|it| it.kind() == IMPL) 84 // When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call.
31 .is_some() 85 // This is usually fine as the node expansion code above already accounts for that with
86 // the ancestors call, but there is one exception to this which is that when an attribute
87 // precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ.
88 // FIXME path expr and statement have a similar problem
89 Some(call)
90 if call.excl_token().is_none()
91 && call.token_tree().is_none()
92 && call.semicolon_token().is_none() =>
93 {
94 call.syntax().parent()?
95 }
96 _ => parent,
97 },
98 // SourceFile
99 None => {
100 return match node.kind() {
101 MACRO_ITEMS | SOURCE_FILE => Some(ImmediateLocation::ItemList),
102 _ => None,
103 }
104 }
105 };
106 let res = match_ast! {
107 match parent {
108 ast::IdentPat(_it) => ImmediateLocation::IdentPat,
109 ast::Use(_it) => ImmediateLocation::Use,
110 ast::BlockExpr(_it) => ImmediateLocation::BlockExpr,
111 ast::SourceFile(_it) => ImmediateLocation::ItemList,
112 ast::ItemList(_it) => ImmediateLocation::ItemList,
113 ast::RefExpr(_it) => ImmediateLocation::RefExpr,
114 ast::RecordField(_it) => ImmediateLocation::RecordField,
115 ast::AssocItemList(it) => match it.syntax().parent().map(|it| it.kind()) {
116 Some(IMPL) => ImmediateLocation::Impl,
117 Some(TRAIT) => ImmediateLocation::Trait,
118 _ => return None,
119 },
120 _ => return None,
121 }
122 };
123 Some(res)
32} 124}
33#[test] 125
34fn test_has_impl_parent() { 126fn maximize_name_ref(name_like: &ast::NameLike) -> Option<SyntaxNode> {
35 check_pattern_is_applicable(r"impl A { f$0 }", has_impl_parent); 127 // First walk the element we are completing up to its highest node that has the same text range
128 // as the element so that we can check in what context it immediately lies. We only do this for
129 // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically.
130 // We only wanna do this if the NameRef is the last segment of the path.
131 let node = match name_like {
132 ast::NameLike::NameRef(name_ref) => {
133 if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
134 let p = segment.parent_path();
135 if p.parent_path().is_none() {
136 p.syntax()
137 .ancestors()
138 .take_while(|it| it.text_range() == p.syntax().text_range())
139 .last()?
140 } else {
141 return None;
142 }
143 } else {
144 return None;
145 }
146 }
147 it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(),
148 };
149 Some(node)
36} 150}
37 151
38pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { 152pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
@@ -53,56 +167,6 @@ fn test_inside_impl_trait_block() {
53 check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block); 167 check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block);
54} 168}
55 169
56pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool {
57 not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some()
58}
59#[test]
60fn test_has_field_list_parent() {
61 check_pattern_is_applicable(r"struct Foo { f$0 }", has_field_list_parent);
62 check_pattern_is_applicable(r"struct Foo { f$0 pub f: i32}", has_field_list_parent);
63}
64
65pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
66 not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
67}
68#[test]
69fn test_has_block_expr_parent() {
70 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f$0 }", has_block_expr_parent);
71}
72
73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
74 element.ancestors().any(|it| it.kind() == IDENT_PAT)
75}
76#[test]
77fn test_has_bind_pat_parent() {
78 check_pattern_is_applicable(r"fn my_fn(m$0) {}", has_bind_pat_parent);
79 check_pattern_is_applicable(r"fn my_fn() { let m$0 }", has_bind_pat_parent);
80}
81
82pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
83 not_same_range_ancestor(element)
84 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
85 .is_some()
86}
87#[test]
88fn test_has_ref_parent() {
89 check_pattern_is_applicable(r"fn my_fn(&m$0) {}", has_ref_parent);
90 check_pattern_is_applicable(r"fn my() { let &m$0 }", has_ref_parent);
91}
92
93pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
94 let ancestor = not_same_range_ancestor(element);
95 if !ancestor.is_some() {
96 return true;
97 }
98 ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some()
99}
100#[test]
101fn test_has_item_list_or_source_file_parent() {
102 check_pattern_is_applicable(r"i$0", has_item_list_or_source_file_parent);
103 check_pattern_is_applicable(r"mod foo { f$0 }", has_item_list_or_source_file_parent);
104}
105
106pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { 170pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
107 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some() 171 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
108 && previous_sibling_or_ancestor_sibling(element) 172 && previous_sibling_or_ancestor_sibling(element)
@@ -115,36 +179,8 @@ fn test_is_match_arm() {
115 check_pattern_is_applicable(r"fn my_fn() { match () { () => m$0 } }", is_match_arm); 179 check_pattern_is_applicable(r"fn my_fn() { match () { () => m$0 } }", is_match_arm);
116} 180}
117 181
118pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool { 182pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
119 element 183 element.into_token().and_then(|it| previous_non_trivia_token(it))
120 .into_token()
121 .and_then(|it| previous_non_trivia_token(it))
122 .filter(|it| it.kind() == T![unsafe])
123 .is_some()
124}
125#[test]
126fn test_unsafe_is_prev() {
127 check_pattern_is_applicable(r"unsafe i$0", unsafe_is_prev);
128}
129
130pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
131 element
132 .into_token()
133 .and_then(|it| previous_non_trivia_token(it))
134 .filter(|it| it.kind() == T![if])
135 .is_some()
136}
137
138pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool {
139 element
140 .into_token()
141 .and_then(|it| previous_non_trivia_token(it))
142 .filter(|it| it.kind() == T![fn])
143 .is_some()
144}
145#[test]
146fn test_fn_is_prev() {
147 check_pattern_is_applicable(r"fn l$0", fn_is_prev);
148} 184}
149 185
150/// Check if the token previous to the previous one is `for`. 186/// Check if the token previous to the previous one is `for`.
@@ -162,55 +198,26 @@ fn test_for_is_prev2() {
162 check_pattern_is_applicable(r"for i i$0", for_is_prev2); 198 check_pattern_is_applicable(r"for i i$0", for_is_prev2);
163} 199}
164 200
165#[test]
166fn test_if_is_prev() {
167 check_pattern_is_applicable(r"if l$0", if_is_prev);
168}
169
170pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
171 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT).is_some()
172}
173#[test]
174fn test_has_trait_as_prev_sibling() {
175 check_pattern_is_applicable(r"trait A w$0 {}", has_trait_as_prev_sibling);
176}
177
178pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
179 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL).is_some()
180}
181#[test]
182fn test_has_impl_as_prev_sibling() {
183 check_pattern_is_applicable(r"impl A w$0 {}", has_impl_as_prev_sibling);
184}
185
186pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { 201pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
187 for node in element.ancestors() {
188 if node.kind() == FN || node.kind() == CLOSURE_EXPR {
189 break;
190 }
191 let loop_body = match_ast! {
192 match node {
193 ast::ForExpr(it) => it.loop_body(),
194 ast::WhileExpr(it) => it.loop_body(),
195 ast::LoopExpr(it) => it.loop_body(),
196 _ => None,
197 }
198 };
199 if let Some(body) = loop_body {
200 if body.syntax().text_range().contains_range(element.text_range()) {
201 return true;
202 }
203 }
204 }
205 false
206}
207
208fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
209 element 202 element
210 .ancestors() 203 .ancestors()
211 .take_while(|it| it.text_range() == element.text_range()) 204 .take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR)
212 .last() 205 .find_map(|it| {
213 .and_then(|it| it.parent()) 206 let loop_body = match_ast! {
207 match it {
208 ast::ForExpr(it) => it.loop_body(),
209 ast::WhileExpr(it) => it.loop_body(),
210 ast::LoopExpr(it) => it.loop_body(),
211 _ => None,
212 }
213 };
214 loop_body.filter(|it| it.syntax().text_range().contains_range(element.text_range()))
215 })
216 .is_some()
217}
218
219pub(crate) fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
220 element.ancestors().skip_while(|it| it.text_range() == element.text_range()).next()
214} 221}
215 222
216fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> { 223fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
@@ -239,3 +246,111 @@ fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<Syntax
239 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev) 246 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
240 } 247 }
241} 248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 fn check_location(code: &str, loc: impl Into<Option<ImmediateLocation>>) {
255 check_pattern_is_applicable(code, |e| {
256 let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike");
257 assert_eq!(determine_location(name), loc.into());
258 true
259 });
260 }
261
262 fn check_prev_sibling(code: &str, sibling: impl Into<Option<ImmediatePrevSibling>>) {
263 check_pattern_is_applicable(code, |e| {
264 let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike");
265 assert_eq!(determine_prev_sibling(name), sibling.into());
266 true
267 });
268 }
269
270 #[test]
271 fn test_trait_loc() {
272 check_location(r"trait A { f$0 }", ImmediateLocation::Trait);
273 check_location(r"trait A { #[attr] f$0 }", ImmediateLocation::Trait);
274 check_location(r"trait A { f$0 fn f() {} }", ImmediateLocation::Trait);
275 check_location(r"trait A { fn f() {} f$0 }", ImmediateLocation::Trait);
276 check_location(r"trait A$0 {}", None);
277 check_location(r"trait A { fn f$0 }", None);
278 }
279
280 #[test]
281 fn test_impl_loc() {
282 check_location(r"impl A { f$0 }", ImmediateLocation::Impl);
283 check_location(r"impl A { #[attr] f$0 }", ImmediateLocation::Impl);
284 check_location(r"impl A { f$0 fn f() {} }", ImmediateLocation::Impl);
285 check_location(r"impl A { fn f() {} f$0 }", ImmediateLocation::Impl);
286 check_location(r"impl A$0 {}", None);
287 check_location(r"impl A { fn f$0 }", None);
288 }
289
290 #[test]
291 fn test_use_loc() {
292 check_location(r"use f$0", ImmediateLocation::Use);
293 check_location(r"use f$0;", ImmediateLocation::Use);
294 check_location(r"use f::{f$0}", None);
295 check_location(r"use {f$0}", None);
296 }
297
298 #[test]
299 fn test_record_field_loc() {
300 check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
301 check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField);
302 check_location(r"struct Foo { pub f: i32, f$0 }", ImmediateLocation::RecordField);
303 }
304
305 #[test]
306 fn test_block_expr_loc() {
307 check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr);
308 check_location(r"fn my_fn() { f$0 f }", ImmediateLocation::BlockExpr);
309 }
310
311 #[test]
312 fn test_ident_pat_loc() {
313 check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat);
314 check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat);
315 check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat);
316 check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat);
317 }
318
319 #[test]
320 fn test_ref_expr_loc() {
321 check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr);
322 }
323
324 #[test]
325 fn test_item_list_loc() {
326 check_location(r"i$0", ImmediateLocation::ItemList);
327 check_location(r"#[attr] i$0", ImmediateLocation::ItemList);
328 check_location(r"fn f() {} i$0", ImmediateLocation::ItemList);
329 check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList);
330 check_location(r"mod foo { #[attr] f$0 }", ImmediateLocation::ItemList);
331 check_location(r"mod foo { fn f() {} f$0 }", ImmediateLocation::ItemList);
332 check_location(r"mod foo$0 {}", None);
333 }
334
335 #[test]
336 fn test_impl_prev_sibling() {
337 check_prev_sibling(r"impl A w$0 ", ImmediatePrevSibling::ImplDefType);
338 check_prev_sibling(r"impl A w$0 {}", ImmediatePrevSibling::ImplDefType);
339 check_prev_sibling(r"impl A for A w$0 ", ImmediatePrevSibling::ImplDefType);
340 check_prev_sibling(r"impl A for A w$0 {}", ImmediatePrevSibling::ImplDefType);
341 check_prev_sibling(r"impl A for w$0 {}", None);
342 check_prev_sibling(r"impl A for w$0", None);
343 }
344
345 #[test]
346 fn test_trait_prev_sibling() {
347 check_prev_sibling(r"trait A w$0 ", ImmediatePrevSibling::TraitDefName);
348 check_prev_sibling(r"trait A w$0 {}", ImmediatePrevSibling::TraitDefName);
349 }
350
351 #[test]
352 fn test_if_expr_prev_sibling() {
353 check_prev_sibling(r"fn foo() { if true {} w$0", ImmediatePrevSibling::IfExpr);
354 check_prev_sibling(r"fn foo() { if true {}; w$0", None);
355 }
356}
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index d7f96b864..91300c56e 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -187,10 +187,7 @@ impl<'a> Render<'a> {
187 ScopeDef::ModuleDef(Function(func)) => { 187 ScopeDef::ModuleDef(Function(func)) => {
188 return render_fn(self.ctx, import_to_add, Some(local_name), *func); 188 return render_fn(self.ctx, import_to_add, Some(local_name), *func);
189 } 189 }
190 ScopeDef::ModuleDef(Variant(_)) 190 ScopeDef::ModuleDef(Variant(_)) if self.ctx.completion.is_pat_or_const.is_some() => {
191 if self.ctx.completion.is_pat_binding_or_const
192 | self.ctx.completion.is_irrefutable_pat_binding =>
193 {
194 CompletionItemKind::SymbolKind(SymbolKind::Variant) 191 CompletionItemKind::SymbolKind(SymbolKind::Variant)
195 } 192 }
196 ScopeDef::ModuleDef(Variant(var)) => { 193 ScopeDef::ModuleDef(Variant(var)) => {
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index 7578ad50b..b90fd3890 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -74,7 +74,11 @@ impl<'a> MacroRender<'a> {
74 if self.needs_bang() && self.ctx.snippet_cap().is_some() { 74 if self.needs_bang() && self.ctx.snippet_cap().is_some() {
75 format!("{}!{}…{}", self.name, self.bra, self.ket) 75 format!("{}!{}…{}", self.name, self.bra, self.ket)
76 } else { 76 } else {
77 self.banged_name() 77 if self.macro_.kind() == hir::MacroKind::Derive {
78 self.name.to_string()
79 } else {
80 self.banged_name()
81 }
78 } 82 }
79 } 83 }
80 84
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index 37be575e5..93c7c872c 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -12,7 +12,7 @@ use ide_db::{
12use itertools::Itertools; 12use itertools::Itertools;
13use stdx::{format_to, trim_indent}; 13use stdx::{format_to, trim_indent};
14use syntax::{AstNode, NodeOrToken, SyntaxElement}; 14use syntax::{AstNode, NodeOrToken, SyntaxElement};
15use test_utils::{assert_eq_text, RangeOrOffset}; 15use test_utils::assert_eq_text;
16 16
17use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; 17use crate::{item::CompletionKind, CompletionConfig, CompletionItem};
18 18
@@ -36,10 +36,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
36 let mut database = RootDatabase::default(); 36 let mut database = RootDatabase::default();
37 database.apply_change(change_fixture.change); 37 database.apply_change(change_fixture.change);
38 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 38 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
39 let offset = match range_or_offset { 39 let offset = range_or_offset.expect_offset();
40 RangeOrOffset::Range(_) => panic!(),
41 RangeOrOffset::Offset(it) => it,
42 };
43 (database, FilePosition { file_id, offset }) 40 (database, FilePosition { file_id, offset })
44} 41}
45 42
@@ -52,10 +49,11 @@ pub(crate) fn do_completion_with_config(
52 code: &str, 49 code: &str,
53 kind: CompletionKind, 50 kind: CompletionKind,
54) -> Vec<CompletionItem> { 51) -> Vec<CompletionItem> {
55 let mut kind_completions: Vec<CompletionItem> = 52 get_all_items(config, code)
56 get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect(); 53 .into_iter()
57 kind_completions.sort_by(|l, r| l.label().cmp(r.label())); 54 .filter(|c| c.completion_kind == kind)
58 kind_completions 55 .sorted_by(|l, r| l.label().cmp(r.label()))
56 .collect()
59} 57}
60 58
61pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { 59pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
@@ -132,7 +130,7 @@ pub(crate) fn check_edit_with_config(
132 assert_eq_text!(&ra_fixture_after, &actual) 130 assert_eq_text!(&ra_fixture_after, &actual)
133} 131}
134 132
135pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { 133pub(crate) fn check_pattern_is_applicable(code: &str, check: impl FnOnce(SyntaxElement) -> bool) {
136 let (db, pos) = position(code); 134 let (db, pos) = position(code);
137 135
138 let sema = Semantics::new(&db); 136 let sema = Semantics::new(&db);