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