]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / ide / src / inlay_hints / closure_captures.rs
CommitLineData
fe692bf9
FG
1//! Implementation of "closure return type" inlay hints.
2//!
3//! Tests live in [`bind_pat`][super::bind_pat] module.
4use ide_db::{base_db::FileId, famous_defs::FamousDefs};
5use syntax::ast::{self, AstNode};
6use text_edit::{TextRange, TextSize};
7
8use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
9
10pub(super) fn hints(
11 acc: &mut Vec<InlayHint>,
12 FamousDefs(sema, _): &FamousDefs<'_, '_>,
13 config: &InlayHintsConfig,
14 _file_id: FileId,
15 closure: ast::ClosureExpr,
16) -> Option<()> {
17 if !config.closure_capture_hints {
18 return None;
19 }
20 let ty = &sema.type_of_expr(&closure.clone().into())?.original;
21 let c = ty.as_closure()?;
22 let captures = c.captured_items(sema.db);
23
24 if captures.is_empty() {
25 return None;
26 }
27
28 let move_kw_range = match closure.move_token() {
29 Some(t) => t.text_range(),
30 None => {
31 let range = closure.syntax().first_token()?.prev_token()?.text_range();
32 let range = TextRange::new(range.end() - TextSize::from(1), range.end());
33 acc.push(InlayHint {
781aab86 34 needs_resolve: false,
fe692bf9
FG
35 range,
36 kind: InlayKind::ClosureCapture,
781aab86 37 label: InlayHintLabel::from("move"),
fe692bf9
FG
38 text_edit: None,
39 position: InlayHintPosition::After,
40 pad_left: false,
41 pad_right: false,
42 });
43 range
44 }
45 };
46 acc.push(InlayHint {
781aab86 47 needs_resolve: false,
fe692bf9
FG
48 range: move_kw_range,
49 kind: InlayKind::ClosureCapture,
50 label: InlayHintLabel::from("("),
51 text_edit: None,
52 position: InlayHintPosition::After,
53 pad_left: false,
54 pad_right: false,
55 });
56 let last = captures.len() - 1;
57 for (idx, capture) in captures.into_iter().enumerate() {
58 let local = capture.local();
59 let source = local.primary_source(sema.db);
60
61 // force cache the source file, otherwise sema lookup will potentially panic
62 _ = sema.parse_or_expand(source.file());
63
781aab86
FG
64 let label = InlayHintLabel::simple(
65 format!(
66 "{}{}",
67 match capture.kind() {
68 hir::CaptureKind::SharedRef => "&",
69 hir::CaptureKind::UniqueSharedRef => "&unique ",
70 hir::CaptureKind::MutableRef => "&mut ",
71 hir::CaptureKind::Move => "",
72 },
73 capture.display_place(sema.db)
74 ),
75 None,
76 source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)),
77 );
fe692bf9 78 acc.push(InlayHint {
781aab86 79 needs_resolve: label.needs_resolve(),
fe692bf9
FG
80 range: move_kw_range,
81 kind: InlayKind::ClosureCapture,
781aab86 82 label,
fe692bf9
FG
83 text_edit: None,
84 position: InlayHintPosition::After,
85 pad_left: false,
86 pad_right: false,
87 });
88
89 if idx != last {
90 acc.push(InlayHint {
781aab86 91 needs_resolve: false,
fe692bf9
FG
92 range: move_kw_range,
93 kind: InlayKind::ClosureCapture,
781aab86 94 label: InlayHintLabel::from(", "),
fe692bf9
FG
95 text_edit: None,
96 position: InlayHintPosition::After,
97 pad_left: false,
98 pad_right: false,
99 });
100 }
101 }
102 acc.push(InlayHint {
781aab86 103 needs_resolve: false,
fe692bf9
FG
104 range: move_kw_range,
105 kind: InlayKind::ClosureCapture,
106 label: InlayHintLabel::from(")"),
107 text_edit: None,
108 position: InlayHintPosition::After,
109 pad_left: false,
110 pad_right: true,
111 });
112
113 Some(())
114}
115
116#[cfg(test)]
117mod tests {
118 use crate::{
119 inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
120 InlayHintsConfig,
121 };
122
123 #[test]
124 fn all_capture_kinds() {
125 check_with_config(
126 InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
127 r#"
128//- minicore: copy, derive
129
130
131#[derive(Copy, Clone)]
132struct Copy;
133
134struct NonCopy;
135
136fn main() {
137 let foo = Copy;
138 let bar = NonCopy;
139 let mut baz = NonCopy;
140 let qux = &mut NonCopy;
141 || {
142// ^ move
143// ^ (
144// ^ &foo
145// ^ , $
146// ^ bar
147// ^ , $
148// ^ baz
149// ^ , $
150// ^ qux
151// ^ )
152 foo;
153 bar;
154 baz;
155 qux;
156 };
157 || {
158// ^ move
159// ^ (
160// ^ &foo
161// ^ , $
162// ^ &bar
163// ^ , $
164// ^ &baz
165// ^ , $
166// ^ &qux
167// ^ )
168 &foo;
169 &bar;
170 &baz;
171 &qux;
172 };
173 || {
174// ^ move
175// ^ (
176// ^ &mut baz
177// ^ )
178 &mut baz;
179 };
180 || {
181// ^ move
182// ^ (
183// ^ &mut baz
184// ^ , $
185// ^ &mut *qux
186// ^ )
187 baz = NonCopy;
188 *qux = NonCopy;
189 };
190}
191"#,
192 );
193 }
194
195 #[test]
196 fn move_token() {
197 check_with_config(
198 InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
199 r#"
200//- minicore: copy, derive
201fn main() {
202 let foo = u32;
203 move || {
204// ^^^^ (
205// ^^^^ foo
206// ^^^^ )
207 foo;
208 };
209}
210"#,
211 );
212 }
213}