Repository: rust-lang/rust-analyzer
Pull request #12064 (2022-04-23 14:37:24)

fix: Fix `ide_db::search` not searching bodies of attributed items
Fixes https://github.com/rust-lang/rust-analyzer/issues/12050


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_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index e698726638..bc5372754d 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -435,6 +435,29 @@ impl MacroCallKind {
         }
     }
 
+    /// Returns the original file range that best describes the location of this macro call.
+    ///
+    /// Unlike `MacroCallKind::original_call_range`, this also spans the item of attributes and derives.
+    pub fn original_call_range_with_body(self, db: &dyn db::AstDatabase) -> FileRange {
+        let mut kind = self;
+        let file_id = loop {
+            match kind.file_id().0 {
+                HirFileIdRepr::MacroFile(file) => {
+                    kind = db.lookup_intern_macro_call(file.macro_call_id).kind;
+                }
+                HirFileIdRepr::FileId(file_id) => break file_id,
+            }
+        };
+
+        let range = match kind {
+            MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
+            MacroCallKind::Derive { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
+            MacroCallKind::Attr { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
+        };
+
+        FileRange { range, file_id }
+    }
+
     /// Returns the original file range that best describes the location of this macro call.
     ///
     /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros
@@ -751,6 +774,9 @@ impl<'a> InFile<&'a SyntaxNode> {
     }
 
     /// Falls back to the macro call range if the node cannot be mapped up fully.
+    ///
+    /// For attributes and derives, this will point back to the attribute only.
+    /// For the entire item `InFile::use original_file_range_full`.
     pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
         if let Some(res) = self.original_file_range_opt(db) {
             return res;
@@ -766,6 +792,22 @@ impl<'a> InFile<&'a SyntaxNode> {
         }
     }
 
+    /// Falls back to the macro call range if the node cannot be mapped up fully.
+    pub fn original_file_range_full(self, db: &dyn db::AstDatabase) -> FileRange {
+        if let Some(res) = self.original_file_range_opt(db) {
+            return res;
+        }
+
+        // Fall back to whole macro call.
+        match self.file_id.0 {
+            HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
+            HirFileIdRepr::MacroFile(mac_file) => {
+                let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
+                loc.kind.original_call_range_with_body(db)
+            }
+        }
+    }
+
     /// Attempts to map the syntax node back up its macro calls.
     pub fn original_file_range_opt(self, db: &dyn db::AstDatabase) -> Option<FileRange> {
         match ascend_node_border_tokens(db, self) {
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 4eca69033a..6dc8b3046c 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -110,12 +110,10 @@ 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());
@@ -139,7 +137,7 @@ fn signature_help_for_call(
             format_to!(res.signature, "{}", self_param)
         }
         let mut buf = String::new();
-        for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() {
+        for (pat, ty) in callable.params(db) {
             buf.clear();
             if let Some(pat) = pat {
                 match pat {
@@ -147,31 +145,18 @@ fn signature_help_for_call(
                     Either::Right(pat) => format_to!(buf, "{}: ", pat),
                 }
             }
-            // 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)),
-            }
+            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 => {
-            render(callable.return_type())
+            let ret_type = callable.return_type();
+            if !ret_type.is_unit() {
+                format_to!(res.signature, " -> {}", ret_type.display(db));
+            }
         }
         hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
     }
@@ -435,8 +420,8 @@ fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
 fn bar() { foo($03, ); }
 "#,
             expect![[r#"
-                fn foo(x: i32, y: U) -> u32
-                       ^^^^^^  ----
+                fn foo(x: i32, y: {unknown}) -> u32
+                       ^^^^^^  ------------
             "#]],
         );
     }
@@ -449,7 +434,7 @@ fn foo<T>() -> T where T: Copy + Display {}
 fn bar() { foo($0); }
 "#,
             expect![[r#"
-                fn foo() -> T
+                fn foo() -> {unknown}
             "#]],
         );
     }
@@ -648,21 +633,26 @@ pub fn do_it() {
     fn test_fn_signature_with_docs_from_actix() {
         check(
             r#"
-trait Actor {
-    /// Actor execution context type
-    type Context;
-}
-trait WriteHandler<E>
-where
-    Self: Actor
-{
+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
+    }
+
     /// Method is called when writer finishes.
     ///
     /// By default this method stops actor's `Context`.
-    fn finished(&mut self, ctx: &mut Self::Context) {}
+    fn finished(&mut self, ctx: &mut Self::Context) {
+        ctx.stop()
+    }
 }
 
-fn foo(mut r: impl WriteHandler<()>) {
+pub fn foo(mut r: WriteHandler<()>) {
     r.finished($0);
 }
 "#,
@@ -671,8 +661,8 @@ fn foo(mut r: impl WriteHandler<()>) {
 
                 By default this method stops actor's `Context`.
                 ------
-                fn finished(&mut self, ctx: &mut <impl WriteHandler<()> as Actor>::Context)
-                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+                fn finished(&mut self, ctx: &mut {unknown})
+                                       ^^^^^^^^^^^^^^^^^^^
             "#]],
         );
     }
@@ -1065,23 +1055,4 @@ 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/src/ssr.rs b/crates/ide/src/ssr.rs
index ba9a7f62e6..6142a0eafd 100644
--- a/crates/ide/src/ssr.rs
+++ b/crates/ide/src/ssr.rs
@@ -45,7 +45,6 @@ pub(crate) fn ssr_assists(
             group: Some(GroupLabel("Apply SSR".into())),
             target: comment_range,
             source_change,
-            trigger_signature_help: false,
         };
 
         ssr_assists.push(assist);
@@ -141,7 +140,6 @@ mod tests {
                         is_snippet: false,
                     },
                 ),
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&apply_in_file_assist);
@@ -188,7 +186,6 @@ mod tests {
                         is_snippet: false,
                     },
                 ),
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&apply_in_workspace_assist);
@@ -228,7 +225,6 @@ mod tests {
                 ),
                 target: 10..21,
                 source_change: None,
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&apply_in_file_assist);
@@ -248,7 +244,6 @@ mod tests {
                 ),
                 target: 10..21,
                 source_change: None,
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&apply_in_workspace_assist);
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs
index ec85faaf46..fd46aa2479 100644
--- a/crates/ide_assists/src/assist_context.rs
+++ b/crates/ide_assists/src/assist_context.rs
@@ -193,11 +193,9 @@ impl Assists {
             return None;
         }
 
