]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / ide / src / inlay_hints / bind_pat.rs
1 //! Implementation of "type" inlay hints:
2 //! ```no_run
3 //! fn f(a: i32, b: i32) -> i32 { a + b }
4 //! let _x /* i32 */= f(4, 4);
5 //! ```
6 use hir::{Semantics, TypeInfo};
7 use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase};
8
9 use itertools::Itertools;
10 use syntax::{
11 ast::{self, AstNode, HasName},
12 match_ast,
13 };
14
15 use crate::{inlay_hints::closure_has_block_body, InlayHint, InlayHintsConfig, InlayKind};
16
17 use super::label_of_ty;
18
19 pub(super) fn hints(
20 acc: &mut Vec<InlayHint>,
21 famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
22 config: &InlayHintsConfig,
23 _file_id: FileId,
24 pat: &ast::IdentPat,
25 ) -> Option<()> {
26 if !config.type_hints {
27 return None;
28 }
29
30 let descended = sema.descend_node_into_attributes(pat.clone()).pop();
31 let desc_pat = descended.as_ref().unwrap_or(pat);
32 let ty = sema.type_of_pat(&desc_pat.clone().into())?.original;
33
34 if should_not_display_type_hint(sema, config, pat, &ty) {
35 return None;
36 }
37
38 let label = label_of_ty(famous_defs, config, ty)?;
39
40 if config.hide_named_constructor_hints
41 && is_named_constructor(sema, pat, &label.to_string()).is_some()
42 {
43 return None;
44 }
45
46 acc.push(InlayHint {
47 range: match pat.name() {
48 Some(name) => name.syntax().text_range(),
49 None => pat.syntax().text_range(),
50 },
51 kind: InlayKind::Type,
52 label,
53 });
54
55 Some(())
56 }
57
58 fn should_not_display_type_hint(
59 sema: &Semantics<'_, RootDatabase>,
60 config: &InlayHintsConfig,
61 bind_pat: &ast::IdentPat,
62 pat_ty: &hir::Type,
63 ) -> bool {
64 let db = sema.db;
65
66 if pat_ty.is_unknown() {
67 return true;
68 }
69
70 if sema.resolve_bind_pat_to_const(bind_pat).is_some() {
71 return true;
72 }
73
74 for node in bind_pat.syntax().ancestors() {
75 match_ast! {
76 match node {
77 ast::LetStmt(it) => {
78 if config.hide_closure_initialization_hints {
79 if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() {
80 if closure_has_block_body(&closure) {
81 return true;
82 }
83 }
84 }
85 return it.ty().is_some()
86 },
87 // FIXME: We might wanna show type hints in parameters for non-top level patterns as well
88 ast::Param(it) => return it.ty().is_some(),
89 ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
90 ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
91 ast::IfExpr(_) => return false,
92 ast::WhileExpr(_) => return false,
93 ast::ForExpr(it) => {
94 // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
95 // Type of expr should be iterable.
96 return it.in_token().is_none() ||
97 it.iterable()
98 .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
99 .map(TypeInfo::original)
100 .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
101 },
102 _ => (),
103 }
104 }
105 }
106 false
107 }
108
109 fn is_named_constructor(
110 sema: &Semantics<'_, RootDatabase>,
111 pat: &ast::IdentPat,
112 ty_name: &str,
113 ) -> Option<()> {
114 let let_node = pat.syntax().parent()?;
115 let expr = match_ast! {
116 match let_node {
117 ast::LetStmt(it) => it.initializer(),
118 ast::LetExpr(it) => it.expr(),
119 _ => None,
120 }
121 }?;
122
123 let expr = sema.descend_node_into_attributes(expr.clone()).pop().unwrap_or(expr);
124 // unwrap postfix expressions
125 let expr = match expr {
126 ast::Expr::TryExpr(it) => it.expr(),
127 ast::Expr::AwaitExpr(it) => it.expr(),
128 expr => Some(expr),
129 }?;
130 let expr = match expr {
131 ast::Expr::CallExpr(call) => match call.expr()? {
132 ast::Expr::PathExpr(path) => path,
133 _ => return None,
134 },
135 ast::Expr::PathExpr(path) => path,
136 _ => return None,
137 };
138 let path = expr.path()?;
139
140 let callable = sema.type_of_expr(&ast::Expr::PathExpr(expr))?.original.as_callable(sema.db);
141 let callable_kind = callable.map(|it| it.kind());
142 let qual_seg = match callable_kind {
143 Some(hir::CallableKind::Function(_) | hir::CallableKind::TupleEnumVariant(_)) => {
144 path.qualifier()?.segment()
145 }
146 _ => path.segment(),
147 }?;
148
149 let ctor_name = match qual_seg.kind()? {
150 ast::PathSegmentKind::Name(name_ref) => {
151 match qual_seg.generic_arg_list().map(|it| it.generic_args()) {
152 Some(generics) => format!("{name_ref}<{}>", generics.format(", ")),
153 None => name_ref.to_string(),
154 }
155 }
156 ast::PathSegmentKind::Type { type_ref: Some(ty), trait_ref: None } => ty.to_string(),
157 _ => return None,
158 };
159 (ctor_name == ty_name).then_some(())
160 }
161
162 fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
163 if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
164 let pat_text = bind_pat.to_string();
165 enum_data
166 .variants(db)
167 .into_iter()
168 .map(|variant| variant.name(db).to_smol_str())
169 .any(|enum_name| enum_name == pat_text)
170 } else {
171 false
172 }
173 }
174
175 #[cfg(test)]
176 mod tests {
177 // This module also contains tests for super::closure_ret
178
179 use expect_test::expect;
180 use syntax::{TextRange, TextSize};
181 use test_utils::extract_annotations;
182
183 use crate::{fixture, inlay_hints::InlayHintsConfig};
184
185 use crate::inlay_hints::tests::{
186 check, check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
187 };
188 use crate::ClosureReturnTypeHints;
189
190 #[track_caller]
191 fn check_types(ra_fixture: &str) {
192 check_with_config(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, ra_fixture);
193 }
194
195 #[test]
196 fn type_hints_only() {
197 check_types(
198 r#"
199 fn foo(a: i32, b: i32) -> i32 { a + b }
200 fn main() {
201 let _x = foo(4, 4);
202 //^^ i32
203 }"#,
204 );
205 }
206
207 #[test]
208 fn type_hints_bindings_after_at() {
209 check_types(
210 r#"
211 //- minicore: option
212 fn main() {
213 let ref foo @ bar @ ref mut baz = 0;
214 //^^^ &i32
215 //^^^ i32
216 //^^^ &mut i32
217 let [x @ ..] = [0];
218 //^ [i32; 1]
219 if let x @ Some(_) = Some(0) {}
220 //^ Option<i32>
221 let foo @ (bar, baz) = (3, 3);
222 //^^^ (i32, i32)
223 //^^^ i32
224 //^^^ i32
225 }"#,
226 );
227 }
228
229 #[test]
230 fn default_generic_types_should_not_be_displayed() {
231 check(
232 r#"
233 struct Test<K, T = u8> { k: K, t: T }
234
235 fn main() {
236 let zz = Test { t: 23u8, k: 33 };
237 //^^ Test<i32>
238 let zz_ref = &zz;
239 //^^^^^^ &Test<i32>
240 let test = || zz;
241 //^^^^ || -> Test<i32>
242 }"#,
243 );
244 }
245
246 #[test]
247 fn shorten_iterators_in_associated_params() {
248 check_types(
249 r#"
250 //- minicore: iterators
251 use core::iter;
252
253 pub struct SomeIter<T> {}
254
255 impl<T> SomeIter<T> {
256 pub fn new() -> Self { SomeIter {} }
257 pub fn push(&mut self, t: T) {}
258 }
259
260 impl<T> Iterator for SomeIter<T> {
261 type Item = T;
262 fn next(&mut self) -> Option<Self::Item> {
263 None
264 }
265 }
266
267 fn main() {
268 let mut some_iter = SomeIter::new();
269 //^^^^^^^^^ SomeIter<Take<Repeat<i32>>>
270 some_iter.push(iter::repeat(2).take(2));
271 let iter_of_iters = some_iter.take(2);
272 //^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>>
273 }
274 "#,
275 );
276 }
277
278 #[test]
279 fn iterator_hint_regression_issue_12674() {
280 // Ensure we don't crash while solving the projection type of iterators.
281 check_expect(
282 InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
283 r#"
284 //- minicore: iterators
285 struct S<T>(T);
286 impl<T> S<T> {
287 fn iter(&self) -> Iter<'_, T> { loop {} }
288 }
289 struct Iter<'a, T: 'a>(&'a T);
290 impl<'a, T> Iterator for Iter<'a, T> {
291 type Item = &'a T;
292 fn next(&mut self) -> Option<Self::Item> { loop {} }
293 }
294 struct Container<'a> {
295 elements: S<&'a str>,
296 }
297 struct SliceIter<'a, T>(&'a T);
298 impl<'a, T> Iterator for SliceIter<'a, T> {
299 type Item = &'a T;
300 fn next(&mut self) -> Option<Self::Item> { loop {} }
301 }
302
303 fn main(a: SliceIter<'_, Container>) {
304 a
305 .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
306 .map(|e| e);
307 }
308 "#,
309 expect![[r#"
310 [
311 InlayHint {
312 range: 484..554,
313 kind: Chaining,
314 label: [
315 "impl ",
316 InlayHintLabelPart {
317 text: "Iterator",
318 linked_location: Some(
319 FileRange {
320 file_id: FileId(
321 1,
322 ),
323 range: 2611..2619,
324 },
325 ),
326 tooltip: "",
327 },
328 "<",
329 InlayHintLabelPart {
330 text: "Item",
331 linked_location: Some(
332 FileRange {
333 file_id: FileId(
334 1,
335 ),
336 range: 2643..2647,
337 },
338 ),
339 tooltip: "",
340 },
341 " = impl ",
342 InlayHintLabelPart {
343 text: "Iterator",
344 linked_location: Some(
345 FileRange {
346 file_id: FileId(
347 1,
348 ),
349 range: 2611..2619,
350 },
351 ),
352 tooltip: "",
353 },
354 "<",
355 InlayHintLabelPart {
356 text: "Item",
357 linked_location: Some(
358 FileRange {
359 file_id: FileId(
360 1,
361 ),
362 range: 2643..2647,
363 },
364 ),
365 tooltip: "",
366 },
367 " = &&str>>",
368 ],
369 },
370 InlayHint {
371 range: 484..485,
372 kind: Chaining,
373 label: [
374 "",
375 InlayHintLabelPart {
376 text: "SliceIter",
377 linked_location: Some(
378 FileRange {
379 file_id: FileId(
380 0,
381 ),
382 range: 289..298,
383 },
384 ),
385 tooltip: "",
386 },
387 "<",
388 InlayHintLabelPart {
389 text: "Container",
390 linked_location: Some(
391 FileRange {
392 file_id: FileId(
393 0,
394 ),
395 range: 238..247,
396 },
397 ),
398 tooltip: "",
399 },
400 ">",
401 ],
402 },
403 ]
404 "#]],
405 );
406 }
407
408 #[test]
409 fn infer_call_method_return_associated_types_with_generic() {
410 check_types(
411 r#"
412 pub trait Default {
413 fn default() -> Self;
414 }
415 pub trait Foo {
416 type Bar: Default;
417 }
418
419 pub fn quux<T: Foo>() -> T::Bar {
420 let y = Default::default();
421 //^ <T as Foo>::Bar
422
423 y
424 }
425 "#,
426 );
427 }
428
429 #[test]
430 fn fn_hints() {
431 check_types(
432 r#"
433 //- minicore: fn, sized
434 fn foo() -> impl Fn() { loop {} }
435 fn foo1() -> impl Fn(f64) { loop {} }
436 fn foo2() -> impl Fn(f64, f64) { loop {} }
437 fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
438 fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
439 fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
440 fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
441 fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
442
443 fn main() {
444 let foo = foo();
445 // ^^^ impl Fn()
446 let foo = foo1();
447 // ^^^ impl Fn(f64)
448 let foo = foo2();
449 // ^^^ impl Fn(f64, f64)
450 let foo = foo3();
451 // ^^^ impl Fn(f64, f64) -> u32
452 let foo = foo4();
453 // ^^^ &dyn Fn(f64, f64) -> u32
454 let foo = foo5();
455 // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
456 let foo = foo6();
457 // ^^^ impl Fn(f64, f64) -> u32
458 let foo = foo7();
459 // ^^^ *const impl Fn(f64, f64) -> u32
460 }
461 "#,
462 )
463 }
464
465 #[test]
466 fn check_hint_range_limit() {
467 let fixture = r#"
468 //- minicore: fn, sized
469 fn foo() -> impl Fn() { loop {} }
470 fn foo1() -> impl Fn(f64) { loop {} }
471 fn foo2() -> impl Fn(f64, f64) { loop {} }
472 fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
473 fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
474 fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
475 fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
476 fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
477
478 fn main() {
479 let foo = foo();
480 let foo = foo1();
481 let foo = foo2();
482 // ^^^ impl Fn(f64, f64)
483 let foo = foo3();
484 // ^^^ impl Fn(f64, f64) -> u32
485 let foo = foo4();
486 let foo = foo5();
487 let foo = foo6();
488 let foo = foo7();
489 }
490 "#;
491 let (analysis, file_id) = fixture::file(fixture);
492 let expected = extract_annotations(&analysis.file_text(file_id).unwrap());
493 let inlay_hints = analysis
494 .inlay_hints(
495 &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
496 file_id,
497 Some(TextRange::new(TextSize::from(500), TextSize::from(600))),
498 )
499 .unwrap();
500 let actual =
501 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
502 assert_eq!(expected, actual, "\nExpected:\n{expected:#?}\n\nActual:\n{actual:#?}");
503 }
504
505 #[test]
506 fn fn_hints_ptr_rpit_fn_parentheses() {
507 check_types(
508 r#"
509 //- minicore: fn, sized
510 trait Trait {}
511
512 fn foo1() -> *const impl Fn() { loop {} }
513 fn foo2() -> *const (impl Fn() + Sized) { loop {} }
514 fn foo3() -> *const (impl Fn() + ?Sized) { loop {} }
515 fn foo4() -> *const (impl Sized + Fn()) { loop {} }
516 fn foo5() -> *const (impl ?Sized + Fn()) { loop {} }
517 fn foo6() -> *const (impl Fn() + Trait) { loop {} }
518 fn foo7() -> *const (impl Fn() + Sized + Trait) { loop {} }
519 fn foo8() -> *const (impl Fn() + ?Sized + Trait) { loop {} }
520 fn foo9() -> *const (impl Fn() -> u8 + ?Sized) { loop {} }
521 fn foo10() -> *const (impl Fn() + Sized + ?Sized) { loop {} }
522
523 fn main() {
524 let foo = foo1();
525 // ^^^ *const impl Fn()
526 let foo = foo2();
527 // ^^^ *const impl Fn()
528 let foo = foo3();
529 // ^^^ *const (impl Fn() + ?Sized)
530 let foo = foo4();
531 // ^^^ *const impl Fn()
532 let foo = foo5();
533 // ^^^ *const (impl Fn() + ?Sized)
534 let foo = foo6();
535 // ^^^ *const (impl Fn() + Trait)
536 let foo = foo7();
537 // ^^^ *const (impl Fn() + Trait)
538 let foo = foo8();
539 // ^^^ *const (impl Fn() + Trait + ?Sized)
540 let foo = foo9();
541 // ^^^ *const (impl Fn() -> u8 + ?Sized)
542 let foo = foo10();
543 // ^^^ *const impl Fn()
544 }
545 "#,
546 )
547 }
548
549 #[test]
550 fn unit_structs_have_no_type_hints() {
551 check_types(
552 r#"
553 //- minicore: result
554 struct SyntheticSyntax;
555
556 fn main() {
557 match Ok(()) {
558 Ok(_) => (),
559 Err(SyntheticSyntax) => (),
560 }
561 }"#,
562 );
563 }
564
565 #[test]
566 fn const_pats_have_no_type_hints() {
567 check_types(
568 r#"
569 const FOO: usize = 0;
570
571 fn main() {
572 match 0 {
573 FOO => (),
574 _ => ()
575 }
576 }"#,
577 );
578 }
579
580 #[test]
581 fn let_statement() {
582 check_types(
583 r#"
584 #[derive(PartialEq)]
585 enum Option<T> { None, Some(T) }
586
587 #[derive(PartialEq)]
588 struct Test { a: Option<u32>, b: u8 }
589
590 fn main() {
591 struct InnerStruct {}
592
593 let test = 54;
594 //^^^^ i32
595 let test: i32 = 33;
596 let mut test = 33;
597 //^^^^ i32
598 let _ = 22;
599 let test = "test";
600 //^^^^ &str
601 let test = InnerStruct {};
602 //^^^^ InnerStruct
603
604 let test = unresolved();
605
606 let test = (42, 'a');
607 //^^^^ (i32, char)
608 let (a, (b, (c,)) = (2, (3, (9.2,));
609 //^ i32 ^ i32 ^ f64
610 let &x = &92;
611 //^ i32
612 }"#,
613 );
614 }
615
616 #[test]
617 fn if_expr() {
618 check_types(
619 r#"
620 //- minicore: option
621 struct Test { a: Option<u32>, b: u8 }
622
623 fn main() {
624 let test = Some(Test { a: Some(3), b: 1 });
625 //^^^^ Option<Test>
626 if let None = &test {};
627 if let test = &test {};
628 //^^^^ &Option<Test>
629 if let Some(test) = &test {};
630 //^^^^ &Test
631 if let Some(Test { a, b }) = &test {};
632 //^ &Option<u32> ^ &u8
633 if let Some(Test { a: x, b: y }) = &test {};
634 //^ &Option<u32> ^ &u8
635 if let Some(Test { a: Some(x), b: y }) = &test {};
636 //^ &u32 ^ &u8
637 if let Some(Test { a: None, b: y }) = &test {};
638 //^ &u8
639 if let Some(Test { b: y, .. }) = &test {};
640 //^ &u8
641 if test == None {}
642 }"#,
643 );
644 }
645
646 #[test]
647 fn while_expr() {
648 check_types(
649 r#"
650 //- minicore: option
651 struct Test { a: Option<u32>, b: u8 }
652
653 fn main() {
654 let test = Some(Test { a: Some(3), b: 1 });
655 //^^^^ Option<Test>
656 while let Some(Test { a: Some(x), b: y }) = &test {};
657 //^ &u32 ^ &u8
658 }"#,
659 );
660 }
661
662 #[test]
663 fn match_arm_list() {
664 check_types(
665 r#"
666 //- minicore: option
667 struct Test { a: Option<u32>, b: u8 }
668
669 fn main() {
670 match Some(Test { a: Some(3), b: 1 }) {
671 None => (),
672 test => (),
673 //^^^^ Option<Test>
674 Some(Test { a: Some(x), b: y }) => (),
675 //^ u32 ^ u8
676 _ => {}
677 }
678 }"#,
679 );
680 }
681
682 #[test]
683 fn complete_for_hint() {
684 check_types(
685 r#"
686 //- minicore: iterator
687 pub struct Vec<T> {}
688
689 impl<T> Vec<T> {
690 pub fn new() -> Self { Vec {} }
691 pub fn push(&mut self, t: T) {}
692 }
693
694 impl<T> IntoIterator for Vec<T> {
695 type Item = T;
696 type IntoIter = IntoIter<T>;
697 }
698
699 struct IntoIter<T> {}
700
701 impl<T> Iterator for IntoIter<T> {
702 type Item = T;
703 }
704
705 fn main() {
706 let mut data = Vec::new();
707 //^^^^ Vec<&str>
708 data.push("foo");
709 for i in data {
710 //^ &str
711 let z = i;
712 //^ &str
713 }
714 }
715 "#,
716 );
717 }
718
719 #[test]
720 fn multi_dyn_trait_bounds() {
721 check_types(
722 r#"
723 pub struct Vec<T> {}
724
725 impl<T> Vec<T> {
726 pub fn new() -> Self { Vec {} }
727 }
728
729 pub struct Box<T> {}
730
731 trait Display {}
732 auto trait Sync {}
733
734 fn main() {
735 // The block expression wrapping disables the constructor hint hiding logic
736 let _v = { Vec::<Box<&(dyn Display + Sync)>>::new() };
737 //^^ Vec<Box<&(dyn Display + Sync)>>
738 let _v = { Vec::<Box<*const (dyn Display + Sync)>>::new() };
739 //^^ Vec<Box<*const (dyn Display + Sync)>>
740 let _v = { Vec::<Box<dyn Display + Sync>>::new() };
741 //^^ Vec<Box<dyn Display + Sync>>
742 }
743 "#,
744 );
745 }
746
747 #[test]
748 fn shorten_iterator_hints() {
749 check_types(
750 r#"
751 //- minicore: iterators
752 use core::iter;
753
754 struct MyIter;
755
756 impl Iterator for MyIter {
757 type Item = ();
758 fn next(&mut self) -> Option<Self::Item> {
759 None
760 }
761 }
762
763 fn main() {
764 let _x = MyIter;
765 //^^ MyIter
766 let _x = iter::repeat(0);
767 //^^ impl Iterator<Item = i32>
768 fn generic<T: Clone>(t: T) {
769 let _x = iter::repeat(t);
770 //^^ impl Iterator<Item = T>
771 let _chained = iter::repeat(t).take(10);
772 //^^^^^^^^ impl Iterator<Item = T>
773 }
774 }
775 "#,
776 );
777 }
778
779 #[test]
780 fn skip_constructor_and_enum_type_hints() {
781 check_with_config(
782 InlayHintsConfig {
783 type_hints: true,
784 hide_named_constructor_hints: true,
785 ..DISABLED_CONFIG
786 },
787 r#"
788 //- minicore: try, option
789 use core::ops::ControlFlow;
790
791 mod x {
792 pub mod y { pub struct Foo; }
793 pub struct Foo;
794 pub enum AnotherEnum {
795 Variant()
796 };
797 }
798 struct Struct;
799 struct TupleStruct();
800
801 impl Struct {
802 fn new() -> Self {
803 Struct
804 }
805 fn try_new() -> ControlFlow<(), Self> {
806 ControlFlow::Continue(Struct)
807 }
808 }
809
810 struct Generic<T>(T);
811 impl Generic<i32> {
812 fn new() -> Self {
813 Generic(0)
814 }
815 }
816
817 enum Enum {
818 Variant(u32)
819 }
820
821 fn times2(value: i32) -> i32 {
822 2 * value
823 }
824
825 fn main() {
826 let enumb = Enum::Variant(0);
827
828 let strukt = x::Foo;
829 let strukt = x::y::Foo;
830 let strukt = Struct;
831 let strukt = Struct::new();
832
833 let tuple_struct = TupleStruct();
834
835 let generic0 = Generic::new();
836 // ^^^^^^^^ Generic<i32>
837 let generic1 = Generic(0);
838 // ^^^^^^^^ Generic<i32>
839 let generic2 = Generic::<i32>::new();
840 let generic3 = <Generic<i32>>::new();
841 let generic4 = Generic::<i32>(0);
842
843
844 let option = Some(0);
845 // ^^^^^^ Option<i32>
846 let func = times2;
847 // ^^^^ fn times2(i32) -> i32
848 let closure = |x: i32| x * 2;
849 // ^^^^^^^ |i32| -> i32
850 }
851
852 fn fallible() -> ControlFlow<()> {
853 let strukt = Struct::try_new()?;
854 }
855 "#,
856 );
857 }
858
859 #[test]
860 fn shows_constructor_type_hints_when_enabled() {
861 check_types(
862 r#"
863 //- minicore: try
864 use core::ops::ControlFlow;
865
866 struct Struct;
867 struct TupleStruct();
868
869 impl Struct {
870 fn new() -> Self {
871 Struct
872 }
873 fn try_new() -> ControlFlow<(), Self> {
874 ControlFlow::Continue(Struct)
875 }
876 }
877
878 struct Generic<T>(T);
879 impl Generic<i32> {
880 fn new() -> Self {
881 Generic(0)
882 }
883 }
884
885 fn main() {
886 let strukt = Struct::new();
887 // ^^^^^^ Struct
888 let tuple_struct = TupleStruct();
889 // ^^^^^^^^^^^^ TupleStruct
890 let generic0 = Generic::new();
891 // ^^^^^^^^ Generic<i32>
892 let generic1 = Generic::<i32>::new();
893 // ^^^^^^^^ Generic<i32>
894 let generic2 = <Generic<i32>>::new();
895 // ^^^^^^^^ Generic<i32>
896 }
897
898 fn fallible() -> ControlFlow<()> {
899 let strukt = Struct::try_new()?;
900 // ^^^^^^ Struct
901 }
902 "#,
903 );
904 }
905
906 #[test]
907 fn closures() {
908 check(
909 r#"
910 fn main() {
911 let mut start = 0;
912 //^^^^^ i32
913 (0..2).for_each(|increment | { start += increment; });
914 //^^^^^^^^^ i32
915
916 let multiply =
917 //^^^^^^^^ |i32, i32| -> i32
918 | a, b| a * b
919 //^ i32 ^ i32
920
921 ;
922
923 let _: i32 = multiply(1, 2);
924 //^ a ^ b
925 let multiply_ref = &multiply;
926 //^^^^^^^^^^^^ &|i32, i32| -> i32
927
928 let return_42 = || 42;
929 //^^^^^^^^^ || -> i32
930 || { 42 };
931 //^^ i32
932 }"#,
933 );
934 }
935
936 #[test]
937 fn return_type_hints_for_closure_without_block() {
938 check_with_config(
939 InlayHintsConfig {
940 closure_return_type_hints: ClosureReturnTypeHints::Always,
941 ..DISABLED_CONFIG
942 },
943 r#"
944 fn main() {
945 let a = || { 0 };
946 //^^ i32
947 let b = || 0;
948 //^^ i32
949 }"#,
950 );
951 }
952
953 #[test]
954 fn skip_closure_type_hints() {
955 check_with_config(
956 InlayHintsConfig {
957 type_hints: true,
958 hide_closure_initialization_hints: true,
959 ..DISABLED_CONFIG
960 },
961 r#"
962 //- minicore: fn
963 fn main() {
964 let multiple_2 = |x: i32| { x * 2 };
965
966 let multiple_2 = |x: i32| x * 2;
967 // ^^^^^^^^^^ |i32| -> i32
968
969 let (not) = (|x: bool| { !x });
970 // ^^^ |bool| -> bool
971
972 let (is_zero, _b) = (|x: usize| { x == 0 }, false);
973 // ^^^^^^^ |usize| -> bool
974 // ^^ bool
975
976 let plus_one = |x| { x + 1 };
977 // ^ u8
978 foo(plus_one);
979
980 let add_mul = bar(|x: u8| { x + 1 });
981 // ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized
982
983 let closure = if let Some(6) = add_mul(2).checked_sub(1) {
984 // ^^^^^^^ fn(i32) -> i32
985 |x: i32| { x * 2 }
986 } else {
987 |x: i32| { x * 3 }
988 };
989 }
990
991 fn foo(f: impl FnOnce(u8) -> u8) {}
992
993 fn bar(f: impl FnOnce(u8) -> u8) -> impl FnOnce(u8) -> u8 {
994 move |x: u8| f(x) * 2
995 }
996 "#,
997 );
998 }
999
1000 #[test]
1001 fn hint_truncation() {
1002 check_with_config(
1003 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
1004 r#"
1005 struct Smol<T>(T);
1006
1007 struct VeryLongOuterName<T>(T);
1008
1009 fn main() {
1010 let a = Smol(0u32);
1011 //^ Smol<u32>
1012 let b = VeryLongOuterName(0usize);
1013 //^ VeryLongOuterName<…>
1014 let c = Smol(Smol(0u32))
1015 //^ Smol<Smol<…>>
1016 }"#,
1017 );
1018 }
1019 }