Skip to content

Commit a609ba6

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 cb468ae commit a609ba6

File tree

2 files changed

+36
-56
lines changed

2 files changed

+36
-56
lines changed

crates/hir/src/lib.rs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2381,30 +2381,24 @@ impl Function {
23812381
}
23822382
}
23832383

2384-
pub fn returns_impl_future(self, db: &dyn HirDatabase) -> bool {
2385-
if self.is_async(db) {
2386-
return true;
2387-
}
2384+
/// Returns `Future::Output`.
2385+
pub fn returns_impl_future(self, db: &dyn HirDatabase) -> Option<Type> {
2386+
let future_trait_id = LangItem::Future.resolve_trait(db, self.ty(db).env.krate)?;
23882387

2389-
let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false };
2390-
let Some(future_trait_id) = LangItem::Future.resolve_trait(db, self.ty(db).env.krate)
2391-
else {
2392-
return false;
2393-
};
2394-
let Some(sized_trait_id) = LangItem::Sized.resolve_trait(db, self.ty(db).env.krate) else {
2395-
return false;
2396-
};
2388+
let ret_type = self.ret_type(db);
2389+
let canonical_ty =
2390+
Canonical { value: ret_type.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
2391+
if !method_resolution::implements_trait_unique(
2392+
&canonical_ty,
2393+
db,
2394+
&ret_type.env,
2395+
future_trait_id,
2396+
) {
2397+
return None;
2398+
}
23972399

2398-
let mut has_impl_future = false;
2399-
impl_traits
2400-
.filter(|t| {
2401-
let fut = t.id == future_trait_id;
2402-
has_impl_future |= fut;
2403-
!fut && t.id != sized_trait_id
2404-
})
2405-
// all traits but the future trait must be auto traits
2406-
.all(|t| t.is_auto(db))
2407-
&& has_impl_future
2400+
let future_output = LangItem::FutureOutput.resolve_type_alias(db, self.ty(db).env.krate)?;
2401+
ret_type.normalize_trait_assoc_type(db, &[], future_output.into())
24082402
}
24092403

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

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

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
//! }
3232
//! ```
3333
34+
use hir::HirDisplay;
3435
use hir::{MacroCallId, 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)