Auto merge of #17985 - riverbl:explicit-enum-discriminant, r=Veykril

Add explicit enum discriminant assist

Add assist for adding explicit discriminants to all variants of an enum.

Closes #17798.
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index 3616fa9..032e4a8 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -374,37 +374,6 @@
         self.arena.alloc(data)
     }
 
-    /// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced
-    /// with the second input.
-    pub fn remove_and_replace(
-        &mut self,
-        id: CrateId,
-        replace_with: CrateId,
-    ) -> Result<(), CyclicDependenciesError> {
-        for (x, data) in self.arena.iter() {
-            if x == id {
-                continue;
-            }
-            for edge in &data.dependencies {
-                if edge.crate_id == id {
-                    self.check_cycle_after_dependency(edge.crate_id, replace_with)?;
-                }
-            }
-        }
-        // if everything was ok, start to replace
-        for (x, data) in self.arena.iter_mut() {
-            if x == id {
-                continue;
-            }
-            for edge in &mut data.dependencies {
-                if edge.crate_id == id {
-                    edge.crate_id = replace_with;
-                }
-            }
-        }
-        Ok(())
-    }
-
     pub fn add_dep(
         &mut self,
         from: CrateId,
@@ -412,26 +381,17 @@
     ) -> Result<(), CyclicDependenciesError> {
         let _p = tracing::info_span!("add_dep").entered();
 
-        self.check_cycle_after_dependency(from, dep.crate_id)?;
-
-        self.arena[from].add_dep(dep);
-        Ok(())
-    }
-
-    /// Check if adding a dep from `from` to `to` creates a cycle. To figure
-    /// that out, look for a  path in the *opposite* direction, from `to` to
-    /// `from`.
-    fn check_cycle_after_dependency(
-        &self,
-        from: CrateId,
-        to: CrateId,
-    ) -> Result<(), CyclicDependenciesError> {
-        if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) {
+        // Check if adding a dep from `from` to `to` creates a cycle. To figure
+        // that out, look for a  path in the *opposite* direction, from `to` to
+        // `from`.
+        if let Some(path) = self.find_path(&mut FxHashSet::default(), dep.crate_id, from) {
             let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
             let err = CyclicDependenciesError { path };
-            assert!(err.from().0 == from && err.to().0 == to);
+            assert!(err.from().0 == from && err.to().0 == dep.crate_id);
             return Err(err);
         }
+
+        self.arena[from].add_dep(dep);
         Ok(())
     }
 
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 96a6e6f..9536f12 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2207,6 +2207,35 @@
         db.function_data(self.id).is_async()
     }
 
+    pub fn returns_impl_future(self, db: &dyn HirDatabase) -> bool {
+        if self.is_async(db) {
+            return true;
+        }
+
+        let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false };
+        let Some(future_trait_id) =
+            db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait())
+        else {
+            return false;
+        };
+        let Some(sized_trait_id) =
+            db.lang_item(self.ty(db).env.krate, LangItem::Sized).and_then(|t| t.as_trait())
+        else {
+            return false;
+        };
+
+        let mut has_impl_future = false;
+        impl_traits
+            .filter(|t| {
+                let fut = t.id == future_trait_id;
+                has_impl_future |= fut;
+                !fut && t.id != sized_trait_id
+            })
+            // all traits but the future trait must be auto traits
+            .all(|t| t.is_auto(db))
+            && has_impl_future
+    }
+
     /// Does this function have `#[test]` attribute?
     pub fn is_test(self, db: &dyn HirDatabase) -> bool {
         db.function_data(self.id).attrs.is_test()
diff --git a/crates/ide-assists/src/handlers/flip_comma.rs b/crates/ide-assists/src/handlers/flip_comma.rs
index f40f271..af2c2c7 100644
--- a/crates/ide-assists/src/handlers/flip_comma.rs
+++ b/crates/ide-assists/src/handlers/flip_comma.rs
@@ -1,4 +1,8 @@
-use syntax::{algo::non_trivia_sibling, Direction, SyntaxKind, T};
+use ide_db::base_db::SourceDatabase;
+use syntax::TextSize;
+use syntax::{
+    algo::non_trivia_sibling, ast, AstNode, Direction, SyntaxKind, SyntaxToken, TextRange, T,
+};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
@@ -21,6 +25,8 @@
     let comma = ctx.find_token_syntax_at_offset(T![,])?;
     let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
     let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
+    let (mut prev_text, mut next_text) = (prev.to_string(), next.to_string());
+    let (mut prev_range, mut next_range) = (prev.text_range(), next.text_range());
 
     // Don't apply a "flip" in case of a last comma
     // that typically comes before punctuation
@@ -34,17 +40,55 @@
         return None;
     }
 
