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