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