Repository: rust-analyzer/rust-analyzer
Pull request #10806 (2021-11-19 11:46:35)

fix: inlay param hint hiding heurstic is case unsensitive
bors r+


Patch:
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 5c4104baae..0f1963e831 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -32,7 +32,6 @@ diagnostics![
     BreakOutsideOfLoop,
     InactiveCode,
     IncorrectCase,
-    InvalidDeriveTarget,
     MacroError,
     MismatchedArgCount,
     MissingFields,
@@ -99,11 +98,6 @@ pub struct UnimplementedBuiltinMacro {
     pub node: InFile<SyntaxNodePtr>,
 }
 
-#[derive(Debug)]
-pub struct InvalidDeriveTarget {
-    pub node: InFile<SyntaxNodePtr>,
-}
-
 #[derive(Debug)]
 pub struct NoSuchField {
     pub field: InFile<AstPtr<ast::RecordExprField>>,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index cca9b08f5b..4fdcb88bbf 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -83,10 +83,10 @@ pub use crate::{
     attrs::{HasAttrs, Namespace},
     diagnostics::{
         AddReferenceHere, AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase,
-        InvalidDeriveTarget, MacroError, MismatchedArgCount, MissingFields, MissingMatchArms,
-        MissingOkOrSomeInTailExpr, MissingUnsafe, NoSuchField, RemoveThisSemicolon,
-        ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro, UnresolvedExternCrate,
-        UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
+        MacroError, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
+        MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
+        UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
+        UnresolvedModule, UnresolvedProcMacro,
     },
     has_source::HasSource,
     semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
@@ -654,21 +654,6 @@ impl Module {
                         .into(),
                     );
                 }
-                DefDiagnosticKind::InvalidDeriveTarget { ast, id } => {
-                    let node = ast.to_node(db.upcast());
-                    let derive = node.attrs().nth(*id as usize);
-                    match derive {
-                        Some(derive) => {
-                            acc.push(
-                                InvalidDeriveTarget {
-                                    node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&derive))),
-                                }
-                                .into(),
-                            );
-                        }
-                        None => stdx::never!("derive diagnostic on item without derive attribute"),
-                    }
-                }
             }
         }
         for decl in self.declarations(db) {
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 711062c713..fd45eccf5a 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -723,32 +723,34 @@ impl Attr {
     /// to derive macros.
     ///
     /// Returns `None` when the attribute does not have a well-formed `#[derive]` attribute input.
-    pub(crate) fn parse_derive(&self) -> Option<impl Iterator<Item = ModPath> + '_> {
-        let args = match self.input.as_deref() {
-            Some(AttrInput::TokenTree(args, _)) => args,
-            _ => return None,
-        };
-
-        if args.delimiter_kind() != Some(DelimiterKind::Parenthesis) {
-            return None;
+    pub(crate) fn parse_derive(&self) -> Option<impl Iterator<Item = ModPath>> {
+        if let Some(AttrInput::TokenTree(args, _)) = self.input.as_deref() {
+            if args.delimiter_kind() != Some(DelimiterKind::Parenthesis) {
+                return None;
+            }
+            let mut counter = 0;
+            let paths = args
+                .token_trees
+                .iter()
+                .group_by(move |tt| {
+                    if let tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })) = tt {
+                        counter += 1;
+                    }
+                    counter
+                })
+                .into_iter()
+                .map(|(_, tts)| {
+                    let segments = tts.filter_map(|tt| match tt {
+                        tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
+                        _ => None,
+                    });
+                    ModPath::from_segments(PathKind::Plain, segments)
+                })
+                .collect::<Vec<_>>();
+
+            return Some(paths.into_iter());
         }
-        let paths = args
-            .token_trees
-            .iter()
-            .group_by(|tt| {
-                matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))
-            })
-            .into_iter()
-            .map(|(_, tts)| {
-                let segments = tts.filter_map(|tt| match tt {
-                    tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
-                    _ => None,
-                });
-                ModPath::from_segments(PathKind::Plain, segments)
-            })
-            .collect::<Vec<_>>();
-
-        return Some(paths.into_iter());
+        None
     }
 
     pub fn string_value(&self) -> Option<&SmolStr> {
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 343e189046..bafba0672f 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -226,6 +226,13 @@ enum MacroDirectiveKind {
     Attr { ast_id: AstIdWithPath<ast::Item>, attr: Attr, mod_item: ModItem },
 }
 
+struct DefData<'a> {
+    id: ModuleDefId,
+    name: &'a Name,
+    visibility: &'a RawVisibility,
+    has_constructor: bool,
+}
+
 /// Walks the tree of module recursively
 struct DefCollector<'a> {
     db: &'a dyn DefDatabase,
