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.rs150
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;
+ };
+}
+"#,
+ );
}
}