src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs RUST 594 lines View on github.com → Search inside
1use hir::ModuleDef;2use ide_db::{assists::AssistId, famous_defs::FamousDefs};3use syntax::{4    AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, TextRange,5    ast::{self, HasGenericArgs, HasVisibility},6};78use crate::{AssistContext, Assists};910// Assist: sugar_impl_future_into_async11//12// Rewrites asynchronous function from `-> impl Future` into `async fn`.13// This action does not touch the function body and therefore `async { 0 }`14// block does not transform to just `0`.15//16// ```17// # //- minicore: future18// pub fn foo() -> impl core::future::F$0uture<Output = usize> {19//     async { 0 }20// }21// ```22// ->23// ```24// pub async fn foo() -> usize {25//     async { 0 }26// }27// ```28pub(crate) fn sugar_impl_future_into_async(29    acc: &mut Assists,30    ctx: &AssistContext<'_>,31) -> Option<()> {32    let ret_type: ast::RetType = ctx.find_node_at_offset()?;33    let function = ret_type.syntax().parent().and_then(ast::Fn::cast)?;3435    if function.async_token().is_some() || function.const_token().is_some() {36        return None;37    }3839    let ast::Type::ImplTraitType(return_impl_trait) = ret_type.ty()? else {40        return None;41    };4243    let main_trait_path = return_impl_trait44        .type_bound_list()?45        .bounds()46        .filter_map(|bound| match bound.ty() {47            Some(ast::Type::PathType(trait_path)) => trait_path.path(),48            _ => None,49        })50        .next()?;5152    let trait_type = ctx.sema.resolve_trait(&main_trait_path)?;53    let scope = ctx.sema.scope(main_trait_path.syntax())?;54    if trait_type != FamousDefs(&ctx.sema, scope.krate()).core_future_Future()? {55        return None;56    }57    let future_output = unwrap_future_output(main_trait_path)?;5859    acc.add(60        AssistId::refactor_rewrite("sugar_impl_future_into_async"),61        "Convert `impl Future` into async",62        function.syntax().text_range(),63        |builder| {64            match future_output {65                // Empty tuple66                ast::Type::TupleType(t) if t.fields().next().is_none() => {67                    let mut ret_type_range = ret_type.syntax().text_range();6869                    // find leftover whitespace70                    let whitespace_range = function71                        .param_list()72                        .as_ref()73                        .map(|params| NodeOrToken::Node(params.syntax()))74                        .and_then(following_whitespace);7576                    if let Some(whitespace_range) = whitespace_range {77                        ret_type_range =78                            TextRange::new(whitespace_range.start(), ret_type_range.end());79                    }8081                    builder.delete(ret_type_range);82                }83                _ => {84                    builder.replace(85                        return_impl_trait.syntax().text_range(),86                        future_output.syntax().text(),87                    );88                }89            }9091            let (place_for_async, async_kw) = match function.visibility() {92                Some(vis) => (vis.syntax().text_range().end(), " async"),93                None => (function.syntax().text_range().start(), "async "),94            };95            builder.insert(place_for_async, async_kw);96        },97    )98}99100// Assist: desugar_async_into_impl_future101//102// Rewrites asynchronous function from `async fn` into `-> impl Future`.103// This action does not touch the function body and therefore `0`104// block does not transform to `async { 0 }`.105//106// ```107// # //- minicore: future108// pub as$0ync fn foo() -> usize {109//     0110// }111// ```112// ->113// ```114// pub fn foo() -> impl core::future::Future<Output = usize> {115//     0116// }117// ```118pub(crate) fn desugar_async_into_impl_future(119    acc: &mut Assists,120    ctx: &AssistContext<'_>,121) -> Option<()> {122    let async_token = ctx.find_token_syntax_at_offset(SyntaxKind::ASYNC_KW)?;123    let function = async_token.parent().and_then(ast::Fn::cast)?;124125    let rparen = function.param_list()?.r_paren_token()?;126    let return_type = match function.ret_type() {127        // unable to get a `ty` makes the action inapplicable128        Some(ret_type) => Some(ret_type.ty()?),129        // No type means `-> ()`130        None => None,131    };132133    let scope = ctx.sema.scope(function.syntax())?;134    let module = scope.module();135    let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.sema.db)));136    let future_trait = FamousDefs(&ctx.sema, scope.krate()).core_future_Future()?;137    let trait_path = module.find_path(ctx.db(), ModuleDef::Trait(future_trait), cfg)?;138    let edition = scope.krate().edition(ctx.db());139    let trait_path = trait_path.display(ctx.db(), edition);140141    acc.add(142        AssistId::refactor_rewrite("desugar_async_into_impl_future"),143        "Convert async into `impl Future`",144        function.syntax().text_range(),145        |builder| {146            let mut async_range = async_token.text_range();147148            if let Some(whitespace_range) = following_whitespace(NodeOrToken::Token(async_token)) {149                async_range = TextRange::new(async_range.start(), whitespace_range.end());150            }151            builder.delete(async_range);152153            match return_type {154                Some(ret_type) => builder.replace(155                    ret_type.syntax().text_range(),156                    format!("impl {trait_path}<Output = {ret_type}>"),157                ),158                None => builder.insert(159                    rparen.text_range().end(),160                    format!(" -> impl {trait_path}<Output = ()>"),161                ),162            }163        },164    )165}166167fn unwrap_future_output(path: ast::Path) -> Option<ast::Type> {168    let future_trait = path.segments().last()?;169    let assoc_list = future_trait.generic_arg_list()?;170    let future_assoc = assoc_list.generic_args().next()?;171    match future_assoc {172        ast::GenericArg::AssocTypeArg(output_type) => output_type.ty(),173        _ => None,174    }175}176177fn following_whitespace(nt: NodeOrToken<&SyntaxNode, SyntaxToken>) -> Option<TextRange> {178    let next_token = match nt {179        NodeOrToken::Node(node) => node.next_sibling_or_token(),180        NodeOrToken::Token(token) => token.next_sibling_or_token(),181    }?;182    (next_token.kind() == SyntaxKind::WHITESPACE).then_some(next_token.text_range())183}184185#[cfg(test)]186mod tests {187    use super::*;188    use crate::tests::{check_assist, check_assist_not_applicable};189190    #[test]191    fn sugar_with_use() {192        check_assist(193            sugar_impl_future_into_async,194            r#"195    //- minicore: future196    use core::future::Future;197    fn foo() -> impl F$0uture<Output = ()> {198        todo!()199    }200    "#,201            r#"202    use core::future::Future;203    async fn foo() {204        todo!()205    }206    "#,207        );208209        check_assist(210            sugar_impl_future_into_async,211            r#"212    //- minicore: future213    use core::future::Future;214    fn foo() -> impl F$0uture<Output = usize> {215        todo!()216    }217    "#,218            r#"219    use core::future::Future;220    async fn foo() -> usize {221        todo!()222    }223    "#,224        );225    }226227    #[test]228    fn desugar_with_use() {229        check_assist(230            desugar_async_into_impl_future,231            r#"232    //- minicore: future233    use core::future::Future;234    as$0ync fn foo() {235        todo!()236    }237    "#,238            r#"239    use core::future::Future;240    fn foo() -> impl Future<Output = ()> {241        todo!()242    }243    "#,244        );245246        check_assist(247            desugar_async_into_impl_future,248            r#"249    //- minicore: future250    use core::future;251    as$0ync fn foo() {252        todo!()253    }254    "#,255            r#"256    use core::future;257    fn foo() -> impl future::Future<Output = ()> {258        todo!()259    }260    "#,261        );262263        check_assist(264            desugar_async_into_impl_future,265            r#"266    //- minicore: future267    use core::future::Future;268    as$0ync fn foo() -> usize {269        todo!()270    }271    "#,272            r#"273    use core::future::Future;274    fn foo() -> impl Future<Output = usize> {275        todo!()276    }277    "#,278        );279280        check_assist(281            desugar_async_into_impl_future,282            r#"283    //- minicore: future284    use core::future::Future;285    as$0ync fn foo() -> impl Future<Output = usize> {286        todo!()287    }288    "#,289            r#"290    use core::future::Future;291    fn foo() -> impl Future<Output = impl Future<Output = usize>> {292        todo!()293    }294    "#,295        );296    }297298    #[test]299    fn sugar_without_use() {300        check_assist(301            sugar_impl_future_into_async,302            r#"303    //- minicore: future304    fn foo() -> impl core::future::F$0uture<Output = ()> {305        todo!()306    }307    "#,308            r#"309    async fn foo() {310        todo!()311    }312    "#,313        );314315        check_assist(316            sugar_impl_future_into_async,317            r#"318    //- minicore: future319    fn foo() -> impl core::future::F$0uture<Output = usize> {320        todo!()321    }322    "#,323            r#"324    async fn foo() -> usize {325        todo!()326    }327    "#,328        );329    }330331    #[test]332    fn desugar_without_use() {333        check_assist(334            desugar_async_into_impl_future,335            r#"336    //- minicore: future337    as$0ync fn foo() {338        todo!()339    }340    "#,341            r#"342    fn foo() -> impl core::future::Future<Output = ()> {343        todo!()344    }345    "#,346        );347348        check_assist(349            desugar_async_into_impl_future,350            r#"351    //- minicore: future352    as$0ync fn foo() -> usize {353        todo!()354    }355    "#,356            r#"357    fn foo() -> impl core::future::Future<Output = usize> {358        todo!()359    }360    "#,361        );362    }363364    #[test]365    fn not_applicable() {366        check_assist_not_applicable(367            sugar_impl_future_into_async,368            r#"369    //- minicore: future370    trait Future {371        type Output;372    }373    fn foo() -> impl F$0uture<Output = ()> {374        todo!()375    }376    "#,377        );378379        check_assist_not_applicable(380            sugar_impl_future_into_async,381            r#"382    //- minicore: future383    trait Future {384        type Output;385    }386    fn foo() -> impl F$0uture<Output = usize> {387        todo!()388    }389    "#,390        );391392        check_assist_not_applicable(393            sugar_impl_future_into_async,394            r#"395    //- minicore: future396    f$0n foo() -> impl core::future::Future<Output = usize> {397        todo!()398    }399    "#,400        );401402        check_assist_not_applicable(403            desugar_async_into_impl_future,404            r#"405    async f$0n foo() {406        todo!()407    }408    "#,409        );410    }411412    #[test]413    fn sugar_definition_with_use() {414        check_assist(415            sugar_impl_future_into_async,416            r#"417    //- minicore: future418    use core::future::Future;419    fn foo() -> impl F$0uture<Output = ()>;420    "#,421            r#"422    use core::future::Future;423    async fn foo();424    "#,425        );426427        check_assist(428            sugar_impl_future_into_async,429            r#"430    //- minicore: future431    use core::future::Future;432    fn foo() -> impl F$0uture<Output = usize>;433    "#,434            r#"435    use core::future::Future;436    async fn foo() -> usize;437    "#,438        );439    }440441    #[test]442    fn sugar_definition_without_use() {443        check_assist(444            sugar_impl_future_into_async,445            r#"446    //- minicore: future447    fn foo() -> impl core::future::F$0uture<Output = ()>;448    "#,449            r#"450    async fn foo();451    "#,452        );453454        check_assist(455            sugar_impl_future_into_async,456            r#"457    //- minicore: future458    fn foo() -> impl core::future::F$0uture<Output = usize>;459    "#,460            r#"461    async fn foo() -> usize;462    "#,463        );464    }465466    #[test]467    fn sugar_more_types() {468        check_assist(469            sugar_impl_future_into_async,470            r#"471    //- minicore: future472    fn foo() -> impl core::future::F$0uture<Output = ()> + Send + Sync;473    "#,474            r#"475    async fn foo();476    "#,477        );478479        check_assist(480            sugar_impl_future_into_async,481            r#"482    //- minicore: future483    fn foo() -> impl core::future::F$0uture<Output = usize> + Debug;484    "#,485            r#"486    async fn foo() -> usize;487    "#,488        );489490        check_assist(491            sugar_impl_future_into_async,492            r#"493    //- minicore: future494    fn foo() -> impl core::future::F$0uture<Output = (usize)> + Debug;495    "#,496            r#"497    async fn foo() -> (usize);498    "#,499        );500501        check_assist(502            sugar_impl_future_into_async,503            r#"504    //- minicore: future505    fn foo() -> impl core::future::F$0uture<Output = (usize, usize)> + Debug;506    "#,507            r#"508    async fn foo() -> (usize, usize);509    "#,510        );511512        check_assist(513            sugar_impl_future_into_async,514            r#"515    //- minicore: future516    fn foo() -> impl core::future::Future<Output = impl core::future::F$0uture<Output = ()> + Send>;517    "#,518            r#"519    async fn foo() -> impl core::future::Future<Output = ()> + Send;520    "#,521        );522    }523524    #[test]525    fn sugar_with_modifiers() {526        check_assist_not_applicable(527            sugar_impl_future_into_async,528            r#"529    //- minicore: future530    const fn foo() -> impl core::future::F$0uture<Output = ()>;531    "#,532        );533534        check_assist(535            sugar_impl_future_into_async,536            r#"537            //- minicore: future538            pub(crate) unsafe fn foo() -> impl core::future::F$0uture<Output = usize>;539        "#,540            r#"541            pub(crate) async unsafe fn foo() -> usize;542        "#,543        );544545        check_assist(546            sugar_impl_future_into_async,547            r#"548    //- minicore: future549    unsafe fn foo() -> impl core::future::F$0uture<Output = ()>;550    "#,551            r#"552    async unsafe fn foo();553    "#,554        );555556        check_assist(557            sugar_impl_future_into_async,558            r#"559    //- minicore: future560    unsafe extern "C" fn foo() -> impl core::future::F$0uture<Output = ()>;561    "#,562            r#"563    async unsafe extern "C" fn foo();564    "#,565        );566567        check_assist(568            sugar_impl_future_into_async,569            r#"570    //- minicore: future571    fn foo<T>() -> impl core::future::F$0uture<Output = T>;572    "#,573            r#"574    async fn foo<T>() -> T;575    "#,576        );577578        check_assist(579            sugar_impl_future_into_async,580            r#"581    //- minicore: future582    fn foo<T>() -> impl core::future::F$0uture<Output = T>583    where584        T: Sized;585    "#,586            r#"587    async fn foo<T>() -> T588    where589        T: Sized;590    "#,591        );592    }593}

Code quality findings 34

Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
pub(crate) unsafe fn foo() -> impl core::future::F$0uture<Output = usize>;
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
pub(crate) async unsafe fn foo() -> usize;
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe fn foo() -> impl core::future::F$0uture<Output = ()>;
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
async unsafe fn foo();
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe extern "C" fn foo() -> impl core::future::F$0uture<Output = ()>;
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
async unsafe extern "C" fn foo();
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
.filter_map(|bound| match bound.ty() {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match future_assoc {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let next_token = match nt {
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use super::*;
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.