Skip to content

Commit 56e8ba6

Browse files
committed
feat(ide-completion): extra sugar auto-completion async fn ... in impl trait for async fn in trait that's defined in desugar form
1 parent 510a8ff commit 56e8ba6

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

crates/hir/src/lib.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,53 @@ impl Function {
21812181
db.function_data(self.id).has_async_kw()
21822182
}
21832183

2184+
/// Whether this function is a `fn` that returns `impl Future`.
2185+
pub fn is_desugar_async(self, db: &dyn HirDatabase) -> bool {
2186+
if self.is_async(db) || self.is_const(db) {
2187+
return false;
2188+
}
2189+
2190+
let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false };
2191+
2192+
let Some(future_trait_id) =
2193+
db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait())
2194+
else {
2195+
return false;
2196+
};
2197+
2198+
let Some(size_trait_id) =
2199+
db.lang_item(self.ty(db).env.krate, LangItem::Sized).and_then(|t| t.as_trait())
2200+
else {
2201+
return false;
2202+
};
2203+
2204+
let Some(sync_trait_id) =
2205+
db.lang_item(self.ty(db).env.krate, LangItem::Sync).and_then(|t| t.as_trait())
2206+
else {
2207+
return false;
2208+
};
2209+
2210+
// TODO: There's no `LangItem::Send`. How do we get the id of `Send` trait?
2211+
// let Some(send_trait_id) = db.lang_item(self.ty(db).env.krate, LangItem::Send).and_then(|t| t.as_trait()) else {
2212+
// eprint!("no future_trait_id\n");
2213+
// return false
2214+
// };
2215+
2216+
let allowed_to_leaked_types = vec![size_trait_id, sync_trait_id];
2217+
2218+
let mut has_impl_future = false;
2219+
let mut has_types_not_allow_to_leaked = false;
2220+
for impl_trait in impl_traits {
2221+
if impl_trait.id == future_trait_id {
2222+
has_impl_future = true;
2223+
} else if !allowed_to_leaked_types.contains(&impl_trait.id) {
2224+
has_types_not_allow_to_leaked = true;
2225+
}
2226+
}
2227+
2228+
has_impl_future && !has_types_not_allow_to_leaked
2229+
}
2230+
21842231
/// Does this function have `#[test]` attribute?
21852232
pub fn is_test(self, db: &dyn HirDatabase) -> bool {
21862233
db.function_data(self.id).attrs.is_test()

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ fn add_function_impl(
210210
ast::AssocItem::Fn(func) => func,
211211
_ => unreachable!(),
212212
};
213-
213+
// TODO: need `function_decl` that unwraps future in the return type
214214
let function_decl = function_declaration(&transformed_fn, source.file_id.is_macro());
215215
match ctx.config.snippet_cap {
216216
Some(cap) => {
@@ -225,6 +225,42 @@ fn add_function_impl(
225225
item.add_to(acc, ctx.db);
226226
}
227227
}
228+
229+
eprint!("is_desugar_async: {}", func.is_desugar_async(ctx.db));
230+
if func.is_desugar_async(ctx.db) {
231+
let label = format_smolstr!(
232+
"async fn {}({})",
233+
fn_name.display(ctx.db),
234+
if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
235+
);
236+
let mut item = CompletionItem::new(completion_kind, replacement_range, label);
237+
item.lookup_by(format!("async fn {}", fn_name.display(ctx.db)))
238+
.set_documentation(func.docs(ctx.db))
239+
.set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
240+
if let Some(source) = ctx.sema.source(func) {
241+
let assoc_item = ast::AssocItem::Fn(source.value);
242+
if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
243+
let transformed_fn = match transformed_item {
244+
ast::AssocItem::Fn(func) => func,
245+
_ => unreachable!(),
246+
};
247+
248+
let function_decl =
249+
function_declaration(&transformed_fn, source.file_id.is_macro());
250+
match ctx.config.snippet_cap {
251+
Some(cap) => {
252+
let snippet = format!("{function_decl} {{\n $0\n}}");
253+
item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet));
254+
}
255+
None => {
256+
let header = format!("{function_decl} {{");
257+
item.text_edit(TextEdit::replace(replacement_range, header));
258+
}
259+
};
260+
item.add_to(acc, ctx.db);
261+
}
262+
}
263+
}
228264
}
229265

230266
/// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc.

0 commit comments

Comments
 (0)