diff options
Diffstat (limited to 'crates/ide_assists/src/utils')
-rw-r--r-- | crates/ide_assists/src/utils/suggest_name.rs | 770 |
1 files changed, 770 insertions, 0 deletions
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs new file mode 100644 index 000000000..345e9af40 --- /dev/null +++ b/crates/ide_assists/src/utils/suggest_name.rs | |||
@@ -0,0 +1,770 @@ | |||
1 | //! This module contains functions to suggest names for expressions, functions and other items | ||
2 | |||
3 | use hir::Semantics; | ||
4 | use ide_db::RootDatabase; | ||
5 | use itertools::Itertools; | ||
6 | use stdx::to_lower_snake_case; | ||
7 | use syntax::{ | ||
8 | ast::{self, NameOwner}, | ||
9 | match_ast, AstNode, | ||
10 | }; | ||
11 | |||
12 | /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` | ||
13 | const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"]; | ||
14 | /// Identifier names that won't be suggested, ever | ||
15 | /// | ||
16 | /// **NOTE**: they all must be snake lower case | ||
17 | const USELESS_NAMES: &[&str] = | ||
18 | &["new", "default", "option", "some", "none", "ok", "err", "str", "string"]; | ||
19 | /// Generic types replaced by their first argument | ||
20 | /// | ||
21 | /// # Examples | ||
22 | /// `Option<Name>` -> `Name` | ||
23 | /// `Result<User, Error>` -> `User` | ||
24 | const WRAPPER_TYPES: &[&str] = &["Box", "Option", "Result"]; | ||
25 | /// Prefixes to strip from methods names | ||
26 | /// | ||
27 | /// # Examples | ||
28 | /// `vec.as_slice()` -> `slice` | ||
29 | /// `args.into_config()` -> `config` | ||
30 | /// `bytes.to_vec()` -> `vec` | ||
31 | const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"]; | ||
32 | |||
33 | /// Suggest name of variable for given expression | ||
34 | /// | ||
35 | /// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name. | ||
36 | /// I.e. it doesn't look for names in scope. | ||
37 | /// | ||
38 | /// # Current implementation | ||
39 | /// | ||
40 | /// In current implementation, the function tries to get the name from | ||
41 | /// the following sources: | ||
42 | /// | ||
43 | /// * if expr is an argument to function/method, use paramter name | ||
44 | /// * if expr is a function/method call, use function name | ||
45 | /// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names) | ||
46 | /// * fallback: `var_name` | ||
47 | /// | ||
48 | /// It also applies heuristics to filter out less informative names | ||
49 | /// | ||
50 | /// Currently it sticks to the first name found. | ||
51 | pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { | ||
52 | from_param(expr, sema) | ||
53 | .or_else(|| from_call(expr)) | ||
54 | .or_else(|| from_type(expr, sema)) | ||
55 | .unwrap_or_else(|| "var_name".to_string()) | ||
56 | } | ||
57 | |||
58 | fn normalize(name: &str) -> Option<String> { | ||
59 | let name = to_lower_snake_case(name); | ||
60 | |||
61 | if USELESS_NAMES.contains(&name.as_str()) { | ||
62 | return None; | ||
63 | } | ||
64 | |||
65 | if !is_valid_name(&name) { | ||
66 | return None; | ||
67 | } | ||
68 | |||
69 | Some(name) | ||
70 | } | ||
71 | |||
72 | fn is_valid_name(name: &str) -> bool { | ||
73 | match syntax::lex_single_syntax_kind(name) { | ||
74 | Some((syntax::SyntaxKind::IDENT, _error)) => true, | ||
75 | _ => false, | ||
76 | } | ||
77 | } | ||
78 | |||
79 | fn from_call(expr: &ast::Expr) -> Option<String> { | ||
80 | from_func_call(expr).or_else(|| from_method_call(expr)) | ||
81 | } | ||
82 | |||
83 | fn from_func_call(expr: &ast::Expr) -> Option<String> { | ||
84 | let call = match expr { | ||
85 | ast::Expr::CallExpr(call) => call, | ||
86 | _ => return None, | ||
87 | }; | ||
88 | let func = match call.expr()? { | ||
89 | ast::Expr::PathExpr(path) => path, | ||
90 | _ => return None, | ||
91 | }; | ||
92 | let ident = func.path()?.segment()?.name_ref()?.ident_token()?; | ||
93 | normalize(ident.text()) | ||
94 | } | ||
95 | |||
96 | fn from_method_call(expr: &ast::Expr) -> Option<String> { | ||
97 | let method = match expr { | ||
98 | ast::Expr::MethodCallExpr(call) => call, | ||
99 | _ => return None, | ||
100 | }; | ||
101 | let ident = method.name_ref()?.ident_token()?; | ||
102 | let name = normalize(ident.text())?; | ||
103 | |||
104 | for prefix in USELESS_METHOD_PREFIXES { | ||
105 | if let Some(suffix) = name.strip_prefix(prefix) { | ||
106 | return Some(suffix.to_string()); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | Some(name) | ||
111 | } | ||
112 | |||
113 | fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { | ||
114 | let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?; | ||
115 | let args_parent = arg_list.syntax().parent()?; | ||
116 | let func = match_ast! { | ||
117 | match args_parent { | ||
118 | ast::CallExpr(call) => { | ||
119 | let func = call.expr()?; | ||
120 | let func_ty = sema.type_of_expr(&func)?; | ||
121 | func_ty.as_callable(sema.db)? | ||
122 | }, | ||
123 | ast::MethodCallExpr(method) => sema.resolve_method_call_as_callable(&method)?, | ||
124 | _ => return None, | ||
125 | } | ||
126 | }; | ||
127 | |||
128 | let (idx, _) = arg_list.args().find_position(|it| it == expr).unwrap(); | ||
129 | let (pat, _) = func.params(sema.db).into_iter().nth(idx)?; | ||
130 | let pat = match pat? { | ||
131 | either::Either::Right(pat) => pat, | ||
132 | _ => return None, | ||
133 | }; | ||
134 | let name = var_name_from_pat(&pat)?; | ||
135 | normalize(&name.to_string()) | ||
136 | } | ||
137 | |||
138 | fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> { | ||
139 | match pat { | ||
140 | ast::Pat::IdentPat(var) => var.name(), | ||
141 | ast::Pat::RefPat(ref_pat) => var_name_from_pat(&ref_pat.pat()?), | ||
142 | ast::Pat::BoxPat(box_pat) => var_name_from_pat(&box_pat.pat()?), | ||
143 | _ => None, | ||
144 | } | ||
145 | } | ||
146 | |||
147 | fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { | ||
148 | let ty = sema.type_of_expr(expr)?; | ||
149 | let ty = ty.remove_ref().unwrap_or(ty); | ||
150 | |||
151 | name_of_type(&ty, sema.db) | ||
152 | } | ||
153 | |||
154 | fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> { | ||
155 | let name = if let Some(adt) = ty.as_adt() { | ||
156 | let name = adt.name(db).to_string(); | ||
157 | |||
158 | if WRAPPER_TYPES.contains(&name.as_str()) { | ||
159 | let inner_ty = ty.type_parameters().next()?; | ||
160 | return name_of_type(&inner_ty, db); | ||
161 | } | ||
162 | |||
163 | name | ||
164 | } else if let Some(trait_) = ty.as_dyn_trait() { | ||
165 | trait_name(&trait_, db)? | ||
166 | } else if let Some(traits) = ty.as_impl_traits(db) { | ||
167 | let mut iter = traits.into_iter().filter_map(|t| trait_name(&t, db)); | ||
168 | let name = iter.next()?; | ||
169 | if iter.next().is_some() { | ||
170 | return None; | ||
171 | } | ||
172 | name | ||
173 | } else { | ||
174 | return None; | ||
175 | }; | ||
176 | normalize(&name) | ||
177 | } | ||
178 | |||
179 | fn trait_name(trait_: &hir::Trait, db: &RootDatabase) -> Option<String> { | ||
180 | let name = trait_.name(db).to_string(); | ||
181 | if USELESS_TRAITS.contains(&name.as_str()) { | ||
182 | return None; | ||
183 | } | ||
184 | Some(name) | ||
185 | } | ||
186 | |||
187 | #[cfg(test)] | ||
188 | mod tests { | ||
189 | use super::*; | ||
190 | |||
191 | use crate::tests::check_name_suggestion; | ||
192 | |||
193 | mod from_func_call { | ||
194 | use super::*; | ||
195 | |||
196 | #[test] | ||
197 | fn no_args() { | ||
198 | check_name_suggestion( | ||
199 | |e, _| from_func_call(e), | ||
200 | r#" | ||
201 | fn foo() { | ||
202 | $0bar()$0 | ||
203 | }"#, | ||
204 | "bar", | ||
205 | ); | ||
206 | } | ||
207 | |||
208 | #[test] | ||
209 | fn single_arg() { | ||
210 | check_name_suggestion( | ||
211 | |e, _| from_func_call(e), | ||
212 | r#" | ||
213 | fn foo() { | ||
214 | $0bar(1)$0 | ||
215 | }"#, | ||
216 | "bar", | ||
217 | ); | ||
218 | } | ||
219 | |||
220 | #[test] | ||
221 | fn many_args() { | ||
222 | check_name_suggestion( | ||
223 | |e, _| from_func_call(e), | ||
224 | r#" | ||
225 | fn foo() { | ||
226 | $0bar(1, 2, 3)$0 | ||
227 | }"#, | ||
228 | "bar", | ||
229 | ); | ||
230 | } | ||
231 | |||
232 | #[test] | ||
233 | fn path() { | ||
234 | check_name_suggestion( | ||
235 | |e, _| from_func_call(e), | ||
236 | r#" | ||
237 | fn foo() { | ||
238 | $0i32::bar(1, 2, 3)$0 | ||
239 | }"#, | ||
240 | "bar", | ||
241 | ); | ||
242 | } | ||
243 | |||
244 | #[test] | ||
245 | fn generic_params() { | ||
246 | check_name_suggestion( | ||
247 | |e, _| from_func_call(e), | ||
248 | r#" | ||
249 | fn foo() { | ||
250 | $0bar::<i32>(1, 2, 3)$0 | ||
251 | }"#, | ||
252 | "bar", | ||
253 | ); | ||
254 | } | ||
255 | } | ||
256 | |||
257 | mod from_method_call { | ||
258 | use super::*; | ||
259 | |||
260 | #[test] | ||
261 | fn no_args() { | ||
262 | check_name_suggestion( | ||
263 | |e, _| from_method_call(e), | ||
264 | r#" | ||
265 | fn foo() { | ||
266 | $0bar.frobnicate()$0 | ||
267 | }"#, | ||
268 | "frobnicate", | ||
269 | ); | ||
270 | } | ||
271 | |||
272 | #[test] | ||
273 | fn generic_params() { | ||
274 | check_name_suggestion( | ||
275 | |e, _| from_method_call(e), | ||
276 | r#" | ||
277 | fn foo() { | ||
278 | $0bar.frobnicate::<i32, u32>()$0 | ||
279 | }"#, | ||
280 | "frobnicate", | ||
281 | ); | ||
282 | } | ||
283 | |||
284 | #[test] | ||
285 | fn to_name() { | ||
286 | check_name_suggestion( | ||
287 | |e, _| from_method_call(e), | ||
288 | r#" | ||
289 | struct Args; | ||
290 | struct Config; | ||
291 | impl Args { | ||
292 | fn to_config(&self) -> Config {} | ||
293 | } | ||
294 | fn foo() { | ||
295 | $0Args.to_config()$0; | ||
296 | }"#, | ||
297 | "config", | ||
298 | ); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | mod from_param { | ||
303 | use crate::tests::check_name_suggestion_not_applicable; | ||
304 | |||
305 | use super::*; | ||
306 | |||
307 | #[test] | ||
308 | fn plain_func() { | ||
309 | check_name_suggestion( | ||
310 | from_param, | ||
311 | r#" | ||
312 | fn bar(n: i32, m: u32); | ||
313 | fn foo() { | ||
314 | bar($01$0, 2) | ||
315 | }"#, | ||
316 | "n", | ||
317 | ); | ||
318 | } | ||
319 | |||
320 | #[test] | ||
321 | fn mut_param() { | ||
322 | check_name_suggestion( | ||
323 | from_param, | ||
324 | r#" | ||
325 | fn bar(mut n: i32, m: u32); | ||
326 | fn foo() { | ||
327 | bar($01$0, 2) | ||
328 | }"#, | ||
329 | "n", | ||
330 | ); | ||
331 | } | ||
332 | |||
333 | #[test] | ||
334 | fn func_does_not_exist() { | ||
335 | check_name_suggestion_not_applicable( | ||
336 | from_param, | ||
337 | r#" | ||
338 | fn foo() { | ||
339 | bar($01$0, 2) | ||
340 | }"#, | ||
341 | ); | ||
342 | } | ||
343 | |||
344 | #[test] | ||
345 | fn unnamed_param() { | ||
346 | check_name_suggestion_not_applicable( | ||
347 | from_param, | ||
348 | r#" | ||
349 | fn bar(_: i32, m: u32); | ||
350 | fn foo() { | ||
351 | bar($01$0, 2) | ||
352 | }"#, | ||
353 | ); | ||
354 | } | ||
355 | |||
356 | #[test] | ||
357 | fn tuple_pat() { | ||
358 | check_name_suggestion_not_applicable( | ||
359 | from_param, | ||
360 | r#" | ||
361 | fn bar((n, k): (i32, i32), m: u32); | ||
362 | fn foo() { | ||
363 | bar($0(1, 2)$0, 3) | ||
364 | }"#, | ||
365 | ); | ||
366 | } | ||
367 | |||
368 | #[test] | ||
369 | fn ref_pat() { | ||
370 | check_name_suggestion( | ||
371 | from_param, | ||
372 | r#" | ||
373 | fn bar(&n: &i32, m: u32); | ||
374 | fn foo() { | ||
375 | bar($0&1$0, 3) | ||
376 | }"#, | ||
377 | "n", | ||
378 | ); | ||
379 | } | ||
380 | |||
381 | #[test] | ||
382 | fn box_pat() { | ||
383 | check_name_suggestion( | ||
384 | from_param, | ||
385 | r#" | ||
386 | fn bar(box n: &i32, m: u32); | ||
387 | fn foo() { | ||
388 | bar($01$0, 3) | ||
389 | }"#, | ||
390 | "n", | ||
391 | ); | ||
392 | } | ||
393 | |||
394 | #[test] | ||
395 | fn param_out_of_index() { | ||
396 | check_name_suggestion_not_applicable( | ||
397 | from_param, | ||
398 | r#" | ||
399 | fn bar(n: i32, m: u32); | ||
400 | fn foo() { | ||
401 | bar(1, 2, $03$0) | ||
402 | }"#, | ||
403 | ); | ||
404 | } | ||
405 | |||
406 | #[test] | ||
407 | fn generic_param_resolved() { | ||
408 | check_name_suggestion( | ||
409 | from_param, | ||
410 | r#" | ||
411 | fn bar<T>(n: T, m: u32); | ||
412 | fn foo() { | ||
413 | bar($01$0, 2) | ||
414 | }"#, | ||
415 | "n", | ||
416 | ); | ||
417 | } | ||
418 | |||
419 | #[test] | ||
420 | fn generic_param_unresolved() { | ||
421 | check_name_suggestion( | ||
422 | from_param, | ||
423 | r#" | ||
424 | fn bar<T>(n: T, m: u32); | ||
425 | fn foo<T>(x: T) { | ||
426 | bar($0x$0, 2) | ||
427 | }"#, | ||
428 | "n", | ||
429 | ); | ||
430 | } | ||
431 | |||
432 | #[test] | ||
433 | fn method() { | ||
434 | check_name_suggestion( | ||
435 | from_param, | ||
436 | r#" | ||
437 | struct S; | ||
438 | impl S { | ||
439 | fn bar(&self, n: i32, m: u32); | ||
440 | } | ||
441 | fn foo() { | ||
442 | S.bar($01$0, 2) | ||
443 | }"#, | ||
444 | "n", | ||
445 | ); | ||
446 | } | ||
447 | |||
448 | #[test] | ||
449 | fn method_ufcs() { | ||
450 | check_name_suggestion( | ||
451 | from_param, | ||
452 | r#" | ||
453 | struct S; | ||
454 | impl S { | ||
455 | fn bar(&self, n: i32, m: u32); | ||
456 | } | ||
457 | fn foo() { | ||
458 | S::bar(&S, $01$0, 2) | ||
459 | }"#, | ||
460 | "n", | ||
461 | ); | ||
462 | } | ||
463 | |||
464 | #[test] | ||
465 | fn method_self() { | ||
466 | check_name_suggestion_not_applicable( | ||
467 | from_param, | ||
468 | r#" | ||
469 | struct S; | ||
470 | impl S { | ||
471 | fn bar(&self, n: i32, m: u32); | ||
472 | } | ||
473 | fn foo() { | ||
474 | S::bar($0&S$0, 1, 2) | ||
475 | }"#, | ||
476 | ); | ||
477 | } | ||
478 | |||
479 | #[test] | ||
480 | fn method_self_named() { | ||
481 | check_name_suggestion( | ||
482 | from_param, | ||
483 | r#" | ||
484 | struct S; | ||
485 | impl S { | ||
486 | fn bar(strukt: &Self, n: i32, m: u32); | ||
487 | } | ||
488 | fn foo() { | ||
489 | S::bar($0&S$0, 1, 2) | ||
490 | }"#, | ||
491 | "strukt", | ||
492 | ); | ||
493 | } | ||
494 | } | ||
495 | |||
496 | mod from_type { | ||
497 | use crate::tests::check_name_suggestion_not_applicable; | ||
498 | |||
499 | use super::*; | ||
500 | |||
501 | #[test] | ||
502 | fn i32() { | ||
503 | check_name_suggestion_not_applicable( | ||
504 | from_type, | ||
505 | r#" | ||
506 | fn foo() { | ||
507 | let _: i32 = $01$0; | ||
508 | }"#, | ||
509 | ); | ||
510 | } | ||
511 | |||
512 | #[test] | ||
513 | fn u64() { | ||
514 | check_name_suggestion_not_applicable( | ||
515 | from_type, | ||
516 | r#" | ||
517 | fn foo() { | ||
518 | let _: u64 = $01$0; | ||
519 | }"#, | ||
520 | ); | ||
521 | } | ||
522 | |||
523 | #[test] | ||
524 | fn bool() { | ||
525 | check_name_suggestion_not_applicable( | ||
526 | from_type, | ||
527 | r#" | ||
528 | fn foo() { | ||
529 | let _: bool = $0true$0; | ||
530 | }"#, | ||
531 | ); | ||
532 | } | ||
533 | |||
534 | #[test] | ||
535 | fn struct_unit() { | ||
536 | check_name_suggestion( | ||
537 | from_type, | ||
538 | r#" | ||
539 | struct Seed; | ||
540 | fn foo() { | ||
541 | let _ = $0Seed$0; | ||
542 | }"#, | ||
543 | "seed", | ||
544 | ); | ||
545 | } | ||
546 | |||
547 | #[test] | ||
548 | fn struct_unit_to_snake() { | ||
549 | check_name_suggestion( | ||
550 | from_type, | ||
551 | r#" | ||
552 | struct SeedState; | ||
553 | fn foo() { | ||
554 | let _ = $0SeedState$0; | ||
555 | }"#, | ||
556 | "seed_state", | ||
557 | ); | ||
558 | } | ||
559 | |||
560 | #[test] | ||
561 | fn struct_single_arg() { | ||
562 | check_name_suggestion( | ||
563 | from_type, | ||
564 | r#" | ||
565 | struct Seed(u32); | ||
566 | fn foo() { | ||
567 | let _ = $0Seed(0)$0; | ||
568 | }"#, | ||
569 | "seed", | ||
570 | ); | ||
571 | } | ||
572 | |||
573 | #[test] | ||
574 | fn struct_with_fields() { | ||
575 | check_name_suggestion( | ||
576 | from_type, | ||
577 | r#" | ||
578 | struct Seed { value: u32 } | ||
579 | fn foo() { | ||
580 | let _ = $0Seed { value: 0 }$0; | ||
581 | }"#, | ||
582 | "seed", | ||
583 | ); | ||
584 | } | ||
585 | |||
586 | #[test] | ||
587 | fn enum_() { | ||
588 | check_name_suggestion( | ||
589 | from_type, | ||
590 | r#" | ||
591 | enum Kind { A, B } | ||
592 | fn foo() { | ||
593 | let _ = $0Kind::A$0; | ||
594 | }"#, | ||
595 | "kind", | ||
596 | ); | ||
597 | } | ||
598 | |||
599 | #[test] | ||
600 | fn enum_generic_resolved() { | ||
601 | check_name_suggestion( | ||
602 | from_type, | ||
603 | r#" | ||
604 | enum Kind<T> { A(T), B } | ||
605 | fn foo() { | ||
606 | let _ = $0Kind::A(1)$0; | ||
607 | }"#, | ||
608 | "kind", | ||
609 | ); | ||
610 | } | ||
611 | |||
612 | #[test] | ||
613 | fn enum_generic_unresolved() { | ||
614 | check_name_suggestion( | ||
615 | from_type, | ||
616 | r#" | ||
617 | enum Kind<T> { A(T), B } | ||
618 | fn foo<T>(x: T) { | ||
619 | let _ = $0Kind::A(x)$0; | ||
620 | }"#, | ||
621 | "kind", | ||
622 | ); | ||
623 | } | ||
624 | |||
625 | #[test] | ||
626 | fn dyn_trait() { | ||
627 | check_name_suggestion( | ||
628 | from_type, | ||
629 | r#" | ||
630 | trait DynHandler {} | ||
631 | fn bar() -> dyn DynHandler {} | ||
632 | fn foo() { | ||
633 | $0bar()$0; | ||
634 | }"#, | ||
635 | "dyn_handler", | ||
636 | ); | ||
637 | } | ||
638 | |||
639 | #[test] | ||
640 | fn impl_trait() { | ||
641 | check_name_suggestion( | ||
642 | from_type, | ||
643 | r#" | ||
644 | trait StaticHandler {} | ||
645 | fn bar() -> impl StaticHandler {} | ||
646 | fn foo() { | ||
647 | $0bar()$0; | ||
648 | }"#, | ||
649 | "static_handler", | ||
650 | ); | ||
651 | } | ||
652 | |||
653 | #[test] | ||
654 | fn impl_trait_plus_clone() { | ||
655 | check_name_suggestion( | ||
656 | from_type, | ||
657 | r#" | ||
658 | trait StaticHandler {} | ||
659 | trait Clone {} | ||
660 | fn bar() -> impl StaticHandler + Clone {} | ||
661 | fn foo() { | ||
662 | $0bar()$0; | ||
663 | }"#, | ||
664 | "static_handler", | ||
665 | ); | ||
666 | } | ||
667 | |||
668 | #[test] | ||
669 | fn impl_trait_plus_lifetime() { | ||
670 | check_name_suggestion( | ||
671 | from_type, | ||
672 | r#" | ||
673 | trait StaticHandler {} | ||
674 | trait Clone {} | ||
675 | fn bar<'a>(&'a i32) -> impl StaticHandler + 'a {} | ||
676 | fn foo() { | ||
677 | $0bar(&1)$0; | ||
678 | }"#, | ||
679 | "static_handler", | ||
680 | ); | ||
681 | } | ||
682 | |||
683 | #[test] | ||
684 | fn impl_trait_plus_trait() { | ||
685 | check_name_suggestion_not_applicable( | ||
686 | from_type, | ||
687 | r#" | ||
688 | trait Handler {} | ||
689 | trait StaticHandler {} | ||
690 | fn bar() -> impl StaticHandler + Handler {} | ||
691 | fn foo() { | ||
692 | $0bar()$0; | ||
693 | }"#, | ||
694 | ); | ||
695 | } | ||
696 | |||
697 | #[test] | ||
698 | fn ref_value() { | ||
699 | check_name_suggestion( | ||
700 | from_type, | ||
701 | r#" | ||
702 | struct Seed; | ||
703 | fn bar() -> &Seed {} | ||
704 | fn foo() { | ||
705 | $0bar()$0; | ||
706 | }"#, | ||
707 | "seed", | ||
708 | ); | ||
709 | } | ||
710 | |||
711 | #[test] | ||
712 | fn box_value() { | ||
713 | check_name_suggestion( | ||
714 | from_type, | ||
715 | r#" | ||
716 | struct Box<T>(*const T); | ||
717 | struct Seed; | ||
718 | fn bar() -> Box<Seed> {} | ||
719 | fn foo() { | ||
720 | $0bar()$0; | ||
721 | }"#, | ||
722 | "seed", | ||
723 | ); | ||
724 | } | ||
725 | |||
726 | #[test] | ||
727 | fn box_generic() { | ||
728 | check_name_suggestion_not_applicable( | ||
729 | from_type, | ||
730 | r#" | ||
731 | struct Box<T>(*const T); | ||
732 | fn bar<T>() -> Box<T> {} | ||
733 | fn foo<T>() { | ||
734 | $0bar::<T>()$0; | ||
735 | }"#, | ||
736 | ); | ||
737 | } | ||
738 | |||
739 | #[test] | ||
740 | fn option_value() { | ||
741 | check_name_suggestion( | ||
742 | from_type, | ||
743 | r#" | ||
744 | enum Option<T> { Some(T) } | ||
745 | struct Seed; | ||
746 | fn bar() -> Option<Seed> {} | ||
747 | fn foo() { | ||
748 | $0bar()$0; | ||
749 | }"#, | ||
750 | "seed", | ||
751 | ); | ||
752 | } | ||
753 | |||
754 | #[test] | ||
755 | fn result_value() { | ||
756 | check_name_suggestion( | ||
757 | from_type, | ||
758 | r#" | ||
759 | enum Result<T, E> { Ok(T), Err(E) } | ||
760 | struct Seed; | ||
761 | struct Error; | ||
762 | fn bar() -> Result<Seed, Error> {} | ||
763 | fn foo() { | ||
764 | $0bar()$0; | ||
765 | }"#, | ||
766 | "seed", | ||
767 | ); | ||
768 | } | ||
769 | } | ||
770 | } | ||