@@ -1055,10 +1062,13 @@ impl DefCollector<'_> {
                         &resolver,
                         &mut |_err| (),
                     );
-                    if let Ok(Ok(call_id)) = call_id {
-                        resolved.push((directive.module_id, call_id, directive.depth));
-                        res = ReachedFixedPoint::No;
-                        return false;
+                    match call_id {
+                        Ok(Ok(call_id)) => {
+                            resolved.push((directive.module_id, call_id, directive.depth));
+                            res = ReachedFixedPoint::No;
+                            return false;
+                        }
+                        Err(UnresolvedMacro { .. }) | Ok(Err(_)) => {}
                     }
                 }
                 MacroDirectiveKind::Derive { ast_id, derive_attr } => {
@@ -1069,16 +1079,19 @@ impl DefCollector<'_> {
                         self.def_map.krate,
                         &resolver,
                     );
-                    if let Ok(call_id) = call_id {
-                        self.def_map.modules[directive.module_id].scope.add_derive_macro_invoc(
-                            ast_id.ast_id,
-                            call_id,
-                            *derive_attr,
-                        );
+                    match call_id {
+                        Ok(call_id) => {
+                            self.def_map.modules[directive.module_id].scope.add_derive_macro_invoc(
+                                ast_id.ast_id,
+                                call_id,
+                                *derive_attr,
+                            );
 
-                        resolved.push((directive.module_id, call_id, directive.depth));
-                        res = ReachedFixedPoint::No;
-                        return false;
+                            resolved.push((directive.module_id, call_id, directive.depth));
+                            res = ReachedFixedPoint::No;
+                            return false;
+                        }
+                        Err(UnresolvedMacro { .. }) => {}
                     }
                 }
                 MacroDirectiveKind::Attr { ast_id, mod_item, attr } => {
@@ -1119,6 +1132,7 @@ impl DefCollector<'_> {
                         if expander.is_derive()
                     ) {
                         // Resolved to `#[derive]`
+                        let file_id = ast_id.ast_id.file_id;
                         let item_tree = self.db.file_item_tree(file_id);
 
                         let ast_id: FileAstId<ast::Item> = match *mod_item {
@@ -1126,12 +1140,8 @@ impl DefCollector<'_> {
                             ModItem::Union(it) => item_tree[it].ast_id.upcast(),
                             ModItem::Enum(it) => item_tree[it].ast_id.upcast(),
                             _ => {
-                                let diag = DefDiagnostic::invalid_derive_target(
-                                    directive.module_id,
-                                    ast_id.ast_id,
-                                    attr.id,
-                                );
-                                self.def_map.diagnostics.push(diag);
+                                // Cannot use derive on this item.
+                                // FIXME: diagnose
                                 res = ReachedFixedPoint::No;
                                 return false;
                             }
@@ -1191,6 +1201,7 @@ impl DefCollector<'_> {
                                         ),
                                     );
 
+                                    let file_id = ast_id.ast_id.file_id;
                                     let item_tree = self.db.file_item_tree(file_id);
                                     return recollect_without(self, &item_tree);
                                 }
