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
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
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
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#"
1097fn test<T>(t: T) {
1098 let f = |a, b, c| {};
1099 let result = f(42, "", t);
1100}
1101"#,
1102 expect![[r#"
1103 fn test<T>(t: T) {
1104 let f = |a: i32, b: &str, c: T| {};
1105 let result: () = f(42, "", t);
1106 }
1107 "#]],
1108 );
1109 }
1110
1111 #[test]
1112 fn edit_for_closure_ret() {
1113 check_edit(
1114 TEST_CONFIG,
1115 r#"
1116struct S<T>(T);
1117fn test() {
1118 let f = || { 3 };
1119 let f = |a: S<usize>| { S(a) };
1120}
1121"#,
1122 expect![[r#"
1123 struct S<T>(T);
1124 fn test() {
1125 let f = || -> i32 { 3 };
1126 let f = |a: S<usize>| -> S<S<usize>> { S(a) };
1127 }
1128 "#]],
1129 );
1130 }
1131
1132 #[test]
1133 fn edit_prefixes_paths() {
1134 check_edit(
1135 TEST_CONFIG,
1136 r#"
1137pub struct S<T>(T);
1138mod middle {
1139 pub struct S<T, U>(T, U);
1140 pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
1141
1142 mod inner {
1143 pub struct S<T>(T);
1144 }
1145
1146 fn test() {
1147 let a = make();
1148 }
1149}
1150"#,
1151 expect![[r#"
1152 pub struct S<T>(T);
1153 mod middle {
1154 pub struct S<T, U>(T, U);
1155 pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
1156
1157 mod inner {
1158 pub struct S<T>(T);
1159 }
1160
1161 fn test() {
1162 let a: S<inner::S<i64>, crate::S<usize>> = make();
1163 }
1164 }
1165 "#]],
1166 );
1167 }
1168
1169 #[test]
1170 fn no_edit_for_top_pat_where_type_annotation_is_invalid() {
1171 check_no_edit(
1172 TEST_CONFIG,
1173 r#"
1174fn test() {
1175 if let a = 42 {}
1176 while let a = 42 {}
1177 match 42 {
1178 a => (),
1179 }
1180}
1181"#,
1182 )
1183 }
1184
1185 #[test]
1186 fn no_edit_for_opaque_type() {
1187 check_no_edit(
1188 TEST_CONFIG,
1189 r#"
1190trait Trait {}
1191struct S<T>(T);
1192fn foo() -> impl Trait {}
1193fn bar() -> S<impl Trait> {}
1194fn test() {
1195 let a = foo();
1196 let a = bar();
1197 let f = || { foo() };
1198 let f = || { bar() };
1199}
1200"#,
1201 );
1202 }
1203
1204 #[test]
1205 fn no_edit_for_closure_return_without_body_block() {
1206 let config = InlayHintsConfig {
1207 closure_return_type_hints: ClosureReturnTypeHints::Always,
1208 ..TEST_CONFIG
1209 };
1210 check_edit(
1211 config,
1212 r#"
1213struct S<T>(T);
1214fn test() {
1215 let f = || 3;
1216 let f = |a: S<usize>| S(a);
1217}
1218"#,
1219 expect![[r#"
1220 struct S<T>(T);
1221 fn test() {
1222 let f = || -> i32 { 3 };
1223 let f = |a: S<usize>| -> S<S<usize>> { S(a) };
1224 }
1225 "#]],
1226 );
1227 }
1228
1229 #[test]
1230 fn type_hints_async_block() {
1231 check_types(
1232 r#"
1233//- minicore: future
1234async fn main() {
1235 let _x = async { 8_i32 };
1236 //^^ impl Future<Output = i32>
1237}"#,
1238 );
1239 }
1240
1241 #[test]
1242 fn type_hints_async_block_with_tail_return_exp() {
1243 check_types(
1244 r#"
1245//- minicore: future
1246async fn main() {
1247 let _x = async {
1248 //^^ impl Future<Output = i32>
1249 return 8_i32;
1250 };
1251}"#,
1252 );
1253 }
1254
1255 #[test]
1256 fn works_in_included_file() {
1257 check_types(
1258 r#"
1259//- minicore: include
1260//- /main.rs
1261include!("foo.rs");
1262//- /foo.rs
1263fn main() {
1264 let _x = 42;
1265 //^^ i32
1266}"#,
1267 );
1268 }
1269
1270 #[test]
1271 fn collapses_nested_impl_projections() {
1272 check_types(
1273 r#"
1274//- minicore: sized
1275trait T {
1276 type Assoc;
1277 fn f(self) -> Self::Assoc;
1278}
1279
1280trait T2 {}
1281trait T3<T> {}
1282
1283fn f(it: impl T<Assoc: T2>) {
1284 let l = it.f();
1285 // ^ impl T2
1286}
1287
1288fn f2<G: T<Assoc: T2 + 'static>>(it: G) {
1289 let l = it.f();
1290 //^ impl T2 + 'static
1291}
1292
1293fn f3<G: T>(it: G) where <G as T>::Assoc: T2 {
1294 let l = it.f();
1295 //^ impl T2
1296}
1297
1298fn f4<G: T<Assoc: T2 + T3<()>>>(it: G) {
1299 let l = it.f();
1300 //^ impl T2 + T3<()>
1301}
1302
1303fn f5<G: T<Assoc = ()>>(it: G) {
1304 let l = it.f();
1305 //^ ()
1306}
1307"#,
1308 );
1309 }
1310
1311 #[test]
1312 fn regression_19007() {
1313 check_types(
1314 r#"
1315trait Foo {
1316 type Assoc;
1317
1318 fn foo(&self) -> Self::Assoc;
1319}
1320
1321trait Bar {
1322 type Target;
1323}
1324
1325trait Baz<T> {}
1326
1327struct Struct<T: Foo> {
1328 field: T,
1329}
1330
1331impl<T> Struct<T>
1332where
1333 T: Foo,
1334 T::Assoc: Baz<<T::Assoc as Bar>::Target> + Bar,
1335{
1336 fn f(&self) {
1337 let x = self.field.foo();
1338 //^ impl Baz<<<T as Foo>::Assoc as Bar>::Target> + Bar
1339 }
1340}
1341"#,
1342 );
1343 }
1344
1345 #[test]
1346 fn type_param_inlay_hint_has_location_link() {
1347 check_expect(
1348 InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
1349 r#"
1350fn identity<T>(t: T) -> T {
1351 let x = t;
1352 x
1353}
1354"#,
1355 expect![[r#"
1356 [
1357 (
1358 36..37,
1359 [
1360 InlayHintLabelPart {
1361 text: "T",
1362 linked_location: Some(
1363 Computed(
1364 FileRangeWrapper {
1365 file_id: FileId(
1366 0,
1367 ),
1368 range: 12..13,
1369 },
1370 ),
1371 ),
1372 tooltip: "",
1373 },
1374 ],
1375 ),
1376 ]
1377 "#]],
1378 );
1379 }
1380
1381 #[test]
1382 fn const_param_inlay_hint_has_location_link() {
1383 check_expect(
1384 InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
1385 r#"
1386fn f<const N: usize>() {
1387 let x = [0; N];
1388}
1389"#,
1390 expect![[r#"
1391 [
1392 (
1393 33..34,
1394 [
1395 "[i32; ",
1396 InlayHintLabelPart {
1397 text: "N",
1398 linked_location: Some(
1399 Computed(
1400 FileRangeWrapper {
1401 file_id: FileId(
1402 0,
1403 ),
1404 range: 11..12,
1405 },
1406 ),
1407 ),
1408 tooltip: "",
1409 },
1410 "]",
1411 ],
1412 ),
1413 ]
1414 "#]],
1415 );
1416 }
1417
1418 #[test]
1419 fn lifetime_param_inlay_hint_has_location_link() {
1420 check_expect(
1421 InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
1422 r#"
1423struct S<'lt>(*mut &'lt ());
1424
1425fn f<'a>() {
1426 let x = S::<'a>(loop {});
1427}
1428"#,
1429 expect![[r#"
1430 [
1431 (
1432 51..52,
1433 [
1434 InlayHintLabelPart {
1435 text: "S",
1436 linked_location: Some(
1437 Computed(
1438 FileRangeWrapper {
1439 file_id: FileId(
1440 0,
1441 ),
1442 range: 7..8,
1443 },
1444 ),
1445 ),
1446 tooltip: "",
1447 },
1448 "<",
1449 InlayHintLabelPart {
1450 text: "'a",
1451 linked_location: Some(
1452 Computed(
1453 FileRangeWrapper {
1454 file_id: FileId(
1455 0,
1456 ),
1457 range: 35..37,
1458 },
1459 ),
1460 ),
1461 tooltip: "",
1462 },
1463 ">",
1464 ],
1465 ),
1466 ]
1467 "#]],
1468 );
1469 }
1470
1471 #[test]
1472 fn ref_multi_trait_impl_trait() {
1473 check_with_config(
1474 InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
1475 r#"
1476//- minicore: sized
1477trait Eq {}
1478trait Ord {}
1479
1480fn foo(argument: &(impl Eq + Ord)) {
1481 let x = argument;
1482 // ^ &(impl Eq + Ord)
1483}
1484 "#,
1485 );
1486 }
1487}