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