+    if let Some(parent) = comma.parent().and_then(ast::TokenTree::cast) {
+        // An attribute. It often contains a path followed by a token tree (e.g. `align(2)`), so we have
+        // to be smarter.
+        let prev_start =
+            match comma.siblings_with_tokens(Direction::Prev).skip(1).find(|it| it.kind() == T![,])
+            {
+                Some(it) => position_after_token(it.as_token().unwrap()),
+                None => position_after_token(&parent.left_delimiter_token()?),
+            };
+        let prev_end = prev.text_range().end();
+        let next_start = next.text_range().start();
+        let next_end =
+            match comma.siblings_with_tokens(Direction::Next).skip(1).find(|it| it.kind() == T![,])
+            {
+                Some(it) => position_before_token(it.as_token().unwrap()),
+                None => position_before_token(&parent.right_delimiter_token()?),
+            };
+        prev_range = TextRange::new(prev_start, prev_end);
+        next_range = TextRange::new(next_start, next_end);
+        let file_text = ctx.db().file_text(ctx.file_id().file_id());
+        prev_text = file_text[prev_range].to_owned();
+        next_text = file_text[next_range].to_owned();
+    }
+
     acc.add(
         AssistId("flip_comma", AssistKind::RefactorRewrite),
         "Flip comma",
         comma.text_range(),
         |edit| {
-            edit.replace(prev.text_range(), next.to_string());
-            edit.replace(next.text_range(), prev.to_string());
+            edit.replace(prev_range, next_text);
+            edit.replace(next_range, prev_text);
         },
     )
 }
 
+fn position_before_token(token: &SyntaxToken) -> TextSize {
+    match non_trivia_sibling(token.clone().into(), Direction::Prev) {
+        Some(prev_token) => prev_token.text_range().end(),
+        None => token.text_range().start(),
+    }
+}
+
+fn position_after_token(token: &SyntaxToken) -> TextSize {
+    match non_trivia_sibling(token.clone().into(), Direction::Next) {
+        Some(prev_token) => prev_token.text_range().start(),
+        None => token.text_range().end(),
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -89,4 +133,18 @@
         // See https://github.com/rust-lang/rust-analyzer/issues/7693
         check_assist_not_applicable(flip_comma, r#"bar!(a,$0 b)"#);
     }
+
+    #[test]
+    fn flip_comma_attribute() {
+        check_assist(
+            flip_comma,
+            r#"#[repr(align(2),$0 C)] struct Foo;"#,
+            r#"#[repr(C, align(2))] struct Foo;"#,
+        );
+        check_assist(
+            flip_comma,
+            r#"#[foo(bar, baz(1 + 1),$0 qux, other)] struct Foo;"#,
+            r#"#[foo(bar, qux, baz(1 + 1), other)] struct Foo;"#,
+        );
+    }
 }
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index fc6e1eb..e93bb8d 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -31,14 +31,14 @@
 //! }
 //! ```
 
-use hir::HasAttrs;
+use hir::{HasAttrs, Name};
 use ide_db::{
     documentation::HasDocs, path_transform::PathTransform,
     syntax_helpers::insert_whitespace_into_node, traits::get_missing_assoc_items, SymbolKind,
 };
 use syntax::{
-    ast::{self, edit_in_place::AttrsOwnerEdit, HasTypeBounds},
-    format_smolstr, AstNode, SmolStr, SyntaxElement, SyntaxKind, TextRange, ToSmolStr, T,
+    ast::{self, edit_in_place::AttrsOwnerEdit, make, HasGenericArgs, HasTypeBounds},
+    format_smolstr, ted, AstNode, SmolStr, SyntaxElement, SyntaxKind, TextRange, ToSmolStr, T,
 };
 use text_edit::TextEdit;
 
@@ -178,12 +178,36 @@
     func: hir::Function,
     impl_def: hir::Impl,
 ) {
-    let fn_name = func.name(ctx.db);
+    let fn_name = &func.name(ctx.db);
+    let sugar: &[_] = if func.is_async(ctx.db) {
+        &[AsyncSugaring::Async, AsyncSugaring::Desugar]
+    } else if func.returns_impl_future(ctx.db) {
+        &[AsyncSugaring::Plain, AsyncSugaring::Resugar]
+    } else {
+        &[AsyncSugaring::Plain]
+    };
+    for &sugaring in sugar {
+        add_function_impl_(acc, ctx, replacement_range, func, impl_def, fn_name, sugaring);
+    }
+}
 
