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.rs42
-rw-r--r--crates/ide_completion/src/completions/attribute.rs939
-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/dot.rs84
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs8
-rw-r--r--crates/ide_completion/src/completions/fn_param.rs15
-rw-r--r--crates/ide_completion/src/completions/keyword.rs249
-rw-r--r--crates/ide_completion/src/completions/lifetime.rs12
-rw-r--r--crates/ide_completion/src/completions/macro_in_item_position.rs19
-rw-r--r--crates/ide_completion/src/completions/mod_.rs6
-rw-r--r--crates/ide_completion/src/completions/pattern.rs49
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs92
-rw-r--r--crates/ide_completion/src/completions/record.rs43
-rw-r--r--crates/ide_completion/src/completions/snippet.rs8
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs94
-rw-r--r--crates/ide_completion/src/config.rs1
-rw-r--r--crates/ide_completion/src/context.rs495
-rw-r--r--crates/ide_completion/src/lib.rs4
-rw-r--r--crates/ide_completion/src/patterns.rs478
-rw-r--r--crates/ide_completion/src/render.rs161
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs55
-rw-r--r--crates/ide_completion/src/render/function.rs50
-rw-r--r--crates/ide_completion/src/render/macro_.rs15
-rw-r--r--crates/ide_completion/src/test_utils.rs19
25 files changed, 2195 insertions, 1077 deletions
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index 78154bf3e..ffdcdc930 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -18,7 +18,7 @@ pub(crate) mod unqualified_path;
18 18
19use std::iter; 19use std::iter;
20 20
21use hir::{known, ModPath, ScopeDef, Type}; 21use hir::known;
22use ide_db::SymbolKind; 22use ide_db::SymbolKind;
23 23
24use crate::{ 24use crate::{
@@ -69,13 +69,25 @@ impl Completions {
69 items.into_iter().for_each(|item| self.add(item.into())) 69 items.into_iter().for_each(|item| self.add(item.into()))
70 } 70 }
71 71
72 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) { 72 pub(crate) fn add_field(
73 let item = render_field(RenderContext::new(ctx), field, ty); 73 &mut self,
74 ctx: &CompletionContext,
75 receiver: Option<hir::Name>,
76 field: hir::Field,
77 ty: &hir::Type,
78 ) {
79 let item = render_field(RenderContext::new(ctx), receiver, field, ty);
74 self.add(item); 80 self.add(item);
75 } 81 }
76 82
77 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) { 83 pub(crate) fn add_tuple_field(
78 let item = render_tuple_field(RenderContext::new(ctx), field, ty); 84 &mut self,
85 ctx: &CompletionContext,
86 receiver: Option<hir::Name>,
87 field: usize,
88 ty: &hir::Type,
89 ) {
90 let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
79 self.add(item); 91 self.add(item);
80 } 92 }
81 93
@@ -89,8 +101,8 @@ impl Completions {
89 pub(crate) fn add_resolution( 101 pub(crate) fn add_resolution(
90 &mut self, 102 &mut self,
91 ctx: &CompletionContext, 103 ctx: &CompletionContext,
92 local_name: String, 104 local_name: hir::Name,
93 resolution: &ScopeDef, 105 resolution: &hir::ScopeDef,
94 ) { 106 ) {
95 if let Some(item) = render_resolution(RenderContext::new(ctx), local_name, resolution) { 107 if let Some(item) = render_resolution(RenderContext::new(ctx), local_name, resolution) {
96 self.add(item); 108 self.add(item);
@@ -100,7 +112,7 @@ impl Completions {
100 pub(crate) fn add_macro( 112 pub(crate) fn add_macro(
101 &mut self, 113 &mut self,
102 ctx: &CompletionContext, 114 ctx: &CompletionContext,
103 name: Option<String>, 115 name: Option<hir::Name>,
104 macro_: hir::MacroDef, 116 macro_: hir::MacroDef,
105 ) { 117 ) {
106 let name = match name { 118 let name = match name {
@@ -116,7 +128,7 @@ impl Completions {
116 &mut self, 128 &mut self,
117 ctx: &CompletionContext, 129 ctx: &CompletionContext,
118 func: hir::Function, 130 func: hir::Function,
119 local_name: Option<String>, 131 local_name: Option<hir::Name>,
120 ) { 132 ) {
121 if let Some(item) = render_fn(RenderContext::new(ctx), None, local_name, func) { 133 if let Some(item) = render_fn(RenderContext::new(ctx), None, local_name, func) {
122 self.add(item) 134 self.add(item)
@@ -127,9 +139,11 @@ impl Completions {
127 &mut self, 139 &mut self,
128 ctx: &CompletionContext, 140 ctx: &CompletionContext,
129 func: hir::Function, 141 func: hir::Function,
130 local_name: Option<String>, 142 receiver: Option<hir::Name>,
143 local_name: Option<hir::Name>,
131 ) { 144 ) {
132 if let Some(item) = render_method(RenderContext::new(ctx), None, local_name, func) { 145 if let Some(item) = render_method(RenderContext::new(ctx), None, receiver, local_name, func)
146 {
133 self.add(item) 147 self.add(item)
134 } 148 }
135 } 149 }
@@ -149,7 +163,7 @@ impl Completions {
149 &mut self, 163 &mut self,
150 ctx: &CompletionContext, 164 ctx: &CompletionContext,
151 variant: hir::Variant, 165 variant: hir::Variant,
152 path: ModPath, 166 path: hir::ModPath,
153 ) { 167 ) {
154 if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)) { 168 if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)) {
155 self.add(item); 169 self.add(item);
@@ -183,7 +197,7 @@ impl Completions {
183 &mut self, 197 &mut self,
184 ctx: &CompletionContext, 198 ctx: &CompletionContext,
185 variant: hir::Variant, 199 variant: hir::Variant,
186 path: ModPath, 200 path: hir::ModPath,
187 ) { 201 ) {
188 let item = render_variant(RenderContext::new(ctx), None, None, variant, Some(path)); 202 let item = render_variant(RenderContext::new(ctx), None, None, variant, Some(path));
189 self.add(item); 203 self.add(item);
@@ -193,7 +207,7 @@ impl Completions {
193 &mut self, 207 &mut self,
194 ctx: &CompletionContext, 208 ctx: &CompletionContext,
195 variant: hir::Variant, 209 variant: hir::Variant,
196 local_name: Option<String>, 210 local_name: Option<hir::Name>,
197 ) { 211 ) {
198 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); 212 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None);
199 self.add(item); 213 self.add(item);
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index b1505c74b..c48bb9e66 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::{algo::non_trivia_sibling, ast, AstNode, Direction, NodeOrToken, SyntaxKind, T};
9 9
10use crate::{ 10use crate::{
11 context::CompletionContext, 11 context::CompletionContext,
@@ -14,33 +14,45 @@ 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) {
42 let is_inner = attribute.kind() == ast::AttrKind::Inner; 40 let is_inner = attribute.kind() == ast::AttrKind::Inner;
43 for attr_completion in ATTRIBUTES.iter().filter(|compl| is_inner || !compl.prefer_inner) { 41 let attribute_annotated_item_kind =
42 attribute.syntax().parent().map(|it| it.kind()).filter(|_| {
43 is_inner
44 // If we got nothing coming after the attribute it could be anything so filter it the kind out
45 || non_trivia_sibling(attribute.syntax().clone().into(), Direction::Next).is_some()
46 });
47 let attributes = attribute_annotated_item_kind.and_then(|kind| {
48 if ast::Expr::can_cast(kind) {
49 Some(EXPR_ATTRIBUTES)
50 } else {
51 KIND_TO_ATTRIBUTES.get(&kind).copied()
52 }
53 });
54
55 let add_completion = |attr_completion: &AttrCompletion| {
44 let mut item = CompletionItem::new( 56 let mut item = CompletionItem::new(
45 CompletionKind::Attribute, 57 CompletionKind::Attribute,
46 ctx.source_range(), 58 ctx.source_range(),
@@ -56,9 +68,19 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
56 item.insert_snippet(cap, snippet); 68 item.insert_snippet(cap, snippet);
57 } 69 }
58 70
59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { 71 if is_inner || !attr_completion.prefer_inner {
60 acc.add(item.build()); 72 acc.add(item.build());
61 } 73 }
74 };
75
76 match attributes {
77 Some(applicable) => applicable
78 .iter()
79 .flat_map(|name| ATTRIBUTES.binary_search_by(|attr| attr.key().cmp(name)).ok())
80 .flat_map(|idx| ATTRIBUTES.get(idx))
81 .for_each(add_completion),
82 None if is_inner => ATTRIBUTES.iter().for_each(add_completion),
83 None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion),
62 } 84 }
63} 85}
64 86
@@ -70,6 +92,10 @@ struct AttrCompletion {
70} 92}
71 93
72impl AttrCompletion { 94impl AttrCompletion {
95 fn key(&self) -> &'static str {
96 self.lookup.unwrap_or(self.label)
97 }
98
73 const fn prefer_inner(self) -> AttrCompletion { 99 const fn prefer_inner(self) -> AttrCompletion {
74 AttrCompletion { prefer_inner: true, ..self } 100 AttrCompletion { prefer_inner: true, ..self }
75 } 101 }
@@ -83,30 +109,122 @@ const fn attr(
83 AttrCompletion { label, lookup, snippet, prefer_inner: false } 109 AttrCompletion { label, lookup, snippet, prefer_inner: false }
84} 110}
85 111
112macro_rules! attrs {
113 // attributes applicable to all items
114 [@ { item $($tt:tt)* } {$($acc:tt)*}] => {
115 attrs!(@ { $($tt)* } { $($acc)*, "deprecated", "doc", "dochidden", "docalias", "must_use", "no_mangle" })
116 };
117 // attributes applicable to all adts
118 [@ { adt $($tt:tt)* } {$($acc:tt)*}] => {
119 attrs!(@ { $($tt)* } { $($acc)*, "derive", "repr" })
120 };
121 // attributes applicable to all linkable things aka functions/statics
122 [@ { linkable $($tt:tt)* } {$($acc:tt)*}] => {
123 attrs!(@ { $($tt)* } { $($acc)*, "export_name", "link_name", "link_section" })
124 };
125 // error fallback for nicer error message
126 [@ { $ty:ident $($tt:tt)* } {$($acc:tt)*}] => {
127 compile_error!(concat!("unknown attr subtype ", stringify!($ty)))
128 };
129 // general push down accumulation
130 [@ { $lit:literal $($tt:tt)*} {$($acc:tt)*}] => {
131 attrs!(@ { $($tt)* } { $($acc)*, $lit })
132 };
133 [@ {$($tt:tt)+} {$($tt2:tt)*}] => {
134 compile_error!(concat!("Unexpected input ", stringify!($($tt)+)))
135 };
136 // final output construction
137 [@ {} {$($tt:tt)*}] => { &[$($tt)*] as _ };
138 // starting matcher
139 [$($tt:tt),*] => {
140 attrs!(@ { $($tt)* } { "allow", "cfg", "cfg_attr", "deny", "forbid", "warn" })
141 };
142}
143
144#[rustfmt::skip]
145static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| {
146 use SyntaxKind::*;
147 std::array::IntoIter::new([
148 (
149 SOURCE_FILE,
150 attrs!(
151 item,
152 "crate_name", "feature", "no_implicit_prelude", "no_main", "no_std",
153 "recursion_limit", "type_length_limit", "windows_subsystem"
154 ),
155 ),
156 (MODULE, attrs!(item, "no_implicit_prelude", "path")),
157 (ITEM_LIST, attrs!(item, "no_implicit_prelude")),
158 (MACRO_RULES, attrs!(item, "macro_export", "macro_use")),
159 (MACRO_DEF, attrs!(item)),
160 (EXTERN_CRATE, attrs!(item, "macro_use", "no_link")),
161 (USE, attrs!(item)),
162 (TYPE_ALIAS, attrs!(item)),
163 (STRUCT, attrs!(item, adt, "non_exhaustive")),
164 (ENUM, attrs!(item, adt, "non_exhaustive")),
165 (UNION, attrs!(item, adt)),
166 (CONST, attrs!(item)),
167 (
168 FN,
169 attrs!(
170 item, linkable,
171 "cold", "ignore", "inline", "must_use", "panic_handler", "proc_macro",
172 "proc_macro_derive", "proc_macro_attribute", "should_panic", "target_feature",
173 "test", "track_caller"
174 ),
175 ),
176 (STATIC, attrs!(item, linkable, "global_allocator", "used")),
177 (TRAIT, attrs!(item, "must_use")),
178 (IMPL, attrs!(item, "automatically_derived")),
179 (ASSOC_ITEM_LIST, attrs!(item)),
180 (EXTERN_BLOCK, attrs!(item, "link")),
181 (EXTERN_ITEM_LIST, attrs!(item, "link")),
182 (MACRO_CALL, attrs!()),
183 (SELF_PARAM, attrs!()),
184 (PARAM, attrs!()),
185 (RECORD_FIELD, attrs!()),
186 (VARIANT, attrs!("non_exhaustive")),
187 (TYPE_PARAM, attrs!()),
188 (CONST_PARAM, attrs!()),
189 (LIFETIME_PARAM, attrs!()),
190 (LET_STMT, attrs!()),
191 (EXPR_STMT, attrs!()),
192 (LITERAL, attrs!()),
193 (RECORD_EXPR_FIELD_LIST, attrs!()),
194 (RECORD_EXPR_FIELD, attrs!()),
195 (MATCH_ARM_LIST, attrs!()),
196 (MATCH_ARM, attrs!()),
197 (IDENT_PAT, attrs!()),
198 (RECORD_PAT_FIELD, attrs!()),
199 ])
200 .collect()
201});
202const EXPR_ATTRIBUTES: &[&str] = attrs!();
203
86/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index 204/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index
205// Keep these sorted for the binary search!
87const ATTRIBUTES: &[AttrCompletion] = &[ 206const ATTRIBUTES: &[AttrCompletion] = &[
88 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), 207 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
89 attr("automatically_derived", None, None), 208 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})")), 209 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
210 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
92 attr("cold", None, None), 211 attr("cold", None, None),
93 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#)) 212 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#))
94 .prefer_inner(), 213 .prefer_inner(),
95 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")), 214 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
96 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)), 215 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)),
97 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)), 216 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
217 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
218 attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
219 attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
98 attr( 220 attr(
99 r#"export_name = "…""#, 221 r#"export_name = "…""#,
100 Some("export_name"), 222 Some("export_name"),
101 Some(r#"export_name = "${0:exported_symbol_name}""#), 223 Some(r#"export_name = "${0:exported_symbol_name}""#),
102 ), 224 ),
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(), 225 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
107 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), 226 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
108 // FIXME: resolve through macro resolution? 227 attr("global_allocator", None, None),
109 attr("global_allocator", None, None).prefer_inner(),
110 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)), 228 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
111 attr("inline", Some("inline"), Some("inline")), 229 attr("inline", Some("inline"), Some("inline")),
112 attr("link", None, None), 230 attr("link", None, None),
@@ -119,13 +237,13 @@ const ATTRIBUTES: &[AttrCompletion] = &[
119 attr("macro_export", None, None), 237 attr("macro_export", None, None),
120 attr("macro_use", None, None), 238 attr("macro_use", None, None),
121 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)), 239 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(), 240 attr("no_implicit_prelude", None, None).prefer_inner(),
241 attr("no_link", None, None).prefer_inner(),
124 attr("no_main", None, None).prefer_inner(), 242 attr("no_main", None, None).prefer_inner(),
125 attr("no_mangle", None, None), 243 attr("no_mangle", None, None),
126 attr("no_std", None, None).prefer_inner(), 244 attr("no_std", None, None).prefer_inner(),
127 attr("non_exhaustive", None, None), 245 attr("non_exhaustive", None, None),
128 attr("panic_handler", None, None).prefer_inner(), 246 attr("panic_handler", None, None),
129 attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)), 247 attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)),
130 attr("proc_macro", None, None), 248 attr("proc_macro", None, None),
131 attr("proc_macro_attribute", None, None), 249 attr("proc_macro_attribute", None, None),
@@ -153,381 +271,539 @@ const ATTRIBUTES: &[AttrCompletion] = &[
153 .prefer_inner(), 271 .prefer_inner(),
154]; 272];
155 273
156fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { 274fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Option<FxHashSet<String>> {
157 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) { 275 let (l_paren, r_paren) = derive_input.l_paren_token().zip(derive_input.r_paren_token())?;
158 for derive_completion in DEFAULT_DERIVE_COMPLETIONS 276 let mut input_derives = FxHashSet::default();
159 .iter() 277 let mut tokens = derive_input
160 .filter(|completion| !existing_derives.contains(completion.label)) 278 .syntax()
161 { 279 .children_with_tokens()
162 let mut components = vec![derive_completion.label]; 280 .filter_map(NodeOrToken::into_token)
163 components.extend( 281 .skip_while(|token| token != &l_paren)
164 derive_completion 282 .skip(1)
165 .dependencies 283 .take_while(|token| token != &r_paren)
166 .iter() 284 .peekable();
167 .filter(|&&dependency| !existing_derives.contains(dependency)), 285 let mut input = String::new();
168 ); 286 while tokens.peek().is_some() {
169 let lookup = components.join(", "); 287 for token in tokens.by_ref().take_while(|t| t.kind() != T![,]) {
170 let label = components.iter().rev().join(", "); 288 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 } 289 }
176 290
177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { 291 if !input.is_empty() {
178 let mut item = CompletionItem::new( 292 input_derives.insert(input.trim().to_owned());
179 CompletionKind::Attribute,
180 ctx.source_range(),
181 custom_derive_name,
182 );
183 item.kind(CompletionItemKind::Attribute);
184 item.add_to(acc);
185 } 293 }
186 }
187}
188
189fn complete_lint(
190 acc: &mut Completions,
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 }
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 294
236 if !current_derive.is_empty() { 295 input.clear();
237 input_derives.insert(current_derive);
238 }
239 Ok(input_derives)
240 }
241 _ => Err(()),
242 } 296 }
243}
244
245fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
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}
262
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 297
277pub(crate) struct LintCompletion { 298 Some(input_derives)
278 pub(crate) label: &'static str,
279 pub(crate) description: &'static str,
280} 299}
281 300
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)] 301#[cfg(test)]
402mod tests { 302mod tests {
303 use super::*;
304
403 use expect_test::{expect, Expect}; 305 use expect_test::{expect, Expect};
404 306
405 use crate::{test_utils::completion_list, CompletionKind}; 307 use crate::{test_utils::completion_list, CompletionKind};
406 308
309 #[test]
310 fn attributes_are_sorted() {
311 let mut attrs = ATTRIBUTES.iter().map(|attr| attr.key());
312 let mut prev = attrs.next().unwrap();
313
314 attrs.for_each(|next| {
315 assert!(
316 prev < next,
317 r#"ATTRIBUTES array is not sorted, "{}" should come after "{}""#,
318 prev,
319 next
320 );
321 prev = next;
322 });
323 }
324
407 fn check(ra_fixture: &str, expect: Expect) { 325 fn check(ra_fixture: &str, expect: Expect) {
408 let actual = completion_list(ra_fixture, CompletionKind::Attribute); 326 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
409 expect.assert_eq(&actual); 327 expect.assert_eq(&actual);
410 } 328 }
411 329
412 #[test] 330 #[test]
413 fn empty_derive_completion() { 331 fn test_attribute_completion_inside_nested_attr() {
332 check(r#"#[cfg($0)]"#, expect![[]])
333 }
334
335 #[test]
336 fn test_attribute_completion_with_existing_attr() {
414 check( 337 check(
415 r#" 338 r#"#[no_mangle] #[$0] mcall!();"#,
416#[derive($0)]
417struct Test {}
418 "#,
419 expect![[r#" 339 expect![[r#"
420 at Clone 340 at allow(…)
421 at Clone, Copy 341 at cfg(…)
422 at Debug 342 at cfg_attr(…)
423 at Default 343 at deny(…)
424 at Hash 344 at forbid(…)
425 at PartialEq 345 at warn(…)
426 at PartialEq, Eq 346 "#]],
427 at PartialEq, PartialOrd 347 )
428 at PartialEq, Eq, PartialOrd, Ord 348 }
349
350 #[test]
351 fn complete_attribute_on_source_file() {
352 check(
353 r#"#![$0]"#,
354 expect![[r#"
355 at allow(…)
356 at cfg(…)
357 at cfg_attr(…)
358 at deny(…)
359 at forbid(…)
360 at warn(…)
361 at deprecated
362 at doc = "…"
363 at doc(hidden)
364 at doc(alias = "…")
365 at must_use
366 at no_mangle
367 at crate_name = ""
368 at feature(…)
369 at no_implicit_prelude
370 at no_main
371 at no_std
372 at recursion_limit = …
373 at type_length_limit = …
374 at windows_subsystem = "…"
429 "#]], 375 "#]],
430 ); 376 );
431 } 377 }
432 378
433 #[test] 379 #[test]
434 fn no_completion_for_incorrect_derive() { 380 fn complete_attribute_on_module() {
435 check( 381 check(
436 r#" 382 r#"#[$0] mod foo;"#,
437#[derive{$0)] 383 expect![[r#"
438struct Test {} 384 at allow(…)
439"#, 385 at cfg(…)
440 expect![[r#""#]], 386 at cfg_attr(…)
441 ) 387 at deny(…)
388 at forbid(…)
389 at warn(…)
390 at deprecated
391 at doc = "…"
392 at doc(hidden)
393 at doc(alias = "…")
394 at must_use
395 at no_mangle
396 at path = "…"
397 "#]],
398 );
399 check(
400 r#"mod foo {#![$0]}"#,
401 expect![[r#"
402 at allow(…)
403 at cfg(…)
404 at cfg_attr(…)
405 at deny(…)
406 at forbid(…)
407 at warn(…)
408 at deprecated
409 at doc = "…"
410 at doc(hidden)
411 at doc(alias = "…")
412 at must_use
413 at no_mangle
414 at no_implicit_prelude
415 "#]],
416 );
442 } 417 }
443 418
444 #[test] 419 #[test]
445 fn derive_with_input_completion() { 420 fn complete_attribute_on_macro_rules() {
446 check( 421 check(
447 r#" 422 r#"#[$0] macro_rules! foo {}"#,
448#[derive(serde::Serialize, PartialEq, $0)]
449struct Test {}
450"#,
451 expect![[r#" 423 expect![[r#"
452 at Clone 424 at allow(…)
453 at Clone, Copy 425 at cfg(…)
454 at Debug 426 at cfg_attr(…)
455 at Default 427 at deny(…)
456 at Hash 428 at forbid(…)
457 at Eq 429 at warn(…)
458 at PartialOrd 430 at deprecated
459 at Eq, PartialOrd, Ord 431 at doc = "…"
432 at doc(hidden)
433 at doc(alias = "…")
434 at must_use
435 at no_mangle
436 at macro_export
437 at macro_use
460 "#]], 438 "#]],
461 ) 439 );
462 } 440 }
463 441
464 #[test] 442 #[test]
465 fn test_attribute_completion() { 443 fn complete_attribute_on_macro_def() {
466 check( 444 check(
467 r#"#[$0]"#, 445 r#"#[$0] macro foo {}"#,
468 expect![[r#" 446 expect![[r#"
469 at allow(…) 447 at allow(…)
470 at automatically_derived 448 at cfg(…)
471 at cfg_attr(…) 449 at cfg_attr(…)
450 at deny(…)
451 at forbid(…)
452 at warn(…)
453 at deprecated
454 at doc = "…"
455 at doc(hidden)
456 at doc(alias = "…")
457 at must_use
458 at no_mangle
459 "#]],
460 );
461 }
462
463 #[test]
464 fn complete_attribute_on_extern_crate() {
465 check(
466 r#"#[$0] extern crate foo;"#,
467 expect![[r#"
468 at allow(…)
472 at cfg(…) 469 at cfg(…)
473 at cold 470 at cfg_attr(…)
474 at deny(…) 471 at deny(…)
472 at forbid(…)
473 at warn(…)
475 at deprecated 474 at deprecated
475 at doc = "…"
476 at doc(hidden)
477 at doc(alias = "…")
478 at must_use
479 at no_mangle
480 at macro_use
481 "#]],
482 );
483 }
484
485 #[test]
486 fn complete_attribute_on_use() {
487 check(
488 r#"#[$0] use foo;"#,
489 expect![[r#"
490 at allow(…)
491 at cfg(…)
492 at cfg_attr(…)
493 at deny(…)
494 at forbid(…)
495 at warn(…)
496 at deprecated
497 at doc = "…"
498 at doc(hidden)
499 at doc(alias = "…")
500 at must_use
501 at no_mangle
502 "#]],
503 );
504 }
505
506 #[test]
507 fn complete_attribute_on_type_alias() {
508 check(
509 r#"#[$0] type foo = ();"#,
510 expect![[r#"
511 at allow(…)
512 at cfg(…)
513 at cfg_attr(…)
514 at deny(…)
515 at forbid(…)
516 at warn(…)
517 at deprecated
518 at doc = "…"
519 at doc(hidden)
520 at doc(alias = "…")
521 at must_use
522 at no_mangle
523 "#]],
524 );
525 }
526
527 #[test]
528 fn complete_attribute_on_struct() {
529 check(
530 r#"#[$0] struct Foo;"#,
531 expect![[r#"
532 at allow(…)
533 at cfg(…)
534 at cfg_attr(…)
535 at deny(…)
536 at forbid(…)
537 at warn(…)
538 at deprecated
539 at doc = "…"
540 at doc(hidden)
541 at doc(alias = "…")
542 at must_use
543 at no_mangle
476 at derive(…) 544 at derive(…)
477 at export_name = "…" 545 at repr(…)
546 at non_exhaustive
547 "#]],
548 );
549 }
550
551 #[test]
552 fn complete_attribute_on_enum() {
553 check(
554 r#"#[$0] enum Foo {}"#,
555 expect![[r#"
556 at allow(…)
557 at cfg(…)
558 at cfg_attr(…)
559 at deny(…)
560 at forbid(…)
561 at warn(…)
562 at deprecated
563 at doc = "…"
564 at doc(hidden)
478 at doc(alias = "…") 565 at doc(alias = "…")
566 at must_use
567 at no_mangle
568 at derive(…)
569 at repr(…)
570 at non_exhaustive
571 "#]],
572 );
573 }
574
575 #[test]
576 fn complete_attribute_on_const() {
577 check(
578 r#"#[$0] const FOO: () = ();"#,
579 expect![[r#"
580 at allow(…)
581 at cfg(…)
582 at cfg_attr(…)
583 at deny(…)
584 at forbid(…)
585 at warn(…)
586 at deprecated
479 at doc = "…" 587 at doc = "…"
480 at doc(hidden) 588 at doc(hidden)
589 at doc(alias = "…")
590 at must_use
591 at no_mangle
592 "#]],
593 );
594 }
595
596 #[test]
597 fn complete_attribute_on_static() {
598 check(
599 r#"#[$0] static FOO: () = ()"#,
600 expect![[r#"
601 at allow(…)
602 at cfg(…)
603 at cfg_attr(…)
604 at deny(…)
481 at forbid(…) 605 at forbid(…)
482 at ignore = "…" 606 at warn(…)
483 at inline 607 at deprecated
484 at link 608 at doc = "…"
609 at doc(hidden)
610 at doc(alias = "…")
611 at must_use
612 at no_mangle
613 at export_name = "…"
485 at link_name = "…" 614 at link_name = "…"
486 at link_section = "…" 615 at link_section = "…"
487 at macro_export 616 at global_allocator
488 at macro_use 617 at used
618 "#]],
619 );
620 }
621
622 #[test]
623 fn complete_attribute_on_trait() {
624 check(
625 r#"#[$0] trait Foo {}"#,
626 expect![[r#"
627 at allow(…)
628 at cfg(…)
629 at cfg_attr(…)
630 at deny(…)
631 at forbid(…)
632 at warn(…)
633 at deprecated
634 at doc = "…"
635 at doc(hidden)
636 at doc(alias = "…")
637 at must_use
638 at no_mangle
639 at must_use
640 "#]],
641 );
642 }
643
644 #[test]
645 fn complete_attribute_on_impl() {
646 check(
647 r#"#[$0] impl () {}"#,
648 expect![[r#"
649 at allow(…)
650 at cfg(…)
651 at cfg_attr(…)
652 at deny(…)
653 at forbid(…)
654 at warn(…)
655 at deprecated
656 at doc = "…"
657 at doc(hidden)
658 at doc(alias = "…")
659 at must_use
660 at no_mangle
661 at automatically_derived
662 "#]],
663 );
664 check(
665 r#"impl () {#![$0]}"#,
666 expect![[r#"
667 at allow(…)
668 at cfg(…)
669 at cfg_attr(…)
670 at deny(…)
671 at forbid(…)
672 at warn(…)
673 at deprecated
674 at doc = "…"
675 at doc(hidden)
676 at doc(alias = "…")
489 at must_use 677 at must_use
490 at no_mangle 678 at no_mangle
679 "#]],
680 );
681 }
682
683 #[test]
684 fn complete_attribute_on_extern_block() {
685 check(
686 r#"#[$0] extern {}"#,
687 expect![[r#"
688 at allow(…)
689 at cfg(…)
690 at cfg_attr(…)
691 at deny(…)
692 at forbid(…)
693 at warn(…)
694 at deprecated
695 at doc = "…"
696 at doc(hidden)
697 at doc(alias = "…")
698 at must_use
699 at no_mangle
700 at link
701 "#]],
702 );
703 check(
704 r#"extern {#![$0]}"#,
705 expect![[r#"
706 at allow(…)
707 at cfg(…)
708 at cfg_attr(…)
709 at deny(…)
710 at forbid(…)
711 at warn(…)
712 at deprecated
713 at doc = "…"
714 at doc(hidden)
715 at doc(alias = "…")
716 at must_use
717 at no_mangle
718 at link
719 "#]],
720 );
721 }
722
723 #[test]
724 fn complete_attribute_on_variant() {
725 check(
726 r#"enum Foo { #[$0] Bar }"#,
727 expect![[r#"
728 at allow(…)
729 at cfg(…)
730 at cfg_attr(…)
731 at deny(…)
732 at forbid(…)
733 at warn(…)
491 at non_exhaustive 734 at non_exhaustive
492 at path = "…" 735 "#]],
736 );
737 }
738
739 #[test]
740 fn complete_attribute_on_fn() {
741 check(
742 r#"#[$0] fn main() {}"#,
743 expect![[r#"
744 at allow(…)
745 at cfg(…)
746 at cfg_attr(…)
747 at deny(…)
748 at forbid(…)
749 at warn(…)
750 at deprecated
751 at doc = "…"
752 at doc(hidden)
753 at doc(alias = "…")
754 at must_use
755 at no_mangle
756 at export_name = "…"
757 at link_name = "…"
758 at link_section = "…"
759 at cold
760 at ignore = "…"
761 at inline
762 at must_use
763 at panic_handler
493 at proc_macro 764 at proc_macro
494 at proc_macro_attribute
495 at proc_macro_derive(…) 765 at proc_macro_derive(…)
496 at repr(…) 766 at proc_macro_attribute
497 at should_panic 767 at should_panic
498 at target_feature = "…" 768 at target_feature = "…"
499 at test 769 at test
500 at track_caller 770 at track_caller
501 at used
502 at warn(…)
503 "#]], 771 "#]],
504 ) 772 );
505 } 773 }
506 774
507 #[test] 775 #[test]
508 fn test_attribute_completion_inside_nested_attr() { 776 fn complete_attribute_on_expr() {
509 check(r#"#[cfg($0)]"#, expect![[]]) 777 check(
778 r#"fn main() { #[$0] foo() }"#,
779 expect![[r#"
780 at allow(…)
781 at cfg(…)
782 at cfg_attr(…)
783 at deny(…)
784 at forbid(…)
785 at warn(…)
786 "#]],
787 );
510 } 788 }
511 789
512 #[test] 790 #[test]
513 fn test_inner_attribute_completion() { 791 fn complete_attribute_in_source_file_end() {
514 check( 792 check(
515 r"#![$0]", 793 r#"#[$0]"#,
516 expect![[r#" 794 expect![[r#"
517 at allow(…) 795 at allow(…)
518 at automatically_derived 796 at automatically_derived
519 at cfg_attr(…)
520 at cfg(…) 797 at cfg(…)
798 at cfg_attr(…)
521 at cold 799 at cold
522 at crate_name = ""
523 at deny(…) 800 at deny(…)
524 at deprecated 801 at deprecated
525 at derive(…) 802 at derive(…)
526 at export_name = "…"
527 at doc(alias = "…")
528 at doc = "…" 803 at doc = "…"
804 at doc(alias = "…")
529 at doc(hidden) 805 at doc(hidden)
530 at feature() 806 at export_name = ""
531 at forbid(…) 807 at forbid(…)
532 at global_allocator 808 at global_allocator
533 at ignore = "…" 809 at ignore = "…"
@@ -538,27 +814,20 @@ struct Test {}
538 at macro_export 814 at macro_export
539 at macro_use 815 at macro_use
540 at must_use 816 at must_use
541 at no_link
542 at no_implicit_prelude
543 at no_main
544 at no_mangle 817 at no_mangle
545 at no_std
546 at non_exhaustive 818 at non_exhaustive
547 at panic_handler 819 at panic_handler
548 at path = "…" 820 at path = "…"
549 at proc_macro 821 at proc_macro
550 at proc_macro_attribute 822 at proc_macro_attribute
551 at proc_macro_derive(…) 823 at proc_macro_derive(…)
552 at recursion_limit = …
553 at repr(…) 824 at repr(…)
554 at should_panic 825 at should_panic
555 at target_feature = "…" 826 at target_feature = "…"
556 at test 827 at test
557 at track_caller 828 at track_caller
558 at type_length_limit = …
559 at used 829 at used
560 at warn(…) 830 at warn(…)
561 at windows_subsystem = "…"
562 "#]], 831 "#]],
563 ); 832 );
564 } 833 }
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/dot.rs b/crates/ide_completion/src/completions/dot.rs
index fd9738743..302c9ccbd 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -1,6 +1,7 @@
1//! Completes references after dot (fields and method calls). 1//! Completes references after dot (fields and method calls).
2 2
3use hir::{HasVisibility, Type}; 3use either::Either;
4use hir::{HasVisibility, ScopeDef};
4use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
5 6
6use crate::{context::CompletionContext, Completions}; 7use crate::{context::CompletionContext, Completions};
@@ -9,7 +10,7 @@ use crate::{context::CompletionContext, Completions};
9pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
10 let dot_receiver = match &ctx.dot_receiver { 11 let dot_receiver = match &ctx.dot_receiver {
11 Some(expr) => expr, 12 Some(expr) => expr,
12 _ => return, 13 _ => return complete_undotted_self(acc, ctx),
13 }; 14 };
14 15
15 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 16 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
@@ -20,12 +21,43 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
20 if ctx.is_call { 21 if ctx.is_call {
21 cov_mark::hit!(test_no_struct_field_completion_for_method_call); 22 cov_mark::hit!(test_no_struct_field_completion_for_method_call);
22 } else { 23 } else {
23 complete_fields(acc, ctx, &receiver_ty); 24 complete_fields(ctx, &receiver_ty, |field, ty| match field {
25 Either::Left(field) => acc.add_field(ctx, None, field, &ty),
26 Either::Right(tuple_idx) => acc.add_tuple_field(ctx, None, tuple_idx, &ty),
27 });
24 } 28 }
25 complete_methods(acc, ctx, &receiver_ty); 29 complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None));
30}
31
32fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
33 if !ctx.is_trivial_path || !ctx.config.enable_self_on_the_fly {
34 return;
35 }
36 ctx.scope.process_all_names(&mut |name, def| {
37 if let ScopeDef::Local(local) = &def {
38 if local.is_self(ctx.db) {
39 let ty = local.ty(ctx.db);
40 complete_fields(ctx, &ty, |field, ty| match field {
41 either::Either::Left(field) => {
42 acc.add_field(ctx, Some(name.clone()), field, &ty)
43 }
44 either::Either::Right(tuple_idx) => {
45 acc.add_tuple_field(ctx, Some(name.clone()), tuple_idx, &ty)
46 }
47 });
48 complete_methods(ctx, &ty, |func| {
49 acc.add_method(ctx, func, Some(name.clone()), None)
50 });
51 }
52 }
53 });
26} 54}
27 55
28fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 56fn complete_fields(
57 ctx: &CompletionContext,
58 receiver: &hir::Type,
59 mut f: impl FnMut(Either<hir::Field, usize>, hir::Type),
60) {
29 for receiver in receiver.autoderef(ctx.db) { 61 for receiver in receiver.autoderef(ctx.db) {
30 for (field, ty) in receiver.fields(ctx.db) { 62 for (field, ty) in receiver.fields(ctx.db) {
31 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { 63 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
@@ -33,16 +65,20 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
33 // field is editable, we should show the completion 65 // field is editable, we should show the completion
34 continue; 66 continue;
35 } 67 }
36 acc.add_field(ctx, field, &ty); 68 f(Either::Left(field), ty);
37 } 69 }
38 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { 70 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
39 // FIXME: Handle visibility 71 // FIXME: Handle visibility
40 acc.add_tuple_field(ctx, i, &ty); 72 f(Either::Right(i), ty);
41 } 73 }
42 } 74 }
43} 75}
44 76
45fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 77fn complete_methods(
78 ctx: &CompletionContext,
79 receiver: &hir::Type,
80 mut f: impl FnMut(hir::Function),
81) {
46 if let Some(krate) = ctx.krate { 82 if let Some(krate) = ctx.krate {
47 let mut seen_methods = FxHashSet::default(); 83 let mut seen_methods = FxHashSet::default();
48 let traits_in_scope = ctx.scope.traits_in_scope(); 84 let traits_in_scope = ctx.scope.traits_in_scope();
@@ -51,7 +87,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
51 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) 87 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
52 && seen_methods.insert(func.name(ctx.db)) 88 && seen_methods.insert(func.name(ctx.db))
53 { 89 {
54 acc.add_method(ctx, func, None); 90 f(func);
55 } 91 }
56 None::<()> 92 None::<()>
57 }); 93 });
@@ -484,4 +520,34 @@ impl S {
484 "#]], 520 "#]],
485 ); 521 );
486 } 522 }
523
524 #[test]
525 fn completes_bare_fields_and_methods_in_methods() {
526 check(
527 r#"
528struct Foo { field: i32 }
529
530impl Foo { fn foo(&self) { $0 } }"#,
531 expect![[r#"
532 lc self &Foo
533 sp Self
534 st Foo
535 fd self.field i32
536 me self.foo() fn(&self)
537 "#]],
538 );
539 check(
540 r#"
541struct Foo(i32);
542
543impl Foo { fn foo(&mut self) { $0 } }"#,
544 expect![[r#"
545 lc self &mut Foo
546 sp Self
547 st Foo
548 fd self.0 i32
549 me self.foo() fn(&mut self)
550 "#]],
551 );
552 }
487} 553}
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/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
index 0ea558489..cb90e8a3e 100644
--- a/crates/ide_completion/src/completions/fn_param.rs
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -128,4 +128,19 @@ fn outer(text: String) {
128 "#]], 128 "#]],
129 ) 129 )
130 } 130 }
131
132 #[test]
133 fn completes_non_ident_pat_param() {
134 check(
135 r#"
136struct Bar { bar: u32 }
137
138fn foo(Bar { bar }: Bar) {}
139fn foo2($0) {}
140"#,
141 expect![[r#"
142 bn Bar { bar }: Bar
143 "#]],
144 )
145 }
131} 146}
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 61b667104..0d035c611 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -2,9 +2,12 @@
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::{
8 patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind,
9 CompletionKind, Completions,
10};
8 11
9pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { 12pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
10 // complete keyword "crate" in use stmt 13 // complete keyword "crate" in use stmt
@@ -44,111 +47,105 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
44 cov_mark::hit!(no_keyword_completion_in_comments); 47 cov_mark::hit!(no_keyword_completion_in_comments);
45 return; 48 return;
46 } 49 }
47 if ctx.record_lit_syntax.is_some() { 50 if matches!(ctx.completion_location, Some(ImmediateLocation::RecordExpr(_))) {
48 cov_mark::hit!(no_keyword_completion_in_record_lit); 51 cov_mark::hit!(no_keyword_completion_in_record_lit);
49 return; 52 return;
50 } 53 }
54 let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet);
51 55
52 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; 56 let expects_assoc_item = ctx.expects_assoc_item();
53 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { 57 let has_block_expr_parent = ctx.has_block_expr_parent();
54 add_keyword(ctx, acc, "where", "where "); 58 let expects_item = ctx.expects_item();
59
60 if ctx.has_impl_or_trait_prev_sibling() {
61 add_keyword("where", "where ");
55 return; 62 return;
56 } 63 }
57 if ctx.unsafe_is_prev { 64 if ctx.previous_token_is(T![unsafe]) {
58 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { 65 if expects_item || expects_assoc_item || has_block_expr_parent {
59 add_keyword(ctx, acc, "fn", "fn $0() {}") 66 add_keyword("fn", "fn $1($2) {\n $0\n}")
60 } 67 }
61 68
62 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { 69 if expects_item || has_block_expr_parent {
63 add_keyword(ctx, acc, "trait", "trait $0 {}"); 70 add_keyword("trait", "trait $1 {\n $0\n}");
64 add_keyword(ctx, acc, "impl", "impl $0 {}"); 71 add_keyword("impl", "impl $1 {\n $0\n}");
65 } 72 }
66 73
67 return; 74 return;
68 } 75 }
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 76
79 if ctx.has_item_list_or_source_file_parent { 77 if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() {
80 add_keyword(ctx, acc, "enum", "enum $0 {}"); 78 add_keyword("pub(crate)", "pub(crate) ");
81 add_keyword(ctx, acc, "struct", "struct $0"); 79 add_keyword("pub", "pub ");
82 add_keyword(ctx, acc, "union", "union $0 {}");
83 } 80 }
84 81
85 if ctx.is_expr { 82 if expects_item || expects_assoc_item || has_block_expr_parent {
86 add_keyword(ctx, acc, "match", "match $0 {}"); 83 add_keyword("unsafe", "unsafe ");
87 add_keyword(ctx, acc, "while", "while $0 {}"); 84 add_keyword("fn", "fn $1($2) {\n $0\n}");
88 add_keyword(ctx, acc, "while let", "while let $1 = $0 {}"); 85 add_keyword("const", "const $0");
89 add_keyword(ctx, acc, "loop", "loop {$0}"); 86 add_keyword("type", "type $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 } 87 }
94 88
95 if ctx.if_is_prev || ctx.block_expr_parent { 89 if expects_item || has_block_expr_parent {
96 add_keyword(ctx, acc, "let", "let "); 90 add_keyword("use", "use $0");
91 add_keyword("impl", "impl $1 {\n $0\n}");
92 add_keyword("trait", "trait $1 {\n $0\n}");
93 add_keyword("static", "static $0");
94 add_keyword("extern", "extern $0");
95 add_keyword("mod", "mod $0");
97 } 96 }
98 97
99 if ctx.after_if { 98 if expects_item {
100 add_keyword(ctx, acc, "else", "else {$0}"); 99 add_keyword("enum", "enum $1 {\n $0\n}");
101 add_keyword(ctx, acc, "else if", "else if $0 {}"); 100 add_keyword("struct", "struct $0");
102 } 101 add_keyword("union", "union $1 {\n $0\n}");
103 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
104 add_keyword(ctx, acc, "mod", "mod $0");
105 } 102 }
106 if ctx.bind_pat_parent || ctx.ref_pat_parent { 103
107 add_keyword(ctx, acc, "mut", "mut "); 104 if ctx.expects_expression() {
105 if !has_block_expr_parent {
106 add_keyword("unsafe", "unsafe {\n $0\n}");
107 }
108 add_keyword("match", "match $1 {\n $0\n}");
109 add_keyword("while", "while $1 {\n $0\n}");
110 add_keyword("while let", "while let $1 = $2 {\n $0\n}");
111 add_keyword("loop", "loop {\n $0\n}");
112 add_keyword("if", "if $1 {\n $0\n}");
113 add_keyword("if let", "if let $1 = $2 {\n $0\n}");
114 add_keyword("for", "for $1 in $2 {\n $0\n}");
108 } 115 }
109 if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent 116
110 { 117 if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent {
111 add_keyword(ctx, acc, "const", "const "); 118 add_keyword("let", "let ");
112 add_keyword(ctx, acc, "type", "type ");
113 } 119 }
114 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { 120
115 add_keyword(ctx, acc, "static", "static "); 121 if ctx.after_if() {
116 }; 122 add_keyword("else", "else {\n $0\n}");
117 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { 123 add_keyword("else if", "else if $1 {\n $0\n}");
118 add_keyword(ctx, acc, "extern", "extern ");
119 } 124 }
120 if ctx.has_item_list_or_source_file_parent 125
121 || has_trait_or_impl_parent 126 if ctx.expects_ident_pat_or_ref_expr() {
122 || ctx.block_expr_parent 127 add_keyword("mut", "mut ");
123 || ctx.is_match_arm
124 {
125 add_keyword(ctx, acc, "unsafe", "unsafe ");
126 } 128 }
129
127 if ctx.in_loop_body { 130 if ctx.in_loop_body {
128 if ctx.can_be_stmt { 131 if ctx.can_be_stmt {
129 add_keyword(ctx, acc, "continue", "continue;"); 132 add_keyword("continue", "continue;");
130 add_keyword(ctx, acc, "break", "break;"); 133 add_keyword("break", "break;");
131 } else { 134 } else {
132 add_keyword(ctx, acc, "continue", "continue"); 135 add_keyword("continue", "continue");
133 add_keyword(ctx, acc, "break", "break"); 136 add_keyword("break", "break");
134 } 137 }
135 } 138 }
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 139
141 if !ctx.is_trivial_path { 140 if !ctx.is_trivial_path {
142 return; 141 return;
143 } 142 }
144 let fn_def = match &ctx.function_syntax { 143 let fn_def = match &ctx.function_def {
145 Some(it) => it, 144 Some(it) => it,
146 None => return, 145 None => return,
147 }; 146 };
148 147
149 add_keyword( 148 add_keyword(
150 ctx,
151 acc,
152 "return", 149 "return",
153 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { 150 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
154 (true, true) => "return $0;", 151 (true, true) => "return $0;",
@@ -165,15 +162,12 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
165 162
166 match ctx.config.snippet_cap { 163 match ctx.config.snippet_cap {
167 Some(cap) => { 164 Some(cap) => {
168 let tmp; 165 if snippet.ends_with('}') && ctx.incomplete_let {
169 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
170 cov_mark::hit!(let_semi); 166 cov_mark::hit!(let_semi);
171 tmp = format!("{};", snippet); 167 item.insert_snippet(cap, format!("{};", snippet));
172 &tmp
173 } else { 168 } else {
174 snippet 169 item.insert_snippet(cap, snippet);
175 }; 170 }
176 item.insert_snippet(cap, snippet);
177 } 171 }
178 None => { 172 None => {
179 item.insert_text(if snippet.contains('$') { kw } else { snippet }); 173 item.insert_text(if snippet.contains('$') { kw } else { snippet });
@@ -236,21 +230,21 @@ mod tests {
236 check( 230 check(
237 r"m$0", 231 r"m$0",
238 expect![[r#" 232 expect![[r#"
233 kw pub(crate)
234 kw pub
235 kw unsafe
239 kw fn 236 kw fn
237 kw const
238 kw type
240 kw use 239 kw use
241 kw impl 240 kw impl
242 kw trait 241 kw trait
242 kw static
243 kw extern
244 kw mod
243 kw enum 245 kw enum
244 kw struct 246 kw struct
245 kw union 247 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 "#]], 248 "#]],
255 ); 249 );
256 } 250 }
@@ -260,10 +254,16 @@ mod tests {
260 check( 254 check(
261 r"fn quux() { $0 }", 255 r"fn quux() { $0 }",
262 expect![[r#" 256 expect![[r#"
257 kw unsafe
263 kw fn 258 kw fn
259 kw const
260 kw type
264 kw use 261 kw use
265 kw impl 262 kw impl
266 kw trait 263 kw trait
264 kw static
265 kw extern
266 kw mod
267 kw match 267 kw match
268 kw while 268 kw while
269 kw while let 269 kw while let
@@ -272,12 +272,6 @@ mod tests {
272 kw if let 272 kw if let
273 kw for 273 kw for
274 kw let 274 kw let
275 kw mod
276 kw const
277 kw type
278 kw static
279 kw extern
280 kw unsafe
281 kw return 275 kw return
282 "#]], 276 "#]],
283 ); 277 );
@@ -288,10 +282,16 @@ mod tests {
288 check( 282 check(
289 r"fn quux() { if true { $0 } }", 283 r"fn quux() { if true { $0 } }",
290 expect![[r#" 284 expect![[r#"
285 kw unsafe
291 kw fn 286 kw fn
287 kw const
288 kw type
292 kw use 289 kw use
293 kw impl 290 kw impl
294 kw trait 291 kw trait
292 kw static
293 kw extern
294 kw mod
295 kw match 295 kw match
296 kw while 296 kw while
297 kw while let 297 kw while let
@@ -300,12 +300,6 @@ mod tests {
300 kw if let 300 kw if let
301 kw for 301 kw for
302 kw let 302 kw let
303 kw mod
304 kw const
305 kw type
306 kw static
307 kw extern
308 kw unsafe
309 kw return 303 kw return
310 "#]], 304 "#]],
311 ); 305 );
@@ -316,10 +310,16 @@ mod tests {
316 check( 310 check(
317 r#"fn quux() { if true { () } $0 }"#, 311 r#"fn quux() { if true { () } $0 }"#,
318 expect![[r#" 312 expect![[r#"
313 kw unsafe
319 kw fn 314 kw fn
315 kw const
316 kw type
320 kw use 317 kw use
321 kw impl 318 kw impl
322 kw trait 319 kw trait
320 kw static
321 kw extern
322 kw mod
323 kw match 323 kw match
324 kw while 324 kw while
325 kw while let 325 kw while let
@@ -330,19 +330,15 @@ mod tests {
330 kw let 330 kw let
331 kw else 331 kw else
332 kw else if 332 kw else if
333 kw mod
334 kw const
335 kw type
336 kw static
337 kw extern
338 kw unsafe
339 kw return 333 kw return
340 "#]], 334 "#]],
341 ); 335 );
342 check_edit( 336 check_edit(
343 "else", 337 "else",
344 r#"fn quux() { if true { () } $0 }"#, 338 r#"fn quux() { if true { () } $0 }"#,
345 r#"fn quux() { if true { () } else {$0} }"#, 339 r#"fn quux() { if true { () } else {
340 $0
341} }"#,
346 ); 342 );
347 } 343 }
348 344
@@ -355,6 +351,7 @@ fn quux() -> i32 {
355} 351}
356"#, 352"#,
357 expect![[r#" 353 expect![[r#"
354 kw unsafe
358 kw match 355 kw match
359 kw while 356 kw while
360 kw while let 357 kw while let
@@ -362,7 +359,6 @@ fn quux() -> i32 {
362 kw if 359 kw if
363 kw if let 360 kw if let
364 kw for 361 kw for
365 kw unsafe
366 kw return 362 kw return
367 "#]], 363 "#]],
368 ); 364 );
@@ -373,10 +369,10 @@ fn quux() -> i32 {
373 check( 369 check(
374 r"trait My { $0 }", 370 r"trait My { $0 }",
375 expect![[r#" 371 expect![[r#"
372 kw unsafe
376 kw fn 373 kw fn
377 kw const 374 kw const
378 kw type 375 kw type
379 kw unsafe
380 "#]], 376 "#]],
381 ); 377 );
382 } 378 }
@@ -386,12 +382,27 @@ fn quux() -> i32 {
386 check( 382 check(
387 r"impl My { $0 }", 383 r"impl My { $0 }",
388 expect![[r#" 384 expect![[r#"
385 kw pub(crate)
386 kw pub
387 kw unsafe
389 kw fn 388 kw fn
390 kw const 389 kw const
391 kw type 390 kw type
392 kw unsafe 391 "#]],
392 );
393 }
394
395 #[test]
396 fn test_keywords_in_impl_def_with_attr() {
397 check(
398 r"impl My { #[foo] $0 }",
399 expect![[r#"
393 kw pub(crate) 400 kw pub(crate)
394 kw pub 401 kw pub
402 kw unsafe
403 kw fn
404 kw const
405 kw type
395 "#]], 406 "#]],
396 ); 407 );
397 } 408 }
@@ -401,10 +412,16 @@ fn quux() -> i32 {
401 check( 412 check(
402 r"fn my() { loop { $0 } }", 413 r"fn my() { loop { $0 } }",
403 expect![[r#" 414 expect![[r#"
415 kw unsafe
404 kw fn 416 kw fn
417 kw const
418 kw type
405 kw use 419 kw use
406 kw impl 420 kw impl
407 kw trait 421 kw trait
422 kw static
423 kw extern
424 kw mod
408 kw match 425 kw match
409 kw while 426 kw while
410 kw while let 427 kw while let
@@ -413,12 +430,6 @@ fn quux() -> i32 {
413 kw if let 430 kw if let
414 kw for 431 kw for
415 kw let 432 kw let
416 kw mod
417 kw const
418 kw type
419 kw static
420 kw extern
421 kw unsafe
422 kw continue 433 kw continue
423 kw break 434 kw break
424 kw return 435 kw return
@@ -565,6 +576,7 @@ pub mod future {
565 check( 576 check(
566 r#"fn main() { let _ = $0 }"#, 577 r#"fn main() { let _ = $0 }"#,
567 expect![[r#" 578 expect![[r#"
579 kw unsafe
568 kw match 580 kw match
569 kw while 581 kw while
570 kw while let 582 kw while let
@@ -625,6 +637,7 @@ fn foo() {
625} 637}
626"#, 638"#,
627 expect![[r#" 639 expect![[r#"
640 kw unsafe
628 kw match 641 kw match
629 kw while 642 kw while
630 kw while let 643 kw while let
@@ -646,7 +659,9 @@ fn foo() {
646fn main() { let x = $0 } 659fn main() { let x = $0 }
647"#, 660"#,
648 r#" 661 r#"
649fn main() { let x = match $0 {}; } 662fn main() { let x = match $1 {
663 $0
664}; }
650"#, 665"#,
651 ); 666 );
652 667
@@ -660,7 +675,9 @@ fn main() {
660"#, 675"#,
661 r#" 676 r#"
662fn main() { 677fn main() {
663 let x = if $0 {}; 678 let x = if $1 {
679 $0
680};
664 let y = 92; 681 let y = 92;
665} 682}
666"#, 683"#,
@@ -676,7 +693,9 @@ fn main() {
676"#, 693"#,
677 r#" 694 r#"
678fn main() { 695fn main() {
679 let x = loop {$0}; 696 let x = loop {
697 $0
698};
680 bar(); 699 bar();
681} 700}
682"#, 701"#,
diff --git a/crates/ide_completion/src/completions/lifetime.rs b/crates/ide_completion/src/completions/lifetime.rs
index 5eeddf7a4..8ccccb646 100644
--- a/crates/ide_completion/src/completions/lifetime.rs
+++ b/crates/ide_completion/src/completions/lifetime.rs
@@ -8,19 +8,23 @@ 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 if param_lifetime != Some(&*name.to_string()) {
23 acc.add_resolution(ctx, name.to_string(), &res); 27 acc.add_resolution(ctx, name, &res);
24 } 28 }
25 } 29 }
26 }); 30 });
@@ -36,7 +40,7 @@ pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
36 } 40 }
37 ctx.scope.process_all_names(&mut |name, res| { 41 ctx.scope.process_all_names(&mut |name, res| {
38 if let ScopeDef::Label(_) = res { 42 if let ScopeDef::Label(_) = res {
39 acc.add_resolution(ctx, name.to_string(), &res); 43 acc.add_resolution(ctx, name, &res);
40 } 44 }
41 }); 45 });
42} 46}
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..202e71215 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.clone()), 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, &res);
19 }
20 })
14} 21}
15 22
16#[cfg(test)] 23#[cfg(test)]
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs
index 4f9415736..6a5746fb9 100644
--- a/crates/ide_completion/src/completions/mod_.rs
+++ b/crates/ide_completion/src/completions/mod_.rs
@@ -9,14 +9,14 @@ use ide_db::{
9}; 9};
10use rustc_hash::FxHashSet; 10use rustc_hash::FxHashSet;
11 11
12use crate::CompletionItem; 12use crate::{patterns::ImmediateLocation, CompletionItem};
13 13
14use crate::{context::CompletionContext, item::CompletionKind, Completions}; 14use crate::{context::CompletionContext, item::CompletionKind, Completions};
15 15
16/// Complete mod declaration, i.e. `mod $0 ;` 16/// Complete mod declaration, i.e. `mod $0 ;`
17pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 17pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
18 let mod_under_caret = match &ctx.mod_declaration_under_caret { 18 let mod_under_caret = match &ctx.completion_location {
19 Some(mod_under_caret) if mod_under_caret.item_list().is_none() => mod_under_caret, 19 Some(ImmediateLocation::ModDeclaration(mod_under_caret)) => mod_under_caret,
20 _ => return None, 20 _ => return None,
21 }; 21 };
22 22
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 8dc9ab73c..8a728c67e 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,13 +45,13 @@ 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,
54 }; 52 };
55 if add_resolution { 53 if add_resolution {
56 acc.add_resolution(ctx, name.to_string(), &res); 54 acc.add_resolution(ctx, name, &res);
57 } 55 }
58 }); 56 });
59} 57}
@@ -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..de58ce1cd 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -1,27 +1,39 @@
1//! Completion of paths, i.e. `some::prefix::$0`. 1//! Completion of paths, i.e. `some::prefix::$0`.
2 2
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; 3use hir::HasVisibility;
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use syntax::AstNode; 5use syntax::AstNode;
6 6
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 hir::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 hir::ScopeDef::MacroDef(macro_def) = def {
28 acc.add_macro(ctx, Some(name.clone()), macro_def);
29 }
30 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
31 acc.add_resolution(ctx, name, &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| {
@@ -30,11 +42,11 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
30 }); 42 });
31 43
32 match resolution { 44 match resolution {
33 PathResolution::Def(hir::ModuleDef::Module(module)) => { 45 hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
34 let module_scope = module.scope(ctx.db, context_module); 46 let module_scope = module.scope(ctx.db, context_module);
35 for (name, def) in module_scope { 47 for (name, def) in module_scope {
36 if ctx.use_item_syntax.is_some() { 48 if ctx.use_item_syntax.is_some() {
37 if let ScopeDef::Unknown = def { 49 if let hir::ScopeDef::Unknown = def {
38 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { 50 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
39 if name_ref.syntax().text() == name.to_string().as_str() { 51 if name_ref.syntax().text() == name.to_string().as_str() {
40 // for `use self::foo$0`, don't suggest `foo` as a completion 52 // for `use self::foo$0`, don't suggest `foo` as a completion
@@ -45,20 +57,20 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
45 } 57 }
46 } 58 }
47 59
48 acc.add_resolution(ctx, name.to_string(), &def); 60 acc.add_resolution(ctx, name, &def);
49 } 61 }
50 } 62 }
51 PathResolution::Def(def @ hir::ModuleDef::Adt(_)) 63 hir::PathResolution::Def(def @ hir::ModuleDef::Adt(_))
52 | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) 64 | hir::PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_))
53 | PathResolution::Def(def @ hir::ModuleDef::BuiltinType(_)) => { 65 | hir::PathResolution::Def(def @ hir::ModuleDef::BuiltinType(_)) => {
54 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { 66 if let hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def {
55 add_enum_variants(ctx, acc, e); 67 add_enum_variants(ctx, acc, e);
56 } 68 }
57 let ty = match def { 69 let ty = match def {
58 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), 70 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
59 hir::ModuleDef::TypeAlias(a) => { 71 hir::ModuleDef::TypeAlias(a) => {
60 let ty = a.ty(ctx.db); 72 let ty = a.ty(ctx.db);
61 if let Some(Adt::Enum(e)) = ty.as_adt() { 73 if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
62 cov_mark::hit!(completes_variant_through_alias); 74 cov_mark::hit!(completes_variant_through_alias);
63 add_enum_variants(ctx, acc, e); 75 add_enum_variants(ctx, acc, e);
64 } 76 }
@@ -105,7 +117,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
105 }); 117 });
106 } 118 }
107 } 119 }
108 PathResolution::Def(hir::ModuleDef::Trait(t)) => { 120 hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
109 // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`. 121 // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
110 for item in t.items(ctx.db) { 122 for item in t.items(ctx.db) {
111 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 123 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
@@ -118,15 +130,15 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
118 } 130 }
119 } 131 }
120 } 132 }
121 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => { 133 hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
122 if let Some(krate) = ctx.krate { 134 if let Some(krate) = ctx.krate {
123 let ty = match resolution { 135 let ty = match resolution {
124 PathResolution::TypeParam(param) => param.ty(ctx.db), 136 hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
125 PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), 137 hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
126 _ => return, 138 _ => return,
127 }; 139 };
128 140
129 if let Some(Adt::Enum(e)) = ty.as_adt() { 141 if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
130 add_enum_variants(ctx, acc, e); 142 add_enum_variants(ctx, acc, e);
131 } 143 }
132 144
@@ -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..0ac47cdbe 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -2,41 +2,44 @@
2use ide_db::{helpers::FamousDefs, SymbolKind}; 2use ide_db::{helpers::FamousDefs, SymbolKind};
3use syntax::ast::Expr; 3use syntax::ast::Expr;
4 4
5use crate::{item::CompletionKind, CompletionContext, CompletionItem, Completions}; 5use crate::{
6 item::CompletionKind, patterns::ImmediateLocation, CompletionContext, CompletionItem,
7 Completions,
8};
6 9
7pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 10pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
8 let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { 11 let missing_fields = match &ctx.completion_location {
9 (None, None) => return None, 12 Some(ImmediateLocation::RecordExpr(record_expr)) => {
10 (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), 13 let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
11 (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
12 (_, Some(record_lit)) => {
13 let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone()));
14 let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default(); 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_expr);
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 }
33 32
34 missing_fields 33 missing_fields
35 } 34 }
35 Some(ImmediateLocation::RecordPat(record_pat)) => {
36 ctx.sema.record_pattern_missing_fields(record_pat)
37 }
38 _ => return None,
36 }; 39 };
37 40
38 for (field, ty) in missing_fields { 41 for (field, ty) in missing_fields {
39 acc.add_field(ctx, field, &ty); 42 acc.add_field(ctx, None, field, &ty);
40 } 43 }
41 44
42 Some(()) 45 Some(())
@@ -108,8 +111,6 @@ fn process(f: S) {
108 check_snippet( 111 check_snippet(
109 test_code, 112 test_code,
110 expect![[r#" 113 expect![[r#"
111 sn pd
112 sn ppd
113 fd ..Default::default() 114 fd ..Default::default()
114 "#]], 115 "#]],
115 ); 116 );
@@ -179,13 +180,7 @@ fn process(f: S) {
179 "#]], 180 "#]],
180 ); 181 );
181 182
182 check_snippet( 183 check_snippet(test_code, expect![[r#""#]]);
183 test_code,
184 expect![[r#"
185 sn pd
186 sn ppd
187 "#]],
188 );
189 } 184 }
190 185
191 #[test] 186 #[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..20188a7dd 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,11 +8,29 @@ 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
16 { 15 if ctx.expects_assoc_item() {
16 ctx.scope.process_all_names(&mut |name, def| {
17 if let ScopeDef::MacroDef(macro_def) = def {
18 acc.add_macro(ctx, Some(name.clone()), macro_def);
19 }
20 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
21 acc.add_resolution(ctx, name, &def);
22 }
23 });
24 return;
25 }
26
27 if ctx.expects_use_tree() {
28 cov_mark::hit!(only_completes_modules_in_import);
29 ctx.scope.process_all_names(&mut |name, res| {
30 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
31 acc.add_resolution(ctx, name, &res);
32 }
33 });
17 return; 34 return;
18 } 35 }
19 36
@@ -30,15 +47,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
30 cov_mark::hit!(skip_lifetime_completion); 47 cov_mark::hit!(skip_lifetime_completion);
31 return; 48 return;
32 } 49 }
33 if ctx.use_item_syntax.is_some() { 50 acc.add_resolution(ctx, name, &res);
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);
42 }); 51 });
43} 52}
44 53
@@ -61,15 +70,17 @@ mod tests {
61 } 70 }
62 71
63 #[test] 72 #[test]
64 fn self_fulfilling_completion() { 73 fn only_completes_modules_in_import() {
65 cov_mark::check!(self_fulfilling_completion); 74 cov_mark::check!(only_completes_modules_in_import);
66 check( 75 check(
67 r#" 76 r#"
68use foo$0 77use f$0
69use std::collections; 78
79struct Foo;
80mod foo {}
70"#, 81"#,
71 expect![[r#" 82 expect![[r#"
72 ?? collections 83 md foo
73 "#]], 84 "#]],
74 ); 85 );
75 } 86 }
@@ -86,7 +97,7 @@ fn quux(x: Option<Enum>) {
86 } 97 }
87} 98}
88"#, 99"#,
89 expect![[""]], 100 expect![[r#""#]],
90 ); 101 );
91 } 102 }
92 103
@@ -102,7 +113,7 @@ fn quux(x: Option<Enum>) {
102 } 113 }
103} 114}
104"#, 115"#,
105 expect![[""]], 116 expect![[r#""#]],
106 ); 117 );
107 } 118 }
108 119
@@ -651,7 +662,7 @@ fn f() {}
651 } 662 }
652 663
653 #[test] 664 #[test]
654 fn completes_type_or_trait_in_impl_block() { 665 fn completes_target_type_or_trait_in_impl_block() {
655 check( 666 check(
656 r#" 667 r#"
657trait MyTrait {} 668trait MyTrait {}
@@ -666,4 +677,41 @@ impl My$0
666 "#]], 677 "#]],
667 ) 678 )
668 } 679 }
680
681 #[test]
682 fn completes_in_assoc_item_list() {
683 check(
684 r#"
685macro_rules! foo {}
686mod bar {}
687
688struct MyStruct {}
689impl MyStruct {
690 $0
691}
692"#,
693 expect![[r#"
694 md bar
695 ma foo! macro_rules! foo
696 "#]],
697 )
698 }
699
700 // FIXME: The completions here currently come from `macro_in_item_position`, but they shouldn't
701 #[test]
702 fn completes_in_item_list() {
703 check(
704 r#"
705struct MyStruct {}
706macro_rules! foo {}
707mod bar {}
708
709$0
710"#,
711 expect![[r#"
712 md bar
713 ma foo!(…) macro_rules! foo
714 "#]],
715 )
716 }
669} 717}
diff --git a/crates/ide_completion/src/config.rs b/crates/ide_completion/src/config.rs
index d70ed6c1c..c300ce887 100644
--- a/crates/ide_completion/src/config.rs
+++ b/crates/ide_completion/src/config.rs
@@ -10,6 +10,7 @@ use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
10pub struct CompletionConfig { 10pub struct CompletionConfig {
11 pub enable_postfix_completions: bool, 11 pub enable_postfix_completions: bool,
12 pub enable_imports_on_the_fly: bool, 12 pub enable_imports_on_the_fly: bool,
13 pub enable_self_on_the_fly: bool,
13 pub add_call_parenthesis: bool, 14 pub add_call_parenthesis: bool,
14 pub add_call_argument_snippets: bool, 15 pub add_call_argument_snippets: bool,
15 pub snippet_cap: Option<SnippetCap>, 16 pub snippet_cap: Option<SnippetCap>,
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 787eb2fd3..7c46c815d 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, 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,34 @@ 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>,
48 pub(super) record_lit_syntax: Option<ast::RecordExpr>, 51
49 pub(super) record_pat_syntax: Option<ast::RecordPat>, 52 /// The parent function of the cursor position if it exists.
50 pub(super) record_field_syntax: Option<ast::RecordExprField>, 53 pub(super) function_def: Option<ast::Fn>,
54 /// The parent impl of the cursor position if it exists.
51 pub(super) impl_def: Option<ast::Impl>, 55 pub(super) impl_def: Option<ast::Impl>,
56
57 // potentially set if we are completing a lifetime
58 pub(super) lifetime_syntax: Option<ast::Lifetime>,
59 pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
52 pub(super) lifetime_allowed: bool, 60 pub(super) lifetime_allowed: bool,
61 pub(super) is_label_ref: bool,
62
63 // potentially set if we are completing a name
64 pub(super) is_pat_or_const: Option<PatternRefutability>,
65 pub(super) is_param: bool,
66
67 pub(super) completion_location: Option<ImmediateLocation>,
68 pub(super) prev_sibling: Option<ImmediatePrevSibling>,
69 pub(super) attribute_under_caret: Option<ast::Attr>,
70
53 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 71 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
54 pub(super) active_parameter: Option<ActiveParameter>, 72 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. 73 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
62 pub(super) is_trivial_path: bool, 74 pub(super) is_trivial_path: bool,
63 /// If not a trivial path, the prefix (qualifier). 75 /// If not a trivial path, the prefix (qualifier).
64 pub(super) path_qual: Option<ast::Path>, 76 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. 77 /// `true` if we are a statement or a last expr in the block.
67 pub(super) can_be_stmt: bool, 78 pub(super) can_be_stmt: bool,
68 /// `true` if we expect an expression at the cursor position. 79 /// `true` if we expect an expression at the cursor position.
@@ -80,26 +91,13 @@ pub(crate) struct CompletionContext<'a> {
80 pub(super) is_macro_call: bool, 91 pub(super) is_macro_call: bool,
81 pub(super) is_path_type: bool, 92 pub(super) is_path_type: bool,
82 pub(super) has_type_args: bool, 93 pub(super) has_type_args: bool,
83 pub(super) attribute_under_caret: Option<ast::Attr>, 94 pub(super) locals: Vec<(String, Local)>,
84 pub(super) mod_declaration_under_caret: Option<ast::Module>, 95
85 pub(super) unsafe_is_prev: bool, 96 pub(super) previous_token: Option<SyntaxToken>,
86 pub(super) if_is_prev: bool,
87 pub(super) block_expr_parent: bool,
88 pub(super) bind_pat_parent: bool,
89 pub(super) ref_pat_parent: bool,
90 pub(super) in_loop_body: bool, 97 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,
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, 98 pub(super) incomplete_let: bool,
102 pub(super) locals: Vec<(String, Local)>, 99
100 no_completion_required: bool,
103} 101}
104 102
105impl<'a> CompletionContext<'a> { 103impl<'a> CompletionContext<'a> {
@@ -149,20 +147,15 @@ impl<'a> CompletionContext<'a> {
149 name_ref_syntax: None, 147 name_ref_syntax: None,
150 lifetime_syntax: None, 148 lifetime_syntax: None,
151 lifetime_param_syntax: None, 149 lifetime_param_syntax: None,
152 function_syntax: None, 150 function_def: None,
153 use_item_syntax: None, 151 use_item_syntax: None,
154 record_lit_syntax: None,
155 record_pat_syntax: None,
156 record_field_syntax: None,
157 impl_def: None, 152 impl_def: None,
158 active_parameter: ActiveParameter::at(db, position), 153 active_parameter: ActiveParameter::at(db, position),
159 is_label_ref: false, 154 is_label_ref: false,
160 is_param: false, 155 is_param: false,
161 is_pat_binding_or_const: false, 156 is_pat_or_const: None,
162 is_irrefutable_pat_binding: false,
163 is_trivial_path: false, 157 is_trivial_path: false,
164 path_qual: None, 158 path_qual: None,
165 after_if: false,
166 can_be_stmt: false, 159 can_be_stmt: false,
167 is_expr: false, 160 is_expr: false,
168 is_new_item: false, 161 is_new_item: false,
@@ -173,69 +166,56 @@ impl<'a> CompletionContext<'a> {
173 is_macro_call: false, 166 is_macro_call: false,
174 is_path_type: false, 167 is_path_type: false,
175 has_type_args: false, 168 has_type_args: false,
176 attribute_under_caret: None, 169 previous_token: None,
177 mod_declaration_under_caret: None,
178 unsafe_is_prev: false,
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, 170 in_loop_body: false,
184 has_trait_parent: false, 171 completion_location: None,
185 has_impl_parent: false, 172 prev_sibling: None,
186 inside_impl_trait_block: false, 173 no_completion_required: false,
187 has_field_list_parent: false,
188 trait_as_prev_sibling: false,
189 impl_as_prev_sibling: false,
190 is_match_arm: false,
191 has_item_list_or_source_file_parent: false,
192 for_is_prev2: false,
193 fn_is_prev: false,
194 incomplete_let: false, 174 incomplete_let: false,
175 attribute_under_caret: None,
195 locals, 176 locals,
196 }; 177 };
197 178
198 let mut original_file = original_file.syntax().clone(); 179 let mut original_file = original_file.syntax().clone();
199 let mut hypothetical_file = file_with_fake_ident.syntax().clone(); 180 let mut speculative_file = file_with_fake_ident.syntax().clone();
200 let mut offset = position.offset; 181 let mut offset = position.offset;
201 let mut fake_ident_token = fake_ident_token; 182 let mut fake_ident_token = fake_ident_token;
202 183
203 // Are we inside a macro call? 184 // Are we inside a macro call?
204 while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( 185 while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
205 find_node_at_offset::<ast::MacroCall>(&original_file, offset), 186 find_node_at_offset::<ast::MacroCall>(&original_file, offset),
206 find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset), 187 find_node_at_offset::<ast::MacroCall>(&speculative_file, offset),
207 ) { 188 ) {
208 if actual_macro_call.path().as_ref().map(|s| s.syntax().text()) 189 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()) 190 != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text())
210 { 191 {
211 break; 192 break;
212 } 193 }
213 let hypothetical_args = match macro_call_with_fake_ident.token_tree() { 194 let speculative_args = match macro_call_with_fake_ident.token_tree() {
214 Some(tt) => tt, 195 Some(tt) => tt,
215 None => break, 196 None => break,
216 }; 197 };
217 if let (Some(actual_expansion), Some(hypothetical_expansion)) = ( 198 if let (Some(actual_expansion), Some(speculative_expansion)) = (
218 ctx.sema.expand(&actual_macro_call), 199 ctx.sema.expand(&actual_macro_call),
219 ctx.sema.speculative_expand( 200 ctx.sema.speculative_expand(
220 &actual_macro_call, 201 &actual_macro_call,
221 &hypothetical_args, 202 &speculative_args,
222 fake_ident_token, 203 fake_ident_token,
223 ), 204 ),
224 ) { 205 ) {
225 let new_offset = hypothetical_expansion.1.text_range().start(); 206 let new_offset = speculative_expansion.1.text_range().start();
226 if new_offset > actual_expansion.text_range().end() { 207 if new_offset > actual_expansion.text_range().end() {
227 break; 208 break;
228 } 209 }
229 original_file = actual_expansion; 210 original_file = actual_expansion;
230 hypothetical_file = hypothetical_expansion.0; 211 speculative_file = speculative_expansion.0;
231 fake_ident_token = hypothetical_expansion.1; 212 fake_ident_token = speculative_expansion.1;
232 offset = new_offset; 213 offset = new_offset;
233 } else { 214 } else {
234 break; 215 break;
235 } 216 }
236 } 217 }
237 ctx.fill_keyword_patterns(&hypothetical_file, offset); 218 ctx.fill(&original_file, speculative_file, offset);
238 ctx.fill(&original_file, hypothetical_file, offset);
239 Some(ctx) 219 Some(ctx)
240 } 220 }
241 221
@@ -245,7 +225,7 @@ impl<'a> CompletionContext<'a> {
245 /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names. 225 /// 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. 226 /// - `for _ i$0` -- obviously, it'll be "in" keyword.
247 pub(crate) fn no_completion_required(&self) -> bool { 227 pub(crate) fn no_completion_required(&self) -> bool {
248 (self.fn_is_prev && !self.inside_impl_trait_block) || self.for_is_prev2 228 self.no_completion_required
249 } 229 }
250 230
251 /// The range of the identifier that is being completed. 231 /// The range of the identifier that is being completed.
@@ -264,33 +244,67 @@ impl<'a> CompletionContext<'a> {
264 } 244 }
265 } 245 }
266 246
267 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 247 pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool {
268 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 248 self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
269 let syntax_element = NodeOrToken::Token(fake_ident_token); 249 }
270 self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); 250
271 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone()); 251 pub(crate) fn expects_assoc_item(&self) -> bool {
272 self.if_is_prev = if_is_prev(syntax_element.clone()); 252 matches!(
273 self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); 253 self.completion_location,
274 self.ref_pat_parent = has_ref_parent(syntax_element.clone()); 254 Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl)
275 self.in_loop_body = is_in_loop_body(syntax_element.clone()); 255 )
276 self.has_trait_parent = has_trait_parent(syntax_element.clone()); 256 }
277 self.has_impl_parent = has_impl_parent(syntax_element.clone()); 257
278 self.inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone()); 258 pub(crate) fn expects_use_tree(&self) -> bool {
279 self.has_field_list_parent = has_field_list_parent(syntax_element.clone()); 259 matches!(self.completion_location, Some(ImmediateLocation::Use))
280 self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); 260 }
281 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); 261
282 self.is_match_arm = is_match_arm(syntax_element.clone()); 262 pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
283 self.has_item_list_or_source_file_parent = 263 matches!(self.completion_location, Some(ImmediateLocation::Impl))
284 has_item_list_or_source_file_parent(syntax_element.clone()); 264 }
285 self.mod_declaration_under_caret = 265
286 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) 266 pub(crate) fn expects_item(&self) -> bool {
287 .filter(|module| module.item_list().is_none()); 267 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
288 self.for_is_prev2 = for_is_prev2(syntax_element.clone()); 268 }
289 self.fn_is_prev = fn_is_prev(syntax_element.clone()); 269
290 self.incomplete_let = 270 pub(crate) fn expects_expression(&self) -> bool {
291 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { 271 self.is_expr
292 it.syntax().text_range().end() == syntax_element.text_range().end() 272 }
293 }); 273
274 pub(crate) fn has_block_expr_parent(&self) -> bool {
275 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
276 }
277
278 pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool {
279 matches!(
280 self.completion_location,
281 Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr)
282 )
283 }
284
285 pub(crate) fn expect_record_field(&self) -> bool {
286 matches!(self.completion_location, Some(ImmediateLocation::RecordField))
287 }
288
289 pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
290 matches!(
291 self.prev_sibling,
292 Some(ImmediatePrevSibling::ImplDefType) | Some(ImmediatePrevSibling::TraitDefName)
293 )
294 }
295
296 pub(crate) fn after_if(&self) -> bool {
297 matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
298 }
299
300 pub(crate) fn is_path_disallowed(&self) -> bool {
301 matches!(
302 self.completion_location,
303 Some(ImmediateLocation::Attribute(_))
304 | Some(ImmediateLocation::ModDeclaration(_))
305 | Some(ImmediateLocation::RecordPat(_))
306 | Some(ImmediateLocation::RecordExpr(_))
307 ) || self.attribute_under_caret.is_some()
294 } 308 }
295 309
296 fn fill_impl_def(&mut self) { 310 fn fill_impl_def(&mut self) {
@@ -337,25 +351,24 @@ impl<'a> CompletionContext<'a> {
337 }, 351 },
338 ast::RecordExprFieldList(_it) => { 352 ast::RecordExprFieldList(_it) => {
339 cov_mark::hit!(expected_type_struct_field_without_leading_char); 353 cov_mark::hit!(expected_type_struct_field_without_leading_char);
340 self.token.prev_sibling_or_token() 354 // wouldn't try {} be nice...
341 .and_then(|se| se.into_node()) 355 (|| {
342 .and_then(|node| ast::RecordExprField::cast(node)) 356 let expr_field = self.token.prev_sibling_or_token()?
343 .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf))) 357 .into_node()
344 .map(|(f, rf)|( 358 .and_then(|node| ast::RecordExprField::cast(node))?;
345 Some(f.0.ty(self.db)), 359 let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?;
346 rf.field_name().map(NameOrNameRef::NameRef), 360 Some((
361 Some(ty),
362 expr_field.field_name().map(NameOrNameRef::NameRef),
347 )) 363 ))
348 .unwrap_or((None, None)) 364 })().unwrap_or((None, None))
349 }, 365 },
350 ast::RecordExprField(it) => { 366 ast::RecordExprField(it) => {
351 cov_mark::hit!(expected_type_struct_field_with_leading_char); 367 cov_mark::hit!(expected_type_struct_field_with_leading_char);
352 self.sema 368 (
353 .resolve_record_field(&it) 369 it.expr().as_ref().and_then(|e| self.sema.type_of_expr(e)),
354 .map(|f|( 370 it.field_name().map(NameOrNameRef::NameRef),
355 Some(f.0.ty(self.db)), 371 )
356 it.field_name().map(NameOrNameRef::NameRef),
357 ))
358 .unwrap_or((None, None))
359 }, 372 },
360 ast::MatchExpr(it) => { 373 ast::MatchExpr(it) => {
361 cov_mark::hit!(expected_type_match_arm_without_leading_char); 374 cov_mark::hit!(expected_type_match_arm_without_leading_char);
@@ -382,6 +395,12 @@ impl<'a> CompletionContext<'a> {
382 let def = self.sema.to_def(&it); 395 let def = self.sema.to_def(&it);
383 (def.map(|def| def.ret_type(self.db)), None) 396 (def.map(|def| def.ret_type(self.db)), None)
384 }, 397 },
398 ast::ClosureExpr(it) => {
399 let ty = self.sema.type_of_expr(&it.into());
400 ty.and_then(|ty| ty.as_callable(self.db))
401 .map(|c| (Some(c.return_type()), None))
402 .unwrap_or((None, None))
403 },
385 ast::Stmt(_it) => (None, None), 404 ast::Stmt(_it) => (None, None),
386 _ => { 405 _ => {
387 match node.parent() { 406 match node.parent() {
@@ -403,71 +422,43 @@ impl<'a> CompletionContext<'a> {
403 file_with_fake_ident: SyntaxNode, 422 file_with_fake_ident: SyntaxNode,
404 offset: TextSize, 423 offset: TextSize,
405 ) { 424 ) {
425 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
426 let syntax_element = NodeOrToken::Token(fake_ident_token);
427 self.previous_token = previous_token(syntax_element.clone());
428 self.attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
429 self.no_completion_required = {
430 let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
431 let fn_is_prev = self.previous_token_is(T![fn]);
432 let for_is_prev2 = for_is_prev2(syntax_element.clone());
433 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2
434 };
435 self.in_loop_body = is_in_loop_body(syntax_element.clone());
436
437 self.incomplete_let =
438 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
439 it.syntax().text_range().end() == syntax_element.text_range().end()
440 });
441
406 let (expected_type, expected_name) = self.expected_type_and_name(); 442 let (expected_type, expected_name) = self.expected_type_and_name();
407 self.expected_type = expected_type; 443 self.expected_type = expected_type;
408 self.expected_name = expected_name; 444 self.expected_name = expected_name;
409 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
410
411 if let Some(lifetime) = find_node_at_offset::<ast::Lifetime>(&file_with_fake_ident, offset)
412 {
413 self.classify_lifetime(original_file, lifetime, offset);
414 }
415
416 // First, let's try to complete a reference to some declaration.
417 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
418 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
419 // See RFC#1685.
420 if is_node::<ast::Param>(name_ref.syntax()) {
421 self.is_param = true;
422 return;
423 }
424 // FIXME: remove this (V) duplication and make the check more precise
425 if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() {
426 self.record_pat_syntax =
427 self.sema.find_node_at_offset_with_macros(&original_file, offset);
428 }
429 self.classify_name_ref(original_file, name_ref, offset);
430 }
431
432 // Otherwise, see if this is a declaration. We can use heuristics to
433 // suggest declaration names, see `CompletionKind::Magic`.
434 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) {
435 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::IdentPat::cast) {
436 self.is_pat_binding_or_const = true;
437 if bind_pat.at_token().is_some()
438 || bind_pat.ref_token().is_some()
439 || bind_pat.mut_token().is_some()
440 {
441 self.is_pat_binding_or_const = false;
442 }
443 if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() {
444 self.is_pat_binding_or_const = false;
445 }
446 if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| {
447 match_ast! {
448 match node {
449 ast::LetStmt(it) => Some(it.pat()),
450 ast::Param(it) => Some(it.pat()),
451 _ => None,
452 }
453 }
454 }) {
455 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
456 self.is_pat_binding_or_const = false;
457 self.is_irrefutable_pat_binding = true;
458 }
459 }
460 445
461 self.fill_impl_def(); 446 let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) {
447 Some(it) => it,
448 None => return,
449 };
450 self.completion_location =
451 determine_location(&self.sema, original_file, offset, &name_like);
452 self.prev_sibling = determine_prev_sibling(&name_like);
453 match name_like {
454 ast::NameLike::Lifetime(lifetime) => {
455 self.classify_lifetime(original_file, lifetime, offset);
462 } 456 }
463 if is_node::<ast::Param>(name.syntax()) { 457 ast::NameLike::NameRef(name_ref) => {
464 self.is_param = true; 458 self.classify_name_ref(original_file, name_ref);
465 return;
466 } 459 }
467 // FIXME: remove this (^) duplication and make the check more precise 460 ast::NameLike::Name(name) => {
468 if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { 461 self.classify_name(name);
469 self.record_pat_syntax =
470 self.sema.find_node_at_offset_with_macros(&original_file, offset);
471 } 462 }
472 } 463 }
473 } 464 }
@@ -501,22 +492,55 @@ impl<'a> CompletionContext<'a> {
501 } 492 }
502 } 493 }
503 494
504 fn classify_name_ref( 495 fn classify_name(&mut self, name: ast::Name) {
505 &mut self, 496 if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
506 original_file: &SyntaxNode, 497 self.is_pat_or_const = Some(PatternRefutability::Refutable);
507 name_ref: ast::NameRef, 498 // if any of these is here our bind pat can't be a const pat anymore
508 offset: TextSize, 499 let complex_ident_pat = bind_pat.at_token().is_some()
509 ) { 500 || bind_pat.ref_token().is_some()
510 self.name_ref_syntax = 501 || bind_pat.mut_token().is_some();
511 find_node_at_offset(original_file, name_ref.syntax().text_range().start()); 502 if complex_ident_pat {
512 let name_range = name_ref.syntax().text_range(); 503 self.is_pat_or_const = None;
513 if ast::RecordExprField::for_field_name(&name_ref).is_some() { 504 } else {
514 self.record_lit_syntax = 505 let irrefutable_pat = bind_pat.syntax().ancestors().find_map(|node| {
515 self.sema.find_node_at_offset_with_macros(original_file, offset); 506 match_ast! {
507 match node {
508 ast::LetStmt(it) => Some(it.pat()),
509 ast::Param(it) => Some(it.pat()),
510 _ => None,
511 }
512 }
513 });
514 if let Some(Some(pat)) = irrefutable_pat {
515 // This check is here since we could be inside a pattern in the initializer expression of the let statement.
516 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
517 self.is_pat_or_const = Some(PatternRefutability::Irrefutable);
518 }
519 }
520
521 let is_name_in_field_pat = bind_pat
522 .syntax()
523 .parent()
524 .and_then(ast::RecordPatField::cast)
525 .map_or(false, |pat_field| pat_field.name_ref().is_none());
526 if is_name_in_field_pat {
527 self.is_pat_or_const = None;
528 }
529 }
530
531 self.fill_impl_def();
516 } 532 }
517 533
534 self.is_param |= is_node::<ast::Param>(name.syntax());
535 }
536
537 fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameRef) {
518 self.fill_impl_def(); 538 self.fill_impl_def();
519 539
540 self.name_ref_syntax =
541 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
542
543 let name_range = name_ref.syntax().text_range();
520 let top_node = name_ref 544 let top_node = name_ref
521 .syntax() 545 .syntax()
522 .ancestors() 546 .ancestors()
@@ -524,31 +548,20 @@ impl<'a> CompletionContext<'a> {
524 .last() 548 .last()
525 .unwrap(); 549 .unwrap();
526 550
527 match top_node.parent().map(|it| it.kind()) { 551 if matches!(top_node.parent().map(|it| it.kind()), Some(SOURCE_FILE) | Some(ITEM_LIST)) {
528 Some(SOURCE_FILE) | Some(ITEM_LIST) => { 552 self.is_new_item = true;
529 self.is_new_item = true; 553 return;
530 return;
531 }
532 _ => (),
533 } 554 }
534 555
535 self.use_item_syntax = 556 self.use_item_syntax =
536 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); 557 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast);
537 558
538 self.function_syntax = self 559 self.function_def = self
539 .sema 560 .sema
540 .token_ancestors_with_macros(self.token.clone()) 561 .token_ancestors_with_macros(self.token.clone())
541 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 562 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
542 .find_map(ast::Fn::cast); 563 .find_map(ast::Fn::cast);
543 564
544 self.record_field_syntax = self
545 .sema
546 .token_ancestors_with_macros(self.token.clone())
547 .take_while(|it| {
548 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
549 })
550 .find_map(ast::RecordExprField::cast);
551
552 let parent = match name_ref.syntax().parent() { 565 let parent = match name_ref.syntax().parent() {
553 Some(it) => it, 566 Some(it) => it,
554 None => return, 567 None => return,
@@ -609,18 +622,8 @@ impl<'a> CompletionContext<'a> {
609 }) 622 })
610 .unwrap_or(false); 623 .unwrap_or(false);
611 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); 624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
612
613 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
614 if let Some(if_expr) =
615 self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
616 {
617 if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start()
618 {
619 self.after_if = true;
620 }
621 }
622 }
623 } 625 }
626
624 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { 627 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
625 // The receiver comes before the point of insertion of the fake 628 // The receiver comes before the point of insertion of the fake
626 // ident, so it should have the same range in the non-modified file 629 // ident, so it should have the same range in the non-modified file
@@ -638,6 +641,7 @@ impl<'a> CompletionContext<'a> {
638 false 641 false
639 }; 642 };
640 } 643 }
644
641 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { 645 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
642 // As above 646 // As above
643 self.dot_receiver = method_call_expr 647 self.dot_receiver = method_call_expr
@@ -785,6 +789,19 @@ fn foo() {
785 } 789 }
786 790
787 #[test] 791 #[test]
792 fn expected_type_generic_struct_field() {
793 check_expected_type_and_name(
794 r#"
795struct Foo<T> { a: T }
796fn foo() -> Foo<u32> {
797 Foo { a: $0 }
798}
799"#,
800 expect![[r#"ty: u32, name: a"#]],
801 )
802 }
803
804 #[test]
788 fn expected_type_struct_field_with_leading_char() { 805 fn expected_type_struct_field_with_leading_char() {
789 cov_mark::check!(expected_type_struct_field_with_leading_char); 806 cov_mark::check!(expected_type_struct_field_with_leading_char);
790 check_expected_type_and_name( 807 check_expected_type_and_name(
@@ -895,4 +912,52 @@ fn foo() -> u32 {
895 expect![[r#"ty: u32, name: ?"#]], 912 expect![[r#"ty: u32, name: ?"#]],
896 ) 913 )
897 } 914 }
915
916 #[test]
917 fn expected_type_closure_param_return() {
918 // FIXME: make this work with `|| $0`
919 check_expected_type_and_name(
920 r#"
921fn foo() {
922 bar(|| a$0);
923}
924
925fn bar(f: impl FnOnce() -> u32) {}
926#[lang = "fn_once"]
927trait FnOnce { type Output; }
928"#,
929 expect![[r#"ty: u32, name: ?"#]],
930 );
931 }
932
933 #[test]
934 fn expected_type_generic_function() {
935 check_expected_type_and_name(
936 r#"
937fn foo() {
938 bar::<u32>($0);
939}
940
941fn bar<T>(t: T) {}
942"#,
943 expect![[r#"ty: u32, name: t"#]],
944 );
945 }
946
947 #[test]
948 fn expected_type_generic_method() {
949 check_expected_type_and_name(
950 r#"
951fn foo() {
952 S(1u32).bar($0);
953}
954
955struct S<T>(T);
956impl<T> S<T> {
957 fn bar(self, t: T) {}
958}
959"#,
960 expect![[r#"ty: u32, name: t"#]],
961 );
962 }
898} 963}
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..26516046b 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -1,38 +1,197 @@
1//! Patterns telling us certain facts about current syntax element, they are used in completion context 1//! Patterns telling us certain facts about current syntax element, they are used in completion context
2 2
3use hir::Semantics;
4use ide_db::RootDatabase;
3use syntax::{ 5use syntax::{
4 algo::non_trivia_sibling, 6 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner}, 7 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, 8 match_ast, AstNode, Direction, SyntaxElement,
7 SyntaxKind::*, 9 SyntaxKind::*,
8 SyntaxNode, SyntaxToken, T, 10 SyntaxNode, SyntaxToken, TextSize, T,
9}; 11};
10 12
11#[cfg(test)] 13#[cfg(test)]
12use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; 14use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
13 15
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { 16/// Direct parent container of the cursor position
15 not_same_range_ancestor(element) 17#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16 .filter(|it| it.kind() == ASSOC_ITEM_LIST) 18pub(crate) enum ImmediatePrevSibling {
17 .and_then(|it| it.parent()) 19 IfExpr,
18 .filter(|it| it.kind() == TRAIT) 20 TraitDefName,
19 .is_some() 21 ImplDefType,
20} 22}
21#[test] 23
22fn test_has_trait_parent() { 24/// Direct parent container of the cursor position
23 check_pattern_is_applicable(r"trait A { f$0 }", has_trait_parent); 25#[derive(Clone, Debug, PartialEq, Eq)]
26pub(crate) enum ImmediateLocation {
27 Use,
28 Impl,
29 Trait,
30 RecordField,
31 RefExpr,
32 IdentPat,
33 BlockExpr,
34 ItemList,
35 // Fake file ast node
36 Attribute(ast::Attr),
37 // Fake file ast node
38 ModDeclaration(ast::Module),
39 // Original file ast node
40 /// The record expr of the field name we are completing
41 RecordExpr(ast::RecordExpr),
42 // Original file ast node
43 /// The record pat of the field name we are completing
44 RecordPat(ast::RecordPat),
24} 45}
25 46
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { 47pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<ImmediatePrevSibling> {
27 not_same_range_ancestor(element) 48 let node = match name_like {
28 .filter(|it| it.kind() == ASSOC_ITEM_LIST) 49 ast::NameLike::NameRef(name_ref) => maximize_name_ref(name_ref),
29 .and_then(|it| it.parent()) 50 ast::NameLike::Name(n) => n.syntax().clone(),
30 .filter(|it| it.kind() == IMPL) 51 ast::NameLike::Lifetime(lt) => lt.syntax().clone(),
31 .is_some() 52 };
53 let node = match node.parent().and_then(ast::MacroCall::cast) {
54 // When a path is being typed after the name of a trait/type of an impl it is being
55 // parsed as a macro, so when the trait/impl has a block following it an we are between the
56 // name and block the macro will attach the block to itself so maximizing fails to take
57 // that into account
58 // FIXME path expr and statement have a similar problem with attrs
59 Some(call)
60 if call.excl_token().is_none()
61 && call.token_tree().map_or(false, |t| t.l_curly_token().is_some())
62 && call.semicolon_token().is_none() =>
63 {
64 call.syntax().clone()
65 }
66 _ => node,
67 };
68 let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
69 let res = match_ast! {
70 match prev_sibling {
71 ast::ExprStmt(it) => {
72 let node = it.expr().filter(|_| it.semicolon_token().is_none())?.syntax().clone();
73 match_ast! {
74 match node {
75 ast::IfExpr(_it) => ImmediatePrevSibling::IfExpr,
76 _ => return None,
77 }
78 }
79 },
80 ast::Trait(it) => if it.assoc_item_list().is_none() {
81 ImmediatePrevSibling::TraitDefName
82 } else {
83 return None
84 },
85 ast::Impl(it) => if it.assoc_item_list().is_none()
86 && (it.for_token().is_none() || it.self_ty().is_some()) {
87 ImmediatePrevSibling::ImplDefType
88 } else {
89 return None
90 },
91 _ => return None,
92 }
93 };
94 Some(res)
32} 95}
33#[test] 96
34fn test_has_impl_parent() { 97pub(crate) fn determine_location(
35 check_pattern_is_applicable(r"impl A { f$0 }", has_impl_parent); 98 sema: &Semantics<RootDatabase>,
99 original_file: &SyntaxNode,
100 offset: TextSize,
101 name_like: &ast::NameLike,
102) -> Option<ImmediateLocation> {
103 let node = match name_like {
104 ast::NameLike::NameRef(name_ref) => {
105 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
106 return sema
107 .find_node_at_offset_with_macros(original_file, offset)
108 .map(ImmediateLocation::RecordExpr);
109 }
110 if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() {
111 return sema
112 .find_node_at_offset_with_macros(original_file, offset)
113 .map(ImmediateLocation::RecordPat);
114 }
115 maximize_name_ref(name_ref)
116 }
117 ast::NameLike::Name(name) => {
118 if ast::RecordPatField::for_field_name(&name).is_some() {
119 return sema
120 .find_node_at_offset_with_macros(original_file, offset)
121 .map(ImmediateLocation::RecordPat);
122 }
123 name.syntax().clone()
124 }
125 ast::NameLike::Lifetime(lt) => lt.syntax().clone(),
126 };
127
128 let parent = match node.parent() {
129 Some(parent) => match ast::MacroCall::cast(parent.clone()) {
130 // When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call.
131 // This is usually fine as the node expansion code above already accounts for that with
132 // the ancestors call, but there is one exception to this which is that when an attribute
133 // precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ.
134 // FIXME path expr and statement have a similar problem
135 Some(call)
136 if call.excl_token().is_none()
137 && call.token_tree().is_none()
138 && call.semicolon_token().is_none() =>
139 {
140 call.syntax().parent()?
141 }
142 _ => parent,
143 },
144 // SourceFile
145 None => {
146 return match node.kind() {
147 MACRO_ITEMS | SOURCE_FILE => Some(ImmediateLocation::ItemList),
148 _ => None,
149 }
150 }
151 };
152
153 let res = match_ast! {
154 match parent {
155 ast::IdentPat(_it) => ImmediateLocation::IdentPat,
156 ast::Use(_it) => ImmediateLocation::Use,
157 ast::BlockExpr(_it) => ImmediateLocation::BlockExpr,
158 ast::SourceFile(_it) => ImmediateLocation::ItemList,
159 ast::ItemList(_it) => ImmediateLocation::ItemList,
160 ast::RefExpr(_it) => ImmediateLocation::RefExpr,
161 ast::RecordField(_it) => ImmediateLocation::RecordField,
162 ast::AssocItemList(it) => match it.syntax().parent().map(|it| it.kind()) {
163 Some(IMPL) => ImmediateLocation::Impl,
164 Some(TRAIT) => ImmediateLocation::Trait,
165 _ => return None,
166 },
167 ast::Module(it) => if it.item_list().is_none() {
168 ImmediateLocation::ModDeclaration(it)
169 } else {
170 return None
171 },
172 ast::Attr(it) => ImmediateLocation::Attribute(it),
173 _ => return None,
174 }
175 };
176 Some(res)
177}
178
179fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode {
180 // Maximize a nameref to its enclosing path if its the last segment of said path
181 if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
182 let p = segment.parent_path();
183 if p.parent_path().is_none() {
184 if let Some(it) = p
185 .syntax()
186 .ancestors()
187 .take_while(|it| it.text_range() == p.syntax().text_range())
188 .last()
189 {
190 return it;
191 }
192 }
193 }
194 name_ref.syntax().clone()
36} 195}
37 196
38pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { 197pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
@@ -53,98 +212,8 @@ fn test_inside_impl_trait_block() {
53 check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block); 212 check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block);
54} 213}
55 214
56pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool { 215pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
57 not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some() 216 element.into_token().and_then(|it| previous_non_trivia_token(it))
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 {
107 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
108 && previous_sibling_or_ancestor_sibling(element)
109 .and_then(|it| it.into_token())
110 .filter(|it| it.kind() == FAT_ARROW)
111 .is_some()
112}
113#[test]
114fn test_is_match_arm() {
115 check_pattern_is_applicable(r"fn my_fn() { match () { () => m$0 } }", is_match_arm);
116}
117
118pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
119 element
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} 217}
149 218
150/// Check if the token previous to the previous one is `for`. 219/// Check if the token previous to the previous one is `for`.
@@ -162,55 +231,22 @@ fn test_for_is_prev2() {
162 check_pattern_is_applicable(r"for i i$0", for_is_prev2); 231 check_pattern_is_applicable(r"for i i$0", for_is_prev2);
163} 232}
164 233
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 { 234pub(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 235 element
210 .ancestors() 236 .ancestors()
211 .take_while(|it| it.text_range() == element.text_range()) 237 .take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR)
212 .last() 238 .find_map(|it| {
213 .and_then(|it| it.parent()) 239 let loop_body = match_ast! {
240 match it {
241 ast::ForExpr(it) => it.loop_body(),
242 ast::WhileExpr(it) => it.loop_body(),
243 ast::LoopExpr(it) => it.loop_body(),
244 _ => None,
245 }
246 };
247 loop_body.filter(|it| it.syntax().text_range().contains_range(element.text_range()))
248 })
249 .is_some()
214} 250}
215 251
216fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> { 252fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
@@ -225,17 +261,119 @@ fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
225 None 261 None
226} 262}
227 263
228fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> { 264#[cfg(test)]
229 let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev); 265mod tests {
230 if let Some(sibling) = token_sibling { 266 use syntax::algo::find_node_at_offset;
231 Some(sibling) 267
232 } else { 268 use crate::test_utils::position;
233 // if not trying to find first ancestor which has such a sibling 269
234 let range = element.text_range(); 270 use super::*;
235 let top_node = element.ancestors().take_while(|it| it.text_range() == range).last()?; 271
236 let prev_sibling_node = top_node.ancestors().find(|it| { 272 fn check_location(code: &str, loc: impl Into<Option<ImmediateLocation>>) {
237 non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some() 273 let (db, pos) = position(code);
238 })?; 274
239 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev) 275 let sema = Semantics::new(&db);
276 let original_file = sema.parse(pos.file_id);
277
278 let name_like = find_node_at_offset(original_file.syntax(), pos.offset).unwrap();
279 assert_eq!(
280 determine_location(&sema, original_file.syntax(), pos.offset, &name_like),
281 loc.into()
282 );
283 }
284
285 fn check_prev_sibling(code: &str, sibling: impl Into<Option<ImmediatePrevSibling>>) {
286 check_pattern_is_applicable(code, |e| {
287 let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike");
288 assert_eq!(determine_prev_sibling(name), sibling.into());
289 true
290 });
291 }
292
293 #[test]
294 fn test_trait_loc() {
295 check_location(r"trait A { f$0 }", ImmediateLocation::Trait);
296 check_location(r"trait A { #[attr] f$0 }", ImmediateLocation::Trait);
297 check_location(r"trait A { f$0 fn f() {} }", ImmediateLocation::Trait);
298 check_location(r"trait A { fn f() {} f$0 }", ImmediateLocation::Trait);
299 check_location(r"trait A$0 {}", None);
300 check_location(r"trait A { fn f$0 }", None);
301 }
302
303 #[test]
304 fn test_impl_loc() {
305 check_location(r"impl A { f$0 }", ImmediateLocation::Impl);
306 check_location(r"impl A { #[attr] f$0 }", ImmediateLocation::Impl);
307 check_location(r"impl A { f$0 fn f() {} }", ImmediateLocation::Impl);
308 check_location(r"impl A { fn f() {} f$0 }", ImmediateLocation::Impl);
309 check_location(r"impl A$0 {}", None);
310 check_location(r"impl A { fn f$0 }", None);
311 }
312
313 #[test]
314 fn test_use_loc() {
315 check_location(r"use f$0", ImmediateLocation::Use);
316 check_location(r"use f$0;", ImmediateLocation::Use);
317 check_location(r"use f::{f$0}", None);
318 check_location(r"use {f$0}", None);
319 }
320
321 #[test]
322 fn test_record_field_loc() {
323 check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
324 check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField);
325 check_location(r"struct Foo { pub f: i32, f$0 }", ImmediateLocation::RecordField);
326 }
327
328 #[test]
329 fn test_block_expr_loc() {
330 check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr);
331 check_location(r"fn my_fn() { f$0 f }", ImmediateLocation::BlockExpr);
332 }
333
334 #[test]
335 fn test_ident_pat_loc() {
336 check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat);
337 check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat);
338 check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat);
339 check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat);
340 }
341
342 #[test]
343 fn test_ref_expr_loc() {
344 check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr);
345 }
346
347 #[test]
348 fn test_item_list_loc() {
349 check_location(r"i$0", ImmediateLocation::ItemList);
350 check_location(r"#[attr] i$0", ImmediateLocation::ItemList);
351 check_location(r"fn f() {} i$0", ImmediateLocation::ItemList);
352 check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList);
353 check_location(r"mod foo { #[attr] f$0 }", ImmediateLocation::ItemList);
354 check_location(r"mod foo { fn f() {} f$0 }", ImmediateLocation::ItemList);
355 check_location(r"mod foo$0 {}", None);
356 }
357
358 #[test]
359 fn test_impl_prev_sibling() {
360 check_prev_sibling(r"impl A w$0 ", ImmediatePrevSibling::ImplDefType);
361 check_prev_sibling(r"impl A w$0 {}", ImmediatePrevSibling::ImplDefType);
362 check_prev_sibling(r"impl A for A w$0 ", ImmediatePrevSibling::ImplDefType);
363 check_prev_sibling(r"impl A for A w$0 {}", ImmediatePrevSibling::ImplDefType);
364 check_prev_sibling(r"impl A for w$0 {}", None);
365 check_prev_sibling(r"impl A for w$0", None);
366 }
367
368 #[test]
369 fn test_trait_prev_sibling() {
370 check_prev_sibling(r"trait A w$0 ", ImmediatePrevSibling::TraitDefName);
371 check_prev_sibling(r"trait A w$0 {}", ImmediatePrevSibling::TraitDefName);
372 }
373
374 #[test]
375 fn test_if_expr_prev_sibling() {
376 check_prev_sibling(r"fn foo() { if true {} w$0", ImmediatePrevSibling::IfExpr);
377 check_prev_sibling(r"fn foo() { if true {}; w$0", None);
240 } 378 }
241} 379}
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 6b04ee164..a49a60711 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -10,9 +10,7 @@ pub(crate) mod type_alias;
10 10
11mod builder_ext; 11mod builder_ext;
12 12
13use hir::{ 13use hir::{AsAssocItem, HasAttrs, HirDisplay};
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
15};
16use ide_db::{ 14use ide_db::{
17 helpers::{item_name, SnippetCap}, 15 helpers::{item_name, SnippetCap},
18 RootDatabase, SymbolKind, 16 RootDatabase, SymbolKind,
@@ -21,31 +19,32 @@ use syntax::TextRange;
21 19
22use crate::{ 20use crate::{
23 item::{CompletionRelevanceTypeMatch, ImportEdit}, 21 item::{CompletionRelevanceTypeMatch, ImportEdit},
22 render::{enum_variant::render_variant, function::render_fn, macro_::render_macro},
24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, 23 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance,
25}; 24};
26 25
27use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
28
29pub(crate) fn render_field<'a>( 26pub(crate) fn render_field<'a>(
30 ctx: RenderContext<'a>, 27 ctx: RenderContext<'a>,
28 receiver: Option<hir::Name>,
31 field: hir::Field, 29 field: hir::Field,
32 ty: &Type, 30 ty: &hir::Type,
33) -> CompletionItem { 31) -> CompletionItem {
34 Render::new(ctx).add_field(field, ty) 32 Render::new(ctx).render_field(receiver, field, ty)
35} 33}
36 34
37pub(crate) fn render_tuple_field<'a>( 35pub(crate) fn render_tuple_field<'a>(
38 ctx: RenderContext<'a>, 36 ctx: RenderContext<'a>,
37 receiver: Option<hir::Name>,
39 field: usize, 38 field: usize,
40 ty: &Type, 39 ty: &hir::Type,
41) -> CompletionItem { 40) -> CompletionItem {
42 Render::new(ctx).add_tuple_field(field, ty) 41 Render::new(ctx).render_tuple_field(receiver, field, ty)
43} 42}
44 43
45pub(crate) fn render_resolution<'a>( 44pub(crate) fn render_resolution<'a>(
46 ctx: RenderContext<'a>, 45 ctx: RenderContext<'a>,
47 local_name: String, 46 local_name: hir::Name,
48 resolution: &ScopeDef, 47 resolution: &hir::ScopeDef,
49) -> Option<CompletionItem> { 48) -> Option<CompletionItem> {
50 Render::new(ctx).render_resolution(local_name, None, resolution) 49 Render::new(ctx).render_resolution(local_name, None, resolution)
51} 50}
@@ -54,12 +53,12 @@ pub(crate) fn render_resolution_with_import<'a>(
54 ctx: RenderContext<'a>, 53 ctx: RenderContext<'a>,
55 import_edit: ImportEdit, 54 import_edit: ImportEdit,
56) -> Option<CompletionItem> { 55) -> Option<CompletionItem> {
57 let resolution = ScopeDef::from(import_edit.import.original_item); 56 let resolution = hir::ScopeDef::from(import_edit.import.original_item);
58 let local_name = match resolution { 57 let local_name = match resolution {
59 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), 58 hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db),
60 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), 59 hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?,
61 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), 60 hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),
62 _ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(), 61 _ => item_name(ctx.db(), import_edit.import.original_item)?,
63 }; 62 };
64 Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map( 63 Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map(
65 |mut item| { 64 |mut item| {
@@ -113,7 +112,7 @@ impl<'a> RenderContext<'a> {
113 || assoc.containing_trait(db).map(|trait_| self.is_deprecated(trait_)).unwrap_or(false) 112 || assoc.containing_trait(db).map(|trait_| self.is_deprecated(trait_)).unwrap_or(false)
114 } 113 }
115 114
116 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { 115 fn docs(&self, node: impl HasAttrs) -> Option<hir::Documentation> {
117 node.docs(self.db()) 116 node.docs(self.db())
118 } 117 }
119} 118}
@@ -129,13 +128,18 @@ impl<'a> Render<'a> {
129 Render { ctx } 128 Render { ctx }
130 } 129 }
131 130
132 fn add_field(&mut self, field: hir::Field, ty: &Type) -> CompletionItem { 131 fn render_field(
132 &self,
133 receiver: Option<hir::Name>,
134 field: hir::Field,
135 ty: &hir::Type,
136 ) -> CompletionItem {
133 let is_deprecated = self.ctx.is_deprecated(field); 137 let is_deprecated = self.ctx.is_deprecated(field);
134 let name = field.name(self.ctx.db()); 138 let name = field.name(self.ctx.db()).to_string();
135 let mut item = CompletionItem::new( 139 let mut item = CompletionItem::new(
136 CompletionKind::Reference, 140 CompletionKind::Reference,
137 self.ctx.source_range(), 141 self.ctx.source_range(),
138 name.to_string(), 142 receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)),
139 ); 143 );
140 item.kind(SymbolKind::Field) 144 item.kind(SymbolKind::Field)
141 .detail(ty.display(self.ctx.db()).to_string()) 145 .detail(ty.display(self.ctx.db()).to_string())
@@ -144,7 +148,7 @@ impl<'a> Render<'a> {
144 148
145 item.set_relevance(CompletionRelevance { 149 item.set_relevance(CompletionRelevance {
146 type_match: compute_type_match(self.ctx.completion, ty), 150 type_match: compute_type_match(self.ctx.completion, ty),
147 exact_name_match: compute_exact_name_match(self.ctx.completion, name.to_string()), 151 exact_name_match: compute_exact_name_match(self.ctx.completion, &name),
148 ..CompletionRelevance::default() 152 ..CompletionRelevance::default()
149 }); 153 });
150 154
@@ -157,11 +161,17 @@ impl<'a> Render<'a> {
157 item.build() 161 item.build()
158 } 162 }
159 163
160 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { 164 fn render_tuple_field(
165 &self,
166 receiver: Option<hir::Name>,
167 field: usize,
168 ty: &hir::Type,
169 ) -> CompletionItem {
161 let mut item = CompletionItem::new( 170 let mut item = CompletionItem::new(
162 CompletionKind::Reference, 171 CompletionKind::Reference,
163 self.ctx.source_range(), 172 self.ctx.source_range(),
164 field.to_string(), 173 receiver
174 .map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)),
165 ); 175 );
166 176
167 item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); 177 item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string());
@@ -171,74 +181,82 @@ impl<'a> Render<'a> {
171 181
172 fn render_resolution( 182 fn render_resolution(
173 self, 183 self,
174 local_name: String, 184 local_name: hir::Name,
175 import_to_add: Option<ImportEdit>, 185 import_to_add: Option<ImportEdit>,
176 resolution: &ScopeDef, 186 resolution: &hir::ScopeDef,
177 ) -> Option<CompletionItem> { 187 ) -> Option<CompletionItem> {
178 let _p = profile::span("render_resolution"); 188 let _p = profile::span("render_resolution");
179 use hir::ModuleDef::*; 189 use hir::ModuleDef::*;
180 190
181 let completion_kind = match resolution { 191 let completion_kind = match resolution {
182 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType, 192 hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
183 _ => CompletionKind::Reference, 193 _ => CompletionKind::Reference,
184 }; 194 };
185 195
186 let kind = match resolution { 196 let kind = match resolution {
187 ScopeDef::ModuleDef(Function(func)) => { 197 hir::ScopeDef::ModuleDef(Function(func)) => {
188 return render_fn(self.ctx, import_to_add, Some(local_name), *func); 198 return render_fn(self.ctx, import_to_add, Some(local_name), *func);
189 } 199 }
190 ScopeDef::ModuleDef(Variant(_)) 200 hir::ScopeDef::ModuleDef(Variant(_))
191 if self.ctx.completion.is_pat_binding_or_const 201 if self.ctx.completion.is_pat_or_const.is_some() =>
192 | self.ctx.completion.is_irrefutable_pat_binding =>
193 { 202 {
194 CompletionItemKind::SymbolKind(SymbolKind::Variant) 203 CompletionItemKind::SymbolKind(SymbolKind::Variant)
195 } 204 }
196 ScopeDef::ModuleDef(Variant(var)) => { 205 hir::ScopeDef::ModuleDef(Variant(var)) => {
197 let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None); 206 let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None);
198 return Some(item); 207 return Some(item);
199 } 208 }
200 ScopeDef::MacroDef(mac) => { 209 hir::ScopeDef::MacroDef(mac) => {
201 let item = render_macro(self.ctx, import_to_add, local_name, *mac); 210 let item = render_macro(self.ctx, import_to_add, local_name, *mac);
202 return item; 211 return item;
203 } 212 }
204 213
205 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module), 214 hir::ScopeDef::ModuleDef(Module(..)) => {
206 ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { 215 CompletionItemKind::SymbolKind(SymbolKind::Module)
216 }
217 hir::ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {
207 hir::Adt::Struct(_) => SymbolKind::Struct, 218 hir::Adt::Struct(_) => SymbolKind::Struct,
208 hir::Adt::Union(_) => SymbolKind::Union, 219 hir::Adt::Union(_) => SymbolKind::Union,
209 hir::Adt::Enum(_) => SymbolKind::Enum, 220 hir::Adt::Enum(_) => SymbolKind::Enum,
210 }), 221 }),
211 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), 222 hir::ScopeDef::ModuleDef(Const(..)) => {
212 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), 223 CompletionItemKind::SymbolKind(SymbolKind::Const)
213 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), 224 }
214 ScopeDef::ModuleDef(TypeAlias(..)) => { 225 hir::ScopeDef::ModuleDef(Static(..)) => {
226 CompletionItemKind::SymbolKind(SymbolKind::Static)
227 }
228 hir::ScopeDef::ModuleDef(Trait(..)) => {
229 CompletionItemKind::SymbolKind(SymbolKind::Trait)
230 }
231 hir::ScopeDef::ModuleDef(TypeAlias(..)) => {
215 CompletionItemKind::SymbolKind(SymbolKind::TypeAlias) 232 CompletionItemKind::SymbolKind(SymbolKind::TypeAlias)
216 } 233 }
217 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, 234 hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
218 ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { 235 hir::ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {
219 hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam, 236 hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam,
220 hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam, 237 hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam,
221 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, 238 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,
222 }), 239 }),
223 ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), 240 hir::ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
224 ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label), 241 hir::ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),
225 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => { 242 hir::ScopeDef::AdtSelfType(..) | hir::ScopeDef::ImplSelfType(..) => {
226 CompletionItemKind::SymbolKind(SymbolKind::SelfParam) 243 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
227 } 244 }
228 ScopeDef::Unknown => { 245 hir::ScopeDef::Unknown => {
229 let mut item = CompletionItem::new( 246 let mut item = CompletionItem::new(
230 CompletionKind::Reference, 247 CompletionKind::Reference,
231 self.ctx.source_range(), 248 self.ctx.source_range(),
232 local_name, 249 local_name.to_string(),
233 ); 250 );
234 item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add); 251 item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add);
235 return Some(item.build()); 252 return Some(item.build());
236 } 253 }
237 }; 254 };
238 255
256 let local_name = local_name.to_string();
239 let mut item = 257 let mut item =
240 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone()); 258 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone());
241 if let ScopeDef::Local(local) = resolution { 259 if let hir::ScopeDef::Local(local) = resolution {
242 let ty = local.ty(self.ctx.db()); 260 let ty = local.ty(self.ctx.db());
243 if !ty.is_unknown() { 261 if !ty.is_unknown() {
244 item.detail(ty.display(self.ctx.db()).to_string()); 262 item.detail(ty.display(self.ctx.db()).to_string());
@@ -263,8 +281,10 @@ impl<'a> Render<'a> {
263 { 281 {
264 if let Some(cap) = self.ctx.snippet_cap() { 282 if let Some(cap) = self.ctx.snippet_cap() {
265 let has_non_default_type_params = match resolution { 283 let has_non_default_type_params = match resolution {
266 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(self.ctx.db()), 284 hir::ScopeDef::ModuleDef(Adt(it)) => {
267 ScopeDef::ModuleDef(TypeAlias(it)) => { 285 it.has_non_default_type_params(self.ctx.db())
286 }
287 hir::ScopeDef::ModuleDef(TypeAlias(it)) => {
268 it.has_non_default_type_params(self.ctx.db()) 288 it.has_non_default_type_params(self.ctx.db())
269 } 289 }
270 _ => false, 290 _ => false,
@@ -284,26 +304,26 @@ impl<'a> Render<'a> {
284 Some(item.build()) 304 Some(item.build())
285 } 305 }
286 306
287 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { 307 fn docs(&self, resolution: &hir::ScopeDef) -> Option<hir::Documentation> {
288 use hir::ModuleDef::*; 308 use hir::ModuleDef::*;
289 match resolution { 309 match resolution {
290 ScopeDef::ModuleDef(Module(it)) => it.docs(self.ctx.db()), 310 hir::ScopeDef::ModuleDef(Module(it)) => it.docs(self.ctx.db()),
291 ScopeDef::ModuleDef(Adt(it)) => it.docs(self.ctx.db()), 311 hir::ScopeDef::ModuleDef(Adt(it)) => it.docs(self.ctx.db()),
292 ScopeDef::ModuleDef(Variant(it)) => it.docs(self.ctx.db()), 312 hir::ScopeDef::ModuleDef(Variant(it)) => it.docs(self.ctx.db()),
293 ScopeDef::ModuleDef(Const(it)) => it.docs(self.ctx.db()), 313 hir::ScopeDef::ModuleDef(Const(it)) => it.docs(self.ctx.db()),
294 ScopeDef::ModuleDef(Static(it)) => it.docs(self.ctx.db()), 314 hir::ScopeDef::ModuleDef(Static(it)) => it.docs(self.ctx.db()),
295 ScopeDef::ModuleDef(Trait(it)) => it.docs(self.ctx.db()), 315 hir::ScopeDef::ModuleDef(Trait(it)) => it.docs(self.ctx.db()),
296 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(self.ctx.db()), 316 hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(self.ctx.db()),
297 _ => None, 317 _ => None,
298 } 318 }
299 } 319 }
300 320
301 fn is_deprecated(&self, resolution: &ScopeDef) -> bool { 321 fn is_deprecated(&self, resolution: &hir::ScopeDef) -> bool {
302 match resolution { 322 match resolution {
303 ScopeDef::ModuleDef(it) => self.ctx.is_deprecated_assoc_item(*it), 323 hir::ScopeDef::ModuleDef(it) => self.ctx.is_deprecated_assoc_item(*it),
304 ScopeDef::MacroDef(it) => self.ctx.is_deprecated(*it), 324 hir::ScopeDef::MacroDef(it) => self.ctx.is_deprecated(*it),
305 ScopeDef::GenericParam(it) => self.ctx.is_deprecated(*it), 325 hir::ScopeDef::GenericParam(it) => self.ctx.is_deprecated(*it),
306 ScopeDef::AdtSelfType(it) => self.ctx.is_deprecated(*it), 326 hir::ScopeDef::AdtSelfType(it) => self.ctx.is_deprecated(*it),
307 _ => false, 327 _ => false,
308 } 328 }
309 } 329 }
@@ -330,21 +350,23 @@ fn compute_type_match(
330 } 350 }
331} 351}
332 352
333fn compute_exact_name_match(ctx: &CompletionContext, completion_name: impl Into<String>) -> bool { 353fn compute_exact_name_match(ctx: &CompletionContext, completion_name: &str) -> bool {
334 let completion_name = completion_name.into();
335 ctx.expected_name.as_ref().map_or(false, |name| name.text() == completion_name) 354 ctx.expected_name.as_ref().map_or(false, |name| name.text() == completion_name)
336} 355}
337 356
338fn compute_ref_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> Option<Mutability> { 357fn compute_ref_match(
358 ctx: &CompletionContext,
359 completion_ty: &hir::Type,
360) -> Option<hir::Mutability> {
339 let expected_type = ctx.expected_type.as_ref()?; 361 let expected_type = ctx.expected_type.as_ref()?;
340 if completion_ty != expected_type { 362 if completion_ty != expected_type {
341 let expected_type_without_ref = expected_type.remove_ref()?; 363 let expected_type_without_ref = expected_type.remove_ref()?;
342 if completion_ty.autoderef(ctx.db).any(|deref_ty| deref_ty == expected_type_without_ref) { 364 if completion_ty.autoderef(ctx.db).any(|deref_ty| deref_ty == expected_type_without_ref) {
343 cov_mark::hit!(suggest_ref); 365 cov_mark::hit!(suggest_ref);
344 let mutability = if expected_type.is_mutable_reference() { 366 let mutability = if expected_type.is_mutable_reference() {
345 Mutability::Mut 367 hir::Mutability::Mut
346 } else { 368 } else {
347 Mutability::Shared 369 hir::Mutability::Shared
348 }; 370 };
349 return Some(mutability); 371 return Some(mutability);
350 }; 372 };
@@ -667,6 +689,13 @@ fn foo() { A { the$0 } }
667 ), 689 ),
668 detail: "u32", 690 detail: "u32",
669 deprecated: true, 691 deprecated: true,
692 relevance: CompletionRelevance {
693 exact_name_match: false,
694 type_match: Some(
695 CouldUnify,
696 ),
697 is_local: false,
698 },
670 }, 699 },
671 ] 700 ]
672 "#]], 701 "#]],
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index 0c0c71134..28f056e77 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -1,6 +1,8 @@
1//! Renderer for `enum` variants. 1//! Renderer for `enum` variants.
2 2
3use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; 3use std::iter;
4
5use hir::{HasAttrs, HirDisplay};
4use ide_db::SymbolKind; 6use ide_db::SymbolKind;
5use itertools::Itertools; 7use itertools::Itertools;
6 8
@@ -13,9 +15,9 @@ use crate::{
13pub(crate) fn render_variant<'a>( 15pub(crate) fn render_variant<'a>(
14 ctx: RenderContext<'a>, 16 ctx: RenderContext<'a>,
15 import_to_add: Option<ImportEdit>, 17 import_to_add: Option<ImportEdit>,
16 local_name: Option<String>, 18 local_name: Option<hir::Name>,
17 variant: hir::Variant, 19 variant: hir::Variant,
18 path: Option<ModPath>, 20 path: Option<hir::ModPath>,
19) -> CompletionItem { 21) -> CompletionItem {
20 let _p = profile::span("render_enum_variant"); 22 let _p = profile::span("render_enum_variant");
21 EnumRender::new(ctx, local_name, variant, path).render(import_to_add) 23 EnumRender::new(ctx, local_name, variant, path).render(import_to_add)
@@ -24,42 +26,45 @@ pub(crate) fn render_variant<'a>(
24#[derive(Debug)] 26#[derive(Debug)]
25struct EnumRender<'a> { 27struct EnumRender<'a> {
26 ctx: RenderContext<'a>, 28 ctx: RenderContext<'a>,
27 name: String, 29 name: hir::Name,
28 variant: hir::Variant, 30 variant: hir::Variant,
29 path: Option<ModPath>, 31 path: Option<hir::ModPath>,
30 qualified_name: String, 32 qualified_name: hir::ModPath,
31 short_qualified_name: String, 33 short_qualified_name: hir::ModPath,
32 variant_kind: StructKind, 34 variant_kind: hir::StructKind,
33} 35}
34 36
35impl<'a> EnumRender<'a> { 37impl<'a> EnumRender<'a> {
36 fn new( 38 fn new(
37 ctx: RenderContext<'a>, 39 ctx: RenderContext<'a>,
38 local_name: Option<String>, 40 local_name: Option<hir::Name>,
39 variant: hir::Variant, 41 variant: hir::Variant,
40 path: Option<ModPath>, 42 path: Option<hir::ModPath>,
41 ) -> EnumRender<'a> { 43 ) -> EnumRender<'a> {
42 let name = local_name.unwrap_or_else(|| variant.name(ctx.db()).to_string()); 44 let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
43 let variant_kind = variant.kind(ctx.db()); 45 let variant_kind = variant.kind(ctx.db());
44 46
45 let (qualified_name, short_qualified_name) = match &path { 47 let (qualified_name, short_qualified_name) = match &path {
46 Some(path) => { 48 Some(path) => {
47 let full = path.to_string(); 49 let short = hir::ModPath::from_segments(
48 let segments = path.segments(); 50 hir::PathKind::Plain,
49 let short = segments[segments.len().saturating_sub(2)..].iter().join("::"); 51 path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(),
50 (full, short) 52 );
53 (path.clone(), short)
51 } 54 }
52 None => (name.to_string(), name.to_string()), 55 None => (
56 hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name.clone())),
57 hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name.clone())),
58 ),
53 }; 59 };
54 60
55 EnumRender { ctx, name, variant, path, qualified_name, short_qualified_name, variant_kind } 61 EnumRender { ctx, name, variant, path, qualified_name, short_qualified_name, variant_kind }
56 } 62 }
57
58 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 63 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
59 let mut item = CompletionItem::new( 64 let mut item = CompletionItem::new(
60 CompletionKind::Reference, 65 CompletionKind::Reference,
61 self.ctx.source_range(), 66 self.ctx.source_range(),
62 self.qualified_name.clone(), 67 self.qualified_name.to_string(),
63 ); 68 );
64 item.kind(SymbolKind::Variant) 69 item.kind(SymbolKind::Variant)
65 .set_documentation(self.variant.docs(self.ctx.db())) 70 .set_documentation(self.variant.docs(self.ctx.db()))
@@ -67,12 +72,16 @@ impl<'a> EnumRender<'a> {
67 .add_import(import_to_add) 72 .add_import(import_to_add)
68 .detail(self.detail()); 73 .detail(self.detail());
69 74
70 if self.variant_kind == StructKind::Tuple { 75 if self.variant_kind == hir::StructKind::Tuple {
71 cov_mark::hit!(inserts_parens_for_tuple_enums); 76 cov_mark::hit!(inserts_parens_for_tuple_enums);
72 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len()); 77 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
73 item.add_call_parens(self.ctx.completion, self.short_qualified_name, params); 78 item.add_call_parens(
79 self.ctx.completion,
80 self.short_qualified_name.to_string(),
81 params,
82 );
74 } else if self.path.is_some() { 83 } else if self.path.is_some() {
75 item.lookup_by(self.short_qualified_name); 84 item.lookup_by(self.short_qualified_name.to_string());
76 } 85 }
77 86
78 let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db); 87 let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db);
@@ -96,11 +105,11 @@ impl<'a> EnumRender<'a> {
96 .map(|field| (field.name(self.ctx.db()), field.ty(self.ctx.db()))); 105 .map(|field| (field.name(self.ctx.db()), field.ty(self.ctx.db())));
97 106
98 match self.variant_kind { 107 match self.variant_kind {
99 StructKind::Tuple | StructKind::Unit => format!( 108 hir::StructKind::Tuple | hir::StructKind::Unit => format!(
100 "({})", 109 "({})",
101 detail_types.map(|(_, t)| t.display(self.ctx.db()).to_string()).format(", ") 110 detail_types.map(|(_, t)| t.display(self.ctx.db()).to_string()).format(", ")
102 ), 111 ),
103 StructKind::Record => format!( 112 hir::StructKind::Record => format!(
104 "{{ {} }}", 113 "{{ {} }}",
105 detail_types 114 detail_types
106 .map(|(n, t)| format!("{}: {}", n, t.display(self.ctx.db()).to_string())) 115 .map(|(n, t)| format!("{}: {}", n, t.display(self.ctx.db()).to_string()))
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index d681e2c91..3ec77ca0f 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -1,6 +1,6 @@
1//! Renderer for function calls. 1//! Renderer for function calls.
2 2
3use hir::{HasSource, HirDisplay, Type}; 3use hir::{HasSource, HirDisplay};
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use itertools::Itertools; 5use itertools::Itertools;
6use syntax::ast::Fn; 6use syntax::ast::Fn;
@@ -16,27 +16,29 @@ use crate::{
16pub(crate) fn render_fn<'a>( 16pub(crate) fn render_fn<'a>(
17 ctx: RenderContext<'a>, 17 ctx: RenderContext<'a>,
18 import_to_add: Option<ImportEdit>, 18 import_to_add: Option<ImportEdit>,
19 local_name: Option<String>, 19 local_name: Option<hir::Name>,
20 fn_: hir::Function, 20 fn_: hir::Function,
21) -> Option<CompletionItem> { 21) -> Option<CompletionItem> {
22 let _p = profile::span("render_fn"); 22 let _p = profile::span("render_fn");
23 Some(FunctionRender::new(ctx, local_name, fn_, false)?.render(import_to_add)) 23 Some(FunctionRender::new(ctx, None, local_name, fn_, false)?.render(import_to_add))
24} 24}
25 25
26pub(crate) fn render_method<'a>( 26pub(crate) fn render_method<'a>(
27 ctx: RenderContext<'a>, 27 ctx: RenderContext<'a>,
28 import_to_add: Option<ImportEdit>, 28 import_to_add: Option<ImportEdit>,
29 local_name: Option<String>, 29 receiver: Option<hir::Name>,
30 local_name: Option<hir::Name>,
30 fn_: hir::Function, 31 fn_: hir::Function,
31) -> Option<CompletionItem> { 32) -> Option<CompletionItem> {
32 let _p = profile::span("render_method"); 33 let _p = profile::span("render_method");
33 Some(FunctionRender::new(ctx, local_name, fn_, true)?.render(import_to_add)) 34 Some(FunctionRender::new(ctx, receiver, local_name, fn_, true)?.render(import_to_add))
34} 35}
35 36
36#[derive(Debug)] 37#[derive(Debug)]
37struct FunctionRender<'a> { 38struct FunctionRender<'a> {
38 ctx: RenderContext<'a>, 39 ctx: RenderContext<'a>,
39 name: String, 40 name: String,
41 receiver: Option<hir::Name>,
40 func: hir::Function, 42 func: hir::Function,
41 ast_node: Fn, 43 ast_node: Fn,
42 is_method: bool, 44 is_method: bool,
@@ -45,18 +47,22 @@ struct FunctionRender<'a> {
45impl<'a> FunctionRender<'a> { 47impl<'a> FunctionRender<'a> {
46 fn new( 48 fn new(
47 ctx: RenderContext<'a>, 49 ctx: RenderContext<'a>,
48 local_name: Option<String>, 50 receiver: Option<hir::Name>,
51 local_name: Option<hir::Name>,
49 fn_: hir::Function, 52 fn_: hir::Function,
50 is_method: bool, 53 is_method: bool,
51 ) -> Option<FunctionRender<'a>> { 54 ) -> Option<FunctionRender<'a>> {
52 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string()); 55 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db())).to_string();
53 let ast_node = fn_.source(ctx.db())?.value; 56 let ast_node = fn_.source(ctx.db())?.value;
54 57
55 Some(FunctionRender { ctx, name, func: fn_, ast_node, is_method }) 58 Some(FunctionRender { ctx, name, receiver, func: fn_, ast_node, is_method })
56 } 59 }
57 60
58 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 61 fn render(mut self, import_to_add: Option<ImportEdit>) -> CompletionItem {
59 let params = self.params(); 62 let params = self.params();
63 if let Some(receiver) = &self.receiver {
64 self.name = format!("{}.{}", receiver, &self.name)
65 }
60 let mut item = CompletionItem::new( 66 let mut item = CompletionItem::new(
61 CompletionKind::Reference, 67 CompletionKind::Reference,
62 self.ctx.source_range(), 68 self.ctx.source_range(),
@@ -74,7 +80,7 @@ impl<'a> FunctionRender<'a> {
74 let ret_type = self.func.ret_type(self.ctx.db()); 80 let ret_type = self.func.ret_type(self.ctx.db());
75 item.set_relevance(CompletionRelevance { 81 item.set_relevance(CompletionRelevance {
76 type_match: compute_type_match(self.ctx.completion, &ret_type), 82 type_match: compute_type_match(self.ctx.completion, &ret_type),
77 exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()), 83 exact_name_match: compute_exact_name_match(self.ctx.completion, &self.name),
78 ..CompletionRelevance::default() 84 ..CompletionRelevance::default()
79 }); 85 });
80 86
@@ -129,7 +135,7 @@ impl<'a> FunctionRender<'a> {
129 format!("-> {}", ret_ty.display(self.ctx.db())) 135 format!("-> {}", ret_ty.display(self.ctx.db()))
130 } 136 }
131 137
132 fn add_arg(&self, arg: &str, ty: &Type) -> String { 138 fn add_arg(&self, arg: &str, ty: &hir::Type) -> String {
133 if let Some(derefed_ty) = ty.remove_ref() { 139 if let Some(derefed_ty) = ty.remove_ref() {
134 for (name, local) in self.ctx.completion.locals.iter() { 140 for (name, local) in self.ctx.completion.locals.iter() {
135 if name == arg && local.ty(self.ctx.db()) == derefed_ty { 141 if name == arg && local.ty(self.ctx.db()) == derefed_ty {
@@ -148,7 +154,7 @@ impl<'a> FunctionRender<'a> {
148 }; 154 };
149 155
150 let mut params_pats = Vec::new(); 156 let mut params_pats = Vec::new();
151 let params_ty = if self.ctx.completion.dot_receiver.is_some() { 157 let params_ty = if self.ctx.completion.dot_receiver.is_some() || self.receiver.is_some() {
152 self.func.method_params(self.ctx.db()).unwrap_or_default() 158 self.func.method_params(self.ctx.db()).unwrap_or_default()
153 } else { 159 } else {
154 if let Some(s) = ast_params.self_param() { 160 if let Some(s) = ast_params.self_param() {
@@ -255,6 +261,26 @@ fn bar(s: &S) {
255} 261}
256"#, 262"#,
257 ); 263 );
264
265 check_edit(
266 "self.foo",
267 r#"
268struct S {}
269impl S {
270 fn foo(&self, x: i32) {
271 $0
272 }
273}
274"#,
275 r#"
276struct S {}
277impl S {
278 fn foo(&self, x: i32) {
279 self.foo(${1:x})$0
280 }
281}
282"#,
283 );
258 } 284 }
259 285
260 #[test] 286 #[test]
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index 7578ad50b..0dfba8acc 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -1,6 +1,6 @@
1//! Renderer for macro invocations. 1//! Renderer for macro invocations.
2 2
3use hir::{Documentation, HasSource}; 3use hir::HasSource;
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use syntax::display::macro_label; 5use syntax::display::macro_label;
6 6
@@ -12,7 +12,7 @@ use crate::{
12pub(crate) fn render_macro<'a>( 12pub(crate) fn render_macro<'a>(
13 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
14 import_to_add: Option<ImportEdit>, 14 import_to_add: Option<ImportEdit>,
15 name: String, 15 name: hir::Name,
16 macro_: hir::MacroDef, 16 macro_: hir::MacroDef,
17) -> Option<CompletionItem> { 17) -> Option<CompletionItem> {
18 let _p = profile::span("render_macro"); 18 let _p = profile::span("render_macro");
@@ -24,13 +24,14 @@ struct MacroRender<'a> {
24 ctx: RenderContext<'a>, 24 ctx: RenderContext<'a>,
25 name: String, 25 name: String,
26 macro_: hir::MacroDef, 26 macro_: hir::MacroDef,
27 docs: Option<Documentation>, 27 docs: Option<hir::Documentation>,
28 bra: &'static str, 28 bra: &'static str,
29 ket: &'static str, 29 ket: &'static str,
30} 30}
31 31
32impl<'a> MacroRender<'a> { 32impl<'a> MacroRender<'a> {
33 fn new(ctx: RenderContext<'a>, name: String, macro_: hir::MacroDef) -> MacroRender<'a> { 33 fn new(ctx: RenderContext<'a>, name: hir::Name, macro_: hir::MacroDef) -> MacroRender<'a> {
34 let name = name.to_string();
34 let docs = ctx.docs(macro_); 35 let docs = ctx.docs(macro_);
35 let docs_str = docs.as_ref().map_or("", |s| s.as_str()); 36 let docs_str = docs.as_ref().map_or("", |s| s.as_str());
36 let (bra, ket) = guess_macro_braces(&name, docs_str); 37 let (bra, ket) = guess_macro_braces(&name, docs_str);
@@ -74,7 +75,11 @@ impl<'a> MacroRender<'a> {
74 if self.needs_bang() && self.ctx.snippet_cap().is_some() { 75 if self.needs_bang() && self.ctx.snippet_cap().is_some() {
75 format!("{}!{}…{}", self.name, self.bra, self.ket) 76 format!("{}!{}…{}", self.name, self.bra, self.ket)
76 } else { 77 } else {
77 self.banged_name() 78 if self.macro_.kind() == hir::MacroKind::Derive {
79 self.name.to_string()
80 } else {
81 self.banged_name()
82 }
78 } 83 }
79 } 84 }
80 85
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index 37be575e5..b0a4b2026 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -12,13 +12,14 @@ 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
19pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { 19pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
20 enable_postfix_completions: true, 20 enable_postfix_completions: true,
21 enable_imports_on_the_fly: true, 21 enable_imports_on_the_fly: true,
22 enable_self_on_the_fly: true,
22 add_call_parenthesis: true, 23 add_call_parenthesis: true,
23 add_call_argument_snippets: true, 24 add_call_argument_snippets: true,
24 snippet_cap: SnippetCap::new(true), 25 snippet_cap: SnippetCap::new(true),
@@ -36,10 +37,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
36 let mut database = RootDatabase::default(); 37 let mut database = RootDatabase::default();
37 database.apply_change(change_fixture.change); 38 database.apply_change(change_fixture.change);
38 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 39 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
39 let offset = match range_or_offset { 40 let offset = range_or_offset.expect_offset();
40 RangeOrOffset::Range(_) => panic!(),
41 RangeOrOffset::Offset(it) => it,
42 };
43 (database, FilePosition { file_id, offset }) 41 (database, FilePosition { file_id, offset })
44} 42}
45 43
@@ -52,10 +50,11 @@ pub(crate) fn do_completion_with_config(
52 code: &str, 50 code: &str,
53 kind: CompletionKind, 51 kind: CompletionKind,
54) -> Vec<CompletionItem> { 52) -> Vec<CompletionItem> {
55 let mut kind_completions: Vec<CompletionItem> = 53 get_all_items(config, code)
56 get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect(); 54 .into_iter()
57 kind_completions.sort_by(|l, r| l.label().cmp(r.label())); 55 .filter(|c| c.completion_kind == kind)
58 kind_completions 56 .sorted_by(|l, r| l.label().cmp(r.label()))
57 .collect()
59} 58}
60 59
61pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { 60pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
@@ -132,7 +131,7 @@ pub(crate) fn check_edit_with_config(
132 assert_eq_text!(&ra_fixture_after, &actual) 131 assert_eq_text!(&ra_fixture_after, &actual)
133} 132}
134 133
135pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { 134pub(crate) fn check_pattern_is_applicable(code: &str, check: impl FnOnce(SyntaxElement) -> bool) {
136 let (db, pos) = position(code); 135 let (db, pos) = position(code);
137 136
138 let sema = Semantics::new(&db); 137 let sema = Semantics::new(&db);