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