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