aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/completions/attribute.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/completions/attribute.rs')
-rw-r--r--crates/completion/src/completions/attribute.rs528
1 files changed, 528 insertions, 0 deletions
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs
new file mode 100644
index 000000000..f3d669458
--- /dev/null
+++ b/crates/completion/src/completions/attribute.rs
@@ -0,0 +1,528 @@
1//! Completion for attributes
2//!
3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes.
5
6use rustc_hash::FxHashSet;
7use syntax::{ast, AstNode, SyntaxKind};
8
9use crate::{
10 context::CompletionContext,
11 generated_lint_completions::{CLIPPY_LINTS, FEATURES},
12 item::{CompletionItem, CompletionItemKind, CompletionKind},
13 Completions,
14};
15
16pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
17 if ctx.mod_declaration_under_caret.is_some() {
18 return None;
19 }
20
21 let attribute = ctx.attribute_under_caret.as_ref()?;
22 match (attribute.path(), attribute.token_tree()) {
23 (Some(path), Some(token_tree)) if path.to_string() == "derive" => {
24 complete_derive(acc, ctx, token_tree)
25 }
26 (Some(path), Some(token_tree)) if path.to_string() == "feature" => {
27 complete_lint(acc, ctx, token_tree, FEATURES);
28 }
29 (Some(path), Some(token_tree))
30 if ["allow", "warn", "deny", "forbid"]
31 .iter()
32 .any(|lint_level| lint_level == &path.to_string()) =>
33 {
34 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS);
35 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
36 }
37 (_, Some(_token_tree)) => {}
38 _ => complete_attribute_start(acc, ctx, attribute),
39 }
40 Some(())
41}
42
43fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
44 for attr_completion in ATTRIBUTES {
45 let mut item = CompletionItem::new(
46 CompletionKind::Attribute,
47 ctx.source_range(),
48 attr_completion.label,
49 )
50 .kind(CompletionItemKind::Attribute);
51
52 if let Some(lookup) = attr_completion.lookup {
53 item = item.lookup_by(lookup);
54 }
55
56 match (attr_completion.snippet, ctx.config.snippet_cap) {
57 (Some(snippet), Some(cap)) => {
58 item = item.insert_snippet(cap, snippet);
59 }
60 _ => {}
61 }
62
63 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
64 acc.add(item.build());
65 }
66 }
67}
68
69struct AttrCompletion {
70 label: &'static str,
71 lookup: Option<&'static str>,
72 snippet: Option<&'static str>,
73 prefer_inner: bool,
74}
75
76impl AttrCompletion {
77 const fn prefer_inner(self) -> AttrCompletion {
78 AttrCompletion { prefer_inner: true, ..self }
79 }
80}
81
82const fn attr(
83 label: &'static str,
84 lookup: Option<&'static str>,
85 snippet: Option<&'static str>,
86) -> AttrCompletion {
87 AttrCompletion { label, lookup, snippet, prefer_inner: false }
88}
89
90const ATTRIBUTES: &[AttrCompletion] = &[
91 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
92 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
93 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
94 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
95 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
96 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
97 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
98 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
99 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
100 // FIXME: resolve through macro resolution?
101 attr("global_allocator", None, None).prefer_inner(),
102 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
103 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
104 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
105 attr("link", None, None),
106 attr("macro_export", None, None),
107 attr("macro_use", None, None),
108 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
109 attr("no_mangle", None, None),
110 attr("no_std", None, None).prefer_inner(),
111 attr("non_exhaustive", None, None),
112 attr("panic_handler", None, None).prefer_inner(),
113 attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")),
114 attr("proc_macro", None, None),
115 attr("proc_macro_attribute", None, None),
116 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
117 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
118 .prefer_inner(),
119 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
120 attr(
121 "should_panic(…)",
122 Some("should_panic"),
123 Some(r#"should_panic(expected = "${0:reason}")"#),
124 ),
125 attr(
126 r#"target_feature = "…""#,
127 Some("target_feature"),
128 Some("target_feature = \"${0:feature}\""),
129 ),
130 attr("test", None, None),
131 attr("used", None, None),
132 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
133 attr(
134 r#"windows_subsystem = "…""#,
135 Some("windows_subsystem"),
136 Some(r#"windows_subsystem = "${0:subsystem}""#),
137 )
138 .prefer_inner(),
139];
140
141fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
142 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
143 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
144 .into_iter()
145 .filter(|completion| !existing_derives.contains(completion.label))
146 {
147 let mut label = derive_completion.label.to_owned();
148 for dependency in derive_completion
149 .dependencies
150 .into_iter()
151 .filter(|&&dependency| !existing_derives.contains(dependency))
152 {
153 label.push_str(", ");
154 label.push_str(dependency);
155 }
156 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
157 .kind(CompletionItemKind::Attribute)
158 .add_to(acc)
159 }
160
161 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
162 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), custom_derive_name)
163 .kind(CompletionItemKind::Attribute)
164 .add_to(acc)
165 }
166 }
167}
168
169fn complete_lint(
170 acc: &mut Completions,
171 ctx: &CompletionContext,
172 derive_input: ast::TokenTree,
173 lints_completions: &[LintCompletion],
174) {
175 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
176 for lint_completion in lints_completions
177 .into_iter()
178 .filter(|completion| !existing_lints.contains(completion.label))
179 {
180 CompletionItem::new(
181 CompletionKind::Attribute,
182 ctx.source_range(),
183 lint_completion.label,
184 )
185 .kind(CompletionItemKind::Attribute)
186 .detail(lint_completion.description)
187 .add_to(acc)
188 }
189 }
190}
191
192fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
193 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
194 (Some(left_paren), Some(right_paren))
195 if left_paren.kind() == SyntaxKind::L_PAREN
196 && right_paren.kind() == SyntaxKind::R_PAREN =>
197 {
198 let mut input_derives = FxHashSet::default();
199 let mut current_derive = String::new();
200 for token in derive_input
201 .syntax()
202 .children_with_tokens()
203 .filter_map(|token| token.into_token())
204 .skip_while(|token| token != &left_paren)
205 .skip(1)
206 .take_while(|token| token != &right_paren)
207 {
208 if SyntaxKind::COMMA == token.kind() {
209 if !current_derive.is_empty() {
210 input_derives.insert(current_derive);
211 current_derive = String::new();
212 }
213 } else {
214 current_derive.push_str(token.to_string().trim());
215 }
216 }
217
218 if !current_derive.is_empty() {
219 input_derives.insert(current_derive);
220 }
221 Ok(input_derives)
222 }
223 _ => Err(()),
224 }
225}
226
227fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
228 let mut result = FxHashSet::default();
229 ctx.scope.process_all_names(&mut |name, scope_def| {
230 if let hir::ScopeDef::MacroDef(mac) = scope_def {
231 if mac.is_derive_macro() {
232 result.insert(name.to_string());
233 }
234 }
235 });
236 result
237}
238
239struct DeriveCompletion {
240 label: &'static str,
241 dependencies: &'static [&'static str],
242}
243
244/// Standard Rust derives and the information about their dependencies
245/// (the dependencies are needed so that the main derive don't break the compilation when added)
246#[rustfmt::skip]
247const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
248 DeriveCompletion { label: "Clone", dependencies: &[] },
249 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
250 DeriveCompletion { label: "Debug", dependencies: &[] },
251 DeriveCompletion { label: "Default", dependencies: &[] },
252 DeriveCompletion { label: "Hash", dependencies: &[] },
253 DeriveCompletion { label: "PartialEq", dependencies: &[] },
254 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
255 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
256 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
257];
258
259pub(crate) struct LintCompletion {
260 pub(crate) label: &'static str,
261 pub(crate) description: &'static str,
262}
263
264#[rustfmt::skip]
265const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
266 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"# },
267 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
268 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
269 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
270 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
271 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
272 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
273 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
274 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
275 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
276 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
277 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
278 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
279 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
280 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
281 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
282 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
283 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
284 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
285 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
286 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
287 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
288 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
289 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
290 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
291 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
292 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
293 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
294 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
295 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
296 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
297 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
298 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
299 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
300 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
301 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
302 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
303 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
304 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
305 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
306 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
307 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
308 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
309 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
310 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
311 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
312 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
313 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
314 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
315 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
316 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
317 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
318 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
319 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
320 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
321 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
322 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
323 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
324 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
325 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
326 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
327 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
328 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
329 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
330 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
331 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
332 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
333 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
334 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
335 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
336 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
337 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
338 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
339 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
340 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
341 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
342 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
343 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
344 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
345 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
346 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
347 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
348 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
349 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
350 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
351 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
352 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
353 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
354 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
355 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
356 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
357 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
358 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
359 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
360 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
361 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
362 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
363 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
364 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
365 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
366 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
367 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
368 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
369 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
370 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"# },
371 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
372 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
373 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
374 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
375 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
376 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
377 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
378 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
379 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
380 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
381];
382
383#[cfg(test)]
384mod tests {
385 use expect_test::{expect, Expect};
386
387 use crate::{test_utils::completion_list, CompletionKind};
388
389 fn check(ra_fixture: &str, expect: Expect) {
390 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
391 expect.assert_eq(&actual);
392 }
393
394 #[test]
395 fn empty_derive_completion() {
396 check(
397 r#"
398#[derive(<|>)]
399struct Test {}
400 "#,
401 expect![[r#"
402 at Clone
403 at Copy, Clone
404 at Debug
405 at Default
406 at Eq, PartialEq
407 at Hash
408 at Ord, PartialOrd, Eq, PartialEq
409 at PartialEq
410 at PartialOrd, PartialEq
411 "#]],
412 );
413 }
414
415 #[test]
416 fn no_completion_for_incorrect_derive() {
417 check(
418 r#"
419#[derive{<|>)]
420struct Test {}
421"#,
422 expect![[r#""#]],
423 )
424 }
425
426 #[test]
427 fn derive_with_input_completion() {
428 check(
429 r#"
430#[derive(serde::Serialize, PartialEq, <|>)]
431struct Test {}
432"#,
433 expect![[r#"
434 at Clone
435 at Copy, Clone
436 at Debug
437 at Default
438 at Eq
439 at Hash
440 at Ord, PartialOrd, Eq
441 at PartialOrd
442 "#]],
443 )
444 }
445
446 #[test]
447 fn test_attribute_completion() {
448 check(
449 r#"#[<|>]"#,
450 expect![[r#"
451 at allow(…)
452 at cfg(…)
453 at cfg_attr(…)
454 at deny(…)
455 at deprecated = "…"
456 at derive(…)
457 at doc = "…"
458 at forbid(…)
459 at ignore = "…"
460 at inline(…)
461 at link
462 at link_name = "…"
463 at macro_export
464 at macro_use
465 at must_use = "…"
466 at no_mangle
467 at non_exhaustive
468 at path = "…"
469 at proc_macro
470 at proc_macro_attribute
471 at proc_macro_derive(…)
472 at repr(…)
473 at should_panic(…)
474 at target_feature = "…"
475 at test
476 at used
477 at warn(…)
478 "#]],
479 )
480 }
481
482 #[test]
483 fn test_attribute_completion_inside_nested_attr() {
484 check(r#"#[cfg(<|>)]"#, expect![[]])
485 }
486
487 #[test]
488 fn test_inner_attribute_completion() {
489 check(
490 r"#![<|>]",
491 expect![[r#"
492 at allow(…)
493 at cfg(…)
494 at cfg_attr(…)
495 at deny(…)
496 at deprecated = "…"
497 at derive(…)
498 at doc = "…"
499 at feature(…)
500 at forbid(…)
501 at global_allocator
502 at ignore = "…"
503 at inline(…)
504 at link
505 at link_name = "…"
506 at macro_export
507 at macro_use
508 at must_use = "…"
509 at no_mangle
510 at no_std
511 at non_exhaustive
512 at panic_handler
513 at path = "…"
514 at proc_macro
515 at proc_macro_attribute
516 at proc_macro_derive(…)
517 at recursion_limit = …
518 at repr(…)
519 at should_panic(…)
520 at target_feature = "…"
521 at test
522 at used
523 at warn(…)
524 at windows_subsystem = "…"
525 "#]],
526 );
527 }
528}