Repository: rust-lang/rust-analyzer
Pull request #12063 (2022-04-23 12:08:59)

fix: Fall back to parameter definitions on error types in signature help
Fixes  #10432


Patch:
diff --git a/.cargo/config.toml b/.cargo/config
similarity index 79%
rename from .cargo/config.toml
rename to .cargo/config
index 982c78cd25..1447614b76 100644
--- a/.cargo/config.toml
+++ b/.cargo/config
@@ -1,5 +1,6 @@
 [alias]
 xtask = "run --package xtask --bin xtask --"
+install-ra = "run --package xtask --bin xtask -- install" # for backwards compat
 tq = "test -- -q"
 qt = "tq"
 lint = "clippy --all-targets -- -Aclippy::collapsible_if -Aclippy::needless_pass_by_value -Aclippy::nonminimal_bool -Aclippy::redundant_pattern_matching --cap-lints warn"
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index df6dcb024b..74bb7472d5 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -18,7 +18,7 @@ use crate::{
     generics::GenericParams,
     import_map::ImportMap,
     intern::Interned,
-    item_tree::{AttrOwner, ItemTree},
+    item_tree::ItemTree,
     lang_item::{LangItemTarget, LangItems},
     nameres::DefMap,
     visibility::{self, Visibility},
@@ -184,8 +184,6 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
 
     #[salsa::transparent]
     fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
-
-    fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
 }
 
 fn crate_def_map_wait(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
@@ -206,38 +204,3 @@ fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits {
         recursion_limit: def_map.recursion_limit().unwrap_or(128),
     }
 }
-
-fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
-    let file = db.crate_graph()[crate_id].root_file_id;
-    let item_tree = db.file_item_tree(file.into());
-    let attrs = item_tree.raw_attrs(AttrOwner::TopLevel);
-    for attr in &**attrs {
-        match attr.path().as_ident().and_then(|id| id.as_text()) {
-            Some(ident) if ident == "no_std" => return true,
-            Some(ident) if ident == "cfg_attr" => {}
-            _ => continue,
-        }
-
-        // This is a `cfg_attr`; check if it could possibly expand to `no_std`.
-        // Syntax is: `#[cfg_attr(condition(cfg, style), attr0, attr1, <...>)]`
-        let tt = match attr.token_tree_value() {
-            Some(tt) => &tt.token_trees,
-            None => continue,
-        };
-
-        let segments = tt.split(|tt| match tt {
-            tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => true,
-            _ => false,
-        });
-        for output in segments.skip(1) {
-            match output {
-                [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "no_std" => {
-                    return true
-                }
-                _ => {}
-            }
-        }
-    }
-
-    false
-}
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 89e961f84f..bb89b8cff4 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -43,7 +43,8 @@ impl ModPathExt for ModPath {
         self.segments().first() == Some(&known::std)
     }
 
