about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/lib.rs10
-rw-r--r--crates/hir-def/src/nameres.rs9
-rw-r--r--crates/hir-def/src/nameres/collector.rs50
-rw-r--r--crates/hir-def/src/resolver.rs1
-rw-r--r--crates/hir/src/lib.rs11
-rw-r--r--crates/hir/src/semantics.rs14
-rw-r--r--crates/hir/src/source_analyzer.rs64
-rw-r--r--crates/ide-completion/src/item.rs1
-rw-r--r--crates/ide-db/src/defs.rs13
-rw-r--r--crates/ide-db/src/lib.rs1
-rw-r--r--crates/ide-db/src/path_transform.rs3
-rw-r--r--crates/ide-db/src/rename.rs2
-rw-r--r--crates/ide/src/doc_links.rs6
-rw-r--r--crates/ide/src/hover/render.rs2
-rw-r--r--crates/ide/src/navigation_target.rs2
-rw-r--r--crates/ide/src/signature_help.rs3
-rw-r--r--crates/ide/src/syntax_highlighting.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs1
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs1
-rw-r--r--crates/rust-analyzer/src/to_proto.rs5
22 files changed, 144 insertions, 58 deletions
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 0dd0a5861ef..56603f4b154 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -934,11 +934,11 @@ fn derive_macro_as_call_id(
     derive_attr: AttrId,
     derive_pos: u32,
     krate: CrateId,
-    resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
-) -> Result<MacroCallId, UnresolvedMacro> {
-    let def: MacroDefId = resolver(item_attr.path.clone())
+    resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>,
+) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
+    let (macro_id, def_id) = resolver(item_attr.path.clone())
         .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
