Skip to content

Commit 7fd49a4

Browse files
authored
Merge pull request #22811 from Veykril/lukaswirth/push-vntrlsumxwqz
feat: Add capture hints to coroutines
2 parents 46af801 + 1f0f52b commit 7fd49a4

6 files changed

Lines changed: 198 additions & 24 deletions

File tree

crates/hir/src/lib.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ use hir_ty::{
8787
GenericPredicates, InferBodyId, InferenceResult, ParamEnvAndCrate, TyDefId,
8888
TyLoweringDiagnostic, ValueTyDefId, all_super_traits, autoderef, check_orphan_rules,
8989
consteval::try_const_usize,
90-
db::{AnonConstId, InternedClosure, InternedClosureId, InternedCoroutineClosureId},
90+
db::{
91+
AnonConstId, InternedClosure, InternedClosureId, InternedCoroutineClosureId,
92+
InternedCoroutineId,
93+
},
9194
diagnostics::BodyValidationDiagnostic,
9295
direct_super_traits, known_const_to_ast,
9396
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
@@ -4937,15 +4940,7 @@ impl<'db> Closure<'db> {
49374940
AnyClosureId::ClosureId(it) => it.loc(db),
49384941
AnyClosureId::CoroutineClosureId(it) => it.loc(db),
49394942
};
4940-
let InternedClosure { owner: infer_owner, expr: closure, .. } = closure;
4941-
let infer = InferenceResult::of(db, infer_owner);
4942-
let owner = infer_owner.expression_store_owner(db);
4943-
infer.closures_data[&closure]
4944-
.min_captures
4945-
.values()
4946-
.flatten()
4947-
.map(|capture| ClosureCapture { owner, infer_owner, closure, capture })
4948-
.collect()
4943+
captured_items(db, closure)
49494944
}
49504945

49514946
pub fn fn_trait(&self, _db: &dyn HirDatabase) -> FnTrait {
@@ -4964,6 +4959,34 @@ impl<'db> Closure<'db> {
49644959
}
49654960
}
49664961

4962+
/// A coroutine expression, including async, generator, and async-generator coroutines.
4963+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4964+
pub struct Coroutine {
4965+
id: InternedCoroutineId,
4966+
}
4967+
4968+
impl Coroutine {
4969+
/// Returns the values captured by this coroutine.
4970+
pub fn captured_items<'db>(&self, db: &'db dyn HirDatabase) -> Vec<ClosureCapture<'db>> {
4971+
captured_items(db, self.id.loc(db))
4972+
}
4973+
}
4974+
4975+
fn captured_items<'db>(
4976+
db: &'db dyn HirDatabase,
4977+
closure: InternedClosure,
4978+
) -> Vec<ClosureCapture<'db>> {
4979+
let InternedClosure { owner: infer_owner, expr: closure, .. } = closure;
4980+
let infer = InferenceResult::of(db, infer_owner);
4981+
let owner = infer_owner.expression_store_owner(db);
4982+
infer.closures_data[&closure]
4983+
.min_captures
4984+
.values()
4985+
.flatten()
4986+
.map(|capture| ClosureCapture { owner, infer_owner, closure, capture })
4987+
.collect()
4988+
}
4989+
49674990
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49684991
pub enum FnTrait {
49694992
FnOnce,
@@ -5952,6 +5975,14 @@ impl<'db> Type<'db> {
59525975
}
59535976
}
59545977

5978+
/// Returns this type as a coroutine.
5979+
pub fn as_coroutine(&self) -> Option<Coroutine> {
5980+
match self.ty.skip_binder().kind() {
5981+
TyKind::Coroutine(id, _) => Some(Coroutine { id: id.0 }),
5982+
_ => None,
5983+
}
5984+
}
5985+
59555986
pub fn is_fn(&self) -> bool {
59565987
matches!(self.ty.skip_binder().kind(), TyKind::FnDef(..) | TyKind::FnPtr { .. })
59575988
}

crates/ide/src/inlay_hints.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,22 @@ fn hints(
235235
param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it))
236236
}
237237
ast::Expr::ClosureExpr(it) => {
238-
closure_captures::hints(hints, famous_defs, config, it.clone(), file_id.edition(sema.db));
238+
closure_captures::hints(
239+
hints,
240+
famous_defs,
241+
config,
242+
Either::Left(it.clone()),
243+
file_id.edition(sema.db),
244+
);
239245
closure_ret::hints(hints, famous_defs, config, display_target, it)
240246
},
247+
ast::Expr::BlockExpr(it) => closure_captures::hints(
248+
hints,
249+
famous_defs,
250+
config,
251+
Either::Right(it),
252+
file_id.edition(sema.db),
253+
),
241254
ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, it),
242255
ast::Expr::Literal(it) => ra_fixture::hints(hints, famous_defs.0, file_id, config, it),
243256
_ => Some(()),

crates/ide/src/inlay_hints/closure_captures.rs

Lines changed: 140 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Implementation of "closure captures" inlay hints.
22
//!
33
//! Tests live in [`bind_pat`][super::bind_pat] module.
4+
use either::Either;
45
use ide_db::famous_defs::FamousDefs;
56
use span::Edition;
67
use 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
}

crates/rust-analyzer/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ config_data! {
216216
/// to always show them).
217217
inlayHints_closingBraceHints_minLines: usize = 25,
218218

219-
/// Show inlay hints for closure captures.
219+
/// Show inlay hints for closure and coroutine captures.
220220
inlayHints_closureCaptureHints_enable: bool = false,
221221

222222
/// Show inlay type hints for return types of closures.

docs/book/src/configuration_generated.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ to always show them).
991991

992992
Default: `false`
993993

994-
Show inlay hints for closure captures.
994+
Show inlay hints for closure and coroutine captures.
995995

996996

997997
## rust-analyzer.inlayHints.closureReturnTypeHints.enable {#inlayHints.closureReturnTypeHints.enable}

editors/code/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2237,7 +2237,7 @@
22372237
"title": "Inlay Hints",
22382238
"properties": {
22392239
"rust-analyzer.inlayHints.closureCaptureHints.enable": {
2240-
"markdownDescription": "Show inlay hints for closure captures.",
2240+
"markdownDescription": "Show inlay hints for closure and coroutine captures.",
22412241
"default": false,
22422242
"type": "boolean"
22432243
}

0 commit comments

Comments
 (0)