ide/inlay_hints/
bounds.rs1use ide_db::{FileRange, famous_defs::FamousDefs};
5
6use syntax::ast::{self, AstNode, HasTypeBounds};
7
8use crate::{
9 InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
10 TryToNav,
11};
12
13pub(super) fn hints(
14 acc: &mut Vec<InlayHint>,
15 famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
16 config: &InlayHintsConfig<'_>,
17 params: ast::GenericParamList,
18) -> Option<()> {
19 if !config.sized_bound {
20 return None;
21 }
22
23 let sized_trait = famous_defs.core_marker_Sized();
24
25 for param in params.type_or_const_params() {
26 match param {
27 ast::TypeOrConstParam::Type(type_param) => {
28 let c = type_param.colon_token().map(|it| it.text_range());
29 let has_bounds =
30 type_param.type_bound_list().is_some_and(|it| it.bounds().next().is_some());
31 acc.push(InlayHint {
32 range: c.unwrap_or_else(|| type_param.syntax().text_range()),
33 kind: InlayKind::Type,
34 label: {
35 let mut hint = InlayHintLabel::default();
36 if c.is_none() {
37 hint.parts.push(InlayHintLabelPart {
38 text: ": ".to_owned(),
39 linked_location: None,
40 tooltip: None,
41 });
42 }
43 hint.parts.push(InlayHintLabelPart {
44 text: "Sized".to_owned(),
45 linked_location: sized_trait.and_then(|it| {
46 config.lazy_location_opt(|| {
47 it.try_to_nav(sema).map(|it| {
48 let n = it.call_site();
49 FileRange {
50 file_id: n.file_id,
51 range: n.focus_or_full_range(),
52 }
53 })
54 })
55 }),
56 tooltip: None,
57 });
58 if has_bounds {
59 hint.parts.push(InlayHintLabelPart {
60 text: " +".to_owned(),
61 linked_location: None,
62 tooltip: None,
63 });
64 }
65 hint
66 },
67 text_edit: None,
68 position: InlayHintPosition::After,
69 pad_left: c.is_some(),
70 pad_right: has_bounds,
71 resolve_parent: Some(params.syntax().text_range()),
72 });
73 }
74 ast::TypeOrConstParam::Const(_) => (),
75 }
76 }
77
78 Some(())
79}
80
81#[cfg(test)]
82mod tests {
83 use expect_test::expect;
84
85 use crate::inlay_hints::InlayHintsConfig;
86
87 use crate::inlay_hints::tests::{DISABLED_CONFIG, check_expect, check_with_config};
88
89 #[track_caller]
90 fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
91 check_with_config(InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG }, ra_fixture);
92 }
93
94 #[test]
95 fn smoke() {
96 check(
97 r#"
98fn foo<T>() {}
99 // ^ : Sized
100"#,
101 );
102 }
103
104 #[test]
105 fn with_colon() {
106 check(
107 r#"
108fn foo<T:>() {}
109 // ^ Sized
110"#,
111 );
112 }
113
114 #[test]
115 fn with_colon_and_bounds() {
116 check(
117 r#"
118fn foo<T: 'static>() {}
119 // ^ Sized +
120"#,
121 );
122 }
123
124 #[test]
125 fn location_works() {
126 check_expect(
127 InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG },
128 r#"
129//- minicore: sized
130fn foo<T>() {}
131"#,
132 expect![[r#"
133 [
134 (
135 7..8,
136 [
137 ": ",
138 InlayHintLabelPart {
139 text: "Sized",
140 linked_location: Some(
141 Computed(
142 FileRangeWrapper {
143 file_id: FileId(
144 1,
145 ),
146 range: 446..451,
147 },
148 ),
149 ),
150 tooltip: "",
151 },
152 ],
153 ),
154 ]
155 "#]],
156 );
157 }
158}