1use hir::{DisplayTarget, Semantics};
7use ide_db::{RootDatabase, famous_defs::FamousDefs};
8
9use itertools::Itertools;
10use syntax::{
11 ast::{self, AstNode, HasGenericArgs, HasName},
12 match_ast,
13};
14
15use crate::{
16 InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind,
17 inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
18};
19
20pub(super) fn hints(
21 acc: &mut Vec<InlayHint>,
22 famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
23 config: &InlayHintsConfig<'_>,
24 display_target: DisplayTarget,
25 pat: &ast::IdentPat,
26) -> Option<()> {
27 if !config.type_hints {
28 return None;
29 }
30
31 let parent = pat.syntax().parent()?;
32 let type_ascriptable = match_ast! {
33 match parent {
34 ast::Param(it) => {
35 if it.ty().is_some() {
36 return None;
37 }
38 if config.hide_closure_parameter_hints && it.syntax().ancestors().nth(2).is_none_or(|n| matches!(ast::Expr::cast(n), Some(ast::Expr::ClosureExpr(_)))) {
39 return None;
40 }
41 Some(it.colon_token())
42 },
43 ast::LetStmt(it) => {
44 if config.hide_closure_initialization_hints
45 && let Some(ast::Expr::ClosureExpr(closure)) = it.initializer()
46 && closure_has_block_body(&closure) {
47 return None;
48 }
49 if it.ty().is_some() {
50 return None;
51 }
52 Some(it.colon_token())
53 },
54 _ => None
55 }
56 };
57
58 let descended = sema.descend_node_into_attributes(pat.clone()).pop();
59 let desc_pat = descended.as_ref().unwrap_or(pat);
60 let ty = sema.type_of_binding_in_pat(desc_pat)?;
61
62 if ty.is_unknown() {
63 return None;
64 }
65
66 if sema.resolve_bind_pat_to_const(pat).is_some() {
67 return None;
68 }
69
70 let mut label = label_of_ty(famous_defs, config, &ty, display_target)?;
71
72 if config.hide_named_constructor_hints
73 && is_named_constructor(sema, pat, &label.to_string()).is_some()
74 {
75 return None;
76 }
77
78 let text_edit = if let Some(colon_token) = &type_ascriptable {
79 ty_to_text_edit(
80 sema,
81 config,
82 desc_pat.syntax(),
83 &ty,
84 colon_token
85 .as_ref()
86 .map_or_else(|| pat.syntax().text_range(), |t| t.text_range())
87 .end(),
88 &|_| (),
89 if colon_token.is_some() { "" } else { ": " },
90 )
91 } else {
92 None
93 };
94
95 let render_colons = config.render_colons && !matches!(type_ascriptable, Some(Some(_)));
96 if render_colons {
97 label.prepend_str(": ");
98 }
99
100 let text_range = match pat.name() {
101 Some(name) => name.syntax().text_range(),
102 None => pat.syntax().text_range(),
103 };
104 acc.push(InlayHint {
105 range: match type_ascriptable {
106 Some(Some(t)) => text_range.cover(t.text_range()),
107 _ => text_range,
108 },
109 kind: InlayKind::Type,
110 label,
111 text_edit,
112 position: InlayHintPosition::After,
113 pad_left: !render_colons,
114 pad_right: false,
115 resolve_parent: Some(pat.syntax().text_range()),
116 });
117
118 Some(())
119}
120
121fn is_named_constructor(
122 sema: &Semantics<'_, RootDatabase>,
123 pat: &ast::IdentPat,
124 ty_name: &str,
125) -> Option<()> {
126 let let_node = pat.syntax().parent()?;
127 let expr = match_ast! {
128 match let_node {
129 ast::LetStmt(it) => it.initializer(),
130 ast::LetExpr(it) => it.expr(),
131 _ => None,
132 }
133 }?;
134
135 let expr = sema.descend_node_into_attributes(expr.clone()).pop().unwrap_or(expr);
136 let expr = match expr {
138 ast::Expr::TryExpr(it) => it.expr(),
139 ast::Expr::AwaitExpr(it) => it.expr(),
140 expr => Some(expr),
141 }?;
142 let expr = match expr {
143 ast::Expr::CallExpr(call) => match call.expr()? {
144 ast::Expr::PathExpr(path) => path,
145 _ => return None,
146 },
147 ast::Expr::PathExpr(path) => path,
148 _ => return None,
149 };
150 let path = expr.path()?;
151
152 let callable = sema.type_of_expr(&ast::Expr::PathExpr(expr))?.original.as_callable(sema.db);
153 let callable_kind = callable.map(|it| it.kind());
154 let qual_seg = match callable_kind {
155 Some(hir::CallableKind::Function(_) | hir::CallableKind::TupleEnumVariant(_)) => {
156 path.qualifier()?.segment()
157 }
158 _ => path.segment(),
159 }?;
160
161 let ctor_name = match qual_seg.kind()? {
162 ast::PathSegmentKind::Name(name_ref) => {
163 match qual_seg.generic_arg_list().map(|it| it.generic_args()) {
164 Some(generics) => format!("{name_ref}<{}>", generics.format(", ")),
165 None => name_ref.to_string(),
166 }
167 }
168 ast::PathSegmentKind::Type { type_ref: Some(ty), trait_ref: None } => ty.to_string(),
169 _ => return None,
170 };
171 (ctor_name == ty_name).then_some(())
172}
173
174#[cfg(test)]
175mod tests {
176 use expect_test::expect;
179 use hir::ClosureStyle;
180 use syntax::{TextRange, TextSize};
181 use test_utils::extract_annotations;
182
183 use crate::{ClosureReturnTypeHints, fixture, inlay_hints::InlayHintsConfig};
184
185 use crate::inlay_hints::tests::{
186 DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_expect, check_no_edit,
187 check_with_config,
188 };
189
190 #[track_caller]
191 fn check_types(#[rust_analyzer::rust_fixture] 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#"
199fn foo(a: i32, b: i32) -> i32 { a + b }
200fn 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
212fn 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#"
233struct Test<K, T = u8> { k: K, t: T }
234
235fn 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 //^^^^ impl FnOnce() -> Test<i32>
242}"#,
243 );
244 }
245
246 #[test]
247 fn shorten_iterators_in_associated_params() {
248 check_types(
249 r#"
250//- minicore: iterators
251use core::iter;
252
253pub struct SomeIter<T> {}
254
255impl<T> SomeIter<T> {
256 pub fn new() -> Self { SomeIter {} }
257 pub fn push(&mut self, t: T) {}
258}
259
260impl<T> Iterator for SomeIter<T> {
261 type Item = T;
262 fn next(&mut self) -> Option<Self::Item> {
263 None
264 }
265}
266
267fn 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 let (analysis, file_id) = fixture::file(
282 r#"
283//- minicore: iterators
284struct S<T>(T);
285impl<T> S<T> {
286 fn iter(&self) -> Iter<'_, T> { loop {} }
287}
288struct Iter<'a, T: 'a>(&'a T);
289impl<'a, T> Iterator for Iter<'a, T> {
290 type Item = &'a T;
291 fn next(&mut self) -> Option<Self::Item> { loop {} }
292}
293struct Container<'a> {
294 elements: S<&'a str>,
295}
296struct SliceIter<'a, T>(&'a T);
297impl<'a, T> Iterator for SliceIter<'a, T> {
298 type Item = &'a T;
299 fn next(&mut self) -> Option<Self::Item> { loop {} }
300}
301
302fn main(a: SliceIter<'_, Container>) {
303 a
304 .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
305 .map(|e| e);
306}
307"#,
308 );
309 analysis
310 .inlay_hints(
311 &InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
312 file_id,
313 None,
314 )
315 .unwrap();
316 }
317
318 #[test]
319 fn infer_call_method_return_associated_types_with_generic() {
320 check_types(
321 r#"
322 pub trait Default {
323 fn default() -> Self;
324 }
325 pub trait Foo {
326 type Bar: Default;
327 }
328
329 pub fn quux<T: Foo>() -> T::Bar {
330 let y = Default::default();
331 //^ <T as Foo>::Bar
332
333 y
334 }
335 "#,
336 );
337 }
338
339 #[test]
340 fn lt_hints() {
341 check_types(
342 r#"
343struct S<'lt>(*mut &'lt ());
344
345fn f<'a>() {
346 let x = S::<'static>(loop {});
347 //^ S<'static>
348 let y = S::<'_>(loop {});
349 //^ S<'_>
350 let z = S::<'a>(loop {});
351 //^ S<'a>
352
353}
354"#,
355 );
356 }
357
358 #[test]
359 fn fn_hints() {
360 check_types(
361 r#"
362//- minicore: fn, sized
363fn foo() -> impl Fn() { loop {} }
364fn foo1() -> impl Fn(f64) { loop {} }
365fn foo2() -> impl Fn(f64, f64) { loop {} }
366fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
367fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
368fn foo5() -> &'static for<'a> dyn Fn(&'a dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
369fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
370fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
371
372fn main() {
373 let foo = foo();
374 // ^^^ impl Fn()
375 let foo = foo1();
376 // ^^^ impl Fn(f64)
377 let foo = foo2();
378 // ^^^ impl Fn(f64, f64)
379 let foo = foo3();
380 // ^^^ impl Fn(f64, f64) -> u32
381 let foo = foo4();
382 // ^^^ &dyn Fn(f64, f64) -> u32
383 let foo = foo5();
384 // ^^^ &dyn Fn(&(dyn Fn(f64, f64) -> u32 + 'static), f64) -> u32
385 let foo = foo6();
386 // ^^^ impl Fn(f64, f64) -> u32
387 let foo = foo7();
388 // ^^^ *const impl Fn(f64, f64) -> u32
389}
390"#,
391 )
392 }
393
394 #[test]
395 fn check_hint_range_limit() {
396 let fixture = r#"
397//- minicore: fn, sized
398fn foo() -> impl Fn() { loop {} }
399fn foo1() -> impl Fn(f64) { loop {} }
400fn foo2() -> impl Fn(f64, f64) { loop {} }
401fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
402fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
403fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
404fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
405fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
406
407fn main() {
408 let foo = foo();
409 let foo = foo1();
410 let foo = foo2();
411 // ^^^ impl Fn(f64, f64)
412 let foo = foo3();
413 // ^^^ impl Fn(f64, f64) -> u32
414 let foo = foo4();
415 // ^^^ &dyn Fn(f64, f64) -> u32
416 let foo = foo5();
417 let foo = foo6();
418 let foo = foo7();
419}
420"#;
421 let (analysis, file_id) = fixture::file(fixture);
422 let expected = extract_annotations(&analysis.file_text(file_id).unwrap());
423 let inlay_hints = analysis
424 .inlay_hints(
425 &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
426 file_id,
427 Some(TextRange::new(TextSize::from(491), TextSize::from(640))),
428 )
429 .unwrap();
430 let actual =
431 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
432 assert_eq!(expected, actual, "\nExpected:\n{expected:#?}\n\nActual:\n{actual:#?}");
433 }
434
435 #[test]
436 fn fn_hints_ptr_rpit_fn_parentheses() {
437 check_types(
438 r#"
439//- minicore: fn, sized
440trait Trait {}
441
442fn foo1() -> *const impl Fn() { loop {} }
443fn foo2() -> *const (impl Fn() + Sized) { loop {} }
444fn foo3() -> *const (impl Fn() + ?Sized) { loop {} }
445fn foo4() -> *const (impl Sized + Fn()) { loop {} }
446fn foo5() -> *const (impl ?Sized + Fn()) { loop {} }
447fn foo6() -> *const (impl Fn() + Trait) { loop {} }
448fn foo7() -> *const (impl Fn() + Sized + Trait) { loop {} }
449fn foo8() -> *const (impl Fn() + ?Sized + Trait) { loop {} }
450fn foo9() -> *const (impl Fn() -> u8 + ?Sized) { loop {} }
451fn foo10() -> *const (impl Fn() + Sized + ?Sized) { loop {} }
452
453fn main() {
454 let foo = foo1();
455 // ^^^ *const impl Fn()
456 let foo = foo2();
457 // ^^^ *const impl Fn()
458 let foo = foo3();
459 // ^^^ *const (impl Fn() + ?Sized)
460 let foo = foo4();
461 // ^^^ *const impl Fn()
462 let foo = foo5();
463 // ^^^ *const (impl Fn() + ?Sized)
464 let foo = foo6();
465 // ^^^ *const (impl Fn() + Trait)
466 let foo = foo7();
467 // ^^^ *const (impl Fn() + Trait)
468 let foo = foo8();
469 // ^^^ *const (impl Fn() + Trait + ?Sized)
470 let foo = foo9();
471 // ^^^ *const (impl Fn() -> u8 + ?Sized)
472 let foo = foo10();
473 // ^^^ *const impl Fn()
474}
475"#,
476 )
477 }
478
479 #[test]
480 fn unit_structs_have_no_type_hints() {
481 check_types(
482 r#"
483//- minicore: result
484struct SyntheticSyntax;
485
486fn main() {
487 match Ok(()) {
488 Ok(_) => (),
489 Err(SyntheticSyntax) => (),
490 }
491}"#,
492 );
493 }
494
495 #[test]
496 fn const_pats_have_no_type_hints() {
497 check_types(
498 r#"
499const FOO: usize = 0;
500
501fn main() {
502 match 0 {
503 FOO => (),
504 _ => ()
505 }
506}"#,
507 );
508 }
509
510 #[test]
511 fn let_statement() {
512 check_types(
513 r#"
514#[derive(PartialEq)]
515enum Option<T> { None, Some(T) }
516
517#[derive(PartialEq)]
518struct Test { a: Option<u32>, b: u8 }
519
520fn main() {
521 struct InnerStruct {}
522
523 let test = 54;
524 //^^^^ i32
525 let test: i32 = 33;
526 let mut test = 33;
527 //^^^^ i32
528 let _ = 22;
529 let test = "test";
530 //^^^^ &str
531 let test = InnerStruct {};
532 //^^^^ InnerStruct
533
534 let test = unresolved();
535
536 let test = (42, 'a');
537 //^^^^ (i32, char)
538 let (a, (b, (c,)) = (2, (3, (9.2,));
539 //^ i32 ^ i32 ^ f64
540 let &x = &92;
541 //^ i32
542}"#,
543 );
544 }
545
546 #[test]
547 fn if_expr() {
548 check_types(
549 r#"
550//- minicore: option
551struct Test { a: Option<u32>, b: u8 }
552
553fn main() {
554
555}"#,
556 );
557 }
558
559 #[test]
560 fn while_expr() {
561 check_types(
562 r#"
563//- minicore: option
564struct Test { a: Option<u32>, b: u8 }
565
566fn main() {
567 let test = Some(Test { a: Some(3), b: 1 });
568 //^^^^ Option<Test>
569 while let Some(Test { a: Some(x), b: y }) = &test {};
570 //^ &u32 ^ &u8
571}"#,
572 );
573 }
574
575 #[test]
576 fn match_arm_list() {
577 check_types(
578 r#"
579//- minicore: option
580struct Test { a: Option<u32>, b: u8 }
581
582fn main() {
583 match Some(Test { a: Some(3), b: 1 }) {
584 None => (),
585 test => (),
586 //^^^^ Option<Test>
587 Some(Test { a: Some(x), b: y }) => (),
588 //^ u32 ^ u8
589 _ => {}
590 }
591}"#,
592 );
593 }
594
595 #[test]
596 fn complete_for_hint() {
597 check_types(
598 r#"
599//- minicore: iterator
600pub struct Vec<T> {}
601
602impl<T> Vec<T> {
603 pub fn new() -> Self { Vec {} }
604 pub fn push(&mut self, t: T) {}
605}
606
607impl<T> IntoIterator for Vec<T> {
608 type Item = T;
609 type IntoIter = IntoIter<T>;
610}
611
612struct IntoIter<T> {}
613
614impl<T> Iterator for IntoIter<T> {
615 type Item = T;
616}
617
618fn main() {
619 let mut data = Vec::new();
620 //^^^^ Vec<&str>
621 data.push("foo");
622 for i in data {
623 //^ &str
624 let z = i;
625 //^ &str
626 }
627}
628"#,
629 );
630 }
631
632 #[test]
633 fn multi_dyn_trait_bounds() {
634 check_types(
635 r#"
636pub struct Vec<T>(*mut T);
637
638impl<T> Vec<T> {
639 pub fn new() -> Self { Vec(0 as *mut T) }
640}
641
642pub struct Box<T> {}
643
644trait Display {}
645auto trait Sync {}
646
647fn main() {
648 // The block expression wrapping disables the constructor hint hiding logic
649 let _v = { Vec::<Box<&(dyn Display + Sync)>>::new() };
650 //^^ Vec<Box<&(dyn Display + Sync + 'static)>>
651 let _v = { Vec::<Box<*const (dyn Display + Sync)>>::new() };
652 //^^ Vec<Box<*const (dyn Display + Sync + 'static)>>
653 let _v = { Vec::<Box<dyn Display + Sync + 'static>>::new() };
654 //^^ Vec<Box<dyn Display + Sync + 'static>>
655}
656"#,
657 );
658 }
659
660 #[test]
661 fn shorten_iterator_hints() {
662 check_types(
663 r#"
664//- minicore: iterators
665use core::iter;
666
667struct MyIter;
668
669impl Iterator for MyIter {
670 type Item = ();
671 fn next(&mut self) -> Option<Self::Item> {
672 None
673 }
674}
675
676fn main() {
677 let _x = MyIter;
678 //^^ MyIter
679 let _x = iter::repeat(0);
680 //^^ impl Iterator<Item = i32>
681 fn generic<T: Clone>(t: T) {
682 let _x = iter::repeat(t);
683 //^^ impl Iterator<Item = T>
684 let _chained = iter::repeat(t).take(10);
685 //^^^^^^^^ impl Iterator<Item = T>
686 }
687}
688"#,
689 );
690 }
691
692 #[test]
693 fn skip_constructor_and_enum_type_hints() {
694 check_with_config(
695 InlayHintsConfig {
696 type_hints: true,
697 hide_named_constructor_hints: true,
698 ..DISABLED_CONFIG
699 },
700 r#"
701//- minicore: try, option
702use core::ops::ControlFlow;
703
704mod x {
705 pub mod y { pub struct Foo; }
706 pub struct Foo;
707 pub enum AnotherEnum {
708 Variant()
709 };
710}
711struct Struct;
712struct TupleStruct();
713
714impl Struct {
715 fn new() -> Self {
716 Struct
717 }
718 fn try_new() -> ControlFlow<(), Self> {
719 ControlFlow::Continue(Struct)
720 }
721}
722
723struct Generic<T>(T);
724impl Generic<i32> {
725 fn new() -> Self {
726 Generic(0)
727 }
728}
729
730enum Enum {
731 Variant(u32)
732}
733
734fn times2(value: i32) -> i32 {
735 2 * value
736}
737
738fn main() {
739 let enumb = Enum::Variant(0);
740
741 let strukt = x::Foo;
742 let strukt = x::y::Foo;
743 let strukt = Struct;
744 let strukt = Struct::new();
745
746 let tuple_struct = TupleStruct();
747
748 let generic0 = Generic::new();
749 // ^^^^^^^^ Generic<i32>
750 let generic1 = Generic(0);
751 // ^^^^^^^^ Generic<i32>
752 let generic2 = Generic::<i32>::new();
753 let generic3 = <Generic<i32>>::new();
754 let generic4 = Generic::<i32>(0);
755
756
757 let option = Some(0);
758 // ^^^^^^ Option<i32>
759 let func = times2;
760 // ^^^^ fn times2(i32) -> i32
761 let closure = |x: i32| x * 2;
762 // ^^^^^^^ impl Fn(i32) -> i32
763}
764
765fn fallible() -> ControlFlow<()> {
766 let strukt = Struct::try_new()?;
767}
768"#,
769 );
770 }
771
772 #[test]
773 fn shows_constructor_type_hints_when_enabled() {
774 check_types(
775 r#"
776//- minicore: try
777use core::ops::ControlFlow;
778
779struct Struct;
780struct TupleStruct();
781
782impl Struct {
783 fn new() -> Self {
784 Struct
785 }
786 fn try_new() -> ControlFlow<(), Self> {
787 ControlFlow::Continue(Struct)
788 }
789}
790
791struct Generic<T>(T);
792impl Generic<i32> {
793 fn new() -> Self {
794 Generic(0)
795 }
796}
797
798fn main() {
799 let strukt = Struct::new();
800 // ^^^^^^ Struct
801 let tuple_struct = TupleStruct();
802 // ^^^^^^^^^^^^ TupleStruct
803 let generic0 = Generic::new();
804 // ^^^^^^^^ Generic<i32>
805 let generic1 = Generic::<i32>::new();
806 // ^^^^^^^^ Generic<i32>
807 let generic2 = <Generic<i32>>::new();
808 // ^^^^^^^^ Generic<i32>
809}
810
811fn fallible() -> ControlFlow<()> {
812 let strukt = Struct::try_new()?;
813 // ^^^^^^ Struct
814}
815"#,
816 );
817 }
818
819 #[test]
820 fn closure_style() {
821 check_with_config(
822 InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
823 r#"
824//- minicore: fn
825fn main() {
826 let x = || 2;
827 //^ impl Fn() -> i32
828 let y = |t: i32| x() + t;
829 //^ impl Fn(i32) -> i32
830 let mut t = 5;
831 //^ i32
832 let z = |k: i32| { t += k; };
833 //^ impl FnMut(i32)
834 let p = (y, z);
835 //^ (impl Fn(i32) -> i32, impl FnMut(i32))
836}
837 "#,
838 );
839 check_with_config(
840 InlayHintsConfig {
841 type_hints: true,
842 closure_style: ClosureStyle::RANotation,
843 ..DISABLED_CONFIG
844 },
845 r#"
846//- minicore: fn
847fn main() {
848 let x = || 2;
849 //^ || -> i32
850 let y = |t: i32| x() + t;
851 //^ |i32| -> i32
852 let mut t = 5;
853 //^ i32
854 let z = |k: i32| { t += k; };
855 //^ |i32| -> ()
856 let p = (y, z);
857 //^ (|i32| -> i32, |i32| -> ())
858}
859 "#,
860 );
861 check_with_config(
862 InlayHintsConfig {
863 type_hints: true,
864 closure_style: ClosureStyle::Hide,
865 ..DISABLED_CONFIG
866 },
867 r#"
868//- minicore: fn
869fn main() {
870 let x = || 2;
871 //^ …
872 let y = |t: i32| x() + t;
873 //^ …
874 let mut t = 5;
875 //^ i32
876 let z = |k: i32| { t += k; };
877 //^ …
878 let p = (y, z);
879 //^ (…, …)
880}
881 "#,
882 );
883 }
884
885 #[test]
886 fn skip_closure_type_hints() {
887 check_with_config(
888 InlayHintsConfig {
889 type_hints: true,
890 hide_closure_initialization_hints: true,
891 ..DISABLED_CONFIG
892 },
893 r#"
894//- minicore: fn
895fn main() {
896 let multiple_2 = |x: i32| { x * 2 };
897
898 let multiple_2 = |x: i32| x * 2;
899 // ^^^^^^^^^^ impl Fn(i32) -> i32
900
901 let (not) = (|x: bool| { !x });
902 // ^^^ impl Fn(bool) -> bool
903
904 let (is_zero, _b) = (|x: usize| { x == 0 }, false);
905 // ^^^^^^^ impl Fn(usize) -> bool
906 // ^^ bool
907
908 let plus_one = |x| { x + 1 };
909 // ^ u8
910 foo(plus_one);
911
912 let add_mul = bar(|x: u8| { x + 1 });
913 // ^^^^^^^ impl FnOnce(u8) -> u8
914
915 let closure = if let Some(6) = add_mul(2).checked_sub(1) {
916 // ^^^^^^^ fn(i32) -> i32
917 |x: i32| { x * 2 }
918 } else {
919 |x: i32| { x * 3 }
920 };
921}
922
923fn foo(f: impl FnOnce(u8) -> u8) {}
924
925fn bar(f: impl FnOnce(u8) -> u8) -> impl FnOnce(u8) -> u8 {
926 move |x: u8| f(x) * 2
927}
928"#,
929 );
930 }
931
932 #[test]
933 fn skip_closure_parameter_hints() {
934 check_with_config(
935 InlayHintsConfig {
936 type_hints: true,
937 hide_closure_parameter_hints: true,
938 ..DISABLED_CONFIG
939 },
940 r#"
941//- minicore: fn
942struct Foo;
943impl Foo {
944 fn foo(self: Self) {}
945 fn bar(self: &Self) {}
946}
947fn main() {
948 let closure = |x, y| x + y;
949 // ^^^^^^^ impl Fn(i32, i32) -> {unknown}
950 closure(2, 3);
951 let point = (10, 20);
952 // ^^^^^ (i32, i32)
953 let (x, y) = point;
954 // ^ i32 ^ i32
955 Foo::foo(Foo);
956 Foo::bar(&Foo);
957}
958"#,
959 );
960 }
961
962 #[test]
963 fn hint_truncation() {
964 check_with_config(
965 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
966 r#"
967struct Smol<T>(T);
968
969struct VeryLongOuterName<T>(T);
970
971fn main() {
972 let a = Smol(0u32);
973 //^ Smol<u32>
974 let b = VeryLongOuterName(0usize);
975 //^ VeryLongOuterName<…>
976 let c = Smol(Smol(0u32))
977 //^ Smol<Smol<…>>
978}"#,
979 );
980 }
981
982 #[test]
983 fn edit_for_let_stmt() {
984 check_edit(
985 TEST_CONFIG,
986 r#"
987struct S<T>(T);
988fn test<F>(v: S<(S<i32>, S<()>)>, f: F) {
989 let a = v;
990 let S((b, c)) = v;
991 let a @ S((b, c)) = v;
992 let a = f;
993}
994"#,
995 expect![[r#"
996 struct S<T>(T);
997 fn test<F>(v: S<(S<i32>, S<()>)>, f: F) {
998 let a: S<(S<i32>, S<()>)> = v;
999 let S((b, c)) = v;
1000 let a @ S((b, c)): S<(S<i32>, S<()>)> = v;
1001 let a: F = f;
1002 }
1003 "#]],
1004 );
1005 }
1006
1007 #[test]
1008 fn edit_for_closure_param() {
1009 check_edit(
1010 TEST_CONFIG,
1011 r#"
1012fn test<T>(t: T) {
1013 let f = |a, b, c| {};
1014 let result = f(42, "", t);
1015}
1016"#,
1017 expect![[r#"
1018 fn test<T>(t: T) {
1019 let f = |a: i32, b: &str, c: T| {};
1020 let result: () = f(42, "", t);
1021 }
1022 "#]],
1023 );
1024 }
1025
1026 #[test]
1027 fn edit_for_closure_ret() {
1028 check_edit(
1029 TEST_CONFIG,
1030 r#"
1031struct S<T>(T);
1032fn test() {
1033 let f = || { 3 };
1034 let f = |a: S<usize>| { S(a) };
1035}
1036"#,
1037 expect![[r#"
1038 struct S<T>(T);
1039 fn test() {
1040 let f = || -> i32 { 3 };
1041 let f = |a: S<usize>| -> S<S<usize>> { S(a) };
1042 }
1043 "#]],
1044 );
1045 }
1046
1047 #[test]
1048 fn edit_prefixes_paths() {
1049 check_edit(
1050 TEST_CONFIG,
1051 r#"
1052pub struct S<T>(T);
1053mod middle {
1054 pub struct S<T, U>(T, U);
1055 pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
1056
1057 mod inner {
1058 pub struct S<T>(T);
1059 }
1060
1061 fn test() {
1062 let a = make();
1063 }
1064}
1065"#,
1066 expect![[r#"
1067 pub struct S<T>(T);
1068 mod middle {
1069 pub struct S<T, U>(T, U);
1070 pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
1071
1072 mod inner {
1073 pub struct S<T>(T);
1074 }
1075
1076 fn test() {
1077 let a: S<inner::S<i64>, crate::S<usize>> = make();
1078 }
1079 }
1080 "#]],
1081 );
1082 }
1083
1084 #[test]
1085 fn no_edit_for_top_pat_where_type_annotation_is_invalid() {
1086 check_no_edit(
1087 TEST_CONFIG,
1088 r#"
1089fn test() {
1090 if let a = 42 {}
1091 while let a = 42 {}
1092 match 42 {
1093 a => (),
1094 }
1095}
1096"#,
1097 )
1098 }
1099
1100 #[test]
1101 fn no_edit_for_opaque_type() {
1102 check_no_edit(
1103 TEST_CONFIG,
1104 r#"
1105trait Trait {}
1106struct S<T>(T);
1107fn foo() -> impl Trait {}
1108fn bar() -> S<impl Trait> {}
1109fn test() {
1110 let a = foo();
1111 let a = bar();
1112 let f = || { foo() };
1113 let f = || { bar() };
1114}
1115"#,
1116 );
1117 }
1118
1119 #[test]
1120 fn no_edit_for_closure_return_without_body_block() {
1121 let config = InlayHintsConfig {
1122 closure_return_type_hints: ClosureReturnTypeHints::Always,
1123 ..TEST_CONFIG
1124 };
1125 check_edit(
1126 config,
1127 r#"
1128struct S<T>(T);
1129fn test() {
1130 let f = || 3;
1131 let f = |a: S<usize>| S(a);
1132}
1133"#,
1134 expect![[r#"
1135 struct S<T>(T);
1136 fn test() {
1137 let f = || -> i32 { 3 };
1138 let f = |a: S<usize>| -> S<S<usize>> { S(a) };
1139 }
1140 "#]],
1141 );
1142 }
1143
1144 #[test]
1145 fn type_hints_async_block() {
1146 check_types(
1147 r#"
1148//- minicore: future
1149async fn main() {
1150 let _x = async { 8_i32 };
1151 //^^ impl Future<Output = i32>
1152}"#,
1153 );
1154 }
1155
1156 #[test]
1157 fn type_hints_async_block_with_tail_return_exp() {
1158 check_types(
1159 r#"
1160//- minicore: future
1161async fn main() {
1162 let _x = async {
1163 //^^ impl Future<Output = i32>
1164 return 8_i32;
1165 };
1166}"#,
1167 );
1168 }
1169
1170 #[test]
1171 fn works_in_included_file() {
1172 check_types(
1173 r#"
1174//- minicore: include
1175//- /main.rs
1176include!("foo.rs");
1177//- /foo.rs
1178fn main() {
1179 let _x = 42;
1180 //^^ i32
1181}"#,
1182 );
1183 }
1184
1185 #[test]
1186 fn collapses_nested_impl_projections() {
1187 check_types(
1188 r#"
1189//- minicore: sized
1190trait T {
1191 type Assoc;
1192 fn f(self) -> Self::Assoc;
1193}
1194
1195trait T2 {}
1196trait T3<T> {}
1197
1198fn f(it: impl T<Assoc: T2>) {
1199 let l = it.f();
1200 // ^ impl T2
1201}
1202
1203fn f2<G: T<Assoc: T2 + 'static>>(it: G) {
1204 let l = it.f();
1205 //^ impl T2 + 'static
1206}
1207
1208fn f3<G: T>(it: G) where <G as T>::Assoc: T2 {
1209 let l = it.f();
1210 //^ impl T2
1211}
1212
1213fn f4<G: T<Assoc: T2 + T3<()>>>(it: G) {
1214 let l = it.f();
1215 //^ impl T2 + T3<()>
1216}
1217
1218fn f5<G: T<Assoc = ()>>(it: G) {
1219 let l = it.f();
1220 //^ ()
1221}
1222"#,
1223 );
1224 }
1225
1226 #[test]
1227 fn regression_19007() {
1228 check_types(
1229 r#"
1230trait Foo {
1231 type Assoc;
1232
1233 fn foo(&self) -> Self::Assoc;
1234}
1235
1236trait Bar {
1237 type Target;
1238}
1239
1240trait Baz<T> {}
1241
1242struct Struct<T: Foo> {
1243 field: T,
1244}
1245
1246impl<T> Struct<T>
1247where
1248 T: Foo,
1249 T::Assoc: Baz<<T::Assoc as Bar>::Target> + Bar,
1250{
1251 fn f(&self) {
1252 let x = self.field.foo();
1253 //^ impl Baz<<<T as Foo>::Assoc as Bar>::Target> + Bar
1254 }
1255}
1256"#,
1257 );
1258 }
1259
1260 #[test]
1261 fn type_param_inlay_hint_has_location_link() {
1262 check_expect(
1263 InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
1264 r#"
1265fn identity<T>(t: T) -> T {
1266 let x = t;
1267 x
1268}
1269"#,
1270 expect![[r#"
1271 [
1272 (
1273 36..37,
1274 [
1275 InlayHintLabelPart {
1276 text: "T",
1277 linked_location: Some(
1278 Computed(
1279 FileRangeWrapper {
1280 file_id: FileId(
1281 0,
1282 ),
1283 range: 12..13,
1284 },
1285 ),
1286 ),
1287 tooltip: "",
1288 },
1289 ],
1290 ),
1291 ]
1292 "#]],
1293 );
1294 }
1295
1296 #[test]
1297 fn const_param_inlay_hint_has_location_link() {
1298 check_expect(
1299 InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
1300 r#"
1301fn f<const N: usize>() {
1302 let x = [0; N];
1303}
1304"#,
1305 expect![[r#"
1306 [
1307 (
1308 33..34,
1309 [
1310 "[i32; ",
1311 InlayHintLabelPart {
1312 text: "N",
1313 linked_location: Some(
1314 Computed(
1315 FileRangeWrapper {
1316 file_id: FileId(
1317 0,
1318 ),
1319 range: 11..12,
1320 },
1321 ),
1322 ),
1323 tooltip: "",
1324 },
1325 "]",
1326 ],
1327 ),
1328 ]
1329 "#]],
1330 );
1331 }
1332
1333 #[test]
1334 fn lifetime_param_inlay_hint_has_location_link() {
1335 check_expect(
1336 InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
1337 r#"
1338struct S<'lt>(*mut &'lt ());
1339
1340fn f<'a>() {
1341 let x = S::<'a>(loop {});
1342}
1343"#,
1344 expect![[r#"
1345 [
1346 (
1347 51..52,
1348 [
1349 InlayHintLabelPart {
1350 text: "S",
1351 linked_location: Some(
1352 Computed(
1353 FileRangeWrapper {
1354 file_id: FileId(
1355 0,
1356 ),
1357 range: 7..8,
1358 },
1359 ),
1360 ),
1361 tooltip: "",
1362 },
1363 "<",
1364 InlayHintLabelPart {
1365 text: "'a",
1366 linked_location: Some(
1367 Computed(
1368 FileRangeWrapper {
1369 file_id: FileId(
1370 0,
1371 ),
1372 range: 35..37,
1373 },
1374 ),
1375 ),
1376 tooltip: "",
1377 },
1378 ">",
1379 ],
1380 ),
1381 ]
1382 "#]],
1383 );
1384 }
1385}