Skip to content

Commit b86adf9

Browse files
Fix resugaring of fn() -> impl Future to async fn in completions
It broke due to the RPITIT changes. And also make it more robust, by relying on semantic instead of textual match.
1 parent e9ecff2 commit b86adf9

File tree

2 files changed

+42
-59
lines changed

2 files changed

+42
-59
lines changed

crates/hir/src/lib.rs

+22-25
Original file line numberDiff line numberDiff line change
@@ -2374,33 +2374,30 @@ impl Function {
23742374
}
23752375
}
23762376

2377-
pub fn returns_impl_future(self, db: &dyn HirDatabase) -> bool {
2378-
if self.is_async(db) {
2379-
return true;
2380-
}
2377+
/// Returns `Future::Output`.
2378+
pub fn returns_impl_future(self, db: &dyn HirDatabase) -> Option<Type> {
2379+
let future_trait_id =
2380+
db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait())?;
23812381

2382-
let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false };
2383-
let Some(future_trait_id) =
2384-
db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait())
2385-
else {
2386-
return false;
2387-
};
2388-
let Some(sized_trait_id) =
2389-
db.lang_item(self.ty(db).env.krate, LangItem::Sized).and_then(|t| t.as_trait())
2390-
else {
2391-
return false;
2392-
};
2382+
let ret_type = self.ret_type(db);
2383+
let canonical_ty =
2384+
Canonical { value: ret_type.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
2385+
// The `is_async()` is an optimization.
2386+
if !self.is_async(db)
2387+
&& !method_resolution::implements_trait_unique(
2388+
&canonical_ty,
2389+
db,
2390+
&ret_type.env,
2391+
future_trait_id,
2392+
)
2393+
{
2394+
return None;
2395+
}
23932396

2394-
let mut has_impl_future = false;
2395-
impl_traits
2396-
.filter(|t| {
2397-
let fut = t.id == future_trait_id;
2398-
has_impl_future |= fut;
2399-
!fut && t.id != sized_trait_id
2400-
})
2401-
// all traits but the future trait must be auto traits
2402-
.all(|t| t.is_auto(db))
2403-
&& has_impl_future
2397+
let future_output = db
2398+
.lang_item(self.ty(db).env.krate, LangItem::FutureOutput)
2399+
.and_then(|t| t.as_type_alias())?;
2400+
ret_type.normalize_trait_assoc_type(db, &[], future_output.into())
24042401
}
24052402

24062403
/// Does this function have `#[test]` attribute?

crates/ide-completion/src/completions/item_list/trait_impl.rs

+20-34
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
//! }
3232
//! ```
3333
34+
use hir::HirDisplay;
3435
use hir::{MacroFileId, Name, db::ExpandDatabase};
3536
use ide_db::text_edit::TextEdit;
3637
use ide_db::{
@@ -39,7 +40,7 @@ use ide_db::{
3940
};
4041
use syntax::{
4142
AstNode, SmolStr, SyntaxElement, SyntaxKind, T, TextRange, ToSmolStr,
42-
ast::{self, HasGenericArgs, HasTypeBounds, edit_in_place::AttrsOwnerEdit, make},
43+
ast::{self, HasTypeBounds, edit_in_place::AttrsOwnerEdit, make},
4344
format_smolstr, ted,
4445
};
4546

@@ -185,12 +186,12 @@ fn add_function_impl(
185186
let fn_name = &func.name(ctx.db);
186187
let sugar: &[_] = if func.is_async(ctx.db) {
187188
&[AsyncSugaring::Async, AsyncSugaring::Desugar]
188-
} else if func.returns_impl_future(ctx.db) {
189-
&[AsyncSugaring::Plain, AsyncSugaring::Resugar]
189+
} else if let Some(future_output) = func.returns_impl_future(ctx.db) {
190+
&[AsyncSugaring::Plain, AsyncSugaring::Resugar { future_output }]
190191
} else {
191192
&[AsyncSugaring::Plain]
192193
};
193-
for &sugaring in sugar {
194+
for sugaring in sugar {
194195
add_function_impl_(acc, ctx, replacement_range, func, impl_def, fn_name, sugaring);
195196
}
196197
}
@@ -202,9 +203,9 @@ fn add_function_impl_(
202203
func: hir::Function,
203204
impl_def: hir::Impl,
204205
fn_name: &Name,
205-
async_sugaring: AsyncSugaring,
206+
async_sugaring: &AsyncSugaring,
206207
) {
207-
let async_ = if let AsyncSugaring::Async | AsyncSugaring::Resugar = async_sugaring {
208+
let async_ = if let AsyncSugaring::Async | AsyncSugaring::Resugar { .. } = async_sugaring {
208209
"async "
209210
} else {
210211
""
@@ -248,10 +249,10 @@ fn add_function_impl_(
248249
}
249250
}
250251

251-
#[derive(Copy, Clone)]
252+
#[derive(Clone)]
252253
enum AsyncSugaring {
253254
Desugar,
254-
Resugar,
255+
Resugar { future_output: hir::Type },
255256
Async,
256257
Plain,
257258
}
@@ -285,7 +286,7 @@ fn get_transformed_fn(
285286
ctx: &CompletionContext<'_>,
286287
fn_: ast::Fn,
287288
impl_def: hir::Impl,
288-
async_: AsyncSugaring,
289+
async_: &AsyncSugaring,
289290
) -> Option<ast::Fn> {
290291
let trait_ = impl_def.trait_(ctx.db)?;
291292
let source_scope = &ctx.sema.scope(fn_.syntax())?;
@@ -323,31 +324,16 @@ fn get_transformed_fn(
323324
}
324325
fn_.async_token().unwrap().detach();
325326
}
326-
AsyncSugaring::Resugar => {
327-
let ty = fn_.ret_type()?.ty()?;
328-
match &ty {
329-
// best effort guessing here
330-
ast::Type::ImplTraitType(t) => {
331-
let output = t.type_bound_list()?.bounds().find_map(|b| match b.ty()? {
332-
ast::Type::PathType(p) => {
333-
let p = p.path()?.segment()?;
334-
if p.name_ref()?.text() != "Future" {
335-
return None;
336-
}
337-
match p.generic_arg_list()?.generic_args().next()? {
338-
ast::GenericArg::AssocTypeArg(a)
339-
if a.name_ref()?.text() == "Output" =>
340-
{
341-
a.ty()
342-
}
343-
_ => None,
344-
}
345-
}
346-
_ => None,
347-
})?;
348-
ted::replace(ty.syntax(), output.syntax());
349-
}
350-
_ => (),
327+
AsyncSugaring::Resugar { future_output } => {
328+
let ast_ret = fn_.ret_type()?;
329+
if future_output.is_unit() {
330+
ted::remove(ast_ret.syntax());
331+
} else {
332+
let ret = future_output
333+
.display_source_code(ctx.db, ctx.module.into(), true)
334+
.unwrap_or_else(|_| "_".to_owned());
335+
let ast_ret_ty = ast_ret.ty()?;
336+
ted::replace(ast_ret_ty.syntax(), make::ty(&ret).syntax().clone_for_update());
351337
}
352338
ted::prepend_child(fn_.syntax(), make::token(T![async]));
353339
}

0 commit comments

Comments
 (0)