aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-05-29 13:14:16 +0100
committerGitHub <[email protected]>2021-05-29 13:14:16 +0100
commit247faf271b9098624cb0b09dd4914da66497dd5a (patch)
treeb327bd6ab1a67f3655a81f37547af05c2ceaef2b
parent7869b01b702d1f12acb03ac131b6eb90ad82e9bb (diff)
parentc9f0f47bbba3d02a6389af14aaf1c3d1d088d191 (diff)
Merge #9027
9027: feat: Attribute completion is context aware r=Veykril a=Veykril This splits off the `lint` and `derive` completions into their own submodules of `attribute`. The idea is to create a lazy global hashmap that maps `SyntaxKind` to attribute names(`&[&str]`) in which we index with the syntax kind of the "thing" we are attributing giving us the attributes back that are valid for this kind. Then we use this name to do a binary search on the attribute list to fetch and build the corresponding completion item. Co-authored-by: Lukas Wirth <[email protected]>
-rw-r--r--Cargo.lock1
-rw-r--r--crates/ide_completion/Cargo.toml1
-rw-r--r--crates/ide_completion/src/completions/attribute.rs914
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs142
-rw-r--r--crates/ide_completion/src/completions/attribute/lint.rs154
-rw-r--r--crates/syntax/src/ast/node_ext.rs7
6 files changed, 860 insertions, 359 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 557d5f5f3..192f0efc2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -647,6 +647,7 @@ dependencies = [
647 "ide_db", 647 "ide_db",
648 "itertools", 648 "itertools",
649 "log", 649 "log",
650 "once_cell",
650 "profile", 651 "profile",
651 "rustc-hash", 652 "rustc-hash",
652 "stdx", 653 "stdx",
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml
index 6bd8a5500..ba81c9e04 100644
--- a/crates/ide_completion/Cargo.toml
+++ b/crates/ide_completion/Cargo.toml
@@ -15,6 +15,7 @@ itertools = "0.10.0"
15log = "0.4.8" 15log = "0.4.8"
16rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
17either = "1.6.1" 17either = "1.6.1"
18once_cell = "1.7"
18 19
19stdx = { path = "../stdx", version = "0.0.0" } 20stdx = { path = "../stdx", version = "0.0.0" }
20syntax = { path = "../syntax", version = "0.0.0" } 21syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index b1505c74b..610fec65a 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -3,9 +3,11 @@
3//! This module uses a bit of static metadata to provide completions 3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes. 4//! for built-in attributes.
5 5
6use itertools::Itertools; 6use std::mem;
7use rustc_hash::FxHashSet; 7
8use syntax::{ast, AstNode, T}; 8use once_cell::sync::Lazy;
9use rustc_hash::{FxHashMap, FxHashSet};
10use syntax::{ast, AstNode, NodeOrToken, SyntaxKind, T};
9 11
10use crate::{ 12use crate::{
11 context::CompletionContext, 13 context::CompletionContext,
@@ -14,33 +16,40 @@ use crate::{
14 Completions, 16 Completions,
15}; 17};
16 18
17pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 19mod derive;
18 if ctx.mod_declaration_under_caret.is_some() { 20mod lint;
19 return None; 21pub(crate) use self::lint::LintCompletion;
20 }
21 22
23pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
22 let attribute = ctx.attribute_under_caret.as_ref()?; 24 let attribute = ctx.attribute_under_caret.as_ref()?;
23 match (attribute.path(), attribute.token_tree()) { 25 match (attribute.path().and_then(|p| p.as_single_name_ref()), attribute.token_tree()) {
24 (Some(path), Some(token_tree)) => { 26 (Some(path), Some(token_tree)) => match path.text().as_str() {
25 let path = path.syntax().text(); 27 "derive" => derive::complete_derive(acc, ctx, token_tree),
26 if path == "derive" { 28 "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES),
27 complete_derive(acc, ctx, token_tree) 29 "allow" | "warn" | "deny" | "forbid" => {
28 } else if path == "feature" { 30 lint::complete_lint(acc, ctx, token_tree.clone(), lint::DEFAULT_LINT_COMPLETIONS);
29 complete_lint(acc, ctx, token_tree, FEATURES) 31 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
30 } else if path == "allow" || path == "warn" || path == "deny" || path == "forbid" {
31 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS);
32 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
33 } 32 }
34 } 33 _ => (),
35 (_, Some(_token_tree)) => {} 34 },
36 _ => complete_attribute_start(acc, ctx, attribute), 35 (None, Some(_)) => (),
36 _ => complete_new_attribute(acc, ctx, attribute),
37 } 37 }
38 Some(()) 38 Some(())
39} 39}
40 40
41fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) { 41fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
42 let attribute_annotated_item_kind = attribute.syntax().parent().map(|it| it.kind());
43 let attributes = attribute_annotated_item_kind.and_then(|kind| {
44 if ast::Expr::can_cast(kind) {
45 Some(EXPR_ATTRIBUTES)
46 } else {
47 KIND_TO_ATTRIBUTES.get(&kind).copied()
48 }
49 });
42 let is_inner = attribute.kind() == ast::AttrKind::Inner; 50 let is_inner = attribute.kind() == ast::AttrKind::Inner;
43 for attr_completion in ATTRIBUTES.iter().filter(|compl| is_inner || !compl.prefer_inner) { 51
52 let add_completion = |attr_completion: &AttrCompletion| {
44 let mut item = CompletionItem::new( 53 let mut item = CompletionItem::new(
45 CompletionKind::Attribute, 54 CompletionKind::Attribute,
46 ctx.source_range(), 55 ctx.source_range(),
@@ -56,9 +65,19 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
56 item.insert_snippet(cap, snippet); 65 item.insert_snippet(cap, snippet);
57 } 66 }
58 67
59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { 68 if is_inner || !attr_completion.prefer_inner {
60 acc.add(item.build()); 69 acc.add(item.build());
61 } 70 }
71 };
72
73 match attributes {
74 Some(applicable) => applicable
75 .iter()
76 .flat_map(|name| ATTRIBUTES.binary_search_by(|attr| attr.key().cmp(name)).ok())
77 .flat_map(|idx| ATTRIBUTES.get(idx))
78 .for_each(add_completion),
79 None if is_inner => ATTRIBUTES.iter().for_each(add_completion),
80 None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion),
62 } 81 }
63} 82}
64 83
@@ -70,6 +89,10 @@ struct AttrCompletion {
70} 89}
71 90
72impl AttrCompletion { 91impl AttrCompletion {
92 fn key(&self) -> &'static str {
93 self.lookup.unwrap_or(self.label)
94 }
95
73 const fn prefer_inner(self) -> AttrCompletion { 96 const fn prefer_inner(self) -> AttrCompletion {
74 AttrCompletion { prefer_inner: true, ..self } 97 AttrCompletion { prefer_inner: true, ..self }
75 } 98 }
@@ -83,26 +106,119 @@ const fn attr(
83 AttrCompletion { label, lookup, snippet, prefer_inner: false } 106 AttrCompletion { label, lookup, snippet, prefer_inner: false }
84} 107}
85 108
109macro_rules! attrs {
110 // attributes applicable to all items
111 [@ { item $($tt:tt)* } {$($acc:tt)*}] => {
112 attrs!(@ { $($tt)* } { $($acc)*, "deprecated", "doc", "dochidden", "docalias", "must_use", "no_mangle" })
113 };
114 // attributes applicable to all adts
115 [@ { adt $($tt:tt)* } {$($acc:tt)*}] => {
116 attrs!(@ { $($tt)* } { $($acc)*, "derive", "repr" })
117 };
118 // attributes applicable to all linkable things aka functions/statics
119 [@ { linkable $($tt:tt)* } {$($acc:tt)*}] => {
120 attrs!(@ { $($tt)* } { $($acc)*, "export_name", "link_name", "link_section" })
121 };
122 // error fallback for nicer error message
123 [@ { $ty:ident $($tt:tt)* } {$($acc:tt)*}] => {
124 compile_error!(concat!("unknown attr subtype ", stringify!($ty)))
125 };
126 // general push down accumulation
127 [@ { $lit:literal $($tt:tt)*} {$($acc:tt)*}] => {
128 attrs!(@ { $($tt)* } { $($acc)*, $lit })
129 };
130 [@ {$($tt:tt)+} {$($tt2:tt)*}] => {
131 compile_error!(concat!("Unexpected input ", stringify!($($tt)+)))
132 };
133 // final output construction
134 [@ {} {$($tt:tt)*}] => { &[$($tt)*] as _ };
135 // starting matcher
136 [$($tt:tt),*] => {
137 attrs!(@ { $($tt)* } { "allow", "cfg", "cfg_attr", "deny", "forbid", "warn" })
138 };
139}
140
141#[rustfmt::skip]
142static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| {
143 use SyntaxKind::*;
144 std::array::IntoIter::new([
145 (
146 SOURCE_FILE,
147 attrs!(
148 item,
149 "crate_name", "feature", "no_implicit_prelude", "no_main", "no_std",
150 "recursion_limit", "type_length_limit", "windows_subsystem"
151 ),
152 ),
153 (MODULE, attrs!(item, "no_implicit_prelude", "path")),
154 (ITEM_LIST, attrs!(item, "no_implicit_prelude")),
155 (MACRO_RULES, attrs!(item, "macro_export", "macro_use")),
156 (MACRO_DEF, attrs!(item)),
157 (EXTERN_CRATE, attrs!(item, "macro_use", "no_link")),
158 (USE, attrs!(item)),
159 (TYPE_ALIAS, attrs!(item)),
160 (STRUCT, attrs!(item, adt, "non_exhaustive")),
161 (ENUM, attrs!(item, adt, "non_exhaustive")),
162 (UNION, attrs!(item, adt)),
163 (CONST, attrs!(item)),
164 (
165 FN,
166 attrs!(
167 item, linkable,
168 "cold", "ignore", "inline", "must_use", "panic_handler", "proc_macro",
169 "proc_macro_derive", "proc_macro_attribute", "should_panic", "target_feature",
170 "test", "track_caller"
171 ),
172 ),
173 (STATIC, attrs!(item, linkable, "global_allocator", "used")),
174 (TRAIT, attrs!(item, "must_use")),
175 (IMPL, attrs!(item, "automatically_derived")),
176 (ASSOC_ITEM_LIST, attrs!(item)),
177 (EXTERN_BLOCK, attrs!(item, "link")),
178 (EXTERN_ITEM_LIST, attrs!(item, "link")),
179 (MACRO_CALL, attrs!()),
180 (SELF_PARAM, attrs!()),
181 (PARAM, attrs!()),
182 (RECORD_FIELD, attrs!()),
183 (VARIANT, attrs!("non_exhaustive")),
184 (TYPE_PARAM, attrs!()),
185 (CONST_PARAM, attrs!()),
186 (LIFETIME_PARAM, attrs!()),
187 (LET_STMT, attrs!()),
188 (EXPR_STMT, attrs!()),
189 (LITERAL, attrs!()),
190 (RECORD_EXPR_FIELD_LIST, attrs!()),
191 (RECORD_EXPR_FIELD, attrs!()),
192 (MATCH_ARM_LIST, attrs!()),
193 (MATCH_ARM, attrs!()),
194 (IDENT_PAT, attrs!()),
195 (RECORD_PAT_FIELD, attrs!()),
196 ])
197 .collect()
198});
199const EXPR_ATTRIBUTES: &[&str] = attrs!();
200
86/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index 201/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index
202// Keep these sorted for the binary search!
87const ATTRIBUTES: &[AttrCompletion] = &[ 203const ATTRIBUTES: &[AttrCompletion] = &[
88 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), 204 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
89 attr("automatically_derived", None, None), 205 attr("automatically_derived", None, None),
90 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
91 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")), 206 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
207 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
92 attr("cold", None, None), 208 attr("cold", None, None),
93 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#)) 209 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#))
94 .prefer_inner(), 210 .prefer_inner(),
95 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")), 211 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
96 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)), 212 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)),
97 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)), 213 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
214 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
215 attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
216 attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
98 attr( 217 attr(
99 r#"export_name = "…""#, 218 r#"export_name = "…""#,
100 Some("export_name"), 219 Some("export_name"),
101 Some(r#"export_name = "${0:exported_symbol_name}""#), 220 Some(r#"export_name = "${0:exported_symbol_name}""#),
102 ), 221 ),
103 attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
104 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
105 attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
106 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(), 222 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
107 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), 223 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
108 // FIXME: resolve through macro resolution? 224 // FIXME: resolve through macro resolution?
@@ -119,8 +235,8 @@ const ATTRIBUTES: &[AttrCompletion] = &[
119 attr("macro_export", None, None), 235 attr("macro_export", None, None),
120 attr("macro_use", None, None), 236 attr("macro_use", None, None),
121 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)), 237 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)),
122 attr("no_link", None, None).prefer_inner(),
123 attr("no_implicit_prelude", None, None).prefer_inner(), 238 attr("no_implicit_prelude", None, None).prefer_inner(),
239 attr("no_link", None, None).prefer_inner(),
124 attr("no_main", None, None).prefer_inner(), 240 attr("no_main", None, None).prefer_inner(),
125 attr("no_mangle", None, None), 241 attr("no_mangle", None, None),
126 attr("no_std", None, None).prefer_inner(), 242 attr("no_std", None, None).prefer_inner(),
@@ -153,412 +269,492 @@ const ATTRIBUTES: &[AttrCompletion] = &[
153 .prefer_inner(), 269 .prefer_inner(),
154]; 270];
155 271
156fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { 272fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Option<FxHashSet<String>> {
157 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) { 273 let (l_paren, r_paren) = derive_input.l_paren_token().zip(derive_input.r_paren_token())?;
158 for derive_completion in DEFAULT_DERIVE_COMPLETIONS 274 let mut input_derives = FxHashSet::default();
159 .iter() 275 let mut current_derive = String::new();
160 .filter(|completion| !existing_derives.contains(completion.label)) 276 for token in derive_input
161 { 277 .syntax()
162 let mut components = vec![derive_completion.label]; 278 .children_with_tokens()
163 components.extend( 279 .filter_map(NodeOrToken::into_token)
164 derive_completion 280 .skip_while(|token| token != &l_paren)
165 .dependencies 281 .skip(1)
166 .iter() 282 .take_while(|token| token != &r_paren)
167 .filter(|&&dependency| !existing_derives.contains(dependency)), 283 {
168 ); 284 if token.kind() == T![,] {
169 let lookup = components.join(", ");
170 let label = components.iter().rev().join(", ");
171 let mut item =
172 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
173 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
174 item.add_to(acc);
175 }
176
177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
178 let mut item = CompletionItem::new(
179 CompletionKind::Attribute,
180 ctx.source_range(),
181 custom_derive_name,
182 );
183 item.kind(CompletionItemKind::Attribute);
184 item.add_to(acc);
185 }
186 }
187}
188
189fn complete_lint(
190 acc: &mut Completions,
191 ctx: &CompletionContext,
192 derive_input: ast::TokenTree,
193 lints_completions: &[LintCompletion],
194) {
195 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
196 for lint_completion in lints_completions
197 .into_iter()
198 .filter(|completion| !existing_lints.contains(completion.label))
199 {
200 let mut item = CompletionItem::new(
201 CompletionKind::Attribute,
202 ctx.source_range(),
203 lint_completion.label,
204 );
205 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description);
206 item.add_to(acc)
207 }
208 }
209}
210
211fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
212 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
213 (Some(left_paren), Some(right_paren))
214 if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] =>
215 {
216 let mut input_derives = FxHashSet::default();
217 let mut current_derive = String::new();
218 for token in derive_input
219 .syntax()
220 .children_with_tokens()
221 .filter_map(|token| token.into_token())
222 .skip_while(|token| token != &left_paren)
223 .skip(1)
224 .take_while(|token| token != &right_paren)
225 {
226 if T![,] == token.kind() {
227 if !current_derive.is_empty() {
228 input_derives.insert(current_derive);
229 current_derive = String::new();
230 }
231 } else {
232 current_derive.push_str(token.text().trim());
233 }
234 }
235
236 if !current_derive.is_empty() { 285 if !current_derive.is_empty() {
237 input_derives.insert(current_derive); 286 input_derives.insert(mem::take(&mut current_derive));
238 } 287 }
239 Ok(input_derives) 288 } else {
289 current_derive.push_str(token.text().trim());
240 } 290 }
241 _ => Err(()),
242 } 291 }
243}
244
245fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
246 let mut result = FxHashSet::default();
247 ctx.scope.process_all_names(&mut |name, scope_def| {
248 if let hir::ScopeDef::MacroDef(mac) = scope_def {
249 // FIXME kind() doesn't check whether proc-macro is a derive
250 if mac.kind() == hir::MacroKind::Derive || mac.kind() == hir::MacroKind::ProcMacro {
251 result.insert(name.to_string());
252 }
253 }
254 });
255 result
256}
257
258struct DeriveCompletion {
259 label: &'static str,
260 dependencies: &'static [&'static str],
261}
262
263/// Standard Rust derives and the information about their dependencies
264/// (the dependencies are needed so that the main derive don't break the compilation when added)
265const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
266 DeriveCompletion { label: "Clone", dependencies: &[] },
267 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
268 DeriveCompletion { label: "Debug", dependencies: &[] },
269 DeriveCompletion { label: "Default", dependencies: &[] },
270 DeriveCompletion { label: "Hash", dependencies: &[] },
271 DeriveCompletion { label: "PartialEq", dependencies: &[] },
272 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
273 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
274 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
275];
276 292
277pub(crate) struct LintCompletion { 293 if !current_derive.is_empty() {
278 pub(crate) label: &'static str, 294 input_derives.insert(current_derive);
279 pub(crate) description: &'static str, 295 }
296 Some(input_derives)
280} 297}
281 298
282#[rustfmt::skip]
283const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
284 LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# },
285 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
286 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
287 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
288 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
289 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
290 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
291 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
292 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
293 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
294 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
295 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
296 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
297 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
298 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
299 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
300 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
301 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
302 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
303 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
304 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
305 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
306 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
307 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
308 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
309 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
310 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
311 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
312 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
313 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
314 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
315 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
316 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
317 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
318 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
319 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
320 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
321 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
322 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
323 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
324 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
325 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
326 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
327 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
328 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
329 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
330 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
331 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
332 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
333 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
334 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
335 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
336 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
337 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
338 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
339 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
340 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
341 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
342 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
343 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
344 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
345 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
346 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
347 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
348 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
349 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
350 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
351 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
352 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
353 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
354 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
355 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
356 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
357 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
358 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
359 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
360 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
361 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
362 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
363 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
364 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
365 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
366 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
367 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
368 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
369 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
370 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
371 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
372 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
373 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
374 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
375 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
376 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
377 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
378 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
379 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
380 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
381 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
382 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
383 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
384 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
385 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
386 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
387 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
388 LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# },
389 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
390 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
391 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
392 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
393 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
394 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
395 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
396 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
397 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
398 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
399];
400
401#[cfg(test)] 299#[cfg(test)]
402mod tests { 300mod tests {
301 use super::*;
302
403 use expect_test::{expect, Expect}; 303 use expect_test::{expect, Expect};
404 304
405 use crate::{test_utils::completion_list, CompletionKind}; 305 use crate::{test_utils::completion_list, CompletionKind};
406 306
307 #[test]
308 fn attributes_are_sorted() {
309 let mut attrs = ATTRIBUTES.iter().map(|attr| attr.key());
310 let mut prev = attrs.next().unwrap();
311
312 attrs.for_each(|next| {
313 assert!(
314 prev < next,
315 r#"ATTRIBUTES array is not sorted, "{}" should come after "{}""#,
316 prev,
317 next
318 );
319 prev = next;
320 });
321 }
322
407 fn check(ra_fixture: &str, expect: Expect) { 323 fn check(ra_fixture: &str, expect: Expect) {
408 let actual = completion_list(ra_fixture, CompletionKind::Attribute); 324 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
409 expect.assert_eq(&actual); 325 expect.assert_eq(&actual);
410 } 326 }
411 327
412 #[test] 328 #[test]
413 fn empty_derive_completion() { 329 fn test_attribute_completion_inside_nested_attr() {
330 check(r#"#[cfg($0)]"#, expect![[]])
331 }
332
333 #[test]
334 fn test_attribute_completion_with_existing_attr() {
335 check(
336 r#"#[no_mangle] #[$0] mcall!();"#,
337 expect![[r#"
338 at allow(…)
339 at cfg(…)
340 at cfg_attr(…)
341 at deny(…)
342 at forbid(…)
343 at warn(…)
344 "#]],
345 )
346 }
347
348 #[test]
349 fn complete_attribute_on_source_file() {
414 check( 350 check(
415 r#" 351 r#"#![$0]"#,
416#[derive($0)]
417struct Test {}
418 "#,
419 expect![[r#" 352 expect![[r#"
420 at Clone 353 at allow(…)
421 at Clone, Copy 354 at cfg(…)
422 at Debug 355 at cfg_attr(…)
423 at Default 356 at deny(…)
424 at Hash 357 at forbid(…)
425 at PartialEq 358 at warn(…)
426 at PartialEq, Eq 359 at deprecated
427 at PartialEq, PartialOrd 360 at doc = "…"
428 at PartialEq, Eq, PartialOrd, Ord 361 at doc(hidden)
362 at doc(alias = "…")
363 at must_use
364 at no_mangle
365 at crate_name = ""
366 at feature(…)
367 at no_implicit_prelude
368 at no_main
369 at no_std
370 at recursion_limit = …
371 at type_length_limit = …
372 at windows_subsystem = "…"
429 "#]], 373 "#]],
430 ); 374 );
431 } 375 }
432 376
433 #[test] 377 #[test]
434 fn no_completion_for_incorrect_derive() { 378 fn complete_attribute_on_module() {
435 check( 379 check(
436 r#" 380 r#"#[$0] mod foo;"#,
437#[derive{$0)] 381 expect![[r#"
438struct Test {} 382 at allow(…)
439"#, 383 at cfg(…)
440 expect![[r#""#]], 384 at cfg_attr(…)
441 ) 385 at deny(…)
386 at forbid(…)
387 at warn(…)
388 at deprecated
389 at doc = "…"
390 at doc(hidden)
391 at doc(alias = "…")
392 at must_use
393 at no_mangle
394 at path = "…"
395 "#]],
396 );
397 check(
398 r#"mod foo {#![$0]}"#,
399 expect![[r#"
400 at allow(…)
401 at cfg(…)
402 at cfg_attr(…)
403 at deny(…)
404 at forbid(…)
405 at warn(…)
406 at deprecated
407 at doc = "…"
408 at doc(hidden)
409 at doc(alias = "…")
410 at must_use
411 at no_mangle
412 at no_implicit_prelude
413 "#]],
414 );
442 } 415 }
443 416
444 #[test] 417 #[test]
445 fn derive_with_input_completion() { 418 fn complete_attribute_on_macro_rules() {
446 check( 419 check(
447 r#" 420 r#"#[$0] macro_rules! foo {}"#,
448#[derive(serde::Serialize, PartialEq, $0)]
449struct Test {}
450"#,
451 expect![[r#" 421 expect![[r#"
452 at Clone 422 at allow(…)
453 at Clone, Copy 423 at cfg(…)
454 at Debug 424 at cfg_attr(…)
455 at Default 425 at deny(…)
456 at Hash 426 at forbid(…)
457 at Eq 427 at warn(…)
458 at PartialOrd 428 at deprecated
459 at Eq, PartialOrd, Ord 429 at doc = "…"
430 at doc(hidden)
431 at doc(alias = "…")
432 at must_use
433 at no_mangle
434 at macro_export
435 at macro_use
460 "#]], 436 "#]],
461 ) 437 );
462 } 438 }
463 439
464 #[test] 440 #[test]
465 fn test_attribute_completion() { 441 fn complete_attribute_on_macro_def() {
466 check( 442 check(
467 r#"#[$0]"#, 443 r#"#[$0] macro foo {}"#,
468 expect![[r#" 444 expect![[r#"
469 at allow(…) 445 at allow(…)
470 at automatically_derived 446 at cfg(…)
471 at cfg_attr(…) 447 at cfg_attr(…)
448 at deny(…)
449 at forbid(…)
450 at warn(…)
451 at deprecated
452 at doc = "…"
453 at doc(hidden)
454 at doc(alias = "…")
455 at must_use
456 at no_mangle
457 "#]],
458 );
459 }
460
461 #[test]
462 fn complete_attribute_on_extern_crate() {
463 check(
464 r#"#[$0] extern crate foo;"#,
465 expect![[r#"
466 at allow(…)
472 at cfg(…) 467 at cfg(…)
473 at cold 468 at cfg_attr(…)
474 at deny(…) 469 at deny(…)
470 at forbid(…)
471 at warn(…)
475 at deprecated 472 at deprecated
476 at derive(…) 473 at doc = "…"
477 at export_name = "…" 474 at doc(hidden)
478 at doc(alias = "…") 475 at doc(alias = "…")
476 at must_use
477 at no_mangle
478 at macro_use
479 "#]],
480 );
481 }
482
483 #[test]
484 fn complete_attribute_on_use() {
485 check(
486 r#"#[$0] use foo;"#,
487 expect![[r#"
488 at allow(…)
489 at cfg(…)
490 at cfg_attr(…)
491 at deny(…)
492 at forbid(…)
493 at warn(…)
494 at deprecated
479 at doc = "…" 495 at doc = "…"
480 at doc(hidden) 496 at doc(hidden)
497 at doc(alias = "…")
498 at must_use
499 at no_mangle
500 "#]],
501 );
502 }
503
504 #[test]
505 fn complete_attribute_on_type_alias() {
506 check(
507 r#"#[$0] type foo = ();"#,
508 expect![[r#"
509 at allow(…)
510 at cfg(…)
511 at cfg_attr(…)
512 at deny(…)
481 at forbid(…) 513 at forbid(…)
482 at ignore = "…" 514 at warn(…)
483 at inline 515 at deprecated
484 at link 516 at doc = "…"
485 at link_name = "…" 517 at doc(hidden)
486 at link_section = "…" 518 at doc(alias = "…")
487 at macro_export
488 at macro_use
489 at must_use 519 at must_use
490 at no_mangle 520 at no_mangle
521 "#]],
522 );
523 }
524
525 #[test]
526 fn complete_attribute_on_struct() {
527 check(
528 r#"#[$0] struct Foo;"#,
529 expect![[r#"
530 at allow(…)
531 at cfg(…)
532 at cfg_attr(…)
533 at deny(…)
534 at forbid(…)
535 at warn(…)
536 at deprecated
537 at doc = "…"
538 at doc(hidden)
539 at doc(alias = "…")
540 at must_use
541 at no_mangle
542 at derive(…)
543 at repr(…)
491 at non_exhaustive 544 at non_exhaustive
492 at path = "…" 545 "#]],
493 at proc_macro 546 );
494 at proc_macro_attribute 547 }
495 at proc_macro_derive(…) 548
549 #[test]
550 fn complete_attribute_on_enum() {
551 check(
552 r#"#[$0] enum Foo {}"#,
553 expect![[r#"
554 at allow(…)
555 at cfg(…)
556 at cfg_attr(…)
557 at deny(…)
558 at forbid(…)
559 at warn(…)
560 at deprecated
561 at doc = "…"
562 at doc(hidden)
563 at doc(alias = "…")
564 at must_use
565 at no_mangle
566 at derive(…)
496 at repr(…) 567 at repr(…)
497 at should_panic 568 at non_exhaustive
498 at target_feature = "…" 569 "#]],
499 at test 570 );
500 at track_caller 571 }
501 at used 572
573 #[test]
574 fn complete_attribute_on_const() {
575 check(
576 r#"#[$0] const FOO: () = ();"#,
577 expect![[r#"
578 at allow(…)
579 at cfg(…)
580 at cfg_attr(…)
581 at deny(…)
582 at forbid(…)
502 at warn(…) 583 at warn(…)
584 at deprecated
585 at doc = "…"
586 at doc(hidden)
587 at doc(alias = "…")
588 at must_use
589 at no_mangle
503 "#]], 590 "#]],
504 ) 591 );
505 } 592 }
506 593
507 #[test] 594 #[test]
508 fn test_attribute_completion_inside_nested_attr() { 595 fn complete_attribute_on_static() {
509 check(r#"#[cfg($0)]"#, expect![[]]) 596 check(
597 r#"#[$0] static FOO: () = ()"#,
598 expect![[r#"
599 at allow(…)
600 at cfg(…)
601 at cfg_attr(…)
602 at deny(…)
603 at forbid(…)
604 at warn(…)
605 at deprecated
606 at doc = "…"
607 at doc(hidden)
608 at doc(alias = "…")
609 at must_use
610 at no_mangle
611 at export_name = "…"
612 at link_name = "…"
613 at link_section = "…"
614 at used
615 "#]],
616 );
510 } 617 }
511 618
512 #[test] 619 #[test]
513 fn test_inner_attribute_completion() { 620 fn complete_attribute_on_trait() {
514 check( 621 check(
515 r"#![$0]", 622 r#"#[$0] trait Foo {}"#,
516 expect![[r#" 623 expect![[r#"
517 at allow(…) 624 at allow(…)
518 at automatically_derived 625 at cfg(…)
519 at cfg_attr(…) 626 at cfg_attr(…)
627 at deny(…)
628 at forbid(…)
629 at warn(…)
630 at deprecated
631 at doc = "…"
632 at doc(hidden)
633 at doc(alias = "…")
634 at must_use
635 at no_mangle
636 at must_use
637 "#]],
638 );
639 }
640
641 #[test]
642 fn complete_attribute_on_impl() {
643 check(
644 r#"#[$0] impl () {}"#,
645 expect![[r#"
646 at allow(…)
520 at cfg(…) 647 at cfg(…)
521 at cold 648 at cfg_attr(…)
522 at crate_name = ""
523 at deny(…) 649 at deny(…)
650 at forbid(…)
651 at warn(…)
524 at deprecated 652 at deprecated
525 at derive(…) 653 at doc = "…"
526 at export_name = "…" 654 at doc(hidden)
527 at doc(alias = "…") 655 at doc(alias = "…")
656 at must_use
657 at no_mangle
658 at automatically_derived
659 "#]],
660 );
661 check(
662 r#"impl () {#![$0]}"#,
663 expect![[r#"
664 at allow(…)
665 at cfg(…)
666 at cfg_attr(…)
667 at deny(…)
668 at forbid(…)
669 at warn(…)
670 at deprecated
528 at doc = "…" 671 at doc = "…"
529 at doc(hidden) 672 at doc(hidden)
530 at feature(…) 673 at doc(alias = "…")
674 at must_use
675 at no_mangle
676 "#]],
677 );
678 }
679
680 #[test]
681 fn complete_attribute_on_extern_block() {
682 check(
683 r#"#[$0] extern {}"#,
684 expect![[r#"
685 at allow(…)
686 at cfg(…)
687 at cfg_attr(…)
688 at deny(…)
531 at forbid(…) 689 at forbid(…)
532 at global_allocator 690 at warn(…)
533 at ignore = "…" 691 at deprecated
534 at inline 692 at doc = "…"
693 at doc(hidden)
694 at doc(alias = "…")
695 at must_use
696 at no_mangle
535 at link 697 at link
536 at link_name = "…" 698 "#]],
537 at link_section = "…" 699 );
538 at macro_export 700 check(
539 at macro_use 701 r#"extern {#![$0]}"#,
702 expect![[r#"
703 at allow(…)
704 at cfg(…)
705 at cfg_attr(…)
706 at deny(…)
707 at forbid(…)
708 at warn(…)
709 at deprecated
710 at doc = "…"
711 at doc(hidden)
712 at doc(alias = "…")
540 at must_use 713 at must_use
541 at no_link
542 at no_implicit_prelude
543 at no_main
544 at no_mangle 714 at no_mangle
545 at no_std 715 at link
716 "#]],
717 );
718 }
719
720 #[test]
721 fn complete_attribute_on_variant() {
722 check(
723 r#"enum Foo { #[$0] Bar }"#,
724 expect![[r#"
725 at allow(…)
726 at cfg(…)
727 at cfg_attr(…)
728 at deny(…)
729 at forbid(…)
730 at warn(…)
546 at non_exhaustive 731 at non_exhaustive
547 at panic_handler 732 "#]],
548 at path = "…" 733 );
549 at proc_macro 734 }
550 at proc_macro_attribute 735
551 at proc_macro_derive(…) 736 #[test]
552 at recursion_limit = … 737 fn complete_attribute_on_expr() {
553 at repr(…) 738 check(
554 at should_panic 739 r#"fn main() { #[$0] foo() }"#,
555 at target_feature = "…" 740 expect![[r#"
556 at test 741 at allow(…)
557 at track_caller 742 at cfg(…)
558 at type_length_limit = … 743 at cfg_attr(…)
559 at used 744 at deny(…)
745 at forbid(…)
746 at warn(…)
747 "#]],
748 );
749 check(
750 r#"fn main() { #[$0] foo(); }"#,
751 expect![[r#"
752 at allow(…)
753 at cfg(…)
754 at cfg_attr(…)
755 at deny(…)
756 at forbid(…)
560 at warn(…) 757 at warn(…)
561 at windows_subsystem = "…"
562 "#]], 758 "#]],
563 ); 759 );
564 } 760 }
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
new file mode 100644
index 000000000..7b0a778a2
--- /dev/null
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -0,0 +1,142 @@
1//! Completion for derives
2use itertools::Itertools;
3use rustc_hash::FxHashSet;
4use syntax::ast;
5
6use crate::{
7 context::CompletionContext,
8 item::{CompletionItem, CompletionItemKind, CompletionKind},
9 Completions,
10};
11
12pub(super) fn complete_derive(
13 acc: &mut Completions,
14 ctx: &CompletionContext,
15 derive_input: ast::TokenTree,
16) {
17 if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) {
18 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
19 .iter()
20 .filter(|completion| !existing_derives.contains(completion.label))
21 {
22 let mut components = vec![derive_completion.label];
23 components.extend(
24 derive_completion
25 .dependencies
26 .iter()
27 .filter(|&&dependency| !existing_derives.contains(dependency)),
28 );
29 let lookup = components.join(", ");
30 let label = components.iter().rev().join(", ");
31 let mut item =
32 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
33 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
34 item.add_to(acc);
35 }
36
37 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
38 let mut item = CompletionItem::new(
39 CompletionKind::Attribute,
40 ctx.source_range(),
41 custom_derive_name,
42 );
43 item.kind(CompletionItemKind::Attribute);
44 item.add_to(acc);
45 }
46 }
47}
48fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
49 let mut result = FxHashSet::default();
50 ctx.scope.process_all_names(&mut |name, scope_def| {
51 if let hir::ScopeDef::MacroDef(mac) = scope_def {
52 // FIXME kind() doesn't check whether proc-macro is a derive
53 if mac.kind() == hir::MacroKind::Derive || mac.kind() == hir::MacroKind::ProcMacro {
54 result.insert(name.to_string());
55 }
56 }
57 });
58 result
59}
60
61struct DeriveCompletion {
62 label: &'static str,
63 dependencies: &'static [&'static str],
64}
65
66/// Standard Rust derives and the information about their dependencies
67/// (the dependencies are needed so that the main derive don't break the compilation when added)
68const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
69 DeriveCompletion { label: "Clone", dependencies: &[] },
70 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
71 DeriveCompletion { label: "Debug", dependencies: &[] },
72 DeriveCompletion { label: "Default", dependencies: &[] },
73 DeriveCompletion { label: "Hash", dependencies: &[] },
74 DeriveCompletion { label: "PartialEq", dependencies: &[] },
75 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
76 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
77 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
78];
79
80#[cfg(test)]
81mod tests {
82 use expect_test::{expect, Expect};
83
84 use crate::{test_utils::completion_list, CompletionKind};
85
86 fn check(ra_fixture: &str, expect: Expect) {
87 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
88 expect.assert_eq(&actual);
89 }
90
91 #[test]
92 fn empty_derive_completion() {
93 check(
94 r#"
95#[derive($0)]
96struct Test {}
97 "#,
98 expect![[r#"
99 at Clone
100 at Clone, Copy
101 at Debug
102 at Default
103 at Hash
104 at PartialEq
105 at PartialEq, Eq
106 at PartialEq, PartialOrd
107 at PartialEq, Eq, PartialOrd, Ord
108 "#]],
109 );
110 }
111
112 #[test]
113 fn no_completion_for_incorrect_derive() {
114 check(
115 r#"
116#[derive{$0)]
117struct Test {}
118"#,
119 expect![[r#""#]],
120 )
121 }
122
123 #[test]
124 fn derive_with_input_completion() {
125 check(
126 r#"
127#[derive(serde::Serialize, PartialEq, $0)]
128struct Test {}
129"#,
130 expect![[r#"
131 at Clone
132 at Clone, Copy
133 at Debug
134 at Default
135 at Hash
136 at Eq
137 at PartialOrd
138 at Eq, PartialOrd, Ord
139 "#]],
140 )
141 }
142}
diff --git a/crates/ide_completion/src/completions/attribute/lint.rs b/crates/ide_completion/src/completions/attribute/lint.rs
new file mode 100644
index 000000000..115c6cfe0
--- /dev/null
+++ b/crates/ide_completion/src/completions/attribute/lint.rs
@@ -0,0 +1,154 @@
1//! Completion for lints
2use syntax::ast;
3
4use crate::{
5 context::CompletionContext,
6 item::{CompletionItem, CompletionItemKind, CompletionKind},
7 Completions,
8};
9
10pub(super) fn complete_lint(
11 acc: &mut Completions,
12 ctx: &CompletionContext,
13 derive_input: ast::TokenTree,
14 lints_completions: &[LintCompletion],
15) {
16 if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) {
17 for lint_completion in lints_completions
18 .into_iter()
19 .filter(|completion| !existing_lints.contains(completion.label))
20 {
21 let mut item = CompletionItem::new(
22 CompletionKind::Attribute,
23 ctx.source_range(),
24 lint_completion.label,
25 );
26 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description);
27 item.add_to(acc)
28 }
29 }
30}
31
32pub(crate) struct LintCompletion {
33 pub(crate) label: &'static str,
34 pub(crate) description: &'static str,
35}
36
37#[rustfmt::skip]
38pub(super) const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
39 LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# },
40 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
41 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
42 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
43 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
44 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
45 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
46 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
47 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
48 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
49 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
50 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
51 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
52 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
53 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
54 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
55 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
56 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
57 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
58 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
59 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
60 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
61 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
62 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
63 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
64 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
65 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
66 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
67 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
68 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
69 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
70 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
71 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
72 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
73 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
74 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
75 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
76 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
77 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
78 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
79 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
80 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
81 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
82 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
83 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
84 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
85 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
86 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
87 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
88 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
89 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
90 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
91 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
92 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
93 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
94 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
95 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
96 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
97 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
98 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
99 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
100 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
101 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
102 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
103 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
104 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
105 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
106 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
107 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
108 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
109 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
110 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
111 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
112 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
113 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
114 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
115 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
116 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
117 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
118 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
119 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
120 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
121 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
122 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
123 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
124 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
125 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
126 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
127 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
128 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
129 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
130 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
131 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
132 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
133 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
134 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
135 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
136 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
137 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
138 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
139 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
140 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
141 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
142 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
143 LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# },
144 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
145 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
146 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
147 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
148 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
149 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
150 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
151 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
152 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
153 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
154];
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index df8f98b5b..884fe0739 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -243,6 +243,13 @@ impl ast::Path {
243 } 243 }
244 } 244 }
245 245
246 pub fn as_single_name_ref(&self) -> Option<ast::NameRef> {
247 match self.qualifier() {
248 Some(_) => None,
249 None => self.segment()?.name_ref(),
250 }
251 }
252
246 pub fn first_qualifier_or_self(&self) -> ast::Path { 253 pub fn first_qualifier_or_self(&self) -> ast::Path {
247 successors(Some(self.clone()), ast::Path::qualifier).last().unwrap() 254 successors(Some(self.clone()), ast::Path::qualifier).last().unwrap()
248 } 255 }