-    // Can we replace the first segment with `std::` and still get a valid, identical path?
+    // When std library is present, paths starting with `std::`
+    // should be preferred over paths starting with `core::` and `alloc::`
     fn can_start_with_std(&self) -> bool {
         let first_segment = self.segments().first();
         first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
@@ -202,7 +203,7 @@ fn find_path_inner_(
     }
 
     // - otherwise, look for modules containing (reexporting) it and import it from one of those
-    let prefer_no_std = db.crate_supports_no_std(crate_root.krate);
+    let prefer_no_std = db.attrs(crate_root.into()).by_key("no_std").exists();
     let mut best_path = None;
     let mut best_path_len = max_len;
 
@@ -829,32 +830,6 @@ pub mod fmt {
 
 //- /zzz.rs crate:core
 
-pub mod fmt {
-    pub struct Error;
-}
-        "#,
-            "core::fmt::Error",
-            "core::fmt::Error",
-            "core::fmt::Error",
-            "core::fmt::Error",
-        );
-
-        // Should also work (on a best-effort basis) if `no_std` is conditional.
-        check_found_path(
-            r#"
-//- /main.rs crate:main deps:core,std
-#![cfg_attr(not(test), no_std)]
-
-$0
-
-//- /std.rs crate:std deps:core
-
-pub mod fmt {
-    pub use core::fmt::Error;
-}
-
-//- /zzz.rs crate:core
-
 pub mod fmt {
     pub struct Error;
 }
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index a3787728ea..24eff4cc9a 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -681,31 +681,23 @@ impl<'a> TyLoweringContext<'a> {
 
         let ty_error = GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner);
 
-        let mut def_generic_iter = def_generics.iter_id();
-
-        for _ in 0..parent_params {
-            if let Some(eid) = def_generic_iter.next() {
-                match eid {
-                    Either::Left(_) => substs.push(ty_error.clone()),
-                    Either::Right(x) => {
-                        substs.push(unknown_const_as_generic(self.db.const_param_ty(x)))
-                    }
+        for eid in def_generics.iter_id().take(parent_params) {
+            match eid {
+                Either::Left(_) => substs.push(ty_error.clone()),
+                Either::Right(x) => {
+                    substs.push(unknown_const_as_generic(self.db.const_param_ty(x)))
                 }
             }
         }
 
         let fill_self_params = || {
-            for x in explicit_self_ty
-                .into_iter()
-                .map(|x| GenericArgData::Ty(x).intern(Interner))
-                .chain(iter::repeat(ty_error.clone()))
-                .take(self_params)
-            {
-                if let Some(id) = def_generic_iter.next() {
-                    assert!(id.is_left());
-                    substs.push(x);
-                }
-            }
+            substs.extend(
+                explicit_self_ty
+                    .into_iter()
+                    .map(|x| GenericArgData::Ty(x).intern(Interner))
+                    .chain(iter::repeat(ty_error.clone()))
+                    .take(self_params),
+            )
         };
         let mut had_explicit_args = false;
 
@@ -720,38 +712,34 @@ impl<'a> TyLoweringContext<'a> {
             };
             let skip = if generic_args.has_self_type && self_params == 0 { 1 } else { 0 };
             // if args are provided, it should be all of them, but we can't rely on that
-            for arg in generic_args
+            for (arg, id) in generic_args
                 .args
                 .iter()
                 .filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
                 .skip(skip)
                 .take(expected_num)
+                .zip(def_generics.iter_id().skip(parent_params + skip))
             {
-                if let Some(id) = def_generic_iter.next() {
-                    if let Some(x) = generic_arg_to_chalk(
-                        self.db,
-                        id,
-                        arg,
-                        &mut (),
-                        |_, type_ref| self.lower_ty(type_ref),
-                        |_, c, ty| {
-                            const_or_path_to_chalk(
-                                self.db,
-                                &self.resolver,
-                                ty,
-                                c,
-                                self.type_param_mode,
-                                || self.generics(),
-                                self.in_binders,
-                            )
-                        },
-                    ) {
-                        had_explicit_args = true;
-                        substs.push(x);
-                    } else {
-                        // we just filtered them out
-                        never!("Unexpected lifetime argument");
-                    }
+                if let Some(x) = generic_arg_to_chalk(
+                    self.db,
+                    id,
+                    arg,
+                    &mut (),
+                    |_, type_ref| self.lower_ty(type_ref),
+                    |_, c, ty| {
+                        const_or_path_to_chalk(
+                            self.db,
+                            &self.resolver,
+                            ty,
+                            c,
+                            self.type_param_mode,
+                            || self.generics(),
+                            self.in_binders,
+                        )
+                    },
+                ) {
+                    had_explicit_args = true;
+                    substs.push(x);
                 }
             }
         } else {
@@ -769,16 +757,14 @@ impl<'a> TyLoweringContext<'a> {
                 for default_ty in defaults.iter().skip(substs.len()) {
                     // each default can depend on the previous parameters
                     let substs_so_far = Substitution::from_iter(Interner, substs.clone());
-                    if let Some(_id) = def_generic_iter.next() {
-                        substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
-                    }
+                    substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
                 }
             }
         }
 
         // add placeholders for args that were not provided
         // FIXME: emit diagnostics in contexts where this is not allowed
-        for eid in def_generic_iter {
+        for eid in def_generics.iter_id().skip(substs.len()) {
             match eid {
                 Either::Left(_) => substs.push(ty_error.clone()),
                 Either::Right(x) => {
@@ -786,7 +772,6 @@ impl<'a> TyLoweringContext<'a> {
                 }
             }
         }
-        // If this assert fails, it means you pushed into subst but didn't call .next() of def_generic_iter
         assert_eq!(substs.len(), total_len);
 
         Substitution::from_iter(Interner, substs)
@@ -1674,10 +1659,6 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut
     }
 }
 
-/// Checks if the provided generic arg matches its expected kind, then lower them via
-/// provided closures. Use unknown if there was kind mismatch.
-///
-/// Returns `Some` of the lowered generic arg. `None` if the provided arg is a lifetime.
 pub(crate) fn generic_arg_to_chalk<'a, T>(
     db: &dyn HirDatabase,
     kind_id: Either<TypeParamId, ConstParamId>,
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 6dc8b3046c..4eca69033a 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -110,10 +110,12 @@ fn signature_help_for_call(
         SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter };
 
     let db = sema.db;
+    let mut fn_params = None;
     match callable.kind() {
         hir::CallableKind::Function(func) => {
             res.doc = func.docs(db).map(|it| it.into());
             format_to!(res.signature, "fn {}", func.name(db));
+            fn_params = Some(func.assoc_fn_params(db));
         }
         hir::CallableKind::TupleStruct(strukt) => {
             res.doc = strukt.docs(db).map(|it| it.into());
@@ -137,7 +139,7 @@ fn signature_help_for_call(
             format_to!(res.signature, "{}", self_param)
         }
         let mut buf = String::new();
-        for (pat, ty) in callable.params(db) {
+        for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() {
             buf.clear();
             if let Some(pat) = pat {
                 match pat {
@@ -145,18 +147,31 @@ fn signature_help_for_call(
                     Either::Right(pat) => format_to!(buf, "{}: ", pat),
                 }
             }
-            format_to!(buf, "{}", ty.display(db));
+            // APITs (argument position `impl Trait`s) are inferred as {unknown} as the user is
+            // in the middle of entering call arguments.
+            // In that case, fall back to render definitions of the respective parameters.
+            // This is overly conservative: we do not substitute known type vars
+            // (see FIXME in tests::impl_trait) and falling back on any unknowns.
+            match (ty.contains_unknown(), fn_params.as_deref()) {
+                (true, Some(fn_params)) => format_to!(buf, "{}", fn_params[idx].ty().display(db)),
+                _ => format_to!(buf, "{}", ty.display(db)),
+            }
             res.push_call_param(&buf);
         }
     }
     res.signature.push(')');
 
+    let mut render = |ret_type: hir::Type| {
+        if !ret_type.is_unit() {
+            format_to!(res.signature, " -> {}", ret_type.display(db));
+        }
+    };
     match callable.kind() {
+        hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => {
+            render(func.ret_type(db))
+        }
         hir::CallableKind::Function(_) | hir::CallableKind::Closure => {
-            let ret_type = callable.return_type();
-            if !ret_type.is_unit() {
-                format_to!(res.signature, " -> {}", ret_type.display(db));
-            }
+            render(callable.return_type())
         }
         hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
     }
@@ -420,8 +435,8 @@ fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
 fn bar() { foo($03, ); }
 "#,
             expect![[r#"
-                fn foo(x: i32, y: {unknown}) -> u32
-                       ^^^^^^  ------------
+                fn foo(x: i32, y: U) -> u32
+                       ^^^^^^  ----
             "#]],
         );
     }
@@ -434,7 +449,7 @@ fn foo<T>() -> T where T: Copy + Display {}
 fn bar() { foo($0); }
 "#,
             expect![[r#"
-                fn foo() -> {unknown}
+                fn foo() -> T
             "#]],
         );
     }
@@ -633,26 +648,21 @@ pub fn do_it() {
     fn test_fn_signature_with_docs_from_actix() {
         check(
             r#"
-struct WriteHandler<E>;
-
-impl<E> WriteHandler<E> {
-    /// Method is called when writer emits error.
-    ///
-    /// If this method returns `ErrorAction::Continue` writer processing
-    /// continues otherwise stream processing stops.
-    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
-        Running::Stop
-    }
-
+trait Actor {
+    /// Actor execution context type
+    type Context;
+}
+trait WriteHandler<E>
+where
+    Self: Actor
+{
     /// Method is called when writer finishes.
     ///
     /// By default this method stops actor's `Context`.
-    fn finished(&mut self, ctx: &mut Self::Context) {
-        ctx.stop()
-    }
+    fn finished(&mut self, ctx: &mut Self::Context) {}
 }
 
-pub fn foo(mut r: WriteHandler<()>) {
+fn foo(mut r: impl WriteHandler<()>) {
     r.finished($0);
 }
 "#,
@@ -661,8 +671,8 @@ pub fn foo(mut r: WriteHandler<()>) {
 
                 By default this method stops actor's `Context`.
                 ------
-                fn finished(&mut self, ctx: &mut {unknown})
-                                       ^^^^^^^^^^^^^^^^^^^
+                fn finished(&mut self, ctx: &mut <impl WriteHandler<()> as Actor>::Context)
+                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
             "#]],
         );
     }
@@ -1055,4 +1065,23 @@ fn f() {
             "#]],
         );
     }
+
+    #[test]
+    fn impl_trait() {
+        // FIXME: Substitute type vars in impl trait (`U` -> `i8`)
+        check(
+            r#"
+trait Trait<T> {}
+struct Wrap<T>(T);
+fn foo<U>(x: Wrap<impl Trait<U>>) {}
+fn f() {
+    foo::<i8>($0)
+}
+"#,
+            expect![[r#"
+                fn foo(x: Wrap<impl Trait<U>>)
+                       ^^^^^^^^^^^^^^^^^^^^^^
+            "#]],
+        );
+    }
 }
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
index ae09339e95..7e9c1f49de 100644
--- a/crates/ide_completion/src/completions/fn_param.rs
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -5,7 +5,7 @@ use rustc_hash::FxHashMap;
 use syntax::{
     algo,
     ast::{self, HasModuleItem},
-    match_ast, AstNode, Direction, SyntaxKind, TextRange,
+    match_ast, AstNode, Direction, SyntaxKind,
 };
 
 use crate::{
@@ -27,13 +27,14 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
 
     let comma_wrapper = comma_wrapper(ctx);
     let mut add_new_item_to_acc = |label: &str, lookup: String| {
-        let mk_item = |label: &str, range: TextRange| {
-            CompletionItem::new(CompletionItemKind::Binding, range, label)
+        let mk_item = |label: &str| {
+            CompletionItem::new(CompletionItemKind::Binding, ctx.source_range(), label)
         };
-        let item = match &comma_wrapper {
-            Some((fmt, range, lookup)) => mk_item(&fmt(label), *range).lookup_by(lookup).to_owned(),
-            None => mk_item(label, ctx.source_range()).lookup_by(lookup).to_owned(),
+        let mut item = match &comma_wrapper {
+            Some(fmt) => mk_item(&fmt(label)),
+            None => mk_item(label),
         };
+        item.lookup_by(lookup);
         item.add_to(acc)
     };
 
@@ -161,16 +162,14 @@ fn should_add_self_completions(ctx: &CompletionContext, param_list: &ast::ParamL
     inside_impl && no_params
 }
 
-fn comma_wrapper(ctx: &CompletionContext) -> Option<(impl Fn(&str) -> String, TextRange, String)> {
-    let param = ctx.token.ancestors().find(|node| node.kind() == SyntaxKind::PARAM)?;
-
+fn comma_wrapper(ctx: &CompletionContext) -> Option<impl Fn(&str) -> String> {
     let next_token_kind = {
-        let t = param.last_token()?.next_token()?;
+        let t = ctx.token.next_token()?;
         let t = algo::skip_whitespace_token(t, Direction::Next)?;
         t.kind()
     };
     let prev_token_kind = {
-        let t = param.first_token()?.prev_token()?;
+        let t = ctx.previous_token.clone()?;
         let t = algo::skip_whitespace_token(t, Direction::Prev)?;
         t.kind()
     };
@@ -183,9 +182,5 @@ fn comma_wrapper(ctx: &CompletionContext) -> Option<(impl Fn(&str) -> String, Te
         matches!(prev_token_kind, SyntaxKind::COMMA | SyntaxKind::L_PAREN | SyntaxKind::PIPE);
     let leading = if has_leading_comma { "" } else { ", " };
 
-    Some((
-        move |label: &_| (format!("{}{}{}", leading, label, trailing)),
-        param.text_range(),
-        format!("{}{}", leading, param.text()),
-    ))
+    Some(move |param: &_| format!("{}{}{}", leading, param, trailing))
 }
diff --git a/crates/ide_completion/src/completions/lifetime.rs b/crates/ide_completion/src/completions/lifetime.rs
index 66f8723912..4fffd1aa0c 100644
--- a/crates/ide_completion/src/completions/lifetime.rs
+++ b/crates/ide_completion/src/completions/lifetime.rs
@@ -22,8 +22,8 @@ pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext)
         Some(LifetimeContext::LifetimeParam { is_decl: false, param }) => Some(param),
         _ => return,
     };
-    let param_lifetime = match (ctx.lifetime(), lp.and_then(|lp| lp.lifetime())) {
-        (Some(lt), Some(lp)) if lp == lt.clone() => return,
+    let param_lifetime = match (&ctx.name_syntax, lp.and_then(|lp| lp.lifetime())) {
+        (Some(ast::NameLike::Lifetime(lt)), Some(lp)) if lp == lt.clone() => return,
         (Some(_), Some(lp)) => Some(lp),
         _ => None,
     };
diff --git a/crates/ide_completion/src/completions/use_.rs b/crates/ide_completion/src/completions/use_.rs
index 07f14c8735..94df46efb0 100644
--- a/crates/ide_completion/src/completions/use_.rs
+++ b/crates/ide_completion/src/completions/use_.rs
@@ -11,9 +11,12 @@ use crate::{
 };
 
 pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) {
-    let (&is_absolute_path, qualifier) = match &ctx.path_context {
+    let (is_absolute_path, qualifier) = match ctx.path_context {
         Some(PathCompletionCtx {
-            kind: Some(PathKind::Use), is_absolute_path, qualifier, ..
+            kind: Some(PathKind::Use),
+            is_absolute_path,
+            ref qualifier,
+            ..
         }) => (is_absolute_path, qualifier),
         _ => return,
     };
@@ -42,9 +45,13 @@ pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext)
             if let Some(list) = ctx.token.ancestors().find_map(ast::UseTreeList::cast) {
                 let use_tree = list.parent_use_tree();
                 if use_tree.path().as_ref() == Some(path) {
-                    for tree in list.use_trees().filter(|tree| tree.is_simple_path()) {
-                        if let Some(name) = tree.path().and_then(|path| path.as_single_name_ref()) {
-                            already_imported_names.insert(name.to_string());
+                    for tree in list.use_trees() {
+                        if tree.is_simple_path() {
+                            if let Some(name) =
+                                tree.path().and_then(|path| path.as_single_name_ref())
+                            {
+                                already_imported_names.insert(name.to_string());
+                            }
                         }
                     }
                 }
@@ -55,14 +62,14 @@ pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext)
                     let module_scope = module.scope(ctx.db, Some(ctx.module));
                     let unknown_is_current = |name: &hir::Name| {
                         matches!(
-                            ctx.name_ref(),
-                            Some(name_ref) if name_ref.syntax().text() == name.to_smol_str().as_str()
+                            ctx.name_syntax.as_ref(),
+                            Some(ast::NameLike::NameRef(name_ref))
+                                if name_ref.syntax().text() == name.to_smol_str().as_str()
                         )
                     };
                     for (name, def) in module_scope {
-                        let is_name_already_imported = name
-                            .as_text()
-                            .map_or(false, |text| already_imported_names.contains(text.as_str()));
+                        let is_name_already_imported =
+                            already_imported_names.contains(name.as_text().unwrap().as_str());
 
                         let add_resolution = match def {
                             ScopeDef::Unknown if unknown_is_current(&name) => {
diff --git a/crates/ide_completion/src/completions/vis.rs b/crates/ide_completion/src/completions/vis.rs
index 7315a488b8..338e003437 100644
--- a/crates/ide_completion/src/completions/vis.rs
+++ b/crates/ide_completion/src/completions/vis.rs
@@ -8,11 +8,11 @@ use crate::{
 };
 
 pub(crate) fn complete_vis(acc: &mut Completions, ctx: &CompletionContext) {
-    let (&is_absolute_path, qualifier, &has_in_token) = match &ctx.path_context {
+    let (is_absolute_path, qualifier, has_in_token) = match ctx.path_context {
         Some(PathCompletionCtx {
             kind: Some(PathKind::Vis { has_in_token }),
             is_absolute_path,
-            qualifier,
+            ref qualifier,
             ..
         }) => (is_absolute_path, qualifier, has_in_token),
         _ => return,
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index a98e10a226..3ba02d78b5 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -50,7 +50,6 @@ pub(super) enum PathKind {
     Type,
     Attr { kind: AttrKind, annotated_item_kind: Option<SyntaxKind> },
     Derive,
-    // This should be removed in favor of `has_macro_bang` in PathCompletionContext
     Mac,
     Pat,
     Vis { has_in_token: bool },
@@ -197,14 +196,6 @@ impl<'a> CompletionContext<'a> {
         }
     }
 
-    pub(crate) fn name_ref(&self) -> Option<&ast::NameRef> {
-        self.name_syntax.as_ref().and_then(ast::NameLike::as_name_ref)
-    }
-
-    pub(crate) fn lifetime(&self) -> Option<&ast::Lifetime> {
-        self.name_syntax.as_ref().and_then(ast::NameLike::as_lifetime)
-    }
-
     pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool {
         self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
     }
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index bd6833567c..9e5f91db39 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -562,90 +562,6 @@ fn qux(Foo { bar }: Foo) {
 fn main() {
   qux(${1:foo})$0
 }
-"#,
-        );
-    }
-
-    #[test]
-    fn complete_fn_param() {
-        // has mut kw
-        check_edit(
-            "mut ba",
-            r#"
-fn f(foo: (), mut bar: u32) {}
-fn g(foo: (), mut ba$0)
-"#,
-            r#"
-fn f(foo: (), mut bar: u32) {}
-fn g(foo: (), mut bar: u32)
-"#,
-        );
-
-        // has type param
-        check_edit(
-            "mut ba: u32",
-            r#"
-fn g(foo: (), mut ba$0: u32)
-fn f(foo: (), mut bar: u32) {}
-"#,
-            r#"
-fn g(foo: (), mut bar: u32)
-fn f(foo: (), mut bar: u32) {}
-"#,
-        );
-    }
-
-    #[test]
-    fn complete_fn_mut_param_add_comma() {
-        // add leading and trailing comma
-        check_edit(
-            ", mut ba",
-            r#"
-fn f(foo: (), mut bar: u32) {}
-fn g(foo: ()mut ba$0 baz: ())
-"#,
-            r#"
-fn f(foo: (), mut bar: u32) {}
-fn g(foo: (), mut bar: u32, baz: ())
-"#,
-        );
-    }
-
-    #[test]
-    fn complete_fn_mut_param_has_attribute() {
-        check_edit(
-            "mut ba",
-            r#"
-fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
-fn g(foo: (), mut ba$0)
-"#,
-            r#"
-fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
-fn g(foo: (), #[baz = "qux"] mut bar: u32)
-"#,
-        );
-
-        check_edit(
-            r#"#[baz = "qux"] mut ba"#,
-            r#"
-fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
-fn g(foo: (), #[baz = "qux"] mut ba$0)
-"#,
-            r#"
-fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
-fn g(foo: (), #[baz = "qux"] mut bar: u32)
-"#,
-        );
-
-        check_edit(
-            r#", #[baz = "qux"] mut ba"#,
-            r#"
-fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
-fn g(foo: ()#[baz = "qux"] mut ba$0)
-"#,
-            r#"
-fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
-fn g(foo: (), #[baz = "qux"] mut bar: u32)
 "#,
         );
     }
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 93255705be..1330a86950 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -759,9 +759,7 @@ fn handle_rustc_crates(
 
             let overrides = match override_cfg {
                 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
-                CfgOverrides::Selective(cfg_overrides) => {
-                    cfg_overrides.get(&rustc_workspace[pkg].name)
-                }
+                CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
             };
 
             if let Some(overrides) = overrides {
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index df61ba8eec..15ae0e6480 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -77,13 +77,9 @@ fn try_main() -> Result<()> {
                 println!("{}", flags::RustAnalyzer::HELP);
                 return Ok(());
             }
-            with_extra_thread("rust-analyzer server thread", run_server)?;
-        }
-        flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => {
-            with_extra_thread("rust-analyzer proc-macro expander", || {
-                proc_macro_srv::cli::run().map_err(Into::into)
-            })?;
+            run_server()?
         }
+        flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => proc_macro_srv::cli::run()?,
         flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::Highlight(cmd) => cmd.run()?,
@@ -132,23 +128,6 @@ fn setup_logging(log_file: Option<&Path>) -> Result<()> {
     Ok(())
 }
 
-const STACK_SIZE: usize = 1024 * 1024 * 8;
-
-/// Parts of rust-analyzer can use a lot of stack space, and some operating systems only give us
-/// 1 MB by default (eg. Windows), so this spawns a new thread with hopefully sufficient stack
-/// space.
-fn with_extra_thread(
-    thread_name: impl Into<String>,
-    f: impl FnOnce() -> Result<()> + Send + 'static,
-) -> Result<()> {
-    let handle =
-        std::thread::Builder::new().name(thread_name.into()).stack_size(STACK_SIZE).spawn(f)?;
-    match handle.join() {
-        Ok(res) => res,
-        Err(panic) => std::panic::resume_unwind(panic),
-    }
-}
-
 fn run_server() -> Result<()> {
     tracing::info!("server version {} will start", env!("REV"));
 
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index f2153ca921..e1d4addb52 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -426,12 +426,6 @@ impl NameLike {
             _ => None,
         }
     }
-    pub fn as_lifetime(&self) -> Option<&ast::Lifetime> {
-        match self {
-            NameLike::Lifetime(lifetime) => Some(lifetime),
-            _ => None,
-        }
-    }
 }
 
 impl ast::AstNode for NameLike {
diff --git a/docs/dev/README.md b/docs/dev/README.md
index b5a71fdae5..4a047e57a0 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -13,7 +13,7 @@ To learn more about how rust-analyzer works, see [./architecture.md](./architect
 It also explains the high-level layout of the source code.
 Do skim through that document.
 
-We also publish rustdoc docs to pages: https://rust-lang.github.io/rust-analyzer/ide/.
+We also publish rustdoc docs to pages: https://rust-analyzer.github.io/rust-analyzer/ide/.
 Note though, that the internal documentation is very incomplete.
 
 Various organizational and process issues are discussed in this document.
diff --git a/editors/code/language-configuration.json b/editors/code/language-configuration.json
index cf7b20eb79..2d772952a8 100644
--- a/editors/code/language-configuration.json
+++ b/editors/code/language-configuration.json
@@ -6,7 +6,8 @@
     "brackets": [
         ["{", "}"],
         ["[", "]"],
-        ["(", ")"]
+        ["(", ")"],
+        ["<", ">"]
     ],
     "colorizedBracketPairs": [
 		["{", "}"],
diff --git a/editors/code/package.json b/editors/code/package.json
index cd0ad92d9a..60c827e31a 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -293,6 +293,11 @@
                     "default": null,
                     "markdownDescription": "Environment variables passed to the runnable launched using `Test` or `Debug` lens or `rust-analyzer.run` command."
                 },
+                "rust-analyzer.inlayHints.enable": {
+                    "type": "boolean",
+                    "default": true,
+                    "description": "Whether to show inlay hints."
+                },
                 "rust-analyzer.server.path": {
                     "type": [
                         "null",
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 1e89938c05..a808d5ec6d 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -300,9 +300,10 @@ export function serverVersion(ctx: Ctx): Cmd {
 
 export function toggleInlayHints(_ctx: Ctx): Cmd {
     return async () => {
-        const config = vscode.workspace.getConfiguration("editor.inlayHints", { languageId: "rust" });
+        const scope = vscode.ConfigurationTarget.Global;
+        const config = vscode.workspace.getConfiguration("editor.inlayHints");
         const value = !config.get("enabled");
-        await config.update('enabled', value, vscode.ConfigurationTarget.Global);
+        await config.update('enabled', value, scope);
     };
 }
 
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 7591865390..0451e4c3d6 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -1,5 +1,4 @@
 import * as vscode from 'vscode';
-import * as lc from 'vscode-languageclient/node';
 import * as os from "os";
 
 import * as commands from './commands';
@@ -15,20 +14,16 @@ let ctx: Ctx | undefined;
 
 const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
 
-export interface RustAnalyzerExtensionApi {
-    client: lc.LanguageClient;
-}
-
-export async function activate(context: vscode.ExtensionContext): Promise<RustAnalyzerExtensionApi> {
+export async function activate(context: vscode.ExtensionContext) {
     // VS Code doesn't show a notification when an extension fails to activate
     // so we do it ourselves.
-    return await tryActivate(context).catch(err => {
+    await tryActivate(context).catch(err => {
         void vscode.window.showErrorMessage(`Cannot activate rust-analyzer: ${err.message}`);
         throw err;
     });
 }
 
-async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyzerExtensionApi> {
+async function tryActivate(context: vscode.ExtensionContext) {
     const config = new Config(context);
     const state = new PersistentState(context.globalState);
     const serverPath = await bootstrap(context, config, state).catch(err => {
@@ -67,10 +62,6 @@ async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyz
         null,
         ctx.subscriptions,
     );
-
-    return {
-        client: ctx.client
-    };
 }
 
 async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
diff --git a/triagebot.toml b/triagebot.toml
deleted file mode 100644
index fa0824ac53..0000000000
--- a/triagebot.toml
+++ /dev/null
@@ -1,1 +0,0 @@
-[assign]


Test patch:
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index ef27667ffa..c3c934c468 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -1470,22 +1470,6 @@ fn regression_11688_3() {
     );
 }
 
-#[test]
-fn regression_11688_4() {
-    check_types(
-        r#"
-        trait Bar<const C: usize> {
-            fn baz(&self) -> [i32; C];
-        }
-
-        fn foo(x: &dyn Bar<2>) {
-            x.baz();
-          //^^^^^^^ [i32; 2]
-        }
-        "#,
-    )
-}
-
 #[test]
 fn gat_crash_1() {
     cov_mark::check!(ignore_gats);
diff --git a/crates/ide_completion/src/tests/fn_param.rs b/crates/ide_completion/src/tests/fn_param.rs
index 678c99aa7a..779ec0c3a7 100644
--- a/crates/ide_completion/src/tests/fn_param.rs
+++ b/crates/ide_completion/src/tests/fn_param.rs
@@ -232,16 +232,3 @@ fn bar(bar$0) {}
         "#]],
     )
 }
-
-#[test]
-fn completes_for_params_with_attributes() {
-    check(
-        r#"
-fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
-fn g(foo: (), #[baz = "qux"] mut ba$0)
-"#,
-        expect![[r##"
-            bn #[baz = "qux"] mut bar: u32
-        "##]],
-    )
-}