Skip to main content

ide/inlay_hints/
bind_pat.rs

1//! Implementation of "type" inlay hints:
2//! ```no_run
3//! fn f(a: i32, b: i32) -> i32 { a + b }
4//! let _x /* i32 */= f(4, 4);
5//! ```
6use 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    // unwrap postfix expressions
151    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    // This module also contains tests for super::closure_ret
191
192    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        // Ensure we don't crash while solving the projection type of iterators.
366        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}