aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-02-17 14:53:31 +0000
committerAleksey Kladov <[email protected]>2021-02-17 14:53:31 +0000
commit3db64a400c78bbd2708e67ddc07df1001fff3f29 (patch)
tree5386aab9c452981be09bc3e4362643a34e6e3617 /crates/ide_completion
parent6334ce866ab095215381c4b72692b20a84d26e96 (diff)
rename completion -> ide_completion
We don't have completion-related PRs in flight, so lets do it
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.rs557
-rw-r--r--crates/ide_completion/src/completions/dot.rs431
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs688
-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.rs5
-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, 10754 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..ab25a8c58
--- /dev/null
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -0,0 +1,557 @@
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("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
105 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
106 // FIXME: resolve through macro resolution?
107 attr("global_allocator", None, None).prefer_inner(),
108 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
109 attr("inline", Some("inline"), Some("inline")),
110 attr("link", None, None),
111 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
112 attr(
113 r#"link_section = "…""#,
114 Some("link_section"),
115 Some(r#"link_section = "${0:section_name}""#),
116 ),
117 attr("macro_export", None, None),
118 attr("macro_use", None, None),
119 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)),
120 attr("no_link", None, None).prefer_inner(),
121 attr("no_implicit_prelude", None, None).prefer_inner(),
122 attr("no_main", None, None).prefer_inner(),
123 attr("no_mangle", None, None),
124 attr("no_std", None, None).prefer_inner(),
125 attr("non_exhaustive", None, None),
126 attr("panic_handler", None, None).prefer_inner(),
127 attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)),
128 attr("proc_macro", None, None),
129 attr("proc_macro_attribute", None, None),
130 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
131 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
132 .prefer_inner(),
133 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
134 attr("should_panic", Some("should_panic"), Some(r#"should_panic"#)),
135 attr(
136 r#"target_feature = "…""#,
137 Some("target_feature"),
138 Some(r#"target_feature = "${0:feature}""#),
139 ),
140 attr("test", None, None),
141 attr("track_caller", None, None),
142 attr("type_length_limit = …", Some("type_length_limit"), Some("type_length_limit = ${0:128}"))
143 .prefer_inner(),
144 attr("used", None, None),
145 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
146 attr(
147 r#"windows_subsystem = "…""#,
148 Some("windows_subsystem"),
149 Some(r#"windows_subsystem = "${0:subsystem}""#),
150 )
151 .prefer_inner(),
152];
153
154fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
155 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
156 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
157 .iter()
158 .filter(|completion| !existing_derives.contains(completion.label))
159 {
160 let mut components = vec![derive_completion.label];
161 components.extend(
162 derive_completion
163 .dependencies
164 .iter()
165 .filter(|&&dependency| !existing_derives.contains(dependency)),
166 );
167 let lookup = components.join(", ");
168 let label = components.iter().rev().join(", ");
169 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
170 .lookup_by(lookup)
171 .kind(CompletionItemKind::Attribute)
172 .add_to(acc)
173 }
174
175 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
176 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), custom_derive_name)
177 .kind(CompletionItemKind::Attribute)
178 .add_to(acc)
179 }
180 }
181}
182
183fn complete_lint(
184 acc: &mut Completions,
185 ctx: &CompletionContext,
186 derive_input: ast::TokenTree,
187 lints_completions: &[LintCompletion],
188) {
189 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
190 for lint_completion in lints_completions
191 .into_iter()
192 .filter(|completion| !existing_lints.contains(completion.label))
193 {
194 CompletionItem::new(
195 CompletionKind::Attribute,
196 ctx.source_range(),
197 lint_completion.label,
198 )
199 .kind(CompletionItemKind::Attribute)
200 .detail(lint_completion.description)
201 .add_to(acc)
202 }
203 }
204}
205
206fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
207 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
208 (Some(left_paren), Some(right_paren))
209 if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] =>
210 {
211 let mut input_derives = FxHashSet::default();
212 let mut current_derive = String::new();
213 for token in derive_input
214 .syntax()
215 .children_with_tokens()
216 .filter_map(|token| token.into_token())
217 .skip_while(|token| token != &left_paren)
218 .skip(1)
219 .take_while(|token| token != &right_paren)
220 {
221 if T![,] == token.kind() {
222 if !current_derive.is_empty() {
223 input_derives.insert(current_derive);
224 current_derive = String::new();
225 }
226 } else {
227 current_derive.push_str(token.text().trim());
228 }
229 }
230
231 if !current_derive.is_empty() {
232 input_derives.insert(current_derive);
233 }
234 Ok(input_derives)
235 }
236 _ => Err(()),
237 }
238}
239
240fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
241 let mut result = FxHashSet::default();
242 ctx.scope.process_all_names(&mut |name, scope_def| {
243 if let hir::ScopeDef::MacroDef(mac) = scope_def {
244 if mac.is_derive_macro() {
245 result.insert(name.to_string());
246 }
247 }
248 });
249 result
250}
251
252struct DeriveCompletion {
253 label: &'static str,
254 dependencies: &'static [&'static str],
255}
256
257/// Standard Rust derives and the information about their dependencies
258/// (the dependencies are needed so that the main derive don't break the compilation when added)
259const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
260 DeriveCompletion { label: "Clone", dependencies: &[] },
261 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
262 DeriveCompletion { label: "Debug", dependencies: &[] },
263 DeriveCompletion { label: "Default", dependencies: &[] },
264 DeriveCompletion { label: "Hash", dependencies: &[] },
265 DeriveCompletion { label: "PartialEq", dependencies: &[] },
266 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
267 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
268 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
269];
270
271pub(crate) struct LintCompletion {
272 pub(crate) label: &'static str,
273 pub(crate) description: &'static str,
274}
275
276#[rustfmt::skip]
277const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
278 LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# },
279 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
280 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
281 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
282 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
283 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
284 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
285 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
286 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
287 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
288 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
289 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
290 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
291 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
292 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
293 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
294 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
295 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
296 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
297 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
298 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
299 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
300 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
301 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
302 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
303 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
304 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
305 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
306 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
307 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
308 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
309 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
310 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
311 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
312 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
313 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
314 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
315 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
316 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
317 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
318 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
319 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
320 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
321 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
322 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
323 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
324 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
325 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
326 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
327 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
328 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
329 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
330 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
331 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
332 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
333 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
334 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
335 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
336 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
337 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
338 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
339 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
340 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
341 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
342 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
343 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
344 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
345 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
346 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
347 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
348 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
349 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
350 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
351 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
352 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
353 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
354 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
355 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
356 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
357 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
358 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
359 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
360 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
361 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
362 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
363 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
364 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
365 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
366 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
367 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
368 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
369 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
370 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
371 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
372 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
373 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
374 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
375 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
376 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
377 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
378 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
379 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
380 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
381 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
382 LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# },
383 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
384 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
385 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
386 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
387 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
388 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
389 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
390 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
391 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
392 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
393];
394
395#[cfg(test)]
396mod tests {
397 use expect_test::{expect, Expect};
398
399 use crate::{test_utils::completion_list, CompletionKind};
400
401 fn check(ra_fixture: &str, expect: Expect) {
402 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
403 expect.assert_eq(&actual);
404 }
405
406 #[test]
407 fn empty_derive_completion() {
408 check(
409 r#"
410#[derive($0)]
411struct Test {}
412 "#,
413 expect![[r#"
414 at Clone
415 at Clone, Copy
416 at Debug
417 at Default
418 at Hash
419 at PartialEq
420 at PartialEq, Eq
421 at PartialEq, PartialOrd
422 at PartialEq, Eq, PartialOrd, Ord
423 "#]],
424 );
425 }
426
427 #[test]
428 fn no_completion_for_incorrect_derive() {
429 check(
430 r#"
431#[derive{$0)]
432struct Test {}
433"#,
434 expect![[r#""#]],
435 )
436 }
437
438 #[test]
439 fn derive_with_input_completion() {
440 check(
441 r#"
442#[derive(serde::Serialize, PartialEq, $0)]
443struct Test {}
444"#,
445 expect![[r#"
446 at Clone
447 at Clone, Copy
448 at Debug
449 at Default
450 at Hash
451 at Eq
452 at PartialOrd
453 at Eq, PartialOrd, Ord
454 "#]],
455 )
456 }
457
458 #[test]
459 fn test_attribute_completion() {
460 check(
461 r#"#[$0]"#,
462 expect![[r#"
463 at allow(…)
464 at automatically_derived
465 at cfg_attr(…)
466 at cfg(…)
467 at cold
468 at deny(…)
469 at deprecated
470 at derive(…)
471 at export_name = "…"
472 at doc(alias = "…")
473 at doc = "…"
474 at forbid(…)
475 at ignore = "…"
476 at inline
477 at link
478 at link_name = "…"
479 at link_section = "…"
480 at macro_export
481 at macro_use
482 at must_use
483 at no_mangle
484 at non_exhaustive
485 at path = "…"
486 at proc_macro
487 at proc_macro_attribute
488 at proc_macro_derive(…)
489 at repr(…)
490 at should_panic
491 at target_feature = "…"
492 at test
493 at track_caller
494 at used
495 at warn(…)
496 "#]],
497 )
498 }
499
500 #[test]
501 fn test_attribute_completion_inside_nested_attr() {
502 check(r#"#[cfg($0)]"#, expect![[]])
503 }
504
505 #[test]
506 fn test_inner_attribute_completion() {
507 check(
508 r"#![$0]",
509 expect![[r#"
510 at allow(…)
511 at automatically_derived
512 at cfg_attr(…)
513 at cfg(…)
514 at cold
515 at crate_name = ""
516 at deny(…)
517 at deprecated
518 at derive(…)
519 at export_name = "…"
520 at doc(alias = "…")
521 at doc = "…"
522 at feature(…)
523 at forbid(…)
524 at global_allocator
525 at ignore = "…"
526 at inline
527 at link
528 at link_name = "…"
529 at link_section = "…"
530 at macro_export
531 at macro_use
532 at must_use
533 at no_link
534 at no_implicit_prelude
535 at no_main
536 at no_mangle
537 at no_std
538 at non_exhaustive
539 at panic_handler
540 at path = "…"
541 at proc_macro
542 at proc_macro_attribute
543 at proc_macro_derive(…)
544 at recursion_limit = …
545 at repr(…)
546 at should_panic
547 at target_feature = "…"
548 at test
549 at track_caller
550 at type_length_limit = …
551 at used
552 at warn(…)
553 at windows_subsystem = "…"
554 "#]],
555 );
556 }
557}
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
new file mode 100644
index 000000000..0880a3830
--- /dev/null
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -0,0 +1,431 @@
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}
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
new file mode 100644
index 000000000..c9f928483
--- /dev/null
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -0,0 +1,688 @@
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 syntax::{AstNode, SyntaxNode, T};
57use test_utils::mark;
58
59use crate::{
60 context::CompletionContext,
61 render::{render_resolution_with_import, RenderContext},
62 ImportEdit,
63};
64
65use super::Completions;
66
67pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
68 if !ctx.config.enable_imports_on_the_fly {
69 return None;
70 }
71 if ctx.use_item_syntax.is_some()
72 || ctx.attribute_under_caret.is_some()
73 || ctx.mod_declaration_under_caret.is_some()
74 {
75 return None;
76 }
77 let potential_import_name = {
78 let token_kind = ctx.token.kind();
79 if matches!(token_kind, T![.] | T![::]) {
80 String::new()
81 } else {
82 ctx.token.to_string()
83 }
84 };
85
86 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
87
88 let user_input_lowercased = potential_import_name.to_lowercase();
89 let import_assets = import_assets(ctx, potential_import_name)?;
90 let import_scope = ImportScope::find_insert_use_container(
91 position_for_import(ctx, Some(import_assets.import_candidate()))?,
92 &ctx.sema,
93 )?;
94 let mut all_mod_paths = import_assets
95 .search_for_relative_paths(&ctx.sema)
96 .into_iter()
97 .map(|(mod_path, item_in_ns)| {
98 let scope_item = match item_in_ns {
99 hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
100 hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
101 hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
102 };
103 (mod_path, scope_item)
104 })
105 .collect::<Vec<_>>();
106 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
107 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
108 });
109
110 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
111 let import_for_trait_assoc_item = match definition {
112 ScopeDef::ModuleDef(module_def) => module_def
113 .as_assoc_item(ctx.db)
114 .and_then(|assoc| assoc.containing_trait(ctx.db))
115 .is_some(),
116 _ => false,
117 };
118 let import_edit = ImportEdit {
119 import_path,
120 import_scope: import_scope.clone(),
121 import_for_trait_assoc_item,
122 };
123 render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
124 }));
125 Some(())
126}
127
128pub(crate) fn position_for_import<'a>(
129 ctx: &'a CompletionContext,
130 import_candidate: Option<&ImportCandidate>,
131) -> Option<&'a SyntaxNode> {
132 Some(match import_candidate {
133 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
134 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(),
135 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver.as_ref()?.syntax(),
136 None => ctx
137 .name_ref_syntax
138 .as_ref()
139 .map(|name_ref| name_ref.syntax())
140 .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax()))
141 .or_else(|| ctx.dot_receiver.as_ref().map(|expr| expr.syntax()))?,
142 })
143}
144
145fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
146 let current_module = ctx.scope.module()?;
147 if let Some(dot_receiver) = &ctx.dot_receiver {
148 ImportAssets::for_fuzzy_method_call(
149 current_module,
150 ctx.sema.type_of_expr(dot_receiver)?,
151 fuzzy_name,
152 )
153 } else {
154 let fuzzy_name_length = fuzzy_name.len();
155 let assets_for_path = ImportAssets::for_fuzzy_path(
156 current_module,
157 ctx.path_qual.clone(),
158 fuzzy_name,
159 &ctx.sema,
160 );
161
162 if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_))
163 && fuzzy_name_length < 2
164 {
165 mark::hit!(ignore_short_input_for_path);
166 None
167 } else {
168 assets_for_path
169 }
170 }
171}
172
173fn compute_fuzzy_completion_order_key(
174 proposed_mod_path: &ModPath,
175 user_input_lowercased: &str,
176) -> usize {
177 mark::hit!(certain_fuzzy_order_test);
178 let proposed_import_name = match proposed_mod_path.segments().last() {
179 Some(name) => name.to_string().to_lowercase(),
180 None => return usize::MAX,
181 };
182 match proposed_import_name.match_indices(user_input_lowercased).next() {
183 Some((first_matching_index, _)) => first_matching_index,
184 None => usize::MAX,
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use expect_test::{expect, Expect};
191 use test_utils::mark;
192
193 use crate::{
194 item::CompletionKind,
195 test_utils::{check_edit, completion_list},
196 };
197
198 fn check(ra_fixture: &str, expect: Expect) {
199 let actual = completion_list(ra_fixture, CompletionKind::Magic);
200 expect.assert_eq(&actual);
201 }
202
203 #[test]
204 fn function_fuzzy_completion() {
205 check_edit(
206 "stdin",
207 r#"
208//- /lib.rs crate:dep
209pub mod io {
210 pub fn stdin() {}
211};
212
213//- /main.rs crate:main deps:dep
214fn main() {
215 stdi$0
216}
217"#,
218 r#"
219use dep::io::stdin;
220
221fn main() {
222 stdin()$0
223}
224"#,
225 );
226 }
227
228 #[test]
229 fn macro_fuzzy_completion() {
230 check_edit(
231 "macro_with_curlies!",
232 r#"
233//- /lib.rs crate:dep
234/// Please call me as macro_with_curlies! {}
235#[macro_export]
236macro_rules! macro_with_curlies {
237 () => {}
238}
239
240//- /main.rs crate:main deps:dep
241fn main() {
242 curli$0
243}
244"#,
245 r#"
246use dep::macro_with_curlies;
247
248fn main() {
249 macro_with_curlies! {$0}
250}
251"#,
252 );
253 }
254
255 #[test]
256 fn struct_fuzzy_completion() {
257 check_edit(
258 "ThirdStruct",
259 r#"
260//- /lib.rs crate:dep
261pub struct FirstStruct;
262pub mod some_module {
263 pub struct SecondStruct;
264 pub struct ThirdStruct;
265}
266
267//- /main.rs crate:main deps:dep
268use dep::{FirstStruct, some_module::SecondStruct};
269
270fn main() {
271 this$0
272}
273"#,
274 r#"
275use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
276
277fn main() {
278 ThirdStruct
279}
280"#,
281 );
282 }
283
284 #[test]
285 fn short_paths_are_ignored() {
286 mark::check!(ignore_short_input_for_path);
287
288 check(
289 r#"
290//- /lib.rs crate:dep
291pub struct FirstStruct;
292pub mod some_module {
293 pub struct SecondStruct;
294 pub struct ThirdStruct;
295}
296
297//- /main.rs crate:main deps:dep
298use dep::{FirstStruct, some_module::SecondStruct};
299
300fn main() {
301 t$0
302}
303"#,
304 expect![[r#""#]],
305 );
306 }
307
308 #[test]
309 fn fuzzy_completions_come_in_specific_order() {
310 mark::check!(certain_fuzzy_order_test);
311 check(
312 r#"
313//- /lib.rs crate:dep
314pub struct FirstStruct;
315pub mod some_module {
316 // already imported, omitted
317 pub struct SecondStruct;
318 // does not contain all letters from the query, omitted
319 pub struct UnrelatedOne;
320 // contains all letters from the query, but not in sequence, displayed last
321 pub struct ThiiiiiirdStruct;
322 // contains all letters from the query, but not in the beginning, displayed second
323 pub struct AfterThirdStruct;
324 // contains all letters from the query in the begginning, displayed first
325 pub struct ThirdStruct;
326}
327
328//- /main.rs crate:main deps:dep
329use dep::{FirstStruct, some_module::SecondStruct};
330
331fn main() {
332 hir$0
333}
334"#,
335 expect![[r#"
336 st dep::some_module::ThirdStruct
337 st dep::some_module::AfterThirdStruct
338 st dep::some_module::ThiiiiiirdStruct
339 "#]],
340 );
341 }
342
343 #[test]
344 fn trait_function_fuzzy_completion() {
345 let fixture = r#"
346 //- /lib.rs crate:dep
347 pub mod test_mod {
348 pub trait TestTrait {
349 const SPECIAL_CONST: u8;
350 type HumbleType;
351 fn weird_function();
352 fn random_method(&self);
353 }
354 pub struct TestStruct {}
355 impl TestTrait for TestStruct {
356 const SPECIAL_CONST: u8 = 42;
357 type HumbleType = ();
358 fn weird_function() {}
359 fn random_method(&self) {}
360 }
361 }
362
363 //- /main.rs crate:main deps:dep
364 fn main() {
365 dep::test_mod::TestStruct::wei$0
366 }
367 "#;
368
369 check(
370 fixture,
371 expect![[r#"
372 fn weird_function() (dep::test_mod::TestTrait) -> ()
373 "#]],
374 );
375
376 check_edit(
377 "weird_function",
378 fixture,
379 r#"
380use dep::test_mod::TestTrait;
381
382fn main() {
383 dep::test_mod::TestStruct::weird_function()$0
384}
385"#,
386 );
387 }
388
389 #[test]
390 fn trait_const_fuzzy_completion() {
391 let fixture = r#"
392 //- /lib.rs crate:dep
393 pub mod test_mod {
394 pub trait TestTrait {
395 const SPECIAL_CONST: u8;
396 type HumbleType;
397 fn weird_function();
398 fn random_method(&self);
399 }
400 pub struct TestStruct {}
401 impl TestTrait for TestStruct {
402 const SPECIAL_CONST: u8 = 42;
403 type HumbleType = ();
404 fn weird_function() {}
405 fn random_method(&self) {}
406 }
407 }
408
409 //- /main.rs crate:main deps:dep
410 fn main() {
411 dep::test_mod::TestStruct::spe$0
412 }
413 "#;
414
415 check(
416 fixture,
417 expect![[r#"
418 ct SPECIAL_CONST (dep::test_mod::TestTrait)
419 "#]],
420 );
421
422 check_edit(
423 "SPECIAL_CONST",
424 fixture,
425 r#"
426use dep::test_mod::TestTrait;
427
428fn main() {
429 dep::test_mod::TestStruct::SPECIAL_CONST
430}
431"#,
432 );
433 }
434
435 #[test]
436 fn trait_method_fuzzy_completion() {
437 let fixture = r#"
438 //- /lib.rs crate:dep
439 pub mod test_mod {
440 pub trait TestTrait {
441 const SPECIAL_CONST: u8;
442 type HumbleType;
443 fn weird_function();
444 fn random_method(&self);
445 }
446 pub struct TestStruct {}
447 impl TestTrait for TestStruct {
448 const SPECIAL_CONST: u8 = 42;
449 type HumbleType = ();
450 fn weird_function() {}
451 fn random_method(&self) {}
452 }
453 }
454
455 //- /main.rs crate:main deps:dep
456 fn main() {
457 let test_struct = dep::test_mod::TestStruct {};
458 test_struct.ran$0
459 }
460 "#;
461
462 check(
463 fixture,
464 expect![[r#"
465 me random_method() (dep::test_mod::TestTrait) -> ()
466 "#]],
467 );
468
469 check_edit(
470 "random_method",
471 fixture,
472 r#"
473use dep::test_mod::TestTrait;
474
475fn main() {
476 let test_struct = dep::test_mod::TestStruct {};
477 test_struct.random_method()$0
478}
479"#,
480 );
481 }
482
483 #[test]
484 fn no_trait_type_fuzzy_completion() {
485 check(
486 r#"
487//- /lib.rs crate:dep
488pub mod test_mod {
489 pub trait TestTrait {
490 const SPECIAL_CONST: u8;
491 type HumbleType;
492 fn weird_function();
493 fn random_method(&self);
494 }
495 pub struct TestStruct {}
496 impl TestTrait for TestStruct {
497 const SPECIAL_CONST: u8 = 42;
498 type HumbleType = ();
499 fn weird_function() {}
500 fn random_method(&self) {}
501 }
502}
503
504//- /main.rs crate:main deps:dep
505fn main() {
506 dep::test_mod::TestStruct::hum$0
507}
508"#,
509 expect![[r#""#]],
510 );
511 }
512
513 #[test]
514 fn does_not_propose_names_in_scope() {
515 check(
516 r#"
517//- /lib.rs crate:dep
518pub mod test_mod {
519 pub trait TestTrait {
520 const SPECIAL_CONST: u8;
521 type HumbleType;
522 fn weird_function();
523 fn random_method(&self);
524 }
525 pub struct TestStruct {}
526 impl TestTrait for TestStruct {
527 const SPECIAL_CONST: u8 = 42;
528 type HumbleType = ();
529 fn weird_function() {}
530 fn random_method(&self) {}
531 }
532}
533
534//- /main.rs crate:main deps:dep
535use dep::test_mod::TestStruct;
536fn main() {
537 TestSt$0
538}
539"#,
540 expect![[r#""#]],
541 );
542 }
543
544 #[test]
545 fn does_not_propose_traits_in_scope() {
546 check(
547 r#"
548//- /lib.rs crate:dep
549pub mod test_mod {
550 pub trait TestTrait {
551 const SPECIAL_CONST: u8;
552 type HumbleType;
553 fn weird_function();
554 fn random_method(&self);
555 }
556 pub struct TestStruct {}
557 impl TestTrait for TestStruct {
558 const SPECIAL_CONST: u8 = 42;
559 type HumbleType = ();
560 fn weird_function() {}
561 fn random_method(&self) {}
562 }
563}
564
565//- /main.rs crate:main deps:dep
566use dep::test_mod::{TestStruct, TestTrait};
567fn main() {
568 dep::test_mod::TestStruct::hum$0
569}
570"#,
571 expect![[r#""#]],
572 );
573 }
574
575 #[test]
576 fn blanket_trait_impl_import() {
577 check_edit(
578 "another_function",
579 r#"
580//- /lib.rs crate:dep
581pub mod test_mod {
582 pub struct TestStruct {}
583 pub trait TestTrait {
584 fn another_function();
585 }
586 impl<T> TestTrait for T {
587 fn another_function() {}
588 }
589}
590
591//- /main.rs crate:main deps:dep
592fn main() {
593 dep::test_mod::TestStruct::ano$0
594}
595"#,
596 r#"
597use dep::test_mod::TestTrait;
598
599fn main() {
600 dep::test_mod::TestStruct::another_function()$0
601}
602"#,
603 );
604 }
605
606 #[test]
607 fn zero_input_deprecated_assoc_item_completion() {
608 check(
609 r#"
610//- /lib.rs crate:dep
611pub mod test_mod {
612 #[deprecated]
613 pub trait TestTrait {
614 const SPECIAL_CONST: u8;
615 type HumbleType;
616 fn weird_function();
617 fn random_method(&self);
618 }
619 pub struct TestStruct {}
620 impl TestTrait for TestStruct {
621 const SPECIAL_CONST: u8 = 42;
622 type HumbleType = ();
623 fn weird_function() {}
624 fn random_method(&self) {}
625 }
626}
627
628//- /main.rs crate:main deps:dep
629fn main() {
630 let test_struct = dep::test_mod::TestStruct {};
631 test_struct.$0
632}
633 "#,
634 expect![[r#"
635 me random_method() (dep::test_mod::TestTrait) -> () DEPRECATED
636 "#]],
637 );
638
639 check(
640 r#"
641//- /lib.rs crate:dep
642pub mod test_mod {
643 #[deprecated]
644 pub trait TestTrait {
645 const SPECIAL_CONST: u8;
646 type HumbleType;
647 fn weird_function();
648 fn random_method(&self);
649 }
650 pub struct TestStruct {}
651 impl TestTrait for TestStruct {
652 const SPECIAL_CONST: u8 = 42;
653 type HumbleType = ();
654 fn weird_function() {}
655 fn random_method(&self) {}
656 }
657}
658
659//- /main.rs crate:main deps:dep
660fn main() {
661 dep::test_mod::TestStruct::$0
662}
663"#,
664 expect![[r#"
665 ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED
666 fn weird_function() (dep::test_mod::TestTrait) -> () DEPRECATED
667 "#]],
668 );
669 }
670
671 #[test]
672 fn no_completions_in_use_statements() {
673 check(
674 r#"
675//- /lib.rs crate:dep
676pub mod io {
677 pub fn stdin() {}
678};
679
680//- /main.rs crate:main deps:dep
681use stdi$0
682
683fn main() {}
684"#,
685 expect![[]],
686 );
687 }
688}
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..87df7f1c9
--- /dev/null
+++ b/crates/ide_completion/src/generated_lint_completions.rs
@@ -0,0 +1,5 @@
1//! Generated file, do not edit by hand, see `xtask/src/codegen`
2
3use crate::completions::attribute::LintCompletion;
4pub (super) const FEATURES : & [LintCompletion] = & [LintCompletion { label : "non_ascii_idents" , description : "# `non_ascii_idents`\n\nThe tracking issue for this feature is: [#55467]\n\n[#55467]: https://github.com/rust-lang/rust/issues/55467\n\n------------------------\n\nThe `non_ascii_idents` feature adds support for non-ASCII identifiers.\n\n## Examples\n\n```rust\n#![feature(non_ascii_idents)]\n\nconst ε: f64 = 0.00001f64;\nconst Π: f64 = 3.14f64;\n```\n\n## Changes to the language reference\n\n> **<sup>Lexer:<sup>** \n> IDENTIFIER : \n> &nbsp;&nbsp; &nbsp;&nbsp; XID_start XID_continue<sup>\\*</sup> \n> &nbsp;&nbsp; | `_` XID_continue<sup>+</sup> \n\nAn identifier is any nonempty Unicode string of the following form:\n\nEither\n\n * The first character has property [`XID_start`]\n * The remaining characters have property [`XID_continue`]\n\nOr\n\n * The first character is `_`\n * The identifier is more than one character, `_` alone is not an identifier\n * The remaining characters have property [`XID_continue`]\n\nthat does _not_ occur in the set of [strict keywords].\n\n> **Note**: [`XID_start`] and [`XID_continue`] as character properties cover the\n> character ranges used to form the more familiar C and Java language-family\n> identifiers.\n\n[`XID_start`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i=\n[`XID_continue`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i=\n[strict keywords]: ../../reference/keywords.md#strict-keywords\n" } , LintCompletion { label : "custom_test_frameworks" , description : "# `custom_test_frameworks`\n\nThe tracking issue for this feature is: [#50297]\n\n[#50297]: https://github.com/rust-lang/rust/issues/50297\n\n------------------------\n\nThe `custom_test_frameworks` feature allows the use of `#[test_case]` and `#![test_runner]`.\nAny function, const, or static can be annotated with `#[test_case]` causing it to be aggregated (like `#[test]`)\nand be passed to the test runner determined by the `#![test_runner]` crate attribute.\n\n```rust\n#![feature(custom_test_frameworks)]\n#![test_runner(my_runner)]\n\nfn my_runner(tests: &[&i32]) {\n for t in tests {\n if **t == 0 {\n println!(\"PASSED\");\n } else {\n println!(\"FAILED\");\n }\n }\n}\n\n#[test_case]\nconst WILL_PASS: i32 = 0;\n\n#[test_case]\nconst WILL_FAIL: i32 = 4;\n```\n\n" } , LintCompletion { label : "abi_msp430_interrupt" , description : "# `abi_msp430_interrupt`\n\nThe tracking issue for this feature is: [#38487]\n\n[#38487]: https://github.com/rust-lang/rust/issues/38487\n\n------------------------\n\nIn the MSP430 architecture, interrupt handlers have a special calling\nconvention. You can use the `\"msp430-interrupt\"` ABI to make the compiler apply\nthe right calling convention to the interrupt handlers you define.\n\n<!-- NOTE(ignore) this example is specific to the msp430 target -->\n\n``` rust,ignore\n#![feature(abi_msp430_interrupt)]\n#![no_std]\n\n// Place the interrupt handler at the appropriate memory address\n// (Alternatively, you can use `#[used]` and remove `pub` and `#[no_mangle]`)\n#[link_section = \"__interrupt_vector_10\"]\n#[no_mangle]\npub static TIM0_VECTOR: extern \"msp430-interrupt\" fn() = tim0;\n\n// The interrupt handler\nextern \"msp430-interrupt\" fn tim0() {\n // ..\n}\n```\n\n``` text\n$ msp430-elf-objdump -CD ./target/msp430/release/app\nDisassembly of section __interrupt_vector_10:\n\n0000fff2 <TIM0_VECTOR>:\n fff2: 00 c0 interrupt service routine at 0xc000\n\nDisassembly of section .text:\n\n0000c000 <int::tim0>:\n c000: 00 13 reti\n```\n" } , LintCompletion { label : "link_args" , description : "# `link_args`\n\nThe tracking issue for this feature is: [#29596]\n\n[#29596]: https://github.com/rust-lang/rust/issues/29596\n\n------------------------\n\nYou can tell `rustc` how to customize linking, and that is via the `link_args`\nattribute. This attribute is applied to `extern` blocks and specifies raw flags\nwhich need to get passed to the linker when producing an artifact. An example\nusage would be:\n\n```rust,no_run\n#![feature(link_args)]\n\n#[link_args = \"-foo -bar -baz\"]\nextern {}\n# fn main() {}\n```\n\nNote that this feature is currently hidden behind the `feature(link_args)` gate\nbecause this is not a sanctioned way of performing linking. Right now `rustc`\nshells out to the system linker (`gcc` on most systems, `link.exe` on MSVC), so\nit makes sense to provide extra command line arguments, but this will not\nalways be the case. In the future `rustc` may use LLVM directly to link native\nlibraries, in which case `link_args` will have no meaning. You can achieve the\nsame effect as the `link_args` attribute with the `-C link-args` argument to\n`rustc`.\n\nIt is highly recommended to *not* use this attribute, and rather use the more\nformal `#[link(...)]` attribute on `extern` blocks instead.\n" } , LintCompletion { label : "const_eval_limit" , description : "# `const_eval_limit`\n\nThe tracking issue for this feature is: [#67217]\n\n[#67217]: https://github.com/rust-lang/rust/issues/67217\n\nThe `const_eval_limit` allows someone to limit the evaluation steps the CTFE undertakes to evaluate a `const fn`.\n" } , LintCompletion { label : "marker_trait_attr" , description : "# `marker_trait_attr`\n\nThe tracking issue for this feature is: [#29864]\n\n[#29864]: https://github.com/rust-lang/rust/issues/29864\n\n------------------------\n\nNormally, Rust keeps you from adding trait implementations that could\noverlap with each other, as it would be ambiguous which to use. This\nfeature, however, carves out an exception to that rule: a trait can\nopt-in to having overlapping implementations, at the cost that those\nimplementations are not allowed to override anything (and thus the\ntrait itself cannot have any associated items, as they're pointless\nwhen they'd need to do the same thing for every type anyway).\n\n```rust\n#![feature(marker_trait_attr)]\n\n#[marker] trait CheapToClone: Clone {}\n\nimpl<T: Copy> CheapToClone for T {}\n\n// These could potentially overlap with the blanket implementation above,\n// so are only allowed because CheapToClone is a marker trait.\nimpl<T: CheapToClone, U: CheapToClone> CheapToClone for (T, U) {}\nimpl<T: CheapToClone> CheapToClone for std::ops::Range<T> {}\n\nfn cheap_clone<T: CheapToClone>(t: T) -> T {\n t.clone()\n}\n```\n\nThis is expected to replace the unstable `overlapping_marker_traits`\nfeature, which applied to all empty traits (without needing an opt-in).\n" } , LintCompletion { label : "ffi_const" , description : "# `ffi_const`\n\nThe tracking issue for this feature is: [#58328]\n\n------\n\nThe `#[ffi_const]` attribute applies clang's `const` attribute to foreign\nfunctions declarations.\n\nThat is, `#[ffi_const]` functions shall have no effects except for its return\nvalue, which can only depend on the values of the function parameters, and is\nnot affected by changes to the observable state of the program.\n\nApplying the `#[ffi_const]` attribute to a function that violates these\nrequirements is undefined behaviour.\n\nThis attribute enables Rust to perform common optimizations, like sub-expression\nelimination, and it can avoid emitting some calls in repeated invocations of the\nfunction with the same argument values regardless of other operations being\nperformed in between these functions calls (as opposed to `#[ffi_pure]`\nfunctions).\n\n## Pitfalls\n\nA `#[ffi_const]` function can only read global memory that would not affect\nits return value for the whole execution of the program (e.g. immutable global\nmemory). `#[ffi_const]` functions are referentially-transparent and therefore\nmore strict than `#[ffi_pure]` functions.\n\nA common pitfall involves applying the `#[ffi_const]` attribute to a\nfunction that reads memory through pointer arguments which do not necessarily\npoint to immutable global memory.\n\nA `#[ffi_const]` function that returns unit has no effect on the abstract\nmachine's state, and a `#[ffi_const]` function cannot be `#[ffi_pure]`.\n\nA `#[ffi_const]` function must not diverge, neither via a side effect (e.g. a\ncall to `abort`) nor by infinite loops.\n\nWhen translating C headers to Rust FFI, it is worth verifying for which targets\nthe `const` attribute is enabled in those headers, and using the appropriate\n`cfg` macros in the Rust side to match those definitions. While the semantics of\n`const` are implemented identically by many C and C++ compilers, e.g., clang,\n[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily\nimplemented in this way on all of them. It is therefore also worth verifying\nthat the semantics of the C toolchain used to compile the binary being linked\nagainst are compatible with those of the `#[ffi_const]`.\n\n[#58328]: https://github.com/rust-lang/rust/issues/58328\n[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacgigch.html\n[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute\n[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_const.htm\n" } , LintCompletion { label : "doc_spotlight" , description : "# `doc_spotlight`\n\nThe tracking issue for this feature is: [#45040]\n\nThe `doc_spotlight` feature allows the use of the `spotlight` parameter to the `#[doc]` attribute,\nto \"spotlight\" a specific trait on the return values of functions. Adding a `#[doc(spotlight)]`\nattribute to a trait definition will make rustdoc print extra information for functions which return\na type that implements that trait. This attribute is applied to the `Iterator`, `io::Read`, and\n`io::Write` traits in the standard library.\n\nYou can do this on your own traits, like this:\n\n```\n#![feature(doc_spotlight)]\n\n#[doc(spotlight)]\npub trait MyTrait {}\n\npub struct MyStruct;\nimpl MyTrait for MyStruct {}\n\n/// The docs for this function will have an extra line about `MyStruct` implementing `MyTrait`,\n/// without having to write that yourself!\npub fn my_fn() -> MyStruct { MyStruct }\n```\n\nThis feature was originally implemented in PR [#45039].\n\n[#45040]: https://github.com/rust-lang/rust/issues/45040\n[#45039]: https://github.com/rust-lang/rust/pull/45039\n" } , LintCompletion { label : "compiler_builtins" , description : "# `compiler_builtins`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "lang_items" , description : "# `lang_items`\n\nThe tracking issue for this feature is: None.\n\n------------------------\n\nThe `rustc` compiler has certain pluggable operations, that is,\nfunctionality that isn't hard-coded into the language, but is\nimplemented in libraries, with a special marker to tell the compiler\nit exists. The marker is the attribute `#[lang = \"...\"]` and there are\nvarious different values of `...`, i.e. various different 'lang\nitems'.\n\nFor example, `Box` pointers require two lang items, one for allocation\nand one for deallocation. A freestanding program that uses the `Box`\nsugar for dynamic allocations via `malloc` and `free`:\n\n```rust,ignore\n#![feature(lang_items, box_syntax, start, libc, core_intrinsics)]\n#![no_std]\nuse core::intrinsics;\nuse core::panic::PanicInfo;\n\nextern crate libc;\n\n#[lang = \"owned_box\"]\npub struct Box<T>(*mut T);\n\n#[lang = \"exchange_malloc\"]\nunsafe fn allocate(size: usize, _align: usize) -> *mut u8 {\n let p = libc::malloc(size as libc::size_t) as *mut u8;\n\n // Check if `malloc` failed:\n if p as usize == 0 {\n intrinsics::abort();\n }\n\n p\n}\n\n#[lang = \"box_free\"]\nunsafe fn box_free<T: ?Sized>(ptr: *mut T) {\n libc::free(ptr as *mut libc::c_void)\n}\n\n#[start]\nfn main(_argc: isize, _argv: *const *const u8) -> isize {\n let _x = box 1;\n\n 0\n}\n\n#[lang = \"eh_personality\"] extern fn rust_eh_personality() {}\n#[lang = \"panic_impl\"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } }\n#[no_mangle] pub extern fn rust_eh_register_frames () {}\n#[no_mangle] pub extern fn rust_eh_unregister_frames () {}\n```\n\nNote the use of `abort`: the `exchange_malloc` lang item is assumed to\nreturn a valid pointer, and so needs to do the check internally.\n\nOther features provided by lang items include:\n\n- overloadable operators via traits: the traits corresponding to the\n `==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all\n marked with lang items; those specific four are `eq`, `ord`,\n `deref`, and `add` respectively.\n- stack unwinding and general failure; the `eh_personality`,\n `panic` and `panic_bounds_checks` lang items.\n- the traits in `std::marker` used to indicate types of\n various kinds; lang items `send`, `sync` and `copy`.\n- the marker types and variance indicators found in\n `std::marker`; lang items `covariant_type`,\n `contravariant_lifetime`, etc.\n\nLang items are loaded lazily by the compiler; e.g. if one never uses\n`Box` then there is no need to define functions for `exchange_malloc`\nand `box_free`. `rustc` will emit an error when an item is needed\nbut not found in the current crate or any that it depends on.\n\nMost lang items are defined by `libcore`, but if you're trying to build\nan executable without the standard library, you'll run into the need\nfor lang items. The rest of this page focuses on this use-case, even though\nlang items are a bit broader than that.\n\n### Using libc\n\nIn order to build a `#[no_std]` executable we will need libc as a dependency.\nWe can specify this using our `Cargo.toml` file:\n\n```toml\n[dependencies]\nlibc = { version = \"0.2.14\", default-features = false }\n```\n\nNote that the default features have been disabled. This is a critical step -\n**the default features of libc include the standard library and so must be\ndisabled.**\n\n### Writing an executable without stdlib\n\nControlling the entry point is possible in two ways: the `#[start]` attribute,\nor overriding the default shim for the C `main` function with your own.\n\nThe function marked `#[start]` is passed the command line parameters\nin the same format as C:\n\n```rust,ignore\n#![feature(lang_items, core_intrinsics)]\n#![feature(start)]\n#![no_std]\nuse core::intrinsics;\nuse core::panic::PanicInfo;\n\n// Pull in the system libc library for what crt0.o likely requires.\nextern crate libc;\n\n// Entry point for this program.\n#[start]\nfn start(_argc: isize, _argv: *const *const u8) -> isize {\n 0\n}\n\n// These functions are used by the compiler, but not\n// for a bare-bones hello world. These are normally\n// provided by libstd.\n#[lang = \"eh_personality\"]\n#[no_mangle]\npub extern fn rust_eh_personality() {\n}\n\n#[lang = \"panic_impl\"]\n#[no_mangle]\npub extern fn rust_begin_panic(info: &PanicInfo) -> ! {\n unsafe { intrinsics::abort() }\n}\n```\n\nTo override the compiler-inserted `main` shim, one has to disable it\nwith `#![no_main]` and then create the appropriate symbol with the\ncorrect ABI and the correct name, which requires overriding the\ncompiler's name mangling too:\n\n```rust,ignore\n#![feature(lang_items, core_intrinsics)]\n#![feature(start)]\n#![no_std]\n#![no_main]\nuse core::intrinsics;\nuse core::panic::PanicInfo;\n\n// Pull in the system libc library for what crt0.o likely requires.\nextern crate libc;\n\n// Entry point for this program.\n#[no_mangle] // ensure that this symbol is called `main` in the output\npub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 {\n 0\n}\n\n// These functions are used by the compiler, but not\n// for a bare-bones hello world. These are normally\n// provided by libstd.\n#[lang = \"eh_personality\"]\n#[no_mangle]\npub extern fn rust_eh_personality() {\n}\n\n#[lang = \"panic_impl\"]\n#[no_mangle]\npub extern fn rust_begin_panic(info: &PanicInfo) -> ! {\n unsafe { intrinsics::abort() }\n}\n```\n\nIn many cases, you may need to manually link to the `compiler_builtins` crate\nwhen building a `no_std` binary. You may observe this via linker error messages\nsuch as \"```undefined reference to `__rust_probestack'```\".\n\n## More about the language items\n\nThe compiler currently makes a few assumptions about symbols which are\navailable in the executable to call. Normally these functions are provided by\nthe standard library, but without it you must define your own. These symbols\nare called \"language items\", and they each have an internal name, and then a\nsignature that an implementation must conform to.\n\nThe first of these functions, `rust_eh_personality`, is used by the failure\nmechanisms of the compiler. This is often mapped to GCC's personality function\n(see the [libstd implementation][unwind] for more information), but crates\nwhich do not trigger a panic can be assured that this function is never\ncalled. The language item's name is `eh_personality`.\n\n[unwind]: https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs\n\nThe second function, `rust_begin_panic`, is also used by the failure mechanisms of the\ncompiler. When a panic happens, this controls the message that's displayed on\nthe screen. While the language item's name is `panic_impl`, the symbol name is\n`rust_begin_panic`.\n\nFinally, a `eh_catch_typeinfo` static is needed for certain targets which\nimplement Rust panics on top of C++ exceptions.\n\n## List of all language items\n\nThis is a list of all language items in Rust along with where they are located in\nthe source code.\n\n- Primitives\n - `i8`: `libcore/num/mod.rs`\n - `i16`: `libcore/num/mod.rs`\n - `i32`: `libcore/num/mod.rs`\n - `i64`: `libcore/num/mod.rs`\n - `i128`: `libcore/num/mod.rs`\n - `isize`: `libcore/num/mod.rs`\n - `u8`: `libcore/num/mod.rs`\n - `u16`: `libcore/num/mod.rs`\n - `u32`: `libcore/num/mod.rs`\n - `u64`: `libcore/num/mod.rs`\n - `u128`: `libcore/num/mod.rs`\n - `usize`: `libcore/num/mod.rs`\n - `f32`: `libstd/f32.rs`\n - `f64`: `libstd/f64.rs`\n - `char`: `libcore/char.rs`\n - `slice`: `liballoc/slice.rs`\n - `str`: `liballoc/str.rs`\n - `const_ptr`: `libcore/ptr.rs`\n - `mut_ptr`: `libcore/ptr.rs`\n - `unsafe_cell`: `libcore/cell.rs`\n- Runtime\n - `start`: `libstd/rt.rs`\n - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC)\n - `eh_personality`: `libpanic_unwind/gcc.rs` (GNU)\n - `eh_personality`: `libpanic_unwind/seh.rs` (SEH)\n - `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC)\n - `panic`: `libcore/panicking.rs`\n - `panic_bounds_check`: `libcore/panicking.rs`\n - `panic_impl`: `libcore/panicking.rs`\n - `panic_impl`: `libstd/panicking.rs`\n- Allocations\n - `owned_box`: `liballoc/boxed.rs`\n - `exchange_malloc`: `liballoc/heap.rs`\n - `box_free`: `liballoc/heap.rs`\n- Operands\n - `not`: `libcore/ops/bit.rs`\n - `bitand`: `libcore/ops/bit.rs`\n - `bitor`: `libcore/ops/bit.rs`\n - `bitxor`: `libcore/ops/bit.rs`\n - `shl`: `libcore/ops/bit.rs`\n - `shr`: `libcore/ops/bit.rs`\n - `bitand_assign`: `libcore/ops/bit.rs`\n - `bitor_assign`: `libcore/ops/bit.rs`\n - `bitxor_assign`: `libcore/ops/bit.rs`\n - `shl_assign`: `libcore/ops/bit.rs`\n - `shr_assign`: `libcore/ops/bit.rs`\n - `deref`: `libcore/ops/deref.rs`\n - `deref_mut`: `libcore/ops/deref.rs`\n - `index`: `libcore/ops/index.rs`\n - `index_mut`: `libcore/ops/index.rs`\n - `add`: `libcore/ops/arith.rs`\n - `sub`: `libcore/ops/arith.rs`\n - `mul`: `libcore/ops/arith.rs`\n - `div`: `libcore/ops/arith.rs`\n - `rem`: `libcore/ops/arith.rs`\n - `neg`: `libcore/ops/arith.rs`\n - `add_assign`: `libcore/ops/arith.rs`\n - `sub_assign`: `libcore/ops/arith.rs`\n - `mul_assign`: `libcore/ops/arith.rs`\n - `div_assign`: `libcore/ops/arith.rs`\n - `rem_assign`: `libcore/ops/arith.rs`\n - `eq`: `libcore/cmp.rs`\n - `ord`: `libcore/cmp.rs`\n- Functions\n - `fn`: `libcore/ops/function.rs`\n - `fn_mut`: `libcore/ops/function.rs`\n - `fn_once`: `libcore/ops/function.rs`\n - `generator_state`: `libcore/ops/generator.rs`\n - `generator`: `libcore/ops/generator.rs`\n- Other\n - `coerce_unsized`: `libcore/ops/unsize.rs`\n - `drop`: `libcore/ops/drop.rs`\n - `drop_in_place`: `libcore/ptr.rs`\n - `clone`: `libcore/clone.rs`\n - `copy`: `libcore/marker.rs`\n - `send`: `libcore/marker.rs`\n - `sized`: `libcore/marker.rs`\n - `unsize`: `libcore/marker.rs`\n - `sync`: `libcore/marker.rs`\n - `phantom_data`: `libcore/marker.rs`\n - `discriminant_kind`: `libcore/marker.rs`\n - `freeze`: `libcore/marker.rs`\n - `debug_trait`: `libcore/fmt/mod.rs`\n - `non_zero`: `libcore/nonzero.rs`\n - `arc`: `liballoc/sync.rs`\n - `rc`: `liballoc/rc.rs`\n" } , LintCompletion { label : "member_constraints" , description : "# `member_constraints`\n\nThe tracking issue for this feature is: [#61997]\n\n[#61997]: https://github.com/rust-lang/rust/issues/61997\n\n------------------------\n\nThe `member_constraints` feature gate lets you use `impl Trait` syntax with\nmultiple unrelated lifetime parameters.\n\nA simple example is:\n\n```rust\n#![feature(member_constraints)]\n\ntrait Trait<'a, 'b> { }\nimpl<T> Trait<'_, '_> for T {}\n\nfn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> {\n (x, y)\n}\n\nfn main() { }\n```\n\nWithout the `member_constraints` feature gate, the above example is an\nerror because both `'a` and `'b` appear in the impl Trait bounds, but\nneither outlives the other.\n" } , LintCompletion { label : "crate_visibility_modifier" , description : "# `crate_visibility_modifier`\n\nThe tracking issue for this feature is: [#53120]\n\n[#53120]: https://github.com/rust-lang/rust/issues/53120\n\n-----\n\nThe `crate_visibility_modifier` feature allows the `crate` keyword to be used\nas a visibility modifier synonymous to `pub(crate)`, indicating that a type\n(function, _&c._) is to be visible to the entire enclosing crate, but not to\nother crates.\n\n```rust\n#![feature(crate_visibility_modifier)]\n\ncrate struct Foo {\n bar: usize,\n}\n```\n" } , LintCompletion { label : "try_blocks" , description : "# `try_blocks`\n\nThe tracking issue for this feature is: [#31436]\n\n[#31436]: https://github.com/rust-lang/rust/issues/31436\n\n------------------------\n\nThe `try_blocks` feature adds support for `try` blocks. A `try`\nblock creates a new scope one can use the `?` operator in.\n\n```rust,edition2018\n#![feature(try_blocks)]\n\nuse std::num::ParseIntError;\n\nlet result: Result<i32, ParseIntError> = try {\n \"1\".parse::<i32>()?\n + \"2\".parse::<i32>()?\n + \"3\".parse::<i32>()?\n};\nassert_eq!(result, Ok(6));\n\nlet result: Result<i32, ParseIntError> = try {\n \"1\".parse::<i32>()?\n + \"foo\".parse::<i32>()?\n + \"3\".parse::<i32>()?\n};\nassert!(result.is_err());\n```\n" } , LintCompletion { label : "const_in_array_repeat_expressions" , description : "# `const_in_array_repeat_expressions`\n\nThe tracking issue for this feature is: [#49147]\n\n[#49147]: https://github.com/rust-lang/rust/issues/49147\n\n------------------------\n\nRelaxes the rules for repeat expressions, `[x; N]` such that `x` may also be `const` (strictly\nspeaking rvalue promotable), in addition to `typeof(x): Copy`. The result of `[x; N]` where `x` is\n`const` is itself also `const`.\n" } , LintCompletion { label : "negative_impls" , description : "# `negative_impls`\n\nThe tracking issue for this feature is [#68318].\n\n[#68318]: https://github.com/rust-lang/rust/issues/68318\n\n----\n\nWith the feature gate `negative_impls`, you can write negative impls as well as positive ones:\n\n```rust\n#![feature(negative_impls)]\ntrait DerefMut { }\nimpl<T: ?Sized> !DerefMut for &T { }\n```\n\nNegative 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.\n\nNegative impls have the following characteristics:\n\n* They do not have any items.\n* They must obey the orphan rules as if they were a positive impl.\n* They cannot \"overlap\" with any positive impls.\n\n## Semver interaction\n\nIt is a breaking change to remove a negative impl. Negative impls are a commitment not to implement the given trait for the named types.\n\n## Orphan and overlap rules\n\nNegative 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.\n\nSimilarly, 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.)\n\n## Interaction with auto traits\n\nDeclaring a negative impl `impl !SomeAutoTrait for SomeType` for an\nauto-trait serves two purposes:\n\n* as with any trait, it declares that `SomeType` will never implement `SomeAutoTrait`;\n* it disables the automatic `SomeType: SomeAutoTrait` impl that would otherwise have been generated.\n\nNote that, at present, there is no way to indicate that a given type\ndoes not implement an auto trait *but that it may do so in the\nfuture*. For ordinary types, this is done by simply not declaring any\nimpl at all, but that is not an option for auto traits. A workaround\nis that one could embed a marker type as one of the fields, where the\nmarker type is `!AutoTrait`.\n\n## Immediate uses\n\nNegative 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).\n\nThis serves two purposes:\n\n* For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists.\n* It prevents downstream crates from creating such impls.\n" } , LintCompletion { label : "c_variadic" , description : "# `c_variadic`\n\nThe tracking issue for this feature is: [#44930]\n\n[#44930]: https://github.com/rust-lang/rust/issues/44930\n\n------------------------\n\nThe `c_variadic` language feature enables C-variadic functions to be\ndefined in Rust. The may be called both from within Rust and via FFI.\n\n## Examples\n\n```rust\n#![feature(c_variadic)]\n\npub unsafe extern \"C\" fn add(n: usize, mut args: ...) -> usize {\n let mut sum = 0;\n for _ in 0..n {\n sum += args.arg::<usize>();\n }\n sum\n}\n```\n" } , LintCompletion { label : "profiler_runtime" , description : "# `profiler_runtime`\n\nThe tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524).\n\n------------------------\n" } , LintCompletion { label : "box_syntax" , description : "# `box_syntax`\n\nThe tracking issue for this feature is: [#49733]\n\n[#49733]: https://github.com/rust-lang/rust/issues/49733\n\nSee also [`box_patterns`](box-patterns.md)\n\n------------------------\n\nCurrently the only stable way to create a `Box` is via the `Box::new` method.\nAlso it is not possible in stable Rust to destructure a `Box` in a match\npattern. The unstable `box` keyword can be used to create a `Box`. An example\nusage would be:\n\n```rust\n#![feature(box_syntax)]\n\nfn main() {\n let b = box 5;\n}\n```\n" } , LintCompletion { label : "ffi_pure" , description : "# `ffi_pure`\n\nThe tracking issue for this feature is: [#58329]\n\n------\n\nThe `#[ffi_pure]` attribute applies clang's `pure` attribute to foreign\nfunctions declarations.\n\nThat is, `#[ffi_pure]` functions shall have no effects except for its return\nvalue, which shall not change across two consecutive function calls with\nthe same parameters.\n\nApplying the `#[ffi_pure]` attribute to a function that violates these\nrequirements is undefined behavior.\n\nThis attribute enables Rust to perform common optimizations, like sub-expression\nelimination and loop optimizations. Some common examples of pure functions are\n`strlen` or `memcmp`.\n\nThese optimizations are only applicable when the compiler can prove that no\nprogram state observable by the `#[ffi_pure]` function has changed between calls\nof the function, which could alter the result. See also the `#[ffi_const]`\nattribute, which provides stronger guarantees regarding the allowable behavior\nof a function, enabling further optimization.\n\n## Pitfalls\n\nA `#[ffi_pure]` function can read global memory through the function\nparameters (e.g. pointers), globals, etc. `#[ffi_pure]` functions are not\nreferentially-transparent, and are therefore more relaxed than `#[ffi_const]`\nfunctions.\n\nHowever, accesing global memory through volatile or atomic reads can violate the\nrequirement that two consecutive function calls shall return the same value.\n\nA `pure` function that returns unit has no effect on the abstract machine's\nstate.\n\nA `#[ffi_pure]` function must not diverge, neither via a side effect (e.g. a\ncall to `abort`) nor by infinite loops.\n\nWhen translating C headers to Rust FFI, it is worth verifying for which targets\nthe `pure` attribute is enabled in those headers, and using the appropriate\n`cfg` macros in the Rust side to match those definitions. While the semantics of\n`pure` are implemented identically by many C and C++ compilers, e.g., clang,\n[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily\nimplemented in this way on all of them. It is therefore also worth verifying\nthat the semantics of the C toolchain used to compile the binary being linked\nagainst are compatible with those of the `#[ffi_pure]`.\n\n\n[#58329]: https://github.com/rust-lang/rust/issues/58329\n[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacigdac.html\n[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute\n[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_pure.htm\n" } , LintCompletion { label : "arbitrary_enum_discriminant" , description : "# `arbitrary_enum_discriminant`\n\nThe tracking issue for this feature is: [#60553]\n\n[#60553]: https://github.com/rust-lang/rust/issues/60553\n\n------------------------\n\nThe `arbitrary_enum_discriminant` feature permits tuple-like and\nstruct-like enum variants with `#[repr(<int-type>)]` to have explicit discriminants.\n\n## Examples\n\n```rust\n#![feature(arbitrary_enum_discriminant)]\n\n#[allow(dead_code)]\n#[repr(u8)]\nenum Enum {\n Unit = 3,\n Tuple(u16) = 2,\n Struct {\n a: u8,\n b: u16,\n } = 1,\n}\n\nimpl Enum {\n fn tag(&self) -> u8 {\n unsafe { *(self as *const Self as *const u8) }\n }\n}\n\nassert_eq!(3, Enum::Unit.tag());\nassert_eq!(2, Enum::Tuple(5).tag());\nassert_eq!(1, Enum::Struct{a: 7, b: 11}.tag());\n```\n" } , LintCompletion { label : "unsized_locals" , description : "# `unsized_locals`\n\nThe tracking issue for this feature is: [#48055]\n\n[#48055]: https://github.com/rust-lang/rust/issues/48055\n\n------------------------\n\nThis implements [RFC1909]. When turned on, you can have unsized arguments and locals:\n\n[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md\n\n```rust\n#![feature(unsized_locals)]\n\nuse std::any::Any;\n\nfn main() {\n let x: Box<dyn Any> = Box::new(42);\n let x: dyn Any = *x;\n // ^ unsized local variable\n // ^^ unsized temporary\n foo(x);\n}\n\nfn foo(_: dyn Any) {}\n// ^^^^^^ unsized argument\n```\n\nThe RFC still forbids the following unsized expressions:\n\n```rust,ignore\n#![feature(unsized_locals)]\n\nuse std::any::Any;\n\nstruct MyStruct<T: ?Sized> {\n content: T,\n}\n\nstruct MyTupleStruct<T: ?Sized>(T);\n\nfn answer() -> Box<dyn Any> {\n Box::new(42)\n}\n\nfn main() {\n // You CANNOT have unsized statics.\n static X: dyn Any = *answer(); // ERROR\n const Y: dyn Any = *answer(); // ERROR\n\n // You CANNOT have struct initialized unsized.\n MyStruct { content: *answer() }; // ERROR\n MyTupleStruct(*answer()); // ERROR\n (42, *answer()); // ERROR\n\n // You CANNOT have unsized return types.\n fn my_function() -> dyn Any { *answer() } // ERROR\n\n // You CAN have unsized local variables...\n let mut x: dyn Any = *answer(); // OK\n // ...but you CANNOT reassign to them.\n x = *answer(); // ERROR\n\n // You CANNOT even initialize them separately.\n let y: dyn Any; // OK\n y = *answer(); // ERROR\n\n // Not mentioned in the RFC, but by-move captured variables are also Sized.\n let x: dyn Any = *answer();\n (move || { // ERROR\n let y = x;\n })();\n\n // You CAN create a closure with unsized arguments,\n // but you CANNOT call it.\n // This is an implementation detail and may be changed in the future.\n let f = |x: dyn Any| {};\n f(*answer()); // ERROR\n}\n```\n\n## By-value trait objects\n\nWith this feature, you can have by-value `self` arguments without `Self: Sized` bounds.\n\n```rust\n#![feature(unsized_locals)]\n\ntrait Foo {\n fn foo(self) {}\n}\n\nimpl<T: ?Sized> Foo for T {}\n\nfn main() {\n let slice: Box<[i32]> = Box::new([1, 2, 3]);\n <[i32] as Foo>::foo(*slice);\n}\n```\n\nAnd `Foo` will also be object-safe.\n\n```rust\n#![feature(unsized_locals)]\n\ntrait Foo {\n fn foo(self) {}\n}\n\nimpl<T: ?Sized> Foo for T {}\n\nfn main () {\n let slice: Box<dyn Foo> = Box::new([1, 2, 3]);\n // doesn't compile yet\n <dyn Foo as Foo>::foo(*slice);\n}\n```\n\nOne of the objectives of this feature is to allow `Box<dyn FnOnce>`.\n\n## Variable length arrays\n\nThe RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`.\n\n```rust,ignore\n#![feature(unsized_locals)]\n\nfn mergesort<T: Ord>(a: &mut [T]) {\n let mut tmp = [T; dyn a.len()];\n // ...\n}\n\nfn main() {\n let mut a = [3, 1, 5, 6];\n mergesort(&mut a);\n assert_eq!(a, [1, 3, 5, 6]);\n}\n```\n\nVLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`.\n\n## Advisory on stack usage\n\nIt's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are:\n\n- When you need a by-value trait objects.\n- When you really need a fast allocation of small temporary arrays.\n\nAnother pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code\n\n```rust\n#![feature(unsized_locals)]\n\nfn main() {\n let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);\n let _x = {{{{{{{{{{*x}}}}}}}}}};\n}\n```\n\nand the code\n\n```rust\n#![feature(unsized_locals)]\n\nfn main() {\n for _ in 0..10 {\n let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);\n let _x = *x;\n }\n}\n```\n\nwill unnecessarily extend the stack frame.\n" } , LintCompletion { label : "cfg_sanitize" , description : "# `cfg_sanitize`\n\nThe tracking issue for this feature is: [#39699]\n\n[#39699]: https://github.com/rust-lang/rust/issues/39699\n\n------------------------\n\nThe `cfg_sanitize` feature makes it possible to execute different code\ndepending on whether a particular sanitizer is enabled or not.\n\n## Examples\n\n```rust\n#![feature(cfg_sanitize)]\n\n#[cfg(sanitize = \"thread\")]\nfn a() {\n // ...\n}\n\n#[cfg(not(sanitize = \"thread\"))]\nfn a() {\n // ...\n}\n\nfn b() {\n if cfg!(sanitize = \"leak\") {\n // ...\n } else {\n // ...\n }\n}\n```\n" } , LintCompletion { label : "cmse_nonsecure_entry" , description : "# `cmse_nonsecure_entry`\n\nThe tracking issue for this feature is: [#75835]\n\n[#75835]: https://github.com/rust-lang/rust/issues/75835\n\n------------------------\n\nThe [TrustZone-M\nfeature](https://developer.arm.com/documentation/100690/latest/) is available\nfor targets with the Armv8-M architecture profile (`thumbv8m` in their target\nname).\nLLVM, the Rust compiler and the linker are providing\n[support](https://developer.arm.com/documentation/ecm0359818/latest/) for the\nTrustZone-M feature.\n\nOne of the things provided, with this unstable feature, is the\n`cmse_nonsecure_entry` attribute. This attribute marks a Secure function as an\nentry function (see [section\n5.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details).\nWith this attribute, the compiler will do the following:\n* add a special symbol on the function which is the `__acle_se_` prefix and the\n standard function name\n* constrain the number of parameters to avoid using the Non-Secure stack\n* before returning from the function, clear registers that might contain Secure\n information\n* use the `BXNS` instruction to return\n\nBecause the stack can not be used to pass parameters, there will be compilation\nerrors if:\n* the total size of all parameters is too big (for example more than four 32\n bits integers)\n* the entry function is not using a C ABI\n\nThe special symbol `__acle_se_` will be used by the linker to generate a secure\ngateway veneer.\n\n<!-- NOTE(ignore) this example is specific to thumbv8m targets -->\n\n``` rust,ignore\n#![feature(cmse_nonsecure_entry)]\n\n#[no_mangle]\n#[cmse_nonsecure_entry]\npub extern \"C\" fn entry_function(input: u32) -> u32 {\n input + 6\n}\n```\n\n``` text\n$ rustc --emit obj --crate-type lib --target thumbv8m.main-none-eabi function.rs\n$ arm-none-eabi-objdump -D function.o\n\n00000000 <entry_function>:\n 0: b580 push {r7, lr}\n 2: 466f mov r7, sp\n 4: b082 sub sp, #8\n 6: 9001 str r0, [sp, #4]\n 8: 1d81 adds r1, r0, #6\n a: 460a mov r2, r1\n c: 4281 cmp r1, r0\n e: 9200 str r2, [sp, #0]\n 10: d30b bcc.n 2a <entry_function+0x2a>\n 12: e7ff b.n 14 <entry_function+0x14>\n 14: 9800 ldr r0, [sp, #0]\n 16: b002 add sp, #8\n 18: e8bd 4080 ldmia.w sp!, {r7, lr}\n 1c: 4671 mov r1, lr\n 1e: 4672 mov r2, lr\n 20: 4673 mov r3, lr\n 22: 46f4 mov ip, lr\n 24: f38e 8800 msr CPSR_f, lr\n 28: 4774 bxns lr\n 2a: f240 0000 movw r0, #0\n 2e: f2c0 0000 movt r0, #0\n 32: f240 0200 movw r2, #0\n 36: f2c0 0200 movt r2, #0\n 3a: 211c movs r1, #28\n 3c: f7ff fffe bl 0 <_ZN4core9panicking5panic17h5c028258ca2fb3f5E>\n 40: defe udf #254 ; 0xfe\n```\n" } , LintCompletion { label : "cfg_version" , description : "# `cfg_version`\n\nThe tracking issue for this feature is: [#64796]\n\n[#64796]: https://github.com/rust-lang/rust/issues/64796\n\n------------------------\n\nThe `cfg_version` feature makes it possible to execute different code\ndepending on the compiler version.\n\n## Examples\n\n```rust\n#![feature(cfg_version)]\n\n#[cfg(version(\"1.42\"))]\nfn a() {\n // ...\n}\n\n#[cfg(not(version(\"1.42\")))]\nfn a() {\n // ...\n}\n\nfn b() {\n if cfg!(version(\"1.42\")) {\n // ...\n } else {\n // ...\n }\n}\n```\n" } , LintCompletion { label : "unsized_tuple_coercion" , description : "# `unsized_tuple_coercion`\n\nThe tracking issue for this feature is: [#42877]\n\n[#42877]: https://github.com/rust-lang/rust/issues/42877\n\n------------------------\n\nThis is a part of [RFC0401]. According to the RFC, there should be an implementation like this:\n\n```rust,ignore\nimpl<..., T, U: ?Sized> Unsized<(..., U)> for (..., T) where T: Unsized<U> {}\n```\n\nThis implementation is currently gated behind `#[feature(unsized_tuple_coercion)]` to avoid insta-stability. Therefore you can use it like this:\n\n```rust\n#![feature(unsized_tuple_coercion)]\n\nfn main() {\n let x : ([i32; 3], [i32; 3]) = ([1, 2, 3], [4, 5, 6]);\n let y : &([i32; 3], [i32]) = &x;\n assert_eq!(y.1[0], 4);\n}\n```\n\n[RFC0401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md\n" } , LintCompletion { label : "generators" , description : "# `generators`\n\nThe tracking issue for this feature is: [#43122]\n\n[#43122]: https://github.com/rust-lang/rust/issues/43122\n\n------------------------\n\nThe `generators` feature gate in Rust allows you to define generator or\ncoroutine literals. A generator is a \"resumable function\" that syntactically\nresembles a closure but compiles to much different semantics in the compiler\nitself. The primary feature of a generator is that it can be suspended during\nexecution to be resumed at a later date. Generators use the `yield` keyword to\n\"return\", and then the caller can `resume` a generator to resume execution just\nafter the `yield` keyword.\n\nGenerators are an extra-unstable feature in the compiler right now. Added in\n[RFC 2033] they're mostly intended right now as a information/constraint\ngathering phase. The intent is that experimentation can happen on the nightly\ncompiler before actual stabilization. A further RFC will be required to\nstabilize generators/coroutines and will likely contain at least a few small\ntweaks to the overall design.\n\n[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033\n\nA syntactical example of a generator is:\n\n```rust\n#![feature(generators, generator_trait)]\n\nuse std::ops::{Generator, GeneratorState};\nuse std::pin::Pin;\n\nfn main() {\n let mut generator = || {\n yield 1;\n return \"foo\"\n };\n\n match Pin::new(&mut generator).resume(()) {\n GeneratorState::Yielded(1) => {}\n _ => panic!(\"unexpected value from resume\"),\n }\n match Pin::new(&mut generator).resume(()) {\n GeneratorState::Complete(\"foo\") => {}\n _ => panic!(\"unexpected value from resume\"),\n }\n}\n```\n\nGenerators are closure-like literals which can contain a `yield` statement. The\n`yield` statement takes an optional expression of a value to yield out of the\ngenerator. All generator literals implement the `Generator` trait in the\n`std::ops` module. The `Generator` trait has one main method, `resume`, which\nresumes execution of the generator at the previous suspension point.\n\nAn example of the control flow of generators is that the following example\nprints all numbers in order:\n\n```rust\n#![feature(generators, generator_trait)]\n\nuse std::ops::Generator;\nuse std::pin::Pin;\n\nfn main() {\n let mut generator = || {\n println!(\"2\");\n yield;\n println!(\"4\");\n };\n\n println!(\"1\");\n Pin::new(&mut generator).resume(());\n println!(\"3\");\n Pin::new(&mut generator).resume(());\n println!(\"5\");\n}\n```\n\nAt this time the main intended use case of generators is an implementation\nprimitive for async/await syntax, but generators will likely be extended to\nergonomic implementations of iterators and other primitives in the future.\nFeedback on the design and usage is always appreciated!\n\n### The `Generator` trait\n\nThe `Generator` trait in `std::ops` currently looks like:\n\n```rust\n# #![feature(arbitrary_self_types, generator_trait)]\n# use std::ops::GeneratorState;\n# use std::pin::Pin;\n\npub trait Generator<R = ()> {\n type Yield;\n type Return;\n fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState<Self::Yield, Self::Return>;\n}\n```\n\nThe `Generator::Yield` type is the type of values that can be yielded with the\n`yield` statement. The `Generator::Return` type is the returned type of the\ngenerator. This is typically the last expression in a generator's definition or\nany value passed to `return` in a generator. The `resume` function is the entry\npoint for executing the `Generator` itself.\n\nThe return value of `resume`, `GeneratorState`, looks like:\n\n```rust\npub enum GeneratorState<Y, R> {\n Yielded(Y),\n Complete(R),\n}\n```\n\nThe `Yielded` variant indicates that the generator can later be resumed. This\ncorresponds to a `yield` point in a generator. The `Complete` variant indicates\nthat the generator is complete and cannot be resumed again. Calling `resume`\nafter a generator has returned `Complete` will likely result in a panic of the\nprogram.\n\n### Closure-like semantics\n\nThe closure-like syntax for generators alludes to the fact that they also have\nclosure-like semantics. Namely:\n\n* When created, a generator executes no code. A closure literal does not\n actually execute any of the closure's code on construction, and similarly a\n generator literal does not execute any code inside the generator when\n constructed.\n\n* Generators can capture outer variables by reference or by move, and this can\n be tweaked with the `move` keyword at the beginning of the closure. Like\n closures all generators will have an implicit environment which is inferred by\n the compiler. Outer variables can be moved into a generator for use as the\n generator progresses.\n\n* Generator literals produce a value with a unique type which implements the\n `std::ops::Generator` trait. This allows actual execution of the generator\n through the `Generator::resume` method as well as also naming it in return\n types and such.\n\n* Traits like `Send` and `Sync` are automatically implemented for a `Generator`\n depending on the captured variables of the environment. Unlike closures,\n generators also depend on variables live across suspension points. This means\n that although the ambient environment may be `Send` or `Sync`, the generator\n itself may not be due to internal variables live across `yield` points being\n not-`Send` or not-`Sync`. Note that generators do\n not implement traits like `Copy` or `Clone` automatically.\n\n* Whenever a generator is dropped it will drop all captured environment\n variables.\n\n### Generators as state machines\n\nIn the compiler, generators are currently compiled as state machines. Each\n`yield` expression will correspond to a different state that stores all live\nvariables over that suspension point. Resumption of a generator will dispatch on\nthe current state and then execute internally until a `yield` is reached, at\nwhich point all state is saved off in the generator and a value is returned.\n\nLet's take a look at an example to see what's going on here:\n\n```rust\n#![feature(generators, generator_trait)]\n\nuse std::ops::Generator;\nuse std::pin::Pin;\n\nfn main() {\n let ret = \"foo\";\n let mut generator = move || {\n yield 1;\n return ret\n };\n\n Pin::new(&mut generator).resume(());\n Pin::new(&mut generator).resume(());\n}\n```\n\nThis generator literal will compile down to something similar to:\n\n```rust\n#![feature(arbitrary_self_types, generators, generator_trait)]\n\nuse std::ops::{Generator, GeneratorState};\nuse std::pin::Pin;\n\nfn main() {\n let ret = \"foo\";\n let mut generator = {\n enum __Generator {\n Start(&'static str),\n Yield1(&'static str),\n Done,\n }\n\n impl Generator for __Generator {\n type Yield = i32;\n type Return = &'static str;\n\n fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState<i32, &'static str> {\n use std::mem;\n match mem::replace(&mut *self, __Generator::Done) {\n __Generator::Start(s) => {\n *self = __Generator::Yield1(s);\n GeneratorState::Yielded(1)\n }\n\n __Generator::Yield1(s) => {\n *self = __Generator::Done;\n GeneratorState::Complete(s)\n }\n\n __Generator::Done => {\n panic!(\"generator resumed after completion\")\n }\n }\n }\n }\n\n __Generator::Start(ret)\n };\n\n Pin::new(&mut generator).resume(());\n Pin::new(&mut generator).resume(());\n}\n```\n\nNotably here we can see that the compiler is generating a fresh type,\n`__Generator` in this case. This type has a number of states (represented here\nas an `enum`) corresponding to each of the conceptual states of the generator.\nAt the beginning we're closing over our outer variable `foo` and then that\nvariable is also live over the `yield` point, so it's stored in both states.\n\nWhen the generator starts it'll immediately yield 1, but it saves off its state\njust before it does so indicating that it has reached the yield point. Upon\nresuming again we'll execute the `return ret` which returns the `Complete`\nstate.\n\nHere we can also note that the `Done` state, if resumed, panics immediately as\nit's invalid to resume a completed generator. It's also worth noting that this\nis just a rough desugaring, not a normative specification for what the compiler\ndoes.\n" } , LintCompletion { label : "transparent_unions" , description : "# `transparent_unions`\n\nThe tracking issue for this feature is [#60405]\n\n[#60405]: https://github.com/rust-lang/rust/issues/60405\n\n----\n\nThe `transparent_unions` feature allows you mark `union`s as\n`#[repr(transparent)]`. A `union` may be `#[repr(transparent)]` in exactly the\nsame conditions in which a `struct` may be `#[repr(transparent)]` (generally,\nthis means the `union` must have exactly one non-zero-sized field). Some\nconcrete illustrations follow.\n\n```rust\n#![feature(transparent_unions)]\n\n// This union has the same representation as `f32`.\n#[repr(transparent)]\nunion SingleFieldUnion {\n field: f32,\n}\n\n// This union has the same representation as `usize`.\n#[repr(transparent)]\nunion MultiFieldUnion {\n field: usize,\n nothing: (),\n}\n```\n\nFor consistency with transparent `struct`s, `union`s must have exactly one\nnon-zero-sized field. If all fields are zero-sized, the `union` must not be\n`#[repr(transparent)]`:\n\n```rust\n#![feature(transparent_unions)]\n\n// This (non-transparent) union is already valid in stable Rust:\npub union GoodUnion {\n pub nothing: (),\n}\n\n// Error: transparent union needs exactly one non-zero-sized field, but has 0\n// #[repr(transparent)]\n// pub union BadUnion {\n// pub nothing: (),\n// }\n```\n\nThe one exception is if the `union` is generic over `T` and has a field of type\n`T`, it may be `#[repr(transparent)]` even if `T` is a zero-sized type:\n\n```rust\n#![feature(transparent_unions)]\n\n// This union has the same representation as `T`.\n#[repr(transparent)]\npub union GenericUnion<T: Copy> { // Unions with non-`Copy` fields are unstable.\n pub field: T,\n pub nothing: (),\n}\n\n// This is okay even though `()` is a zero-sized type.\npub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () };\n```\n\nLike transarent `struct`s, a transparent `union` of type `U` has the same\nlayout, size, and ABI as its single non-ZST field. If it is generic over a type\n`T`, and all its fields are ZSTs except for exactly one field of type `T`, then\nit has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized).\n\nLike transparent `struct`s, transparent `union`s are FFI-safe if and only if\ntheir underlying representation type is also FFI-safe.\n\nA `union` may not be eligible for the same nonnull-style optimizations that a\n`struct` or `enum` (with the same fields) are eligible for. Adding\n`#[repr(transparent)]` to `union` does not change this. To give a more concrete\nexample, it is unspecified whether `size_of::<T>()` is equal to\n`size_of::<Option<T>>()`, where `T` is a `union` (regardless of whether or not\nit is transparent). The Rust compiler is free to perform this optimization if\npossible, but is not required to, and different compiler versions may differ in\ntheir application of these optimizations.\n" } , LintCompletion { label : "plugin_registrar" , description : "# `plugin_registrar`\n\nThe tracking issue for this feature is: [#29597]\n\n[#29597]: https://github.com/rust-lang/rust/issues/29597\n\nThis feature is part of \"compiler plugins.\" It will often be used with the\n[`plugin`] and `rustc_private` features as well. For more details, see\ntheir docs.\n\n[`plugin`]: plugin.md\n\n------------------------\n" } , LintCompletion { label : "or_patterns" , description : "# `or_patterns`\n\nThe tracking issue for this feature is: [#54883]\n\n[#54883]: https://github.com/rust-lang/rust/issues/54883\n\n------------------------\n\nThe `or_pattern` language feature allows `|` to be arbitrarily nested within\na pattern, for example, `Some(A(0) | B(1 | 2))` becomes a valid pattern.\n\n## Examples\n\n```rust,ignore\n#![feature(or_patterns)]\n\npub enum Foo {\n Bar,\n Baz,\n Quux,\n}\n\npub fn example(maybe_foo: Option<Foo>) {\n match maybe_foo {\n Some(Foo::Bar | Foo::Baz) => {\n println!(\"The value contained `Bar` or `Baz`\");\n }\n Some(_) => {\n println!(\"The value did not contain `Bar` or `Baz`\");\n }\n None => {\n println!(\"The value was `None`\");\n }\n }\n}\n```\n" } , LintCompletion { label : "repr128" , description : "# `repr128`\n\nThe tracking issue for this feature is: [#56071]\n\n[#56071]: https://github.com/rust-lang/rust/issues/56071\n\n------------------------\n\nThe `repr128` feature adds support for `#[repr(u128)]` on `enum`s.\n\n```rust\n#![feature(repr128)]\n\n#[repr(u128)]\nenum Foo {\n Bar(u64),\n}\n```\n" } , LintCompletion { label : "unboxed_closures" , description : "# `unboxed_closures`\n\nThe tracking issue for this feature is [#29625]\n\nSee Also: [`fn_traits`](../library-features/fn-traits.md)\n\n[#29625]: https://github.com/rust-lang/rust/issues/29625\n\n----\n\nThe `unboxed_closures` feature allows you to write functions using the `\"rust-call\"` ABI,\nrequired for implementing the [`Fn*`] family of traits. `\"rust-call\"` functions must have \nexactly one (non self) argument, a tuple representing the argument list.\n\n[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html\n\n```rust\n#![feature(unboxed_closures)]\n\nextern \"rust-call\" fn add_args(args: (u32, u32)) -> u32 {\n args.0 + args.1\n}\n\nfn main() {}\n```\n" } , LintCompletion { label : "link_cfg" , description : "# `link_cfg`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "rustc_attrs" , description : "# `rustc_attrs`\n\nThis feature has no tracking issue, and is therefore internal to\nthe compiler, not being intended for general use.\n\nNote: `rustc_attrs` enables many rustc-internal attributes and this page\nonly discuss a few of them.\n\n------------------------\n\nThe `rustc_attrs` feature allows debugging rustc type layouts by using\n`#[rustc_layout(...)]` to debug layout at compile time (it even works\nwith `cargo check`) as an alternative to `rustc -Z print-type-sizes`\nthat is way more verbose.\n\nOptions provided by `#[rustc_layout(...)]` are `debug`, `size`, `align`,\n`abi`. Note that it only works on sized types without generics.\n\n## Examples\n\n```rust,ignore\n#![feature(rustc_attrs)]\n\n#[rustc_layout(abi, size)]\npub enum X {\n Y(u8, u8, u8),\n Z(isize),\n}\n```\n\nWhen that is compiled, the compiler will error with something like\n\n```text\nerror: abi: Aggregate { sized: true }\n --> src/lib.rs:4:1\n |\n4 | / pub enum T {\n5 | | Y(u8, u8, u8),\n6 | | Z(isize),\n7 | | }\n | |_^\n\nerror: size: Size { raw: 16 }\n --> src/lib.rs:4:1\n |\n4 | / pub enum T {\n5 | | Y(u8, u8, u8),\n6 | | Z(isize),\n7 | | }\n | |_^\n\nerror: aborting due to 2 previous errors\n```\n" } , LintCompletion { label : "box_patterns" , description : "# `box_patterns`\n\nThe tracking issue for this feature is: [#29641]\n\n[#29641]: https://github.com/rust-lang/rust/issues/29641\n\nSee also [`box_syntax`](box-syntax.md)\n\n------------------------\n\nBox patterns let you match on `Box<T>`s:\n\n\n```rust\n#![feature(box_patterns)]\n\nfn main() {\n let b = Some(Box::new(5));\n match b {\n Some(box n) if n < 0 => {\n println!(\"Box contains negative number {}\", n);\n },\n Some(box n) if n >= 0 => {\n println!(\"Box contains non-negative number {}\", n);\n },\n None => {\n println!(\"No box\");\n },\n _ => unreachable!()\n }\n}\n```\n" } , LintCompletion { label : "infer_static_outlives_requirements" , description : "# `infer_static_outlives_requirements`\n\nThe tracking issue for this feature is: [#54185]\n\n[#54185]: https://github.com/rust-lang/rust/issues/54185\n\n------------------------\nThe `infer_static_outlives_requirements` feature indicates that certain\n`'static` outlives requirements can be inferred by the compiler rather than\nstating them explicitly.\n\nNote: It is an accompanying feature to `infer_outlives_requirements`,\nwhich must be enabled to infer outlives requirements.\n\nFor example, currently generic struct definitions that contain\nreferences, require where-clauses of the form T: 'static. By using\nthis feature the outlives predicates will be inferred, although\nthey may still be written explicitly.\n\n```rust,ignore (pseudo-Rust)\nstruct Foo<U> where U: 'static { // <-- currently required\n bar: Bar<U>\n}\nstruct Bar<T: 'static> {\n x: T,\n}\n```\n\n\n## Examples:\n\n```rust,ignore (pseudo-Rust)\n#![feature(infer_outlives_requirements)]\n#![feature(infer_static_outlives_requirements)]\n\n#[rustc_outlives]\n// Implicitly infer U: 'static\nstruct Foo<U> {\n bar: Bar<U>\n}\nstruct Bar<T: 'static> {\n x: T,\n}\n```\n\n" } , LintCompletion { label : "trait_alias" , description : "# `trait_alias`\n\nThe tracking issue for this feature is: [#41517]\n\n[#41517]: https://github.com/rust-lang/rust/issues/41517\n\n------------------------\n\nThe `trait_alias` feature adds support for trait aliases. These allow aliases\nto be created for one or more traits (currently just a single regular trait plus\nany number of auto-traits), and used wherever traits would normally be used as\neither bounds or trait objects.\n\n```rust\n#![feature(trait_alias)]\n\ntrait Foo = std::fmt::Debug + Send;\ntrait Bar = Foo + Sync;\n\n// Use trait alias as bound on type parameter.\nfn foo<T: Foo>(v: &T) {\n println!(\"{:?}\", v);\n}\n\npub fn main() {\n foo(&1);\n\n // Use trait alias for trait objects.\n let a: &Bar = &123;\n println!(\"{:?}\", a);\n let b = Box::new(456) as Box<dyn Foo>;\n println!(\"{:?}\", b);\n}\n```\n" } , LintCompletion { label : "const_fn" , description : "# `const_fn`\n\nThe tracking issue for this feature is: [#57563]\n\n[#57563]: https://github.com/rust-lang/rust/issues/57563\n\n------------------------\n\nThe `const_fn` feature allows marking free functions and inherent methods as\n`const`, enabling them to be called in constants contexts, with constant\narguments.\n\n## Examples\n\n```rust\n#![feature(const_fn)]\n\nconst fn double(x: i32) -> i32 {\n x * 2\n}\n\nconst FIVE: i32 = 5;\nconst TEN: i32 = double(FIVE);\n\nfn main() {\n assert_eq!(5, FIVE);\n assert_eq!(10, TEN);\n}\n```\n" } , LintCompletion { label : "doc_cfg" , description : "# `doc_cfg`\n\nThe tracking issue for this feature is: [#43781]\n\n------\n\nThe `doc_cfg` feature allows an API be documented as only available in some specific platforms.\nThis attribute has two effects:\n\n1. In the annotated item's documentation, there will be a message saying \"This is supported on\n (platform) only\".\n\n2. The item's doc-tests will only run on the specific platform.\n\nIn addition to allowing the use of the `#[doc(cfg)]` attribute, this feature enables the use of a\nspecial conditional compilation flag, `#[cfg(doc)]`, set whenever building documentation on your\ncrate.\n\nThis feature was introduced as part of PR [#43348] to allow the platform-specific parts of the\nstandard library be documented.\n\n```rust\n#![feature(doc_cfg)]\n\n#[cfg(any(windows, doc))]\n#[doc(cfg(windows))]\n/// The application's icon in the notification area (a.k.a. system tray).\n///\n/// # Examples\n///\n/// ```no_run\n/// extern crate my_awesome_ui_library;\n/// use my_awesome_ui_library::current_app;\n/// use my_awesome_ui_library::windows::notification;\n///\n/// let icon = current_app().get::<notification::Icon>();\n/// icon.show();\n/// icon.show_message(\"Hello\");\n/// ```\npub struct Icon {\n // ...\n}\n```\n\n[#43781]: https://github.com/rust-lang/rust/issues/43781\n[#43348]: https://github.com/rust-lang/rust/issues/43348\n" } , LintCompletion { label : "allocator_internals" , description : "# `allocator_internals`\n\nThis feature does not have a tracking issue, it is an unstable implementation\ndetail of the `global_allocator` feature not intended for use outside the\ncompiler.\n\n------------------------\n" } , LintCompletion { label : "doc_masked" , description : "# `doc_masked`\n\nThe tracking issue for this feature is: [#44027]\n\n-----\n\nThe `doc_masked` feature allows a crate to exclude types from a given crate from appearing in lists\nof trait implementations. The specifics of the feature are as follows:\n\n1. When rustdoc encounters an `extern crate` statement annotated with a `#[doc(masked)]` attribute,\n it marks the crate as being masked.\n\n2. When listing traits a given type implements, rustdoc ensures that traits from masked crates are\n not emitted into the documentation.\n\n3. When listing types that implement a given trait, rustdoc ensures that types from masked crates\n are not emitted into the documentation.\n\nThis feature was introduced in PR [#44026] to ensure that compiler-internal and\nimplementation-specific types and traits were not included in the standard library's documentation.\nSuch types would introduce broken links into the documentation.\n\n[#44026]: https://github.com/rust-lang/rust/pull/44026\n[#44027]: https://github.com/rust-lang/rust/pull/44027\n" } , LintCompletion { label : "no_sanitize" , description : "# `no_sanitize`\n\nThe tracking issue for this feature is: [#39699]\n\n[#39699]: https://github.com/rust-lang/rust/issues/39699\n\n------------------------\n\nThe `no_sanitize` attribute can be used to selectively disable sanitizer\ninstrumentation in an annotated function. This might be useful to: avoid\ninstrumentation overhead in a performance critical function, or avoid\ninstrumenting code that contains constructs unsupported by given sanitizer.\n\nThe precise effect of this annotation depends on particular sanitizer in use.\nFor example, with `no_sanitize(thread)`, the thread sanitizer will no longer\ninstrument non-atomic store / load operations, but it will instrument atomic\noperations to avoid reporting false positives and provide meaning full stack\ntraces.\n\n## Examples\n\n``` rust\n#![feature(no_sanitize)]\n\n#[no_sanitize(address)]\nfn foo() {\n // ...\n}\n```\n" } , LintCompletion { label : "intrinsics" , description : "# `intrinsics`\n\nThe tracking issue for this feature is: None.\n\nIntrinsics are never intended to be stable directly, but intrinsics are often\nexported in some sort of stable manner. Prefer using the stable interfaces to\nthe intrinsic directly when you can.\n\n------------------------\n\n\nThese are imported as if they were FFI functions, with the special\n`rust-intrinsic` ABI. For example, if one was in a freestanding\ncontext, but wished to be able to `transmute` between types, and\nperform efficient pointer arithmetic, one would import those functions\nvia a declaration like\n\n```rust\n#![feature(intrinsics)]\n# fn main() {}\n\nextern \"rust-intrinsic\" {\n fn transmute<T, U>(x: T) -> U;\n\n fn offset<T>(dst: *const T, offset: isize) -> *const T;\n}\n```\n\nAs with any other FFI functions, these are always `unsafe` to call.\n\n" } , LintCompletion { label : "external_doc" , description : "# `external_doc`\n\nThe tracking issue for this feature is: [#44732]\n\nThe `external_doc` feature allows the use of the `include` parameter to the `#[doc]` attribute, to\ninclude external files in documentation. Use the attribute in place of, or in addition to, regular\ndoc comments and `#[doc]` attributes, and `rustdoc` will load the given file when it renders\ndocumentation for your crate.\n\nWith the following files in the same directory:\n\n`external-doc.md`:\n\n```markdown\n# My Awesome Type\n\nThis is the documentation for this spectacular type.\n```\n\n`lib.rs`:\n\n```no_run (needs-external-files)\n#![feature(external_doc)]\n\n#[doc(include = \"external-doc.md\")]\npub struct MyAwesomeType;\n```\n\n`rustdoc` will load the file `external-doc.md` and use it as the documentation for the `MyAwesomeType`\nstruct.\n\nWhen locating files, `rustdoc` will base paths in the `src/` directory, as if they were alongside the\n`lib.rs` for your crate. So if you want a `docs/` folder to live alongside the `src/` directory,\nstart your paths with `../docs/` for `rustdoc` to properly find the file.\n\nThis feature was proposed in [RFC #1990] and initially implemented in PR [#44781].\n\n[#44732]: https://github.com/rust-lang/rust/issues/44732\n[RFC #1990]: https://github.com/rust-lang/rfcs/pull/1990\n[#44781]: https://github.com/rust-lang/rust/pull/44781\n" } , LintCompletion { label : "inline_const" , description : "# `inline_const`\n\nThe tracking issue for this feature is: [#76001]\n\n------\n\nThis feature allows you to use inline constant expressions. For example, you can\nturn this code:\n\n```rust\n# fn add_one(x: i32) -> i32 { x + 1 }\nconst MY_COMPUTATION: i32 = 1 + 2 * 3 / 4;\n\nfn main() {\n let x = add_one(MY_COMPUTATION);\n}\n```\n\ninto this code:\n\n```rust\n#![feature(inline_const)]\n\n# fn add_one(x: i32) -> i32 { x + 1 }\nfn main() {\n let x = add_one(const { 1 + 2 * 3 / 4 });\n}\n```\n\nYou can also use inline constant expressions in patterns:\n\n```rust\n#![feature(inline_const)]\n\nconst fn one() -> i32 { 1 }\n\nlet some_int = 3;\nmatch some_int {\n const { 1 + 2 } => println!(\"Matched 1 + 2\"),\n const { one() } => println!(\"Matched const fn returning 1\"),\n _ => println!(\"Didn't match anything :(\"),\n}\n```\n\n[#76001]: https://github.com/rust-lang/rust/issues/76001\n" } , LintCompletion { label : "abi_thiscall" , description : "# `abi_thiscall`\n\nThe tracking issue for this feature is: [#42202]\n\n[#42202]: https://github.com/rust-lang/rust/issues/42202\n\n------------------------\n\nThe MSVC ABI on x86 Windows uses the `thiscall` calling convention for C++\ninstance methods by default; it is identical to the usual (C) calling\nconvention on x86 Windows except that the first parameter of the method,\nthe `this` pointer, is passed in the ECX register.\n" } , LintCompletion { label : "plugin" , description : "# `plugin`\n\nThe tracking issue for this feature is: [#29597]\n\n[#29597]: https://github.com/rust-lang/rust/issues/29597\n\n\nThis feature is part of \"compiler plugins.\" It will often be used with the\n[`plugin_registrar`] and `rustc_private` features.\n\n[`plugin_registrar`]: plugin-registrar.md\n\n------------------------\n\n`rustc` can load compiler plugins, which are user-provided libraries that\nextend the compiler's behavior with new lint checks, etc.\n\nA plugin is a dynamic library crate with a designated *registrar* function that\nregisters extensions with `rustc`. Other crates can load these extensions using\nthe crate attribute `#![plugin(...)]`. See the\n`rustc_driver::plugin` documentation for more about the\nmechanics of defining and loading a plugin.\n\nIn the vast majority of cases, a plugin should *only* be used through\n`#![plugin]` and not through an `extern crate` item. Linking a plugin would\npull in all of librustc_ast and librustc as dependencies of your crate. This is\ngenerally unwanted unless you are building another plugin.\n\nThe usual practice is to put compiler plugins in their own crate, separate from\nany `macro_rules!` macros or ordinary Rust code meant to be used by consumers\nof a library.\n\n# Lint plugins\n\nPlugins can extend [Rust's lint\ninfrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with\nadditional checks for code style, safety, etc. Now let's write a plugin\n[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs)\nthat warns about any item named `lintme`.\n\n```rust,ignore\n#![feature(plugin_registrar)]\n#![feature(box_syntax, rustc_private)]\n\nextern crate rustc_ast;\n\n// Load rustc as a plugin to get macros\nextern crate rustc_driver;\n#[macro_use]\nextern crate rustc_lint;\n#[macro_use]\nextern crate rustc_session;\n\nuse rustc_driver::plugin::Registry;\nuse rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};\nuse rustc_ast::ast;\ndeclare_lint!(TEST_LINT, Warn, \"Warn about items named 'lintme'\");\n\ndeclare_lint_pass!(Pass => [TEST_LINT]);\n\nimpl EarlyLintPass for Pass {\n fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {\n if it.ident.name.as_str() == \"lintme\" {\n cx.lint(TEST_LINT, |lint| {\n lint.build(\"item is named 'lintme'\").set_span(it.span).emit()\n });\n }\n }\n}\n\n#[plugin_registrar]\npub fn plugin_registrar(reg: &mut Registry) {\n reg.lint_store.register_lints(&[&TEST_LINT]);\n reg.lint_store.register_early_pass(|| box Pass);\n}\n```\n\nThen code like\n\n```rust,ignore\n#![feature(plugin)]\n#![plugin(lint_plugin_test)]\n\nfn lintme() { }\n```\n\nwill produce a compiler warning:\n\n```txt\nfoo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default\nfoo.rs:4 fn lintme() { }\n ^~~~~~~~~~~~~~~\n```\n\nThe components of a lint plugin are:\n\n* one or more `declare_lint!` invocations, which define static `Lint` structs;\n\n* a struct holding any state needed by the lint pass (here, none);\n\n* a `LintPass`\n implementation defining how to check each syntax element. A single\n `LintPass` may call `span_lint` for several different `Lint`s, but should\n register them all through the `get_lints` method.\n\nLint passes are syntax traversals, but they run at a late stage of compilation\nwhere type information is available. `rustc`'s [built-in\nlints](https://github.com/rust-lang/rust/blob/master/src/librustc_session/lint/builtin.rs)\nmostly use the same infrastructure as lint plugins, and provide examples of how\nto access type information.\n\nLints defined by plugins are controlled by the usual [attributes and compiler\nflags](../../reference/attributes/diagnostics.md#lint-check-attributes), e.g.\n`#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the\nfirst argument to `declare_lint!`, with appropriate case and punctuation\nconversion.\n\nYou can run `rustc -W help foo.rs` to see a list of lints known to `rustc`,\nincluding those provided by plugins loaded by `foo.rs`.\n" } , LintCompletion { label : "optin_builtin_traits" , description : "# `optin_builtin_traits`\n\nThe tracking issue for this feature is [#13231] \n\n[#13231]: https://github.com/rust-lang/rust/issues/13231\n\n----\n\nThe `optin_builtin_traits` feature gate allows you to define auto traits.\n\nAuto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits\nthat are automatically implemented for every type, unless the type, or a type it contains, \nhas explicitly opted out via a negative impl. (Negative impls are separately controlled\nby the `negative_impls` feature.)\n\n[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html\n[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html\n\n```rust,ignore\nimpl !Trait for Type\n```\n\nExample:\n\n```rust\n#![feature(negative_impls)]\n#![feature(optin_builtin_traits)]\n\nauto trait Valid {}\n\nstruct True;\nstruct False;\n\nimpl !Valid for False {}\n\nstruct MaybeValid<T>(T);\n\nfn must_be_valid<T: Valid>(_t: T) { }\n\nfn main() {\n // works\n must_be_valid( MaybeValid(True) );\n \n // compiler error - trait bound not satisfied\n // must_be_valid( MaybeValid(False) );\n}\n```\n\n## Automatic trait implementations\n\nWhen a type is declared as an `auto trait`, we will automatically\ncreate impls for every struct/enum/union, unless an explicit impl is\nprovided. These automatic impls contain a where clause for each field\nof the form `T: AutoTrait`, where `T` is the type of the field and\n`AutoTrait` is the auto trait in question. As an example, consider the\nstruct `List` and the auto trait `Send`:\n\n```rust\nstruct List<T> {\n data: T,\n next: Option<Box<List<T>>>,\n}\n```\n\nPresuming that there is no explicit impl of `Send` for `List`, the\ncompiler will supply an automatic impl of the form:\n\n```rust\nstruct List<T> {\n data: T,\n next: Option<Box<List<T>>>,\n}\n\nunsafe impl<T> Send for List<T>\nwhere\n T: Send, // from the field `data`\n Option<Box<List<T>>>: Send, // from the field `next`\n{ }\n```\n\nExplicit impls may be either positive or negative. They take the form:\n\n```rust,ignore\nimpl<...> AutoTrait for StructName<..> { }\nimpl<...> !AutoTrait for StructName<..> { }\n```\n\n## Coinduction: Auto traits permit cyclic matching\n\nUnlike ordinary trait matching, auto traits are **coinductive**. This\nmeans, in short, that cycles which occur in trait matching are\nconsidered ok. As an example, consider the recursive struct `List`\nintroduced in the previous section. In attempting to determine whether\n`List: Send`, we would wind up in a cycle: to apply the impl, we must\nshow that `Option<Box<List>>: Send`, which will in turn require\n`Box<List>: Send` and then finally `List: Send` again. Under ordinary\ntrait matching, this cycle would be an error, but for an auto trait it\nis considered a successful match.\n\n## Items\n\nAuto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations.\n\n## Supertraits\n\nAuto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile.\n\n" } , LintCompletion { label : "impl_trait_in_bindings" , description : "# `impl_trait_in_bindings`\n\nThe tracking issue for this feature is: [#63065]\n\n[#63065]: https://github.com/rust-lang/rust/issues/63065\n\n------------------------\n\nThe `impl_trait_in_bindings` feature gate lets you use `impl Trait` syntax in\n`let`, `static`, and `const` bindings.\n\nA simple example is:\n\n```rust\n#![feature(impl_trait_in_bindings)]\n\nuse std::fmt::Debug;\n\nfn main() {\n let a: impl Debug + Clone = 42;\n let b = a.clone();\n println!(\"{:?}\", b); // prints `42`\n}\n```\n\nNote however that because the types of `a` and `b` are opaque in the above\nexample, calling inherent methods or methods outside of the specified traits\n(e.g., `a.abs()` or `b.abs()`) is not allowed, and yields an error.\n" } , LintCompletion { label : "abi_ptx" , description : "# `abi_ptx`\n\nThe tracking issue for this feature is: [#38788]\n\n[#38788]: https://github.com/rust-lang/rust/issues/38788\n\n------------------------\n\nWhen emitting PTX code, all vanilla Rust functions (`fn`) get translated to\n\"device\" functions. These functions are *not* callable from the host via the\nCUDA API so a crate with only device functions is not too useful!\n\nOTOH, \"global\" functions *can* be called by the host; you can think of them\nas the real public API of your crate. To produce a global function use the\n`\"ptx-kernel\"` ABI.\n\n<!-- NOTE(ignore) this example is specific to the nvptx targets -->\n\n``` rust,ignore\n#![feature(abi_ptx)]\n#![no_std]\n\npub unsafe extern \"ptx-kernel\" fn global_function() {\n device_function();\n}\n\npub fn device_function() {\n // ..\n}\n```\n\n``` text\n$ xargo rustc --target nvptx64-nvidia-cuda --release -- --emit=asm\n\n$ cat $(find -name '*.s')\n//\n// Generated by LLVM NVPTX Back-End\n//\n\n.version 3.2\n.target sm_20\n.address_size 64\n\n // .globl _ZN6kernel15global_function17h46111ebe6516b382E\n\n.visible .entry _ZN6kernel15global_function17h46111ebe6516b382E()\n{\n\n\n ret;\n}\n\n // .globl _ZN6kernel15device_function17hd6a0e4993bbf3f78E\n.visible .func _ZN6kernel15device_function17hd6a0e4993bbf3f78E()\n{\n\n\n ret;\n}\n```\n" } , LintCompletion { label : "dec2flt" , description : "# `dec2flt`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "int_error_internals" , description : "# `int_error_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "llvm_asm" , description : "# `llvm_asm`\n\nThe tracking issue for this feature is: [#70173]\n\n[#70173]: https://github.com/rust-lang/rust/issues/70173\n\n------------------------\n\nFor extremely low-level manipulations and performance reasons, one\nmight wish to control the CPU directly. Rust supports using inline\nassembly to do this via the `llvm_asm!` macro.\n\n```rust,ignore\nllvm_asm!(assembly template\n : output operands\n : input operands\n : clobbers\n : options\n );\n```\n\nAny use of `llvm_asm` is feature gated (requires `#![feature(llvm_asm)]` on the\ncrate to allow) and of course requires an `unsafe` block.\n\n> **Note**: the examples here are given in x86/x86-64 assembly, but\n> all platforms are supported.\n\n## Assembly template\n\nThe `assembly template` is the only required parameter and must be a\nliteral string (i.e. `\"\"`)\n\n```rust\n#![feature(llvm_asm)]\n\n#[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\nfn foo() {\n unsafe {\n llvm_asm!(\"NOP\");\n }\n}\n\n// Other platforms:\n#[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\nfn foo() { /* ... */ }\n\nfn main() {\n // ...\n foo();\n // ...\n}\n```\n\n(The `feature(llvm_asm)` and `#[cfg]`s are omitted from now on.)\n\nOutput operands, input operands, clobbers and options are all optional\nbut you must add the right number of `:` if you skip them:\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n# fn main() { unsafe {\nllvm_asm!(\"xor %eax, %eax\"\n :\n :\n : \"eax\"\n );\n# } }\n# #[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n# fn main() {}\n```\n\nWhitespace also doesn't matter:\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n# fn main() { unsafe {\nllvm_asm!(\"xor %eax, %eax\" ::: \"eax\");\n# } }\n# #[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n# fn main() {}\n```\n\n## Operands\n\nInput and output operands follow the same format: `:\n\"constraints1\"(expr1), \"constraints2\"(expr2), ...\"`. Output operand\nexpressions must be mutable place, or not yet assigned:\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\nfn add(a: i32, b: i32) -> i32 {\n let c: i32;\n unsafe {\n llvm_asm!(\"add $2, $0\"\n : \"=r\"(c)\n : \"0\"(a), \"r\"(b)\n );\n }\n c\n}\n# #[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n# fn add(a: i32, b: i32) -> i32 { a + b }\n\nfn main() {\n assert_eq!(add(3, 14159), 14162)\n}\n```\n\nIf you would like to use real operands in this position, however,\nyou are required to put curly braces `{}` around the register that\nyou want, and you are required to put the specific size of the\noperand. This is useful for very low level programming, where\nwhich register you use is important:\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n# unsafe fn read_byte_in(port: u16) -> u8 {\nlet result: u8;\nllvm_asm!(\"in %dx, %al\" : \"={al}\"(result) : \"{dx}\"(port));\nresult\n# }\n```\n\n## Clobbers\n\nSome instructions modify registers which might otherwise have held\ndifferent values so we use the clobbers list to indicate to the\ncompiler not to assume any values loaded into those registers will\nstay valid.\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n# fn main() { unsafe {\n// Put the value 0x200 in eax:\nllvm_asm!(\"mov $$0x200, %eax\" : /* no outputs */ : /* no inputs */ : \"eax\");\n# } }\n# #[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n# fn main() {}\n```\n\nInput and output registers need not be listed since that information\nis already communicated by the given constraints. Otherwise, any other\nregisters used either implicitly or explicitly should be listed.\n\nIf the assembly changes the condition code register `cc` should be\nspecified as one of the clobbers. Similarly, if the assembly modifies\nmemory, `memory` should also be specified.\n\n## Options\n\nThe last section, `options` is specific to Rust. The format is comma\nseparated literal strings (i.e. `:\"foo\", \"bar\", \"baz\"`). It's used to\nspecify some extra info about the inline assembly:\n\nCurrent valid options are:\n\n1. `volatile` - specifying this is analogous to\n `__asm__ __volatile__ (...)` in gcc/clang.\n2. `alignstack` - certain instructions expect the stack to be\n aligned a certain way (i.e. SSE) and specifying this indicates to\n the compiler to insert its usual stack alignment code\n3. `intel` - use intel syntax instead of the default AT&T.\n\n```rust\n# #![feature(llvm_asm)]\n# #[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n# fn main() {\nlet result: i32;\nunsafe {\n llvm_asm!(\"mov eax, 2\" : \"={eax}\"(result) : : : \"intel\")\n}\nprintln!(\"eax is currently {}\", result);\n# }\n# #[cfg(not(any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n# fn main() {}\n```\n\n## More Information\n\nThe current implementation of the `llvm_asm!` macro is a direct binding to [LLVM's\ninline assembler expressions][llvm-docs], so be sure to check out [their\ndocumentation as well][llvm-docs] for more information about clobbers,\nconstraints, etc.\n\n[llvm-docs]: http://llvm.org/docs/LangRef.html#inline-assembler-expressions\n\nIf you need more power and don't mind losing some of the niceties of\n`llvm_asm!`, check out [global_asm](global-asm.md).\n" } , LintCompletion { label : "default_free_fn" , description : "# `default_free_fn`\n\nThe tracking issue for this feature is: [#73014]\n\n[#73014]: https://github.com/rust-lang/rust/issues/73014\n\n------------------------\n\nAdds a free `default()` function to the `std::default` module. This function\njust forwards to [`Default::default()`], but may remove repetition of the word\n\"default\" from the call site.\n\n[`Default::default()`]: https://doc.rust-lang.org/nightly/std/default/trait.Default.html#tymethod.default\n\nHere is an example:\n\n```rust\n#![feature(default_free_fn)]\nuse std::default::default;\n\n#[derive(Default)]\nstruct AppConfig {\n foo: FooConfig,\n bar: BarConfig,\n}\n\n#[derive(Default)]\nstruct FooConfig {\n foo: i32,\n}\n\n#[derive(Default)]\nstruct BarConfig {\n bar: f32,\n baz: u8,\n}\n\nfn main() {\n let options = AppConfig {\n foo: default(),\n bar: BarConfig {\n bar: 10.1,\n ..default()\n },\n };\n}\n```\n" } , LintCompletion { label : "libstd_thread_internals" , description : "# `libstd_thread_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "char_error_internals" , description : "# `char_error_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "windows_handle" , description : "# `windows_handle`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "global_asm" , description : "# `global_asm`\n\nThe tracking issue for this feature is: [#35119]\n\n[#35119]: https://github.com/rust-lang/rust/issues/35119\n\n------------------------\n\nThe `global_asm!` macro allows the programmer to write arbitrary\nassembly outside the scope of a function body, passing it through\n`rustc` and `llvm` to the assembler. The macro is a no-frills\ninterface to LLVM's concept of [module-level inline assembly]. That is,\nall caveats applicable to LLVM's module-level inline assembly apply\nto `global_asm!`.\n\n[module-level inline assembly]: http://llvm.org/docs/LangRef.html#module-level-inline-assembly\n\n`global_asm!` fills a role not currently satisfied by either `asm!`\nor `#[naked]` functions. The programmer has _all_ features of the\nassembler at their disposal. The linker will expect to resolve any\nsymbols defined in the inline assembly, modulo any symbols marked as\nexternal. It also means syntax for directives and assembly follow the\nconventions of the assembler in your toolchain.\n\nA simple usage looks like this:\n\n```rust,ignore\n# #![feature(global_asm)]\n# you also need relevant target_arch cfgs\nglobal_asm!(include_str!(\"something_neato.s\"));\n```\n\nAnd a more complicated usage looks like this:\n\n```rust,ignore\n# #![feature(global_asm)]\n# #![cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\n\npub mod sally {\n global_asm!(r#\"\n .global foo\n foo:\n jmp baz\n \"#);\n\n #[no_mangle]\n pub unsafe extern \"C\" fn baz() {}\n}\n\n// the symbols `foo` and `bar` are global, no matter where\n// `global_asm!` was used.\nextern \"C\" {\n fn foo();\n fn bar();\n}\n\npub mod harry {\n global_asm!(r#\"\n .global bar\n bar:\n jmp quux\n \"#);\n\n #[no_mangle]\n pub unsafe extern \"C\" fn quux() {}\n}\n```\n\nYou may use `global_asm!` multiple times, anywhere in your crate, in\nwhatever way suits you. The effect is as if you concatenated all\nusages and placed the larger, single usage in the crate root.\n\n------------------------\n\nIf you don't need quite as much power and flexibility as\n`global_asm!` provides, and you don't mind restricting your inline\nassembly to `fn` bodies only, you might try the\n[asm](asm.md) feature instead.\n" } , LintCompletion { label : "windows_c" , description : "# `windows_c`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "asm" , description : "# `asm`\n\nThe tracking issue for this feature is: [#72016]\n\n[#72016]: https://github.com/rust-lang/rust/issues/72016\n\n------------------------\n\nFor extremely low-level manipulations and performance reasons, one\nmight wish to control the CPU directly. Rust supports using inline\nassembly to do this via the `asm!` macro.\n\n# Guide-level explanation\n[guide-level-explanation]: #guide-level-explanation\n\nRust provides support for inline assembly via the `asm!` macro.\nIt can be used to embed handwritten assembly in the assembly output generated by the compiler.\nGenerally this should not be necessary, but might be where the required performance or timing\ncannot be otherwise achieved. Accessing low level hardware primitives, e.g. in kernel code, may also demand this functionality.\n\n> **Note**: the examples here are given in x86/x86-64 assembly, but other architectures are also supported.\n\nInline assembly is currently supported on the following architectures:\n- x86 and x86-64\n- ARM\n- AArch64\n- RISC-V\n- NVPTX\n- Hexagon\n- MIPS32r2 and MIPS64r2\n\n## Basic usage\n\nLet us start with the simplest possible example:\n\n```rust,allow_fail\n# #![feature(asm)]\nunsafe {\n asm!(\"nop\");\n}\n```\n\nThis will insert a NOP (no operation) instruction into the assembly generated by the compiler.\nNote that all `asm!` invocations have to be inside an `unsafe` block, as they could insert\narbitrary instructions and break various invariants. The instructions to be inserted are listed\nin the first argument of the `asm!` macro as a string literal.\n\n## Inputs and outputs\n\nNow inserting an instruction that does nothing is rather boring. Let us do something that\nactually acts on data:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet x: u64;\nunsafe {\n asm!(\"mov {}, 5\", out(reg) x);\n}\nassert_eq!(x, 5);\n```\n\nThis will write the value `5` into the `u64` variable `x`.\nYou can see that the string literal we use to specify instructions is actually a template string.\nIt is governed by the same rules as Rust [format strings][format-syntax].\nThe arguments that are inserted into the template however look a bit different then you may\nbe familiar with. First we need to specify if the variable is an input or an output of the\ninline assembly. In this case it is an output. We declared this by writing `out`.\nWe also need to specify in what kind of register the assembly expects the variable.\nIn this case we put it in an arbitrary general purpose register by specifying `reg`.\nThe compiler will choose an appropriate register to insert into\nthe template and will read the variable from there after the inline assembly finishes executing.\n\nLet us see another example that also uses an input:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet i: u64 = 3;\nlet o: u64;\nunsafe {\n asm!(\n \"mov {0}, {1}\",\n \"add {0}, {number}\",\n out(reg) o,\n in(reg) i,\n number = const 5,\n );\n}\nassert_eq!(o, 8);\n```\n\nThis will add `5` to the input in variable `i` and write the result to variable `o`.\nThe particular way this assembly does this is first copying the value from `i` to the output,\nand then adding `5` to it.\n\nThe example shows a few things:\n\nFirst, we can see that `asm!` allows multiple template string arguments; each\none is treated as a separate line of assembly code, as if they were all joined\ntogether with newlines between them. This makes it easy to format assembly\ncode.\n\nSecond, we can see that inputs are declared by writing `in` instead of `out`.\n\nThird, one of our operands has a type we haven't seen yet, `const`.\nThis tells the compiler to expand this argument to value directly inside the assembly template.\nThis is only possible for constants and literals.\n\nFourth, we can see that we can specify an argument number, or name as in any format string.\nFor inline assembly templates this is particularly useful as arguments are often used more than once.\nFor more complex inline assembly using this facility is generally recommended, as it improves\nreadability, and allows reordering instructions without changing the argument order.\n\nWe can further refine the above example to avoid the `mov` instruction:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet mut x: u64 = 3;\nunsafe {\n asm!(\"add {0}, {number}\", inout(reg) x, number = const 5);\n}\nassert_eq!(x, 8);\n```\n\nWe can see that `inout` is used to specify an argument that is both input and output.\nThis is different from specifying an input and output separately in that it is guaranteed to assign both to the same register.\n\nIt is also possible to specify different variables for the input and output parts of an `inout` operand:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet x: u64 = 3;\nlet y: u64;\nunsafe {\n asm!(\"add {0}, {number}\", inout(reg) x => y, number = const 5);\n}\nassert_eq!(y, 8);\n```\n\n## Late output operands\n\nThe Rust compiler is conservative with its allocation of operands. It is assumed that an `out`\ncan be written at any time, and can therefore not share its location with any other argument.\nHowever, to guarantee optimal performance it is important to use as few registers as possible,\nso they won't have to be saved and reloaded around the inline assembly block.\nTo achieve this Rust provides a `lateout` specifier. This can be used on any output that is\nwritten only after all inputs have been consumed.\nThere is also a `inlateout` variant of this specifier.\n\nHere is an example where `inlateout` *cannot* be used:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet mut a: u64 = 4;\nlet b: u64 = 4;\nlet c: u64 = 4;\nunsafe {\n asm!(\n \"add {0}, {1}\",\n \"add {0}, {2}\",\n inout(reg) a,\n in(reg) b,\n in(reg) c,\n );\n}\nassert_eq!(a, 12);\n```\n\nHere the compiler is free to allocate the same register for inputs `b` and `c` since it knows they have the same value. However it must allocate a separate register for `a` since it uses `inout` and not `inlateout`. If `inlateout` was used, then `a` and `c` could be allocated to the same register, in which case the first instruction to overwrite the value of `c` and cause the assembly code to produce the wrong result.\n\nHowever the following example can use `inlateout` since the output is only modified after all input registers have been read:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet mut a: u64 = 4;\nlet b: u64 = 4;\nunsafe {\n asm!(\"add {0}, {1}\", inlateout(reg) a, in(reg) b);\n}\nassert_eq!(a, 8);\n```\n\nAs you can see, this assembly fragment will still work correctly if `a` and `b` are assigned to the same register.\n\n## Explicit register operands\n\nSome instructions require that the operands be in a specific register.\nTherefore, Rust inline assembly provides some more specific constraint specifiers.\nWhile `reg` is generally available on any architecture, these are highly architecture specific. E.g. for x86 the general purpose registers `eax`, `ebx`, `ecx`, `edx`, `ebp`, `esi`, and `edi`\namong others can be addressed by their name.\n\n```rust,allow_fail,no_run\n# #![feature(asm)]\nlet cmd = 0xd1;\nunsafe {\n asm!(\"out 0x64, eax\", in(\"eax\") cmd);\n}\n```\n\nIn this example we call the `out` instruction to output the content of the `cmd` variable\nto port `0x64`. Since the `out` instruction only accepts `eax` (and its sub registers) as operand\nwe had to use the `eax` constraint specifier.\n\nNote that unlike other operand types, explicit register operands cannot be used in the template string: you can't use `{}` and should write the register name directly instead. Also, they must appear at the end of the operand list after all other operand types.\n\nConsider this example which uses the x86 `mul` instruction:\n\n```rust,allow_fail\n# #![feature(asm)]\nfn mul(a: u64, b: u64) -> u128 {\n let lo: u64;\n let hi: u64;\n\n unsafe {\n asm!(\n // The x86 mul instruction takes rax as an implicit input and writes\n // the 128-bit result of the multiplication to rax:rdx.\n \"mul {}\",\n in(reg) a,\n inlateout(\"rax\") b => lo,\n lateout(\"rdx\") hi\n );\n }\n\n ((hi as u128) << 64) + lo as u128\n}\n```\n\nThis uses the `mul` instruction to multiply two 64-bit inputs with a 128-bit result.\nThe only explicit operand is a register, that we fill from the variable `a`.\nThe second operand is implicit, and must be the `rax` register, which we fill from the variable `b`.\nThe lower 64 bits of the result are stored in `rax` from which we fill the variable `lo`.\nThe higher 64 bits are stored in `rdx` from which we fill the variable `hi`.\n\n## Clobbered registers\n\nIn many cases inline assembly will modify state that is not needed as an output.\nUsually this is either because we have to use a scratch register in the assembly,\nor instructions modify state that we don't need to further examine.\nThis state is generally referred to as being \"clobbered\".\nWe need to tell the compiler about this since it may need to save and restore this state\naround the inline assembly block.\n\n```rust,allow_fail\n# #![feature(asm)]\nlet ebx: u32;\nlet ecx: u32;\n\nunsafe {\n asm!(\n \"cpuid\",\n // EAX 4 selects the \"Deterministic Cache Parameters\" CPUID leaf\n inout(\"eax\") 4 => _,\n // ECX 0 selects the L0 cache information.\n inout(\"ecx\") 0 => ecx,\n lateout(\"ebx\") ebx,\n lateout(\"edx\") _,\n );\n}\n\nprintln!(\n \"L1 Cache: {}\",\n ((ebx >> 22) + 1) * (((ebx >> 12) & 0x3ff) + 1) * ((ebx & 0xfff) + 1) * (ecx + 1)\n);\n```\n\nIn the example above we use the `cpuid` instruction to get the L1 cache size.\nThis instruction writes to `eax`, `ebx`, `ecx`, and `edx`, but for the cache size we only care about the contents of `ebx` and `ecx`.\n\nHowever we still need to tell the compiler that `eax` and `edx` have been modified so that it can save any values that were in these registers before the asm. This is done by declaring these as outputs but with `_` instead of a variable name, which indicates that the output value is to be discarded.\n\nThis can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code:\n\n```rust,allow_fail\n# #![feature(asm)]\n// Multiply x by 6 using shifts and adds\nlet mut x: u64 = 4;\nunsafe {\n asm!(\n \"mov {tmp}, {x}\",\n \"shl {tmp}, 1\",\n \"shl {x}, 2\",\n \"add {x}, {tmp}\",\n x = inout(reg) x,\n tmp = out(reg) _,\n );\n}\nassert_eq!(x, 4 * 6);\n```\n\n## Symbol operands\n\nA special operand type, `sym`, allows you to use the symbol name of a `fn` or `static` in inline assembly code.\nThis allows you to call a function or access a global variable without needing to keep its address in a register.\n\n```rust,allow_fail\n# #![feature(asm)]\nextern \"C\" fn foo(arg: i32) {\n println!(\"arg = {}\", arg);\n}\n\nfn call_foo(arg: i32) {\n unsafe {\n asm!(\n \"call {}\",\n sym foo,\n // 1st argument in rdi, which is caller-saved\n inout(\"rdi\") arg => _,\n // All caller-saved registers must be marked as clobberred\n out(\"rax\") _, out(\"rcx\") _, out(\"rdx\") _, out(\"rsi\") _,\n out(\"r8\") _, out(\"r9\") _, out(\"r10\") _, out(\"r11\") _,\n out(\"xmm0\") _, out(\"xmm1\") _, out(\"xmm2\") _, out(\"xmm3\") _,\n out(\"xmm4\") _, out(\"xmm5\") _, out(\"xmm6\") _, out(\"xmm7\") _,\n out(\"xmm8\") _, out(\"xmm9\") _, out(\"xmm10\") _, out(\"xmm11\") _,\n out(\"xmm12\") _, out(\"xmm13\") _, out(\"xmm14\") _, out(\"xmm15\") _,\n )\n }\n}\n```\n\nNote that the `fn` or `static` item does not need to be public or `#[no_mangle]`:\nthe compiler will automatically insert the appropriate mangled symbol name into the assembly code.\n\n## Register template modifiers\n\nIn some cases, fine control is needed over the way a register name is formatted when inserted into the template string. This is needed when an architecture's assembly language has several names for the same register, each typically being a \"view\" over a subset of the register (e.g. the low 32 bits of a 64-bit register).\n\nBy default the compiler will always choose the name that refers to the full register size (e.g. `rax` on x86-64, `eax` on x86, etc).\n\nThis default can be overriden by using modifiers on the template string operands, just like you would with format strings:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet mut x: u16 = 0xab;\n\nunsafe {\n asm!(\"mov {0:h}, {0:l}\", inout(reg_abcd) x);\n}\n\nassert_eq!(x, 0xabab);\n```\n\nIn this example, we use the `reg_abcd` register class to restrict the register allocator to the 4 legacy x86 register (`ax`, `bx`, `cx`, `dx`) of which the first two bytes can be addressed independently.\n\nLet us assume that the register allocator has chosen to allocate `x` in the `ax` register.\nThe `h` modifier will emit the register name for the high byte of that register and the `l` modifier will emit the register name for the low byte. The asm code will therefore be expanded as `mov ah, al` which copies the low byte of the value into the high byte.\n\nIf you use a smaller data type (e.g. `u16`) with an operand and forget the use template modifiers, the compiler will emit a warning and suggest the correct modifier to use.\n\n## Memory address operands\n\nSometimes assembly instructions require operands passed via memory addresses/memory locations.\nYou have to manually use the memory address syntax specified by the respectively architectures.\nFor example, in x86/x86_64 and intel assembly syntax, you should wrap inputs/outputs in `[]`\nto indicate they are memory operands:\n\n```rust,allow_fail\n# #![feature(asm, llvm_asm)]\n# fn load_fpu_control_word(control: u16) {\nunsafe {\n asm!(\"fldcw [{}]\", in(reg) &control, options(nostack));\n\n // Previously this would have been written with the deprecated `llvm_asm!` like this\n llvm_asm!(\"fldcw $0\" :: \"m\" (control) :: \"volatile\");\n}\n# }\n```\n\n## Options\n\nBy default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However in many cases, it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better.\n\nLet's take our previous example of an `add` instruction:\n\n```rust,allow_fail\n# #![feature(asm)]\nlet mut a: u64 = 4;\nlet b: u64 = 4;\nunsafe {\n asm!(\n \"add {0}, {1}\",\n inlateout(reg) a, in(reg) b,\n options(pure, nomem, nostack),\n );\n}\nassert_eq!(a, 8);\n```\n\nOptions can be provided as an optional final argument to the `asm!` macro. We specified three options here:\n- `pure` means that the asm code has no observable side effects and that its output depends only on its inputs. This allows the compiler optimizer to call the inline asm fewer times or even eliminate it entirely.\n- `nomem` means that the asm code does not read or write to memory. By default the compiler will assume that inline assembly can read or write any memory address that is accessible to it (e.g. through a pointer passed as an operand, or a global).\n- `nostack` means that the asm code does not push any data onto the stack. This allows the compiler to use optimizations such as the stack red zone on x86-64 to avoid stack pointer adjustments.\n\nThese allow the compiler to better optimize code using `asm!`, for example by eliminating pure `asm!` blocks whose outputs are not needed.\n\nSee the reference for the full list of available options and their effects.\n\n# Reference-level explanation\n[reference-level-explanation]: #reference-level-explanation\n\nInline assembler is implemented as an unsafe macro `asm!()`.\nThe first argument to this macro is a template string literal used to build the final assembly.\nThe following arguments specify input and output operands.\nWhen required, options are specified as the final argument.\n\nThe following ABNF specifies the general syntax:\n\n```ignore\ndir_spec := \"in\" / \"out\" / \"lateout\" / \"inout\" / \"inlateout\"\nreg_spec := <register class> / \"<explicit register>\"\noperand_expr := expr / \"_\" / expr \"=>\" expr / expr \"=>\" \"_\"\nreg_operand := dir_spec \"(\" reg_spec \")\" operand_expr\noperand := reg_operand / \"const\" const_expr / \"sym\" path\noption := \"pure\" / \"nomem\" / \"readonly\" / \"preserves_flags\" / \"noreturn\" / \"nostack\" / \"att_syntax\"\noptions := \"options(\" option *[\",\" option] [\",\"] \")\"\nasm := \"asm!(\" format_string *(\",\" format_string) *(\",\" [ident \"=\"] operand) [\",\" options] [\",\"] \")\"\n```\n\nThe macro will initially be supported only on ARM, AArch64, Hexagon, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target.\n\n[format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax\n\n## Template string arguments\n\nThe assembler template uses the same syntax as [format strings][format-syntax] (i.e. placeholders are specified by curly braces). The corresponding arguments are accessed in order, by index, or by name. However, implicit named arguments (introduced by [RFC #2795][rfc-2795]) are not supported.\n\nAn `asm!` invocation may have one or more template string arguments; an `asm!` with multiple template string arguments is treated as if all the strings were concatenated with a `\\n` between them. The expected usage is for each template string argument to correspond to a line of assembly code. All template string arguments must appear before any other arguments.\n\nAs with format strings, named arguments must appear after positional arguments. Explicit register operands must appear at the end of the operand list, after named arguments if any.\n\nExplicit register operands cannot be used by placeholders in the template string. All other named and positional operands must appear at least once in the template string, otherwise a compiler error is generated.\n\nThe exact assembly code syntax is target-specific and opaque to the compiler except for the way operands are substituted into the template string to form the code passed to the assembler.\n\nThe 5 targets specified in this RFC (x86, ARM, AArch64, RISC-V, Hexagon) all use the assembly code syntax of the GNU assembler (GAS). On x86, the `.intel_syntax noprefix` mode of GAS is used by default. On ARM, the `.syntax unified` mode is used. These targets impose an additional restriction on the assembly code: any assembler state (e.g. the current section which can be changed with `.section`) must be restored to its original value at the end of the asm string. Assembly code that does not conform to the GAS syntax will result in assembler-specific behavior.\n\n[rfc-2795]: https://github.com/rust-lang/rfcs/pull/2795\n\n## Operand type\n\nSeveral types of operands are supported:\n\n* `in(<reg>) <expr>`\n - `<reg>` can refer to a register class or an explicit register. The allocated register name is substituted into the asm template string.\n - The allocated register will contain the value of `<expr>` at the start of the asm code.\n - The allocated register must contain the same value at the end of the asm code (except if a `lateout` is allocated to the same register).\n* `out(<reg>) <expr>`\n - `<reg>` can refer to a register class or an explicit register. The allocated register name is substituted into the asm template string.\n - The allocated register will contain an undefined value at the start of the asm code.\n - `<expr>` must be a (possibly uninitialized) place expression, to which the contents of the allocated register is written to at the end of the asm code.\n - An underscore (`_`) may be specified instead of an expression, which will cause the contents of the register to be discarded at the end of the asm code (effectively acting as a clobber).\n* `lateout(<reg>) <expr>`\n - Identical to `out` except that the register allocator can reuse a register allocated to an `in`.\n - You should only write to the register after all inputs are read, otherwise you may clobber an input.\n* `inout(<reg>) <expr>`\n - `<reg>` can refer to a register class or an explicit register. The allocated register name is substituted into the asm template string.\n - The allocated register will contain the value of `<expr>` at the start of the asm code.\n - `<expr>` must be a mutable initialized place expression, to which the contents of the allocated register is written to at the end of the asm code.\n* `inout(<reg>) <in expr> => <out expr>`\n - Same as `inout` except that the initial value of the register is taken from the value of `<in expr>`.\n - `<out expr>` must be a (possibly uninitialized) place expression, to which the contents of the allocated register is written to at the end of the asm code.\n - An underscore (`_`) may be specified instead of an expression for `<out expr>`, which will cause the contents of the register to be discarded at the end of the asm code (effectively acting as a clobber).\n - `<in expr>` and `<out expr>` may have different types.\n* `inlateout(<reg>) <expr>` / `inlateout(<reg>) <in expr> => <out expr>`\n - Identical to `inout` except that the register allocator can reuse a register allocated to an `in` (this can happen if the compiler knows the `in` has the same initial value as the `inlateout`).\n - You should only write to the register after all inputs are read, otherwise you may clobber an input.\n* `const <expr>`\n - `<expr>` must be an integer or floating-point constant expression.\n - The value of the expression is formatted as a string and substituted directly into the asm template string.\n* `sym <path>`\n - `<path>` must refer to a `fn` or `static`.\n - A mangled symbol name referring to the item is substituted into the asm template string.\n - The substituted string does not include any modifiers (e.g. GOT, PLT, relocations, etc).\n - `<path>` is allowed to point to a `#[thread_local]` static, in which case the asm code can combine the symbol with relocations (e.g. `@plt`, `@TPOFF`) to read from thread-local data.\n\nOperand expressions are evaluated from left to right, just like function call arguments. After the `asm!` has executed, outputs are written to in left to right order. This is significant if two outputs point to the same place: that place will contain the value of the rightmost output.\n\n## Register operands\n\nInput and output operands can be specified either as an explicit register or as a register class from which the register allocator can select a register. Explicit registers are specified as string literals (e.g. `\"eax\"`) while register classes are specified as identifiers (e.g. `reg`). Using string literals for register names enables support for architectures that use special characters in register names, such as MIPS (`$0`, `$1`, etc).\n\nNote that explicit registers treat register aliases (e.g. `r14` vs `lr` on ARM) and smaller views of a register (e.g. `eax` vs `rax`) as equivalent to the base register. It is a compile-time error to use the same explicit register for two input operands or two output operands. Additionally, it is also a compile-time error to use overlapping registers (e.g. ARM VFP) in input operands or in output operands.\n\nOnly the following types are allowed as operands for inline assembly:\n- Integers (signed and unsigned)\n- Floating-point numbers\n- Pointers (thin only)\n- Function pointers\n- SIMD vectors (structs defined with `#[repr(simd)]` and which implement `Copy`). This includes architecture-specific vector types defined in `std::arch` such as `__m128` (x86) or `int8x16_t` (ARM).\n\nHere is the list of currently supported register classes:\n\n| Architecture | Register class | Registers | LLVM constraint code |\n| ------------ | -------------- | --------- | -------------------- |\n| x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `r[8-15]` (x86-64 only) | `r` |\n| x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` |\n| x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` |\n| x86-64 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b`, `ah`\\*, `bh`\\*, `ch`\\*, `dh`\\* | `q` |\n| x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` |\n| x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` |\n| x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` |\n| x86 | `kreg` | `k[1-7]` | `Yk` |\n| AArch64 | `reg` | `x[0-28]`, `x30` | `r` |\n| AArch64 | `vreg` | `v[0-31]` | `w` |\n| AArch64 | `vreg_low16` | `v[0-15]` | `x` |\n| ARM | `reg` | `r[0-5]` `r7`\\*, `r[8-10]`, `r11`\\*, `r12`, `r14` | `r` |\n| ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` |\n| ARM (ARM) | `reg_thumb` | `r[0-r10]`, `r12`, `r14` | `l` |\n| ARM | `sreg` | `s[0-31]` | `t` |\n| ARM | `sreg_low16` | `s[0-15]` | `x` |\n| ARM | `dreg` | `d[0-31]` | `w` |\n| ARM | `dreg_low16` | `d[0-15]` | `t` |\n| ARM | `dreg_low8` | `d[0-8]` | `x` |\n| ARM | `qreg` | `q[0-15]` | `w` |\n| ARM | `qreg_low8` | `q[0-7]` | `t` |\n| ARM | `qreg_low4` | `q[0-3]` | `x` |\n| MIPS | `reg` | `$[2-25]` | `r` |\n| MIPS | `freg` | `$f[0-31]` | `f` |\n| NVPTX | `reg16` | None\\* | `h` |\n| NVPTX | `reg32` | None\\* | `r` |\n| NVPTX | `reg64` | None\\* | `l` |\n| RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` |\n| RISC-V | `freg` | `f[0-31]` | `f` |\n| Hexagon | `reg` | `r[0-28]` | `r` |\n\n> **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register.\n>\n> Note #2: On x86-64 the high byte registers (e.g. `ah`) are only available when used as an explicit register. Specifying the `reg_byte` register class for an operand will always allocate a low byte register.\n>\n> Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported.\n>\n> Note #4: On ARM the frame pointer is either `r7` or `r11` depending on the platform.\n\nAdditional register classes may be added in the future based on demand (e.g. MMX, x87, etc).\n\nEach register class has constraints on which value types they can be used with. This is necessary because the way a value is loaded into a register depends on its type. For example, on big-endian systems, loading a `i32x4` and a `i8x16` into a SIMD register may result in different register contents even if the byte-wise memory representation of both values is identical. The availability of supported types for a particular register class may depend on what target features are currently enabled.\n\n| Architecture | Register class | Target feature | Allowed types |\n| ------------ | -------------- | -------------- | ------------- |\n| x86-32 | `reg` | None | `i16`, `i32`, `f32` |\n| x86-64 | `reg` | None | `i16`, `i32`, `f32`, `i64`, `f64` |\n| x86 | `reg_byte` | None | `i8` |\n| x86 | `xmm_reg` | `sse` | `i32`, `f32`, `i64`, `f64`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` |\n| x86 | `ymm_reg` | `avx` | `i32`, `f32`, `i64`, `f64`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` <br> `i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` |\n| x86 | `zmm_reg` | `avx512f` | `i32`, `f32`, `i64`, `f64`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` <br> `i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` <br> `i8x64`, `i16x32`, `i32x16`, `i64x8`, `f32x16`, `f64x8` |\n| x86 | `kreg` | `axv512f` | `i8`, `i16` |\n| x86 | `kreg` | `axv512bw` | `i32`, `i64` |\n| AArch64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |\n| AArch64 | `vreg` | `fp` | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`, <br> `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` |\n| ARM | `reg` | None | `i8`, `i16`, `i32`, `f32` |\n| ARM | `sreg` | `vfp2` | `i32`, `f32` |\n| ARM | `dreg` | `vfp2` | `i64`, `f64`, `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2` |\n| ARM | `qreg` | `neon` | `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4` |\n| MIPS32 | `reg` | None | `i8`, `i16`, `i32`, `f32` |\n| MIPS32 | `freg` | None | `f32`, `f64` |\n| MIPS64 | `reg` | None | `i8`, `i16`, `i32`, `i64`, `f32`, `f64` |\n| MIPS64 | `freg` | None | `f32`, `f64` |\n| NVPTX | `reg16` | None | `i8`, `i16` |\n| NVPTX | `reg32` | None | `i8`, `i16`, `i32`, `f32` |\n| NVPTX | `reg64` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |\n| RISC-V32 | `reg` | None | `i8`, `i16`, `i32`, `f32` |\n| RISC-V64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |\n| RISC-V | `freg` | `f` | `f32` |\n| RISC-V | `freg` | `d` | `f64` |\n| Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` |\n\n> **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target).\n\nIf a value is of a smaller size than the register it is allocated in then the upper bits of that register will have an undefined value for inputs and will be ignored for outputs. The only exception is the `freg` register class on RISC-V where `f32` values are NaN-boxed in a `f64` as required by the RISC-V architecture.\n\nWhen separate input and output expressions are specified for an `inout` operand, both expressions must have the same type. The only exception is if both operands are pointers or integers, in which case they are only required to have the same size. This restriction exists because the register allocators in LLVM and GCC sometimes cannot handle tied operands with different types.\n\n## Register names\n\nSome registers have multiple names. These are all treated by the compiler as identical to the base register name. Here is the list of all supported register aliases:\n\n| Architecture | Base register | Aliases |\n| ------------ | ------------- | ------- |\n| x86 | `ax` | `eax`, `rax` |\n| x86 | `bx` | `ebx`, `rbx` |\n| x86 | `cx` | `ecx`, `rcx` |\n| x86 | `dx` | `edx`, `rdx` |\n| x86 | `si` | `esi`, `rsi` |\n| x86 | `di` | `edi`, `rdi` |\n| x86 | `bp` | `bpl`, `ebp`, `rbp` |\n| x86 | `sp` | `spl`, `esp`, `rsp` |\n| x86 | `ip` | `eip`, `rip` |\n| x86 | `st(0)` | `st` |\n| x86 | `r[8-15]` | `r[8-15]b`, `r[8-15]w`, `r[8-15]d` |\n| x86 | `xmm[0-31]` | `ymm[0-31]`, `zmm[0-31]` |\n| AArch64 | `x[0-30]` | `w[0-30]` |\n| AArch64 | `x29` | `fp` |\n| AArch64 | `x30` | `lr` |\n| AArch64 | `sp` | `wsp` |\n| AArch64 | `xzr` | `wzr` |\n| AArch64 | `v[0-31]` | `b[0-31]`, `h[0-31]`, `s[0-31]`, `d[0-31]`, `q[0-31]` |\n| ARM | `r[0-3]` | `a[1-4]` |\n| ARM | `r[4-9]` | `v[1-6]` |\n| ARM | `r9` | `rfp` |\n| ARM | `r10` | `sl` |\n| ARM | `r11` | `fp` |\n| ARM | `r12` | `ip` |\n| ARM | `r13` | `sp` |\n| ARM | `r14` | `lr` |\n| ARM | `r15` | `pc` |\n| RISC-V | `x0` | `zero` |\n| RISC-V | `x1` | `ra` |\n| RISC-V | `x2` | `sp` |\n| RISC-V | `x3` | `gp` |\n| RISC-V | `x4` | `tp` |\n| RISC-V | `x[5-7]` | `t[0-2]` |\n| RISC-V | `x8` | `fp`, `s0` |\n| RISC-V | `x9` | `s1` |\n| RISC-V | `x[10-17]` | `a[0-7]` |\n| RISC-V | `x[18-27]` | `s[2-11]` |\n| RISC-V | `x[28-31]` | `t[3-6]` |\n| RISC-V | `f[0-7]` | `ft[0-7]` |\n| RISC-V | `f[8-9]` | `fs[0-1]` |\n| RISC-V | `f[10-17]` | `fa[0-7]` |\n| RISC-V | `f[18-27]` | `fs[2-11]` |\n| RISC-V | `f[28-31]` | `ft[8-11]` |\n| Hexagon | `r29` | `sp` |\n| Hexagon | `r30` | `fr` |\n| Hexagon | `r31` | `lr` |\n\nSome registers cannot be used for input or output operands:\n\n| Architecture | Unsupported register | Reason |\n| ------------ | -------------------- | ------ |\n| All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. |\n| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS) | The frame pointer cannot be used as an input or output. |\n| ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. |\n| ARM | `r6` | `r6` is used internally by LLVM as a base pointer and therefore cannot be used as an input or output. |\n| x86 | `k0` | This is a constant zero register which can't be modified. |\n| x86 | `ip` | This is the program counter, not a real register. |\n| x86 | `mm[0-7]` | MMX registers are not currently supported (but may be in the future). |\n| x86 | `st([0-7])` | x87 registers are not currently supported (but may be in the future). |\n| AArch64 | `xzr` | This is a constant zero register which can't be modified. |\n| ARM | `pc` | This is the program counter, not a real register. |\n| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. |\n| MIPS | `$1` or `$at` | Reserved for assembler. |\n| MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. |\n| MIPS | `$28`/`$gp` | Global pointer cannot be used as inputs or outputs. |\n| MIPS | `$ra` | Return address cannot be used as inputs or outputs. |\n| RISC-V | `x0` | This is a constant zero register which can't be modified. |\n| RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. |\n| Hexagon | `lr` | This is the link register which cannot be used as an input or output. |\n\nIn some cases LLVM will allocate a \"reserved register\" for `reg` operands even though this register cannot be explicitly specified. Assembly code making use of reserved registers should be careful since `reg` operands may alias with those registers. Reserved registers are:\n- The frame pointer on all architectures.\n- `r6` on ARM.\n\n## Template modifiers\n\nThe placeholders can be augmented by modifiers which are specified after the `:` in the curly braces. These modifiers do not affect register allocation, but change the way operands are formatted when inserted into the template string. Only one modifier is allowed per template placeholder.\n\nThe supported modifiers are a subset of LLVM's (and GCC's) [asm template argument modifiers][llvm-argmod], but do not use the same letter codes.\n\n| Architecture | Register class | Modifier | Example output | LLVM modifier |\n| ------------ | -------------- | -------- | -------------- | ------------- |\n| x86-32 | `reg` | None | `eax` | `k` |\n| x86-64 | `reg` | None | `rax` | `q` |\n| x86-32 | `reg_abcd` | `l` | `al` | `b` |\n| x86-64 | `reg` | `l` | `al` | `b` |\n| x86 | `reg_abcd` | `h` | `ah` | `h` |\n| x86 | `reg` | `x` | `ax` | `w` |\n| x86 | `reg` | `e` | `eax` | `k` |\n| x86-64 | `reg` | `r` | `rax` | `q` |\n| x86 | `reg_byte` | None | `al` / `ah` | None |\n| x86 | `xmm_reg` | None | `xmm0` | `x` |\n| x86 | `ymm_reg` | None | `ymm0` | `t` |\n| x86 | `zmm_reg` | None | `zmm0` | `g` |\n| x86 | `*mm_reg` | `x` | `xmm0` | `x` |\n| x86 | `*mm_reg` | `y` | `ymm0` | `t` |\n| x86 | `*mm_reg` | `z` | `zmm0` | `g` |\n| x86 | `kreg` | None | `k1` | None |\n| AArch64 | `reg` | None | `x0` | `x` |\n| AArch64 | `reg` | `w` | `w0` | `w` |\n| AArch64 | `reg` | `x` | `x0` | `x` |\n| AArch64 | `vreg` | None | `v0` | None |\n| AArch64 | `vreg` | `v` | `v0` | None |\n| AArch64 | `vreg` | `b` | `b0` | `b` |\n| AArch64 | `vreg` | `h` | `h0` | `h` |\n| AArch64 | `vreg` | `s` | `s0` | `s` |\n| AArch64 | `vreg` | `d` | `d0` | `d` |\n| AArch64 | `vreg` | `q` | `q0` | `q` |\n| ARM | `reg` | None | `r0` | None |\n| ARM | `sreg` | None | `s0` | None |\n| ARM | `dreg` | None | `d0` | `P` |\n| ARM | `qreg` | None | `q0` | `q` |\n| ARM | `qreg` | `e` / `f` | `d0` / `d1` | `e` / `f` |\n| MIPS | `reg` | None | `$2` | None |\n| MIPS | `freg` | None | `$f0` | None |\n| NVPTX | `reg16` | None | `rs0` | None |\n| NVPTX | `reg32` | None | `r0` | None |\n| NVPTX | `reg64` | None | `rd0` | None |\n| RISC-V | `reg` | None | `x1` | None |\n| RISC-V | `freg` | None | `f0` | None |\n| Hexagon | `reg` | None | `r0` | None |\n\n> Notes:\n> - on ARM `e` / `f`: this prints the low or high doubleword register name of a NEON quad (128-bit) register.\n> - on x86: our behavior for `reg` with no modifiers differs from what GCC does. GCC will infer the modifier based on the operand value type, while we default to the full register size.\n> - on x86 `xmm_reg`: the `x`, `t` and `g` LLVM modifiers are not yet implemented in LLVM (they are supported by GCC only), but this should be a simple change.\n\nAs stated in the previous section, passing an input value smaller than the register width will result in the upper bits of the register containing undefined values. This is not a problem if the inline asm only accesses the lower bits of the register, which can be done by using a template modifier to use a subregister name in the asm code (e.g. `ax` instead of `rax`). Since this an easy pitfall, the compiler will suggest a template modifier to use where appropriate given the input type. If all references to an operand already have modifiers then the warning is suppressed for that operand.\n\n[llvm-argmod]: http://llvm.org/docs/LangRef.html#asm-template-argument-modifiers\n\n## Options\n\nFlags are used to further influence the behavior of the inline assembly block.\nCurrently the following options are defined:\n- `pure`: The `asm` block has no side effects, and its outputs depend only on its direct inputs (i.e. the values themselves, not what they point to) or values read from memory (unless the `nomem` options is also set). This allows the compiler to execute the `asm` block fewer times than specified in the program (e.g. by hoisting it out of a loop) or even eliminate it entirely if the outputs are not used.\n- `nomem`: The `asm` blocks does not read or write to any memory. This allows the compiler to cache the values of modified global variables in registers across the `asm` block since it knows that they are not read or written to by the `asm`.\n- `readonly`: The `asm` block does not write to any memory. This allows the compiler to cache the values of unmodified global variables in registers across the `asm` block since it knows that they are not written to by the `asm`.\n- `preserves_flags`: The `asm` block does not modify the flags register (defined in the rules below). This allows the compiler to avoid recomputing the condition flags after the `asm` block.\n- `noreturn`: The `asm` block never returns, and its return type is defined as `!` (never). Behavior is undefined if execution falls through past the end of the asm code. A `noreturn` asm block behaves just like a function which doesn't return; notably, local variables in scope are not dropped before it is invoked.\n- `nostack`: The `asm` block does not push data to the stack, or write to the stack red-zone (if supported by the target). If this option is *not* used then the stack pointer is guaranteed to be suitably aligned (according to the target ABI) for a function call.\n- `att_syntax`: This option is only valid on x86, and causes the assembler to use the `.att_syntax prefix` mode of the GNU assembler. Register operands are substituted in with a leading `%`.\n\nThe compiler performs some additional checks on options:\n- The `nomem` and `readonly` options are mutually exclusive: it is a compile-time error to specify both.\n- The `pure` option must be combined with either the `nomem` or `readonly` options, otherwise a compile-time error is emitted.\n- It is a compile-time error to specify `pure` on an asm block with no outputs or only discarded outputs (`_`).\n- It is a compile-time error to specify `noreturn` on an asm block with outputs.\n\n## Rules for inline assembly\n\n- Any registers not specified as inputs will contain an undefined value on entry to the asm block.\n - An \"undefined value\" in the context of inline assembly means that the register can (non-deterministically) have any one of the possible values allowed by the architecture. Notably it is not the same as an LLVM `undef` which can have a different value every time you read it (since such a concept does not exist in assembly code).\n- Any registers not specified as outputs must have the same value upon exiting the asm block as they had on entry, otherwise behavior is undefined.\n - This only applies to registers which can be specified as an input or output. Other registers follow target-specific rules.\n - Note that a `lateout` may be allocated to the same register as an `in`, in which case this rule does not apply. Code should not rely on this however since it depends on the results of register allocation.\n- Behavior is undefined if execution unwinds out of an asm block.\n - This also applies if the assembly code calls a function which then unwinds.\n- The set of memory locations that assembly code is allowed the read and write are the same as those allowed for an FFI function.\n - Refer to the unsafe code guidelines for the exact rules.\n - If the `readonly` option is set, then only memory reads are allowed.\n - If the `nomem` option is set then no reads or writes to memory are allowed.\n - These rules do not apply to memory which is private to the asm code, such as stack space allocated within the asm block.\n- The compiler cannot assume that the instructions in the asm are the ones that will actually end up executed.\n - This effectively means that the compiler must treat the `asm!` as a black box and only take the interface specification into account, not the instructions themselves.\n - Runtime code patching is allowed, via target-specific mechanisms (outside the scope of this RFC).\n- Unless the `nostack` option is set, asm code is allowed to use stack space below the stack pointer.\n - On entry to the asm block the stack pointer is guaranteed to be suitably aligned (according to the target ABI) for a function call.\n - You are responsible for making sure you don't overflow the stack (e.g. use stack probing to ensure you hit a guard page).\n - You should adjust the stack pointer when allocating stack memory as required by the target ABI.\n - The stack pointer must be restored to its original value before leaving the asm block.\n- If the `noreturn` option is set then behavior is undefined if execution falls through to the end of the asm block.\n- If the `pure` option is set then behavior is undefined if the `asm` has side-effects other than its direct outputs. Behavior is also undefined if two executions of the `asm` code with the same inputs result in different outputs.\n - When used with the `nomem` option, \"inputs\" are just the direct inputs of the `asm!`.\n - When used with the `readonly` option, \"inputs\" comprise the direct inputs of the `asm!` and any memory that the `asm!` block is allowed to read.\n- These flags registers must be restored upon exiting the asm block if the `preserves_flags` option is set:\n - x86\n - Status flags in `EFLAGS` (CF, PF, AF, ZF, SF, OF).\n - Floating-point status word (all).\n - Floating-point exception flags in `MXCSR` (PE, UE, OE, ZE, DE, IE).\n - ARM\n - Condition flags in `CPSR` (N, Z, C, V)\n - Saturation flag in `CPSR` (Q)\n - Greater than or equal flags in `CPSR` (GE).\n - Condition flags in `FPSCR` (N, Z, C, V)\n - Saturation flag in `FPSCR` (QC)\n - Floating-point exception flags in `FPSCR` (IDC, IXC, UFC, OFC, DZC, IOC).\n - AArch64\n - Condition flags (`NZCV` register).\n - Floating-point status (`FPSR` register).\n - RISC-V\n - Floating-point exception flags in `fcsr` (`fflags`).\n- On x86, the direction flag (DF in `EFLAGS`) is clear on entry to an asm block and must be clear on exit.\n - Behavior is undefined if the direction flag is set on exiting an asm block.\n- The requirement of restoring the stack pointer and non-output registers to their original value only applies when exiting an `asm!` block.\n - This means that `asm!` blocks that never return (even if not marked `noreturn`) don't need to preserve these registers.\n - When returning to a different `asm!` block than you entered (e.g. for context switching), these registers must contain the value they had upon entering the `asm!` block that you are *exiting*.\n - You cannot exit an `asm!` block that has not been entered. Neither can you exit an `asm!` block that has already been exited.\n - You are responsible for switching any target-specific state (e.g. thread-local storage, stack bounds).\n - The set of memory locations that you may access is the intersection of those allowed by the `asm!` blocks you entered and exited.\n- You cannot assume that an `asm!` block will appear exactly once in the output binary. The compiler is allowed to instantiate multiple copies of the `asm!` block, for example when the function containing it is inlined in multiple places.\n - As a consequence, you should only use [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions.\n\n> **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call.\n\n[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels\n" } , LintCompletion { label : "allocator_api" , description : "# `allocator_api`\n\nThe tracking issue for this feature is [#32838]\n\n[#32838]: https://github.com/rust-lang/rust/issues/32838\n\n------------------------\n\nSometimes you want the memory for one collection to use a different\nallocator than the memory for another collection. In this case,\nreplacing the global allocator is not a workable option. Instead,\nyou need to pass in an instance of an `AllocRef` to each collection\nfor which you want a custom allocator.\n\nTBD\n" } , LintCompletion { label : "set_stdio" , description : "# `set_stdio`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "libstd_sys_internals" , description : "# `libstd_sys_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "concat_idents" , description : "# `concat_idents`\n\nThe tracking issue for this feature is: [#29599]\n\n[#29599]: https://github.com/rust-lang/rust/issues/29599\n\n------------------------\n\nThe `concat_idents` feature adds a macro for concatenating multiple identifiers\ninto one identifier.\n\n## Examples\n\n```rust\n#![feature(concat_idents)]\n\nfn main() {\n fn foobar() -> u32 { 23 }\n let f = concat_idents!(foo, bar);\n assert_eq!(f(), 23);\n}\n```" } , LintCompletion { label : "format_args_capture" , description : "# `format_args_capture`\n\nThe tracking issue for this feature is: [#67984]\n\n[#67984]: https://github.com/rust-lang/rust/issues/67984\n\n------------------------\n\nEnables `format_args!` (and macros which use `format_args!` in their implementation, such\nas `format!`, `print!` and `panic!`) to capture variables from the surrounding scope.\nThis avoids the need to pass named parameters when the binding in question\nalready exists in scope.\n\n```rust\n#![feature(format_args_capture)]\n\nlet (person, species, name) = (\"Charlie Brown\", \"dog\", \"Snoopy\");\n\n// captures named argument `person`\nprint!(\"Hello {person}\");\n\n// captures named arguments `species` and `name`\nformat!(\"The {species}'s name is {name}.\");\n```\n\nThis also works for formatting parameters such as width and precision:\n\n```rust\n#![feature(format_args_capture)]\n\nlet precision = 2;\nlet s = format!(\"{:.precision$}\", 1.324223);\n\nassert_eq!(&s, \"1.32\");\n```\n\nA non-exhaustive list of macros which benefit from this functionality include:\n- `format!`\n- `print!` and `println!`\n- `eprint!` and `eprintln!`\n- `write!` and `writeln!`\n- `panic!`\n- `unreachable!`\n- `unimplemented!`\n- `todo!`\n- `assert!` and similar\n- macros in many thirdparty crates, such as `log`\n" } , LintCompletion { label : "is_sorted" , description : "# `is_sorted`\n\nThe tracking issue for this feature is: [#53485]\n\n[#53485]: https://github.com/rust-lang/rust/issues/53485\n\n------------------------\n\nAdd the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to `[T]`;\nadd the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to\n`Iterator`.\n" } , LintCompletion { label : "sort_internals" , description : "# `sort_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "c_variadic" , description : "# `c_variadic`\n\nThe tracking issue for this feature is: [#44930]\n\n[#44930]: https://github.com/rust-lang/rust/issues/44930\n\n------------------------\n\nThe `c_variadic` library feature exposes the `VaList` structure,\nRust's analogue of C's `va_list` type.\n\n## Examples\n\n```rust\n#![feature(c_variadic)]\n\nuse std::ffi::VaList;\n\npub unsafe extern \"C\" fn vadd(n: usize, mut args: VaList) -> usize {\n let mut sum = 0;\n for _ in 0..n {\n sum += args.arg::<usize>();\n }\n sum\n}\n```\n" } , LintCompletion { label : "trace_macros" , description : "# `trace_macros`\n\nThe tracking issue for this feature is [#29598].\n\n[#29598]: https://github.com/rust-lang/rust/issues/29598\n\n------------------------\n\nWith `trace_macros` you can trace the expansion of macros in your code.\n\n## Examples\n\n```rust\n#![feature(trace_macros)]\n\nfn main() {\n trace_macros!(true);\n println!(\"Hello, Rust!\");\n trace_macros!(false);\n}\n```\n\nThe `cargo build` output:\n\n```txt\nnote: trace_macro\n --> src/main.rs:5:5\n |\n5 | println!(\"Hello, Rust!\");\n | ^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = note: expanding `println! { \"Hello, Rust!\" }`\n = note: to `print ! ( concat ! ( \"Hello, Rust!\" , \"\\n\" ) )`\n = note: expanding `print! { concat ! ( \"Hello, Rust!\" , \"\\n\" ) }`\n = note: to `$crate :: io :: _print ( format_args ! ( concat ! ( \"Hello, Rust!\" , \"\\n\" ) )\n )`\n\n Finished dev [unoptimized + debuginfo] target(s) in 0.60 secs\n```\n" } , LintCompletion { label : "c_void_variant" , description : "# `c_void_variant`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "core_panic" , description : "# `core_panic`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "fmt_internals" , description : "# `fmt_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "rt" , description : "# `rt`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "thread_local_internals" , description : "# `thread_local_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "core_intrinsics" , description : "# `core_intrinsics`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "update_panic_count" , description : "# `update_panic_count`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "profiler_runtime_lib" , description : "# `profiler_runtime_lib`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "core_private_bignum" , description : "# `core_private_bignum`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "core_private_diy_float" , description : "# `core_private_diy_float`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "str_internals" , description : "# `str_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "derive_clone_copy" , description : "# `derive_clone_copy`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "fn_traits" , description : "# `fn_traits`\n\nThe tracking issue for this feature is [#29625]\n\nSee Also: [`unboxed_closures`](../language-features/unboxed-closures.md)\n\n[#29625]: https://github.com/rust-lang/rust/issues/29625\n\n----\n\nThe `fn_traits` feature allows for implementation of the [`Fn*`] traits\nfor creating custom closure-like types.\n\n[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html\n\n```rust\n#![feature(unboxed_closures)]\n#![feature(fn_traits)]\n\nstruct Adder {\n a: u32\n}\n\nimpl FnOnce<(u32, )> for Adder {\n type Output = u32;\n extern \"rust-call\" fn call_once(self, b: (u32, )) -> Self::Output {\n self.a + b.0\n }\n}\n\nfn main() {\n let adder = Adder { a: 3 };\n assert_eq!(adder(2), 5);\n}\n```\n" } , LintCompletion { label : "fd_read" , description : "# `fd_read`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "windows_net" , description : "# `windows_net`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "derive_eq" , description : "# `derive_eq`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "windows_stdio" , description : "# `windows_stdio`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "test" , description : "# `test`\n\nThe tracking issue for this feature is: None.\n\n------------------------\n\nThe internals of the `test` crate are unstable, behind the `test` flag. The\nmost widely used part of the `test` crate are benchmark tests, which can test\nthe performance of your code. Let's make our `src/lib.rs` look like this\n(comments elided):\n\n```rust,ignore\n#![feature(test)]\n\nextern crate test;\n\npub fn add_two(a: i32) -> i32 {\n a + 2\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use test::Bencher;\n\n #[test]\n fn it_works() {\n assert_eq!(4, add_two(2));\n }\n\n #[bench]\n fn bench_add_two(b: &mut Bencher) {\n b.iter(|| add_two(2));\n }\n}\n```\n\nNote the `test` feature gate, which enables this unstable feature.\n\nWe've imported the `test` crate, which contains our benchmarking support.\nWe have a new function as well, with the `bench` attribute. Unlike regular\ntests, which take no arguments, benchmark tests take a `&mut Bencher`. This\n`Bencher` provides an `iter` method, which takes a closure. This closure\ncontains the code we'd like to benchmark.\n\nWe can run benchmark tests with `cargo bench`:\n\n```bash\n$ cargo bench\n Compiling adder v0.0.1 (file:///home/steve/tmp/adder)\n Running target/release/adder-91b3e234d4ed382a\n\nrunning 2 tests\ntest tests::it_works ... ignored\ntest tests::bench_add_two ... bench: 1 ns/iter (+/- 0)\n\ntest result: ok. 0 passed; 0 failed; 1 ignored; 1 measured\n```\n\nOur non-benchmark test was ignored. You may have noticed that `cargo bench`\ntakes a bit longer than `cargo test`. This is because Rust runs our benchmark\na number of times, and then takes the average. Because we're doing so little\nwork in this example, we have a `1 ns/iter (+/- 0)`, but this would show\nthe variance if there was one.\n\nAdvice on writing benchmarks:\n\n\n* Move setup code outside the `iter` loop; only put the part you want to measure inside\n* Make the code do \"the same thing\" on each iteration; do not accumulate or change state\n* Make the outer function idempotent too; the benchmark runner is likely to run\n it many times\n* Make the inner `iter` loop short and fast so benchmark runs are fast and the\n calibrator can adjust the run-length at fine resolution\n* Make the code in the `iter` loop do something simple, to assist in pinpointing\n performance improvements (or regressions)\n\n## Gotcha: optimizations\n\nThere's another tricky part to writing benchmarks: benchmarks compiled with\noptimizations activated can be dramatically changed by the optimizer so that\nthe benchmark is no longer benchmarking what one expects. For example, the\ncompiler might recognize that some calculation has no external effects and\nremove it entirely.\n\n```rust,ignore\n#![feature(test)]\n\nextern crate test;\nuse test::Bencher;\n\n#[bench]\nfn bench_xor_1000_ints(b: &mut Bencher) {\n b.iter(|| {\n (0..1000).fold(0, |old, new| old ^ new);\n });\n}\n```\n\ngives the following results\n\n```text\nrunning 1 test\ntest bench_xor_1000_ints ... bench: 0 ns/iter (+/- 0)\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 1 measured\n```\n\nThe benchmarking runner offers two ways to avoid this. Either, the closure that\nthe `iter` method receives can return an arbitrary value which forces the\noptimizer to consider the result used and ensures it cannot remove the\ncomputation entirely. This could be done for the example above by adjusting the\n`b.iter` call to\n\n```rust\n# struct X;\n# impl X { fn iter<T, F>(&self, _: F) where F: FnMut() -> T {} } let b = X;\nb.iter(|| {\n // Note lack of `;` (could also use an explicit `return`).\n (0..1000).fold(0, |old, new| old ^ new)\n});\n```\n\nOr, the other option is to call the generic `test::black_box` function, which\nis an opaque \"black box\" to the optimizer and so forces it to consider any\nargument as used.\n\n```rust\n#![feature(test)]\n\nextern crate test;\n\n# fn main() {\n# struct X;\n# impl X { fn iter<T, F>(&self, _: F) where F: FnMut() -> T {} } let b = X;\nb.iter(|| {\n let n = test::black_box(1000);\n\n (0..n).fold(0, |a, b| a ^ b)\n})\n# }\n```\n\nNeither of these read or modify the value, and are very cheap for small values.\nLarger values can be passed indirectly to reduce overhead (e.g.\n`black_box(&huge_struct)`).\n\nPerforming either of the above changes gives the following benchmarking results\n\n```text\nrunning 1 test\ntest bench_xor_1000_ints ... bench: 131 ns/iter (+/- 3)\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 1 measured\n```\n\nHowever, the optimizer can still modify a testcase in an undesirable manner\neven when using either of the above.\n" } , LintCompletion { label : "flt2dec" , description : "# `flt2dec`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "libstd_io_internals" , description : "# `libstd_io_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "fd" , description : "# `fd`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "print_internals" , description : "# `print_internals`\n\nThis feature is internal to the Rust compiler and is not intended for general use.\n\n------------------------\n" } , LintCompletion { label : "range_bounds_assert_len" , description : "# `range_bounds_assert_len`\n\nThe tracking issue for this feature is: [#76393]\n\n------------------------\n\nThis adds [`RangeBounds::assert_len`].\n\n[#76393]: https://github.com/rust-lang/rust/issues/76393\n[`RangeBounds::assert_len`]: https://doc.rust-lang.org/nightly/std/ops/trait.RangeBounds.html#method.assert_len\n" } , LintCompletion { label : "try_trait" , description : "# `try_trait`\n\nThe tracking issue for this feature is: [#42327]\n\n[#42327]: https://github.com/rust-lang/rust/issues/42327\n\n------------------------\n\nThis introduces a new trait `Try` for extending the `?` operator to types\nother than `Result` (a part of [RFC 1859]). The trait provides the canonical\nway to _view_ a type in terms of a success/failure dichotomy. This will\nallow `?` to supplant the `try_opt!` macro on `Option` and the `try_ready!`\nmacro on `Poll`, among other things.\n\n[RFC 1859]: https://github.com/rust-lang/rfcs/pull/1859\n\nHere's an example implementation of the trait:\n\n```rust,ignore\n/// A distinct type to represent the `None` value of an `Option`.\n///\n/// This enables using the `?` operator on `Option`; it's rarely useful alone.\n#[derive(Debug)]\n#[unstable(feature = \"try_trait\", issue = \"42327\")]\npub struct None { _priv: () }\n\n#[unstable(feature = \"try_trait\", issue = \"42327\")]\nimpl<T> ops::Try for Option<T> {\n type Ok = T;\n type Error = None;\n\n fn into_result(self) -> Result<T, None> {\n self.ok_or(None { _priv: () })\n }\n\n fn from_ok(v: T) -> Self {\n Some(v)\n }\n\n fn from_error(_: None) -> Self {\n None\n }\n}\n```\n\nNote the `Error` associated type here is a new marker. The `?` operator\nallows interconversion between different `Try` implementers only when\nthe error type can be converted `Into` the error type of the enclosing\nfunction (or catch block). Having a distinct error type (as opposed to\njust `()`, or similar) restricts this to where it's semantically meaningful.\n" }] ;
5pub (super) const CLIPPY_LINTS : & [LintCompletion] = & [LintCompletion { label : "clippy::absurd_extreme_comparisons" , description : "Checks for comparisons where one side of the relation is\\neither the minimum or maximum value for its type and warns if it involves a\\ncase that is always true or always false. Only integer and boolean types are\\nchecked." } , LintCompletion { label : "clippy::almost_swapped" , description : "Checks for `foo = bar; bar = foo` sequences." } , LintCompletion { label : "clippy::approx_constant" , description : "Checks for floating point literals that approximate\\nconstants which are defined in\\n[`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)\\nor\\n[`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),\\nrespectively, suggesting to use the predefined constant." } , LintCompletion { label : "clippy::as_conversions" , description : "Checks for usage of `as` conversions." } , LintCompletion { label : "clippy::assertions_on_constants" , description : "Checks for `assert!(true)` and `assert!(false)` calls." } , LintCompletion { label : "clippy::assign_op_pattern" , description : "Checks for `a = a op b` or `a = b commutative_op a`\\npatterns." } , LintCompletion { label : "clippy::assign_ops" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::async_yields_async" , description : "Checks for async blocks that yield values of types\\nthat can themselves be awaited." } , LintCompletion { label : "clippy::await_holding_lock" , description : "Checks for calls to await while holding a\\nnon-async-aware MutexGuard." } , LintCompletion { label : "clippy::bad_bit_mask" , description : "Checks for incompatible bit masks in comparisons.\\n\\nThe formula for detecting if an expression of the type `_ <bit_op> m\\n<cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of\\n{`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following\\ntable:\\n\\n|Comparison |Bit Op|Example |is always|Formula |\\n|------------|------|------------|---------|----------------------|\\n|`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |\\n|`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |\\n|`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |\\n|`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |\\n|`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |\\n|`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |" } , LintCompletion { label : "clippy::bind_instead_of_map" , description : "Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or\\n`_.or_else(|x| Err(y))`." } , LintCompletion { label : "clippy::blacklisted_name" , description : "Checks for usage of blacklisted names for variables, such\\nas `foo`." } , LintCompletion { label : "clippy::blanket_clippy_restriction_lints" , description : "Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category." } , LintCompletion { label : "clippy::blocks_in_if_conditions" , description : "Checks for `if` conditions that use blocks containing an\\nexpression, statements or conditions that use closures with blocks." } , LintCompletion { label : "clippy::bool_comparison" , description : "Checks for expressions of the form `x == true`,\\n`x != true` and order comparisons such as `x < true` (or vice versa) and\\nsuggest using the variable directly." } , LintCompletion { label : "clippy::borrow_interior_mutable_const" , description : "Checks if `const` items which is interior mutable (e.g.,\\ncontains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly." } , LintCompletion { label : "clippy::borrowed_box" , description : "Checks for use of `&Box<T>` anywhere in the code.\\nCheck the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information." } , LintCompletion { label : "clippy::box_vec" , description : "Checks for use of `Box<Vec<_>>` anywhere in the code.\\nCheck the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information." } , LintCompletion { label : "clippy::boxed_local" , description : "Checks for usage of `Box<T>` where an unboxed `T` would\\nwork fine." } , LintCompletion { label : "clippy::builtin_type_shadow" , description : "Warns if a generic shadows a built-in type." } , LintCompletion { label : "clippy::cargo_common_metadata" , description : "Checks to see if all common metadata is defined in\\n`Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata" } , LintCompletion { label : "clippy::cast_lossless" , description : "Checks for casts between numerical types that may\\nbe replaced by safe conversion functions." } , LintCompletion { label : "clippy::cast_possible_truncation" , description : "Checks for casts between numerical types that may\\ntruncate large values. This is expected behavior, so the cast is `Allow` by\\ndefault." } , LintCompletion { label : "clippy::cast_possible_wrap" , description : "Checks for casts from an unsigned type to a signed type of\\nthe same size. Performing such a cast is a 'no-op' for the compiler,\\ni.e., nothing is changed at the bit level, and the binary representation of\\nthe value is reinterpreted. This can cause wrapping if the value is too big\\nfor the target signed type. However, the cast works as defined, so this lint\\nis `Allow` by default." } , LintCompletion { label : "clippy::cast_precision_loss" , description : "Checks for casts from any numerical to a float type where\\nthe receiving type cannot store all values from the original type without\\nrounding errors. This possible rounding is to be expected, so this lint is\\n`Allow` by default.\\n\\nBasically, this warns on casting any integer with 32 or more bits to `f32`\\nor any 64-bit integer to `f64`." } , LintCompletion { label : "clippy::cast_ptr_alignment" , description : "Checks for casts from a less-strictly-aligned pointer to a\\nmore-strictly-aligned pointer" } , LintCompletion { label : "clippy::cast_ref_to_mut" , description : "Checks for casts of `&T` to `&mut T` anywhere in the code." } , LintCompletion { label : "clippy::cast_sign_loss" , description : "Checks for casts from a signed to an unsigned numerical\\ntype. In this case, negative values wrap around to large positive values,\\nwhich can be quite surprising in practice. However, as the cast works as\\ndefined, this lint is `Allow` by default." } , LintCompletion { label : "clippy::char_lit_as_u8" , description : "Checks for expressions where a character literal is cast\\nto `u8` and suggests using a byte literal instead." } , LintCompletion { label : "clippy::chars_last_cmp" , description : "Checks for usage of `_.chars().last()` or\\n`_.chars().next_back()` on a `str` to check if it ends with a given char." } , LintCompletion { label : "clippy::chars_next_cmp" , description : "Checks for usage of `.chars().next()` on a `str` to check\\nif it starts with a given char." } , LintCompletion { label : "clippy::checked_conversions" , description : "Checks for explicit bounds checking when casting." } , LintCompletion { label : "clippy::clone_double_ref" , description : "Checks for usage of `.clone()` on an `&&T`." } , LintCompletion { label : "clippy::clone_on_copy" , description : "Checks for usage of `.clone()` on a `Copy` type." } , LintCompletion { label : "clippy::clone_on_ref_ptr" , description : "Checks for usage of `.clone()` on a ref-counted pointer,\\n(`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified\\nfunction syntax instead (e.g., `Rc::clone(foo)`)." } , LintCompletion { label : "clippy::cmp_nan" , description : "Checks for comparisons to NaN." } , LintCompletion { label : "clippy::cmp_null" , description : "This lint checks for equality comparisons with `ptr::null`" } , LintCompletion { label : "clippy::cmp_owned" , description : "Checks for conversions to owned values just for the sake\\nof a comparison." } , LintCompletion { label : "clippy::cognitive_complexity" , description : "Checks for methods with high cognitive complexity." } , LintCompletion { label : "clippy::collapsible_if" , description : "Checks for nested `if` statements which can be collapsed\\nby `&&`-combining their conditions and for `else { if ... }` expressions\\nthat\\ncan be collapsed to `else if ...`." } , LintCompletion { label : "clippy::comparison_chain" , description : "Checks comparison chains written with `if` that can be\\nrewritten with `match` and `cmp`." } , LintCompletion { label : "clippy::copy_iterator" , description : "Checks for types that implement `Copy` as well as\\n`Iterator`." } , LintCompletion { label : "clippy::create_dir" , description : "Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead." } , LintCompletion { label : "clippy::crosspointer_transmute" , description : "Checks for transmutes between a type `T` and `*T`." } , LintCompletion { label : "clippy::dbg_macro" , description : "Checks for usage of dbg!() macro." } , LintCompletion { label : "clippy::debug_assert_with_mut_call" , description : "Checks for function/method calls with a mutable\\nparameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros." } , LintCompletion { label : "clippy::decimal_literal_representation" , description : "Warns if there is a better representation for a numeric literal." } , LintCompletion { label : "clippy::declare_interior_mutable_const" , description : "Checks for declaration of `const` items which is interior\\nmutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.)." } , LintCompletion { label : "clippy::default_trait_access" , description : "Checks for literal calls to `Default::default()`." } , LintCompletion { label : "clippy::deprecated_cfg_attr" , description : "Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it\\nwith `#[rustfmt::skip]`." } , LintCompletion { label : "clippy::deprecated_semver" , description : "Checks for `#[deprecated]` annotations with a `since`\\nfield that is not a valid semantic version." } , LintCompletion { label : "clippy::deref_addrof" , description : "Checks for usage of `*&` and `*&mut` in expressions." } , LintCompletion { label : "clippy::derive_hash_xor_eq" , description : "Checks for deriving `Hash` but implementing `PartialEq`\\nexplicitly or vice versa." } , LintCompletion { label : "clippy::derive_ord_xor_partial_ord" , description : "Checks for deriving `Ord` but implementing `PartialOrd`\\nexplicitly or vice versa." } , LintCompletion { label : "clippy::disallowed_method" , description : "Lints for specific trait methods defined in clippy.toml" } , LintCompletion { label : "clippy::diverging_sub_expression" , description : "Checks for diverging calls that are not match arms or\\nstatements." } , LintCompletion { label : "clippy::doc_markdown" , description : "Checks for the presence of `_`, `::` or camel-case words\\noutside ticks in documentation." } , LintCompletion { label : "clippy::double_comparisons" , description : "Checks for double comparisons that could be simplified to a single expression." } , LintCompletion { label : "clippy::double_must_use" , description : "Checks for a [`#[must_use]`] attribute without\\nfurther information on functions and methods that return a type already\\nmarked as `#[must_use]`.\\n\\n[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute" } , LintCompletion { label : "clippy::double_neg" , description : "Detects expressions of the form `--x`." } , LintCompletion { label : "clippy::double_parens" , description : "Checks for unnecessary double parentheses." } , LintCompletion { label : "clippy::drop_bounds" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::drop_copy" , description : "Checks for calls to `std::mem::drop` with a value\\nthat derives the Copy trait" } , LintCompletion { label : "clippy::drop_ref" , description : "Checks for calls to `std::mem::drop` with a reference\\ninstead of an owned value." } , LintCompletion { label : "clippy::duplicate_underscore_argument" , description : "Checks for function arguments having the similar names\\ndiffering by an underscore." } , LintCompletion { label : "clippy::duration_subsec" , description : "Checks for calculation of subsecond microseconds or milliseconds\\nfrom other `Duration` methods." } , LintCompletion { label : "clippy::else_if_without_else" , description : "Checks for usage of if expressions with an `else if` branch,\\nbut without a final `else` branch." } , LintCompletion { label : "clippy::empty_enum" , description : "Checks for `enum`s with no variants." } , LintCompletion { label : "clippy::empty_line_after_outer_attr" , description : "Checks for empty lines after outer attributes" } , LintCompletion { label : "clippy::empty_loop" , description : "Checks for empty `loop` expressions." } , LintCompletion { label : "clippy::enum_clike_unportable_variant" , description : "Checks for C-like enumerations that are\\n`repr(isize/usize)` and have values that don't fit into an `i32`." } , LintCompletion { label : "clippy::enum_glob_use" , description : "Checks for `use Enum::*`." } , LintCompletion { label : "clippy::enum_variant_names" , description : "Detects enumeration variants that are prefixed or suffixed\\nby the same characters." } , LintCompletion { label : "clippy::eq_op" , description : "Checks for equal operands to comparison, logical and\\nbitwise, difference and division binary operators (`==`, `>`, etc., `&&`,\\n`||`, `&`, `|`, `^`, `-` and `/`)." } , LintCompletion { label : "clippy::erasing_op" , description : "Checks for erasing operations, e.g., `x * 0`." } , LintCompletion { label : "clippy::eval_order_dependence" , description : "Checks for a read and a write to the same variable where\\nwhether the read occurs before or after the write depends on the evaluation\\norder of sub-expressions." } , LintCompletion { label : "clippy::excessive_precision" , description : "Checks for float literals with a precision greater\\nthan that supported by the underlying type." } , LintCompletion { label : "clippy::exit" , description : "`exit()` terminates the program and doesn't provide a\\nstack trace." } , LintCompletion { label : "clippy::expect_fun_call" , description : "Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,\\netc., and suggests to use `unwrap_or_else` instead" } , LintCompletion { label : "clippy::expect_used" , description : "Checks for `.expect()` calls on `Option`s and `Result`s." } , LintCompletion { label : "clippy::expl_impl_clone_on_copy" , description : "Checks for explicit `Clone` implementations for `Copy`\\ntypes." } , LintCompletion { label : "clippy::explicit_counter_loop" , description : "Checks `for` loops over slices with an explicit counter\\nand suggests the use of `.enumerate()`." } , LintCompletion { label : "clippy::explicit_deref_methods" , description : "Checks for explicit `deref()` or `deref_mut()` method calls." } , LintCompletion { label : "clippy::explicit_into_iter_loop" , description : "Checks for loops on `y.into_iter()` where `y` will do, and\\nsuggests the latter." } , LintCompletion { label : "clippy::explicit_iter_loop" , description : "Checks for loops on `x.iter()` where `&x` will do, and\\nsuggests the latter." } , LintCompletion { label : "clippy::explicit_write" , description : "Checks for usage of `write!()` / `writeln()!` which can be\\nreplaced with `(e)print!()` / `(e)println!()`" } , LintCompletion { label : "clippy::extend_from_slice" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::extra_unused_lifetimes" , description : "Checks for lifetimes in generics that are never used\\nanywhere else." } , LintCompletion { label : "clippy::fallible_impl_from" , description : "Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`" } , LintCompletion { label : "clippy::filetype_is_file" , description : "Checks for `FileType::is_file()`." } , LintCompletion { label : "clippy::filter_map" , description : "Checks for usage of `_.filter(_).map(_)`,\\n`_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar." } , LintCompletion { label : "clippy::filter_map_next" , description : "Checks for usage of `_.filter_map(_).next()`." } , LintCompletion { label : "clippy::filter_next" , description : "Checks for usage of `_.filter(_).next()`." } , LintCompletion { label : "clippy::find_map" , description : "Checks for usage of `_.find(_).map(_)`." } , LintCompletion { label : "clippy::flat_map_identity" , description : "Checks for usage of `flat_map(|x| x)`." } , LintCompletion { label : "clippy::float_arithmetic" , description : "Checks for float arithmetic." } , LintCompletion { label : "clippy::float_cmp" , description : "Checks for (in-)equality comparisons on floating-point\\nvalues (apart from zero), except in functions called `*eq*` (which probably\\nimplement equality for a type involving floats)." } , LintCompletion { label : "clippy::float_cmp_const" , description : "Checks for (in-)equality comparisons on floating-point\\nvalue and constant, except in functions called `*eq*` (which probably\\nimplement equality for a type involving floats)." } , LintCompletion { label : "clippy::float_equality_without_abs" , description : "Checks for statements of the form `(a - b) < f32::EPSILON` or\\n`(a - b) < f64::EPSILON`. Notes the missing `.abs()`." } , LintCompletion { label : "clippy::fn_address_comparisons" , description : "Checks for comparisons with an address of a function item." } , LintCompletion { label : "clippy::fn_params_excessive_bools" , description : "Checks for excessive use of\\nbools in function definitions." } , LintCompletion { label : "clippy::fn_to_numeric_cast" , description : "Checks for casts of function pointers to something other than usize" } , LintCompletion { label : "clippy::fn_to_numeric_cast_with_truncation" , description : "Checks for casts of a function pointer to a numeric type not wide enough to\\nstore address." } , LintCompletion { label : "clippy::for_kv_map" , description : "Checks for iterating a map (`HashMap` or `BTreeMap`) and\\nignoring either the keys or values." } , LintCompletion { label : "clippy::for_loops_over_fallibles" , description : "Checks for `for` loops over `Option` or `Result` values." } , LintCompletion { label : "clippy::forget_copy" , description : "Checks for calls to `std::mem::forget` with a value that\\nderives the Copy trait" } , LintCompletion { label : "clippy::forget_ref" , description : "Checks for calls to `std::mem::forget` with a reference\\ninstead of an owned value." } , LintCompletion { label : "clippy::future_not_send" , description : "This lint requires Future implementations returned from\\nfunctions and methods to implement the `Send` marker trait. It is mostly\\nused by library authors (public and internal) that target an audience where\\nmultithreaded executors are likely to be used for running these Futures." } , LintCompletion { label : "clippy::get_last_with_len" , description : "Checks for using `x.get(x.len() - 1)` instead of\\n`x.last()`." } , LintCompletion { label : "clippy::get_unwrap" , description : "Checks for use of `.get().unwrap()` (or\\n`.get_mut().unwrap`) on a standard library type which implements `Index`" } , LintCompletion { label : "clippy::identity_op" , description : "Checks for identity operations, e.g., `x + 0`." } , LintCompletion { label : "clippy::if_let_mutex" , description : "Checks for `Mutex::lock` calls in `if let` expression\\nwith lock calls in any of the else blocks." } , LintCompletion { label : "clippy::if_let_redundant_pattern_matching" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::if_let_some_result" , description : "* Checks for unnecessary `ok()` in if let." } , LintCompletion { label : "clippy::if_not_else" , description : "Checks for usage of `!` or `!=` in an if condition with an\\nelse branch." } , LintCompletion { label : "clippy::if_same_then_else" , description : "Checks for `if/else` with the same body as the *then* part\\nand the *else* part." } , LintCompletion { label : "clippy::ifs_same_cond" , description : "Checks for consecutive `if`s with the same condition." } , LintCompletion { label : "clippy::implicit_hasher" , description : "Checks for public `impl` or `fn` missing generalization\\nover different hashers and implicitly defaulting to the default hashing\\nalgorithm (`SipHash`)." } , LintCompletion { label : "clippy::implicit_return" , description : "Checks for missing return statements at the end of a block." } , LintCompletion { label : "clippy::implicit_saturating_sub" , description : "Checks for implicit saturating subtraction." } , LintCompletion { label : "clippy::imprecise_flops" , description : "Looks for floating-point expressions that\\ncan be expressed using built-in methods to improve accuracy\\nat the cost of performance." } , LintCompletion { label : "clippy::inconsistent_digit_grouping" , description : "Warns if an integral or floating-point constant is\\ngrouped inconsistently with underscores." } , LintCompletion { label : "clippy::indexing_slicing" , description : "Checks for usage of indexing or slicing. Arrays are special cases, this lint\\ndoes report on arrays if we can tell that slicing operations are in bounds and does not\\nlint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint." } , LintCompletion { label : "clippy::ineffective_bit_mask" , description : "Checks for bit masks in comparisons which can be removed\\nwithout changing the outcome. The basic structure can be seen in the\\nfollowing table:\\n\\n|Comparison| Bit Op |Example |equals |\\n|----------|---------|-----------|-------|\\n|`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|\\n|`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|" } , LintCompletion { label : "clippy::inefficient_to_string" , description : "Checks for usage of `.to_string()` on an `&&T` where\\n`T` implements `ToString` directly (like `&&str` or `&&String`)." } , LintCompletion { label : "clippy::infallible_destructuring_match" , description : "Checks for matches being used to destructure a single-variant enum\\nor tuple struct where a `let` will suffice." } , LintCompletion { label : "clippy::infinite_iter" , description : "Checks for iteration that is guaranteed to be infinite." } , LintCompletion { label : "clippy::inherent_to_string" , description : "Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`." } , LintCompletion { label : "clippy::inherent_to_string_shadow_display" , description : "Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait." } , LintCompletion { label : "clippy::inline_always" , description : "Checks for items annotated with `#[inline(always)]`,\\nunless the annotated function is empty or simply panics." } , LintCompletion { label : "clippy::inline_asm_x86_att_syntax" , description : "Checks for usage of AT&T x86 assembly syntax." } , LintCompletion { label : "clippy::inline_asm_x86_intel_syntax" , description : "Checks for usage of Intel x86 assembly syntax." } , LintCompletion { label : "clippy::inline_fn_without_body" , description : "Checks for `#[inline]` on trait methods without bodies" } , LintCompletion { label : "clippy::int_plus_one" , description : "Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block" } , LintCompletion { label : "clippy::integer_arithmetic" , description : "Checks for integer arithmetic operations which could overflow or panic.\\n\\nSpecifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable\\nof overflowing according to the [Rust\\nReference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),\\nor which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is\\nattempted." } , LintCompletion { label : "clippy::integer_division" , description : "Checks for division of integers" } , LintCompletion { label : "clippy::into_iter_on_array" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::into_iter_on_ref" , description : "Checks for `into_iter` calls on references which should be replaced by `iter`\\nor `iter_mut`." } , LintCompletion { label : "clippy::invalid_atomic_ordering" , description : "Checks for usage of invalid atomic\\nordering in atomic loads/stores/exchanges/updates and\\nmemory fences." } , LintCompletion { label : "clippy::invalid_ref" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::invalid_regex" , description : "Checks [regex](https://crates.io/crates/regex) creation\\n(with `Regex::new`,`RegexBuilder::new` or `RegexSet::new`) for correct\\nregex syntax." } , LintCompletion { label : "clippy::invalid_upcast_comparisons" , description : "Checks for comparisons where the relation is always either\\ntrue or false, but where one side has been upcast so that the comparison is\\nnecessary. Only integer types are checked." } , LintCompletion { label : "clippy::invisible_characters" , description : "Checks for invisible Unicode characters in the code." } , LintCompletion { label : "clippy::items_after_statements" , description : "Checks for items declared after some statement in a block." } , LintCompletion { label : "clippy::iter_cloned_collect" , description : "Checks for the use of `.cloned().collect()` on slice to\\ncreate a `Vec`." } , LintCompletion { label : "clippy::iter_next_loop" , description : "Checks for loops on `x.next()`." } , LintCompletion { label : "clippy::iter_next_slice" , description : "Checks for usage of `iter().next()` on a Slice or an Array" } , LintCompletion { label : "clippy::iter_nth" , description : "Checks for use of `.iter().nth()` (and the related\\n`.iter_mut().nth()`) on standard library types with O(1) element access." } , LintCompletion { label : "clippy::iter_nth_zero" , description : "Checks for the use of `iter.nth(0)`." } , LintCompletion { label : "clippy::iter_skip_next" , description : "Checks for use of `.skip(x).next()` on iterators." } , LintCompletion { label : "clippy::iterator_step_by_zero" , description : "Checks for calling `.step_by(0)` on iterators which panics." } , LintCompletion { label : "clippy::just_underscores_and_digits" , description : "Checks if you have variables whose name consists of just\\nunderscores and digits." } , LintCompletion { label : "clippy::large_const_arrays" , description : "Checks for large `const` arrays that should\\nbe defined as `static` instead." } , LintCompletion { label : "clippy::large_digit_groups" , description : "Warns if the digits of an integral or floating-point\\nconstant are grouped into groups that\\nare too large." } , LintCompletion { label : "clippy::large_enum_variant" , description : "Checks for large size differences between variants on\\n`enum`s." } , LintCompletion { label : "clippy::large_stack_arrays" , description : "Checks for local arrays that may be too large." } , LintCompletion { label : "clippy::large_types_passed_by_value" , description : "Checks for functions taking arguments by value, where\\nthe argument type is `Copy` and large enough to be worth considering\\npassing by reference. Does not trigger if the function is being exported,\\nbecause that might induce API breakage, if the parameter is declared as mutable,\\nor if the argument is a `self`." } , LintCompletion { label : "clippy::len_without_is_empty" , description : "Checks for items that implement `.len()` but not\\n`.is_empty()`." } , LintCompletion { label : "clippy::len_zero" , description : "Checks for getting the length of something via `.len()`\\njust to compare to zero, and suggests using `.is_empty()` where applicable." } , LintCompletion { label : "clippy::let_and_return" , description : "Checks for `let`-bindings, which are subsequently\\nreturned." } , LintCompletion { label : "clippy::let_underscore_lock" , description : "Checks for `let _ = sync_lock`" } , LintCompletion { label : "clippy::let_underscore_must_use" , description : "Checks for `let _ = <expr>`\\nwhere expr is #[must_use]" } , LintCompletion { label : "clippy::let_unit_value" , description : "Checks for binding a unit value." } , LintCompletion { label : "clippy::linkedlist" , description : "Checks for usage of any `LinkedList`, suggesting to use a\\n`Vec` or a `VecDeque` (formerly called `RingBuf`)." } , LintCompletion { label : "clippy::logic_bug" , description : "Checks for boolean expressions that contain terminals that\\ncan be eliminated." } , LintCompletion { label : "clippy::lossy_float_literal" , description : "Checks for whole number float literals that\\ncannot be represented as the underlying type without loss." } , LintCompletion { label : "clippy::macro_use_imports" , description : "Checks for `#[macro_use] use...`." } , LintCompletion { label : "clippy::main_recursion" , description : "Checks for recursion using the entrypoint." } , LintCompletion { label : "clippy::manual_async_fn" , description : "It checks for manual implementations of `async` functions." } , LintCompletion { label : "clippy::manual_memcpy" , description : "Checks for for-loops that manually copy items between\\nslices that could be optimized by having a memcpy." } , LintCompletion { label : "clippy::manual_non_exhaustive" , description : "Checks for manual implementations of the non-exhaustive pattern." } , LintCompletion { label : "clippy::manual_saturating_arithmetic" , description : "Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`." } , LintCompletion { label : "clippy::manual_strip" , description : "Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using\\nthe pattern's length." } , LintCompletion { label : "clippy::manual_swap" , description : "Checks for manual swapping." } , LintCompletion { label : "clippy::manual_unwrap_or" , description : "Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`." } , LintCompletion { label : "clippy::many_single_char_names" , description : "Checks for too many variables whose name consists of a\\nsingle character." } , LintCompletion { label : "clippy::map_clone" , description : "Checks for usage of `iterator.map(|x| x.clone())` and suggests\\n`iterator.cloned()` instead" } , LintCompletion { label : "clippy::map_entry" , description : "Checks for uses of `contains_key` + `insert` on `HashMap`\\nor `BTreeMap`." } , LintCompletion { label : "clippy::map_err_ignore" , description : "Checks for instances of `map_err(|_| Some::Enum)`" } , LintCompletion { label : "clippy::map_flatten" , description : "Checks for usage of `_.map(_).flatten(_)`," } , LintCompletion { label : "clippy::map_identity" , description : "Checks for instances of `map(f)` where `f` is the identity function." } , LintCompletion { label : "clippy::map_unwrap_or" , description : "Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or\\n`result.map(_).unwrap_or_else(_)`." } , LintCompletion { label : "clippy::match_as_ref" , description : "Checks for match which is used to add a reference to an\\n`Option` value." } , LintCompletion { label : "clippy::match_bool" , description : "Checks for matches where match expression is a `bool`. It\\nsuggests to replace the expression with an `if...else` block." } , LintCompletion { label : "clippy::match_like_matches_macro" , description : "Checks for `match` or `if let` expressions producing a\\n`bool` that could be written using `matches!`" } , LintCompletion { label : "clippy::match_on_vec_items" , description : "Checks for `match vec[idx]` or `match vec[n..m]`." } , LintCompletion { label : "clippy::match_overlapping_arm" , description : "Checks for overlapping match arms." } , LintCompletion { label : "clippy::match_ref_pats" , description : "Checks for matches where all arms match a reference,\\nsuggesting to remove the reference and deref the matched expression\\ninstead. It also checks for `if let &foo = bar` blocks." } , LintCompletion { label : "clippy::match_same_arms" , description : "Checks for `match` with identical arm bodies." } , LintCompletion { label : "clippy::match_single_binding" , description : "Checks for useless match that binds to only one value." } , LintCompletion { label : "clippy::match_wild_err_arm" , description : "Checks for arm which matches all errors with `Err(_)`\\nand take drastic actions like `panic!`." } , LintCompletion { label : "clippy::match_wildcard_for_single_variants" , description : "Checks for wildcard enum matches for a single variant." } , LintCompletion { label : "clippy::maybe_infinite_iter" , description : "Checks for iteration that may be infinite." } , LintCompletion { label : "clippy::mem_discriminant_non_enum" , description : "Checks for calls of `mem::discriminant()` on a non-enum type." } , LintCompletion { label : "clippy::mem_forget" , description : "Checks for usage of `std::mem::forget(t)` where `t` is\\n`Drop`." } , LintCompletion { label : "clippy::mem_replace_option_with_none" , description : "Checks for `mem::replace()` on an `Option` with\\n`None`." } , LintCompletion { label : "clippy::mem_replace_with_default" , description : "Checks for `std::mem::replace` on a value of type\\n`T` with `T::default()`." } , LintCompletion { label : "clippy::mem_replace_with_uninit" , description : "Checks for `mem::replace(&mut _, mem::uninitialized())`\\nand `mem::replace(&mut _, mem::zeroed())`." } , LintCompletion { label : "clippy::min_max" , description : "Checks for expressions where `std::cmp::min` and `max` are\\nused to clamp values, but switched so that the result is constant." } , LintCompletion { label : "clippy::misaligned_transmute" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::mismatched_target_os" , description : "Checks for cfg attributes having operating systems used in target family position." } , LintCompletion { label : "clippy::misrefactored_assign_op" , description : "Checks for `a op= a op b` or `a op= b op a` patterns." } , LintCompletion { label : "clippy::missing_const_for_fn" , description : "Suggests the use of `const` in functions and methods where possible." } , LintCompletion { label : "clippy::missing_docs_in_private_items" , description : "Warns if there is missing doc for any documentable item\\n(public or private)." } , LintCompletion { label : "clippy::missing_errors_doc" , description : "Checks the doc comments of publicly visible functions that\\nreturn a `Result` type and warns if there is no `# Errors` section." } , LintCompletion { label : "clippy::missing_inline_in_public_items" , description : "it lints if an exported function, method, trait method with default impl,\\nor trait method impl is not `#[inline]`." } , LintCompletion { label : "clippy::missing_safety_doc" , description : "Checks for the doc comments of publicly visible\\nunsafe functions and warns if there is no `# Safety` section." } , LintCompletion { label : "clippy::mistyped_literal_suffixes" , description : "Warns for mistyped suffix in literals" } , LintCompletion { label : "clippy::mixed_case_hex_literals" , description : "Warns on hexadecimal literals with mixed-case letter\\ndigits." } , LintCompletion { label : "clippy::module_inception" , description : "Checks for modules that have the same name as their\\nparent module" } , LintCompletion { label : "clippy::module_name_repetitions" , description : "Detects type names that are prefixed or suffixed by the\\ncontaining module's name." } , LintCompletion { label : "clippy::modulo_arithmetic" , description : "Checks for modulo arithmetic." } , LintCompletion { label : "clippy::modulo_one" , description : "Checks for getting the remainder of a division by one." } , LintCompletion { label : "clippy::multiple_crate_versions" , description : "Checks to see if multiple versions of a crate are being\\nused." } , LintCompletion { label : "clippy::multiple_inherent_impl" , description : "Checks for multiple inherent implementations of a struct" } , LintCompletion { label : "clippy::must_use_candidate" , description : "Checks for public functions that have no\\n[`#[must_use]`] attribute, but return something not already marked\\nmust-use, have no mutable arg and mutate no statics.\\n\\n[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute" } , LintCompletion { label : "clippy::must_use_unit" , description : "Checks for a [`#[must_use]`] attribute on\\nunit-returning functions and methods.\\n\\n[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute" } , LintCompletion { label : "clippy::mut_from_ref" , description : "This lint checks for functions that take immutable\\nreferences and return mutable ones." } , LintCompletion { label : "clippy::mut_mut" , description : "Checks for instances of `mut mut` references." } , LintCompletion { label : "clippy::mut_range_bound" , description : "Checks for loops which have a range bound that is a mutable variable" } , LintCompletion { label : "clippy::mutable_key_type" , description : "Checks for sets/maps with mutable key types." } , LintCompletion { label : "clippy::mutex_atomic" , description : "Checks for usages of `Mutex<X>` where an atomic will do." } , LintCompletion { label : "clippy::mutex_integer" , description : "Checks for usages of `Mutex<X>` where `X` is an integral\\ntype." } , LintCompletion { label : "clippy::naive_bytecount" , description : "Checks for naive byte counts" } , LintCompletion { label : "clippy::needless_arbitrary_self_type" , description : "The lint checks for `self` in fn parameters that\\nspecify the `Self`-type explicitly" } , LintCompletion { label : "clippy::needless_bool" , description : "Checks for expressions of the form `if c { true } else {\\nfalse }` (or vice versa) and suggests using the condition directly." } , LintCompletion { label : "clippy::needless_borrow" , description : "Checks for address of operations (`&`) that are going to\\nbe dereferenced immediately by the compiler." } , LintCompletion { label : "clippy::needless_borrowed_reference" , description : "Checks for useless borrowed references." } , LintCompletion { label : "clippy::needless_collect" , description : "Checks for functions collecting an iterator when collect\\nis not needed." } , LintCompletion { label : "clippy::needless_continue" , description : "The lint checks for `if`-statements appearing in loops\\nthat contain a `continue` statement in either their main blocks or their\\n`else`-blocks, when omitting the `else`-block possibly with some\\nrearrangement of code can make the code easier to understand." } , LintCompletion { label : "clippy::needless_doctest_main" , description : "Checks for `fn main() { .. }` in doctests" } , LintCompletion { label : "clippy::needless_lifetimes" , description : "Checks for lifetime annotations which can be removed by\\nrelying on lifetime elision." } , LintCompletion { label : "clippy::needless_pass_by_value" , description : "Checks for functions taking arguments by value, but not\\nconsuming them in its\\nbody." } , LintCompletion { label : "clippy::needless_range_loop" , description : "Checks for looping over the range of `0..len` of some\\ncollection just to get the values by index." } , LintCompletion { label : "clippy::needless_return" , description : "Checks for return statements at the end of a block." } , LintCompletion { label : "clippy::needless_update" , description : "Checks for needlessly including a base struct on update\\nwhen all fields are changed anyway." } , LintCompletion { label : "clippy::neg_cmp_op_on_partial_ord" , description : "Checks for the usage of negated comparison operators on types which only implement\\n`PartialOrd` (e.g., `f64`)." } , LintCompletion { label : "clippy::neg_multiply" , description : "Checks for multiplication by -1 as a form of negation." } , LintCompletion { label : "clippy::never_loop" , description : "Checks for loops that will always `break`, `return` or\\n`continue` an outer loop." } , LintCompletion { label : "clippy::new_ret_no_self" , description : "Checks for `new` not returning a type that contains `Self`." } , LintCompletion { label : "clippy::new_without_default" , description : "Checks for types with a `fn new() -> Self` method and no\\nimplementation of\\n[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)." } , LintCompletion { label : "clippy::no_effect" , description : "Checks for statements which have no effect." } , LintCompletion { label : "clippy::non_ascii_literal" , description : "Checks for non-ASCII characters in string literals." } , LintCompletion { label : "clippy::nonminimal_bool" , description : "Checks for boolean expressions that can be written more\\nconcisely." } , LintCompletion { label : "clippy::nonsensical_open_options" , description : "Checks for duplicate open options as well as combinations\\nthat make no sense." } , LintCompletion { label : "clippy::not_unsafe_ptr_arg_deref" , description : "Checks for public functions that dereference raw pointer\\narguments but are not marked unsafe." } , LintCompletion { label : "clippy::ok_expect" , description : "Checks for usage of `ok().expect(..)`." } , LintCompletion { label : "clippy::op_ref" , description : "Checks for arguments to `==` which have their address\\ntaken to satisfy a bound\\nand suggests to dereference the other argument instead" } , LintCompletion { label : "clippy::option_as_ref_deref" , description : "Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str)." } , LintCompletion { label : "clippy::option_env_unwrap" , description : "Checks for usage of `option_env!(...).unwrap()` and\\nsuggests usage of the `env!` macro." } , LintCompletion { label : "clippy::option_if_let_else" , description : "Lints usage of `if let Some(v) = ... { y } else { x }` which is more\\nidiomatically done with `Option::map_or` (if the else bit is a pure\\nexpression) or `Option::map_or_else` (if the else bit is an impure\\nexpression)." } , LintCompletion { label : "clippy::option_map_or_none" , description : "Checks for usage of `_.map_or(None, _)`." } , LintCompletion { label : "clippy::option_map_unit_fn" , description : "Checks for usage of `option.map(f)` where f is a function\\nor closure that returns the unit type `()`." } , LintCompletion { label : "clippy::option_option" , description : "Checks for use of `Option<Option<_>>` in function signatures and type\\ndefinitions" } , LintCompletion { label : "clippy::or_fun_call" , description : "Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,\\netc., and suggests to use `or_else`, `unwrap_or_else`, etc., or\\n`unwrap_or_default` instead." } , LintCompletion { label : "clippy::out_of_bounds_indexing" , description : "Checks for out of bounds array indexing with a constant\\nindex." } , LintCompletion { label : "clippy::overflow_check_conditional" , description : "Detects classic underflow/overflow checks." } , LintCompletion { label : "clippy::panic" , description : "Checks for usage of `panic!`." } , LintCompletion { label : "clippy::panic_in_result_fn" , description : "Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result." } , LintCompletion { label : "clippy::panic_params" , description : "Checks for missing parameters in `panic!`." } , LintCompletion { label : "clippy::panicking_unwrap" , description : "Checks for calls of `unwrap[_err]()` that will always fail." } , LintCompletion { label : "clippy::partialeq_ne_impl" , description : "Checks for manual re-implementations of `PartialEq::ne`." } , LintCompletion { label : "clippy::path_buf_push_overwrite" , description : "* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)\\ncalls on `PathBuf` that can cause overwrites." } , LintCompletion { label : "clippy::pattern_type_mismatch" , description : "Checks for patterns that aren't exact representations of the types\\nthey are applied to.\\n\\nTo satisfy this lint, you will have to adjust either the expression that is matched\\nagainst or the pattern itself, as well as the bindings that are introduced by the\\nadjusted patterns. For matching you will have to either dereference the expression\\nwith the `*` operator, or amend the patterns to explicitly match against `&<pattern>`\\nor `&mut <pattern>` depending on the reference mutability. For the bindings you need\\nto use the inverse. You can leave them as plain bindings if you wish for the value\\nto be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct\\na reference into the matched structure.\\n\\nIf you are looking for a way to learn about ownership semantics in more detail, it\\nis recommended to look at IDE options available to you to highlight types, lifetimes\\nand reference semantics in your code. The available tooling would expose these things\\nin a general way even outside of the various pattern matching mechanics. Of course\\nthis lint can still be used to highlight areas of interest and ensure a good understanding\\nof ownership semantics." } , LintCompletion { label : "clippy::possible_missing_comma" , description : "Checks for possible missing comma in an array. It lints if\\nan array element is a binary operator expression and it lies on two lines." } , LintCompletion { label : "clippy::precedence" , description : "Checks for operations where precedence may be unclear\\nand suggests to add parentheses. Currently it catches the following:\\n* mixed usage of arithmetic and bit shifting/combining operators without\\nparentheses\\n* a \\\"negative\\\" numeric literal (which is really a unary `-` followed by a\\nnumeric literal)\\n followed by a method call" } , LintCompletion { label : "clippy::print_literal" , description : "This lint warns about the use of literals as `print!`/`println!` args." } , LintCompletion { label : "clippy::print_stdout" , description : "Checks for printing on *stdout*. The purpose of this lint\\nis to catch debugging remnants." } , LintCompletion { label : "clippy::print_with_newline" , description : "This lint warns when you use `print!()` with a format\\nstring that ends in a newline." } , LintCompletion { label : "clippy::println_empty_string" , description : "This lint warns when you use `println!(\\\"\\\")` to\\nprint a newline." } , LintCompletion { label : "clippy::ptr_arg" , description : "This lint checks for function arguments of type `&String`\\nor `&Vec` unless the references are mutable. It will also suggest you\\nreplace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`\\ncalls." } , LintCompletion { label : "clippy::ptr_eq" , description : "Use `std::ptr::eq` when applicable" } , LintCompletion { label : "clippy::ptr_offset_with_cast" , description : "Checks for usage of the `offset` pointer method with a `usize` casted to an\\n`isize`." } , LintCompletion { label : "clippy::pub_enum_variant_names" , description : "Detects public enumeration variants that are\\nprefixed or suffixed by the same characters." } , LintCompletion { label : "clippy::question_mark" , description : "Checks for expressions that could be replaced by the question mark operator." } , LintCompletion { label : "clippy::range_minus_one" , description : "Checks for inclusive ranges where 1 is subtracted from\\nthe upper bound, e.g., `x..=(y-1)`." } , LintCompletion { label : "clippy::range_plus_one" , description : "Checks for exclusive ranges where 1 is added to the\\nupper bound, e.g., `x..(y+1)`." } , LintCompletion { label : "clippy::range_step_by_zero" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::range_zip_with_len" , description : "Checks for zipping a collection with the range of\\n`0.._.len()`." } , LintCompletion { label : "clippy::rc_buffer" , description : "Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`." } , LintCompletion { label : "clippy::redundant_allocation" , description : "Checks for use of redundant allocations anywhere in the code." } , LintCompletion { label : "clippy::redundant_clone" , description : "Checks for a redundant `clone()` (and its relatives) which clones an owned\\nvalue that is going to be dropped without further use." } , LintCompletion { label : "clippy::redundant_closure" , description : "Checks for closures which just call another function where\\nthe function can be called directly. `unsafe` functions or calls where types\\nget adjusted are ignored." } , LintCompletion { label : "clippy::redundant_closure_call" , description : "Detects closures called in the same expression where they\\nare defined." } , LintCompletion { label : "clippy::redundant_closure_for_method_calls" , description : "Checks for closures which only invoke a method on the closure\\nargument and can be replaced by referencing the method directly." } , LintCompletion { label : "clippy::redundant_field_names" , description : "Checks for fields in struct literals where shorthands\\ncould be used." } , LintCompletion { label : "clippy::redundant_pattern" , description : "Checks for patterns in the form `name @ _`." } , LintCompletion { label : "clippy::redundant_pattern_matching" , description : "Lint for redundant pattern matching over `Result` or\\n`Option`" } , LintCompletion { label : "clippy::redundant_pub_crate" , description : "Checks for items declared `pub(crate)` that are not crate visible because they\\nare inside a private module." } , LintCompletion { label : "clippy::redundant_static_lifetimes" , description : "Checks for constants and statics with an explicit `'static` lifetime." } , LintCompletion { label : "clippy::ref_in_deref" , description : "Checks for references in expressions that use\\nauto dereference." } , LintCompletion { label : "clippy::regex_macro" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::repeat_once" , description : "Checks for usage of `.repeat(1)` and suggest the following method for each types.\\n- `.to_string()` for `str`\\n- `.clone()` for `String`\\n- `.to_vec()` for `slice`" } , LintCompletion { label : "clippy::replace_consts" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::rest_pat_in_fully_bound_structs" , description : "Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched." } , LintCompletion { label : "clippy::result_map_or_into_option" , description : "Checks for usage of `_.map_or(None, Some)`." } , LintCompletion { label : "clippy::result_map_unit_fn" , description : "Checks for usage of `result.map(f)` where f is a function\\nor closure that returns the unit type `()`." } , LintCompletion { label : "clippy::result_unit_err" , description : "Checks for public functions that return a `Result`\\nwith an `Err` type of `()`. It suggests using a custom type that\\nimplements [`std::error::Error`]." } , LintCompletion { label : "clippy::reversed_empty_ranges" , description : "Checks for range expressions `x..y` where both `x` and `y`\\nare constant and `x` is greater or equal to `y`." } , LintCompletion { label : "clippy::same_functions_in_if_condition" , description : "Checks for consecutive `if`s with the same function call." } , LintCompletion { label : "clippy::same_item_push" , description : "Checks whether a for loop is being used to push a constant\\nvalue into a Vec." } , LintCompletion { label : "clippy::search_is_some" , description : "Checks for an iterator search (such as `find()`,\\n`position()`, or `rposition()`) followed by a call to `is_some()`." } , LintCompletion { label : "clippy::self_assignment" , description : "Checks for explicit self-assignments." } , LintCompletion { label : "clippy::serde_api_misuse" , description : "Checks for mis-uses of the serde API." } , LintCompletion { label : "clippy::shadow_reuse" , description : "Checks for bindings that shadow other bindings already in\\nscope, while reusing the original value." } , LintCompletion { label : "clippy::shadow_same" , description : "Checks for bindings that shadow other bindings already in\\nscope, while just changing reference level or mutability." } , LintCompletion { label : "clippy::shadow_unrelated" , description : "Checks for bindings that shadow other bindings already in\\nscope, either without a initialization or with one that does not even use\\nthe original value." } , LintCompletion { label : "clippy::short_circuit_statement" , description : "Checks for the use of short circuit boolean conditions as\\na\\nstatement." } , LintCompletion { label : "clippy::should_assert_eq" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::should_implement_trait" , description : "Checks for methods that should live in a trait\\nimplementation of a `std` trait (see [llogiq's blog\\npost](http://llogiq.github.io/2015/07/30/traits.html) for further\\ninformation) instead of an inherent implementation." } , LintCompletion { label : "clippy::similar_names" , description : "Checks for names that are very similar and thus confusing." } , LintCompletion { label : "clippy::single_char_pattern" , description : "Checks for string methods that receive a single-character\\n`str` as an argument, e.g., `_.split(\\\"x\\\")`." } , LintCompletion { label : "clippy::single_char_push_str" , description : "Warns when using `push_str` with a single-character string literal,\\nand `push` with a `char` would work fine." } , LintCompletion { label : "clippy::single_component_path_imports" , description : "Checking for imports with single component use path." } , LintCompletion { label : "clippy::single_match" , description : "Checks for matches with a single arm where an `if let`\\nwill usually suffice." } , LintCompletion { label : "clippy::single_match_else" , description : "Checks for matches with two arms where an `if let else` will\\nusually suffice." } , LintCompletion { label : "clippy::skip_while_next" , description : "Checks for usage of `_.skip_while(condition).next()`." } , LintCompletion { label : "clippy::slow_vector_initialization" , description : "Checks slow zero-filled vector initialization" } , LintCompletion { label : "clippy::stable_sort_primitive" , description : "When sorting primitive values (integers, bools, chars, as well\\nas arrays, slices, and tuples of such items), it is better to\\nuse an unstable sort than a stable sort." } , LintCompletion { label : "clippy::str_to_string" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::string_add" , description : "Checks for all instances of `x + _` where `x` is of type\\n`String`, but only if [`string_add_assign`](#string_add_assign) does *not*\\nmatch." } , LintCompletion { label : "clippy::string_add_assign" , description : "Checks for string appends of the form `x = x + y` (without\\n`let`!)." } , LintCompletion { label : "clippy::string_extend_chars" , description : "Checks for the use of `.extend(s.chars())` where s is a\\n`&str` or `String`." } , LintCompletion { label : "clippy::string_lit_as_bytes" , description : "Checks for the `as_bytes` method called on string literals\\nthat contain only ASCII characters." } , LintCompletion { label : "clippy::string_to_string" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::struct_excessive_bools" , description : "Checks for excessive\\nuse of bools in structs." } , LintCompletion { label : "clippy::suboptimal_flops" , description : "Looks for floating-point expressions that\\ncan be expressed using built-in methods to improve both\\naccuracy and performance." } , LintCompletion { label : "clippy::suspicious_arithmetic_impl" , description : "Lints for suspicious operations in impls of arithmetic operators, e.g.\\nsubtracting elements in an Add impl." } , LintCompletion { label : "clippy::suspicious_assignment_formatting" , description : "Checks for use of the non-existent `=*`, `=!` and `=-`\\noperators." } , LintCompletion { label : "clippy::suspicious_else_formatting" , description : "Checks for formatting of `else`. It lints if the `else`\\nis followed immediately by a newline or the `else` seems to be missing." } , LintCompletion { label : "clippy::suspicious_map" , description : "Checks for calls to `map` followed by a `count`." } , LintCompletion { label : "clippy::suspicious_op_assign_impl" , description : "Lints for suspicious operations in impls of OpAssign, e.g.\\nsubtracting elements in an AddAssign impl." } , LintCompletion { label : "clippy::suspicious_unary_op_formatting" , description : "Checks the formatting of a unary operator on the right hand side\\nof a binary operator. It lints if there is no space between the binary and unary operators,\\nbut there is a space between the unary and its operand." } , LintCompletion { label : "clippy::tabs_in_doc_comments" , description : "Checks doc comments for usage of tab characters." } , LintCompletion { label : "clippy::temporary_assignment" , description : "Checks for construction of a structure or tuple just to\\nassign a value in it." } , LintCompletion { label : "clippy::temporary_cstring_as_ptr" , description : "Checks for getting the inner pointer of a temporary\\n`CString`." } , LintCompletion { label : "clippy::to_digit_is_some" , description : "Checks for `.to_digit(..).is_some()` on `char`s." } , LintCompletion { label : "clippy::to_string_in_display" , description : "Checks for uses of `to_string()` in `Display` traits." } , LintCompletion { label : "clippy::todo" , description : "Checks for usage of `todo!`." } , LintCompletion { label : "clippy::too_many_arguments" , description : "Checks for functions with too many parameters." } , LintCompletion { label : "clippy::too_many_lines" , description : "Checks for functions with a large amount of lines." } , LintCompletion { label : "clippy::toplevel_ref_arg" , description : "Checks for function arguments and let bindings denoted as\\n`ref`." } , LintCompletion { label : "clippy::trait_duplication_in_bounds" , description : "Checks for cases where generics are being used and multiple\\nsyntax specifications for trait bounds are used simultaneously." } , LintCompletion { label : "clippy::transmute_bytes_to_str" , description : "Checks for transmutes from a `&[u8]` to a `&str`." } , LintCompletion { label : "clippy::transmute_float_to_int" , description : "Checks for transmutes from a float to an integer." } , LintCompletion { label : "clippy::transmute_int_to_bool" , description : "Checks for transmutes from an integer to a `bool`." } , LintCompletion { label : "clippy::transmute_int_to_char" , description : "Checks for transmutes from an integer to a `char`." } , LintCompletion { label : "clippy::transmute_int_to_float" , description : "Checks for transmutes from an integer to a float." } , LintCompletion { label : "clippy::transmute_ptr_to_ptr" , description : "Checks for transmutes from a pointer to a pointer, or\\nfrom a reference to a reference." } , LintCompletion { label : "clippy::transmute_ptr_to_ref" , description : "Checks for transmutes from a pointer to a reference." } , LintCompletion { label : "clippy::transmutes_expressible_as_ptr_casts" , description : "Checks for transmutes that could be a pointer cast." } , LintCompletion { label : "clippy::transmuting_null" , description : "Checks for transmute calls which would receive a null pointer." } , LintCompletion { label : "clippy::trivial_regex" , description : "Checks for trivial [regex](https://crates.io/crates/regex)\\ncreation (with `Regex::new`, `RegexBuilder::new` or `RegexSet::new`)." } , LintCompletion { label : "clippy::trivially_copy_pass_by_ref" , description : "Checks for functions taking arguments by reference, where\\nthe argument type is `Copy` and small enough to be more efficient to always\\npass by value." } , LintCompletion { label : "clippy::try_err" , description : "Checks for usages of `Err(x)?`." } , LintCompletion { label : "clippy::type_complexity" , description : "Checks for types used in structs, parameters and `let`\\ndeclarations above a certain complexity threshold." } , LintCompletion { label : "clippy::type_repetition_in_bounds" , description : "This lint warns about unnecessary type repetitions in trait bounds" } , LintCompletion { label : "clippy::unicode_not_nfc" , description : "Checks for string literals that contain Unicode in a form\\nthat is not equal to its\\n[NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms)." } , LintCompletion { label : "clippy::unimplemented" , description : "Checks for usage of `unimplemented!`." } , LintCompletion { label : "clippy::uninit_assumed_init" , description : "Checks for `MaybeUninit::uninit().assume_init()`." } , LintCompletion { label : "clippy::unit_arg" , description : "Checks for passing a unit value as an argument to a function without using a\\nunit literal (`()`)." } , LintCompletion { label : "clippy::unit_cmp" , description : "Checks for comparisons to unit. This includes all binary\\ncomparisons (like `==` and `<`) and asserts." } , LintCompletion { label : "clippy::unit_return_expecting_ord" , description : "Checks for functions that expect closures of type\\nFn(...) -> Ord where the implemented closure returns the unit type.\\nThe lint also suggests to remove the semi-colon at the end of the statement if present." } , LintCompletion { label : "clippy::unknown_clippy_lints" , description : "Checks for `allow`/`warn`/`deny`/`forbid` attributes with scoped clippy\\nlints and if those lints exist in clippy. If there is an uppercase letter in the lint name\\n(not the tool name) and a lowercase version of this lint exists, it will suggest to lowercase\\nthe lint name." } , LintCompletion { label : "clippy::unnecessary_cast" , description : "Checks for casts to the same type, casts of int literals to integer types\\nand casts of float literals to float types." } , LintCompletion { label : "clippy::unnecessary_filter_map" , description : "Checks for `filter_map` calls which could be replaced by `filter` or `map`.\\nMore specifically it checks if the closure provided is only performing one of the\\nfilter or map operations and suggests the appropriate option." } , LintCompletion { label : "clippy::unnecessary_fold" , description : "Checks for using `fold` when a more succinct alternative exists.\\nSpecifically, this checks for `fold`s which could be replaced by `any`, `all`,\\n`sum` or `product`." } , LintCompletion { label : "clippy::unnecessary_lazy_evaluations" , description : "As the counterpart to `or_fun_call`, this lint looks for unnecessary\\nlazily evaluated closures on `Option` and `Result`.\\n\\nThis lint suggests changing the following functions, when eager evaluation results in\\nsimpler code:\\n - `unwrap_or_else` to `unwrap_or`\\n - `and_then` to `and`\\n - `or_else` to `or`\\n - `get_or_insert_with` to `get_or_insert`\\n - `ok_or_else` to `ok_or`" } , LintCompletion { label : "clippy::unnecessary_mut_passed" , description : "Detects passing a mutable reference to a function that only\\nrequires an immutable reference." } , LintCompletion { label : "clippy::unnecessary_operation" , description : "Checks for expression statements that can be reduced to a\\nsub-expression." } , LintCompletion { label : "clippy::unnecessary_sort_by" , description : "Detects uses of `Vec::sort_by` passing in a closure\\nwhich compares the two arguments, either directly or indirectly." } , LintCompletion { label : "clippy::unnecessary_unwrap" , description : "Checks for calls of `unwrap[_err]()` that cannot fail." } , LintCompletion { label : "clippy::unneeded_field_pattern" , description : "Checks for structure field patterns bound to wildcards." } , LintCompletion { label : "clippy::unneeded_wildcard_pattern" , description : "Checks for tuple patterns with a wildcard\\npattern (`_`) is next to a rest pattern (`..`).\\n\\n_NOTE_: While `_, ..` means there is at least one element left, `..`\\nmeans there are 0 or more elements left. This can make a difference\\nwhen refactoring, but shouldn't result in errors in the refactored code,\\nsince the wildcard pattern isn't used anyway." } , LintCompletion { label : "clippy::unnested_or_patterns" , description : "Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and\\nsuggests replacing the pattern with a nested one, `Some(0 | 2)`.\\n\\nAnother way to think of this is that it rewrites patterns in\\n*disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*." } , LintCompletion { label : "clippy::unreachable" , description : "Checks for usage of `unreachable!`." } , LintCompletion { label : "clippy::unreadable_literal" , description : "Warns if a long integral or floating-point constant does\\nnot contain underscores." } , LintCompletion { label : "clippy::unsafe_derive_deserialize" , description : "Checks for deriving `serde::Deserialize` on a type that\\nhas methods using `unsafe`." } , LintCompletion { label : "clippy::unsafe_removed_from_name" , description : "Checks for imports that remove \\\"unsafe\\\" from an item's\\nname." } , LintCompletion { label : "clippy::unsafe_vector_initialization" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::unseparated_literal_suffix" , description : "Warns if literal suffixes are not separated by an\\nunderscore." } , LintCompletion { label : "clippy::unsound_collection_transmute" , description : "Checks for transmutes between collections whose\\ntypes have different ABI, size or alignment." } , LintCompletion { label : "clippy::unstable_as_mut_slice" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::unstable_as_slice" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::unused_collect" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::unused_io_amount" , description : "Checks for unused written/read amount." } , LintCompletion { label : "clippy::unused_label" , description : "Nothing. This lint has been deprecated." } , LintCompletion { label : "clippy::unused_self" , description : "Checks methods that contain a `self` argument but don't use it" } , LintCompletion { label : "clippy::unused_unit" , description : "Checks for unit (`()`) expressions that can be removed." } , LintCompletion { label : "clippy::unwrap_in_result" , description : "Checks for functions of type Result that contain `expect()` or `unwrap()`" } , LintCompletion { label : "clippy::unwrap_used" , description : "Checks for `.unwrap()` calls on `Option`s and on `Result`s." } , LintCompletion { label : "clippy::use_debug" , description : "Checks for use of `Debug` formatting. The purpose of this\\nlint is to catch debugging remnants." } , LintCompletion { label : "clippy::use_self" , description : "Checks for unnecessary repetition of structure name when a\\nreplacement with `Self` is applicable." } , LintCompletion { label : "clippy::used_underscore_binding" , description : "Checks for the use of bindings with a single leading\\nunderscore." } , LintCompletion { label : "clippy::useless_asref" , description : "Checks for usage of `.as_ref()` or `.as_mut()` where the\\ntypes before and after the call are the same." } , LintCompletion { label : "clippy::useless_attribute" , description : "Checks for `extern crate` and `use` items annotated with\\nlint attributes.\\n\\nThis lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,\\n`#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and\\n`#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on\\n`extern crate` items with a `#[macro_use]` attribute." } , LintCompletion { label : "clippy::useless_conversion" , description : "Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls\\nthat useless converts to the same type as caller." } , LintCompletion { label : "clippy::useless_format" , description : "Checks for the use of `format!(\\\"string literal with no\\nargument\\\")` and `format!(\\\"{}\\\", foo)` where `foo` is a string." } , LintCompletion { label : "clippy::useless_let_if_seq" , description : "Checks for variable declarations immediately followed by a\\nconditional affectation." } , LintCompletion { label : "clippy::useless_transmute" , description : "Checks for transmutes to the original type of the object\\nand transmutes that could be a cast." } , LintCompletion { label : "clippy::useless_vec" , description : "Checks for usage of `&vec![..]` when using `&[..]` would\\nbe possible." } , LintCompletion { label : "clippy::vec_box" , description : "Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.\\nCheck the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information." } , LintCompletion { label : "clippy::vec_resize_to_zero" , description : "Finds occurrences of `Vec::resize(0, an_int)`" } , LintCompletion { label : "clippy::verbose_bit_mask" , description : "Checks for bit masks that can be replaced by a call\\nto `trailing_zeros`" } , LintCompletion { label : "clippy::verbose_file_reads" , description : "Checks for use of File::read_to_end and File::read_to_string." } , LintCompletion { label : "clippy::vtable_address_comparisons" , description : "Checks for comparisons with an address of a trait vtable." } , LintCompletion { label : "clippy::while_immutable_condition" , description : "Checks whether variables used within while loop condition\\ncan be (and are) mutated in the body." } , LintCompletion { label : "clippy::while_let_loop" , description : "Detects `loop + match` combinations that are easier\\nwritten as a `while let` loop." } , LintCompletion { label : "clippy::while_let_on_iterator" , description : "Checks for `while let` expressions on iterators." } , LintCompletion { label : "clippy::wildcard_dependencies" , description : "Checks for wildcard dependencies in the `Cargo.toml`." } , LintCompletion { label : "clippy::wildcard_enum_match_arm" , description : "Checks for wildcard enum matches using `_`." } , LintCompletion { label : "clippy::wildcard_imports" , description : "Checks for wildcard imports `use _::*`." } , LintCompletion { label : "clippy::wildcard_in_or_patterns" , description : "Checks for wildcard pattern used with others patterns in same match arm." } , LintCompletion { label : "clippy::write_literal" , description : "This lint warns about the use of literals as `write!`/`writeln!` args." } , LintCompletion { label : "clippy::write_with_newline" , description : "This lint warns when you use `write!()` with a format\\nstring that\\nends in a newline." } , LintCompletion { label : "clippy::writeln_empty_string" , description : "This lint warns when you use `writeln!(buf, \\\"\\\")` to\\nprint a newline." } , LintCompletion { label : "clippy::wrong_pub_self_convention" , description : "This is the same as\\n[`wrong_self_convention`](#wrong_self_convention), but for public items." } , LintCompletion { label : "clippy::wrong_self_convention" , description : "Checks for methods with certain name prefixes and which\\ndoesn't match how self is taken. The actual rules are:\\n\\n|Prefix |`self` taken |\\n|-------|----------------------|\\n|`as_` |`&self` or `&mut self`|\\n|`from_`| none |\\n|`into_`|`self` |\\n|`is_` |`&self` or none |\\n|`to_` |`&self` |" } , LintCompletion { label : "clippy::wrong_transmute" , description : "Checks for transmutes that can't ever be correct on any\\narchitecture." } , LintCompletion { label : "clippy::zero_divided_by_zero" , description : "Checks for `0.0 / 0.0`." } , LintCompletion { label : "clippy::zero_prefixed_literal" , description : "Warns if an integral constant literal starts with `0`." } , LintCompletion { label : "clippy::zero_ptr" , description : "Catch casts from `0` to some pointer type" } , LintCompletion { label : "clippy::zst_offset" , description : "Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to\\nzero-sized types" }] ;
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
new file mode 100644
index 000000000..884711f11
--- /dev/null
+++ b/crates/ide_completion/src/item.rs
@@ -0,0 +1,450 @@
1//! See `CompletionItem` structure.
2
3use std::fmt;
4
5use hir::{Documentation, ModPath, Mutability};
6use ide_db::{
7 helpers::{
8 insert_use::{self, ImportScope, MergeBehavior},
9 mod_path_to_ast, SnippetCap,
10 },
11 SymbolKind,
12};
13use stdx::{impl_from, never};
14use syntax::{algo, TextRange};
15use text_edit::TextEdit;
16
17/// `CompletionItem` describes a single completion variant in the editor pop-up.
18/// It is basically a POD with various properties. To construct a
19/// `CompletionItem`, use `new` method and the `Builder` struct.
20#[derive(Clone)]
21pub struct CompletionItem {
22 /// Used only internally in tests, to check only specific kind of
23 /// completion (postfix, keyword, reference, etc).
24 #[allow(unused)]
25 pub(crate) completion_kind: CompletionKind,
26 /// Label in the completion pop up which identifies completion.
27 label: String,
28 /// Range of identifier that is being completed.
29 ///
30 /// It should be used primarily for UI, but we also use this to convert
31 /// genetic TextEdit into LSP's completion edit (see conv.rs).
32 ///
33 /// `source_range` must contain the completion offset. `insert_text` should
34 /// start with what `source_range` points to, or VSCode will filter out the
35 /// completion silently.
36 source_range: TextRange,
37 /// What happens when user selects this item.
38 ///
39 /// Typically, replaces `source_range` with new identifier.
40 text_edit: TextEdit,
41
42 insert_text_format: InsertTextFormat,
43
44 /// What item (struct, function, etc) are we completing.
45 kind: Option<CompletionItemKind>,
46
47 /// Lookup is used to check if completion item indeed can complete current
48 /// ident.
49 ///
50 /// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it
51 /// contains `bar` sub sequence), and `quux` will rejected.
52 lookup: Option<String>,
53
54 /// Additional info to show in the UI pop up.
55 detail: Option<String>,
56 documentation: Option<Documentation>,
57
58 /// Whether this item is marked as deprecated
59 deprecated: bool,
60
61 /// If completing a function call, ask the editor to show parameter popup
62 /// after completion.
63 trigger_call_info: bool,
64
65 /// Score is useful to pre select or display in better order completion items
66 score: Option<CompletionScore>,
67
68 /// Indicates that a reference or mutable reference to this variable is a
69 /// possible match.
70 ref_match: Option<(Mutability, CompletionScore)>,
71
72 /// The import data to add to completion's edits.
73 import_to_add: Option<ImportEdit>,
74}
75
76// We use custom debug for CompletionItem to make snapshot tests more readable.
77impl fmt::Debug for CompletionItem {
78 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79 let mut s = f.debug_struct("CompletionItem");
80 s.field("label", &self.label()).field("source_range", &self.source_range());
81 if self.text_edit().len() == 1 {
82 let atom = &self.text_edit().iter().next().unwrap();
83 s.field("delete", &atom.delete);
84 s.field("insert", &atom.insert);
85 } else {
86 s.field("text_edit", &self.text_edit);
87 }
88 if let Some(kind) = self.kind().as_ref() {
89 s.field("kind", kind);
90 }
91 if self.lookup() != self.label() {
92 s.field("lookup", &self.lookup());
93 }
94 if let Some(detail) = self.detail() {
95 s.field("detail", &detail);
96 }
97 if let Some(documentation) = self.documentation() {
98 s.field("documentation", &documentation);
99 }
100 if self.deprecated {
101 s.field("deprecated", &true);
102 }
103 if let Some(score) = &self.score {
104 s.field("score", score);
105 }
106 if self.trigger_call_info {
107 s.field("trigger_call_info", &true);
108 }
109 s.finish()
110 }
111}
112
113#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
114pub enum CompletionScore {
115 /// If only type match
116 TypeMatch,
117 /// If type and name match
118 TypeAndNameMatch,
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum CompletionItemKind {
123 SymbolKind(SymbolKind),
124 Attribute,
125 Binding,
126 BuiltinType,
127 Keyword,
128 Method,
129 Snippet,
130 UnresolvedReference,
131}
132
133impl_from!(SymbolKind for CompletionItemKind);
134
135impl CompletionItemKind {
136 #[cfg(test)]
137 pub(crate) fn tag(&self) -> &'static str {
138 match self {
139 CompletionItemKind::SymbolKind(kind) => match kind {
140 SymbolKind::Const => "ct",
141 SymbolKind::ConstParam => "cp",
142 SymbolKind::Enum => "en",
143 SymbolKind::Field => "fd",
144 SymbolKind::Function => "fn",
145 SymbolKind::Impl => "im",
146 SymbolKind::Label => "lb",
147 SymbolKind::LifetimeParam => "lt",
148 SymbolKind::Local => "lc",
149 SymbolKind::Macro => "ma",
150 SymbolKind::Module => "md",
151 SymbolKind::SelfParam => "sp",
152 SymbolKind::Static => "sc",
153 SymbolKind::Struct => "st",
154 SymbolKind::Trait => "tt",
155 SymbolKind::TypeAlias => "ta",
156 SymbolKind::TypeParam => "tp",
157 SymbolKind::Union => "un",
158 SymbolKind::ValueParam => "vp",
159 SymbolKind::Variant => "ev",
160 },
161 CompletionItemKind::Attribute => "at",
162 CompletionItemKind::Binding => "bn",
163 CompletionItemKind::BuiltinType => "bt",
164 CompletionItemKind::Keyword => "kw",
165 CompletionItemKind::Method => "me",
166 CompletionItemKind::Snippet => "sn",
167 CompletionItemKind::UnresolvedReference => "??",
168 }
169 }
170}
171
172#[derive(Debug, PartialEq, Eq, Copy, Clone)]
173pub(crate) enum CompletionKind {
174 /// Parser-based keyword completion.
175 Keyword,
176 /// Your usual "complete all valid identifiers".
177 Reference,
178 /// "Secret sauce" completions.
179 Magic,
180 Snippet,
181 Postfix,
182 BuiltinType,
183 Attribute,
184}
185
186#[derive(Debug, PartialEq, Eq, Copy, Clone)]
187pub enum InsertTextFormat {
188 PlainText,
189 Snippet,
190}
191
192impl CompletionItem {
193 pub(crate) fn new(
194 completion_kind: CompletionKind,
195 source_range: TextRange,
196 label: impl Into<String>,
197 ) -> Builder {
198 let label = label.into();
199 Builder {
200 source_range,
201 completion_kind,
202 label,
203 insert_text: None,
204 insert_text_format: InsertTextFormat::PlainText,
205 detail: None,
206 documentation: None,
207 lookup: None,
208 kind: None,
209 text_edit: None,
210 deprecated: None,
211 trigger_call_info: None,
212 score: None,
213 ref_match: None,
214 import_to_add: None,
215 }
216 }
217
218 /// What user sees in pop-up in the UI.
219 pub fn label(&self) -> &str {
220 &self.label
221 }
222 pub fn source_range(&self) -> TextRange {
223 self.source_range
224 }
225
226 pub fn insert_text_format(&self) -> InsertTextFormat {
227 self.insert_text_format
228 }
229
230 pub fn text_edit(&self) -> &TextEdit {
231 &self.text_edit
232 }
233
234 /// Short one-line additional information, like a type
235 pub fn detail(&self) -> Option<&str> {
236 self.detail.as_deref()
237 }
238 /// A doc-comment
239 pub fn documentation(&self) -> Option<Documentation> {
240 self.documentation.clone()
241 }
242 /// What string is used for filtering.
243 pub fn lookup(&self) -> &str {
244 self.lookup.as_deref().unwrap_or(&self.label)
245 }
246
247 pub fn kind(&self) -> Option<CompletionItemKind> {
248 self.kind
249 }
250
251 pub fn deprecated(&self) -> bool {
252 self.deprecated
253 }
254
255 pub fn score(&self) -> Option<CompletionScore> {
256 self.score
257 }
258
259 pub fn trigger_call_info(&self) -> bool {
260 self.trigger_call_info
261 }
262
263 pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> {
264 self.ref_match
265 }
266
267 pub fn import_to_add(&self) -> Option<&ImportEdit> {
268 self.import_to_add.as_ref()
269 }
270}
271
272/// An extra import to add after the completion is applied.
273#[derive(Debug, Clone)]
274pub struct ImportEdit {
275 pub import_path: ModPath,
276 pub import_scope: ImportScope,
277 pub import_for_trait_assoc_item: bool,
278}
279
280impl ImportEdit {
281 /// Attempts to insert the import to the given scope, producing a text edit.
282 /// May return no edit in edge cases, such as scope already containing the import.
283 pub fn to_text_edit(&self, merge_behavior: Option<MergeBehavior>) -> Option<TextEdit> {
284 let _p = profile::span("ImportEdit::to_text_edit");
285
286 let rewriter = insert_use::insert_use(
287 &self.import_scope,
288 mod_path_to_ast(&self.import_path),
289 merge_behavior,
290 );
291 let old_ast = rewriter.rewrite_root()?;
292 let mut import_insert = TextEdit::builder();
293 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
294
295 Some(import_insert.finish())
296 }
297}
298
299/// A helper to make `CompletionItem`s.
300#[must_use]
301#[derive(Clone)]
302pub(crate) struct Builder {
303 source_range: TextRange,
304 completion_kind: CompletionKind,
305 import_to_add: Option<ImportEdit>,
306 label: String,
307 insert_text: Option<String>,
308 insert_text_format: InsertTextFormat,
309 detail: Option<String>,
310 documentation: Option<Documentation>,
311 lookup: Option<String>,
312 kind: Option<CompletionItemKind>,
313 text_edit: Option<TextEdit>,
314 deprecated: Option<bool>,
315 trigger_call_info: Option<bool>,
316 score: Option<CompletionScore>,
317 ref_match: Option<(Mutability, CompletionScore)>,
318}
319
320impl Builder {
321 pub(crate) fn build(self) -> CompletionItem {
322 let _p = profile::span("item::Builder::build");
323
324 let mut label = self.label;
325 let mut lookup = self.lookup;
326 let mut insert_text = self.insert_text;
327
328 if let Some(import_to_add) = self.import_to_add.as_ref() {
329 if import_to_add.import_for_trait_assoc_item {
330 lookup = lookup.or_else(|| Some(label.clone()));
331 insert_text = insert_text.or_else(|| Some(label.clone()));
332 label = format!("{} ({})", label, import_to_add.import_path);
333 } else {
334 let mut import_path_without_last_segment = import_to_add.import_path.to_owned();
335 let _ = import_path_without_last_segment.pop_segment();
336
337 if !import_path_without_last_segment.segments().is_empty() {
338 lookup = lookup.or_else(|| Some(label.clone()));
339 insert_text = insert_text.or_else(|| Some(label.clone()));
340 label = format!("{}::{}", import_path_without_last_segment, label);
341 }
342 }
343 }
344
345 let text_edit = match self.text_edit {
346 Some(it) => it,
347 None => {
348 TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone()))
349 }
350 };
351
352 CompletionItem {
353 source_range: self.source_range,
354 label,
355 insert_text_format: self.insert_text_format,
356 text_edit,
357 detail: self.detail,
358 documentation: self.documentation,
359 lookup,
360 kind: self.kind,
361 completion_kind: self.completion_kind,
362 deprecated: self.deprecated.unwrap_or(false),
363 trigger_call_info: self.trigger_call_info.unwrap_or(false),
364 score: self.score,
365 ref_match: self.ref_match,
366 import_to_add: self.import_to_add,
367 }
368 }
369 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
370 self.lookup = Some(lookup.into());
371 self
372 }
373 pub(crate) fn label(mut self, label: impl Into<String>) -> Builder {
374 self.label = label.into();
375 self
376 }
377 pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder {
378 self.insert_text = Some(insert_text.into());
379 self
380 }
381 pub(crate) fn insert_snippet(
382 mut self,
383 _cap: SnippetCap,
384 snippet: impl Into<String>,
385 ) -> Builder {
386 self.insert_text_format = InsertTextFormat::Snippet;
387 self.insert_text(snippet)
388 }
389 pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder {
390 self.kind = Some(kind.into());
391 self
392 }
393 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder {
394 self.text_edit = Some(edit);
395 self
396 }
397 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
398 self.insert_text_format = InsertTextFormat::Snippet;
399 self.text_edit(edit)
400 }
401 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder {
402 self.set_detail(Some(detail))
403 }
404 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
405 self.detail = detail.map(Into::into);
406 if let Some(detail) = &self.detail {
407 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) {
408 self.detail = Some(detail.splitn(2, '\n').next().unwrap().to_string());
409 }
410 }
411 self
412 }
413 #[allow(unused)]
414 pub(crate) fn documentation(self, docs: Documentation) -> Builder {
415 self.set_documentation(Some(docs))
416 }
417 pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder {
418 self.documentation = docs.map(Into::into);
419 self
420 }
421 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder {
422 self.deprecated = Some(deprecated);
423 self
424 }
425 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder {
426 self.score = Some(score);
427 self
428 }
429 pub(crate) fn trigger_call_info(mut self) -> Builder {
430 self.trigger_call_info = Some(true);
431 self
432 }
433 pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder {
434 self.import_to_add = import_to_add;
435 self
436 }
437 pub(crate) fn set_ref_match(
438 mut self,
439 ref_match: Option<(Mutability, CompletionScore)>,
440 ) -> Builder {
441 self.ref_match = ref_match;
442 self
443 }
444}
445
446impl<'a> Into<CompletionItem> for Builder {
447 fn into(self) -> CompletionItem {
448 self.build()
449 }
450}
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
new file mode 100644
index 000000000..db8bfbbc3
--- /dev/null
+++ b/crates/ide_completion/src/lib.rs
@@ -0,0 +1,275 @@
1//! `completions` crate provides utilities for generating completions of user input.
2
3mod config;
4mod item;
5mod context;
6mod patterns;
7mod generated_lint_completions;
8#[cfg(test)]
9mod test_utils;
10mod render;
11
12mod completions;
13
14use completions::flyimport::position_for_import;
15use ide_db::{
16 base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase,
17};
18use text_edit::TextEdit;
19
20use crate::{completions::Completions, context::CompletionContext, item::CompletionKind};
21
22pub use crate::{
23 config::CompletionConfig,
24 item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat},
25};
26
27//FIXME: split the following feature into fine-grained features.
28
29// Feature: Magic Completions
30//
31// In addition to usual reference completion, rust-analyzer provides some ✨magic✨
32// completions as well:
33//
34// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
35// is placed at the appropriate position. Even though `if` is easy to type, you
36// still want to complete it, to get ` { }` for free! `return` is inserted with a
37// space or `;` depending on the return type of the function.
38//
39// When completing a function call, `()` are automatically inserted. If a function
40// takes arguments, the cursor is positioned inside the parenthesis.
41//
42// There are postfix completions, which can be triggered by typing something like
43// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
44//
45// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
46// - `expr.match` -> `match expr {}`
47// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
48// - `expr.ref` -> `&expr`
49// - `expr.refm` -> `&mut expr`
50// - `expr.let` -> `let $0 = expr;`
51// - `expr.letm` -> `let mut $0 = expr;`
52// - `expr.not` -> `!expr`
53// - `expr.dbg` -> `dbg!(expr)`
54// - `expr.dbgr` -> `dbg!(&expr)`
55// - `expr.call` -> `(expr)`
56//
57// There also snippet completions:
58//
59// .Expressions
60// - `pd` -> `eprintln!(" = {:?}", );`
61// - `ppd` -> `eprintln!(" = {:#?}", );`
62//
63// .Items
64// - `tfn` -> `#[test] fn feature(){}`
65// - `tmod` ->
66// ```rust
67// #[cfg(test)]
68// mod tests {
69// use super::*;
70//
71// #[test]
72// fn test_name() {}
73// }
74// ```
75//
76// And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities.
77// Those are the additional completion options with automatic `use` import and options from all project importable items,
78// fuzzy matched agains the completion imput.
79
80/// Main entry point for completion. We run completion as a two-phase process.
81///
82/// First, we look at the position and collect a so-called `CompletionContext.
83/// This is a somewhat messy process, because, during completion, syntax tree is
84/// incomplete and can look really weird.
85///
86/// Once the context is collected, we run a series of completion routines which
87/// look at the context and produce completion items. One subtlety about this
88/// phase is that completion engine should not filter by the substring which is
89/// already present, it should give all possible variants for the identifier at
90/// the caret. In other words, for
91///
92/// ```no_run
93/// fn f() {
94/// let foo = 92;
95/// let _ = bar$0
96/// }
97/// ```
98///
99/// `foo` *should* be present among the completion variants. Filtering by
100/// identifier prefix/fuzzy match should be done higher in the stack, together
101/// with ordering of completions (currently this is done by the client).
102pub fn completions(
103 db: &RootDatabase,
104 config: &CompletionConfig,
105 position: FilePosition,
106) -> Option<Completions> {
107 let ctx = CompletionContext::new(db, position, config)?;
108
109 if ctx.no_completion_required() {
110 // No work required here.
111 return None;
112 }
113
114 let mut acc = Completions::default();
115 completions::attribute::complete_attribute(&mut acc, &ctx);
116 completions::fn_param::complete_fn_param(&mut acc, &ctx);
117 completions::keyword::complete_expr_keyword(&mut acc, &ctx);
118 completions::keyword::complete_use_tree_keyword(&mut acc, &ctx);
119 completions::snippet::complete_expr_snippet(&mut acc, &ctx);
120 completions::snippet::complete_item_snippet(&mut acc, &ctx);
121 completions::qualified_path::complete_qualified_path(&mut acc, &ctx);
122 completions::unqualified_path::complete_unqualified_path(&mut acc, &ctx);
123 completions::dot::complete_dot(&mut acc, &ctx);
124 completions::record::complete_record(&mut acc, &ctx);
125 completions::pattern::complete_pattern(&mut acc, &ctx);
126 completions::postfix::complete_postfix(&mut acc, &ctx);
127 completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
128 completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
129 completions::mod_::complete_mod(&mut acc, &ctx);
130 completions::flyimport::import_on_the_fly(&mut acc, &ctx);
131
132 Some(acc)
133}
134
135/// Resolves additional completion data at the position given.
136pub fn resolve_completion_edits(
137 db: &RootDatabase,
138 config: &CompletionConfig,
139 position: FilePosition,
140 full_import_path: &str,
141 imported_name: String,
142 import_for_trait_assoc_item: bool,
143) -> Option<Vec<TextEdit>> {
144 let ctx = CompletionContext::new(db, position, config)?;
145 let position_for_import = position_for_import(&ctx, None)?;
146 let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
147
148 let current_module = ctx.sema.scope(position_for_import).module()?;
149 let current_crate = current_module.krate();
150
151 let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name)
152 .filter_map(|candidate| {
153 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
154 current_module.find_use_path(db, item)
155 })
156 .find(|mod_path| mod_path.to_string() == full_import_path)?;
157
158 ImportEdit { import_path, import_scope, import_for_trait_assoc_item }
159 .to_text_edit(config.insert_use.merge)
160 .map(|edit| vec![edit])
161}
162
163#[cfg(test)]
164mod tests {
165 use crate::test_utils::{self, TEST_CONFIG};
166
167 struct DetailAndDocumentation<'a> {
168 detail: &'a str,
169 documentation: &'a str,
170 }
171
172 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
173 let (db, position) = test_utils::position(ra_fixture);
174 let config = TEST_CONFIG;
175 let completions: Vec<_> = crate::completions(&db, &config, position).unwrap().into();
176 for item in completions {
177 if item.detail() == Some(expected.detail) {
178 let opt = item.documentation();
179 let doc = opt.as_ref().map(|it| it.as_str());
180 assert_eq!(doc, Some(expected.documentation));
181 return;
182 }
183 }
184 panic!("completion detail not found: {}", expected.detail)
185 }
186
187 fn check_no_completion(ra_fixture: &str) {
188 let (db, position) = test_utils::position(ra_fixture);
189 let config = TEST_CONFIG;
190
191 let completions: Option<Vec<String>> = crate::completions(&db, &config, position)
192 .and_then(|completions| {
193 let completions: Vec<_> = completions.into();
194 if completions.is_empty() {
195 None
196 } else {
197 Some(completions)
198 }
199 })
200 .map(|completions| {
201 completions.into_iter().map(|completion| format!("{:?}", completion)).collect()
202 });
203
204 // `assert_eq` instead of `assert!(completions.is_none())` to get the list of completions if test will panic.
205 assert_eq!(completions, None, "Completions were generated, but weren't expected");
206 }
207
208 #[test]
209 fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
210 check_detail_and_documentation(
211 r#"
212macro_rules! bar {
213 () => {
214 struct Bar;
215 impl Bar {
216 #[doc = "Do the foo"]
217 fn foo(&self) {}
218 }
219 }
220}
221
222bar!();
223
224fn foo() {
225 let bar = Bar;
226 bar.fo$0;
227}
228"#,
229 DetailAndDocumentation { detail: "-> ()", documentation: "Do the foo" },
230 );
231 }
232
233 #[test]
234 fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
235 check_detail_and_documentation(
236 r#"
237macro_rules! bar {
238 () => {
239 struct Bar;
240 impl Bar {
241 /// Do the foo
242 fn foo(&self) {}
243 }
244 }
245}
246
247bar!();
248
249fn foo() {
250 let bar = Bar;
251 bar.fo$0;
252}
253"#,
254 DetailAndDocumentation { detail: "-> ()", documentation: " Do the foo" },
255 );
256 }
257
258 #[test]
259 fn test_no_completions_required() {
260 // There must be no hint for 'in' keyword.
261 check_no_completion(r#"fn foo() { for i i$0 }"#);
262 // After 'in' keyword hints may be spawned.
263 check_detail_and_documentation(
264 r#"
265/// Do the foo
266fn foo() -> &'static str { "foo" }
267
268fn bar() {
269 for c in fo$0
270}
271"#,
272 DetailAndDocumentation { detail: "-> &str", documentation: "Do the foo" },
273 );
274 }
275}
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
new file mode 100644
index 000000000..f3ce91dd1
--- /dev/null
+++ b/crates/ide_completion/src/patterns.rs
@@ -0,0 +1,249 @@
1//! Patterns telling us certain facts about current syntax element, they are used in completion context
2
3use syntax::{
4 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken, T,
9};
10
11#[cfg(test)]
12use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
13
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool {
15 not_same_range_ancestor(element)
16 .filter(|it| it.kind() == ASSOC_ITEM_LIST)
17 .and_then(|it| it.parent())
18 .filter(|it| it.kind() == TRAIT)
19 .is_some()
20}
21#[test]
22fn test_has_trait_parent() {
23 check_pattern_is_applicable(r"trait A { f$0 }", has_trait_parent);
24}
25
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
27 not_same_range_ancestor(element)
28 .filter(|it| it.kind() == ASSOC_ITEM_LIST)
29 .and_then(|it| it.parent())
30 .filter(|it| it.kind() == IMPL)
31 .is_some()
32}
33#[test]
34fn test_has_impl_parent() {
35 check_pattern_is_applicable(r"impl A { f$0 }", has_impl_parent);
36}
37
38pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
39 // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
40 // where we only check the first parent with different text range.
41 element
42 .ancestors()
43 .find(|it| it.kind() == IMPL)
44 .map(|it| ast::Impl::cast(it).unwrap())
45 .map(|it| it.trait_().is_some())
46 .unwrap_or(false)
47}
48#[test]
49fn test_inside_impl_trait_block() {
50 check_pattern_is_applicable(r"impl Foo for Bar { f$0 }", inside_impl_trait_block);
51 check_pattern_is_applicable(r"impl Foo for Bar { fn f$0 }", inside_impl_trait_block);
52 check_pattern_is_not_applicable(r"impl A { f$0 }", inside_impl_trait_block);
53 check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block);
54}
55
56pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool {
57 not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some()
58}
59#[test]
60fn test_has_field_list_parent() {
61 check_pattern_is_applicable(r"struct Foo { f$0 }", has_field_list_parent);
62 check_pattern_is_applicable(r"struct Foo { f$0 pub f: i32}", has_field_list_parent);
63}
64
65pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
66 not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
67}
68#[test]
69fn test_has_block_expr_parent() {
70 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f$0 }", has_block_expr_parent);
71}
72
73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
74 element.ancestors().find(|it| it.kind() == IDENT_PAT).is_some()
75}
76#[test]
77fn test_has_bind_pat_parent() {
78 check_pattern_is_applicable(r"fn my_fn(m$0) {}", has_bind_pat_parent);
79 check_pattern_is_applicable(r"fn my_fn() { let m$0 }", has_bind_pat_parent);
80}
81
82pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
83 not_same_range_ancestor(element)
84 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
85 .is_some()
86}
87#[test]
88fn test_has_ref_parent() {
89 check_pattern_is_applicable(r"fn my_fn(&m$0) {}", has_ref_parent);
90 check_pattern_is_applicable(r"fn my() { let &m$0 }", has_ref_parent);
91}
92
93pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
94 let ancestor = not_same_range_ancestor(element);
95 if !ancestor.is_some() {
96 return true;
97 }
98 ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some()
99}
100#[test]
101fn test_has_item_list_or_source_file_parent() {
102 check_pattern_is_applicable(r"i$0", has_item_list_or_source_file_parent);
103 check_pattern_is_applicable(r"mod foo { f$0 }", has_item_list_or_source_file_parent);
104}
105
106pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
107 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
108 && previous_sibling_or_ancestor_sibling(element)
109 .and_then(|it| it.into_token())
110 .filter(|it| it.kind() == FAT_ARROW)
111 .is_some()
112}
113#[test]
114fn test_is_match_arm() {
115 check_pattern_is_applicable(r"fn my_fn() { match () { () => m$0 } }", is_match_arm);
116}
117
118pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
119 element
120 .into_token()
121 .and_then(|it| previous_non_trivia_token(it))
122 .filter(|it| it.kind() == T![unsafe])
123 .is_some()
124}
125#[test]
126fn test_unsafe_is_prev() {
127 check_pattern_is_applicable(r"unsafe i$0", unsafe_is_prev);
128}
129
130pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
131 element
132 .into_token()
133 .and_then(|it| previous_non_trivia_token(it))
134 .filter(|it| it.kind() == T![if])
135 .is_some()
136}
137
138pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool {
139 element
140 .into_token()
141 .and_then(|it| previous_non_trivia_token(it))
142 .filter(|it| it.kind() == T![fn])
143 .is_some()
144}
145#[test]
146fn test_fn_is_prev() {
147 check_pattern_is_applicable(r"fn l$0", fn_is_prev);
148}
149
150/// Check if the token previous to the previous one is `for`.
151/// For example, `for _ i$0` => true.
152pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool {
153 element
154 .into_token()
155 .and_then(|it| previous_non_trivia_token(it))
156 .and_then(|it| previous_non_trivia_token(it))
157 .filter(|it| it.kind() == T![for])
158 .is_some()
159}
160#[test]
161fn test_for_is_prev2() {
162 check_pattern_is_applicable(r"for i i$0", for_is_prev2);
163}
164
165#[test]
166fn test_if_is_prev() {
167 check_pattern_is_applicable(r"if l$0", if_is_prev);
168}
169
170pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
171 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT).is_some()
172}
173#[test]
174fn test_has_trait_as_prev_sibling() {
175 check_pattern_is_applicable(r"trait A w$0 {}", has_trait_as_prev_sibling);
176}
177
178pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
179 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL).is_some()
180}
181#[test]
182fn test_has_impl_as_prev_sibling() {
183 check_pattern_is_applicable(r"impl A w$0 {}", has_impl_as_prev_sibling);
184}
185
186pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
187 let leaf = match element {
188 NodeOrToken::Node(node) => node,
189 NodeOrToken::Token(token) => token.parent(),
190 };
191 for node in leaf.ancestors() {
192 if node.kind() == FN || node.kind() == CLOSURE_EXPR {
193 break;
194 }
195 let loop_body = match_ast! {
196 match node {
197 ast::ForExpr(it) => it.loop_body(),
198 ast::WhileExpr(it) => it.loop_body(),
199 ast::LoopExpr(it) => it.loop_body(),
200 _ => None,
201 }
202 };
203 if let Some(body) = loop_body {
204 if body.syntax().text_range().contains_range(leaf.text_range()) {
205 return true;
206 }
207 }
208 }
209 false
210}
211
212fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
213 element
214 .ancestors()
215 .take_while(|it| it.text_range() == element.text_range())
216 .last()
217 .and_then(|it| it.parent())
218}
219
220fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
221 let mut token = token.prev_token();
222 while let Some(inner) = token.clone() {
223 if !inner.kind().is_trivia() {
224 return Some(inner);
225 } else {
226 token = inner.prev_token();
227 }
228 }
229 None
230}
231
232fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> {
233 let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev);
234 if let Some(sibling) = token_sibling {
235 Some(sibling)
236 } else {
237 // if not trying to find first ancestor which has such a sibling
238 let node = match element {
239 NodeOrToken::Node(node) => node,
240 NodeOrToken::Token(token) => token.parent(),
241 };
242 let range = node.text_range();
243 let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?;
244 let prev_sibling_node = top_node.ancestors().find(|it| {
245 non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some()
246 })?;
247 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
248 }
249}
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
new file mode 100644
index 000000000..eddaaa6f3
--- /dev/null
+++ b/crates/ide_completion/src/render.rs
@@ -0,0 +1,945 @@
1//! `render` module provides utilities for rendering completion suggestions
2//! into code pieces that will be presented to user.
3
4pub(crate) mod macro_;
5pub(crate) mod function;
6pub(crate) mod enum_variant;
7pub(crate) mod const_;
8pub(crate) mod pattern;
9pub(crate) mod type_alias;
10
11mod builder_ext;
12
13use hir::{
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
15};
16use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind};
17use syntax::TextRange;
18use test_utils::mark;
19
20use crate::{
21 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
22 CompletionScore,
23};
24
25use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
26
27pub(crate) fn render_field<'a>(
28 ctx: RenderContext<'a>,
29 field: hir::Field,
30 ty: &Type,
31) -> CompletionItem {
32 Render::new(ctx).add_field(field, ty)
33}
34
35pub(crate) fn render_tuple_field<'a>(
36 ctx: RenderContext<'a>,
37 field: usize,
38 ty: &Type,
39) -> CompletionItem {
40 Render::new(ctx).add_tuple_field(field, ty)
41}
42
43pub(crate) fn render_resolution<'a>(
44 ctx: RenderContext<'a>,
45 local_name: String,
46 resolution: &ScopeDef,
47) -> Option<CompletionItem> {
48 Render::new(ctx).render_resolution(local_name, None, resolution)
49}
50
51pub(crate) fn render_resolution_with_import<'a>(
52 ctx: RenderContext<'a>,
53 import_edit: ImportEdit,
54 resolution: &ScopeDef,
55) -> Option<CompletionItem> {
56 let local_name = match resolution {
57 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(),
58 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(),
59 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(),
60 _ => import_edit.import_path.segments().last()?.to_string(),
61 };
62 Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| {
63 item.completion_kind = CompletionKind::Magic;
64 item
65 })
66}
67
68/// Interface for data and methods required for items rendering.
69#[derive(Debug)]
70pub(crate) struct RenderContext<'a> {
71 completion: &'a CompletionContext<'a>,
72}
73
74impl<'a> RenderContext<'a> {
75 pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> {
76 RenderContext { completion }
77 }
78
79 fn snippet_cap(&self) -> Option<SnippetCap> {
80 self.completion.config.snippet_cap.clone()
81 }
82
83 fn db(&self) -> &'a RootDatabase {
84 &self.completion.db
85 }
86
87 fn source_range(&self) -> TextRange {
88 self.completion.source_range()
89 }
90
91 fn is_deprecated(&self, node: impl HasAttrs) -> bool {
92 let attrs = node.attrs(self.db());
93 attrs.by_key("deprecated").exists() || attrs.by_key("rustc_deprecated").exists()
94 }
95
96 fn is_deprecated_assoc_item(&self, as_assoc_item: impl AsAssocItem) -> bool {
97 let db = self.db();
98 let assoc = match as_assoc_item.as_assoc_item(db) {
99 Some(assoc) => assoc,
100 None => return false,
101 };
102
103 let is_assoc_deprecated = match assoc {
104 hir::AssocItem::Function(it) => self.is_deprecated(it),
105 hir::AssocItem::Const(it) => self.is_deprecated(it),
106 hir::AssocItem::TypeAlias(it) => self.is_deprecated(it),
107 };
108 is_assoc_deprecated
109 || assoc.containing_trait(db).map(|trait_| self.is_deprecated(trait_)).unwrap_or(false)
110 }
111
112 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> {
113 node.docs(self.db())
114 }
115
116 fn active_name_and_type(&self) -> Option<(String, Type)> {
117 if let Some(record_field) = &self.completion.record_field_syntax {
118 mark::hit!(record_field_type_match);
119 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?;
120 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db())))
121 } else if let Some(active_parameter) = &self.completion.active_parameter {
122 mark::hit!(active_param_type_match);
123 Some((active_parameter.name.clone(), active_parameter.ty.clone()))
124 } else {
125 None
126 }
127 }
128}
129
130/// Generic renderer for completion items.
131#[derive(Debug)]
132struct Render<'a> {
133 ctx: RenderContext<'a>,
134}
135
136impl<'a> Render<'a> {
137 fn new(ctx: RenderContext<'a>) -> Render<'a> {
138 Render { ctx }
139 }
140
141 fn add_field(&mut self, field: hir::Field, ty: &Type) -> CompletionItem {
142 let is_deprecated = self.ctx.is_deprecated(field);
143 let name = field.name(self.ctx.db());
144 let mut item = CompletionItem::new(
145 CompletionKind::Reference,
146 self.ctx.source_range(),
147 name.to_string(),
148 )
149 .kind(SymbolKind::Field)
150 .detail(ty.display(self.ctx.db()).to_string())
151 .set_documentation(field.docs(self.ctx.db()))
152 .set_deprecated(is_deprecated);
153
154 if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) {
155 item = item.set_score(score);
156 }
157
158 item.build()
159 }
160
161 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem {
162 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string())
163 .kind(SymbolKind::Field)
164 .detail(ty.display(self.ctx.db()).to_string())
165 .build()
166 }
167
168 fn render_resolution(
169 self,
170 local_name: String,
171 import_to_add: Option<ImportEdit>,
172 resolution: &ScopeDef,
173 ) -> Option<CompletionItem> {
174 let _p = profile::span("render_resolution");
175 use hir::ModuleDef::*;
176
177 let completion_kind = match resolution {
178 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
179 _ => CompletionKind::Reference,
180 };
181
182 let kind = match resolution {
183 ScopeDef::ModuleDef(Function(func)) => {
184 return render_fn(self.ctx, import_to_add, Some(local_name), *func);
185 }
186 ScopeDef::ModuleDef(Variant(_))
187 if self.ctx.completion.is_pat_binding_or_const
188 | self.ctx.completion.is_irrefutable_pat_binding =>
189 {
190 CompletionItemKind::SymbolKind(SymbolKind::Variant)
191 }
192 ScopeDef::ModuleDef(Variant(var)) => {
193 let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None);
194 return Some(item);
195 }
196 ScopeDef::MacroDef(mac) => {
197 let item = render_macro(self.ctx, import_to_add, local_name, *mac);
198 return item;
199 }
200
201 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),
202 ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {
203 hir::Adt::Struct(_) => SymbolKind::Struct,
204 hir::Adt::Union(_) => SymbolKind::Union,
205 hir::Adt::Enum(_) => SymbolKind::Enum,
206 }),
207 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const),
208 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static),
209 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait),
210 ScopeDef::ModuleDef(TypeAlias(..)) => {
211 CompletionItemKind::SymbolKind(SymbolKind::TypeAlias)
212 }
213 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
214 ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {
215 hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam,
216 hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam,
217 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,
218 }),
219 ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
220 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {
221 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
222 }
223 ScopeDef::Unknown => {
224 let item = CompletionItem::new(
225 CompletionKind::Reference,
226 self.ctx.source_range(),
227 local_name,
228 )
229 .kind(CompletionItemKind::UnresolvedReference)
230 .add_import(import_to_add)
231 .build();
232 return Some(item);
233 }
234 };
235
236 let mut item =
237 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone());
238 if let ScopeDef::Local(local) = resolution {
239 let ty = local.ty(self.ctx.db());
240 if !ty.is_unknown() {
241 item = item.detail(ty.display(self.ctx.db()).to_string());
242 }
243 };
244
245 let mut ref_match = None;
246 if let ScopeDef::Local(local) = resolution {
247 if let Some((active_name, active_type)) = self.ctx.active_name_and_type() {
248 let ty = local.ty(self.ctx.db());
249 if let Some(score) =
250 compute_score_from_active(&active_type, &active_name, &ty, &local_name)
251 {
252 item = item.set_score(score);
253 }
254 ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
255 }
256 }
257
258 // Add `<>` for generic types
259 if self.ctx.completion.is_path_type
260 && !self.ctx.completion.has_type_args
261 && self.ctx.completion.config.add_call_parenthesis
262 {
263 if let Some(cap) = self.ctx.snippet_cap() {
264 let has_non_default_type_params = match resolution {
265 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(self.ctx.db()),
266 ScopeDef::ModuleDef(TypeAlias(it)) => {
267 it.has_non_default_type_params(self.ctx.db())
268 }
269 _ => false,
270 };
271 if has_non_default_type_params {
272 mark::hit!(inserts_angle_brackets_for_generics);
273 item = item
274 .lookup_by(local_name.clone())
275 .label(format!("{}<…>", local_name))
276 .insert_snippet(cap, format!("{}<$0>", local_name));
277 }
278 }
279 }
280
281 Some(
282 item.kind(kind)
283 .add_import(import_to_add)
284 .set_ref_match(ref_match)
285 .set_documentation(self.docs(resolution))
286 .set_deprecated(self.is_deprecated(resolution))
287 .build(),
288 )
289 }
290
291 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
292 use hir::ModuleDef::*;
293 match resolution {
294 ScopeDef::ModuleDef(Module(it)) => it.docs(self.ctx.db()),
295 ScopeDef::ModuleDef(Adt(it)) => it.docs(self.ctx.db()),
296 ScopeDef::ModuleDef(Variant(it)) => it.docs(self.ctx.db()),
297 ScopeDef::ModuleDef(Const(it)) => it.docs(self.ctx.db()),
298 ScopeDef::ModuleDef(Static(it)) => it.docs(self.ctx.db()),
299 ScopeDef::ModuleDef(Trait(it)) => it.docs(self.ctx.db()),
300 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(self.ctx.db()),
301 _ => None,
302 }
303 }
304
305 fn is_deprecated(&self, resolution: &ScopeDef) -> bool {
306 match resolution {
307 ScopeDef::ModuleDef(it) => self.ctx.is_deprecated_assoc_item(*it),
308 ScopeDef::MacroDef(it) => self.ctx.is_deprecated(*it),
309 ScopeDef::GenericParam(it) => self.ctx.is_deprecated(*it),
310 ScopeDef::AdtSelfType(it) => self.ctx.is_deprecated(*it),
311 _ => false,
312 }
313 }
314}
315
316fn compute_score_from_active(
317 active_type: &Type,
318 active_name: &str,
319 ty: &Type,
320 name: &str,
321) -> Option<CompletionScore> {
322 // Compute score
323 // For the same type
324 if active_type != ty {
325 return None;
326 }
327
328 let mut res = CompletionScore::TypeMatch;
329
330 // If same type + same name then go top position
331 if active_name == name {
332 res = CompletionScore::TypeAndNameMatch
333 }
334
335 Some(res)
336}
337fn refed_type_matches(
338 active_type: &Type,
339 active_name: &str,
340 ty: &Type,
341 name: &str,
342) -> Option<(Mutability, CompletionScore)> {
343 let derefed_active = active_type.remove_ref()?;
344 let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?;
345 Some((
346 if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared },
347 score,
348 ))
349}
350
351fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> {
352 let (active_name, active_type) = ctx.active_name_and_type()?;
353 compute_score_from_active(&active_type, &active_name, ty, name)
354}
355
356#[cfg(test)]
357mod tests {
358 use std::cmp::Reverse;
359
360 use expect_test::{expect, Expect};
361 use test_utils::mark;
362
363 use crate::{
364 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
365 CompletionKind, CompletionScore,
366 };
367
368 fn check(ra_fixture: &str, expect: Expect) {
369 let actual = do_completion(ra_fixture, CompletionKind::Reference);
370 expect.assert_debug_eq(&actual);
371 }
372
373 fn check_scores(ra_fixture: &str, expect: Expect) {
374 fn display_score(score: Option<CompletionScore>) -> &'static str {
375 match score {
376 Some(CompletionScore::TypeMatch) => "[type]",
377 Some(CompletionScore::TypeAndNameMatch) => "[type+name]",
378 None => "[]".into(),
379 }
380 }
381
382 let mut completions = get_all_items(TEST_CONFIG, ra_fixture);
383 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
384 let actual = completions
385 .into_iter()
386 .filter(|it| it.completion_kind == CompletionKind::Reference)
387 .map(|it| {
388 let tag = it.kind().unwrap().tag();
389 let score = display_score(it.score());
390 format!("{} {} {}\n", tag, it.label(), score)
391 })
392 .collect::<String>();
393 expect.assert_eq(&actual);
394 }
395
396 #[test]
397 fn enum_detail_includes_record_fields() {
398 check(
399 r#"
400enum Foo { Foo { x: i32, y: i32 } }
401
402fn main() { Foo::Fo$0 }
403"#,
404 expect![[r#"
405 [
406 CompletionItem {
407 label: "Foo",
408 source_range: 54..56,
409 delete: 54..56,
410 insert: "Foo",
411 kind: SymbolKind(
412 Variant,
413 ),
414 detail: "{ x: i32, y: i32 }",
415 },
416 ]
417 "#]],
418 );
419 }
420
421 #[test]
422 fn enum_detail_doesnt_include_tuple_fields() {
423 check(
424 r#"
425enum Foo { Foo (i32, i32) }
426
427fn main() { Foo::Fo$0 }
428"#,
429 expect![[r#"
430 [
431 CompletionItem {
432 label: "Foo(…)",
433 source_range: 46..48,
434 delete: 46..48,
435 insert: "Foo($0)",
436 kind: SymbolKind(
437 Variant,
438 ),
439 lookup: "Foo",
440 detail: "(i32, i32)",
441 trigger_call_info: true,
442 },
443 ]
444 "#]],
445 );
446 }
447
448 #[test]
449 fn enum_detail_just_parentheses_for_unit() {
450 check(
451 r#"
452enum Foo { Foo }
453
454fn main() { Foo::Fo$0 }
455"#,
456 expect![[r#"
457 [
458 CompletionItem {
459 label: "Foo",
460 source_range: 35..37,
461 delete: 35..37,
462 insert: "Foo",
463 kind: SymbolKind(
464 Variant,
465 ),
466 detail: "()",
467 },
468 ]
469 "#]],
470 );
471 }
472
473 #[test]
474 fn lookup_enums_by_two_qualifiers() {
475 check(
476 r#"
477mod m {
478 pub enum Spam { Foo, Bar(i32) }
479}
480fn main() { let _: m::Spam = S$0 }
481"#,
482 expect![[r#"
483 [
484 CompletionItem {
485 label: "Spam::Bar(…)",
486 source_range: 75..76,
487 delete: 75..76,
488 insert: "Spam::Bar($0)",
489 kind: SymbolKind(
490 Variant,
491 ),
492 lookup: "Spam::Bar",
493 detail: "(i32)",
494 trigger_call_info: true,
495 },
496 CompletionItem {
497 label: "m",
498 source_range: 75..76,
499 delete: 75..76,
500 insert: "m",
501 kind: SymbolKind(
502 Module,
503 ),
504 },
505 CompletionItem {
506 label: "m::Spam::Foo",
507 source_range: 75..76,
508 delete: 75..76,
509 insert: "m::Spam::Foo",
510 kind: SymbolKind(
511 Variant,
512 ),
513 lookup: "Spam::Foo",
514 detail: "()",
515 },
516 CompletionItem {
517 label: "main()",
518 source_range: 75..76,
519 delete: 75..76,
520 insert: "main()$0",
521 kind: SymbolKind(
522 Function,
523 ),
524 lookup: "main",
525 detail: "-> ()",
526 },
527 ]
528 "#]],
529 )
530 }
531
532 #[test]
533 fn sets_deprecated_flag_in_items() {
534 check(
535 r#"
536#[deprecated]
537fn something_deprecated() {}
538#[rustc_deprecated(since = "1.0.0")]
539fn something_else_deprecated() {}
540
541fn main() { som$0 }
542"#,
543 expect![[r#"
544 [
545 CompletionItem {
546 label: "main()",
547 source_range: 127..130,
548 delete: 127..130,
549 insert: "main()$0",
550 kind: SymbolKind(
551 Function,
552 ),
553 lookup: "main",
554 detail: "-> ()",
555 },
556 CompletionItem {
557 label: "something_deprecated()",
558 source_range: 127..130,
559 delete: 127..130,
560 insert: "something_deprecated()$0",
561 kind: SymbolKind(
562 Function,
563 ),
564 lookup: "something_deprecated",
565 detail: "-> ()",
566 deprecated: true,
567 },
568 CompletionItem {
569 label: "something_else_deprecated()",
570 source_range: 127..130,
571 delete: 127..130,
572 insert: "something_else_deprecated()$0",
573 kind: SymbolKind(
574 Function,
575 ),
576 lookup: "something_else_deprecated",
577 detail: "-> ()",
578 deprecated: true,
579 },
580 ]
581 "#]],
582 );
583
584 check(
585 r#"
586struct A { #[deprecated] the_field: u32 }
587fn foo() { A { the$0 } }
588"#,
589 expect![[r#"
590 [
591 CompletionItem {
592 label: "the_field",
593 source_range: 57..60,
594 delete: 57..60,
595 insert: "the_field",
596 kind: SymbolKind(
597 Field,
598 ),
599 detail: "u32",
600 deprecated: true,
601 },
602 ]
603 "#]],
604 );
605 }
606
607 #[test]
608 fn renders_docs() {
609 check(
610 r#"
611struct S {
612 /// Field docs
613 foo:
614}
615impl S {
616 /// Method docs
617 fn bar(self) { self.$0 }
618}"#,
619 expect![[r#"
620 [
621 CompletionItem {
622 label: "bar()",
623 source_range: 94..94,
624 delete: 94..94,
625 insert: "bar()$0",
626 kind: Method,
627 lookup: "bar",
628 detail: "-> ()",
629 documentation: Documentation(
630 "Method docs",
631 ),
632 },
633 CompletionItem {
634 label: "foo",
635 source_range: 94..94,
636 delete: 94..94,
637 insert: "foo",
638 kind: SymbolKind(
639 Field,
640 ),
641 detail: "{unknown}",
642 documentation: Documentation(
643 "Field docs",
644 ),
645 },
646 ]
647 "#]],
648 );
649
650 check(
651 r#"
652use self::my$0;
653
654/// mod docs
655mod my { }
656
657/// enum docs
658enum E {
659 /// variant docs
660 V
661}
662use self::E::*;
663"#,
664 expect![[r#"
665 [
666 CompletionItem {
667 label: "E",
668 source_range: 10..12,
669 delete: 10..12,
670 insert: "E",
671 kind: SymbolKind(
672 Enum,
673 ),
674 documentation: Documentation(
675 "enum docs",
676 ),
677 },
678 CompletionItem {
679 label: "V",
680 source_range: 10..12,
681 delete: 10..12,
682 insert: "V",
683 kind: SymbolKind(
684 Variant,
685 ),
686 detail: "()",
687 documentation: Documentation(
688 "variant docs",
689 ),
690 },
691 CompletionItem {
692 label: "my",
693 source_range: 10..12,
694 delete: 10..12,
695 insert: "my",
696 kind: SymbolKind(
697 Module,
698 ),
699 documentation: Documentation(
700 "mod docs",
701 ),
702 },
703 ]
704 "#]],
705 )
706 }
707
708 #[test]
709 fn dont_render_attrs() {
710 check(
711 r#"
712struct S;
713impl S {
714 #[inline]
715 fn the_method(&self) { }
716}
717fn foo(s: S) { s.$0 }
718"#,
719 expect![[r#"
720 [
721 CompletionItem {
722 label: "the_method()",
723 source_range: 81..81,
724 delete: 81..81,
725 insert: "the_method()$0",
726 kind: Method,
727 lookup: "the_method",
728 detail: "-> ()",
729 },
730 ]
731 "#]],
732 )
733 }
734
735 #[test]
736 fn no_call_parens_if_fn_ptr_needed() {
737 mark::check!(no_call_parens_if_fn_ptr_needed);
738 check_edit(
739 "foo",
740 r#"
741fn foo(foo: u8, bar: u8) {}
742struct ManualVtable { f: fn(u8, u8) }
743
744fn main() -> ManualVtable {
745 ManualVtable { f: f$0 }
746}
747"#,
748 r#"
749fn foo(foo: u8, bar: u8) {}
750struct ManualVtable { f: fn(u8, u8) }
751
752fn main() -> ManualVtable {
753 ManualVtable { f: foo }
754}
755"#,
756 );
757 }
758
759 #[test]
760 fn no_parens_in_use_item() {
761 mark::check!(no_parens_in_use_item);
762 check_edit(
763 "foo",
764 r#"
765mod m { pub fn foo() {} }
766use crate::m::f$0;
767"#,
768 r#"
769mod m { pub fn foo() {} }
770use crate::m::foo;
771"#,
772 );
773 }
774
775 #[test]
776 fn no_parens_in_call() {
777 check_edit(
778 "foo",
779 r#"
780fn foo(x: i32) {}
781fn main() { f$0(); }
782"#,
783 r#"
784fn foo(x: i32) {}
785fn main() { foo(); }
786"#,
787 );
788 check_edit(
789 "foo",
790 r#"
791struct Foo;
792impl Foo { fn foo(&self){} }
793fn f(foo: &Foo) { foo.f$0(); }
794"#,
795 r#"
796struct Foo;
797impl Foo { fn foo(&self){} }
798fn f(foo: &Foo) { foo.foo(); }
799"#,
800 );
801 }
802
803 #[test]
804 fn inserts_angle_brackets_for_generics() {
805 mark::check!(inserts_angle_brackets_for_generics);
806 check_edit(
807 "Vec",
808 r#"
809struct Vec<T> {}
810fn foo(xs: Ve$0)
811"#,
812 r#"
813struct Vec<T> {}
814fn foo(xs: Vec<$0>)
815"#,
816 );
817 check_edit(
818 "Vec",
819 r#"
820type Vec<T> = (T,);
821fn foo(xs: Ve$0)
822"#,
823 r#"
824type Vec<T> = (T,);
825fn foo(xs: Vec<$0>)
826"#,
827 );
828 check_edit(
829 "Vec",
830 r#"
831struct Vec<T = i128> {}
832fn foo(xs: Ve$0)
833"#,
834 r#"
835struct Vec<T = i128> {}
836fn foo(xs: Vec)
837"#,
838 );
839 check_edit(
840 "Vec",
841 r#"
842struct Vec<T> {}
843fn foo(xs: Ve$0<i128>)
844"#,
845 r#"
846struct Vec<T> {}
847fn foo(xs: Vec<i128>)
848"#,
849 );
850 }
851
852 #[test]
853 fn active_param_score() {
854 mark::check!(active_param_type_match);
855 check_scores(
856 r#"
857struct S { foo: i64, bar: u32, baz: u32 }
858fn test(bar: u32) { }
859fn foo(s: S) { test(s.$0) }
860"#,
861 expect![[r#"
862 fd bar [type+name]
863 fd baz [type]
864 fd foo []
865 "#]],
866 );
867 }
868
869 #[test]
870 fn record_field_scores() {
871 mark::check!(record_field_type_match);
872 check_scores(
873 r#"
874struct A { foo: i64, bar: u32, baz: u32 }
875struct B { x: (), y: f32, bar: u32 }
876fn foo(a: A) { B { bar: a.$0 }; }
877"#,
878 expect![[r#"
879 fd bar [type+name]
880 fd baz [type]
881 fd foo []
882 "#]],
883 )
884 }
885
886 #[test]
887 fn record_field_and_call_scores() {
888 check_scores(
889 r#"
890struct A { foo: i64, bar: u32, baz: u32 }
891struct B { x: (), y: f32, bar: u32 }
892fn f(foo: i64) { }
893fn foo(a: A) { B { bar: f(a.$0) }; }
894"#,
895 expect![[r#"
896 fd foo [type+name]
897 fd bar []
898 fd baz []
899 "#]],
900 );
901 check_scores(
902 r#"
903struct A { foo: i64, bar: u32, baz: u32 }
904struct B { x: (), y: f32, bar: u32 }
905fn f(foo: i64) { }
906fn foo(a: A) { f(B { bar: a.$0 }); }
907"#,
908 expect![[r#"
909 fd bar [type+name]
910 fd baz [type]
911 fd foo []
912 "#]],
913 );
914 }
915
916 #[test]
917 fn prioritize_exact_ref_match() {
918 check_scores(
919 r#"
920struct WorldSnapshot { _f: () };
921fn go(world: &WorldSnapshot) { go(w$0) }
922"#,
923 expect![[r#"
924 lc world [type+name]
925 st WorldSnapshot []
926 fn go(…) []
927 "#]],
928 );
929 }
930
931 #[test]
932 fn too_many_arguments() {
933 check_scores(
934 r#"
935struct Foo;
936fn f(foo: &Foo) { f(foo, w$0) }
937"#,
938 expect![[r#"
939 st Foo []
940 fn f(…) []
941 lc foo []
942 "#]],
943 );
944 }
945}
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs
new file mode 100644
index 000000000..d053a988b
--- /dev/null
+++ b/crates/ide_completion/src/render/builder_ext.rs
@@ -0,0 +1,94 @@
1//! Extensions for `Builder` structure required for item rendering.
2
3use itertools::Itertools;
4use test_utils::mark;
5
6use crate::{item::Builder, CompletionContext};
7
8#[derive(Debug)]
9pub(super) enum Params {
10 Named(Vec<String>),
11 Anonymous(usize),
12}
13
14impl Params {
15 pub(super) fn len(&self) -> usize {
16 match self {
17 Params::Named(xs) => xs.len(),
18 Params::Anonymous(len) => *len,
19 }
20 }
21
22 pub(super) fn is_empty(&self) -> bool {
23 self.len() == 0
24 }
25}
26
27impl Builder {
28 fn should_add_parens(&self, ctx: &CompletionContext) -> bool {
29 if !ctx.config.add_call_parenthesis {
30 return false;
31 }
32 if ctx.use_item_syntax.is_some() {
33 mark::hit!(no_parens_in_use_item);
34 return false;
35 }
36 if ctx.is_pattern_call {
37 return false;
38 }
39 if ctx.is_call {
40 return false;
41 }
42
43 // Don't add parentheses if the expected type is some function reference.
44 if let Some(ty) = &ctx.expected_type {
45 if ty.is_fn() {
46 mark::hit!(no_call_parens_if_fn_ptr_needed);
47 return false;
48 }
49 }
50
51 // Nothing prevents us from adding parentheses
52 true
53 }
54
55 pub(super) fn add_call_parens(
56 mut self,
57 ctx: &CompletionContext,
58 name: String,
59 params: Params,
60 ) -> Builder {
61 if !self.should_add_parens(ctx) {
62 return self;
63 }
64
65 let cap = match ctx.config.snippet_cap {
66 Some(it) => it,
67 None => return self,
68 };
69 // If not an import, add parenthesis automatically.
70 mark::hit!(inserts_parens_for_function_calls);
71
72 let (snippet, label) = if params.is_empty() {
73 (format!("{}()$0", name), format!("{}()", name))
74 } else {
75 self = self.trigger_call_info();
76 let snippet = match (ctx.config.add_call_argument_snippets, params) {
77 (true, Params::Named(params)) => {
78 let function_params_snippet =
79 params.iter().enumerate().format_with(", ", |(index, param_name), f| {
80 f(&format_args!("${{{}:{}}}", index + 1, param_name))
81 });
82 format!("{}({})$0", name, function_params_snippet)
83 }
84 _ => {
85 mark::hit!(suppress_arg_snippets);
86 format!("{}($0)", name)
87 }
88 };
89
90 (snippet, format!("{}(…)", name))
91 };
92 self.lookup_by(name).label(label).insert_snippet(cap, snippet)
93 }
94}
diff --git a/crates/ide_completion/src/render/const_.rs b/crates/ide_completion/src/render/const_.rs
new file mode 100644
index 000000000..5010b642a
--- /dev/null
+++ b/crates/ide_completion/src/render/const_.rs
@@ -0,0 +1,59 @@
1//! Renderer for `const` fields.
2
3use hir::HasSource;
4use ide_db::SymbolKind;
5use syntax::{
6 ast::{Const, NameOwner},
7 display::const_label,
8};
9
10use crate::{
11 item::{CompletionItem, CompletionKind},
12 render::RenderContext,
13};
14
15pub(crate) fn render_const<'a>(
16 ctx: RenderContext<'a>,
17 const_: hir::Const,
18) -> Option<CompletionItem> {
19 ConstRender::new(ctx, const_)?.render()
20}
21
22#[derive(Debug)]
23struct ConstRender<'a> {
24 ctx: RenderContext<'a>,
25 const_: hir::Const,
26 ast_node: Const,
27}
28
29impl<'a> ConstRender<'a> {
30 fn new(ctx: RenderContext<'a>, const_: hir::Const) -> Option<ConstRender<'a>> {
31 let ast_node = const_.source(ctx.db())?.value;
32 Some(ConstRender { ctx, const_, ast_node })
33 }
34
35 fn render(self) -> Option<CompletionItem> {
36 let name = self.name()?;
37 let detail = self.detail();
38
39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
40 .kind(SymbolKind::Const)
41 .set_documentation(self.ctx.docs(self.const_))
42 .set_deprecated(
43 self.ctx.is_deprecated(self.const_)
44 || self.ctx.is_deprecated_assoc_item(self.const_),
45 )
46 .detail(detail)
47 .build();
48
49 Some(item)
50 }
51
52 fn name(&self) -> Option<String> {
53 self.ast_node.name().map(|name| name.text().to_string())
54 }
55
56 fn detail(&self) -> String {
57 const_label(&self.ast_node)
58 }
59}
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
new file mode 100644
index 000000000..9214193b4
--- /dev/null
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -0,0 +1,131 @@
1//! Renderer for `enum` variants.
2
3use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
4use ide_db::SymbolKind;
5use itertools::Itertools;
6use test_utils::mark;
7
8use crate::{
9 item::{CompletionItem, CompletionKind, ImportEdit},
10 render::{builder_ext::Params, RenderContext},
11};
12
13pub(crate) fn render_variant<'a>(
14 ctx: RenderContext<'a>,
15 import_to_add: Option<ImportEdit>,
16 local_name: Option<String>,
17 variant: hir::Variant,
18 path: Option<ModPath>,
19) -> CompletionItem {
20 let _p = profile::span("render_enum_variant");
21 EnumRender::new(ctx, local_name, variant, path).render(import_to_add)
22}
23
24#[derive(Debug)]
25struct EnumRender<'a> {
26 ctx: RenderContext<'a>,
27 name: String,
28 variant: hir::Variant,
29 path: Option<ModPath>,
30 qualified_name: String,
31 short_qualified_name: String,
32 variant_kind: StructKind,
33}
34
35impl<'a> EnumRender<'a> {
36 fn new(
37 ctx: RenderContext<'a>,
38 local_name: Option<String>,
39 variant: hir::Variant,
40 path: Option<ModPath>,
41 ) -> EnumRender<'a> {
42 let name = local_name.unwrap_or_else(|| variant.name(ctx.db()).to_string());
43 let variant_kind = variant.kind(ctx.db());
44
45 let (qualified_name, short_qualified_name) = match &path {
46 Some(path) => {
47 let full = path.to_string();
48 let segments = path.segments();
49 let short = segments[segments.len().saturating_sub(2)..].iter().join("::");
50 (full, short)
51 }
52 None => (name.to_string(), name.to_string()),
53 };
54
55 EnumRender { ctx, name, variant, path, qualified_name, short_qualified_name, variant_kind }
56 }
57
58 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
59 let mut builder = CompletionItem::new(
60 CompletionKind::Reference,
61 self.ctx.source_range(),
62 self.qualified_name.clone(),
63 )
64 .kind(SymbolKind::Variant)
65 .set_documentation(self.variant.docs(self.ctx.db()))
66 .set_deprecated(self.ctx.is_deprecated(self.variant))
67 .add_import(import_to_add)
68 .detail(self.detail());
69
70 if self.variant_kind == StructKind::Tuple {
71 mark::hit!(inserts_parens_for_tuple_enums);
72 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
73 builder =
74 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
75 } else if self.path.is_some() {
76 builder = builder.lookup_by(self.short_qualified_name);
77 }
78
79 builder.build()
80 }
81
82 fn detail(&self) -> String {
83 let detail_types = self
84 .variant
85 .fields(self.ctx.db())
86 .into_iter()
87 .map(|field| (field.name(self.ctx.db()), field.signature_ty(self.ctx.db())));
88
89 match self.variant_kind {
90 StructKind::Tuple | StructKind::Unit => format!(
91 "({})",
92 detail_types.map(|(_, t)| t.display(self.ctx.db()).to_string()).format(", ")
93 ),
94 StructKind::Record => format!(
95 "{{ {} }}",
96 detail_types
97 .map(|(n, t)| format!("{}: {}", n, t.display(self.ctx.db()).to_string()))
98 .format(", ")
99 ),
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use test_utils::mark;
107
108 use crate::test_utils::check_edit;
109
110 #[test]
111 fn inserts_parens_for_tuple_enums() {
112 mark::check!(inserts_parens_for_tuple_enums);
113 check_edit(
114 "Some",
115 r#"
116enum Option<T> { Some(T), None }
117use Option::*;
118fn main() -> Option<i32> {
119 Som$0
120}
121"#,
122 r#"
123enum Option<T> { Some(T), None }
124use Option::*;
125fn main() -> Option<i32> {
126 Some($0)
127}
128"#,
129 );
130 }
131}
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
new file mode 100644
index 000000000..e46e21d24
--- /dev/null
+++ b/crates/ide_completion/src/render/function.rs
@@ -0,0 +1,345 @@
1//! Renderer for function calls.
2
3use hir::{HasSource, HirDisplay, Type};
4use ide_db::SymbolKind;
5use syntax::ast::Fn;
6use test_utils::mark;
7
8use crate::{
9 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
10 render::{builder_ext::Params, RenderContext},
11};
12
13pub(crate) fn render_fn<'a>(
14 ctx: RenderContext<'a>,
15 import_to_add: Option<ImportEdit>,
16 local_name: Option<String>,
17 fn_: hir::Function,
18) -> Option<CompletionItem> {
19 let _p = profile::span("render_fn");
20 Some(FunctionRender::new(ctx, local_name, fn_)?.render(import_to_add))
21}
22
23#[derive(Debug)]
24struct FunctionRender<'a> {
25 ctx: RenderContext<'a>,
26 name: String,
27 func: hir::Function,
28 ast_node: Fn,
29}
30
31impl<'a> FunctionRender<'a> {
32 fn new(
33 ctx: RenderContext<'a>,
34 local_name: Option<String>,
35 fn_: hir::Function,
36 ) -> Option<FunctionRender<'a>> {
37 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string());
38 let ast_node = fn_.source(ctx.db())?.value;
39
40 Some(FunctionRender { ctx, name, func: fn_, ast_node })
41 }
42
43 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
44 let params = self.params();
45 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
46 .kind(self.kind())
47 .set_documentation(self.ctx.docs(self.func))
48 .set_deprecated(
49 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
50 )
51 .detail(self.detail())
52 .add_call_parens(self.ctx.completion, self.name, params)
53 .add_import(import_to_add)
54 .build()
55 }
56
57 fn detail(&self) -> String {
58 let ty = self.func.ret_type(self.ctx.db());
59 format!("-> {}", ty.display(self.ctx.db()))
60 }
61
62 fn add_arg(&self, arg: &str, ty: &Type) -> String {
63 if let Some(derefed_ty) = ty.remove_ref() {
64 for (name, local) in self.ctx.completion.locals.iter() {
65 if name == arg && local.ty(self.ctx.db()) == derefed_ty {
66 let mutability = if ty.is_mutable_reference() { "&mut " } else { "&" };
67 return format!("{}{}", mutability, arg);
68 }
69 }
70 }
71 arg.to_string()
72 }
73
74 fn params(&self) -> Params {
75 let ast_params = match self.ast_node.param_list() {
76 Some(it) => it,
77 None => return Params::Named(Vec::new()),
78 };
79
80 let mut params_pats = Vec::new();
81 let params_ty = if self.ctx.completion.dot_receiver.is_some() {
82 self.func.method_params(self.ctx.db()).unwrap_or_default()
83 } else {
84 if let Some(s) = ast_params.self_param() {
85 mark::hit!(parens_for_method_call_as_assoc_fn);
86 params_pats.push(Some(s.to_string()));
87 }
88 self.func.assoc_fn_params(self.ctx.db())
89 };
90 params_pats
91 .extend(ast_params.params().into_iter().map(|it| it.pat().map(|it| it.to_string())));
92
93 let params = params_pats
94 .into_iter()
95 .zip(params_ty)
96 .flat_map(|(pat, param_ty)| {
97 let pat = pat?;
98 let name = pat;
99 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
100 Some(self.add_arg(arg, param_ty.ty()))
101 })
102 .collect();
103 Params::Named(params)
104 }
105
106 fn kind(&self) -> CompletionItemKind {
107 if self.func.self_param(self.ctx.db()).is_some() {
108 CompletionItemKind::Method
109 } else {
110 SymbolKind::Function.into()
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use test_utils::mark;
118
119 use crate::{
120 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG},
121 CompletionConfig,
122 };
123
124 #[test]
125 fn inserts_parens_for_function_calls() {
126 mark::check!(inserts_parens_for_function_calls);
127 check_edit(
128 "no_args",
129 r#"
130fn no_args() {}
131fn main() { no_$0 }
132"#,
133 r#"
134fn no_args() {}
135fn main() { no_args()$0 }
136"#,
137 );
138
139 check_edit(
140 "with_args",
141 r#"
142fn with_args(x: i32, y: String) {}
143fn main() { with_$0 }
144"#,
145 r#"
146fn with_args(x: i32, y: String) {}
147fn main() { with_args(${1:x}, ${2:y})$0 }
148"#,
149 );
150
151 check_edit(
152 "foo",
153 r#"
154struct S;
155impl S {
156 fn foo(&self) {}
157}
158fn bar(s: &S) { s.f$0 }
159"#,
160 r#"
161struct S;
162impl S {
163 fn foo(&self) {}
164}
165fn bar(s: &S) { s.foo()$0 }
166"#,
167 );
168
169 check_edit(
170 "foo",
171 r#"
172struct S {}
173impl S {
174 fn foo(&self, x: i32) {}
175}
176fn bar(s: &S) {
177 s.f$0
178}
179"#,
180 r#"
181struct S {}
182impl S {
183 fn foo(&self, x: i32) {}
184}
185fn bar(s: &S) {
186 s.foo(${1:x})$0
187}
188"#,
189 );
190 }
191
192 #[test]
193 fn parens_for_method_call_as_assoc_fn() {
194 mark::check!(parens_for_method_call_as_assoc_fn);
195 check_edit(
196 "foo",
197 r#"
198struct S;
199impl S {
200 fn foo(&self) {}
201}
202fn main() { S::f$0 }
203"#,
204 r#"
205struct S;
206impl S {
207 fn foo(&self) {}
208}
209fn main() { S::foo(${1:&self})$0 }
210"#,
211 );
212 }
213
214 #[test]
215 fn suppress_arg_snippets() {
216 mark::check!(suppress_arg_snippets);
217 check_edit_with_config(
218 CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
219 "with_args",
220 r#"
221fn with_args(x: i32, y: String) {}
222fn main() { with_$0 }
223"#,
224 r#"
225fn with_args(x: i32, y: String) {}
226fn main() { with_args($0) }
227"#,
228 );
229 }
230
231 #[test]
232 fn strips_underscores_from_args() {
233 check_edit(
234 "foo",
235 r#"
236fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
237fn main() { f$0 }
238"#,
239 r#"
240fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
241fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
242"#,
243 );
244 }
245
246 #[test]
247 fn insert_ref_when_matching_local_in_scope() {
248 check_edit(
249 "ref_arg",
250 r#"
251struct Foo {}
252fn ref_arg(x: &Foo) {}
253fn main() {
254 let x = Foo {};
255 ref_ar$0
256}
257"#,
258 r#"
259struct Foo {}
260fn ref_arg(x: &Foo) {}
261fn main() {
262 let x = Foo {};
263 ref_arg(${1:&x})$0
264}
265"#,
266 );
267 }
268
269 #[test]
270 fn insert_mut_ref_when_matching_local_in_scope() {
271 check_edit(
272 "ref_arg",
273 r#"
274struct Foo {}
275fn ref_arg(x: &mut Foo) {}
276fn main() {
277 let x = Foo {};
278 ref_ar$0
279}
280"#,
281 r#"
282struct Foo {}
283fn ref_arg(x: &mut Foo) {}
284fn main() {
285 let x = Foo {};
286 ref_arg(${1:&mut x})$0
287}
288"#,
289 );
290 }
291
292 #[test]
293 fn insert_ref_when_matching_local_in_scope_for_method() {
294 check_edit(
295 "apply_foo",
296 r#"
297struct Foo {}
298struct Bar {}
299impl Bar {
300 fn apply_foo(&self, x: &Foo) {}
301}
302
303fn main() {
304 let x = Foo {};
305 let y = Bar {};
306 y.$0
307}
308"#,
309 r#"
310struct Foo {}
311struct Bar {}
312impl Bar {
313 fn apply_foo(&self, x: &Foo) {}
314}
315
316fn main() {
317 let x = Foo {};
318 let y = Bar {};
319 y.apply_foo(${1:&x})$0
320}
321"#,
322 );
323 }
324
325 #[test]
326 fn trim_mut_keyword_in_func_completion() {
327 check_edit(
328 "take_mutably",
329 r#"
330fn take_mutably(mut x: &i32) {}
331
332fn main() {
333 take_m$0
334}
335"#,
336 r#"
337fn take_mutably(mut x: &i32) {}
338
339fn main() {
340 take_mutably(${1:x})$0
341}
342"#,
343 );
344 }
345}
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
new file mode 100644
index 000000000..a4535786f
--- /dev/null
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -0,0 +1,214 @@
1//! Renderer for macro invocations.
2
3use hir::{Documentation, HasSource};
4use ide_db::SymbolKind;
5use syntax::display::macro_label;
6use test_utils::mark;
7
8use crate::{
9 item::{CompletionItem, CompletionKind, ImportEdit},
10 render::RenderContext,
11};
12
13pub(crate) fn render_macro<'a>(
14 ctx: RenderContext<'a>,
15 import_to_add: Option<ImportEdit>,
16 name: String,
17 macro_: hir::MacroDef,
18) -> Option<CompletionItem> {
19 let _p = profile::span("render_macro");
20 MacroRender::new(ctx, name, macro_).render(import_to_add)
21}
22
23#[derive(Debug)]
24struct MacroRender<'a> {
25 ctx: RenderContext<'a>,
26 name: String,
27 macro_: hir::MacroDef,
28 docs: Option<Documentation>,
29 bra: &'static str,
30 ket: &'static str,
31}
32
33impl<'a> MacroRender<'a> {
34 fn new(ctx: RenderContext<'a>, name: String, macro_: hir::MacroDef) -> MacroRender<'a> {
35 let docs = ctx.docs(macro_);
36 let docs_str = docs.as_ref().map_or("", |s| s.as_str());
37 let (bra, ket) = guess_macro_braces(&name, docs_str);
38
39 MacroRender { ctx, name, macro_, docs, bra, ket }
40 }
41
42 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
43 let mut builder =
44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label())
45 .kind(SymbolKind::Macro)
46 .set_documentation(self.docs.clone())
47 .set_deprecated(self.ctx.is_deprecated(self.macro_))
48 .add_import(import_to_add)
49 .set_detail(self.detail());
50
51 let needs_bang = self.needs_bang();
52 builder = match self.ctx.snippet_cap() {
53 Some(cap) if needs_bang => {
54 let snippet = self.snippet();
55 let lookup = self.lookup();
56 builder.insert_snippet(cap, snippet).lookup_by(lookup)
57 }
58 None if needs_bang => builder.insert_text(self.banged_name()),
59 _ => {
60 mark::hit!(dont_insert_macro_call_parens_unncessary);
61 builder.insert_text(&self.name)
62 }
63 };
64
65 Some(builder.build())
66 }
67
68 fn needs_bang(&self) -> bool {
69 self.ctx.completion.use_item_syntax.is_none() && !self.ctx.completion.is_macro_call
70 }
71
72 fn label(&self) -> String {
73 if self.needs_bang() && self.ctx.snippet_cap().is_some() {
74 format!("{}!{}…{}", self.name, self.bra, self.ket)
75 } else {
76 self.banged_name()
77 }
78 }
79
80 fn snippet(&self) -> String {
81 format!("{}!{}$0{}", self.name, self.bra, self.ket)
82 }
83
84 fn lookup(&self) -> String {
85 self.banged_name()
86 }
87
88 fn banged_name(&self) -> String {
89 format!("{}!", self.name)
90 }
91
92 fn detail(&self) -> Option<String> {
93 let ast_node = self.macro_.source(self.ctx.db())?.value;
94 Some(macro_label(&ast_node))
95 }
96}
97
98fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
99 let mut votes = [0, 0, 0];
100 for (idx, s) in docs.match_indices(&macro_name) {
101 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
102 // Ensure to match the full word
103 if after.starts_with('!')
104 && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
105 {
106 // It may have spaces before the braces like `foo! {}`
107 match after[1..].chars().find(|&c| !c.is_whitespace()) {
108 Some('{') => votes[0] += 1,
109 Some('[') => votes[1] += 1,
110 Some('(') => votes[2] += 1,
111 _ => {}
112 }
113 }
114 }
115
116 // Insert a space before `{}`.
117 // We prefer the last one when some votes equal.
118 let (_vote, (bra, ket)) = votes
119 .iter()
120 .zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
121 .max_by_key(|&(&vote, _)| vote)
122 .unwrap();
123 (*bra, *ket)
124}
125
126#[cfg(test)]
127mod tests {
128 use test_utils::mark;
129
130 use crate::test_utils::check_edit;
131
132 #[test]
133 fn dont_insert_macro_call_parens_unncessary() {
134 mark::check!(dont_insert_macro_call_parens_unncessary);
135 check_edit(
136 "frobnicate!",
137 r#"
138//- /main.rs crate:main deps:foo
139use foo::$0;
140//- /foo/lib.rs crate:foo
141#[macro_export]
142macro_rules! frobnicate { () => () }
143"#,
144 r#"
145use foo::frobnicate;
146"#,
147 );
148
149 check_edit(
150 "frobnicate!",
151 r#"
152macro_rules! frobnicate { () => () }
153fn main() { frob$0!(); }
154"#,
155 r#"
156macro_rules! frobnicate { () => () }
157fn main() { frobnicate!(); }
158"#,
159 );
160 }
161
162 #[test]
163 fn guesses_macro_braces() {
164 check_edit(
165 "vec!",
166 r#"
167/// Creates a [`Vec`] containing the arguments.
168///
169/// ```
170/// let v = vec![1, 2, 3];
171/// assert_eq!(v[0], 1);
172/// assert_eq!(v[1], 2);
173/// assert_eq!(v[2], 3);
174/// ```
175macro_rules! vec { () => {} }
176
177fn fn main() { v$0 }
178"#,
179 r#"
180/// Creates a [`Vec`] containing the arguments.
181///
182/// ```
183/// let v = vec![1, 2, 3];
184/// assert_eq!(v[0], 1);
185/// assert_eq!(v[1], 2);
186/// assert_eq!(v[2], 3);
187/// ```
188macro_rules! vec { () => {} }
189
190fn fn main() { vec![$0] }
191"#,
192 );
193
194 check_edit(
195 "foo!",
196 r#"
197/// Foo
198///
199/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
200/// call as `let _=foo! { hello world };`
201macro_rules! foo { () => {} }
202fn main() { $0 }
203"#,
204 r#"
205/// Foo
206///
207/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
208/// call as `let _=foo! { hello world };`
209macro_rules! foo { () => {} }
210fn main() { foo! {$0} }
211"#,
212 )
213 }
214}
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
new file mode 100644
index 000000000..465dfe00c
--- /dev/null
+++ b/crates/ide_completion/src/render/pattern.rs
@@ -0,0 +1,150 @@
1//! Renderer for patterns.
2
3use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
4use ide_db::helpers::SnippetCap;
5use itertools::Itertools;
6
7use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind};
8
9fn visible_fields(
10 ctx: &RenderContext<'_>,
11 fields: &[hir::Field],
12 item: impl HasAttrs,
13) -> Option<(Vec<hir::Field>, bool)> {
14 let module = ctx.completion.scope.module()?;
15 let n_fields = fields.len();
16 let fields = fields
17 .into_iter()
18 .filter(|field| field.is_visible_from(ctx.db(), module))
19 .copied()
20 .collect::<Vec<_>>();
21
22 let fields_omitted =
23 n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
24 Some((fields, fields_omitted))
25}
26
27pub(crate) fn render_struct_pat(
28 ctx: RenderContext<'_>,
29 strukt: hir::Struct,
30 local_name: Option<Name>,
31) -> Option<CompletionItem> {
32 let _p = profile::span("render_struct_pat");
33
34 let fields = strukt.fields(ctx.db());
35 let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
36
37 if visible_fields.is_empty() {
38 // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields
39 return None;
40 }
41
42 let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string();
43 let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &visible_fields, fields_omitted)?;
44
45 Some(build_completion(ctx, name, pat, strukt))
46}
47
48pub(crate) fn render_variant_pat(
49 ctx: RenderContext<'_>,
50 variant: hir::Variant,
51 local_name: Option<Name>,
52 path: Option<hir::ModPath>,
53) -> Option<CompletionItem> {
54 let _p = profile::span("render_variant_pat");
55
56 let fields = variant.fields(ctx.db());
57 let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?;
58
59 let name = match &path {
60 Some(path) => path.to_string(),
61 None => local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(),
62 };
63 let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?;
64
65 Some(build_completion(ctx, name, pat, variant))
66}
67
68fn build_completion(
69 ctx: RenderContext<'_>,
70 name: String,
71 pat: String,
72 item: impl HasAttrs + Copy,
73) -> CompletionItem {
74 let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name)
75 .kind(CompletionItemKind::Binding)
76 .set_documentation(ctx.docs(item))
77 .set_deprecated(ctx.is_deprecated(item))
78 .detail(&pat);
79 let completion = if let Some(snippet_cap) = ctx.snippet_cap() {
80 completion.insert_snippet(snippet_cap, pat)
81 } else {
82 completion.insert_text(pat)
83 };
84 completion.build()
85}
86
87fn render_pat(
88 ctx: &RenderContext<'_>,
89 name: &str,
90 kind: StructKind,
91 fields: &[hir::Field],
92 fields_omitted: bool,
93) -> Option<String> {
94 let mut pat = match kind {
95 StructKind::Tuple if ctx.snippet_cap().is_some() => {
96 render_tuple_as_pat(&fields, &name, fields_omitted)
97 }
98 StructKind::Record => {
99 render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted)
100 }
101 _ => return None,
102 };
103
104 if ctx.completion.is_param {
105 pat.push(':');
106 pat.push(' ');
107 pat.push_str(&name);
108 }
109 if ctx.snippet_cap().is_some() {
110 pat.push_str("$0");
111 }
112 Some(pat)
113}
114
115fn render_record_as_pat(
116 db: &dyn HirDatabase,
117 snippet_cap: Option<SnippetCap>,
118 fields: &[hir::Field],
119 name: &str,
120 fields_omitted: bool,
121) -> String {
122 let fields = fields.iter();
123 if snippet_cap.is_some() {
124 format!(
125 "{name} {{ {}{} }}",
126 fields
127 .enumerate()
128 .map(|(idx, field)| format!("{}${}", field.name(db), idx + 1))
129 .format(", "),
130 if fields_omitted { ", .." } else { "" },
131 name = name
132 )
133 } else {
134 format!(
135 "{name} {{ {}{} }}",
136 fields.map(|field| field.name(db)).format(", "),
137 if fields_omitted { ", .." } else { "" },
138 name = name
139 )
140 }
141}
142
143fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) -> String {
144 format!(
145 "{name}({}{})",
146 fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "),
147 if fields_omitted { ", .." } else { "" },
148 name = name
149 )
150}
diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs
new file mode 100644
index 000000000..bd97c3692
--- /dev/null
+++ b/crates/ide_completion/src/render/type_alias.rs
@@ -0,0 +1,59 @@
1//! Renderer for type aliases.
2
3use hir::HasSource;
4use ide_db::SymbolKind;
5use syntax::{
6 ast::{NameOwner, TypeAlias},
7 display::type_label,
8};
9
10use crate::{
11 item::{CompletionItem, CompletionKind},
12 render::RenderContext,
13};
14
15pub(crate) fn render_type_alias<'a>(
16 ctx: RenderContext<'a>,
17 type_alias: hir::TypeAlias,
18) -> Option<CompletionItem> {
19 TypeAliasRender::new(ctx, type_alias)?.render()
20}
21
22#[derive(Debug)]
23struct TypeAliasRender<'a> {
24 ctx: RenderContext<'a>,
25 type_alias: hir::TypeAlias,
26 ast_node: TypeAlias,
27}
28
29impl<'a> TypeAliasRender<'a> {
30 fn new(ctx: RenderContext<'a>, type_alias: hir::TypeAlias) -> Option<TypeAliasRender<'a>> {
31 let ast_node = type_alias.source(ctx.db())?.value;
32 Some(TypeAliasRender { ctx, type_alias, ast_node })
33 }
34
35 fn render(self) -> Option<CompletionItem> {
36 let name = self.name()?;
37 let detail = self.detail();
38
39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
40 .kind(SymbolKind::TypeAlias)
41 .set_documentation(self.ctx.docs(self.type_alias))
42 .set_deprecated(
43 self.ctx.is_deprecated(self.type_alias)
44 || self.ctx.is_deprecated_assoc_item(self.type_alias),
45 )
46 .detail(detail)
47 .build();
48
49 Some(item)
50 }
51
52 fn name(&self) -> Option<String> {
53 self.ast_node.name().map(|name| name.text().to_string())
54 }
55
56 fn detail(&self) -> String {
57 type_label(&self.ast_node)
58 }
59}
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
new file mode 100644
index 000000000..baff83305
--- /dev/null
+++ b/crates/ide_completion/src/test_utils.rs
@@ -0,0 +1,153 @@
1//! Runs completion for testing purposes.
2
3use hir::{PrefixKind, Semantics};
4use ide_db::{
5 base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
6 helpers::{
7 insert_use::{InsertUseConfig, MergeBehavior},
8 SnippetCap,
9 },
10 RootDatabase,
11};
12use itertools::Itertools;
13use stdx::{format_to, trim_indent};
14use syntax::{AstNode, NodeOrToken, SyntaxElement};
15use test_utils::{assert_eq_text, RangeOrOffset};
16
17use crate::{item::CompletionKind, CompletionConfig, CompletionItem};
18
19pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
20 enable_postfix_completions: true,
21 enable_imports_on_the_fly: true,
22 add_call_parenthesis: true,
23 add_call_argument_snippets: true,
24 snippet_cap: SnippetCap::new(true),
25 insert_use: InsertUseConfig {
26 merge: Some(MergeBehavior::Full),
27 prefix_kind: PrefixKind::Plain,
28 },
29};
30
31/// Creates analysis from a multi-file fixture, returns positions marked with $0.
32pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
33 let change_fixture = ChangeFixture::parse(ra_fixture);
34 let mut database = RootDatabase::default();
35 database.apply_change(change_fixture.change);
36 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
37 let offset = match range_or_offset {
38 RangeOrOffset::Range(_) => panic!(),
39 RangeOrOffset::Offset(it) => it,
40 };
41 (database, FilePosition { file_id, offset })
42}
43
44pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
45 do_completion_with_config(TEST_CONFIG, code, kind)
46}
47
48pub(crate) fn do_completion_with_config(
49 config: CompletionConfig,
50 code: &str,
51 kind: CompletionKind,
52) -> Vec<CompletionItem> {
53 let mut kind_completions: Vec<CompletionItem> =
54 get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect();
55 kind_completions.sort_by(|l, r| l.label().cmp(r.label()));
56 kind_completions
57}
58
59pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
60 completion_list_with_config(TEST_CONFIG, code, kind)
61}
62
63pub(crate) fn completion_list_with_config(
64 config: CompletionConfig,
65 code: &str,
66 kind: CompletionKind,
67) -> String {
68 let kind_completions: Vec<CompletionItem> =
69 get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect();
70 let label_width = kind_completions
71 .iter()
72 .map(|it| monospace_width(it.label()))
73 .max()
74 .unwrap_or_default()
75 .min(16);
76 kind_completions
77 .into_iter()
78 .map(|it| {
79 let tag = it.kind().unwrap().tag();
80 let var_name = format!("{} {}", tag, it.label());
81 let mut buf = var_name;
82 if let Some(detail) = it.detail() {
83 let width = label_width.saturating_sub(monospace_width(it.label()));
84 format_to!(buf, "{:width$} {}", "", detail, width = width);
85 }
86 if it.deprecated() {
87 format_to!(buf, " DEPRECATED");
88 }
89 format_to!(buf, "\n");
90 buf
91 })
92 .collect()
93}
94
95fn monospace_width(s: &str) -> usize {
96 s.chars().count()
97}
98
99pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
100 check_edit_with_config(TEST_CONFIG, what, ra_fixture_before, ra_fixture_after)
101}
102
103pub(crate) fn check_edit_with_config(
104 config: CompletionConfig,
105 what: &str,
106 ra_fixture_before: &str,
107 ra_fixture_after: &str,
108) {
109 let ra_fixture_after = trim_indent(ra_fixture_after);
110 let (db, position) = position(ra_fixture_before);
111 let completions: Vec<CompletionItem> =
112 crate::completions(&db, &config, position).unwrap().into();
113 let (completion,) = completions
114 .iter()
115 .filter(|it| it.lookup() == what)
116 .collect_tuple()
117 .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions));
118 let mut actual = db.file_text(position.file_id).to_string();
119
120 let mut combined_edit = completion.text_edit().to_owned();
121 if let Some(import_text_edit) =
122 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use.merge))
123 {
124 combined_edit.union(import_text_edit).expect(
125 "Failed to apply completion resolve changes: change ranges overlap, but should not",
126 )
127 }
128
129 combined_edit.apply(&mut actual);
130 assert_eq_text!(&ra_fixture_after, &actual)
131}
132
133pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) {
134 let (db, pos) = position(code);
135
136 let sema = Semantics::new(&db);
137 let original_file = sema.parse(pos.file_id);
138 let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap();
139 assert!(check(NodeOrToken::Token(token)));
140}
141
142pub(crate) fn check_pattern_is_not_applicable(code: &str, check: fn(SyntaxElement) -> bool) {
143 let (db, pos) = position(code);
144 let sema = Semantics::new(&db);
145 let original_file = sema.parse(pos.file_id);
146 let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap();
147 assert!(!check(NodeOrToken::Token(token)));
148}
149
150pub(crate) fn get_all_items(config: CompletionConfig, code: &str) -> Vec<CompletionItem> {
151 let (db, position) = position(code);
152 crate::completions(&db, &config, position).unwrap().into()
153}