1 //! Implementation of "type" inlay hints:
3 //! fn f(a: i32, b: i32) -> i32 { a + b }
4 //! let _x /* i32 */= f(4, 4);
6 use hir
::{Semantics, TypeInfo}
;
7 use ide_db
::{base_db::FileId, famous_defs::FamousDefs, RootDatabase}
;
9 use itertools
::Itertools
;
11 ast
::{self, AstNode, HasName}
,
15 use crate::{inlay_hints::closure_has_block_body, InlayHint, InlayHintsConfig, InlayKind}
;
17 use super::label_of_ty
;
20 acc
: &mut Vec
<InlayHint
>,
21 famous_defs @
FamousDefs(sema
, _
): &FamousDefs
<'_
, '_
>,
22 config
: &InlayHintsConfig
,
26 if !config
.type_hints
{
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
;
34 if should_not_display_type_hint(sema
, config
, pat
, &ty
) {
38 let label
= label_of_ty(famous_defs
, config
, ty
)?
;
40 if config
.hide_named_constructor_hints
41 && is_named_constructor(sema
, pat
, &label
.to_string()).is_some()
47 range
: match pat
.name() {
48 Some(name
) => name
.syntax().text_range(),
49 None
=> pat
.syntax().text_range(),
51 kind
: InlayKind
::Type
,
58 fn should_not_display_type_hint(
59 sema
: &Semantics
<'_
, RootDatabase
>,
60 config
: &InlayHintsConfig
,
61 bind_pat
: &ast
::IdentPat
,
66 if pat_ty
.is_unknown() {
70 if sema
.resolve_bind_pat_to_const(bind_pat
).is_some() {
74 for node
in bind_pat
.syntax().ancestors() {
78 if config
.hide_closure_initialization_hints
{
79 if let Some(ast
::Expr
::ClosureExpr(closure
)) = it
.initializer() {
80 if closure_has_block_body(&closure
) {
85 return it
.ty().is_some()
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,
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() ||
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())
109 fn is_named_constructor(
110 sema
: &Semantics
<'_
, RootDatabase
>,
114 let let_node
= pat
.syntax().parent()?
;
115 let expr
= match_ast
! {
117 ast
::LetStmt(it
) => it
.initializer(),
118 ast
::LetExpr(it
) => it
.expr(),
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(),
130 let expr
= match expr
{
131 ast
::Expr
::CallExpr(call
) => match call
.expr()?
{
132 ast
::Expr
::PathExpr(path
) => path
,
135 ast
::Expr
::PathExpr(path
) => path
,
138 let path
= expr
.path()?
;
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()
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(),
156 ast
::PathSegmentKind
::Type { type_ref: Some(ty), trait_ref: None }
=> ty
.to_string(),
159 (ctor_name
== ty_name
).then_some(())
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();
168 .map(|variant
| variant
.name(db
).to_smol_str())
169 .any(|enum_name
| enum_name
== pat_text
)
177 // This module also contains tests for super::closure_ret
179 use expect_test
::expect
;
180 use syntax
::{TextRange, TextSize}
;
181 use test_utils
::extract_annotations
;
183 use crate::{fixture, inlay_hints::InlayHintsConfig}
;
185 use crate::inlay_hints
::tests
::{
186 check
, check_expect
, check_with_config
, DISABLED_CONFIG
, TEST_CONFIG
,
188 use crate::ClosureReturnTypeHints
;
191 fn check_types(ra_fixture
: &str) {
192 check_with_config(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }
, ra_fixture
);
196 fn type_hints_only() {
199 fn foo(a: i32, b: i32) -> i32 { a + b }
208 fn type_hints_bindings_after_at() {
213 let ref foo @ bar @ ref mut baz = 0;
219 if let x @ Some(_) = Some(0) {}
221 let foo @ (bar, baz) = (3, 3);
230 fn default_generic_types_should_not_be_displayed() {
233 struct Test<K, T = u8> { k: K, t: T }
236 let zz = Test { t: 23u8, k: 33 };
241 //^^^^ || -> Test<i32>
247 fn shorten_iterators_in_associated_params() {
250 //- minicore: iterators
253 pub struct SomeIter<T> {}
255 impl<T> SomeIter<T> {
256 pub fn new() -> Self { SomeIter {} }
257 pub fn push(&mut self, t: T) {}
260 impl<T> Iterator for SomeIter<T> {
262 fn next(&mut self) -> Option<Self::Item> {
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>>
279 fn iterator_hint_regression_issue_12674() {
280 // Ensure we don't crash while solving the projection type of iterators.
282 InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }
,
284 //- minicore: iterators
287 fn iter(&self) -> Iter<'_, T> { loop {} }
289 struct Iter<'a, T: 'a>(&'a T);
290 impl<'a, T> Iterator for Iter<'a, T> {
292 fn next(&mut self) -> Option<Self::Item> { loop {} }
294 struct Container<'a> {
295 elements: S<&'a str>,
297 struct SliceIter<'a, T>(&'a T);
298 impl<'a, T> Iterator for SliceIter<'a, T> {
300 fn next(&mut self) -> Option<Self::Item> { loop {} }
303 fn main(a: SliceIter<'_, Container>) {
305 .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
318 linked_location: Some(
331 linked_location: Some(
344 linked_location: Some(
357 linked_location: Some(
377 linked_location: Some(
390 linked_location: Some(
409 fn infer_call_method_return_associated_types_with_generic() {
413 fn default() -> Self;
419 pub fn quux<T: Foo>() -> T::Bar {
420 let y = Default::default();
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 {} }
449 // ^^^ impl Fn(f64, f64)
451 // ^^^ impl Fn(f64, f64) -> u32
453 // ^^^ &dyn Fn(f64, f64) -> u32
455 // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
457 // ^^^ impl Fn(f64, f64) -> u32
459 // ^^^ *const impl Fn(f64, f64) -> u32
466 fn check_hint_range_limit() {
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 {} }
482 // ^^^ impl Fn(f64, f64)
484 // ^^^ impl Fn(f64, f64) -> u32
491 let (analysis
, file_id
) = fixture
::file(fixture
);
492 let expected
= extract_annotations(&analysis
.file_text(file_id
).unwrap());
493 let inlay_hints
= analysis
495 &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }
,
497 Some(TextRange
::new(TextSize
::from(500), TextSize
::from(600))),
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:#?}");
506 fn fn_hints_ptr_rpit_fn_parentheses() {
509 //- minicore: fn, sized
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 {} }
525 // ^^^ *const impl Fn()
527 // ^^^ *const impl Fn()
529 // ^^^ *const (impl Fn() + ?Sized)
531 // ^^^ *const impl Fn()
533 // ^^^ *const (impl Fn() + ?Sized)
535 // ^^^ *const (impl Fn() + Trait)
537 // ^^^ *const (impl Fn() + Trait)
539 // ^^^ *const (impl Fn() + Trait + ?Sized)
541 // ^^^ *const (impl Fn() -> u8 + ?Sized)
543 // ^^^ *const impl Fn()
550 fn unit_structs_have_no_type_hints() {
554 struct SyntheticSyntax;
559 Err(SyntheticSyntax) => (),
566 fn const_pats_have_no_type_hints() {
569 const FOO: usize = 0;
585 enum Option<T> { None, Some(T) }
588 struct Test { a: Option<u32>, b: u8 }
591 struct InnerStruct {}
601 let test = InnerStruct {};
604 let test = unresolved();
606 let test = (42, 'a');
608 let (a, (b, (c,)) = (2, (3, (9.2,));
621 struct Test { a: Option<u32>, b: u8 }
624 let test = Some(Test { a: Some(3), b: 1 });
626 if let None = &test {};
627 if let test = &test {};
629 if let Some(test) = &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 {};
637 if let Some(Test { a: None, b: y }) = &test {};
639 if let Some(Test { b: y, .. }) = &test {};
651 struct Test { a: Option<u32>, b: u8 }
654 let test = Some(Test { a: Some(3), b: 1 });
656 while let Some(Test { a: Some(x), b: y }) = &test {};
663 fn match_arm_list() {
667 struct Test { a: Option<u32>, b: u8 }
670 match Some(Test { a: Some(3), b: 1 }) {
674 Some(Test { a: Some(x), b: y }) => (),
683 fn complete_for_hint() {
686 //- minicore: iterator
690 pub fn new() -> Self { Vec {} }
691 pub fn push(&mut self, t: T) {}
694 impl<T> IntoIterator for Vec<T> {
696 type IntoIter = IntoIter<T>;
699 struct IntoIter<T> {}
701 impl<T> Iterator for IntoIter<T> {
706 let mut data = Vec::new();
720 fn multi_dyn_trait_bounds() {
726 pub fn new() -> Self { Vec {} }
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>>
748 fn shorten_iterator_hints() {
751 //- minicore: iterators
756 impl Iterator for MyIter {
758 fn next(&mut self) -> Option<Self::Item> {
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>
780 fn skip_constructor_and_enum_type_hints() {
784 hide_named_constructor_hints
: true,
788 //- minicore: try, option
789 use core::ops::ControlFlow;
792 pub mod y { pub struct Foo; }
794 pub enum AnotherEnum {
799 struct TupleStruct();
805 fn try_new() -> ControlFlow<(), Self> {
806 ControlFlow::Continue(Struct)
810 struct Generic<T>(T);
821 fn times2(value: i32) -> i32 {
826 let enumb = Enum::Variant(0);
829 let strukt = x::y::Foo;
831 let strukt = Struct::new();
833 let tuple_struct = TupleStruct();
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);
844 let option = Some(0);
845 // ^^^^^^ Option<i32>
847 // ^^^^ fn times2(i32) -> i32
848 let closure = |x: i32| x * 2;
849 // ^^^^^^^ |i32| -> i32
852 fn fallible() -> ControlFlow<()> {
853 let strukt = Struct::try_new()?;
860 fn shows_constructor_type_hints_when_enabled() {
864 use core::ops::ControlFlow;
867 struct TupleStruct();
873 fn try_new() -> ControlFlow<(), Self> {
874 ControlFlow::Continue(Struct)
878 struct Generic<T>(T);
886 let strukt = Struct::new();
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>
898 fn fallible() -> ControlFlow<()> {
899 let strukt = Struct::try_new()?;
913 (0..2).for_each(|increment | { start += increment; });
917 //^^^^^^^^ |i32, i32| -> i32
923 let _: i32 = multiply(1, 2);
925 let multiply_ref = &multiply;
926 //^^^^^^^^^^^^ &|i32, i32| -> i32
928 let return_42 = || 42;
929 //^^^^^^^^^ || -> i32
937 fn return_type_hints_for_closure_without_block() {
940 closure_return_type_hints
: ClosureReturnTypeHints
::Always
,
954 fn skip_closure_type_hints() {
958 hide_closure_initialization_hints
: true,
964 let multiple_2 = |x: i32| { x * 2 };
966 let multiple_2 = |x: i32| x * 2;
967 // ^^^^^^^^^^ |i32| -> i32
969 let (not) = (|x: bool| { !x });
970 // ^^^ |bool| -> bool
972 let (is_zero, _b) = (|x: usize| { x == 0 }, false);
973 // ^^^^^^^ |usize| -> bool
976 let plus_one = |x| { x + 1 };
980 let add_mul = bar(|x: u8| { x + 1 });
981 // ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized
983 let closure = if let Some(6) = add_mul(2).checked_sub(1) {
984 // ^^^^^^^ fn(i32) -> i32
991 fn foo(f: impl FnOnce(u8) -> u8) {}
993 fn bar(f: impl FnOnce(u8) -> u8) -> impl FnOnce(u8) -> u8 {
994 move |x: u8| f(x) * 2
1001 fn hint_truncation() {
1003 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG }
,
1007 struct VeryLongOuterName<T>(T);
1012 let b = VeryLongOuterName(0usize);
1013 //^ VeryLongOuterName<…>
1014 let c = Smol(Smol(0u32))