Skip to content

Commit 540891b

Browse files
committed
Auto merge of #18152 - CryZe:highlight-async-block-exit-points, r=Veykril
feat: Highlight exit points of async blocks Async blocks act similar to async functions in that the await keywords are related, but also act like functions where the exit points are related. Fixes #18147
2 parents e2c3ce5 + 4b30d25 commit 540891b

File tree

1 file changed

+101
-73
lines changed

1 file changed

+101
-73
lines changed

src/tools/rust-analyzer/crates/ide/src/highlight_related.rs

+101-73
Original file line numberDiff line numberDiff line change
@@ -281,99 +281,95 @@ fn highlight_references(
281281
}
282282
}
283283

284-
// If `file_id` is None,
285-
pub(crate) fn highlight_exit_points(
284+
fn hl_exit_points(
286285
sema: &Semantics<'_, RootDatabase>,
287-
token: SyntaxToken,
288-
) -> FxHashMap<EditionedFileId, Vec<HighlightedRange>> {
289-
fn hl(
290-
sema: &Semantics<'_, RootDatabase>,
291-
def_token: Option<SyntaxToken>,
292-
body: ast::Expr,
293-
) -> Option<FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>> {
294-
let mut highlights: FxHashMap<EditionedFileId, FxHashSet<_>> = FxHashMap::default();
286+
def_token: Option<SyntaxToken>,
287+
body: ast::Expr,
288+
) -> Option<FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>> {
289+
let mut highlights: FxHashMap<EditionedFileId, FxHashSet<_>> = FxHashMap::default();
290+
291+
let mut push_to_highlights = |file_id, range| {
292+
if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) {
293+
let hrange = HighlightedRange { category: ReferenceCategory::empty(), range };
294+
highlights.entry(file_id).or_default().insert(hrange);
295+
}
296+
};
295297

296-
let mut push_to_highlights = |file_id, range| {
297-
if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) {
298-
let hrange = HighlightedRange { category: ReferenceCategory::empty(), range };
299-
highlights.entry(file_id).or_default().insert(hrange);
298+
if let Some(tok) = def_token {
299+
let file_id = sema.hir_file_for(&tok.parent()?);
300+
let range = Some(tok.text_range());
301+
push_to_highlights(file_id, range);
302+
}
303+
304+
WalkExpandedExprCtx::new(sema).walk(&body, &mut |_, expr| {
305+
let file_id = sema.hir_file_for(expr.syntax());
306+
307+
let range = match &expr {
308+
ast::Expr::TryExpr(try_) => try_.question_mark_token().map(|token| token.text_range()),
309+
ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_)
310+
if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) =>
311+
{
312+
Some(expr.syntax().text_range())
300313
}
314+
_ => None,
301315
};
302316

303-
if let Some(tok) = def_token {
304-
let file_id = sema.hir_file_for(&tok.parent()?);
305-
let range = Some(tok.text_range());
306-
push_to_highlights(file_id, range);
307-
}
317+
push_to_highlights(file_id, range);
318+
});
308319

309-
WalkExpandedExprCtx::new(sema).walk(&body, &mut |_, expr| {
320+
// We should handle `return` separately, because when it is used in a `try` block,
321+
// it will exit the outside function instead of the block itself.
322+
WalkExpandedExprCtx::new(sema)
323+
.with_check_ctx(&WalkExpandedExprCtx::is_async_const_block_or_closure)
324+
.walk(&body, &mut |_, expr| {
310325
let file_id = sema.hir_file_for(expr.syntax());
311326

312327
let range = match &expr {
313-
ast::Expr::TryExpr(try_) => {
314-
try_.question_mark_token().map(|token| token.text_range())
315-
}
316-
ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_)
317-
if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) =>
318-
{
319-
Some(expr.syntax().text_range())
320-
}
328+
ast::Expr::ReturnExpr(expr) => expr.return_token().map(|token| token.text_range()),
321329
_ => None,
322330
};
323331

324332
push_to_highlights(file_id, range);
325333
});
326334

327-
// We should handle `return` separately, because when it is used in a `try` block,
328-
// it will exit the outside function instead of the block itself.
329-
WalkExpandedExprCtx::new(sema)
330-
.with_check_ctx(&WalkExpandedExprCtx::is_async_const_block_or_closure)
331-
.walk(&body, &mut |_, expr| {
332-
let file_id = sema.hir_file_for(expr.syntax());
333-
334-
let range = match &expr {
335-
ast::Expr::ReturnExpr(expr) => {
336-
expr.return_token().map(|token| token.text_range())
337-
}
338-
_ => None,
339-
};
340-
341-
push_to_highlights(file_id, range);
342-
});
343-
344-
let tail = match body {
345-
ast::Expr::BlockExpr(b) => b.tail_expr(),
346-
e => Some(e),
347-
};
335+
let tail = match body {
336+
ast::Expr::BlockExpr(b) => b.tail_expr(),
337+
e => Some(e),
338+
};
348339

349-
if let Some(tail) = tail {
350-
for_each_tail_expr(&tail, &mut |tail| {
351-
let file_id = sema.hir_file_for(tail.syntax());
352-
let range = match tail {
353-
ast::Expr::BreakExpr(b) => b
354-
.break_token()
355-
.map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()),
356-
_ => tail.syntax().text_range(),
357-
};
358-
push_to_highlights(file_id, Some(range));
359-
});
360-
}
361-
Some(highlights)
340+
if let Some(tail) = tail {
341+
for_each_tail_expr(&tail, &mut |tail| {
342+
let file_id = sema.hir_file_for(tail.syntax());
343+
let range = match tail {
344+
ast::Expr::BreakExpr(b) => b
345+
.break_token()
346+
.map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()),
347+
_ => tail.syntax().text_range(),
348+
};
349+
push_to_highlights(file_id, Some(range));
350+
});
362351
}
352+
Some(highlights)
353+
}
363354

