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