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