diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | crates/ide_completion/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/attribute.rs | 914 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/attribute/derive.rs | 142 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/attribute/lint.rs | 154 | ||||
-rw-r--r-- | crates/syntax/src/ast/node_ext.rs | 7 |
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" | |||
15 | log = "0.4.8" | 15 | log = "0.4.8" |
16 | rustc-hash = "1.1.0" | 16 | rustc-hash = "1.1.0" |
17 | either = "1.6.1" | 17 | either = "1.6.1" |
18 | once_cell = "1.7" | ||
18 | 19 | ||
19 | stdx = { path = "../stdx", version = "0.0.0" } | 20 | stdx = { path = "../stdx", version = "0.0.0" } |
20 | syntax = { path = "../syntax", version = "0.0.0" } | 21 | syntax = { 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 | ||
6 | use itertools::Itertools; | 6 | use std::mem; |
7 | use rustc_hash::FxHashSet; | 7 | |
8 | use syntax::{ast, AstNode, T}; | 8 | use once_cell::sync::Lazy; |
9 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
10 | use syntax::{ast, AstNode, NodeOrToken, SyntaxKind, T}; | ||
9 | 11 | ||
10 | use crate::{ | 12 | use crate::{ |
11 | context::CompletionContext, | 13 | context::CompletionContext, |
@@ -14,33 +16,40 @@ use crate::{ | |||
14 | Completions, | 16 | Completions, |
15 | }; | 17 | }; |
16 | 18 | ||
17 | pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 19 | mod derive; |
18 | if ctx.mod_declaration_under_caret.is_some() { | 20 | mod lint; |
19 | return None; | 21 | pub(crate) use self::lint::LintCompletion; |
20 | } | ||
21 | 22 | ||
23 | pub(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 | ||
41 | fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) { | 41 | fn 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 | ||
72 | impl AttrCompletion { | 91 | impl 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 | ||
109 | macro_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] | ||
142 | static 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 | }); | ||
199 | const 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! | ||
87 | const ATTRIBUTES: &[AttrCompletion] = &[ | 203 | const 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 | ||
156 | fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { | 272 | fn 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 | |||
189 | fn 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 | |||
211 | fn 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 | |||
245 | fn 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 | |||
258 | struct 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) | ||
265 | const 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 | ||
277 | pub(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] | ||
283 | const 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)] |
402 | mod tests { | 300 | mod 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)] | ||
417 | struct 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#" |
438 | struct 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)] | ||
449 | struct 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 | ||
2 | use itertools::Itertools; | ||
3 | use rustc_hash::FxHashSet; | ||
4 | use syntax::ast; | ||
5 | |||
6 | use crate::{ | ||
7 | context::CompletionContext, | ||
8 | item::{CompletionItem, CompletionItemKind, CompletionKind}, | ||
9 | Completions, | ||
10 | }; | ||
11 | |||
12 | pub(super) fn complete_derive( | ||
13 | acc: &mut Completions, | ||
14 | ctx: &CompletionContext, | ||
15 | derive_input: ast::TokenTree, | ||
16 | ) { | ||
17 | if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) { | ||
18 | for derive_completion in DEFAULT_DERIVE_COMPLETIONS | ||
19 | .iter() | ||
20 | .filter(|completion| !existing_derives.contains(completion.label)) | ||
21 | { | ||
22 | let mut components = vec![derive_completion.label]; | ||
23 | components.extend( | ||
24 | derive_completion | ||
25 | .dependencies | ||
26 | .iter() | ||
27 | .filter(|&&dependency| !existing_derives.contains(dependency)), | ||
28 | ); | ||
29 | let lookup = components.join(", "); | ||
30 | let label = components.iter().rev().join(", "); | ||
31 | let mut item = | ||
32 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); | ||
33 | item.lookup_by(lookup).kind(CompletionItemKind::Attribute); | ||
34 | item.add_to(acc); | ||
35 | } | ||
36 | |||
37 | for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { | ||
38 | let mut item = CompletionItem::new( | ||
39 | CompletionKind::Attribute, | ||
40 | ctx.source_range(), | ||
41 | custom_derive_name, | ||
42 | ); | ||
43 | item.kind(CompletionItemKind::Attribute); | ||
44 | item.add_to(acc); | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | fn 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 | |||
61 | struct 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) | ||
68 | const 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)] | ||
81 | mod 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)] | ||
96 | struct 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)] | ||
117 | struct 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)] | ||
128 | struct 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 | ||
2 | use syntax::ast; | ||
3 | |||
4 | use crate::{ | ||
5 | context::CompletionContext, | ||
6 | item::{CompletionItem, CompletionItemKind, CompletionKind}, | ||
7 | Completions, | ||
8 | }; | ||
9 | |||
10 | pub(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 | |||
32 | pub(crate) struct LintCompletion { | ||
33 | pub(crate) label: &'static str, | ||
34 | pub(crate) description: &'static str, | ||
35 | } | ||
36 | |||
37 | #[rustfmt::skip] | ||
38 | pub(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 | } |