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