]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/attribute/derive.rs
Move test_utils into tests module
[rust.git] / crates / ide_completion / src / completions / attribute / derive.rs
1 //! Completion for derives
2 use hir::HasAttrs;
3 use itertools::Itertools;
4 use rustc_hash::FxHashMap;
5 use syntax::ast;
6
7 use crate::{
8     context::CompletionContext,
9     item::{CompletionItem, CompletionItemKind, CompletionKind},
10     Completions,
11 };
12
13 pub(super) fn complete_derive(
14     acc: &mut Completions,
15     ctx: &CompletionContext,
16     derive_input: ast::TokenTree,
17 ) {
18     if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) {
19         for (derive, docs) in get_derive_names_in_scope(ctx) {
20             let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS
21                 .iter()
22                 .find(|derive_completion| derive_completion.label == derive)
23             {
24                 let mut components = vec![derive_completion.label];
25                 components.extend(
26                     derive_completion
27                         .dependencies
28                         .iter()
29                         .filter(|&&dependency| !existing_derives.contains(dependency)),
30                 );
31                 let lookup = components.join(", ");
32                 let label = components.iter().rev().join(", ");
33                 (label, Some(lookup))
34             } else if existing_derives.contains(&derive) {
35                 continue;
36             } else {
37                 (derive, None)
38             };
39             let mut item =
40                 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
41             item.kind(CompletionItemKind::Attribute);
42             if let Some(docs) = docs {
43                 item.documentation(docs);
44             }
45             if let Some(lookup) = lookup {
46                 item.lookup_by(lookup);
47             }
48             item.add_to(acc);
49         }
50     }
51 }
52
53 fn get_derive_names_in_scope(
54     ctx: &CompletionContext,
55 ) -> FxHashMap<String, Option<hir::Documentation>> {
56     let mut result = FxHashMap::default();
57     ctx.scope.process_all_names(&mut |name, scope_def| {
58         if let hir::ScopeDef::MacroDef(mac) = scope_def {
59             if mac.kind() == hir::MacroKind::Derive {
60                 result.insert(name.to_string(), mac.docs(ctx.db));
61             }
62         }
63     });
64     result
65 }
66
67 struct DeriveDependencies {
68     label: &'static str,
69     dependencies: &'static [&'static str],
70 }
71
72 /// Standard Rust derives that have dependencies
73 /// (the dependencies are needed so that the main derive don't break the compilation when added)
74 const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[
75     DeriveDependencies { label: "Copy", dependencies: &["Clone"] },
76     DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] },
77     DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
78     DeriveDependencies { label: "PartialOrd", dependencies: &["PartialEq"] },
79 ];
80
81 #[cfg(test)]
82 mod tests {
83     use expect_test::{expect, Expect};
84
85     use crate::{tests::filtered_completion_list, CompletionKind};
86
87     fn check(ra_fixture: &str, expect: Expect) {
88         let builtin_derives = r#"
89 #[rustc_builtin_macro]
90 pub macro Clone {}
91 #[rustc_builtin_macro]
92 pub macro Copy {}
93 #[rustc_builtin_macro]
94 pub macro Default {}
95 #[rustc_builtin_macro]
96 pub macro Debug {}
97 #[rustc_builtin_macro]
98 pub macro Hash {}
99 #[rustc_builtin_macro]
100 pub macro PartialEq {}
101 #[rustc_builtin_macro]
102 pub macro Eq {}
103 #[rustc_builtin_macro]
104 pub macro PartialOrd {}
105 #[rustc_builtin_macro]
106 pub macro Ord {}
107
108 "#;
109         let actual = filtered_completion_list(
110             &format!("{} {}", builtin_derives, ra_fixture),
111             CompletionKind::Attribute,
112         );
113         expect.assert_eq(&actual);
114     }
115
116     #[test]
117     fn no_completion_for_incorrect_derive() {
118         check(r#"#[derive{$0)] struct Test;"#, expect![[]])
119     }
120
121     #[test]
122     fn empty_derive() {
123         check(
124             r#"#[derive($0)] struct Test;"#,
125             expect![[r#"
126             at PartialEq
127             at Default
128             at PartialEq, Eq
129             at PartialEq, Eq, PartialOrd, Ord
130             at Clone, Copy
131             at Debug
132             at Clone
133             at Hash
134             at PartialEq, PartialOrd
135         "#]],
136         );
137     }
138
139     #[test]
140     fn derive_with_input() {
141         check(
142             r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#,
143             expect![[r#"
144                 at Default
145                 at Eq
146                 at Eq, PartialOrd, Ord
147                 at Clone, Copy
148                 at Debug
149                 at Clone
150                 at Hash
151                 at PartialOrd
152             "#]],
153         )
154     }
155
156     #[test]
157     fn derive_with_input2() {
158         check(
159             r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#,
160             expect![[r#"
161                 at Default
162                 at Eq
163                 at Eq, PartialOrd, Ord
164                 at Clone, Copy
165                 at Debug
166                 at Clone
167                 at Hash
168                 at PartialOrd
169             "#]],
170         )
171     }
172 }