11//! Implementation of "closure captures" inlay hints.
22//!
33//! Tests live in [`bind_pat`][super::bind_pat] module.
4+ use either:: Either ;
45use ide_db:: famous_defs:: FamousDefs ;
56use span:: Edition ;
67use stdx:: { TupleExt , never} ;
@@ -14,26 +15,56 @@ pub(super) fn hints(
1415 acc : & mut Vec < InlayHint > ,
1516 FamousDefs ( sema, _) : & FamousDefs < ' _ , ' _ > ,
1617 config : & InlayHintsConfig < ' _ > ,
17- closure : ast:: ClosureExpr ,
18+ expr : Either < ast:: ClosureExpr , ast :: BlockExpr > ,
1819 edition : Edition ,
1920) -> Option < ( ) > {
2021 if !config. closure_capture_hints {
2122 return None ;
2223 }
23- let ty = & sema. type_of_expr ( & closure. clone ( ) . into ( ) ) ?. original ;
24- let c = ty. as_closure ( ) ?;
25- let captures = c. captured_items ( sema. db ) ;
24+
25+ let ( expr, move_token, capture_anchor) = match expr {
26+ Either :: Left ( closure) => {
27+ let move_token = closure. move_token ( ) ;
28+ let capture_anchor = closure. param_list ( ) ?. pipe_token ( ) ?;
29+ ( closure. into ( ) , move_token, capture_anchor)
30+ }
31+ Either :: Right ( block) => {
32+ let modifier = block. modifier ( ) ?;
33+ match modifier {
34+ ast:: BlockModifier :: Async ( _)
35+ | ast:: BlockModifier :: Gen ( _)
36+ | ast:: BlockModifier :: AsyncGen ( _) => ( ) ,
37+ ast:: BlockModifier :: Unsafe ( _)
38+ | ast:: BlockModifier :: Try { .. }
39+ | ast:: BlockModifier :: Const ( _)
40+ | ast:: BlockModifier :: Label ( _) => return None ,
41+ }
42+ let move_token = block. move_token ( ) ;
43+ let capture_anchor = block. stmt_list ( ) ?. l_curly_token ( ) ?;
44+ ( block. into ( ) , move_token, capture_anchor)
45+ }
46+ } ;
47+
48+ let ty = & sema. type_of_expr ( & expr) ?. original ;
49+ let captures = match ty. as_closure ( ) {
50+ Some ( closure) => closure. captured_items ( sema. db ) ,
51+ None => ty. as_coroutine ( ) ?. captured_items ( sema. db ) ,
52+ } ;
2653
2754 if captures. is_empty ( ) {
2855 return None ;
2956 }
3057
31- let ( range, label, position, pad_right) = match closure. move_token ( ) {
32- Some ( t) => ( t. text_range ( ) , InlayHintLabel :: default ( ) , InlayHintPosition :: After , false ) ,
33- None => {
34- let l_pipe = closure. param_list ( ) ?. pipe_token ( ) ?. text_range ( ) ;
35- ( l_pipe, InlayHintLabel :: from ( "move" ) , InlayHintPosition :: Before , true )
58+ let ( range, label, position, pad_right) = match move_token {
59+ Some ( token) => {
60+ ( token. text_range ( ) , InlayHintLabel :: default ( ) , InlayHintPosition :: After , false )
3661 }
62+ None => (
63+ capture_anchor. text_range ( ) ,
64+ InlayHintLabel :: from ( "move" ) ,
65+ InlayHintPosition :: Before ,
66+ true ,
67+ ) ,
3768 } ;
3869 let mut hint = InlayHint {
3970 range,
@@ -43,7 +74,7 @@ pub(super) fn hints(
4374 position,
4475 pad_left : false ,
4576 pad_right,
46- resolve_parent : Some ( closure . syntax ( ) . text_range ( ) ) ,
77+ resolve_parent : Some ( expr . syntax ( ) . text_range ( ) ) ,
4778 } ;
4879 hint. label . append_str ( "(" ) ;
4980 let last = captures. len ( ) - 1 ;
@@ -186,6 +217,92 @@ fn main() {
186217 };
187218}
188219
220+ "# ,
221+ ) ;
222+ }
223+
224+ #[ test]
225+ fn all_capture_kinds_async_block ( ) {
226+ check_with_config (
227+ InlayHintsConfig { closure_capture_hints : true , ..DISABLED_CONFIG } ,
228+ r#"
229+ //- minicore: copy, derive, future
230+
231+ #[derive(Copy, Clone)]
232+ struct Copy;
233+
234+ struct NonCopy;
235+
236+ fn main() {
237+ let foo = Copy;
238+ let bar = NonCopy;
239+ let mut baz = NonCopy;
240+ let qux = &mut NonCopy;
241+ async {
242+ // ^ move(&foo, bar, baz, qux)
243+ foo;
244+ bar;
245+ baz;
246+ qux;
247+ };
248+ async {
249+ // ^ move(&foo, &bar, &baz, &qux)
250+ &foo;
251+ &bar;
252+ &baz;
253+ &qux;
254+ };
255+ async {
256+ // ^ move(&mut baz)
257+ &mut baz;
258+ };
259+ async {
260+ // ^ move(&mut baz, &mut *qux)
261+ baz = NonCopy;
262+ *qux = NonCopy;
263+ };
264+ }
265+ "# ,
266+ ) ;
267+ }
268+
269+ #[ test]
270+ fn coroutine_blocks ( ) {
271+ check_with_config (
272+ InlayHintsConfig { closure_capture_hints : true , ..DISABLED_CONFIG } ,
273+ r#"
274+ //- minicore: copy, future
275+ fn main() {
276+ let foo = 0;
277+ gen {
278+ // ^ move(&foo)
279+ foo;
280+ yield ();
281+ };
282+ async gen {
283+ // ^ move(&foo)
284+ foo;
285+ yield ();
286+ };
287+ }
288+ "# ,
289+ ) ;
290+ }
291+
292+ #[ test]
293+ fn legacy_coroutine ( ) {
294+ check_with_config (
295+ InlayHintsConfig { closure_capture_hints : true , ..DISABLED_CONFIG } ,
296+ r#"
297+ //- minicore: copy, coroutine
298+ fn main() {
299+ let foo = 0;
300+ let coroutine = #[coroutine] || {
301+ // ^ move(&foo)
302+ foo;
303+ yield ();
304+ };
305+ }
189306"# ,
190307 ) ;
191308 }
@@ -216,6 +333,19 @@ fn main() {
216333 foo;
217334 };
218335}
336+ "# ,
337+ ) ;
338+ check_with_config (
339+ InlayHintsConfig { closure_capture_hints : true , ..DISABLED_CONFIG } ,
340+ r#"
341+ //- minicore: copy, future
342+ fn main() {
343+ let foo = 0;
344+ async move {
345+ // ^^^^ (foo)
346+ foo;
347+ };
348+ }
219349"# ,
220350 ) ;
221351 }
0 commit comments