-    let is_async = func.is_async(ctx.db);
+fn add_function_impl_(
+    acc: &mut Completions,
+    ctx: &CompletionContext<'_>,
+    replacement_range: TextRange,
+    func: hir::Function,
+    impl_def: hir::Impl,
+    fn_name: &Name,
+    async_sugaring: AsyncSugaring,
+) {
+    let async_ = if let AsyncSugaring::Async | AsyncSugaring::Resugar = async_sugaring {
+        "async "
+    } else {
+        ""
+    };
     let label = format_smolstr!(
         "{}fn {}({})",
-        if is_async { "async " } else { "" },
+        async_,
         fn_name.display(ctx.db, ctx.edition),
         if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
     );
@@ -195,22 +219,14 @@
     });
 
     let mut item = CompletionItem::new(completion_kind, replacement_range, label, ctx.edition);
-    item.lookup_by(format!(
-        "{}fn {}",
-        if is_async { "async " } else { "" },
-        fn_name.display(ctx.db, ctx.edition)
-    ))
-    .set_documentation(func.docs(ctx.db))
-    .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
+    item.lookup_by(format!("{}fn {}", async_, fn_name.display(ctx.db, ctx.edition)))
+        .set_documentation(func.docs(ctx.db))
+        .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
 
     if let Some(source) = ctx.sema.source(func) {
-        let assoc_item = ast::AssocItem::Fn(source.value);
-        if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
-            let transformed_fn = match transformed_item {
-                ast::AssocItem::Fn(func) => func,
-                _ => unreachable!(),
-            };
-
+        if let Some(transformed_fn) =
+            get_transformed_fn(ctx, source.value, impl_def, async_sugaring)
+        {
             let function_decl = function_declaration(&transformed_fn, source.file_id.is_macro());
             match ctx.config.snippet_cap {
                 Some(cap) => {
@@ -227,6 +243,14 @@
     }
 }
 
+#[derive(Copy, Clone)]
+enum AsyncSugaring {
+    Desugar,
+    Resugar,
+    Async,
+    Plain,
+}
+
 /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc.
 fn get_transformed_assoc_item(
     ctx: &CompletionContext<'_>,
@@ -251,6 +275,82 @@
     Some(assoc_item)
 }
 
+/// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc.
+fn get_transformed_fn(
+    ctx: &CompletionContext<'_>,
+    fn_: ast::Fn,
+    impl_def: hir::Impl,
+    async_: AsyncSugaring,
+) -> Option<ast::Fn> {
+    let trait_ = impl_def.trait_(ctx.db)?;
+    let source_scope = &ctx.sema.scope(fn_.syntax())?;
+    let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?;
+    let transform = PathTransform::trait_impl(
+        target_scope,
+        source_scope,
+        trait_,
+        ctx.sema.source(impl_def)?.value,
+    );
+
+    let fn_ = fn_.clone_for_update();
+    // FIXME: Paths in nested macros are not handled well. See
+    // `macro_generated_assoc_item2` test.
+    transform.apply(fn_.syntax());
+    fn_.remove_attrs_and_docs();
+    match async_ {
+        AsyncSugaring::Desugar => {
+            match fn_.ret_type() {
+                Some(ret_ty) => {
+                    let ty = ret_ty.ty()?;
+                    ted::replace(
+                        ty.syntax(),
+                        make::ty(&format!("impl Future<Output = {ty}>"))
+                            .syntax()
+                            .clone_for_update(),
+                    );
+                }
+                None => ted::append_child(
+                    fn_.param_list()?.syntax(),
+                    make::ret_type(make::ty("impl Future<Output = ()>"))
+                        .syntax()
+                        .clone_for_update(),
+                ),
+            }
+            fn_.async_token().unwrap().detach();
+        }
+        AsyncSugaring::Resugar => {
+            let ty = fn_.ret_type()?.ty()?;
+            match &ty {
+                // best effort guessing here
+                ast::Type::ImplTraitType(t) => {
+                    let output = t.type_bound_list()?.bounds().find_map(|b| match b.ty()? {
+                        ast::Type::PathType(p) => {
+                            let p = p.path()?.segment()?;
+                            if p.name_ref()?.text() != "Future" {
+                                return None;
+                            }
+                            match p.generic_arg_list()?.generic_args().next()? {
+                                ast::GenericArg::AssocTypeArg(a)
+                                    if a.name_ref()?.text() == "Output" =>
+                                {
+                                    a.ty()
+                                }
+                                _ => None,
+                            }
+                        }
+                        _ => None,
+                    })?;
+                    ted::replace(ty.syntax(), output.syntax());
+                }
+                _ => (),
+            }
+            ted::prepend_child(fn_.syntax(), make::token(T![async]));
+        }
+        AsyncSugaring::Async | AsyncSugaring::Plain => (),
+    }
+    Some(fn_)
+}
+
 fn add_type_alias_impl(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
@@ -1404,4 +1504,132 @@
 "#,
         );
     }
+
+    #[test]
+    fn impl_fut() {
+        check_edit(
+            "fn foo",
+            r#"
+//- minicore: future, send, sized
+use core::future::Future;
+
+trait DesugaredAsyncTrait {
+    fn foo(&self) -> impl Future<Output = usize> + Send;
+}
+
+impl DesugaredAsyncTrait for () {
+    $0
+}
+"#,
+            r#"
+use core::future::Future;
+
+trait DesugaredAsyncTrait {
+    fn foo(&self) -> impl Future<Output = usize> + Send;
+}
+
+impl DesugaredAsyncTrait for () {
+    fn foo(&self) -> impl Future<Output = usize> + Send {
+    $0
+}
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn impl_fut_resugared() {
+        check_edit(
+            "async fn foo",
+            r#"
+//- minicore: future, send, sized
+use core::future::Future;
+
+trait DesugaredAsyncTrait {
+    fn foo(&self) -> impl Future<Output = usize> + Send;
+}
+
+impl DesugaredAsyncTrait for () {
+    $0
+}
+"#,
+            r#"
+use core::future::Future;
+
+trait DesugaredAsyncTrait {
+    fn foo(&self) -> impl Future<Output = usize> + Send;
+}
+
+impl DesugaredAsyncTrait for () {
+    async fn foo(&self) -> usize {
+    $0
+}
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn async_desugared() {
+        check_edit(
+            "fn foo",
+            r#"
+//- minicore: future, send, sized
+use core::future::Future;
+
+trait DesugaredAsyncTrait {
+    async fn foo(&self) -> usize;
+}
+
+impl DesugaredAsyncTrait for () {
+    $0
+}
+"#,
+            r#"
+use core::future::Future;
+
+trait DesugaredAsyncTrait {
+    async fn foo(&self) -> usize;
+}
+
+impl DesugaredAsyncTrait for () {
+     fn foo(&self) -> impl Future<Output = usize> {
+    $0
+}
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn async_() {
+        check_edit(
+            "async fn foo",
+            r#"
+//- minicore: future, send, sized
+use core::future::Future;
+
+trait DesugaredAsyncTrait {
+    async fn foo(&self) -> usize;
+}
+
+impl DesugaredAsyncTrait for () {
+    $0
+}
+"#,
+            r#"
+use core::future::Future;
+
+trait DesugaredAsyncTrait {
+    async fn foo(&self) -> usize;
+}
+
+impl DesugaredAsyncTrait for () {
+    async fn foo(&self) -> usize {
+    $0
+}
+}
+"#,
+        );
+    }
 }
diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs
index 8aad7bf..532d492 100644
--- a/crates/ide-completion/src/tests/item_list.rs
+++ b/crates/ide-completion/src/tests/item_list.rs
@@ -313,6 +313,7 @@
             ct const CONST1: () =
             fn async fn function2()
             fn fn function1()
+            fn fn function2()
             ma makro!(…)            macro_rules! makro
             md module
             ta type Type1 =
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index be99510..2e49af4 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -15,7 +15,7 @@
 use stdx::never;
 use syntax::{
     ast::{self, AstNode, HasGenericParams},
-    format_smolstr, match_ast, NodeOrToken, SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent,
+    format_smolstr, match_ast, SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent,
 };
 use text_edit::TextEdit;
 
@@ -95,26 +95,27 @@
     let famous_defs = FamousDefs(&sema, scope.krate());
 
     let ctx = &mut InlayHintCtx::default();
-    let hints = |node| hints(&mut acc, ctx, &famous_defs, config, file_id, node);
-    match range_limit {
-        // FIXME: This can miss some hints that require the parent of the range to calculate
-        Some(range) => match file.covering_element(range) {
-            NodeOrToken::Token(_) => return acc,
-            NodeOrToken::Node(n) => n
-                .preorder()
-                .filter(|event| matches!(event, WalkEvent::Enter(node) if range.intersect(node.text_range()).is_some()))
-                .for_each(hints),
-        },
-        None => file.preorder().for_each(hints),
+    let mut hints = |event| {
+        if let Some(node) = handle_event(ctx, event) {
+            hints(&mut acc, ctx, &famous_defs, config, file_id, node);
+        }
     };
-
+    let mut preorder = file.preorder();
+    while let Some(event) = preorder.next() {
+        // FIXME: This can miss some hints that require the parent of the range to calculate
+        if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none())
+        {
+            preorder.skip_subtree();
+            continue;
+        }
+        hints(event);
+    }
     acc
 }
 
 #[derive(Default)]
 struct InlayHintCtx {
     lifetime_stacks: Vec<Vec<SmolStr>>,
-    is_param_list: bool,
 }
 
 pub(crate) fn inlay_hints_resolve(
@@ -138,20 +139,52 @@
     let mut acc = Vec::new();
 
     let ctx = &mut InlayHintCtx::default();
-    let hints = |node| hints(&mut acc, ctx, &famous_defs, config, file_id, node);
-
-    let mut res = file.clone();
-    let res = loop {
-        res = match res.child_or_token_at_range(resolve_range) {
-            Some(NodeOrToken::Node(n)) if n.text_range() == resolve_range => break n,
-            Some(NodeOrToken::Node(n)) => n,
-            _ => break res,
-        };
+    let mut hints = |event| {
+        if let Some(node) = handle_event(ctx, event) {
+            hints(&mut acc, ctx, &famous_defs, config, file_id, node);
+        }
     };
-    res.preorder().for_each(hints);
+
+    let mut preorder = file.preorder();
+    while let Some(event) = preorder.next() {
+        // FIXME: This can miss some hints that require the parent of the range to calculate
+        if matches!(&event, WalkEvent::Enter(node) if resolve_range.intersect(node.text_range()).is_none())
+        {
+            preorder.skip_subtree();
+            continue;
+        }
+        hints(event);
+    }
     acc.into_iter().find(|hint| hasher(hint) == hash)
 }
 
+fn handle_event(ctx: &mut InlayHintCtx, node: WalkEvent<SyntaxNode>) -> Option<SyntaxNode> {
+    match node {
+        WalkEvent::Enter(node) => {
+            if let Some(node) = ast::AnyHasGenericParams::cast(node.clone()) {
+                let params = node
+                    .generic_param_list()
+                    .map(|it| {
+                        it.lifetime_params()
+                            .filter_map(|it| {
+                                it.lifetime().map(|it| format_smolstr!("{}", &it.text()[1..]))
+                            })
+                            .collect()
+                    })
+                    .unwrap_or_default();
+                ctx.lifetime_stacks.push(params);
+            }
+            Some(node)
+        }
+        WalkEvent::Leave(n) => {
+            if ast::AnyHasGenericParams::can_cast(n.kind()) {
+                ctx.lifetime_stacks.pop();
+            }
+            None
+        }
+    }
+}
+
 // FIXME: At some point when our hir infra is fleshed out enough we should flip this and traverse the
 // HIR instead of the syntax tree.
 fn hints(
@@ -160,35 +193,8 @@
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
     file_id: EditionedFileId,
-    node: WalkEvent<SyntaxNode>,
+    node: SyntaxNode,
 ) {
-    let node = match node {
-        WalkEvent::Enter(node) => node,
-        WalkEvent::Leave(n) => {
-            if ast::AnyHasGenericParams::can_cast(n.kind()) {
-                ctx.lifetime_stacks.pop();
-                // pop
-            }
-            if ast::ParamList::can_cast(n.kind()) {
-                ctx.is_param_list = false;
-                // pop
-            }
-            return;
-        }
-    };
-
-    if let Some(node) = ast::AnyHasGenericParams::cast(node.clone()) {
-        let params = node
-            .generic_param_list()
-            .map(|it| {
-                it.lifetime_params()
-                    .filter_map(|it| it.lifetime().map(|it| format_smolstr!("{}", &it.text()[1..])))
-                    .collect()
-            })
-            .unwrap_or_default();
-        ctx.lifetime_stacks.push(params);
-    }
-
     closing_brace::hints(hints, sema, config, file_id, node.clone());
     if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
         generic_param::hints(hints, sema, config, any_has_generic_args);
@@ -242,10 +248,6 @@
                 ast::Type::PathType(path) => lifetime::fn_path_hints(hints, ctx, famous_defs, config, file_id, path),
                 _ => Some(()),
             },
-            ast::ParamList(_) => {
-                ctx.is_param_list = true;
-                Some(())
-            },
             _ => Some(()),
         }
     };
diff --git a/crates/ide/src/inlay_hints/lifetime.rs b/crates/ide/src/inlay_hints/lifetime.rs
index de46367..653e3a6 100644
--- a/crates/ide/src/inlay_hints/lifetime.rs
+++ b/crates/ide/src/inlay_hints/lifetime.rs
@@ -532,7 +532,7 @@
                        //^^ for<'1>
                           //^'1
                                   //^'1
-fn fn_ptr2(a: for<'a> fn(&()) -> &())) {}
+fn fn_ptr2(a: for<'a> fn(&()) -> &()) {}
                //^'0, $
                        //^'0
                                //^'0
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 7834238..4fc9ef3 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -1554,6 +1554,6 @@
 
 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
     if let Err(err) = graph.add_dep(from, dep) {
-        tracing::error!("{}", err)
+        tracing::warn!("{}", err)
     }
 }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index c884216..714c835 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -429,6 +429,8 @@
         completion_callable_snippets: CallableCompletionDef  = CallableCompletionDef::FillArguments,
         /// Whether to show full function/method signatures in completion docs.
         completion_fullFunctionSignatures_enable: bool = false,
+        /// Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden.
+        completion_hideDeprecated: bool = false,
         /// Maximum number of completions to return. If `None`, the limit is infinite.
         completion_limit: Option<usize> = None,
         /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
@@ -1443,6 +1445,10 @@
         }
     }
 
+    pub fn completion_hide_deprecated(&self) -> bool {
+        *self.completion_hideDeprecated(None)
+    }
+
     pub fn detached_files(&self) -> &Vec<AbsPathBuf> {
         // FIXME @alibektas : This is the only config that is confusing. If it's a proper configuration
         // why is it not among the others? If it's client only which I doubt it is current state should be alright
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index 6dff8ab..b29268f 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -228,8 +228,12 @@
     line_index: &LineIndex,
     version: Option<i32>,
     tdpp: lsp_types::TextDocumentPositionParams,
-    items: Vec<CompletionItem>,
+    mut items: Vec<CompletionItem>,
 ) -> Vec<lsp_types::CompletionItem> {
+    if config.completion_hide_deprecated() {
+        items.retain(|item| !item.deprecated);
+    }
+
     let max_relevance = items.iter().map(|it| it.relevance.score()).max().unwrap_or_default();
     let mut res = Vec::with_capacity(items.len());
     for item in items {
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index abf1a1f..2eb9c1e 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -1162,7 +1162,7 @@
 
     pub(super) static SOURCE_FILE: LazyLock<Parse<SourceFile>> = LazyLock::new(|| {
         SourceFile::parse(
-            "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let _ @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT,
+            "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, async { let _ @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT,
         )
     });
 
diff --git a/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs
index 29788d0..8592df1 100644
--- a/crates/syntax/src/ted.rs
+++ b/crates/syntax/src/ted.rs
@@ -147,6 +147,11 @@
     insert_raw(position, child);
 }
 
+pub fn prepend_child(node: &(impl Into<SyntaxNode> + Clone), child: impl Element) {
+    let position = Position::first_child_of(node);
+    insert(position, child);
+}
+
 fn ws_before(position: &Position, new: &SyntaxElement) -> Option<SyntaxToken> {
     let prev = match &position.repr {
         PositionRepr::FirstChild(_) => return None,
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index e4a8c64..4fcf580 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -283,6 +283,11 @@
 --
 Whether to show full function/method signatures in completion docs.
 --
+[[rust-analyzer.completion.hideDeprecated]]rust-analyzer.completion.hideDeprecated (default: `false`)::
++
+--
+Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden.
+--
 [[rust-analyzer.completion.limit]]rust-analyzer.completion.limit (default: `null`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index 98e8bbf..0b02946 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -1080,6 +1080,16 @@
             {
                 "title": "completion",
                 "properties": {
+                    "rust-analyzer.completion.hideDeprecated": {
+                        "markdownDescription": "Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden.",
+                        "default": false,
+                        "type": "boolean"
+                    }
+                }
+            },
+            {
+                "title": "completion",
+                "properties": {
                     "rust-analyzer.completion.limit": {
                         "markdownDescription": "Maximum number of completions to return. If `None`, the limit is infinite.",
                         "default": null,