@@ -1342,18 +1353,19 @@ impl DefCollector<'_> {
         }
 
         for directive in &self.unresolved_imports {
-            if let ImportSource::Import { id: import, use_tree } = directive.import.source {
-                if matches!(
-                    (directive.import.path.segments().first(), &directive.import.path.kind),
-                    (Some(krate), PathKind::Plain | PathKind::Abs) if diagnosed_extern_crates.contains(krate)
-                ) {
-                    continue;
+            if let ImportSource::Import { id: import, use_tree } = &directive.import.source {
+                if let (Some(krate), PathKind::Plain | PathKind::Abs) =
+                    (directive.import.path.segments().first(), &directive.import.path.kind)
+                {
+                    if diagnosed_extern_crates.contains(krate) {
+                        continue;
+                    }
                 }
 
                 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
                     directive.module_id,
-                    import,
-                    use_tree,
+                    *import,
+                    *use_tree,
                 ));
             }
         }
@@ -1374,13 +1386,6 @@ struct ModCollector<'a, 'b> {
 
 impl ModCollector<'_, '_> {
     fn collect(&mut self, items: &[ModItem]) {
-        struct DefData<'a> {
-            id: ModuleDefId,
-            name: &'a Name,
-            visibility: &'a RawVisibility,
-            has_constructor: bool,
-        }
-
         let krate = self.def_collector.def_map.krate;
 
         // Note: don't assert that inserted value is fresh: it's simply not true
@@ -1398,18 +1403,18 @@ impl ModCollector<'_, '_> {
         // This should be processed eagerly instead of deferred to resolving.
         // `#[macro_use] extern crate` is hoisted to imports macros before collecting
         // any other items.
-        for &item in items {
-            let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
+        for item in items {
+            let attrs = self.item_tree.attrs(self.def_collector.db, krate, (*item).into());
             if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
                 if let ModItem::ExternCrate(id) = item {
-                    let import = &self.item_tree[id];
+                    let import = self.item_tree[*id].clone();
                     let attrs = self.item_tree.attrs(
                         self.def_collector.db,
                         krate,
-                        ModItem::from(id).into(),
+                        ModItem::from(*id).into(),
                     );
                     if attrs.by_key("macro_use").exists() {
-                        self.def_collector.import_macros_from_extern_crate(self.module_id, import);
+                        self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
                     }
                 }
             }
@@ -1651,7 +1656,9 @@ impl ModCollector<'_, '_> {
                         let is_enabled = item_tree
                             .top_level_attrs(db, self.def_collector.def_map.krate)
                             .cfg()
-                            .map_or(true, |cfg| self.is_cfg_enabled(&cfg));
+                            .map_or(true, |cfg| {
+                                self.def_collector.cfg_options.check(&cfg) != Some(false)
+                            });
                         if is_enabled {
                             let module_id = self.push_child_module(
                                 module.name.clone(),
@@ -1668,12 +1675,12 @@ impl ModCollector<'_, '_> {
                                 mod_dir,
                             }
                             .collect(item_tree.top_level_items());
-                            let is_macro_use = is_macro_use
+                            if is_macro_use
                                 || item_tree
                                     .top_level_attrs(db, self.def_collector.def_map.krate)
                                     .by_key("macro_use")
-                                    .exists();
-                            if is_macro_use {
+                                    .exists()
+                            {
                                 self.import_all_legacy_macros(module_id);
                             }
                         }
@@ -1707,17 +1714,14 @@ impl ModCollector<'_, '_> {
                 ModuleOrigin::File { declaration, definition, is_mod_rs }
             }
         };
-
         let res = modules.alloc(ModuleData::new(origin, vis));
         modules[res].parent = Some(self.module_id);
         for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
             modules[res].scope.define_legacy_macro(name, mac)
         }
         modules[self.module_id].children.insert(name.clone(), res);
-
         let module = self.def_collector.def_map.module_id(res);
-        let def = ModuleDefId::from(module);
-
+        let def: ModuleDefId = module.into();
         self.def_collector.def_map.modules[self.module_id].scope.declare(def);
         self.def_collector.update(
             self.module_id,
@@ -1782,29 +1786,30 @@ impl ModCollector<'_, '_> {
     }
 
     fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
-        if path.kind != PathKind::Plain {
-            return false;
-        }
-
-        let segments = path.segments();
-
-        if let Some(name) = segments.first() {
-            let name = name.to_smol_str();
-            let pred = |n: &_| *n == name;
-
-            let registered = self.def_collector.registered_tools.iter().map(SmolStr::as_str);
-            let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
-            if is_tool {
-                return true;
+        if path.kind == PathKind::Plain {
+            if let Some(tool_module) = path.segments().first() {
+                let tool_module = tool_module.to_smol_str();
+                let is_tool = builtin_attr::TOOL_MODULES
+                    .iter()
+                    .copied()
+                    .chain(self.def_collector.registered_tools.iter().map(SmolStr::as_str))
+                    .any(|m| tool_module == *m);
+                if is_tool {
+                    return true;
+                }
             }
 
-            if segments.len() == 1 {
-                let registered = self.def_collector.registered_attrs.iter().map(SmolStr::as_str);
-                let is_inert =
-                    builtin_attr::INERT_ATTRIBUTES.iter().copied().chain(registered).any(pred);
+            if let Some(name) = path.as_ident() {
+                let name = name.to_smol_str();
+                let is_inert = builtin_attr::INERT_ATTRIBUTES
+                    .iter()
+                    .copied()
+                    .chain(self.def_collector.registered_attrs.iter().map(SmolStr::as_str))
+                    .any(|attr| name == *attr);
                 return is_inert;
             }
         }
+
         false
     }
 
@@ -1826,7 +1831,7 @@ impl ModCollector<'_, '_> {
 
         let is_export = export_attr.exists();
         let is_local_inner = if is_export {
-            export_attr.tt_values().flat_map(|it| &it.token_trees).any(|it| match it {
+            export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
                 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
                     ident.text.contains("local_inner_macros")
                 }
@@ -1847,14 +1852,12 @@ impl ModCollector<'_, '_> {
                     &name
                 }
                 None => {
-                    let explicit_name =
-                        attrs.by_key("rustc_builtin_macro").tt_values().next().and_then(|tt| {
-                            match tt.token_trees.first() {
-                                Some(tt::TokenTree::Leaf(tt::Leaf::Ident(name))) => Some(name),
-                                _ => None,
-                            }
-                        });
-                    match explicit_name {
+                    match attrs.by_key("rustc_builtin_macro").tt_values().next().and_then(|tt| {
+                        match tt.token_trees.first() {
+                            Some(tt::TokenTree::Leaf(tt::Leaf::Ident(name))) => Some(name),
+                            _ => None,
+                        }
+                    }) {
                         Some(ident) => {
                             name = ident.as_name();
                             &name
@@ -1944,7 +1947,7 @@ impl ModCollector<'_, '_> {
     }
 
     fn collect_macro_call(&mut self, mac: &MacroCall) {
-        let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path));
+        let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, (*mac.path).clone());
 
         // Case 1: try to resolve in legacy scope and expand macro_rules
         let mut error = None;
diff --git a/crates/hir_def/src/nameres/diagnostics.rs b/crates/hir_def/src/nameres/diagnostics.rs
index 1eb3844731..95061f6015 100644
--- a/crates/hir_def/src/nameres/diagnostics.rs
+++ b/crates/hir_def/src/nameres/diagnostics.rs
@@ -6,7 +6,6 @@ use la_arena::Idx;
 use syntax::ast;
 
 use crate::{
-    attr::AttrId,
     item_tree::{self, ItemTreeId},
     nameres::LocalModuleId,
     path::ModPath,
@@ -30,8 +29,6 @@ pub enum DefDiagnosticKind {
     MacroError { ast: MacroCallKind, message: String },
 
     UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
-
-    InvalidDeriveTarget { ast: AstId<ast::Item>, id: u32 },
 }
 
 #[derive(Debug, PartialEq, Eq)]
@@ -105,15 +102,4 @@ impl DefDiagnostic {
     ) -> Self {
         Self { in_module: container, kind: DefDiagnosticKind::UnimplementedBuiltinMacro { ast } }
     }
-
-    pub(super) fn invalid_derive_target(
-        container: LocalModuleId,
-        ast: AstId<ast::Item>,
-        id: AttrId,
-    ) -> Self {
-        Self {
-            in_module: container,
-            kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id: id.ast_index },
-        }
-    }
 }
diff --git a/crates/hir_def/src/nameres/mod_resolution.rs b/crates/hir_def/src/nameres/mod_resolution.rs
index b6f1b17870..bd2588e592 100644
--- a/crates/hir_def/src/nameres/mod_resolution.rs
+++ b/crates/hir_def/src/nameres/mod_resolution.rs
@@ -24,13 +24,22 @@ impl ModDir {
     pub(super) fn root() -> ModDir {
         ModDir { dir_path: DirPath::empty(), root_non_dir_owner: false, depth: 0 }
     }
+    fn child(&self, dir_path: DirPath, root_non_dir_owner: bool) -> Option<ModDir> {
+        let depth = self.depth + 1;
+        if MOD_DEPTH_LIMIT.check(depth as usize).is_err() {
+            tracing::error!("MOD_DEPTH_LIMIT exceeded");
+            cov_mark::hit!(circular_mods);
+            return None;
+        }
+        Some(ModDir { dir_path, root_non_dir_owner, depth })
+    }
 
     pub(super) fn descend_into_definition(
         &self,
         name: &Name,
         attr_path: Option<&SmolStr>,
     ) -> Option<ModDir> {
-        let path = match attr_path.map(SmolStr::as_str) {
+        let path = match attr_path.map(|it| it.as_str()) {
             None => {
                 let mut path = self.dir_path.clone();
                 path.push(&name.to_smol_str());
@@ -47,16 +56,6 @@ impl ModDir {
         self.child(path, false)
     }
 
-    fn child(&self, dir_path: DirPath, root_non_dir_owner: bool) -> Option<ModDir> {
-        let depth = self.depth + 1;
-        if MOD_DEPTH_LIMIT.check(depth as usize).is_err() {
-            tracing::error!("MOD_DEPTH_LIMIT exceeded");
-            cov_mark::hit!(circular_mods);
-            return None;
-        }
-        Some(ModDir { dir_path, root_non_dir_owner, depth })
-    }
-
     pub(super) fn resolve_declaration(
         &self,
         db: &dyn DefDatabase,
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 8d24bdcf4f..2ebf46b564 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -11,7 +11,7 @@ use ide_db::{
     base_db::FileRange,
     defs::Definition,
     helpers::{pick_best_token, FamousDefs},
-    FxIndexSet, RootDatabase,
+    RootDatabase,
 };
 use itertools::Itertools;
 use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, T};
@@ -69,7 +69,7 @@ impl HoverAction {
     }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+#[derive(Debug, Clone, Eq, PartialEq)]
 pub struct HoverGotoTypeData {
     pub mod_path: String,
     pub nav: NavigationTarget,
@@ -136,12 +136,11 @@ pub(crate) fn hover(
         .flatten()
         .unique_by(|&(def, _)| def)
         .filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
-        .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
+        .reduce(|mut acc, HoverResult { markup, actions }| {
             acc.actions.extend(actions);
             acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
             acc
         });
-
     if result.is_none() {
         // fallbacks, show keywords or types
         if let Some(res) = render::keyword(sema, config, &original_token) {
@@ -153,10 +152,7 @@ pub(crate) fn hover(
             return res;
         }
     }
-    result.map(|mut res: HoverResult| {
-        res.actions = dedupe_or_merge_hover_actions(res.actions);
-        RangeInfo::new(original_token.text_range(), res)
-    })
+    result.map(|res| RangeInfo::new(original_token.text_range(), res))
 }
 
 pub(crate) fn hover_for_definition(
@@ -345,43 +341,3 @@ fn walk_and_push_ty(
         }
     });
 }
-
-fn dedupe_or_merge_hover_actions(actions: Vec<HoverAction>) -> Vec<HoverAction> {
-    let mut deduped_actions = Vec::with_capacity(actions.len());
-    let mut go_to_type_targets = FxIndexSet::default();
-
-    let mut seen_implementation = false;
-    let mut seen_reference = false;
-    let mut seen_runnable = false;
-    for action in actions {
-        match action {
-            HoverAction::GoToType(targets) => {
-                go_to_type_targets.extend(targets);
-            }
-            HoverAction::Implementation(..) => {
-                if !seen_implementation {
-                    seen_implementation = true;
-                    deduped_actions.push(action);
-                }
-            }
-            HoverAction::Reference(..) => {
-                if !seen_reference {
-                    seen_reference = true;
-                    deduped_actions.push(action);
-                }
-            }
-            HoverAction::Runnable(..) => {
-                if !seen_runnable {
-                    seen_runnable = true;
-                    deduped_actions.push(action);
-                }
-            }
-        };
-    }
-
-    if !go_to_type_targets.is_empty() {
-        deduped_actions.push(HoverAction::GoToType(go_to_type_targets.into_iter().collect()));
-    }
-
-    deduped_actions
-}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index f3c5dce7c3..94af017d64 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -441,15 +441,25 @@ fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) ->
         None => return false,
     };
 
+    // std is honestly too panic happy...
+    let str_split_at = |str: &str, at| str.is_char_boundary(at).then(|| argument.split_at(at));
+
     let param_name = param_name.trim_start_matches('_');
     let argument = argument.trim_start_matches('_');
-    if argument.strip_prefix(param_name).map_or(false, |s| s.starts_with('_')) {
-        return true;
+
+    match str_split_at(argument, param_name.len()) {
+        Some((prefix, rest)) if prefix.eq_ignore_ascii_case(param_name) => {
+            return rest.is_empty() || rest.starts_with('_');
+        }
+        _ => (),
     }
-    if argument.strip_suffix(param_name).map_or(false, |s| s.ends_with('_')) {
-        return true;
+    match argument.len().checked_sub(param_name.len()).and_then(|at| str_split_at(argument, at)) {
+        Some((rest, suffix)) if param_name.eq_ignore_ascii_case(suffix) => {
+            return rest.is_empty() || rest.ends_with('_');
+        }
+        _ => (),
     }
-    argument == param_name
+    false
 }
 
 /// Hide the parameter name of a unary function if it is a `_` - prefixed suffix of the function's name, or equal.
@@ -464,9 +474,13 @@ fn is_param_name_suffix_of_fn_name(
     match (callable.n_params(), fn_name) {
         (1, Some(function)) => {
             function == param_name
-                || (function.len() > param_name.len()
-                    && function.ends_with(param_name)
-                    && function[..function.len() - param_name.len()].ends_with('_'))
+                || function
+                    .len()
+                    .checked_sub(param_name.len())
+                    .and_then(|at| function.is_char_boundary(at).then(|| function.split_at(at)))
+                    .map_or(false, |(prefix, suffix)| {
+                        suffix.eq_ignore_ascii_case(param_name) && prefix.ends_with('_')
+                    })
         }
         _ => false,
     }
@@ -852,6 +866,9 @@ enum CompletionKind {
 fn non_ident_pat((a, b): (u32, u32)) {}
 
 fn main() {
+    const PARAM: u32 = 0;
+    foo(PARAM);
+
     check("");
 
     map(0);
diff --git a/crates/ide_diagnostics/src/handlers/invalid_derive_target.rs b/crates/ide_diagnostics/src/handlers/invalid_derive_target.rs
deleted file mode 100644
index 556d2dc0cb..0000000000
--- a/crates/ide_diagnostics/src/handlers/invalid_derive_target.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-use crate::{Diagnostic, DiagnosticsContext, Severity};
-
-// Diagnostic: invalid-derive-target
-//
-// This diagnostic is shown when the derive attribute is used on an item other than a `struct`,
-// `enum` or `union`.
-pub(crate) fn invalid_derive_target(
-    ctx: &DiagnosticsContext<'_>,
-    d: &hir::InvalidDeriveTarget,
-) -> Diagnostic {
-    // Use more accurate position if available.
-    let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
-
-    Diagnostic::new(
-        "invalid-derive-target",
-        "`derive` may only be applied to `struct`s, `enum`s and `union`s",
-        display_range,
-    )
-    .severity(Severity::Error)
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::check_diagnostics;
-
-    #[test]
-    fn fails_on_function() {
-        check_diagnostics(
-            r#"
-//- minicore:derive
-mod __ {
-    #[derive()]
-  //^^^^^^^^^^^ error: `derive` may only be applied to `struct`s, `enum`s and `union`s
-    fn main() {}
-}
-            "#,
-        );
-    }
-}
diff --git a/crates/ide_diagnostics/src/lib.rs b/crates/ide_diagnostics/src/lib.rs
index 5070f24003..2f53b647b1 100644
--- a/crates/ide_diagnostics/src/lib.rs
+++ b/crates/ide_diagnostics/src/lib.rs
@@ -28,7 +28,6 @@ mod handlers {
     pub(crate) mod break_outside_of_loop;
     pub(crate) mod inactive_code;
     pub(crate) mod incorrect_case;
-    pub(crate) mod invalid_derive_target;
     pub(crate) mod macro_error;
     pub(crate) mod mismatched_arg_count;
     pub(crate) mod missing_fields;
@@ -196,7 +195,6 @@ pub fn diagnostics(
             AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
             AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
             AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
-            AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
 
             AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
                 Some(it) => it,


Test patch:
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 5db6fd7974..3b415c54fb 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -173,7 +173,7 @@ m!(ab$0c);
             ---
 
             Outer
-        "#]],
+            "#]],
     );
 }
 
@@ -1135,39 +1135,6 @@ fn foo() {
     );
 }
 
-#[test]
-fn test_hover_multiple_actions() {
-    check_actions(
-        r#"
-struct Bar;
-struct Foo { bar: Bar }
-
-fn foo(Foo { b$0ar }: &Foo) {}
-        "#,
-        expect![[r#"
-            [
-                GoToType(
-                    [
-                        HoverGotoTypeData {
-                            mod_path: "test::Bar",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..11,
-                                focus_range: 7..10,
-                                name: "Bar",
-                                kind: Struct,
-                                description: "struct Bar",
-                            },
-                        },
-                    ],
-                ),
-            ]
-        "#]],
-    )
-}
-
 #[test]
 fn test_hover_through_literal_string_in_builtin_macro() {
     check_hover_no_result(
@@ -1783,6 +1750,9 @@ fn foo_$0test() {}
                         cfg: None,
                     },
                 ),
+                GoToType(
+                    [],
+                ),
             ]
         "#]],
     );
@@ -2779,21 +2749,21 @@ fn main() {
 }
 "#,
         expect![[r#"
-            *f*
+                *f*
 
-            ```rust
-            f: &i32
-            ```
-            ---
+                ```rust
+                f: &i32
+                ```
+                ---
 
-            ```rust
-            test::S
-            ```
+                ```rust
+                test::S
+                ```
 
-            ```rust
-            f: i32
-            ```
-        "#]],
+                ```rust
+                f: i32
+                ```
+            "#]],
     );
 }