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