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