-    let res = def.as_lazy_macro(
+    let call_id = def_id.as_lazy_macro(
         db.upcast(),
         krate,
         MacroCallKind::Derive {
@@ -947,7 +947,7 @@ fn derive_macro_as_call_id(
             derive_attr_index: derive_attr.ast_index,
         },
     );
-    Ok(res)
+    Ok((macro_id, def_id, call_id))
 }
 
 fn attr_macro_as_call_id(
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index 8165055e092..25fb302e875 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -76,7 +76,7 @@ use crate::{
     path::ModPath,
     per_ns::PerNs,
     visibility::Visibility,
-    AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, ModuleId, ProcMacroId,
+    AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, MacroId, ModuleId, ProcMacroId,
 };
 
 /// Contains the results of (early) name resolution.
@@ -108,7 +108,7 @@ pub struct DefMap {
     proc_macro_loading_error: Option<Box<str>>,
     /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
     /// attributes.
-    derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroCallId)>>,
+    derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
 
     /// Custom attributes registered with `#![register_attr]`.
     registered_attrs: Vec<SmolStr>,
@@ -299,7 +299,10 @@ impl DefMap {
         self.modules.iter()
     }
 
-    pub fn derive_helpers_in_scope(&self, id: AstId<ast::Adt>) -> Option<&[(Name, MacroCallId)]> {
+    pub fn derive_helpers_in_scope(
+        &self,
+        id: AstId<ast::Adt>,
+    ) -> Option<&[(Name, MacroId, MacroCallId)]> {
         self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref)
     }
 
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index e14d29952dd..f394c541719 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -18,7 +18,7 @@ use hir_expand::{
     ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
     MacroDefKind,
 };
-use itertools::Itertools;
+use itertools::{izip, Itertools};
 use la_arena::Idx;
 use limit::Limit;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -1055,7 +1055,7 @@ impl DefCollector<'_> {
         };
         let mut res = ReachedFixedPoint::Yes;
         macros.retain(|directive| {
-            let resolver = |path| {
+            let resolver2 = |path| {
                 let resolved_res = self.def_map.resolve_path_fp_with_macro(
                     self.db,
                     ResolveMode::Other,
@@ -1063,8 +1063,12 @@ impl DefCollector<'_> {
                     &path,
                     BuiltinShadowMode::Module,
                 );
-                resolved_res.resolved_def.take_macros().map(|it| macro_id_to_def_id(self.db, it))
+                resolved_res
+                    .resolved_def
+                    .take_macros()
+                    .map(|it| (it, macro_id_to_def_id(self.db, it)))
             };
+            let resolver = |path| resolver2(path).map(|(_, it)| it);
 
             match &directive.kind {
                 MacroDirectiveKind::FnLike { ast_id, expand_to } => {
@@ -1083,21 +1087,37 @@ impl DefCollector<'_> {
                     }
                 }
                 MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos } => {
-                    let call_id = derive_macro_as_call_id(
+                    let id = derive_macro_as_call_id(
                         self.db,
                         ast_id,
                         *derive_attr,
                         *derive_pos as u32,
                         self.def_map.krate,
-                        &resolver,
+                        &resolver2,
                     );
-                    if let Ok(call_id) = call_id {
+
+                    if let Ok((macro_id, def_id, call_id)) = id {
                         self.def_map.modules[directive.module_id].scope.set_derive_macro_invoc(
                             ast_id.ast_id,
                             call_id,
                             *derive_attr,
                             *derive_pos,
                         );
+                        // Record its helper attributes.
+                        if def_id.krate != self.def_map.krate {
+                            let def_map = self.db.crate_def_map(def_id.krate);
+                            if let Some(helpers) = def_map.exported_derives.get(&def_id) {
+                                self.def_map
+                                    .derive_helpers_in_scope
+                                    .entry(ast_id.ast_id.map(|it| it.upcast()))
+                                    .or_default()
+                                    .extend(izip!(
+                                        helpers.iter().cloned(),
+                                        iter::repeat(macro_id),
+                                        iter::repeat(call_id),
+                                    ));
+                            }
+                        }
 
                         push_resolved(directive, call_id);
                         res = ReachedFixedPoint::No;
@@ -1129,7 +1149,7 @@ impl DefCollector<'_> {
 
                     if let Some(ident) = path.as_ident() {
                         if let Some(helpers) = self.def_map.derive_helpers_in_scope.get(&ast_id) {
-                            if helpers.iter().any(|(it, _)| it == ident) {
+                            if helpers.iter().any(|(it, ..)| it == ident) {
                                 cov_mark::hit!(resolved_derive_helper);
                                 // Resolved to derive helper. Collect the item's attributes again,
                                 // starting after the derive helper.
@@ -1144,7 +1164,7 @@ impl DefCollector<'_> {
                     };
                     if matches!(
                         def,
-                        MacroDefId {  kind:MacroDefKind::BuiltInAttr(expander, _),.. }
+                        MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. }
                         if expander.is_derive()
                     ) {
                         // Resolved to `#[derive]`
@@ -1313,20 +1333,6 @@ impl DefCollector<'_> {
             self.def_map.diagnostics.push(diag);
         }
 
-        // If we've just resolved a derive, record its helper attributes.
-        if let MacroCallKind::Derive { ast_id, .. } = &loc.kind {
-            if loc.def.krate != self.def_map.krate {
-                let def_map = self.db.crate_def_map(loc.def.krate);
-                if let Some(helpers) = def_map.exported_derives.get(&loc.def) {
-                    self.def_map
-                        .derive_helpers_in_scope
-                        .entry(ast_id.map(|it| it.upcast()))
-                        .or_default()
-                        .extend(helpers.iter().cloned().zip(std::iter::repeat(macro_call_id)));
-                }
-            }
-        }
-
         // Then, fetch and process the item tree. This will reuse the expansion result from above.
         let item_tree = self.db.file_item_tree(file_id);
         let mod_dir = self.mod_dirs[&module_id].clone();
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 0c1ae8a064a..3163fa0f93f 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -149,6 +149,7 @@ impl Resolver {
         self.resolve_module_path(db, path, BuiltinShadowMode::Module)
     }
 
+    // FIXME: This shouldn't exist
     pub fn resolve_module_path_in_trait_assoc_items(
         &self,
         db: &dyn DefDatabase,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 9ffbb3964cf..86b5bd3c2c8 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2252,6 +2252,17 @@ impl Local {
     }
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub struct DeriveHelper {
+    pub(crate) derive: MacroId,
+}
+
+impl DeriveHelper {
+    pub fn derive(&self) -> Macro {
+        Macro { id: self.derive.into() }
+    }
+}
+
 // FIXME: Wrong name? This is could also be a registered attribute
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct BuiltinAttr {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 218e15989ad..fc8f23f19ab 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -29,9 +29,9 @@ use crate::{
     db::HirDatabase,
     semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
     source_analyzer::{resolve_hir_path, SourceAnalyzer},
-    Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource,
-    HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, Path,
-    ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
+    Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function,
+    HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef,
+    Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -47,6 +47,7 @@ pub enum PathResolution {
     SelfType(Impl),
     BuiltinAttr(BuiltinAttr),
     ToolModule(ToolModule),
+    DeriveHelper(DeriveHelper),
 }
 
 impl PathResolution {
@@ -71,6 +72,7 @@ impl PathResolution {
             PathResolution::BuiltinAttr(_)
             | PathResolution::ToolModule(_)
             | PathResolution::Local(_)
+            | PathResolution::DeriveHelper(_)
             | PathResolution::ConstParam(_) => None,
             PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
             PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
@@ -856,7 +858,9 @@ impl<'db> SemanticsImpl<'db> {
                             None
                         }
                     }?;
-
+                    if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(token.file_id, &adt))) {
+                        return None;
+                    }
                     // Not an attribute, nor a derive, so it's either a builtin or a derive helper
                     // Try to resolve to a derive helper and downmap
                     let attr_name = attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
@@ -865,7 +869,7 @@ impl<'db> SemanticsImpl<'db> {
                         def_map.derive_helpers_in_scope(InFile::new(token.file_id, id))?;
                     let item = Some(adt.into());
                     let mut res = None;
-                    for (_, derive) in helpers.iter().filter(|(helper, _)| *helper == attr_name) {
+                    for (.., derive) in helpers.iter().filter(|(helper, ..)| *helper == attr_name) {
                         res = res.or(process_expansion_for_token(
                             &mut stack,
                             derive.as_file(),
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index e89f8a54298..d57a73ade3a 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -43,8 +43,8 @@ use syntax::{
 
 use crate::{
     db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
-    BuiltinType, Callable, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct,
-    ToolModule, Trait, Type, TypeAlias, Variant,
+    BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static,
+    Struct, ToolModule, Trait, Type, TypeAlias, Variant,
 };
 
 /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@@ -429,19 +429,21 @@ impl SourceAnalyzer {
             }
         }
 
-        let is_path_of_attr = path
+        let meta_path = path
             .syntax()
             .ancestors()
-            .map(|it| it.kind())
-            .take_while(|&kind| ast::Path::can_cast(kind) || ast::Meta::can_cast(kind))
+            .take_while(|it| {
+                let kind = it.kind();
+                ast::Path::can_cast(kind) || ast::Meta::can_cast(kind)
+            })
             .last()
-            .map_or(false, ast::Meta::can_cast);
+            .and_then(ast::Meta::cast);
 
         // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are
         // trying to resolve foo::bar.
         if path.parent_path().is_some() {
             return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path) {
-                None if is_path_of_attr => {
+                None if meta_path.is_some() => {
                     path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| {
                         ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
                             .map(PathResolution::ToolModule)
@@ -449,16 +451,50 @@ impl SourceAnalyzer {
                 }
                 res => res,
             };
-        } else if is_path_of_attr {
+        } else if let Some(meta_path) = meta_path {
             // Case where we are resolving the final path segment of a path in an attribute
             // in this case we have to check for inert/builtin attributes and tools and prioritize
             // resolution of attributes over other namespaces
-            let name_ref = path.as_single_name_ref();
-            let builtin = name_ref.as_ref().and_then(|name_ref| {
-                BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text())
-            });
-            if let Some(_) = builtin {
-                return builtin.map(PathResolution::BuiltinAttr);
+            if let Some(name_ref) = path.as_single_name_ref() {
+                let builtin =
+                    BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text());
+                if let Some(_) = builtin {
+                    return builtin.map(PathResolution::BuiltinAttr);
+                }
+
+                if let Some(attr) = meta_path.parent_attr() {
+                    let adt = if let Some(field) =
+                        attr.syntax().parent().and_then(ast::RecordField::cast)
+                    {
+                        field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
+                    } else if let Some(field) =
+                        attr.syntax().parent().and_then(ast::TupleField::cast)
+                    {
+                        field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
+                    } else if let Some(variant) =
+                        attr.syntax().parent().and_then(ast::Variant::cast)
+                    {
+                        variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast)
+                    } else {
+                        None
+                    };
+                    if let Some(adt) = adt {
+                        let ast_id = db.ast_id_map(self.file_id).ast_id(&adt);
+                        if let Some(helpers) = self
+                            .resolver
+                            .def_map()
+                            .derive_helpers_in_scope(InFile::new(self.file_id, ast_id))
+                        {
+                            // FIXME: Multiple derives can have the same helper
+                            let name_ref = name_ref.as_name();
+                            if let Some(&(_, derive, _)) =
+                                helpers.iter().find(|(name, ..)| *name == name_ref)
+                            {
+                                return Some(PathResolution::DeriveHelper(DeriveHelper { derive }));
+                            }
+                        }
+                    }
+                }
             }
             return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) {
                 Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))),
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 27482ea489b..27c3ccb35a1 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -292,6 +292,7 @@ impl CompletionItemKind {
                 SymbolKind::Const => "ct",
                 SymbolKind::ConstParam => "cp",
                 SymbolKind::Derive => "de",
+                SymbolKind::DeriveHelper => "dh",
                 SymbolKind::Enum => "en",
                 SymbolKind::Field => "fd",
                 SymbolKind::Function => "fn",
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index 692a3157203..a9a78e67290 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -7,9 +7,9 @@
 
 use arrayvec::ArrayVec;
 use hir::{
-    Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, Field, Function,
-    GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef, Name,
-    PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility,
+    Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field,
+    Function, GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef,
+    Name, PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility,
 };
 use stdx::impl_from;
 use syntax::{
@@ -37,6 +37,7 @@ pub enum Definition {
     Local(Local),
     GenericParam(GenericParam),
     Label(Label),
+    DeriveHelper(DeriveHelper),
     BuiltinAttr(BuiltinAttr),
     ToolModule(ToolModule),
 }
@@ -69,6 +70,7 @@ impl Definition {
             Definition::Local(it) => it.module(db),
             Definition::GenericParam(it) => it.module(db),
             Definition::Label(it) => it.module(db),
+            Definition::DeriveHelper(it) => it.derive().module(db),
             Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => {
                 return None
             }
@@ -94,7 +96,8 @@ impl Definition {
             | Definition::SelfType(_)
             | Definition::Local(_)
             | Definition::GenericParam(_)
-            | Definition::Label(_) => return None,
+            | Definition::Label(_)
+            | Definition::DeriveHelper(_) => return None,
         };
         Some(vis)
     }
@@ -118,6 +121,7 @@ impl Definition {
             Definition::Label(it) => it.name(db),
             Definition::BuiltinAttr(_) => return None, // FIXME
             Definition::ToolModule(_) => return None,  // FIXME
+            Definition::DeriveHelper(_) => return None, // FIXME
         };
         Some(name)
     }
@@ -500,6 +504,7 @@ impl From<PathResolution> for Definition {
             PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
             PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr),
             PathResolution::ToolModule(tool) => Definition::ToolModule(tool),
+            PathResolution::DeriveHelper(helper) => Definition::DeriveHelper(helper),
         }
     }
 }
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index 26648b4d763..966bba616f6 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -173,6 +173,7 @@ pub enum SymbolKind {
     Const,
     ConstParam,
     Derive,
+    DeriveHelper,
     Enum,
     Field,
     Function,
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index d78b8758d65..40af9e6fe2a 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -224,7 +224,8 @@ impl<'a> Ctx<'a> {
             | hir::PathResolution::SelfType(_)
             | hir::PathResolution::Def(_)
             | hir::PathResolution::BuiltinAttr(_)
-            | hir::PathResolution::ToolModule(_) => (),
+            | hir::PathResolution::ToolModule(_)
+            | hir::PathResolution::DeriveHelper(_) => (),
         }
         Some(())
     }
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index dc1626f4396..517fe3f246d 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -156,6 +156,8 @@ impl Definition {
             Definition::SelfType(_) => return None,
             Definition::BuiltinAttr(_) => return None,
             Definition::ToolModule(_) => return None,
+            // FIXME: This should be doable in theory
+            Definition::DeriveHelper(_) => return None,
         };
         return res;
 
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index fed327f52be..582e9fe7e80 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -190,7 +190,8 @@ pub(crate) fn resolve_doc_path_for_def(
         | Definition::SelfType(_)
         | Definition::Local(_)
         | Definition::GenericParam(_)
-        | Definition::Label(_) => None,
+        | Definition::Label(_)
+        | Definition::DeriveHelper(_) => None,
     }
     .map(Definition::from)
 }
@@ -515,7 +516,8 @@ fn filename_and_frag_for_def(
         | Definition::GenericParam(_)
         | Definition::Label(_)
         | Definition::BuiltinAttr(_)
-        | Definition::ToolModule(_) => return None,
+        | Definition::ToolModule(_)
+        | Definition::DeriveHelper(_) => return None,
     };
 
     Some((def, res, None))
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 925aaa61cdd..fcdb23fa18a 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -370,6 +370,8 @@ pub(super) fn definition(
         // FIXME: We should be able to show more info about these
         Definition::BuiltinAttr(it) => return render_builtin_attr(db, it),
         Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))),
+        // FIXME: it.name(db)
+        Definition::DeriveHelper(_it) => ("derive-helper".to_owned(), None),
     };
 
     let docs = match config.documentation {
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 1fb3b5ec3f3..9f049e298ad 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -196,6 +196,8 @@ impl TryToNav for Definition {
             Definition::BuiltinType(_) => None,
             Definition::ToolModule(_) => None,
             Definition::BuiltinAttr(_) => None,
+            // FIXME: The focus range should be set to the helper declaration
+            Definition::DeriveHelper(it) => it.derive().try_to_nav(db),
         }
     }
 }
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index ba287d13aec..fedc1a43582 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -237,7 +237,8 @@ fn signature_help_for_generics(
             | hir::PathResolution::Local(_)
             | hir::PathResolution::TypeParam(_)
             | hir::PathResolution::ConstParam(_)
-            | hir::PathResolution::SelfType(_) => return None,
+            | hir::PathResolution::SelfType(_)
+            | hir::PathResolution::DeriveHelper(_) => return None,
         };
 
         generic_def
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index d7ad6a75799..eef717c16f2 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -107,6 +107,7 @@ pub struct HlRange {
 // builtinType:: Emitted for builtin types like `u32`, `str` and `f32`.
 // comment:: Emitted for comments.
 // constParameter:: Emitted for const parameters.
+// deriveHelper:: Emitted for derive helper attributes.
 // enumMember:: Emitted for enum variants.
 // generic:: Emitted for generic tokens that have no mapping.
 // keyword:: Emitted for keywords.
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index fd3723ed454..9395e914c43 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -472,6 +472,7 @@ fn highlight_def(
         Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)),
         Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)),
         Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)),
+        Definition::DeriveHelper(_) => Highlight::new(HlTag::Symbol(SymbolKind::DeriveHelper)),
     };
 
     let def_crate = def.krate(db);
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 94d573a30b0..f779a985a99 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -270,6 +270,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag {
         Definition::Label(_) => SymbolKind::Label,
         Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr,
         Definition::ToolModule(_) => SymbolKind::ToolModule,
+        Definition::DeriveHelper(_) => SymbolKind::DeriveHelper,
     };
     HlTag::Symbol(symbol)
 }
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index b900dadcfa3..5262770f303 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -134,6 +134,7 @@ impl HlTag {
                 SymbolKind::Const => "constant",
                 SymbolKind::ConstParam => "const_param",
                 SymbolKind::Derive => "derive",
+                SymbolKind::DeriveHelper => "derive_helper",
                 SymbolKind::Enum => "enum",
                 SymbolKind::Field => "field",
                 SymbolKind::Function => "function",
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 5fb945ea987..6c78b5df1a7 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -54,6 +54,7 @@ define_semantic_token_types![
     (COMPARISON, "comparison"),
     (CONST_PARAMETER, "constParameter"),
     (DERIVE, "derive"),
+    (DERIVE_HELPER, "deriveHelper"),
     (DOT, "dot"),
     (ESCAPE_SEQUENCE, "escapeSequence"),
     (FORMAT_SPECIFIER, "formatSpecifier"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 9c8e618b6ed..7f4fa57fa1e 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -53,7 +53,8 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
         SymbolKind::Macro
         | SymbolKind::BuiltinAttr
         | SymbolKind::Attribute
-        | SymbolKind::Derive => lsp_types::SymbolKind::FUNCTION,
+        | SymbolKind::Derive
+        | SymbolKind::DeriveHelper => lsp_types::SymbolKind::FUNCTION,
         SymbolKind::Module | SymbolKind::ToolModule => lsp_types::SymbolKind::MODULE,
         SymbolKind::TypeAlias | SymbolKind::TypeParam | SymbolKind::SelfType => {
             lsp_types::SymbolKind::TYPE_PARAMETER
@@ -117,6 +118,7 @@ pub(crate) fn completion_item_kind(
             SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT,
             SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
             SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION,
+            SymbolKind::DeriveHelper => lsp_types::CompletionItemKind::FUNCTION,
             SymbolKind::Enum => lsp_types::CompletionItemKind::ENUM,
             SymbolKind::Field => lsp_types::CompletionItemKind::FIELD,
             SymbolKind::Function => lsp_types::CompletionItemKind::FUNCTION,
@@ -561,6 +563,7 @@ fn semantic_token_type_and_modifiers(
         HlTag::Symbol(symbol) => match symbol {
             SymbolKind::Attribute => semantic_tokens::ATTRIBUTE,
             SymbolKind::Derive => semantic_tokens::DERIVE,
+            SymbolKind::DeriveHelper => semantic_tokens::DERIVE_HELPER,
             SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE,
             SymbolKind::Impl => semantic_tokens::TYPE_ALIAS,
             SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,