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, 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}