Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/inlay_hints/closure_captures.rs')
| -rw-r--r-- | crates/ide/src/inlay_hints/closure_captures.rs | 150 |
1 files changed, 140 insertions, 10 deletions
diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs index df2c42a68c..3f0e4e7b05 100644 --- a/crates/ide/src/inlay_hints/closure_captures.rs +++ b/crates/ide/src/inlay_hints/closure_captures.rs @@ -1,6 +1,7 @@ //! Implementation of "closure captures" inlay hints. //! //! Tests live in [`bind_pat`][super::bind_pat] module. +use either::Either; use ide_db::famous_defs::FamousDefs; use span::Edition; use stdx::{TupleExt, never}; @@ -14,26 +15,56 @@ pub(super) fn hints( acc: &mut Vec<InlayHint>, FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig<'_>, - closure: ast::ClosureExpr, + expr: Either<ast::ClosureExpr, ast::BlockExpr>, edition: Edition, ) -> Option<()> { if !config.closure_capture_hints { return None; } - let ty = &sema.type_of_expr(&closure.clone().into())?.original; - let c = ty.as_closure()?; - let captures = c.captured_items(sema.db); + + let (expr, move_token, capture_anchor) = match expr { + Either::Left(closure) => { + let move_token = closure.move_token(); + let capture_anchor = closure.param_list()?.pipe_token()?; + (closure.into(), move_token, capture_anchor) + } + Either::Right(block) => { + let modifier = block.modifier()?; + match modifier { + ast::BlockModifier::Async(_) + | ast::BlockModifier::Gen(_) + | ast::BlockModifier::AsyncGen(_) => (), + ast::BlockModifier::Unsafe(_) + | ast::BlockModifier::Try { .. } + | ast::BlockModifier::Const(_) + | ast::BlockModifier::Label(_) => return None, + } + let move_token = block.move_token(); + let capture_anchor = block.stmt_list()?.l_curly_token()?; + (block.into(), move_token, capture_anchor) + } + }; + + let ty = &sema.type_of_expr(&expr)?.original; + let captures = match ty.as_closure() { + Some(closure) => closure.captured_items(sema.db), + None => ty.as_coroutine()?.captured_items(sema.db), + }; if captures.is_empty() { return None; } - let (range, label, position, pad_right) = match closure.move_token() { - Some(t) => (t.text_range(), InlayHintLabel::default(), InlayHintPosition::After, false), - None => { - let l_pipe = closure.param_list()?.pipe_token()?.text_range(); - (l_pipe, InlayHintLabel::from("move"), InlayHintPosition::Before, true) + let (range, label, position, pad_right) = match move_token { + Some(token) => { + (token.text_range(), InlayHintLabel::default(), InlayHintPosition::After, false) } + None => ( + capture_anchor.text_range(), + InlayHintLabel::from("move"), + InlayHintPosition::Before, + true, + ), }; let mut hint = InlayHint { range, @@ -43,7 +74,7 @@ pub(super) fn hints( position, pad_left: false, pad_right, - resolve_parent: Some(closure.syntax().text_range()), + resolve_parent: Some(expr.syntax().text_range()), }; hint.label.append_str("("); let last = captures.len() - 1; @@ -191,6 +222,92 @@ fn main() { } #[test] + fn all_capture_kinds_async_block() { + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, derive, future + +#[derive(Copy, Clone)] +struct Copy; + +struct NonCopy; + +fn main() { + let foo = Copy; + let bar = NonCopy; + let mut baz = NonCopy; + let qux = &mut NonCopy; + async { + // ^ move(&foo, bar, baz, qux) + foo; + bar; + baz; + qux; + }; + async { + // ^ move(&foo, &bar, &baz, &qux) + &foo; + &bar; + &baz; + &qux; + }; + async { + // ^ move(&mut baz) + &mut baz; + }; + async { + // ^ move(&mut baz, &mut *qux) + baz = NonCopy; + *qux = NonCopy; + }; +} +"#, + ); + } + + #[test] + fn coroutine_blocks() { + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, future +fn main() { + let foo = 0; + gen { + // ^ move(&foo) + foo; + yield (); + }; + async gen { + // ^ move(&foo) + foo; + yield (); + }; +} +"#, + ); + } + + #[test] + fn legacy_coroutine() { + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, coroutine +fn main() { + let foo = 0; + let coroutine = #[coroutine] || { + // ^ move(&foo) + foo; + yield (); + }; +} +"#, + ); + } + + #[test] fn move_token() { check_with_config( InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, @@ -218,5 +335,18 @@ fn main() { } "#, ); + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, future +fn main() { + let foo = 0; + async move { + // ^^^^ (foo) + foo; + }; +} +"#, + ); } } |