355+
// If `file_id` is None,
356+
pub(crate) fn highlight_exit_points(
357+
sema: &Semantics<'_, RootDatabase>,
358+
token: SyntaxToken,
359+
) -> FxHashMap<EditionedFileId, Vec<HighlightedRange>> {
364360
let mut res = FxHashMap::default();
365361
for def in goto_definition::find_fn_or_blocks(sema, &token) {
366362
let new_map = match_ast! {
367363
match def {
368-
ast::Fn(fn_) => fn_.body().and_then(|body| hl(sema, fn_.fn_token(), body.into())),
364+
ast::Fn(fn_) => fn_.body().and_then(|body| hl_exit_points(sema, fn_.fn_token(), body.into())),
369365
ast::ClosureExpr(closure) => {
370366
let pipe_tok = closure.param_list().and_then(|p| p.pipe_token());
371-
closure.body().and_then(|body| hl(sema, pipe_tok, body))
367+
closure.body().and_then(|body| hl_exit_points(sema, pipe_tok, body))
372368
},
373369
ast::BlockExpr(blk) => match blk.modifier() {
374-
Some(ast::BlockModifier::Async(t)) => hl(sema, Some(t), blk.into()),
370+
Some(ast::BlockModifier::Async(t)) => hl_exit_points(sema, Some(t), blk.into()),
375371
Some(ast::BlockModifier::Try(t)) if token.kind() != T![return] => {
376-
hl(sema, Some(t), blk.into())
372+
hl_exit_points(sema, Some(t), blk.into())
377373
},
378374
_ => continue,
379375
},
@@ -517,10 +513,23 @@ pub(crate) fn highlight_yield_points(
517513
match anc {
518514
ast::Fn(fn_) => hl(sema, fn_.async_token(), fn_.body().map(ast::Expr::BlockExpr)),
519515
ast::BlockExpr(block_expr) => {
520-
if block_expr.async_token().is_none() {
516+
let Some(async_token) = block_expr.async_token() else {
521517
continue;
518+
};
519+
520+
// Async blocks act similar to closures. So we want to
521+
// highlight their exit points too, but only if we are on
522+
// the async token.
523+
if async_token == token {
524+
let exit_points = hl_exit_points(
525+
sema,
526+
Some(async_token.clone()),
527+
block_expr.clone().into(),
528+
);
529+
merge_map(&mut res, exit_points);
522530
}
523-
hl(sema, block_expr.async_token(), Some(block_expr.into()))
531+
532+
hl(sema, Some(async_token), Some(block_expr.into()))
524533
},
525534
ast::ClosureExpr(closure) => hl(sema, closure.async_token(), closure.body()),
526535
_ => continue,
@@ -876,6 +885,27 @@ pub async$0 fn foo() {
876885
);
877886
}
878887

888+
#[test]
889+
fn test_hl_exit_points_of_async_blocks() {
890+
check(
891+
r#"
892+
pub fn foo() {
893+
let x = async$0 {
894+
// ^^^^^
895+
0.await;
896+
// ^^^^^
897+
0?;
898+
// ^
899+
return 0;
900+
// ^^^^^^
901+
0
902+
// ^
903+
};
904+
}
905+
"#,
906+
);
907+
}
908+
879909
#[test]
880910
fn test_hl_let_else_yield_points() {
881911
check(
@@ -925,11 +955,9 @@ async fn foo() {
925955
async fn foo() {
926956
(async {
927957
// ^^^^^
928-
(async {
929-
0.await
930-
}).await$0 }
931-
// ^^^^^
932-
).await;
958+
(async { 0.await }).await$0
959+
// ^^^^^
960+
}).await;
933961
}
934962
"#,
935963
);

0 commit comments

Comments
 (0)