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