]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/lifetime.rs
Merge #9558
[rust.git] / crates / ide_completion / src / completions / lifetime.rs
1 //! Completes lifetimes and labels.
2 //!
3 //! These completions work a bit differently in that they are only shown when what the user types
4 //! has a `'` preceding it, as our fake syntax tree is invalid otherwise (due to us not inserting
5 //! a lifetime but an ident for obvious reasons).
6 //! Due to this all the tests for lifetimes and labels live in this module for the time being as
7 //! there is no value in lifting these out into the outline module test since they will either not
8 //! show up for normal completions, or they won't show completions other than lifetimes depending
9 //! on the fixture input.
10 use hir::ScopeDef;
11
12 use crate::{completions::Completions, context::CompletionContext};
13
14 /// Completes lifetimes.
15 pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) {
16     if !ctx.lifetime_allowed {
17         return;
18     }
19     let lp_string;
20     let param_lifetime = match (
21         &ctx.lifetime_syntax,
22         ctx.lifetime_param_syntax.as_ref().and_then(|lp| lp.lifetime()),
23     ) {
24         (Some(lt), Some(lp)) if lp == lt.clone() => return,
25         (Some(_), Some(lp)) => {
26             lp_string = lp.to_string();
27             Some(&*lp_string)
28         }
29         _ => None,
30     };
31
32     ctx.scope.process_all_names(&mut |name, res| {
33         if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
34             if param_lifetime != Some(&*name.to_string()) {
35                 acc.add_resolution(ctx, name, &res);
36             }
37         }
38     });
39     if param_lifetime.is_none() {
40         acc.add_static_lifetime(ctx);
41     }
42 }
43
44 /// Completes labels.
45 pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
46     if !ctx.is_label_ref {
47         return;
48     }
49     ctx.scope.process_all_names(&mut |name, res| {
50         if let ScopeDef::Label(_) = res {
51             acc.add_resolution(ctx, name, &res);
52         }
53     });
54 }
55
56 #[cfg(test)]
57 mod tests {
58     use expect_test::{expect, Expect};
59
60     use crate::tests::{check_edit, completion_list};
61
62     fn check(ra_fixture: &str, expect: Expect) {
63         let actual = completion_list(ra_fixture);
64         expect.assert_eq(&actual);
65     }
66
67     #[test]
68     fn check_lifetime_edit() {
69         check_edit(
70             "'lifetime",
71             r#"
72 fn func<'lifetime>(foo: &'li$0) {}
73 "#,
74             r#"
75 fn func<'lifetime>(foo: &'lifetime) {}
76 "#,
77         );
78         cov_mark::check!(completes_if_lifetime_without_idents);
79         check_edit(
80             "'lifetime",
81             r#"
82 fn func<'lifetime>(foo: &'$0) {}
83 "#,
84             r#"
85 fn func<'lifetime>(foo: &'lifetime) {}
86 "#,
87         );
88     }
89
90     #[test]
91     fn complete_lifetime_in_ref() {
92         check(
93             r#"
94 fn foo<'lifetime>(foo: &'a$0 usize) {}
95 "#,
96             expect![[r#"
97                 lt 'lifetime
98                 lt 'static
99             "#]],
100         );
101     }
102
103     #[test]
104     fn complete_lifetime_in_ref_missing_ty() {
105         check(
106             r#"
107 fn foo<'lifetime>(foo: &'a$0) {}
108 "#,
109             expect![[r#"
110                 lt 'lifetime
111                 lt 'static
112             "#]],
113         );
114     }
115     #[test]
116     fn complete_lifetime_in_self_ref() {
117         check(
118             r#"
119 struct Foo;
120 impl<'impl> Foo {
121     fn foo<'func>(&'a$0 self) {}
122 }
123 "#,
124             expect![[r#"
125                 lt 'func
126                 lt 'impl
127                 lt 'static
128             "#]],
129         );
130     }
131
132     #[test]
133     fn complete_lifetime_in_arg_list() {
134         check(
135             r#"
136 struct Foo<'lt>;
137 fn foo<'lifetime>(_: Foo<'a$0>) {}
138 "#,
139             expect![[r#"
140                 lt 'lifetime
141                 lt 'static
142             "#]],
143         );
144     }
145
146     #[test]
147     fn complete_lifetime_in_where_pred() {
148         check(
149             r#"
150 fn foo2<'lifetime, T>() where 'a$0 {}
151 "#,
152             expect![[r#"
153                 lt 'lifetime
154                 lt 'static
155             "#]],
156         );
157     }
158
159     #[test]
160     fn complete_lifetime_in_ty_bound() {
161         check(
162             r#"
163 fn foo2<'lifetime, T>() where T: 'a$0 {}
164 "#,
165             expect![[r#"
166                 lt 'lifetime
167                 lt 'static
168             "#]],
169         );
170         check(
171             r#"
172 fn foo2<'lifetime, T>() where T: Trait<'a$0> {}
173 "#,
174             expect![[r#"
175                 lt 'lifetime
176                 lt 'static
177             "#]],
178         );
179     }
180
181     #[test]
182     fn dont_complete_lifetime_in_assoc_ty_bound() {
183         check(
184             r#"
185 fn foo2<'lifetime, T>() where T: Trait<Item = 'a$0> {}
186 "#,
187             expect![[r#""#]],
188         );
189     }
190
191     #[test]
192     fn complete_lifetime_in_param_list() {
193         check(
194             r#"
195 fn foo<'a$0>() {}
196 "#,
197             expect![[r#""#]],
198         );
199         check(
200             r#"
201 fn foo<'footime, 'lifetime: 'a$0>() {}
202 "#,
203             expect![[r#"
204                 lt 'footime
205             "#]],
206         );
207     }
208
209     #[test]
210     fn check_label_edit() {
211         check_edit(
212             "'label",
213             r#"
214 fn foo() {
215     'label: loop {
216         break '$0
217     }
218 }
219 "#,
220             r#"
221 fn foo() {
222     'label: loop {
223         break 'label
224     }
225 }
226 "#,
227         );
228     }
229
230     #[test]
231     fn complete_label_in_loop() {
232         check(
233             r#"
234 fn foo() {
235     'foop: loop {
236         break '$0
237     }
238 }
239 "#,
240             expect![[r#"
241                 lb 'foop
242             "#]],
243         );
244         check(
245             r#"
246 fn foo() {
247     'foop: loop {
248         continue '$0
249     }
250 }
251 "#,
252             expect![[r#"
253                 lb 'foop
254             "#]],
255         );
256     }
257
258     #[test]
259     fn complete_label_in_block_nested() {
260         check(
261             r#"
262 fn foo() {
263     'foop: {
264         'baap: {
265             break '$0
266         }
267     }
268 }
269 "#,
270             expect![[r#"
271                 lb 'baap
272                 lb 'foop
273             "#]],
274         );
275     }
276
277     #[test]
278     fn complete_label_in_loop_with_value() {
279         check(
280             r#"
281 fn foo() {
282     'foop: loop {
283         break '$0 i32;
284     }
285 }
286 "#,
287             expect![[r#"
288                 lb 'foop
289             "#]],
290         );
291     }
292
293     #[test]
294     fn complete_label_in_while_cond() {
295         check(
296             r#"
297 fn foo() {
298     'outer: while { 'inner: loop { break '$0 } } {}
299 }
300 "#,
301             expect![[r#"
302                 lb 'inner
303                 lb 'outer
304             "#]],
305         );
306     }
307
308     #[test]
309     fn complete_label_in_for_iterable() {
310         check(
311             r#"
312 fn foo() {
313     'outer: for _ in [{ 'inner: loop { break '$0 } }] {}
314 }
315 "#,
316             expect![[r#"
317                 lb 'inner
318             "#]],
319         );
320     }
321 }