aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion')
-rw-r--r--crates/ide_completion/Cargo.toml2
-rw-r--r--crates/ide_completion/src/completions.rs62
-rw-r--r--crates/ide_completion/src/completions/attribute.rs28
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs80
-rw-r--r--crates/ide_completion/src/completions/attribute/lint.rs139
-rw-r--r--crates/ide_completion/src/completions/dot.rs8
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs27
-rw-r--r--crates/ide_completion/src/completions/keyword.rs25
-rw-r--r--crates/ide_completion/src/completions/macro_in_item_position.rs48
-rw-r--r--crates/ide_completion/src/completions/pattern.rs24
-rw-r--r--crates/ide_completion/src/completions/postfix.rs22
-rw-r--r--crates/ide_completion/src/completions/postfix/format_like.rs22
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs84
-rw-r--r--crates/ide_completion/src/completions/snippet.rs14
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs18
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs130
-rw-r--r--crates/ide_completion/src/context.rs174
-rw-r--r--crates/ide_completion/src/generated_lint_completions.rs6380
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_completion/src/patterns.rs30
-rw-r--r--crates/ide_completion/src/render.rs400
-rw-r--r--crates/ide_completion/src/render/builder_ext.rs12
-rw-r--r--crates/ide_completion/src/render/function.rs26
-rw-r--r--crates/ide_completion/src/render/macro_.rs4
-rw-r--r--crates/ide_completion/src/render/pattern.rs6
-rw-r--r--crates/ide_completion/src/render/type_alias.rs23
26 files changed, 743 insertions, 7047 deletions
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml
index ba81c9e04..3c45fe1cb 100644
--- a/crates/ide_completion/Cargo.toml
+++ b/crates/ide_completion/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14itertools = "0.10.0" 14itertools = "0.10.0"
15log = "0.4.8" 15log = "0.4.8"
16rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index ffdcdc930..bd90cefb2 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -6,7 +6,6 @@ pub(crate) mod flyimport;
6pub(crate) mod fn_param; 6pub(crate) mod fn_param;
7pub(crate) mod keyword; 7pub(crate) mod keyword;
8pub(crate) mod lifetime; 8pub(crate) mod lifetime;
9pub(crate) mod macro_in_item_position;
10pub(crate) mod mod_; 9pub(crate) mod mod_;
11pub(crate) mod pattern; 10pub(crate) mod pattern;
12pub(crate) mod postfix; 11pub(crate) mod postfix;
@@ -30,7 +29,7 @@ use crate::{
30 macro_::render_macro, 29 macro_::render_macro,
31 pattern::{render_struct_pat, render_variant_pat}, 30 pattern::{render_struct_pat, render_variant_pat},
32 render_field, render_resolution, render_tuple_field, 31 render_field, render_resolution, render_tuple_field,
33 type_alias::render_type_alias, 32 type_alias::{render_type_alias, render_type_alias_with_eq},
34 RenderContext, 33 RenderContext,
35 }, 34 },
36 CompletionContext, CompletionItem, CompletionItemKind, 35 CompletionContext, CompletionItem, CompletionItemKind,
@@ -57,10 +56,16 @@ impl Builder {
57} 56}
58 57
59impl Completions { 58impl Completions {
60 pub(crate) fn add(&mut self, item: CompletionItem) { 59 fn add(&mut self, item: CompletionItem) {
61 self.buf.push(item) 60 self.buf.push(item)
62 } 61 }
63 62
63 fn add_opt(&mut self, item: Option<CompletionItem>) {
64 if let Some(item) = item {
65 self.buf.push(item)
66 }
67 }
68
64 pub(crate) fn add_all<I>(&mut self, items: I) 69 pub(crate) fn add_all<I>(&mut self, items: I)
65 where 70 where
66 I: IntoIterator, 71 I: IntoIterator,
@@ -104,9 +109,10 @@ impl Completions {
104 local_name: hir::Name, 109 local_name: hir::Name,
105 resolution: &hir::ScopeDef, 110 resolution: &hir::ScopeDef,
106 ) { 111 ) {
107 if let Some(item) = render_resolution(RenderContext::new(ctx), local_name, resolution) { 112 if ctx.expects_type() && resolution.is_value_def() {
108 self.add(item); 113 return;
109 } 114 }
115 self.add_opt(render_resolution(RenderContext::new(ctx), local_name, resolution));
110 } 116 }
111 117
112 pub(crate) fn add_macro( 118 pub(crate) fn add_macro(
@@ -119,9 +125,7 @@ impl Completions {
119 Some(it) => it, 125 Some(it) => it,
120 None => return, 126 None => return,
121 }; 127 };
122 if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) { 128 self.add_opt(render_macro(RenderContext::new(ctx), None, name, macro_));
123 self.add(item);
124 }
125 } 129 }
126 130
127 pub(crate) fn add_function( 131 pub(crate) fn add_function(
@@ -130,9 +134,10 @@ impl Completions {
130 func: hir::Function, 134 func: hir::Function,
131 local_name: Option<hir::Name>, 135 local_name: Option<hir::Name>,
132 ) { 136 ) {
133 if let Some(item) = render_fn(RenderContext::new(ctx), None, local_name, func) { 137 if ctx.expects_type() {
134 self.add(item) 138 return;
135 } 139 }
140 self.add_opt(render_fn(RenderContext::new(ctx), None, local_name, func));
136 } 141 }
137 142
138 pub(crate) fn add_method( 143 pub(crate) fn add_method(
@@ -142,10 +147,7 @@ impl Completions {
142 receiver: Option<hir::Name>, 147 receiver: Option<hir::Name>,
143 local_name: Option<hir::Name>, 148 local_name: Option<hir::Name>,
144 ) { 149 ) {
145 if let Some(item) = render_method(RenderContext::new(ctx), None, receiver, local_name, func) 150 self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func));
146 {
147 self.add(item)
148 }
149 } 151 }
150 152
151 pub(crate) fn add_variant_pat( 153 pub(crate) fn add_variant_pat(
@@ -154,9 +156,7 @@ impl Completions {
154 variant: hir::Variant, 156 variant: hir::Variant,
155 local_name: Option<hir::Name>, 157 local_name: Option<hir::Name>,
156 ) { 158 ) {
157 if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name, None) { 159 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None));
158 self.add(item);
159 }
160 } 160 }
161 161
162 pub(crate) fn add_qualified_variant_pat( 162 pub(crate) fn add_qualified_variant_pat(
@@ -165,9 +165,7 @@ impl Completions {
165 variant: hir::Variant, 165 variant: hir::Variant,
166 path: hir::ModPath, 166 path: hir::ModPath,
167 ) { 167 ) {
168 if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)) { 168 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)));
169 self.add(item);
170 }
171 } 169 }
172 170
173 pub(crate) fn add_struct_pat( 171 pub(crate) fn add_struct_pat(
@@ -176,21 +174,26 @@ impl Completions {
176 strukt: hir::Struct, 174 strukt: hir::Struct,
177 local_name: Option<hir::Name>, 175 local_name: Option<hir::Name>,
178 ) { 176 ) {
179 if let Some(item) = render_struct_pat(RenderContext::new(ctx), strukt, local_name) { 177 self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
180 self.add(item);
181 }
182 } 178 }
183 179
184 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { 180 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
185 if let Some(item) = render_const(RenderContext::new(ctx), constant) { 181 if ctx.expects_type() {
186 self.add(item); 182 return;
187 } 183 }
184 self.add_opt(render_const(RenderContext::new(ctx), constant));
188 } 185 }
189 186
190 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { 187 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
191 if let Some(item) = render_type_alias(RenderContext::new(ctx), type_alias) { 188 self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias));
192 self.add(item) 189 }
193 } 190
191 pub(crate) fn add_type_alias_with_eq(
192 &mut self,
193 ctx: &CompletionContext,
194 type_alias: hir::TypeAlias,
195 ) {
196 self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
194 } 197 }
195 198
196 pub(crate) fn add_qualified_enum_variant( 199 pub(crate) fn add_qualified_enum_variant(
@@ -209,6 +212,9 @@ impl Completions {
209 variant: hir::Variant, 212 variant: hir::Variant,
210 local_name: Option<hir::Name>, 213 local_name: Option<hir::Name>,
211 ) { 214 ) {
215 if ctx.expects_type() {
216 return;
217 }
212 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); 218 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None);
213 self.add(item); 219 self.add(item);
214 } 220 }
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index c48bb9e66..6df569c2a 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -3,20 +3,20 @@
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 hir::HasAttrs;
7use ide_db::helpers::generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES};
6use once_cell::sync::Lazy; 8use once_cell::sync::Lazy;
7use rustc_hash::{FxHashMap, FxHashSet}; 9use rustc_hash::{FxHashMap, FxHashSet};
8use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, NodeOrToken, SyntaxKind, T}; 10use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, NodeOrToken, SyntaxKind, T};
9 11
10use crate::{ 12use crate::{
11 context::CompletionContext, 13 context::CompletionContext,
12 generated_lint_completions::{CLIPPY_LINTS, FEATURES},
13 item::{CompletionItem, CompletionItemKind, CompletionKind}, 14 item::{CompletionItem, CompletionItemKind, CompletionKind},
14 Completions, 15 Completions,
15}; 16};
16 17
17mod derive; 18mod derive;
18mod lint; 19mod lint;
19pub(crate) use self::lint::LintCompletion;
20 20
21pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 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()?;
@@ -25,7 +25,7 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
25 "derive" => derive::complete_derive(acc, ctx, token_tree), 25 "derive" => derive::complete_derive(acc, ctx, token_tree),
26 "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES), 26 "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES),
27 "allow" | "warn" | "deny" | "forbid" => { 27 "allow" | "warn" | "deny" | "forbid" => {
28 lint::complete_lint(acc, ctx, token_tree.clone(), lint::DEFAULT_LINT_COMPLETIONS); 28 lint::complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINTS);
29 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); 29 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
30 } 30 }
31 _ => (), 31 _ => (),
@@ -69,7 +69,7 @@ fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attrib
69 } 69 }
70 70
71 if is_inner || !attr_completion.prefer_inner { 71 if is_inner || !attr_completion.prefer_inner {
72 acc.add(item.build()); 72 item.add_to(acc);
73 } 73 }
74 }; 74 };
75 75
@@ -82,6 +82,24 @@ fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attrib
82 None if is_inner => ATTRIBUTES.iter().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), 83 None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion),
84 } 84 }
85
86 // FIXME: write a test for this when we can
87 ctx.scope.process_all_names(&mut |name, scope_def| {
88 if let hir::ScopeDef::MacroDef(mac) = scope_def {
89 if mac.kind() == hir::MacroKind::Attr {
90 let mut item = CompletionItem::new(
91 CompletionKind::Attribute,
92 ctx.source_range(),
93 name.to_string(),
94 );
95 item.kind(CompletionItemKind::Attribute);
96 if let Some(docs) = mac.docs(ctx.sema.db) {
97 item.documentation(docs);
98 }
99 item.add_to(acc);
100 }
101 }
102 });
85} 103}
86 104
87struct AttrCompletion { 105struct AttrCompletion {
@@ -201,7 +219,7 @@ static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| {
201}); 219});
202const EXPR_ATTRIBUTES: &[&str] = attrs!(); 220const EXPR_ATTRIBUTES: &[&str] = attrs!();
203 221
204/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index 222/// <https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index>
205// Keep these sorted for the binary search! 223// Keep these sorted for the binary search!
206const ATTRIBUTES: &[AttrCompletion] = &[ 224const ATTRIBUTES: &[AttrCompletion] = &[
207 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), 225 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
index 0bc3eab98..d526824fb 100644
--- a/crates/ide_completion/src/completions/attribute/derive.rs
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -1,6 +1,7 @@
1//! Completion for derives 1//! Completion for derives
2use hir::HasAttrs;
2use itertools::Itertools; 3use itertools::Itertools;
3use rustc_hash::FxHashSet; 4use rustc_hash::FxHashMap;
4use syntax::ast; 5use syntax::ast;
5 6
6use crate::{ 7use crate::{
@@ -15,66 +16,64 @@ pub(super) fn complete_derive(
15 derive_input: ast::TokenTree, 16 derive_input: ast::TokenTree,
16) { 17) {
17 if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) { 18 if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) {
18 for derive_completion in DEFAULT_DERIVE_COMPLETIONS 19 for (derive, docs) in get_derive_names_in_scope(ctx) {
19 .iter() 20 let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS
20 .filter(|completion| !existing_derives.contains(completion.label)) 21 .iter()
21 { 22 .find(|derive_completion| derive_completion.label == derive)
22 let mut components = vec![derive_completion.label]; 23 {
23 components.extend( 24 let mut components = vec![derive_completion.label];
24 derive_completion 25 components.extend(
25 .dependencies 26 derive_completion
26 .iter() 27 .dependencies
27 .filter(|&&dependency| !existing_derives.contains(dependency)), 28 .iter()
28 ); 29 .filter(|&&dependency| !existing_derives.contains(dependency)),
29 let lookup = components.join(", "); 30 );
30 let label = components.iter().rev().join(", "); 31 let lookup = components.join(", ");
32 let label = components.iter().rev().join(", ");
33 (label, Some(lookup))
34 } else {
35 (derive, None)
36 };
31 let mut item = 37 let mut item =
32 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); 38 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); 39 item.kind(CompletionItemKind::Attribute);
40 if let Some(docs) = docs {
41 item.documentation(docs);
42 }
43 if let Some(lookup) = lookup {
44 item.lookup_by(lookup);
45 }
44 item.add_to(acc); 46 item.add_to(acc);
45 } 47 }
46 } 48 }
47} 49}
48 50
49fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { 51fn get_derive_names_in_scope(
50 let mut result = FxHashSet::default(); 52 ctx: &CompletionContext,
53) -> FxHashMap<String, Option<hir::Documentation>> {
54 let mut result = FxHashMap::default();
51 ctx.scope.process_all_names(&mut |name, scope_def| { 55 ctx.scope.process_all_names(&mut |name, scope_def| {
52 if let hir::ScopeDef::MacroDef(mac) = scope_def { 56 if let hir::ScopeDef::MacroDef(mac) = scope_def {
53 if mac.kind() == hir::MacroKind::Derive { 57 if mac.kind() == hir::MacroKind::Derive {
54 result.insert(name.to_string()); 58 result.insert(name.to_string(), mac.docs(ctx.db));
55 } 59 }
56 } 60 }
57 }); 61 });
58 result 62 result
59} 63}
60 64
61struct DeriveCompletion { 65struct DeriveDependencies {
62 label: &'static str, 66 label: &'static str,
63 dependencies: &'static [&'static str], 67 dependencies: &'static [&'static str],
64} 68}
65 69
66/// Standard Rust derives and the information about their dependencies 70/// Standard Rust derives that have dependencies
67/// (the dependencies are needed so that the main derive don't break the compilation when added) 71/// (the dependencies are needed so that the main derive don't break the compilation when added)
68const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ 72const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[
69 DeriveCompletion { label: "Clone", dependencies: &[] }, 73 DeriveDependencies { label: "Copy", dependencies: &["Clone"] },
70 DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, 74 DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] },
71 DeriveCompletion { label: "Debug", dependencies: &[] }, 75 DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
72 DeriveCompletion { label: "Default", dependencies: &[] }, 76 DeriveDependencies { label: "PartialOrd", dependencies: &["PartialEq"] },
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]; 77];
79 78
80#[cfg(test)] 79#[cfg(test)]
@@ -94,6 +93,7 @@ mod tests {
94 } 93 }
95 94
96 #[test] 95 #[test]
96 #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
97 fn empty_derive() { 97 fn empty_derive() {
98 check( 98 check(
99 r#"#[derive($0)] struct Test;"#, 99 r#"#[derive($0)] struct Test;"#,
@@ -112,6 +112,7 @@ mod tests {
112 } 112 }
113 113
114 #[test] 114 #[test]
115 #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
115 fn derive_with_input() { 116 fn derive_with_input() {
116 check( 117 check(
117 r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, 118 r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#,
@@ -129,6 +130,7 @@ mod tests {
129 } 130 }
130 131
131 #[test] 132 #[test]
133 #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
132 fn derive_with_input2() { 134 fn derive_with_input2() {
133 check( 135 check(
134 r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, 136 r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#,
diff --git a/crates/ide_completion/src/completions/attribute/lint.rs b/crates/ide_completion/src/completions/attribute/lint.rs
index 403630dce..ca99e9759 100644
--- a/crates/ide_completion/src/completions/attribute/lint.rs
+++ b/crates/ide_completion/src/completions/attribute/lint.rs
@@ -1,4 +1,5 @@
1//! Completion for lints 1//! Completion for lints
2use ide_db::helpers::generated_lints::Lint;
2use syntax::ast; 3use syntax::ast;
3 4
4use crate::{ 5use crate::{
@@ -11,7 +12,7 @@ pub(super) fn complete_lint(
11 acc: &mut Completions, 12 acc: &mut Completions,
12 ctx: &CompletionContext, 13 ctx: &CompletionContext,
13 derive_input: ast::TokenTree, 14 derive_input: ast::TokenTree,
14 lints_completions: &[LintCompletion], 15 lints_completions: &[Lint],
15) { 16) {
16 if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) { 17 if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) {
17 for lint_completion in lints_completions 18 for lint_completion in lints_completions
@@ -23,136 +24,13 @@ pub(super) fn complete_lint(
23 ctx.source_range(), 24 ctx.source_range(),
24 lint_completion.label, 25 lint_completion.label,
25 ); 26 );
26 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description); 27 item.kind(CompletionItemKind::Attribute)
28 .documentation(hir::Documentation::new(lint_completion.description.to_owned()));
27 item.add_to(acc) 29 item.add_to(acc)
28 } 30 }
29 } 31 }
30} 32}
31 33
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)] 34#[cfg(test)]
157mod tests { 35mod tests {
158 36
@@ -184,4 +62,13 @@ mod tests {
184 r#"#[allow(keyword_idents, deprecated)] struct Test;"#, 62 r#"#[allow(keyword_idents, deprecated)] struct Test;"#,
185 ) 63 )
186 } 64 }
65
66 #[test]
67 fn check_feature() {
68 check_edit(
69 "box_syntax",
70 r#"#[feature(box_$0)] struct Test;"#,
71 r#"#[feature(box_syntax)] struct Test;"#,
72 )
73 }
187} 74}
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index e0a7021fd..9552875c1 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -4,7 +4,7 @@ use either::Either;
4use hir::{HasVisibility, ScopeDef}; 4use hir::{HasVisibility, ScopeDef};
5use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
6 6
7use crate::{context::CompletionContext, Completions}; 7use crate::{context::CompletionContext, patterns::ImmediateLocation, Completions};
8 8
9/// Complete dot accesses, i.e. fields or methods. 9/// Complete dot accesses, i.e. fields or methods.
10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
@@ -13,12 +13,12 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
13 _ => return complete_undotted_self(acc, ctx), 13 _ => return complete_undotted_self(acc, ctx),
14 }; 14 };
15 15
16 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 16 let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) {
17 Some(ty) => ty, 17 Some(ty) => ty,
18 _ => return, 18 _ => return,
19 }; 19 };
20 20
21 if ctx.is_call { 21 if matches!(ctx.completion_location, Some(ImmediateLocation::MethodCall { .. })) {
22 cov_mark::hit!(test_no_struct_field_completion_for_method_call); 22 cov_mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else { 23 } else {
24 complete_fields(ctx, &receiver_ty, |field, ty| match field { 24 complete_fields(ctx, &receiver_ty, |field, ty| match field {
@@ -33,7 +33,7 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
33 if !ctx.config.enable_self_on_the_fly { 33 if !ctx.config.enable_self_on_the_fly {
34 return; 34 return;
35 } 35 }
36 if !ctx.is_trivial_path || ctx.is_path_disallowed() { 36 if !ctx.is_trivial_path() || ctx.is_path_disallowed() {
37 return; 37 return;
38 } 38 }
39 ctx.scope.process_all_names(&mut |name, def| { 39 ctx.scope.process_all_names(&mut |name, def| {
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index d72bf13d3..30b8d44bd 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -1,10 +1,10 @@
1//! Feature: completion with imports-on-the-fly 1//! Feature: completion with imports-on-the-fly
2//! 2//!
3//! When completing names in the current scope, proposes additional imports from other modules or crates, 3//! When completing names in the current scope, proposes additional imports from other modules or crates,
4//! if they can be qualified in the scope and their name contains all symbols from the completion input. 4//! if they can be qualified in the scope, and their name contains all symbols from the completion input.
5//! 5//!
6//! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent. 6//! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent.
7//! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the contaning is checked case-insensitively. 7//! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the containing is checked case-insensitively.
8//! 8//!
9//! ``` 9//! ```
10//! fn main() { 10//! fn main() {
@@ -23,8 +23,8 @@
23//! ``` 23//! ```
24//! 24//!
25//! Also completes associated items, that require trait imports. 25//! Also completes associated items, that require trait imports.
26//! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account. 26//! If any unresolved and/or partially-qualified path precedes the input, it will be taken into account.
27//! Currently, only the imports with their import path ending with the whole qialifier will be proposed 27//! Currently, only the imports with their import path ending with the whole qualifier will be proposed
28//! (no fuzzy matching for qualifier). 28//! (no fuzzy matching for qualifier).
29//! 29//!
30//! ``` 30//! ```
@@ -61,14 +61,14 @@
61//! } 61//! }
62//! ``` 62//! ```
63//! 63//!
64//! NOTE: currently, if an assoc item comes from a trait that's not currently imported and it also has an unresolved and/or partially-qualified path, 64//! NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path,
65//! no imports will be proposed. 65//! no imports will be proposed.
66//! 66//!
67//! .Fuzzy search details 67//! .Fuzzy search details
68//! 68//!
69//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only 69//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
70//! (i.e. in `HashMap` in the `std::collections::HashMap` path). 70//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
71//! For the same reasons, avoids searching for any path imports for inputs with their length less that 2 symbols 71//! For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols
72//! (but shows all associated items for any input length). 72//! (but shows all associated items for any input length).
73//! 73//!
74//! .Import configuration 74//! .Import configuration
@@ -79,18 +79,17 @@
79//! .LSP and performance implications 79//! .LSP and performance implications
80//! 80//!
81//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits` 81//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
82//! (case sensitive) resolve client capability in its client capabilities. 82//! (case-sensitive) resolve client capability in its client capabilities.
83//! This way the server is able to defer the costly computations, doing them for a selected completion item only. 83//! This way the server is able to defer the costly computations, doing them for a selected completion item only.
84//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones, 84//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
85//! which might be slow ergo the feature is automatically disabled. 85//! which might be slow ergo the feature is automatically disabled.
86//! 86//!
87//! .Feature toggle 87//! .Feature toggle
88//! 88//!
89//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag. 89//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag.
90//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 90//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding
91//! capability enabled. 91//! capability enabled.
92 92
93use hir::ModPath;
94use ide_db::helpers::{ 93use ide_db::helpers::{
95 import_assets::{ImportAssets, ImportCandidate}, 94 import_assets::{ImportAssets, ImportCandidate},
96 insert_use::ImportScope, 95 insert_use::ImportScope,
@@ -161,13 +160,13 @@ pub(crate) fn position_for_import<'a>(
161) -> Option<&'a SyntaxNode> { 160) -> Option<&'a SyntaxNode> {
162 Some(match import_candidate { 161 Some(match import_candidate {
163 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(), 162 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
164 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(), 163 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(),
165 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(), 164 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
166 None => ctx 165 None => ctx
167 .name_ref_syntax 166 .name_ref_syntax
168 .as_ref() 167 .as_ref()
169 .map(|name_ref| name_ref.syntax()) 168 .map(|name_ref| name_ref.syntax())
170 .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax())) 169 .or_else(|| ctx.path_qual().map(|path| path.syntax()))
171 .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?, 170 .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?,
172 }) 171 })
173} 172}
@@ -190,7 +189,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
190 }; 189 };
191 let assets_for_path = ImportAssets::for_fuzzy_path( 190 let assets_for_path = ImportAssets::for_fuzzy_path(
192 current_module, 191 current_module,
193 ctx.path_qual.clone(), 192 ctx.path_qual().cloned(),
194 fuzzy_name, 193 fuzzy_name,
195 &ctx.sema, 194 &ctx.sema,
196 approximate_node, 195 approximate_node,
@@ -208,7 +207,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
208} 207}
209 208
210fn compute_fuzzy_completion_order_key( 209fn compute_fuzzy_completion_order_key(
211 proposed_mod_path: &ModPath, 210 proposed_mod_path: &hir::ModPath,
212 user_input_lowercased: &str, 211 user_input_lowercased: &str,
213) -> usize { 212) -> usize {
214 cov_mark::hit!(certain_fuzzy_order_test); 213 cov_mark::hit!(certain_fuzzy_order_test);
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 1a7a484a4..ba13d3707 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -5,8 +5,8 @@ use std::iter;
5use syntax::{SyntaxKind, T}; 5use syntax::{SyntaxKind, T};
6 6
7use crate::{ 7use crate::{
8 patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind, 8 context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, CompletionItem,
9 CompletionKind, Completions, 9 CompletionItemKind, CompletionKind, Completions,
10}; 10};
11 11
12pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { 12pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
@@ -19,11 +19,12 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
19 }; 19 };
20 20
21 if ctx.use_item_syntax.is_some() { 21 if ctx.use_item_syntax.is_some() {
22 if ctx.path_qual.is_none() { 22 let qual = ctx.path_qual();
23 if qual.is_none() {
23 kw_completion("crate::").add_to(acc); 24 kw_completion("crate::").add_to(acc);
24 } 25 }
25 kw_completion("self").add_to(acc); 26 kw_completion("self").add_to(acc);
26 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) 27 if iter::successors(qual.cloned(), |p| p.qualifier())
27 .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) 28 .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
28 { 29 {
29 kw_completion("super::").add_to(acc); 30 kw_completion("super::").add_to(acc);
@@ -127,8 +128,15 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
127 add_keyword("mut", "mut "); 128 add_keyword("mut", "mut ");
128 } 129 }
129 130
130 if ctx.in_loop_body { 131 let (can_be_stmt, in_loop_body) = match ctx.path_context {
131 if ctx.can_be_stmt { 132 Some(PathCompletionContext {
133 is_trivial_path: true, can_be_stmt, in_loop_body, ..
134 }) => (can_be_stmt, in_loop_body),
135 _ => return,
136 };
137
138 if in_loop_body {
139 if can_be_stmt {
132 add_keyword("continue", "continue;"); 140 add_keyword("continue", "continue;");
133 add_keyword("break", "break;"); 141 add_keyword("break", "break;");
134 } else { 142 } else {
@@ -137,9 +145,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
137 } 145 }
138 } 146 }
139 147
140 if !ctx.is_trivial_path {
141 return;
142 }
143 let fn_def = match &ctx.function_def { 148 let fn_def = match &ctx.function_def {
144 Some(it) => it, 149 Some(it) => it,
145 None => return, 150 None => return,
@@ -147,7 +152,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
147 152
148 add_keyword( 153 add_keyword(
149 "return", 154 "return",
150 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { 155 match (can_be_stmt, fn_def.ret_type().is_some()) {
151 (true, true) => "return $0;", 156 (true, true) => "return $0;",
152 (true, false) => "return;", 157 (true, false) => "return;",
153 (false, true) => "return $0", 158 (false, true) => "return $0",
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs
deleted file mode 100644
index 781b96ff1..000000000
--- a/crates/ide_completion/src/completions/macro_in_item_position.rs
+++ /dev/null
@@ -1,48 +0,0 @@
1//! Completes macro invocations used in item position.
2
3use crate::{CompletionContext, Completions};
4
5// Ideally this should be removed and moved into `(un)qualified_path` respectively
6pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
7 // Show only macros in top level.
8 if !ctx.expects_item() {
9 return;
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 })
21}
22
23#[cfg(test)]
24mod tests {
25 use expect_test::{expect, Expect};
26
27 use crate::{test_utils::completion_list, CompletionKind};
28
29 fn check(ra_fixture: &str, expect: Expect) {
30 let actual = completion_list(ra_fixture, CompletionKind::Reference);
31 expect.assert_eq(&actual)
32 }
33
34 #[test]
35 fn completes_macros_as_item() {
36 check(
37 r#"
38macro_rules! foo { () => {} }
39fn foo() {}
40
41$0
42"#,
43 expect![[r#"
44 ma foo!(…) macro_rules! foo
45 "#]],
46 )
47 }
48}
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 8a728c67e..1daa8595a 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -39,7 +39,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
39 | hir::ModuleDef::Module(..) => refutable, 39 | hir::ModuleDef::Module(..) => refutable,
40 _ => false, 40 _ => false,
41 }, 41 },
42 hir::ScopeDef::MacroDef(_) => true, 42 hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(),
43 hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() { 43 hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() {
44 Some(hir::Adt::Struct(strukt)) => { 44 Some(hir::Adt::Struct(strukt)) => {
45 acc.add_struct_pat(ctx, strukt, Some(name.clone())); 45 acc.add_struct_pat(ctx, strukt, Some(name.clone()));
@@ -102,6 +102,28 @@ fn foo() {
102 } 102 }
103 103
104 #[test] 104 #[test]
105 fn does_not_complete_non_fn_macros() {
106 check(
107 r#"
108macro_rules! m { ($e:expr) => { $e } }
109enum E { X }
110
111#[rustc_builtin_macro]
112macro Clone {}
113
114fn foo() {
115 match E::X { $0 }
116}
117"#,
118 expect![[r#"
119 ev E::X ()
120 en E
121 ma m!(…) macro_rules! m
122 "#]],
123 );
124 }
125
126 #[test]
105 fn completes_in_simple_macro_call() { 127 fn completes_in_simple_macro_call() {
106 check( 128 check(
107 r#" 129 r#"
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 86bbb58e2..9f98b21be 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -24,7 +24,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
24 } 24 }
25 25
26 let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location { 26 let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location {
27 Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false), 27 Some(ImmediateLocation::MethodCall { receiver: Some(it), .. }) => (it, false),
28 Some(ImmediateLocation::FieldAccess { 28 Some(ImmediateLocation::FieldAccess {
29 receiver: Some(it), 29 receiver: Some(it),
30 receiver_is_ambiguous_float_literal, 30 receiver_is_ambiguous_float_literal,
@@ -34,7 +34,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
34 34
35 let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal); 35 let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
36 36
37 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 37 let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) {
38 Some(it) => it, 38 Some(it) => it,
39 None => return, 39 None => return,
40 }; 40 };
@@ -50,7 +50,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
50 postfix_snippet( 50 postfix_snippet(
51 ctx, 51 ctx,
52 cap, 52 cap,
53 &dot_receiver, 53 dot_receiver,
54 "ifl", 54 "ifl",
55 "if let Ok {}", 55 "if let Ok {}",
56 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text), 56 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text),
@@ -60,7 +60,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
60 postfix_snippet( 60 postfix_snippet(
61 ctx, 61 ctx,
62 cap, 62 cap,
63 &dot_receiver, 63 dot_receiver,
64 "while", 64 "while",
65 "while let Ok {}", 65 "while let Ok {}",
66 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text), 66 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text),
@@ -71,7 +71,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
71 postfix_snippet( 71 postfix_snippet(
72 ctx, 72 ctx,
73 cap, 73 cap,
74 &dot_receiver, 74 dot_receiver,
75 "ifl", 75 "ifl",
76 "if let Some {}", 76 "if let Some {}",
77 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text), 77 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text),
@@ -81,7 +81,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
81 postfix_snippet( 81 postfix_snippet(
82 ctx, 82 ctx,
83 cap, 83 cap,
84 &dot_receiver, 84 dot_receiver,
85 "while", 85 "while",
86 "while let Some {}", 86 "while let Some {}",
87 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text), 87 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text),
@@ -93,7 +93,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
93 postfix_snippet( 93 postfix_snippet(
94 ctx, 94 ctx,
95 cap, 95 cap,
96 &dot_receiver, 96 dot_receiver,
97 "if", 97 "if",
98 "if expr {}", 98 "if expr {}",
99 &format!("if {} {{\n $0\n}}", receiver_text), 99 &format!("if {} {{\n $0\n}}", receiver_text),
@@ -102,22 +102,22 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
102 postfix_snippet( 102 postfix_snippet(
103 ctx, 103 ctx,
104 cap, 104 cap,
105 &dot_receiver, 105 dot_receiver,
106 "while", 106 "while",
107 "while expr {}", 107 "while expr {}",
108 &format!("while {} {{\n $0\n}}", receiver_text), 108 &format!("while {} {{\n $0\n}}", receiver_text),
109 ) 109 )
110 .add_to(acc); 110 .add_to(acc);
111 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)) 111 postfix_snippet(ctx, cap, dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
112 .add_to(acc); 112 .add_to(acc);
113 } 113 }
114 114
115 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)) 115 postfix_snippet(ctx, cap, dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
116 .add_to(acc); 116 .add_to(acc);
117 postfix_snippet( 117 postfix_snippet(
118 ctx, 118 ctx,
119 cap, 119 cap,
120 &dot_receiver, 120 dot_receiver,
121 "refm", 121 "refm",
122 "&mut expr", 122 "&mut expr",
123 &format!("&mut {}", receiver_text), 123 &format!("&mut {}", receiver_text),
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs
index 0dcb3e898..2dc13c293 100644
--- a/crates/ide_completion/src/completions/postfix/format_like.rs
+++ b/crates/ide_completion/src/completions/postfix/format_like.rs
@@ -4,15 +4,15 @@
4// 4//
5// The following postfix snippets are available: 5// The following postfix snippets are available:
6// 6//
7// - `format` -> `format!(...)` 7// * `format` -> `format!(...)`
8// - `panic` -> `panic!(...)` 8// * `panic` -> `panic!(...)`
9// - `println` -> `println!(...)` 9// * `println` -> `println!(...)`
10// - `log`: 10// * `log`:
11// + `logd` -> `log::debug!(...)` 11// ** `logd` -> `log::debug!(...)`
12// + `logt` -> `log::trace!(...)` 12// ** `logt` -> `log::trace!(...)`
13// + `logi` -> `log::info!(...)` 13// ** `logi` -> `log::info!(...)`
14// + `logw` -> `log::warn!(...)` 14// ** `logw` -> `log::warn!(...)`
15// + `loge` -> `log::error!(...)` 15// ** `loge` -> `log::error!(...)`
16// 16//
17// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[] 17// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[]
18 18
@@ -53,7 +53,7 @@ pub(crate) fn add_format_like_completions(
53 for (label, macro_name) in KINDS { 53 for (label, macro_name) in KINDS {
54 let snippet = parser.into_suggestion(macro_name); 54 let snippet = parser.into_suggestion(macro_name);
55 55
56 postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc); 56 postfix_snippet(ctx, cap, dot_receiver, label, macro_name, &snippet).add_to(acc);
57 } 57 }
58 } 58 }
59} 59}
@@ -91,7 +91,7 @@ enum State {
91impl FormatStrParser { 91impl FormatStrParser {
92 pub(crate) fn new(input: String) -> Self { 92 pub(crate) fn new(input: String) -> Self {
93 Self { 93 Self {
94 input: input, 94 input,
95 output: String::new(), 95 output: String::new(),
96 extracted_expressions: Vec::new(), 96 extracted_expressions: Vec::new(),
97 state: State::NotExpr, 97 state: State::NotExpr,
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index de58ce1cd..6083537b7 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -7,25 +7,28 @@ use syntax::AstNode;
7use crate::{CompletionContext, Completions}; 7use crate::{CompletionContext, Completions};
8 8
9pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { 9pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 if ctx.is_path_disallowed() || ctx.expects_item() { 10 if ctx.is_path_disallowed() {
11 return; 11 return;
12 } 12 }
13 let path = match &ctx.path_qual { 13 let path = match ctx.path_qual() {
14 Some(path) => path.clone(), 14 Some(path) => path,
15 None => return, 15 None => return,
16 }; 16 };
17 17
18 let resolution = match ctx.sema.resolve_path(&path) { 18 let resolution = match ctx.sema.resolve_path(path) {
19 Some(res) => res, 19 Some(res) => res,
20 None => return, 20 None => return,
21 }; 21 };
22 let context_module = ctx.scope.module(); 22 let context_module = ctx.scope.module();
23 if ctx.expects_assoc_item() { 23
24 if ctx.expects_item() || ctx.expects_assoc_item() {
24 if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { 25 if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
25 let module_scope = module.scope(ctx.db, context_module); 26 let module_scope = module.scope(ctx.db, context_module);
26 for (name, def) in module_scope { 27 for (name, def) in module_scope {
27 if let hir::ScopeDef::MacroDef(macro_def) = def { 28 if let hir::ScopeDef::MacroDef(macro_def) = def {
28 acc.add_macro(ctx, Some(name.clone()), macro_def); 29 if macro_def.is_fn_like() {
30 acc.add_macro(ctx, Some(name.clone()), macro_def);
31 }
29 } 32 }
30 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { 33 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
31 acc.add_resolution(ctx, name, &def); 34 acc.add_resolution(ctx, name, &def);
@@ -57,6 +60,13 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
57 } 60 }
58 } 61 }
59 62
63 if let hir::ScopeDef::MacroDef(macro_def) = def {
64 if !macro_def.is_fn_like() {
65 // Don't suggest attribute macros and derives.
66 continue;
67 }
68 }
69
60 acc.add_resolution(ctx, name, &def); 70 acc.add_resolution(ctx, name, &def);
61 } 71 }
62 } 72 }
@@ -198,6 +208,36 @@ mod tests {
198 } 208 }
199 209
200 #[test] 210 #[test]
211 fn dont_complete_values_in_type_pos() {
212 check(
213 r#"
214const FOO: () = ();
215static BAR: () = ();
216struct Baz;
217fn foo() {
218 let _: self::$0;
219}
220"#,
221 expect![[r#"
222 st Baz
223 "#]],
224 );
225 }
226
227 #[test]
228 fn dont_complete_enum_variants_in_type_pos() {
229 check(
230 r#"
231enum Foo { Bar }
232fn foo() {
233 let _: Foo::$0;
234}
235"#,
236 expect![[r#""#]],
237 );
238 }
239
240 #[test]
201 fn dont_complete_current_use_in_braces_with_glob() { 241 fn dont_complete_current_use_in_braces_with_glob() {
202 check( 242 check(
203 r#" 243 r#"
@@ -617,6 +657,32 @@ fn main() { let _ = crate::$0 }
617 } 657 }
618 658
619 #[test] 659 #[test]
660 fn does_not_complete_non_fn_macros() {
661 check(
662 r#"
663mod m {
664 #[rustc_builtin_macro]
665 pub macro Clone {}
666}
667
668fn f() {m::$0}
669"#,
670 expect![[r#""#]],
671 );
672 check(
673 r#"
674mod m {
675 #[rustc_builtin_macro]
676 pub macro bench {}
677}
678
679fn f() {m::$0}
680"#,
681 expect![[r#""#]],
682 );
683 }
684
685 #[test]
620 fn completes_in_assoc_item_list() { 686 fn completes_in_assoc_item_list() {
621 check( 687 check(
622 r#" 688 r#"
@@ -631,17 +697,17 @@ impl MyStruct {
631"#, 697"#,
632 expect![[r##" 698 expect![[r##"
633 md bar 699 md bar
634 ma foo! #[macro_export] macro_rules! foo 700 ma foo!(…) #[macro_export] macro_rules! foo
635 "##]], 701 "##]],
636 ); 702 );
637 } 703 }
638 704
639 #[test] 705 #[test]
640 #[ignore] // FIXME doesn't complete anything atm
641 fn completes_in_item_list() { 706 fn completes_in_item_list() {
642 check( 707 check(
643 r#" 708 r#"
644struct MyStruct {} 709struct MyStruct {}
710#[macro_export]
645macro_rules! foo {} 711macro_rules! foo {}
646mod bar {} 712mod bar {}
647 713
@@ -649,7 +715,7 @@ crate::$0
649"#, 715"#,
650 expect![[r#" 716 expect![[r#"
651 md bar 717 md bar
652 ma foo! macro_rules! foo 718 ma foo!(…) #[macro_export] macro_rules! foo
653 "#]], 719 "#]],
654 ) 720 )
655 } 721 }
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index 6e6a6eb92..b9862de67 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -3,8 +3,8 @@
3use ide_db::helpers::SnippetCap; 3use ide_db::helpers::SnippetCap;
4 4
5use crate::{ 5use crate::{
6 item::Builder, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 6 context::PathCompletionContext, item::Builder, CompletionContext, CompletionItem,
7 Completions, 7 CompletionItemKind, CompletionKind, Completions,
8}; 8};
9 9
10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { 10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
@@ -14,15 +14,21 @@ 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_def.is_some()) { 17 if ctx.function_def.is_none() {
18 return; 18 return;
19 } 19 }
20
21 let can_be_stmt = match ctx.path_context {
22 Some(PathCompletionContext { is_trivial_path: true, can_be_stmt, .. }) => can_be_stmt,
23 _ => return,
24 };
25
20 let cap = match ctx.config.snippet_cap { 26 let cap = match ctx.config.snippet_cap {
21 Some(it) => it, 27 Some(it) => it,
22 None => return, 28 None => return,
23 }; 29 };
24 30
25 if ctx.can_be_stmt { 31 if can_be_stmt {
26 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); 32 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
27 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); 33 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
28 } 34 }
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index 968c0254d..a60e5f43c 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -34,20 +34,13 @@
34use hir::{self, HasAttrs, HasSource}; 34use hir::{self, HasAttrs, HasSource};
35use ide_db::{traits::get_missing_assoc_items, SymbolKind}; 35use ide_db::{traits::get_missing_assoc_items, SymbolKind};
36use syntax::{ 36use syntax::{
37 ast::{self, edit, Impl}, 37 ast::{self, edit},
38 display::function_declaration, 38 display::function_declaration,
39 AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T, 39 AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, T,
40}; 40};
41use text_edit::TextEdit; 41use text_edit::TextEdit;
42 42
43use crate::{ 43use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
44 CompletionContext,
45 CompletionItem,
46 CompletionItemKind,
47 CompletionKind,
48 Completions,
49 // display::function_declaration,
50};
51 44
52#[derive(Debug, PartialEq, Eq)] 45#[derive(Debug, PartialEq, Eq)]
53enum ImplCompletionKind { 46enum ImplCompletionKind {
@@ -58,7 +51,7 @@ enum ImplCompletionKind {
58} 51}
59 52
60pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 53pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
61 if let Some((kind, trigger, impl_def)) = completion_match(ctx) { 54 if let Some((kind, trigger, impl_def)) = completion_match(ctx.token.clone()) {
62 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item { 55 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item {
63 hir::AssocItem::Function(fn_item) 56 hir::AssocItem::Function(fn_item)
64 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => 57 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
@@ -80,8 +73,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
80 } 73 }
81} 74}
82 75
83fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> { 76fn completion_match(mut token: SyntaxToken) -> Option<(ImplCompletionKind, SyntaxNode, ast::Impl)> {
84 let mut token = ctx.token.clone();
85 // For keyword without name like `impl .. { fn $0 }`, the current position is inside 77 // For keyword without name like `impl .. { fn $0 }`, the current position is inside
86 // the whitespace token, which is outside `FN` syntax node. 78 // the whitespace token, which is outside `FN` syntax node.
87 // We need to follow the previous token in this case. 79 // We need to follow the previous token in this case.
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index bd955aa85..952f052a1 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -1,30 +1,32 @@
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::{ast, AstNode};
4 5
5use crate::{CompletionContext, Completions}; 6use crate::{patterns::ImmediateLocation, CompletionContext, Completions};
6 7
7pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 8pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
8 if !ctx.is_trivial_path { 9 if ctx.is_path_disallowed() || !ctx.is_trivial_path() {
9 return;
10 }
11 if ctx.is_path_disallowed() || ctx.expects_item() {
12 return; 10 return;
13 } 11 }
14 12
15 if ctx.expects_assoc_item() { 13 if ctx.expects_item() || ctx.expects_assoc_item() {
16 ctx.scope.process_all_names(&mut |name, def| { 14 // only show macros in {Assoc}ItemList
17 if let ScopeDef::MacroDef(macro_def) = def { 15 ctx.scope.process_all_names(&mut |name, res| {
18 acc.add_macro(ctx, Some(name.clone()), macro_def); 16 if let hir::ScopeDef::MacroDef(mac) = res {
17 if mac.is_fn_like() {
18 acc.add_macro(ctx, Some(name.clone()), mac);
19 }
19 } 20 }
20 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { 21 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
21 acc.add_resolution(ctx, name, &def); 22 acc.add_resolution(ctx, name, &res);
22 } 23 }
23 }); 24 });
24 return; 25 return;
25 } 26 }
26 27
27 if ctx.expects_use_tree() { 28 if ctx.expects_use_tree() {
29 // only show modules in a fresh UseTree
28 cov_mark::hit!(only_completes_modules_in_import); 30 cov_mark::hit!(only_completes_modules_in_import);
29 ctx.scope.process_all_names(&mut |name, res| { 31 ctx.scope.process_all_names(&mut |name, res| {
30 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { 32 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
@@ -42,12 +44,32 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
42 }); 44 });
43 } 45 }
44 46
47 if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location {
48 if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast) {
49 if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) =
50 ctx.sema.resolve_path(&path_seg.parent_path())
51 {
52 trait_.items(ctx.sema.db).into_iter().for_each(|it| {
53 if let hir::AssocItem::TypeAlias(alias) = it {
54 acc.add_type_alias_with_eq(ctx, alias)
55 }
56 });
57 }
58 }
59 }
60
45 ctx.scope.process_all_names(&mut |name, res| { 61 ctx.scope.process_all_names(&mut |name, res| {
46 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { 62 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
47 cov_mark::hit!(skip_lifetime_completion); 63 cov_mark::hit!(skip_lifetime_completion);
48 return; 64 return;
49 } 65 }
50 acc.add_resolution(ctx, name, &res); 66 let add_resolution = match res {
67 ScopeDef::MacroDef(mac) => mac.is_fn_like(),
68 _ => true,
69 };
70 if add_resolution {
71 acc.add_resolution(ctx, name, &res);
72 }
51 }); 73 });
52} 74}
53 75
@@ -70,6 +92,28 @@ mod tests {
70 } 92 }
71 93
72 #[test] 94 #[test]
95 fn dont_complete_values_in_type_pos() {
96 check(
97 r#"
98const FOO: () = ();
99static BAR: () = ();
100enum Foo {
101 Bar
102}
103struct Baz;
104fn foo() {
105 let local = ();
106 let _: $0;
107}
108"#,
109 expect![[r#"
110 en Foo
111 st Baz
112 "#]],
113 );
114 }
115
116 #[test]
73 fn only_completes_modules_in_import() { 117 fn only_completes_modules_in_import() {
74 cov_mark::check!(only_completes_modules_in_import); 118 cov_mark::check!(only_completes_modules_in_import);
75 check( 119 check(
@@ -340,7 +384,6 @@ fn x() -> $0
340"#, 384"#,
341 expect![[r#" 385 expect![[r#"
342 st Foo 386 st Foo
343 fn x() fn()
344 "#]], 387 "#]],
345 ); 388 );
346 } 389 }
@@ -392,7 +435,6 @@ pub mod prelude {
392} 435}
393"#, 436"#,
394 expect![[r#" 437 expect![[r#"
395 fn foo() fn()
396 md std 438 md std
397 st Option 439 st Option
398 "#]], 440 "#]],
@@ -428,6 +470,44 @@ mod macros {
428 } 470 }
429 471
430 #[test] 472 #[test]
473 fn does_not_complete_non_fn_macros() {
474 check(
475 r#"
476#[rustc_builtin_macro]
477pub macro Clone {}
478
479fn f() {$0}
480"#,
481 expect![[r#"
482 fn f() fn()
483 "#]],
484 );
485 check(
486 r#"
487#[rustc_builtin_macro]
488pub macro Clone {}
489
490struct S;
491impl S {
492 $0
493}
494"#,
495 expect![[r#""#]],
496 );
497 check(
498 r#"
499#[rustc_builtin_macro]
500pub macro bench {}
501
502fn f() {$0}
503"#,
504 expect![[r#"
505 fn f() fn()
506 "#]],
507 );
508 }
509
510 #[test]
431 fn completes_std_prelude_if_core_is_defined() { 511 fn completes_std_prelude_if_core_is_defined() {
432 check( 512 check(
433 r#" 513 r#"
@@ -449,7 +529,6 @@ pub mod prelude {
449} 529}
450"#, 530"#,
451 expect![[r#" 531 expect![[r#"
452 fn foo() fn()
453 md std 532 md std
454 md core 533 md core
455 st String 534 st String
@@ -510,7 +589,6 @@ macro_rules! foo { () => {} }
510fn main() { let x: $0 } 589fn main() { let x: $0 }
511"#, 590"#,
512 expect![[r#" 591 expect![[r#"
513 fn main() fn()
514 ma foo!(…) macro_rules! foo 592 ma foo!(…) macro_rules! foo
515 "#]], 593 "#]],
516 ); 594 );
@@ -693,12 +771,11 @@ impl MyStruct {
693"#, 771"#,
694 expect![[r#" 772 expect![[r#"
695 md bar 773 md bar
696 ma foo! macro_rules! foo 774 ma foo!(…) macro_rules! foo
697 "#]], 775 "#]],
698 ) 776 )
699 } 777 }
700 778
701 // FIXME: The completions here currently come from `macro_in_item_position`, but they shouldn't
702 #[test] 779 #[test]
703 fn completes_in_item_list() { 780 fn completes_in_item_list() {
704 check( 781 check(
@@ -715,4 +792,21 @@ $0
715 "#]], 792 "#]],
716 ) 793 )
717 } 794 }
795
796 #[test]
797 fn completes_assoc_types_in_dynimpl_trait() {
798 check(
799 r#"
800trait Foo {
801 type Bar;
802}
803
804fn foo(_: impl Foo<B$0>) {}
805"#,
806 expect![[r#"
807 ta Bar = type Bar;
808 tt Foo
809 "#]],
810 );
811 }
718} 812}
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 6f685c02f..4c3929a26 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -29,6 +29,34 @@ pub(crate) enum PatternRefutability {
29 Irrefutable, 29 Irrefutable,
30} 30}
31 31
32#[derive(Debug)]
33pub(super) enum PathKind {
34 Expr,
35 Type,
36}
37
38#[derive(Debug)]
39pub(crate) struct PathCompletionContext {
40 /// If this is a call with () already there
41 call_kind: Option<CallKind>,
42 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
43 pub(super) is_trivial_path: bool,
44 /// If not a trivial path, the prefix (qualifier).
45 pub(super) qualifier: Option<ast::Path>,
46 pub(super) kind: Option<PathKind>,
47 /// Whether the path segment has type args or not.
48 pub(super) has_type_args: bool,
49 /// `true` if we are a statement or a last expr in the block.
50 pub(super) can_be_stmt: bool,
51 pub(super) in_loop_body: bool,
52}
53
54#[derive(Copy, Clone, Debug, PartialEq, Eq)]
55pub(crate) enum CallKind {
56 Pat,
57 Mac,
58 Expr,
59}
32/// `CompletionContext` is created early during completion to figure out, where 60/// `CompletionContext` is created early during completion to figure out, where
33/// exactly is the cursor, syntax-wise. 61/// exactly is the cursor, syntax-wise.
34#[derive(Debug)] 62#[derive(Debug)]
@@ -45,14 +73,13 @@ pub(crate) struct CompletionContext<'a> {
45 pub(super) krate: Option<hir::Crate>, 73 pub(super) krate: Option<hir::Crate>,
46 pub(super) expected_name: Option<NameOrNameRef>, 74 pub(super) expected_name: Option<NameOrNameRef>,
47 pub(super) expected_type: Option<Type>, 75 pub(super) expected_type: Option<Type>,
48 pub(super) name_ref_syntax: Option<ast::NameRef>,
49
50 pub(super) use_item_syntax: Option<ast::Use>,
51 76
52 /// The parent function of the cursor position if it exists. 77 /// The parent function of the cursor position if it exists.
53 pub(super) function_def: Option<ast::Fn>, 78 pub(super) function_def: Option<ast::Fn>,
54 /// The parent impl of the cursor position if it exists. 79 /// The parent impl of the cursor position if it exists.
55 pub(super) impl_def: Option<ast::Impl>, 80 pub(super) impl_def: Option<ast::Impl>,
81 pub(super) name_ref_syntax: Option<ast::NameRef>,
82 pub(super) use_item_syntax: Option<ast::Use>,
56 83
57 // potentially set if we are completing a lifetime 84 // potentially set if we are completing a lifetime
58 pub(super) lifetime_syntax: Option<ast::Lifetime>, 85 pub(super) lifetime_syntax: Option<ast::Lifetime>,
@@ -67,29 +94,12 @@ pub(crate) struct CompletionContext<'a> {
67 pub(super) completion_location: Option<ImmediateLocation>, 94 pub(super) completion_location: Option<ImmediateLocation>,
68 pub(super) prev_sibling: Option<ImmediatePrevSibling>, 95 pub(super) prev_sibling: Option<ImmediatePrevSibling>,
69 pub(super) attribute_under_caret: Option<ast::Attr>, 96 pub(super) attribute_under_caret: Option<ast::Attr>,
97 pub(super) previous_token: Option<SyntaxToken>,
70 98
71 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 99 pub(super) path_context: Option<PathCompletionContext>,
72 pub(super) active_parameter: Option<ActiveParameter>, 100 pub(super) active_parameter: Option<ActiveParameter>,
73 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
74 pub(super) is_trivial_path: bool,
75 /// If not a trivial path, the prefix (qualifier).
76 pub(super) path_qual: Option<ast::Path>,
77 /// `true` if we are a statement or a last expr in the block.
78 pub(super) can_be_stmt: bool,
79 /// `true` if we expect an expression at the cursor position.
80 pub(super) is_expr: bool,
81 /// If this is a call (method or function) in particular, i.e. the () are already there.
82 pub(super) is_call: bool,
83 /// Like `is_call`, but for tuple patterns.
84 pub(super) is_pattern_call: bool,
85 /// If this is a macro call, i.e. the () are already there.
86 pub(super) is_macro_call: bool,
87 pub(super) is_path_type: bool,
88 pub(super) has_type_args: bool,
89 pub(super) locals: Vec<(String, Local)>, 101 pub(super) locals: Vec<(String, Local)>,
90 102
91 pub(super) previous_token: Option<SyntaxToken>,
92 pub(super) in_loop_body: bool,
93 pub(super) incomplete_let: bool, 103 pub(super) incomplete_let: bool,
94 104
95 no_completion_required: bool, 105 no_completion_required: bool,
@@ -136,36 +146,27 @@ impl<'a> CompletionContext<'a> {
136 original_token, 146 original_token,
137 token, 147 token,
138 krate, 148 krate,
139 lifetime_allowed: false,
140 expected_name: None, 149 expected_name: None,
141 expected_type: None, 150 expected_type: None,
151 function_def: None,
152 impl_def: None,
142 name_ref_syntax: None, 153 name_ref_syntax: None,
154 use_item_syntax: None,
143 lifetime_syntax: None, 155 lifetime_syntax: None,
144 lifetime_param_syntax: None, 156 lifetime_param_syntax: None,
145 function_def: None, 157 lifetime_allowed: false,
146 use_item_syntax: None,
147 impl_def: None,
148 active_parameter: ActiveParameter::at(db, position),
149 is_label_ref: false, 158 is_label_ref: false,
150 is_param: false,
151 is_pat_or_const: None, 159 is_pat_or_const: None,
152 is_trivial_path: false, 160 is_param: false,
153 path_qual: None,
154 can_be_stmt: false,
155 is_expr: false,
156 is_call: false,
157 is_pattern_call: false,
158 is_macro_call: false,
159 is_path_type: false,
160 has_type_args: false,
161 previous_token: None,
162 in_loop_body: false,
163 completion_location: None, 161 completion_location: None,
164 prev_sibling: None, 162 prev_sibling: None,
165 no_completion_required: false,
166 incomplete_let: false,
167 attribute_under_caret: None, 163 attribute_under_caret: None,
164 previous_token: None,
165 path_context: None,
166 active_parameter: ActiveParameter::at(db, position),
168 locals, 167 locals,
168 incomplete_let: false,
169 no_completion_required: false,
169 }; 170 };
170 171
171 let mut original_file = original_file.syntax().clone(); 172 let mut original_file = original_file.syntax().clone();
@@ -250,14 +251,14 @@ impl<'a> CompletionContext<'a> {
250 pub(crate) fn has_dot_receiver(&self) -> bool { 251 pub(crate) fn has_dot_receiver(&self) -> bool {
251 matches!( 252 matches!(
252 &self.completion_location, 253 &self.completion_location,
253 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver }) 254 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver,.. })
254 if receiver.is_some() 255 if receiver.is_some()
255 ) 256 )
256 } 257 }
257 258
258 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { 259 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
259 match &self.completion_location { 260 match &self.completion_location {
260 Some(ImmediateLocation::MethodCall { receiver }) 261 Some(ImmediateLocation::MethodCall { receiver, .. })
261 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), 262 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(),
262 _ => None, 263 _ => None,
263 } 264 }
@@ -275,11 +276,6 @@ impl<'a> CompletionContext<'a> {
275 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 276 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
276 } 277 }
277 278
278 // fn expects_value(&self) -> bool {
279 pub(crate) fn expects_expression(&self) -> bool {
280 self.is_expr
281 }
282
283 pub(crate) fn has_block_expr_parent(&self) -> bool { 279 pub(crate) fn has_block_expr_parent(&self) -> bool {
284 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) 280 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
285 } 281 }
@@ -316,6 +312,26 @@ impl<'a> CompletionContext<'a> {
316 ) || self.attribute_under_caret.is_some() 312 ) || self.attribute_under_caret.is_some()
317 } 313 }
318 314
315 pub(crate) fn expects_expression(&self) -> bool {
316 matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Expr), .. }))
317 }
318
319 pub(crate) fn expects_type(&self) -> bool {
320 matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), .. }))
321 }
322
323 pub(crate) fn path_call_kind(&self) -> Option<CallKind> {
324 self.path_context.as_ref().and_then(|it| it.call_kind)
325 }
326
327 pub(crate) fn is_trivial_path(&self) -> bool {
328 matches!(self.path_context, Some(PathCompletionContext { is_trivial_path: true, .. }))
329 }
330
331 pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
332 self.path_context.as_ref().and_then(|it| it.qualifier.as_ref())
333 }
334
319 fn fill_impl_def(&mut self) { 335 fn fill_impl_def(&mut self) {
320 self.impl_def = self 336 self.impl_def = self
321 .sema 337 .sema
@@ -364,7 +380,7 @@ impl<'a> CompletionContext<'a> {
364 (|| { 380 (|| {
365 let expr_field = self.token.prev_sibling_or_token()? 381 let expr_field = self.token.prev_sibling_or_token()?
366 .into_node() 382 .into_node()
367 .and_then(|node| ast::RecordExprField::cast(node))?; 383 .and_then(ast::RecordExprField::cast)?;
368 let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?; 384 let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?;
369 Some(( 385 Some((
370 Some(ty), 386 Some(ty),
@@ -441,7 +457,6 @@ impl<'a> CompletionContext<'a> {
441 let for_is_prev2 = for_is_prev2(syntax_element.clone()); 457 let for_is_prev2 = for_is_prev2(syntax_element.clone());
442 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2 458 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2
443 }; 459 };
444 self.in_loop_body = is_in_loop_body(syntax_element.clone());
445 460
446 self.incomplete_let = 461 self.incomplete_let =
447 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { 462 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
@@ -452,7 +467,7 @@ impl<'a> CompletionContext<'a> {
452 self.expected_type = expected_type; 467 self.expected_type = expected_type;
453 self.expected_name = expected_name; 468 self.expected_name = expected_name;
454 469
455 let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { 470 let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
456 Some(it) => it, 471 Some(it) => it,
457 None => return, 472 None => return,
458 }; 473 };
@@ -549,10 +564,6 @@ impl<'a> CompletionContext<'a> {
549 self.name_ref_syntax = 564 self.name_ref_syntax =
550 find_node_at_offset(original_file, name_ref.syntax().text_range().start()); 565 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
551 566
552 if matches!(self.completion_location, Some(ImmediateLocation::ItemList)) {
553 return;
554 }
555
556 self.use_item_syntax = 567 self.use_item_syntax =
557 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); 568 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast);
558 569
@@ -567,23 +578,43 @@ impl<'a> CompletionContext<'a> {
567 None => return, 578 None => return,
568 }; 579 };
569 580
570 if let Some(segment) = ast::PathSegment::cast(parent.clone()) { 581 if let Some(segment) = ast::PathSegment::cast(parent) {
582 let path_ctx = self.path_context.get_or_insert(PathCompletionContext {
583 call_kind: None,
584 is_trivial_path: false,
585 qualifier: None,
586 has_type_args: false,
587 can_be_stmt: false,
588 in_loop_body: false,
589 kind: None,
590 });
591 path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax());
571 let path = segment.parent_path(); 592 let path = segment.parent_path();
572 self.is_call = path
573 .syntax()
574 .parent()
575 .and_then(ast::PathExpr::cast)
576 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
577 .is_some();
578 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
579 self.is_pattern_call =
580 path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
581 593
582 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 594 if let Some(p) = path.syntax().parent() {
583 self.has_type_args = segment.generic_arg_list().is_some(); 595 path_ctx.call_kind = match_ast! {
596 match p {
597 ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr),
598 ast::MacroCall(it) => it.excl_token().and(Some(CallKind::Mac)),
599 ast::TupleStructPat(_it) => Some(CallKind::Pat),
600 _ => None
601 }
602 };
603 }
604
605 if let Some(parent) = path.syntax().parent() {
606 path_ctx.kind = match_ast! {
607 match parent {
608 ast::PathType(_it) => Some(PathKind::Type),
609 ast::PathExpr(_it) => Some(PathKind::Expr),
610 _ => None,
611 }
612 };
613 }
614 path_ctx.has_type_args = segment.generic_arg_list().is_some();
584 615
585 if let Some(path) = path_or_use_tree_qualifier(&path) { 616 if let Some(path) = path_or_use_tree_qualifier(&path) {
586 self.path_qual = path 617 path_ctx.qualifier = path
587 .segment() 618 .segment()
588 .and_then(|it| { 619 .and_then(|it| {
589 find_node_with_range::<ast::PathSegment>( 620 find_node_with_range::<ast::PathSegment>(
@@ -601,11 +632,11 @@ impl<'a> CompletionContext<'a> {
601 } 632 }
602 } 633 }
603 634
604 self.is_trivial_path = true; 635 path_ctx.is_trivial_path = true;
605 636
606 // Find either enclosing expr statement (thing with `;`) or a 637 // Find either enclosing expr statement (thing with `;`) or a
607 // block. If block, check that we are the last expr. 638 // block. If block, check that we are the last expr.
608 self.can_be_stmt = name_ref 639 path_ctx.can_be_stmt = name_ref
609 .syntax() 640 .syntax()
610 .ancestors() 641 .ancestors()
611 .find_map(|node| { 642 .find_map(|node| {
@@ -621,10 +652,7 @@ impl<'a> CompletionContext<'a> {
621 None 652 None
622 }) 653 })
623 .unwrap_or(false); 654 .unwrap_or(false);
624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
625 } 655 }
626 self.is_call |=
627 matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. }));
628 } 656 }
629} 657}
630 658
diff --git a/crates/ide_completion/src/generated_lint_completions.rs b/crates/ide_completion/src/generated_lint_completions.rs
deleted file mode 100644
index 0d405350d..000000000
--- a/crates/ide_completion/src/generated_lint_completions.rs
+++ /dev/null
@@ -1,6380 +0,0 @@
1//! Generated file, do not edit by hand, see `xtask/src/codegen`
2
3use crate::completions::attribute::LintCompletion;
4pub(super) const FEATURES: &[LintCompletion] = &[
5 LintCompletion {
6 label: "plugin_registrar",
7 description: r##"# `plugin_registrar`
8
9The tracking issue for this feature is: [#29597]
10
11[#29597]: https://github.com/rust-lang/rust/issues/29597
12
13This feature is part of "compiler plugins." It will often be used with the
14[`plugin`] and `rustc_private` features as well. For more details, see
15their docs.
16
17[`plugin`]: plugin.md
18
19------------------------
20"##,
21 },
22 LintCompletion {
23 label: "inline_const",
24 description: r##"# `inline_const`
25
26The tracking issue for this feature is: [#76001]
27
28------
29
30This feature allows you to use inline constant expressions. For example, you can
31turn this code:
32
33```rust
34# fn add_one(x: i32) -> i32 { x + 1 }
35const MY_COMPUTATION: i32 = 1 + 2 * 3 / 4;
36
37fn main() {
38 let x = add_one(MY_COMPUTATION);
39}
40```
41
42into this code:
43
44```rust
45#![feature(inline_const)]
46
47# fn add_one(x: i32) -> i32 { x + 1 }
48fn main() {
49 let x = add_one(const { 1 + 2 * 3 / 4 });
50}
51```
52
53You can also use inline constant expressions in patterns:
54
55```rust
56#![feature(inline_const)]
57
58const fn one() -> i32 { 1 }
59
60let some_int = 3;
61match some_int {
62 const { 1 + 2 } => println!("Matched 1 + 2"),
63 const { one() } => println!("Matched const fn returning 1"),
64 _ => println!("Didn't match anything :("),
65}
66```
67
68[#76001]: https://github.com/rust-lang/rust/issues/76001
69"##,
70 },
71 LintCompletion {
72 label: "auto_traits",
73 description: r##"# `auto_traits`
74
75The tracking issue for this feature is [#13231]
76
77[#13231]: https://github.com/rust-lang/rust/issues/13231
78
79----
80
81The `auto_traits` feature gate allows you to define auto traits.
82
83Auto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits
84that are automatically implemented for every type, unless the type, or a type it contains,
85has explicitly opted out via a negative impl. (Negative impls are separately controlled
86by the `negative_impls` feature.)
87
88[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
89[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
90
91```rust,ignore (partial-example)
92impl !Trait for Type {}
93```
94
95Example:
96
97```rust
98#![feature(negative_impls)]
99#![feature(auto_traits)]
100
101auto trait Valid {}
102
103struct True;
104struct False;
105
106impl !Valid for False {}
107
108struct MaybeValid<T>(T);
109
110fn must_be_valid<T: Valid>(_t: T) { }
111
112fn main() {
113 // works
114 must_be_valid( MaybeValid(True) );
115
116 // compiler error - trait bound not satisfied
117 // must_be_valid( MaybeValid(False) );
118}
119```
120
121## Automatic trait implementations
122
123When a type is declared as an `auto trait`, we will automatically
124create impls for every struct/enum/union, unless an explicit impl is
125provided. These automatic impls contain a where clause for each field
126of the form `T: AutoTrait`, where `T` is the type of the field and
127`AutoTrait` is the auto trait in question. As an example, consider the
128struct `List` and the auto trait `Send`:
129
130```rust
131struct List<T> {
132 data: T,
133 next: Option<Box<List<T>>>,
134}
135```
136
137Presuming that there is no explicit impl of `Send` for `List`, the
138compiler will supply an automatic impl of the form:
139
140```rust
141struct List<T> {
142 data: T,
143 next: Option<Box<List<T>>>,
144}
145
146unsafe impl<T> Send for List<T>
147where
148 T: Send, // from the field `data`
149 Option<Box<List<T>>>: Send, // from the field `next`
150{ }
151```
152
153Explicit impls may be either positive or negative. They take the form:
154
155```rust,ignore (partial-example)
156impl<...> AutoTrait for StructName<..> { }
157impl<...> !AutoTrait for StructName<..> { }
158```
159
160## Coinduction: Auto traits permit cyclic matching
161
162Unlike ordinary trait matching, auto traits are **coinductive**. This
163means, in short, that cycles which occur in trait matching are
164considered ok. As an example, consider the recursive struct `List`
165introduced in the previous section. In attempting to determine whether
166`List: Send`, we would wind up in a cycle: to apply the impl, we must
167show that `Option<Box<List>>: Send`, which will in turn require
168`Box<List>: Send` and then finally `List: Send` again. Under ordinary
169trait matching, this cycle would be an error, but for an auto trait it
170is considered a successful match.
171
172## Items
173
174Auto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations.
175
176## Supertraits
177
178Auto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile.
179"##,
180 },
181 LintCompletion {
182 label: "ffi_const",
183 description: r##"# `ffi_const`
184
185The tracking issue for this feature is: [#58328]
186
187------
188
189The `#[ffi_const]` attribute applies clang's `const` attribute to foreign
190functions declarations.
191
192That is, `#[ffi_const]` functions shall have no effects except for its return
193value, which can only depend on the values of the function parameters, and is
194not affected by changes to the observable state of the program.
195
196Applying the `#[ffi_const]` attribute to a function that violates these
197requirements is undefined behaviour.
198
199This attribute enables Rust to perform common optimizations, like sub-expression
200elimination, and it can avoid emitting some calls in repeated invocations of the
201function with the same argument values regardless of other operations being
202performed in between these functions calls (as opposed to `#[ffi_pure]`
203functions).
204
205## Pitfalls
206
207A `#[ffi_const]` function can only read global memory that would not affect
208its return value for the whole execution of the program (e.g. immutable global
209memory). `#[ffi_const]` functions are referentially-transparent and therefore
210more strict than `#[ffi_pure]` functions.
211
212A common pitfall involves applying the `#[ffi_const]` attribute to a
213function that reads memory through pointer arguments which do not necessarily
214point to immutable global memory.
215
216A `#[ffi_const]` function that returns unit has no effect on the abstract
217machine's state, and a `#[ffi_const]` function cannot be `#[ffi_pure]`.
218
219A `#[ffi_const]` function must not diverge, neither via a side effect (e.g. a
220call to `abort`) nor by infinite loops.
221
222When translating C headers to Rust FFI, it is worth verifying for which targets
223the `const` attribute is enabled in those headers, and using the appropriate
224`cfg` macros in the Rust side to match those definitions. While the semantics of
225`const` are implemented identically by many C and C++ compilers, e.g., clang,
226[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily
227implemented in this way on all of them. It is therefore also worth verifying
228that the semantics of the C toolchain used to compile the binary being linked
229against are compatible with those of the `#[ffi_const]`.
230
231[#58328]: https://github.com/rust-lang/rust/issues/58328
232[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacgigch.html
233[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute
234[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_const.htm
235"##,
236 },
237 LintCompletion {
238 label: "external_doc",
239 description: r##"# `external_doc`
240
241The tracking issue for this feature is: [#44732]
242
243The `external_doc` feature allows the use of the `include` parameter to the `#[doc]` attribute, to
244include external files in documentation. Use the attribute in place of, or in addition to, regular
245doc comments and `#[doc]` attributes, and `rustdoc` will load the given file when it renders
246documentation for your crate.
247
248With the following files in the same directory:
249
250`external-doc.md`:
251
252```markdown
253# My Awesome Type
254
255This is the documentation for this spectacular type.
256```
257
258`lib.rs`:
259
260```no_run (needs-external-files)
261#![feature(external_doc)]
262
263#[doc(include = "external-doc.md")]
264pub struct MyAwesomeType;
265```
266
267`rustdoc` will load the file `external-doc.md` and use it as the documentation for the `MyAwesomeType`
268struct.
269
270When locating files, `rustdoc` will base paths in the `src/` directory, as if they were alongside the
271`lib.rs` for your crate. So if you want a `docs/` folder to live alongside the `src/` directory,
272start your paths with `../docs/` for `rustdoc` to properly find the file.
273
274This feature was proposed in [RFC #1990] and initially implemented in PR [#44781].
275
276[#44732]: https://github.com/rust-lang/rust/issues/44732
277[RFC #1990]: https://github.com/rust-lang/rfcs/pull/1990
278[#44781]: https://github.com/rust-lang/rust/pull/44781
279"##,
280 },
281 LintCompletion {
282 label: "box_patterns",
283 description: r##"# `box_patterns`
284
285The tracking issue for this feature is: [#29641]
286
287[#29641]: https://github.com/rust-lang/rust/issues/29641
288
289See also [`box_syntax`](box-syntax.md)
290
291------------------------
292
293Box patterns let you match on `Box<T>`s:
294
295
296```rust
297#![feature(box_patterns)]
298
299fn main() {
300 let b = Some(Box::new(5));
301 match b {
302 Some(box n) if n < 0 => {
303 println!("Box contains negative number {}", n);
304 },
305 Some(box n) if n >= 0 => {
306 println!("Box contains non-negative number {}", n);
307 },
308 None => {
309 println!("No box");
310 },
311 _ => unreachable!()
312 }
313}
314```
315"##,
316 },
317 LintCompletion {
318 label: "abi_c_cmse_nonsecure_call",
319 description: r##"# `abi_c_cmse_nonsecure_call`
320
321The tracking issue for this feature is: [#81391]
322
323[#81391]: https://github.com/rust-lang/rust/issues/81391
324
325------------------------
326
327The [TrustZone-M
328feature](https://developer.arm.com/documentation/100690/latest/) is available
329for targets with the Armv8-M architecture profile (`thumbv8m` in their target
330name).
331LLVM, the Rust compiler and the linker are providing
332[support](https://developer.arm.com/documentation/ecm0359818/latest/) for the
333TrustZone-M feature.
334
335One of the things provided, with this unstable feature, is the
336`C-cmse-nonsecure-call` function ABI. This ABI is used on function pointers to
337non-secure code to mark a non-secure function call (see [section
3385.5](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
339
340With this ABI, the compiler will do the following to perform the call:
341* save registers needed after the call to Secure memory
342* clear all registers that might contain confidential information
343* clear the Least Significant Bit of the function address
344* branches using the BLXNS instruction
345
346To avoid using the non-secure stack, the compiler will constrain the number and
347type of parameters/return value.
348
349The `extern "C-cmse-nonsecure-call"` ABI is otherwise equivalent to the
350`extern "C"` ABI.
351
352<!-- NOTE(ignore) this example is specific to thumbv8m targets -->
353
354``` rust,ignore
355#![no_std]
356#![feature(abi_c_cmse_nonsecure_call)]
357
358#[no_mangle]
359pub fn call_nonsecure_function(addr: usize) -> u32 {
360 let non_secure_function =
361 unsafe { core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn() -> u32>(addr) };
362 non_secure_function()
363}
364```
365
366``` text
367$ rustc --emit asm --crate-type lib --target thumbv8m.main-none-eabi function.rs
368
369call_nonsecure_function:
370 .fnstart
371 .save {r7, lr}
372 push {r7, lr}
373 .setfp r7, sp
374 mov r7, sp
375 .pad #16
376 sub sp, #16
377 str r0, [sp, #12]
378 ldr r0, [sp, #12]
379 str r0, [sp, #8]
380 b .LBB0_1
381.LBB0_1:
382 ldr r0, [sp, #8]
383 push.w {r4, r5, r6, r7, r8, r9, r10, r11}
384 bic r0, r0, #1
385 mov r1, r0
386 mov r2, r0
387 mov r3, r0
388 mov r4, r0
389 mov r5, r0
390 mov r6, r0
391 mov r7, r0
392 mov r8, r0
393 mov r9, r0
394 mov r10, r0
395 mov r11, r0
396 mov r12, r0
397 msr apsr_nzcvq, r0
398 blxns r0
399 pop.w {r4, r5, r6, r7, r8, r9, r10, r11}
400 str r0, [sp, #4]
401 b .LBB0_2
402.LBB0_2:
403 ldr r0, [sp, #4]
404 add sp, #16
405 pop {r7, pc}
406```
407"##,
408 },
409 LintCompletion {
410 label: "member_constraints",
411 description: r##"# `member_constraints`
412
413The tracking issue for this feature is: [#61997]
414
415[#61997]: https://github.com/rust-lang/rust/issues/61997
416
417------------------------
418
419The `member_constraints` feature gate lets you use `impl Trait` syntax with
420multiple unrelated lifetime parameters.
421
422A simple example is:
423
424```rust
425#![feature(member_constraints)]
426
427trait Trait<'a, 'b> { }
428impl<T> Trait<'_, '_> for T {}
429
430fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> {
431 (x, y)
432}
433
434fn main() { }
435```
436
437Without the `member_constraints` feature gate, the above example is an
438error because both `'a` and `'b` appear in the impl Trait bounds, but
439neither outlives the other.
440"##,
441 },
442 LintCompletion {
443 label: "allocator_internals",
444 description: r##"# `allocator_internals`
445
446This feature does not have a tracking issue, it is an unstable implementation
447detail of the `global_allocator` feature not intended for use outside the
448compiler.
449
450------------------------
451"##,
452 },
453 LintCompletion {
454 label: "cfg_sanitize",
455 description: r##"# `cfg_sanitize`
456
457The tracking issue for this feature is: [#39699]
458
459[#39699]: https://github.com/rust-lang/rust/issues/39699
460
461------------------------
462
463The `cfg_sanitize` feature makes it possible to execute different code
464depending on whether a particular sanitizer is enabled or not.
465
466## Examples
467
468```rust
469#![feature(cfg_sanitize)]
470
471#[cfg(sanitize = "thread")]
472fn a() {
473 // ...
474}
475
476#[cfg(not(sanitize = "thread"))]
477fn a() {
478 // ...
479}
480
481fn b() {
482 if cfg!(sanitize = "leak") {
483 // ...
484 } else {
485 // ...
486 }
487}
488```
489"##,
490 },
491 LintCompletion {
492 label: "cfg_panic",
493 description: r##"# `cfg_panic`
494
495The tracking issue for this feature is: [#77443]
496
497[#77443]: https://github.com/rust-lang/rust/issues/77443
498
499------------------------
500
501The `cfg_panic` feature makes it possible to execute different code
502depending on the panic strategy.
503
504Possible values at the moment are `"unwind"` or `"abort"`, although
505it is possible that new panic strategies may be added to Rust in the
506future.
507
508## Examples
509
510```rust
511#![feature(cfg_panic)]
512
513#[cfg(panic = "unwind")]
514fn a() {
515 // ...
516}
517
518#[cfg(not(panic = "unwind"))]
519fn a() {
520 // ...
521}
522
523fn b() {
524 if cfg!(panic = "abort") {
525 // ...
526 } else {
527 // ...
528 }
529}
530```
531"##,
532 },
533 LintCompletion {
534 label: "ffi_pure",
535 description: r##"# `ffi_pure`
536
537The tracking issue for this feature is: [#58329]
538
539------
540
541The `#[ffi_pure]` attribute applies clang's `pure` attribute to foreign
542functions declarations.
543
544That is, `#[ffi_pure]` functions shall have no effects except for its return
545value, which shall not change across two consecutive function calls with
546the same parameters.
547
548Applying the `#[ffi_pure]` attribute to a function that violates these
549requirements is undefined behavior.
550
551This attribute enables Rust to perform common optimizations, like sub-expression
552elimination and loop optimizations. Some common examples of pure functions are
553`strlen` or `memcmp`.
554
555These optimizations are only applicable when the compiler can prove that no
556program state observable by the `#[ffi_pure]` function has changed between calls
557of the function, which could alter the result. See also the `#[ffi_const]`
558attribute, which provides stronger guarantees regarding the allowable behavior
559of a function, enabling further optimization.
560
561## Pitfalls
562
563A `#[ffi_pure]` function can read global memory through the function
564parameters (e.g. pointers), globals, etc. `#[ffi_pure]` functions are not
565referentially-transparent, and are therefore more relaxed than `#[ffi_const]`
566functions.
567
568However, accessing global memory through volatile or atomic reads can violate the
569requirement that two consecutive function calls shall return the same value.
570
571A `pure` function that returns unit has no effect on the abstract machine's
572state.
573
574A `#[ffi_pure]` function must not diverge, neither via a side effect (e.g. a
575call to `abort`) nor by infinite loops.
576
577When translating C headers to Rust FFI, it is worth verifying for which targets
578the `pure` attribute is enabled in those headers, and using the appropriate
579`cfg` macros in the Rust side to match those definitions. While the semantics of
580`pure` are implemented identically by many C and C++ compilers, e.g., clang,
581[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily
582implemented in this way on all of them. It is therefore also worth verifying
583that the semantics of the C toolchain used to compile the binary being linked
584against are compatible with those of the `#[ffi_pure]`.
585
586
587[#58329]: https://github.com/rust-lang/rust/issues/58329
588[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacigdac.html
589[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute
590[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_pure.htm
591"##,
592 },
593 LintCompletion {
594 label: "repr128",
595 description: r##"# `repr128`
596
597The tracking issue for this feature is: [#56071]
598
599[#56071]: https://github.com/rust-lang/rust/issues/56071
600
601------------------------
602
603The `repr128` feature adds support for `#[repr(u128)]` on `enum`s.
604
605```rust
606#![feature(repr128)]
607
608#[repr(u128)]
609enum Foo {
610 Bar(u64),
611}
612```
613"##,
614 },
615 LintCompletion {
616 label: "generators",
617 description: r##"# `generators`
618
619The tracking issue for this feature is: [#43122]
620
621[#43122]: https://github.com/rust-lang/rust/issues/43122
622
623------------------------
624
625The `generators` feature gate in Rust allows you to define generator or
626coroutine literals. A generator is a "resumable function" that syntactically
627resembles a closure but compiles to much different semantics in the compiler
628itself. The primary feature of a generator is that it can be suspended during
629execution to be resumed at a later date. Generators use the `yield` keyword to
630"return", and then the caller can `resume` a generator to resume execution just
631after the `yield` keyword.
632
633Generators are an extra-unstable feature in the compiler right now. Added in
634[RFC 2033] they're mostly intended right now as a information/constraint
635gathering phase. The intent is that experimentation can happen on the nightly
636compiler before actual stabilization. A further RFC will be required to
637stabilize generators/coroutines and will likely contain at least a few small
638tweaks to the overall design.
639
640[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033
641
642A syntactical example of a generator is:
643
644```rust
645#![feature(generators, generator_trait)]
646
647use std::ops::{Generator, GeneratorState};
648use std::pin::Pin;
649
650fn main() {
651 let mut generator = || {
652 yield 1;
653 return "foo"
654 };
655
656 match Pin::new(&mut generator).resume(()) {
657 GeneratorState::Yielded(1) => {}
658 _ => panic!("unexpected value from resume"),
659 }
660 match Pin::new(&mut generator).resume(()) {
661 GeneratorState::Complete("foo") => {}
662 _ => panic!("unexpected value from resume"),
663 }
664}
665```
666
667Generators are closure-like literals which can contain a `yield` statement. The
668`yield` statement takes an optional expression of a value to yield out of the
669generator. All generator literals implement the `Generator` trait in the
670`std::ops` module. The `Generator` trait has one main method, `resume`, which
671resumes execution of the generator at the previous suspension point.
672
673An example of the control flow of generators is that the following example
674prints all numbers in order:
675
676```rust
677#![feature(generators, generator_trait)]
678
679use std::ops::Generator;
680use std::pin::Pin;
681
682fn main() {
683 let mut generator = || {
684 println!("2");
685 yield;
686 println!("4");
687 };
688
689 println!("1");
690 Pin::new(&mut generator).resume(());
691 println!("3");
692 Pin::new(&mut generator).resume(());
693 println!("5");
694}
695```
696
697At this time the main intended use case of generators is an implementation
698primitive for async/await syntax, but generators will likely be extended to
699ergonomic implementations of iterators and other primitives in the future.
700Feedback on the design and usage is always appreciated!
701
702### The `Generator` trait
703
704The `Generator` trait in `std::ops` currently looks like:
705
706```rust
707# #![feature(arbitrary_self_types, generator_trait)]
708# use std::ops::GeneratorState;
709# use std::pin::Pin;
710
711pub trait Generator<R = ()> {
712 type Yield;
713 type Return;
714 fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState<Self::Yield, Self::Return>;
715}
716```
717
718The `Generator::Yield` type is the type of values that can be yielded with the
719`yield` statement. The `Generator::Return` type is the returned type of the
720generator. This is typically the last expression in a generator's definition or
721any value passed to `return` in a generator. The `resume` function is the entry
722point for executing the `Generator` itself.
723
724The return value of `resume`, `GeneratorState`, looks like:
725
726```rust
727pub enum GeneratorState<Y, R> {
728 Yielded(Y),
729 Complete(R),
730}
731```
732
733The `Yielded` variant indicates that the generator can later be resumed. This
734corresponds to a `yield` point in a generator. The `Complete` variant indicates
735that the generator is complete and cannot be resumed again. Calling `resume`
736after a generator has returned `Complete` will likely result in a panic of the
737program.
738
739### Closure-like semantics
740
741The closure-like syntax for generators alludes to the fact that they also have
742closure-like semantics. Namely:
743
744* When created, a generator executes no code. A closure literal does not
745 actually execute any of the closure's code on construction, and similarly a
746 generator literal does not execute any code inside the generator when
747 constructed.
748
749* Generators can capture outer variables by reference or by move, and this can
750 be tweaked with the `move` keyword at the beginning of the closure. Like
751 closures all generators will have an implicit environment which is inferred by
752 the compiler. Outer variables can be moved into a generator for use as the
753 generator progresses.
754
755* Generator literals produce a value with a unique type which implements the
756 `std::ops::Generator` trait. This allows actual execution of the generator
757 through the `Generator::resume` method as well as also naming it in return
758 types and such.
759
760* Traits like `Send` and `Sync` are automatically implemented for a `Generator`
761 depending on the captured variables of the environment. Unlike closures,
762 generators also depend on variables live across suspension points. This means
763 that although the ambient environment may be `Send` or `Sync`, the generator
764 itself may not be due to internal variables live across `yield` points being
765 not-`Send` or not-`Sync`. Note that generators do
766 not implement traits like `Copy` or `Clone` automatically.
767
768* Whenever a generator is dropped it will drop all captured environment
769 variables.
770
771### Generators as state machines
772
773In the compiler, generators are currently compiled as state machines. Each
774`yield` expression will correspond to a different state that stores all live
775variables over that suspension point. Resumption of a generator will dispatch on
776the current state and then execute internally until a `yield` is reached, at
777which point all state is saved off in the generator and a value is returned.
778
779Let's take a look at an example to see what's going on here:
780
781```rust
782#![feature(generators, generator_trait)]
783
784use std::ops::Generator;
785use std::pin::Pin;
786
787fn main() {
788 let ret = "foo";
789 let mut generator = move || {
790 yield 1;
791 return ret
792 };
793
794 Pin::new(&mut generator).resume(());
795 Pin::new(&mut generator).resume(());
796}
797```
798
799This generator literal will compile down to something similar to:
800
801```rust
802#![feature(arbitrary_self_types, generators, generator_trait)]
803
804use std::ops::{Generator, GeneratorState};
805use std::pin::Pin;
806
807fn main() {
808 let ret = "foo";
809 let mut generator = {
810 enum __Generator {
811 Start(&'static str),
812 Yield1(&'static str),
813 Done,
814 }
815
816 impl Generator for __Generator {
817 type Yield = i32;
818 type Return = &'static str;
819
820 fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState<i32, &'static str> {
821 use std::mem;
822 match mem::replace(&mut *self, __Generator::Done) {
823 __Generator::Start(s) => {
824 *self = __Generator::Yield1(s);
825 GeneratorState::Yielded(1)
826 }
827
828 __Generator::Yield1(s) => {
829 *self = __Generator::Done;
830 GeneratorState::Complete(s)
831 }
832
833 __Generator::Done => {
834 panic!("generator resumed after completion")
835 }
836 }
837 }
838 }
839
840 __Generator::Start(ret)
841 };
842
843 Pin::new(&mut generator).resume(());
844 Pin::new(&mut generator).resume(());
845}
846```
847
848Notably here we can see that the compiler is generating a fresh type,
849`__Generator` in this case. This type has a number of states (represented here
850as an `enum`) corresponding to each of the conceptual states of the generator.
851At the beginning we're closing over our outer variable `foo` and then that
852variable is also live over the `yield` point, so it's stored in both states.
853
854When the generator starts it'll immediately yield 1, but it saves off its state
855just before it does so indicating that it has reached the yield point. Upon
856resuming again we'll execute the `return ret` which returns the `Complete`
857state.
858
859Here we can also note that the `Done` state, if resumed, panics immediately as
860it's invalid to resume a completed generator. It's also worth noting that this
861is just a rough desugaring, not a normative specification for what the compiler
862does.
863"##,
864 },
865 LintCompletion {
866 label: "non_ascii_idents",
867 description: r##"# `non_ascii_idents`
868
869The tracking issue for this feature is: [#55467]
870
871[#55467]: https://github.com/rust-lang/rust/issues/55467
872
873------------------------
874
875The `non_ascii_idents` feature adds support for non-ASCII identifiers.
876
877## Examples
878
879```rust
880#![feature(non_ascii_idents)]
881
882const ε: f64 = 0.00001f64;
883const Π: f64 = 3.14f64;
884```
885
886## Changes to the language reference
887
888> **<sup>Lexer:<sup>**\
889> IDENTIFIER :\
890> &nbsp;&nbsp; &nbsp;&nbsp; XID_start XID_continue<sup>\*</sup>\
891> &nbsp;&nbsp; | `_` XID_continue<sup>+</sup>
892
893An identifier is any nonempty Unicode string of the following form:
894
895Either
896
897 * The first character has property [`XID_start`]
898 * The remaining characters have property [`XID_continue`]
899
900Or
901
902 * The first character is `_`
903 * The identifier is more than one character, `_` alone is not an identifier
904 * The remaining characters have property [`XID_continue`]
905
906that does _not_ occur in the set of [strict keywords].
907
908> **Note**: [`XID_start`] and [`XID_continue`] as character properties cover the
909> character ranges used to form the more familiar C and Java language-family
910> identifiers.
911
912[`XID_start`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i=
913[`XID_continue`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i=
914[strict keywords]: ../../reference/keywords.md#strict-keywords
915"##,
916 },
917 LintCompletion {
918 label: "compiler_builtins",
919 description: r##"# `compiler_builtins`
920
921This feature is internal to the Rust compiler and is not intended for general use.
922
923------------------------
924"##,
925 },
926 LintCompletion {
927 label: "or_patterns",
928 description: r##"# `or_patterns`
929
930The tracking issue for this feature is: [#54883]
931
932[#54883]: https://github.com/rust-lang/rust/issues/54883
933
934------------------------
935
936The `or_pattern` language feature allows `|` to be arbitrarily nested within
937a pattern, for example, `Some(A(0) | B(1 | 2))` becomes a valid pattern.
938
939## Examples
940
941```rust,no_run
942#![feature(or_patterns)]
943
944pub enum Foo {
945 Bar,
946 Baz,
947 Quux,
948}
949
950pub fn example(maybe_foo: Option<Foo>) {
951 match maybe_foo {
952 Some(Foo::Bar | Foo::Baz) => {
953 println!("The value contained `Bar` or `Baz`");
954 }
955 Some(_) => {
956 println!("The value did not contain `Bar` or `Baz`");
957 }
958 None => {
959 println!("The value was `None`");
960 }
961 }
962}
963```
964"##,
965 },
966 LintCompletion {
967 label: "negative_impls",
968 description: r##"# `negative_impls`
969
970The tracking issue for this feature is [#68318].
971
972[#68318]: https://github.com/rust-lang/rust/issues/68318
973
974----
975
976With the feature gate `negative_impls`, you can write negative impls as well as positive ones:
977
978```rust
979#![feature(negative_impls)]
980trait DerefMut { }
981impl<T: ?Sized> !DerefMut for &T { }
982```
983
984Negative impls indicate a semver guarantee that the given trait will not be implemented for the given types. Negative impls play an additional purpose for auto traits, described below.
985
986Negative impls have the following characteristics:
987
988* They do not have any items.
989* They must obey the orphan rules as if they were a positive impl.
990* They cannot "overlap" with any positive impls.
991
992## Semver interaction
993
994It is a breaking change to remove a negative impl. Negative impls are a commitment not to implement the given trait for the named types.
995
996## Orphan and overlap rules
997
998Negative impls must obey the same orphan rules as a positive impl. This implies you cannot add a negative impl for types defined in upstream crates and so forth.
999
1000Similarly, negative impls cannot overlap with positive impls, again using the same "overlap" check that we ordinarily use to determine if two impls overlap. (Note that positive impls typically cannot overlap with one another either, except as permitted by specialization.)
1001
1002## Interaction with auto traits
1003
1004Declaring a negative impl `impl !SomeAutoTrait for SomeType` for an
1005auto-trait serves two purposes:
1006
1007* as with any trait, it declares that `SomeType` will never implement `SomeAutoTrait`;
1008* it disables the automatic `SomeType: SomeAutoTrait` impl that would otherwise have been generated.
1009
1010Note that, at present, there is no way to indicate that a given type
1011does not implement an auto trait *but that it may do so in the
1012future*. For ordinary types, this is done by simply not declaring any
1013impl at all, but that is not an option for auto traits. A workaround
1014is that one could embed a marker type as one of the fields, where the
1015marker type is `!AutoTrait`.
1016
1017## Immediate uses
1018
1019Negative impls are used to declare that `&T: !DerefMut` and `&mut T: !Clone`, as required to fix the soundness of `Pin` described in [#66544](https://github.com/rust-lang/rust/issues/66544).
1020
1021This serves two purposes:
1022
1023* For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists.
1024* It prevents downstream crates from creating such impls.
1025"##,
1026 },
1027 LintCompletion {
1028 label: "cmse_nonsecure_entry",
1029 description: r##"# `cmse_nonsecure_entry`
1030
1031The tracking issue for this feature is: [#75835]
1032
1033[#75835]: https://github.com/rust-lang/rust/issues/75835
1034
1035------------------------
1036
1037The [TrustZone-M
1038feature](https://developer.arm.com/documentation/100690/latest/) is available
1039for targets with the Armv8-M architecture profile (`thumbv8m` in their target
1040name).
1041LLVM, the Rust compiler and the linker are providing
1042[support](https://developer.arm.com/documentation/ecm0359818/latest/) for the
1043TrustZone-M feature.
1044
1045One of the things provided, with this unstable feature, is the
1046`cmse_nonsecure_entry` attribute. This attribute marks a Secure function as an
1047entry function (see [section
10485.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
1049With this attribute, the compiler will do the following:
1050* add a special symbol on the function which is the `__acle_se_` prefix and the
1051 standard function name
1052* constrain the number of parameters to avoid using the Non-Secure stack
1053* before returning from the function, clear registers that might contain Secure
1054 information
1055* use the `BXNS` instruction to return
1056
1057Because the stack can not be used to pass parameters, there will be compilation
1058errors if:
1059* the total size of all parameters is too big (for example more than four 32
1060 bits integers)
1061* the entry function is not using a C ABI
1062
1063The special symbol `__acle_se_` will be used by the linker to generate a secure
1064gateway veneer.
1065
1066<!-- NOTE(ignore) this example is specific to thumbv8m targets -->
1067
1068``` rust,ignore
1069#![feature(cmse_nonsecure_entry)]
1070
1071#[no_mangle]
1072#[cmse_nonsecure_entry]
1073pub extern "C" fn entry_function(input: u32) -> u32 {
1074 input + 6
1075}
1076```
1077
1078``` text
1079$ rustc --emit obj --crate-type lib --target thumbv8m.main-none-eabi function.rs
1080$ arm-none-eabi-objdump -D function.o
1081
108200000000 <entry_function>:
1083 0: b580 push {r7, lr}
1084 2: 466f mov r7, sp
1085 4: b082 sub sp, #8
1086 6: 9001 str r0, [sp, #4]
1087 8: 1d81 adds r1, r0, #6
1088 a: 460a mov r2, r1
1089 c: 4281 cmp r1, r0
1090 e: 9200 str r2, [sp, #0]
1091 10: d30b bcc.n 2a <entry_function+0x2a>
1092 12: e7ff b.n 14 <entry_function+0x14>
1093 14: 9800 ldr r0, [sp, #0]
1094 16: b002 add sp, #8
1095 18: e8bd 4080 ldmia.w sp!, {r7, lr}
1096 1c: 4671 mov r1, lr
1097 1e: 4672 mov r2, lr
1098 20: 4673 mov r3, lr
1099 22: 46f4 mov ip, lr
1100 24: f38e 8800 msr CPSR_f, lr
1101 28: 4774 bxns lr
1102 2a: f240 0000 movw r0, #0
1103 2e: f2c0 0000 movt r0, #0
1104 32: f240 0200 movw r2, #0
1105 36: f2c0 0200 movt r2, #0
1106 3a: 211c movs r1, #28
1107 3c: f7ff fffe bl 0 <_ZN4core9panicking5panic17h5c028258ca2fb3f5E>
1108 40: defe udf #254 ; 0xfe
1109```
1110"##,
1111 },
1112 LintCompletion {
1113 label: "plugin",
1114 description: r##"# `plugin`
1115
1116The tracking issue for this feature is: [#29597]
1117
1118[#29597]: https://github.com/rust-lang/rust/issues/29597
1119
1120
1121This feature is part of "compiler plugins." It will often be used with the
1122[`plugin_registrar`] and `rustc_private` features.
1123
1124[`plugin_registrar`]: plugin-registrar.md
1125
1126------------------------
1127
1128`rustc` can load compiler plugins, which are user-provided libraries that
1129extend the compiler's behavior with new lint checks, etc.
1130
1131A plugin is a dynamic library crate with a designated *registrar* function that
1132registers extensions with `rustc`. Other crates can load these extensions using
1133the crate attribute `#![plugin(...)]`. See the
1134`rustc_driver::plugin` documentation for more about the
1135mechanics of defining and loading a plugin.
1136
1137In the vast majority of cases, a plugin should *only* be used through
1138`#![plugin]` and not through an `extern crate` item. Linking a plugin would
1139pull in all of librustc_ast and librustc as dependencies of your crate. This is
1140generally unwanted unless you are building another plugin.
1141
1142The usual practice is to put compiler plugins in their own crate, separate from
1143any `macro_rules!` macros or ordinary Rust code meant to be used by consumers
1144of a library.
1145
1146# Lint plugins
1147
1148Plugins can extend [Rust's lint
1149infrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with
1150additional checks for code style, safety, etc. Now let's write a plugin
1151[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs)
1152that warns about any item named `lintme`.
1153
1154```rust,ignore (requires-stage-2)
1155#![feature(plugin_registrar)]
1156#![feature(box_syntax, rustc_private)]
1157
1158extern crate rustc_ast;
1159
1160// Load rustc as a plugin to get macros
1161extern crate rustc_driver;
1162#[macro_use]
1163extern crate rustc_lint;
1164#[macro_use]
1165extern crate rustc_session;
1166
1167use rustc_driver::plugin::Registry;
1168use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
1169use rustc_ast::ast;
1170declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
1171
1172declare_lint_pass!(Pass => [TEST_LINT]);
1173
1174impl EarlyLintPass for Pass {
1175 fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {
1176 if it.ident.name.as_str() == "lintme" {
1177 cx.lint(TEST_LINT, |lint| {
1178 lint.build("item is named 'lintme'").set_span(it.span).emit()
1179 });
1180 }
1181 }
1182}
1183