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!()