-        let mut trigger_signature_help = false;
         let source_change = if self.resolve.should_resolve(&id) {
             let mut builder = AssistBuilder::new(self.file);
             f(&mut builder);
-            trigger_signature_help = builder.trigger_signature_help;
             Some(builder.finish())
         } else {
             None
@@ -205,7 +203,7 @@ impl Assists {
 
         let label = Label::new(label);
         let group = group.cloned();
-        self.buf.push(Assist { id, label, group, target, source_change, trigger_signature_help });
+        self.buf.push(Assist { id, label, group, target, source_change });
         Some(())
     }
 
@@ -221,7 +219,6 @@ pub(crate) struct AssistBuilder {
     edit: TextEditBuilder,
     file_id: FileId,
     source_change: SourceChange,
-    trigger_signature_help: bool,
 
     /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin.
     mutated_tree: Option<TreeMutator>,
@@ -255,7 +252,6 @@ impl AssistBuilder {
             edit: TextEdit::builder(),
             file_id,
             source_change: SourceChange::default(),
-            trigger_signature_help: false,
             mutated_tree: None,
         }
     }
@@ -336,9 +332,6 @@ impl AssistBuilder {
         let file_system_edit = FileSystemEdit::MoveFile { src, dst };
         self.source_change.push_file_system_edit(file_system_edit);
     }
-    pub(crate) fn trigger_signature_help(&mut self) {
-        self.trigger_signature_help = true;
-    }
 
     fn finish(mut self) -> SourceChange {
         self.commit();
diff --git a/crates/ide_assists/src/handlers/add_turbo_fish.rs b/crates/ide_assists/src/handlers/add_turbo_fish.rs
index a82dca9db0..c3d27f7ea6 100644
--- a/crates/ide_assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ide_assists/src/handlers/add_turbo_fish.rs
@@ -89,18 +89,15 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
         AssistId("add_turbo_fish", AssistKind::RefactorRewrite),
         "Add `::<>`",
         ident.text_range(),
-        |builder| {
-            builder.trigger_signature_help();
-            match ctx.config.snippet_cap {
-                Some(cap) => {
-                    let snip = format!("::<{}>", get_snippet_fish_head(number_of_arguments));
-                    builder.insert_snippet(cap, ident.text_range().end(), snip)
-                }
-                None => {
-                    let fish_head = std::iter::repeat("_").take(number_of_arguments).format(", ");
-                    let snip = format!("::<{}>", fish_head);
-                    builder.insert(ident.text_range().end(), snip);
-                }
+        |builder| match ctx.config.snippet_cap {
+            Some(cap) => {
+                let snip = format!("::<{}>", get_snippet_fish_head(number_of_arguments));
+                builder.insert_snippet(cap, ident.text_range().end(), snip)
+            }
+            None => {
+                let fish_head = std::iter::repeat("_").take(number_of_arguments).format(", ");
+                let snip = format!("::<{}>", fish_head);
+                builder.insert(ident.text_range().end(), snip);
             }
         },
     )
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index ae4436ce88..79f5d0499e 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -171,7 +171,7 @@ fn get_adt_source(
     adt: &hir::Adt,
     fn_name: &str,
 ) -> Option<(Option<ast::Impl>, FileId)> {
-    let range = adt.source(ctx.sema.db)?.syntax().original_file_range(ctx.sema.db);
+    let range = adt.source(ctx.sema.db)?.syntax().original_file_range_full(ctx.sema.db);
     let file = ctx.sema.parse(range.file_id);
     let adt_source =
         ctx.sema.find_node_at_offset_with_macros(file.syntax(), range.range.start())?;
diff --git a/crates/ide_assists/src/handlers/inline_call.rs b/crates/ide_assists/src/handlers/inline_call.rs
index c857adf876..ab614b0ace 100644
--- a/crates/ide_assists/src/handlers/inline_call.rs
+++ b/crates/ide_assists/src/handlers/inline_call.rs
@@ -198,7 +198,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
     let fn_body = fn_source.value.body()?;
     let param_list = fn_source.value.param_list()?;
 
-    let FileRange { file_id, range } = fn_source.syntax().original_file_range(ctx.sema.db);
+    let FileRange { file_id, range } = fn_source.syntax().original_file_range_full(ctx.sema.db);
     if file_id == ctx.file_id() && range.contains(ctx.offset()) {
         cov_mark::hit!(inline_call_recursive);
         return None;
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/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/ide_db/src/assists.rs b/crates/ide_db/src/assists.rs
index da23763dc2..be5f063f0f 100644
--- a/crates/ide_db/src/assists.rs
+++ b/crates/ide_db/src/assists.rs
@@ -29,7 +29,6 @@ pub struct Assist {
     /// cumbersome, especially if you want to embed an assist into another data
     /// structure, such as a diagnostic.
     pub source_change: Option<SourceChange>,
-    pub trigger_signature_help: bool,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 4a11fb73cd..e6bd46347d 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -239,14 +239,14 @@ impl Definition {
                 DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
             };
             return match def {
-                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
 
         if let Definition::SelfType(impl_) = self {
             return match impl_.source(db).map(|src| src.syntax().cloned()) {
-                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
@@ -262,7 +262,7 @@ impl Definition {
                 hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
             };
             return match def {
-                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
diff --git a/crates/ide_diagnostics/src/handlers/unresolved_module.rs b/crates/ide_diagnostics/src/handlers/unresolved_module.rs
index b8f2a9e94a..587a3ee26d 100644
--- a/crates/ide_diagnostics/src/handlers/unresolved_module.rs
+++ b/crates/ide_diagnostics/src/handlers/unresolved_module.rs
@@ -117,7 +117,6 @@ mod baz {}
                                             is_snippet: false,
                                         },
                                     ),
-                                    trigger_signature_help: false,
                                 },
                                 Assist {
                                     id: AssistId(
@@ -144,7 +143,6 @@ mod baz {}
                                             is_snippet: false,
                                         },
                                     ),
-                                    trigger_signature_help: false,
                                 },
                             ],
                         ),
diff --git a/crates/ide_diagnostics/src/lib.rs b/crates/ide_diagnostics/src/lib.rs
index b04da2e092..13ac0a2450 100644
--- a/crates/ide_diagnostics/src/lib.rs
+++ b/crates/ide_diagnostics/src/lib.rs
@@ -238,6 +238,5 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
         group: None,
         target,
         source_change: None,
-        trigger_signature_help: false,
     }
 }
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/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index e9a192cdfb..45e46c1a06 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -204,7 +204,6 @@ fn map_rust_child_diagnostic(
                     }),
                     is_preferred: Some(true),
                     data: None,
-                    command: None,
                 },
             }),
         })
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index a1b71ab143..fb64eeea4f 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -1160,9 +1160,8 @@ pub(crate) fn handle_code_action_resolve(
         ))
         .into());
     }
