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};
7
8use crate::{AssistContext, Assists};
9
10pub(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)?;
34
35 if function.async_token().is_some() || function.const_token().is_some() {
36 return None;
37 }
38
39 let ast::Type::ImplTraitType(return_impl_trait) = ret_type.ty()? else {
40 return None;
41 };
42
43 let main_trait_path = return_impl_trait
44 .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()?;
51
52 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)?;
58
59 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 ast::Type::TupleType(t) if t.fields().next().is_none() => {
67 let mut ret_type_range = ret_type.syntax().text_range();
68
69 let whitespace_range = function
71 .param_list()
72 .as_ref()
73 .map(|params| NodeOrToken::Node(params.syntax()))
74 .and_then(following_whitespace);
75
76 if let Some(whitespace_range) = whitespace_range {
77 ret_type_range =
78 TextRange::new(whitespace_range.start(), ret_type_range.end());
79 }
80
81 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 }
90
91 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}
99
100pub(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)?;
124
125 let rparen = function.param_list()?.r_paren_token()?;
126 let return_type = match function.ret_type() {
127 Some(ret_type) => Some(ret_type.ty()?),
129 None => None,
131 };
132
133 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);
140
141 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();
147
148 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);
152
153 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}
166
167fn 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}
176
177fn 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}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188 use crate::tests::{check_assist, check_assist_not_applicable};
189
190 #[test]
191 fn sugar_with_use() {
192 check_assist(
193 sugar_impl_future_into_async,
194 r#"
195 //- minicore: future
196 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 );
208
209 check_assist(
210 sugar_impl_future_into_async,
211 r#"
212 //- minicore: future
213 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 }
226
227 #[test]
228 fn desugar_with_use() {
229 check_assist(
230 desugar_async_into_impl_future,
231 r#"
232 //- minicore: future
233 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 );
245
246 check_assist(
247 desugar_async_into_impl_future,
248 r#"
249 //- minicore: future
250 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 );
262
263 check_assist(
264 desugar_async_into_impl_future,
265 r#"
266 //- minicore: future
267 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 );
279
280 check_assist(
281 desugar_async_into_impl_future,
282 r#"
283 //- minicore: future
284 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 }
297
298 #[test]
299 fn sugar_without_use() {
300 check_assist(
301 sugar_impl_future_into_async,
302 r#"
303 //- minicore: future
304 fn foo() -> impl core::future::F$0uture<Output = ()> {
305 todo!()
306 }
307 "#,
308 r#"
309 async fn foo() {
310 todo!()
311 }
312 "#,
313 );
314
315 check_assist(
316 sugar_impl_future_into_async,
317 r#"
318 //- minicore: future
319 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 }
330
331 #[test]
332 fn desugar_without_use() {
333 check_assist(
334 desugar_async_into_impl_future,
335 r#"
336 //- minicore: future
337 as$0ync fn foo() {
338 todo!()
339 }
340 "#,
341 r#"
342 fn foo() -> impl core::future::Future<Output = ()> {
343 todo!()
344 }
345 "#,
346 );
347
348 check_assist(
349 desugar_async_into_impl_future,
350 r#"
351 //- minicore: future
352 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 }
363
364 #[test]
365 fn not_applicable() {
366 check_assist_not_applicable(
367 sugar_impl_future_into_async,
368 r#"
369 //- minicore: future
370 trait Future {
371 type Output;
372 }
373 fn foo() -> impl F$0uture<Output = ()> {
374 todo!()
375 }
376 "#,
377 );
378
379 check_assist_not_applicable(
380 sugar_impl_future_into_async,
381 r#"
382 //- minicore: future
383 trait Future {
384 type Output;
385 }
386 fn foo() -> impl F$0uture<Output = usize> {
387 todo!()
388 }
389 "#,
390 );
391
392 check_assist_not_applicable(
393 sugar_impl_future_into_async,
394 r#"
395 //- minicore: future
396 f$0n foo() -> impl core::future::Future<Output = usize> {
397 todo!()
398 }
399 "#,
400 );
401
402 check_assist_not_applicable(
403 desugar_async_into_impl_future,
404 r#"
405 async f$0n foo() {
406 todo!()
407 }
408 "#,
409 );
410 }
411
412 #[test]
413 fn sugar_definition_with_use() {
414 check_assist(
415 sugar_impl_future_into_async,
416 r#"
417 //- minicore: future
418 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 );
426
427 check_assist(
428 sugar_impl_future_into_async,
429 r#"
430 //- minicore: future
431 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 }
440
441 #[test]
442 fn sugar_definition_without_use() {
443 check_assist(
444 sugar_impl_future_into_async,
445 r#"
446 //- minicore: future
447 fn foo() -> impl core::future::F$0uture<Output = ()>;
448 "#,
449 r#"
450 async fn foo();
451 "#,
452 );
453
454 check_assist(
455 sugar_impl_future_into_async,
456 r#"
457 //- minicore: future
458 fn foo() -> impl core::future::F$0uture<Output = usize>;
459 "#,
460 r#"
461 async fn foo() -> usize;
462 "#,
463 );
464 }
465
466 #[test]
467 fn sugar_more_types() {
468 check_assist(
469 sugar_impl_future_into_async,
470 r#"
471 //- minicore: future
472 fn foo() -> impl core::future::F$0uture<Output = ()> + Send + Sync;
473 "#,
474 r#"
475 async fn foo();
476 "#,
477 );
478
479 check_assist(
480 sugar_impl_future_into_async,
481 r#"
482 //- minicore: future
483 fn foo() -> impl core::future::F$0uture<Output = usize> + Debug;
484 "#,
485 r#"
486 async fn foo() -> usize;
487 "#,
488 );
489
490 check_assist(
491 sugar_impl_future_into_async,
492 r#"
493 //- minicore: future
494 fn foo() -> impl core::future::F$0uture<Output = (usize)> + Debug;
495 "#,
496 r#"
497 async fn foo() -> (usize);
498 "#,
499 );
500
501 check_assist(
502 sugar_impl_future_into_async,
503 r#"
504 //- minicore: future
505 fn foo() -> impl core::future::F$0uture<Output = (usize, usize)> + Debug;
506 "#,
507 r#"
508 async fn foo() -> (usize, usize);
509 "#,
510 );
511
512 check_assist(
513 sugar_impl_future_into_async,
514 r#"
515 //- minicore: future
516 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 }
523
524 #[test]
525 fn sugar_with_modifiers() {
526 check_assist_not_applicable(
527 sugar_impl_future_into_async,
528 r#"
529 //- minicore: future
530 const fn foo() -> impl core::future::F$0uture<Output = ()>;
531 "#,
532 );
533
534 check_assist(
535 sugar_impl_future_into_async,
536 r#"
537 //- minicore: future
538 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 );
544
545 check_assist(
546 sugar_impl_future_into_async,
547 r#"
548 //- minicore: future
549 unsafe fn foo() -> impl core::future::F$0uture<Output = ()>;
550 "#,
551 r#"
552 async unsafe fn foo();
553 "#,
554 );
555
556 check_assist(
557 sugar_impl_future_into_async,
558 r#"
559 //- minicore: future
560 unsafe extern "C" fn foo() -> impl core::future::F$0uture<Output = ()>;
561 "#,
562 r#"
563 async unsafe extern "C" fn foo();
564 "#,
565 );
566
567 check_assist(
568 sugar_impl_future_into_async,
569 r#"
570 //- minicore: future
571 fn foo<T>() -> impl core::future::F$0uture<Output = T>;
572 "#,
573 r#"
574 async fn foo<T>() -> T;
575 "#,
576 );
577
578 check_assist(
579 sugar_impl_future_into_async,
580 r#"
581 //- minicore: future
582 fn foo<T>() -> impl core::future::F$0uture<Output = T>
583 where
584 T: Sized;
585 "#,
586 r#"
587 async fn foo<T>() -> T
588 where
589 T: Sized;
590 "#,
591 );
592 }
593}