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