-    let ca = to_proto::code_action(&snap, assist.clone(), None)?;
-    code_action.edit = ca.edit;
-    code_action.command = ca.command;
+    let edit = to_proto::code_action(&snap, assist.clone(), None)?.edit;
+    code_action.edit = edit;
     Ok(code_action)
 }
 
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 1cfaa13327..489a233a0a 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -311,8 +311,9 @@ pub struct CodeAction {
     pub group: Option<String>,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub kind: Option<CodeActionKind>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub command: Option<lsp_types::Command>,
+    // We don't handle commands on the client-side
+    // #[serde(skip_serializing_if = "Option::is_none")]
+    // pub command: Option<lsp_types::Command>,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub edit: Option<SnippetWorkspaceEdit>,
     #[serde(skip_serializing_if = "Option::is_none")]
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 57b0e93765..deea54cf7d 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -1018,13 +1018,7 @@ pub(crate) fn code_action(
         edit: None,
         is_preferred: None,
         data: None,
-        command: None,
     };
-
-    if assist.trigger_signature_help && snap.config.client_commands().trigger_parameter_hints {
-        res.command = Some(command::trigger_parameter_hints());
-    }
-
     match (assist.source_change, resolve_data) {
         (Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?),
         (None, Some((index, code_action_params))) => {
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/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 2e1cee8955..46193a6ff3 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp_ext.rs hash: 7a34bc3f38e2a7d8
+lsp_ext.rs hash: 326ad62235135223
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this issue:
diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md
index 863808e083..4f45aa321b 100644
--- a/docs/dev/syntax.md
+++ b/docs/dev/syntax.md
@@ -220,7 +220,7 @@ type SyntaxNode = Arc<SyntaxData>;
 struct SyntaxData {
     offset: usize,
     parent: Option<SyntaxNode>,
-    green: Arc<GreenNode>,
+    green: Arc<GreeNode>,
 }
 
 impl SyntaxNode {
@@ -302,7 +302,7 @@ type SyntaxNode = Arc<SyntaxData>;
 struct SyntaxData {
     offset: usize,
     parent: Option<SyntaxNode>,
-    green: Arc<GreenNode>,
+    green: Arc<GreeNode>,
     children: Vec<OnceCell<SyntaxNode>>,
 }
 ```
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..18f5b3aa10 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);
     };
 }
 
@@ -734,9 +735,6 @@ export function resolveCodeAction(ctx: Ctx): Cmd {
         const fileSystemEdit = await client.protocol2CodeConverter.asWorkspaceEdit(lcFileSystemEdit);
         await vscode.workspace.applyEdit(fileSystemEdit);
         await applySnippetWorkspaceEdit(edit);
-        if (item.command != null) {
-            await vscode.commands.executeCommand(item.command.command, item.command.arguments);
-        }
     };
 }
 
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]
diff --git a/xtask/src/release/changelog.rs b/xtask/src/release/changelog.rs
index 2647f7794f..ceef9fac2d 100644
--- a/xtask/src/release/changelog.rs
+++ b/xtask/src/release/changelog.rs
@@ -11,53 +11,56 @@ pub(crate) fn get_changelog(
     prev_tag: &str,
     today: &str,
 ) -> anyhow::Result<String> {
-    let token = match env::var("GITHUB_TOKEN") {
-        Ok(token) => token,
-        Err(_) => bail!("Please obtain a personal access token from https://github.com/settings/tokens and set the `GITHUB_TOKEN` environment variable."),
-    };
-
-    let git_log = cmd!(sh, "git log {prev_tag}..HEAD --reverse").read()?;
+    let git_log = cmd!(sh, "git log {prev_tag}..HEAD --merges --reverse").read()?;
     let mut features = String::new();
     let mut fixes = String::new();
     let mut internal = String::new();
     let mut others = String::new();
     for line in git_log.lines() {
         let line = line.trim_start();
-        if let Some(pr_num) = parse_pr_number(&line) {
-            let accept = "Accept: application/vnd.github.v3+json";
-            let authorization = format!("Authorization: token {}", token);
-            let pr_url = "https://api.github.com/repos/rust-lang/rust-analyzer/issues";
-
-            // we don't use an HTTPS client or JSON parser to keep the build times low
-            let pr = pr_num.to_string();
-            let pr_json =
-                cmd!(sh, "curl -s -H {accept} -H {authorization} {pr_url}/{pr}").read()?;
-            let pr_title = cmd!(sh, "jq .title").stdin(&pr_json).read()?;
-            let pr_title = unescape(&pr_title[1..pr_title.len() - 1]);
-            let pr_comment = cmd!(sh, "jq .body").stdin(pr_json).read()?;
-
-            let comments_json =
-                cmd!(sh, "curl -s -H {accept} -H {authorization} {pr_url}/{pr}/comments").read()?;
-            let pr_comments = cmd!(sh, "jq .[].body").stdin(comments_json).read()?;
-
-            let l = iter::once(pr_comment.as_str())
-                .chain(pr_comments.lines())
-                .rev()
-                .find_map(|it| {
-                    let it = unescape(&it[1..it.len() - 1]);
-                    it.lines().find_map(parse_changelog_line)
-                })
-                .into_iter()
-                .next()
-                .unwrap_or_else(|| parse_title_line(&pr_title));
-            let s = match l.kind {
-                PrKind::Feature => &mut features,
-                PrKind::Fix => &mut fixes,
-                PrKind::Internal => &mut internal,
-                PrKind::Other => &mut others,
-                PrKind::Skip => continue,
-            };
-            writeln!(s, "* pr:{}[] {}", pr_num, l.message.as_deref().unwrap_or(&pr_title)).unwrap();
+        if let Some(p) = line.find(':') {
+            let pr = &line[..p];
+            if let Ok(pr_num) = pr.parse::<u32>() {
+                let accept = "Accept: application/vnd.github.v3+json";
+                let token = match env::var("GITHUB_TOKEN") {
+                    Ok(token) => token,
+                    Err(_) => bail!("Please obtain a personal access token from https://github.com/settings/tokens and set the `GITHUB_TOKEN` environment variable."),
+                };
+                let authorization = format!("Authorization: token {}", token);
+                let pr_url = "https://api.github.com/repos/rust-analyzer/rust-analyzer/issues";
+
+                // we don't use an HTTPS client or JSON parser to keep the build times low
+                let pr_json =
+                    cmd!(sh, "curl -s -H {accept} -H {authorization} {pr_url}/{pr}").read()?;
+                let pr_title = cmd!(sh, "jq .title").stdin(&pr_json).read()?;
+                let pr_title = unescape(&pr_title[1..pr_title.len() - 1]);
+                let pr_comment = cmd!(sh, "jq .body").stdin(pr_json).read()?;
+
+                let comments_json =
+                    cmd!(sh, "curl -s -H {accept} -H {authorization} {pr_url}/{pr}/comments")
+                        .read()?;
+                let pr_comments = cmd!(sh, "jq .[].body").stdin(comments_json).read()?;
+
+                let l = iter::once(pr_comment.as_str())
+                    .chain(pr_comments.lines())
+                    .rev()
+                    .find_map(|it| {
+                        let it = unescape(&it[1..it.len() - 1]);
+                        it.lines().find_map(parse_changelog_line)
+                    })
+                    .into_iter()
+                    .next()
+                    .unwrap_or_else(|| parse_title_line(&pr_title));
+                let s = match l.kind {
+                    PrKind::Feature => &mut features,
+                    PrKind::Fix => &mut fixes,
+                    PrKind::Internal => &mut internal,
+                    PrKind::Other => &mut others,
+                    PrKind::Skip => continue,
+                };
+                writeln!(s, "* pr:{}[] {}", pr_num, l.message.as_deref().unwrap_or(&pr_title))
+                    .unwrap();
+            }
         }
     }
 
@@ -70,6 +73,11 @@ pub(crate) fn get_changelog(
 Commit: commit:{}[] +
 Release: release:{}[]
 
+== Sponsors
+
+**Become a sponsor:** On https://opencollective.com/rust-analyzer/[OpenCollective] or
+https://github.com/sponsors/rust-analyzer[GitHub Sponsors].
+
 == New Features
 
 {}
@@ -109,24 +117,6 @@ fn unescape(s: &str) -> String {
     s.replace(r#"\""#, "").replace(r#"\n"#, "\n").replace(r#"\r"#, "")
 }
 
-fn parse_pr_number(s: &str) -> Option<u32> {
-    const BORS_PREFIX: &str = "Merge #";
-    const HOMU_PREFIX: &str = "Auto merge of #";
-    if s.starts_with(BORS_PREFIX) {
-        let s = &s[BORS_PREFIX.len()..];
-        s.parse().ok()
-    } else if s.starts_with(HOMU_PREFIX) {
-        let s = &s[HOMU_PREFIX.len()..];
-        if let Some(space) = s.find(' ') {
-            s[..space].parse().ok()
-        } else {
-            None
-        }
-    } else {
-        None
-    }
-}
-
 fn parse_changelog_line(s: &str) -> Option<PrInfo> {
     let parts = s.splitn(3, ' ').collect::<Vec<_>>();
     if parts.len() < 2 || parts[0] != "changelog" {


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_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 6b57f9962c..b3a0bd2773 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -341,7 +341,6 @@ pub fn test_some_range(a: int) -> bool {
                 group: None,
                 target: 59..60,
                 source_change: None,
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&extract_into_variable_assist);
@@ -357,7 +356,6 @@ pub fn test_some_range(a: int) -> bool {
                 group: None,
                 target: 59..60,
                 source_change: None,
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&extract_into_function_assist);
@@ -387,7 +385,6 @@ pub fn test_some_range(a: int) -> bool {
                 group: None,
                 target: 59..60,
                 source_change: None,
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&extract_into_variable_assist);
@@ -403,7 +400,6 @@ pub fn test_some_range(a: int) -> bool {
                 group: None,
                 target: 59..60,
                 source_change: None,
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&extract_into_function_assist);
@@ -454,7 +450,6 @@ pub fn test_some_range(a: int) -> bool {
                         is_snippet: true,
                     },
                 ),
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&extract_into_variable_assist);
@@ -470,7 +465,6 @@ pub fn test_some_range(a: int) -> bool {
                 group: None,
                 target: 59..60,
                 source_change: None,
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&extract_into_function_assist);
@@ -513,7 +507,6 @@ pub fn test_some_range(a: int) -> bool {
                         is_snippet: true,
                     },
                 ),
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&extract_into_variable_assist);
@@ -550,7 +543,6 @@ pub fn test_some_range(a: int) -> bool {
                         is_snippet: true,
                     },
                 ),
-                trigger_signature_help: false,
             }
         "#]]
         .assert_debug_eq(&extract_into_function_assist);
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
-        "##]],
-    )
-}
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
index 41c509452a..4610984acb 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
@@ -318,7 +318,6 @@
                             "quickfix",
                         ),
                     ),
-                    command: None,
                     edit: Some(
                         SnippetWorkspaceEdit {
                             changes: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
index 1c5c336223..46d44192c5 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
@@ -165,7 +165,6 @@
                             "quickfix",
                         ),
                     ),
-                    command: None,
                     edit: Some(
                         SnippetWorkspaceEdit {
                             changes: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
index 3ab3412d97..b595487e8f 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
@@ -165,7 +165,6 @@
                             "quickfix",
                         ),
                     ),
-                    command: None,
                     edit: Some(
                         SnippetWorkspaceEdit {
                             changes: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
index 0702420aa5..3ee50392a7 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
@@ -165,7 +165,6 @@
                             "quickfix",
                         ),
                     ),
-                    command: None,
                     edit: Some(
                         SnippetWorkspaceEdit {
                             changes: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
index 4365e450df..d7987f65ed 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
@@ -328,7 +328,6 @@
                             "quickfix",
                         ),
                     ),
-                    command: None,
                     edit: Some(
                         SnippetWorkspaceEdit {
                             changes: Some(