Auto merge of #12864 - Veykril:derive-helpers, r=Veykril

feat: Resolve derive helper attributes in IDE layer

![image](https://user-images.githubusercontent.com/3757771/180647125-98cae13b-a7b4-446b-8a64-fae014f982e2.png)

Also downmaps tokens inside deriver helpers to the derive expansion
![image](https://user-images.githubusercontent.com/3757771/180642110-699bdc73-e1f3-4692-94f2-21544f824ab9.png)

This does not handle the case where multiple derives make use of the same helper name though, unsure how to tackle that yet.

Partially addresses https://github.com/rust-lang/rust-analyzer/issues/10935
This commit is contained in:
bors 2022-07-26 07:34:11 +00:00
commit e36a20c24f
27 changed files with 334 additions and 137 deletions

View File

@ -12,7 +12,7 @@ use crate::{
db::DefDatabase, db::DefDatabase,
intern::Interned, intern::Interned,
item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId}, item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
nameres::{attr_resolution::ResolvedAttr, DefMap}, nameres::{attr_resolution::ResolvedAttr, proc_macro::ProcMacroKind, DefMap},
type_ref::{TraitRef, TypeBound, TypeRef}, type_ref::{TraitRef, TypeBound, TypeRef},
visibility::RawVisibility, visibility::RawVisibility,
AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
@ -348,7 +348,8 @@ impl MacroRulesData {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProcMacroData { pub struct ProcMacroData {
pub name: Name, pub name: Name,
// FIXME: Record deriver helper here? /// Derive helpers, if this is a derive
pub helpers: Option<Box<[Name]>>,
} }
impl ProcMacroData { impl ProcMacroData {
@ -360,17 +361,23 @@ impl ProcMacroData {
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let makro = &item_tree[loc.id.value]; let makro = &item_tree[loc.id.value];
let name = if let Some(def) = item_tree let (name, helpers) = if let Some(def) = item_tree
.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()) .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into())
.parse_proc_macro_decl(&makro.name) .parse_proc_macro_decl(&makro.name)
{ {
def.name (
def.name,
match def.kind {
ProcMacroKind::CustomDerive { helpers } => Some(helpers),
ProcMacroKind::FnLike | ProcMacroKind::Attr => None,
},
)
} else { } else {
// eeeh... // eeeh...
stdx::never!("proc macro declaration is not a proc macro"); stdx::never!("proc macro declaration is not a proc macro");
makro.name.clone() (makro.name.clone(), None)
}; };
Arc::new(ProcMacroData { name }) Arc::new(ProcMacroData { name, helpers })
} }
} }

View File

@ -66,10 +66,14 @@ pub struct ItemScope {
attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>, attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>,
/// The derive macro invocations in this scope, keyed by the owner item over the actual derive attributes /// The derive macro invocations in this scope, keyed by the owner item over the actual derive attributes
/// paired with the derive macro invocations for the specific attribute. /// paired with the derive macro invocations for the specific attribute.
derive_macros: FxHashMap< derive_macros: FxHashMap<AstId<ast::Adt>, SmallVec<[DeriveMacroInvocation; 1]>>,
AstId<ast::Adt>, }
SmallVec<[(AttrId, MacroCallId, SmallVec<[Option<MacroCallId>; 1]>); 1]>,
>, #[derive(Debug, PartialEq, Eq)]
struct DeriveMacroInvocation {
attr_id: AttrId,
attr_call_id: MacroCallId,
derive_call_ids: SmallVec<[Option<MacroCallId>; 1]>,
} }
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
@ -210,12 +214,14 @@ impl ItemScope {
&mut self, &mut self,
adt: AstId<ast::Adt>, adt: AstId<ast::Adt>,
call: MacroCallId, call: MacroCallId,
attr_id: AttrId, id: AttrId,
idx: usize, idx: usize,
) { ) {
if let Some(derives) = self.derive_macros.get_mut(&adt) { if let Some(derives) = self.derive_macros.get_mut(&adt) {
if let Some((.., invocs)) = derives.iter_mut().find(|&&mut (id, ..)| id == attr_id) { if let Some(DeriveMacroInvocation { derive_call_ids, .. }) =
invocs[idx] = Some(call); derives.iter_mut().find(|&&mut DeriveMacroInvocation { attr_id, .. }| id == attr_id)
{
derive_call_ids[idx] = Some(call);
} }
} }
} }
@ -227,10 +233,14 @@ impl ItemScope {
&mut self, &mut self,
adt: AstId<ast::Adt>, adt: AstId<ast::Adt>,
attr_id: AttrId, attr_id: AttrId,
call_id: MacroCallId, attr_call_id: MacroCallId,
len: usize, len: usize,
) { ) {
self.derive_macros.entry(adt).or_default().push((attr_id, call_id, smallvec![None; len])); self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation {
attr_id,
attr_call_id,
derive_call_ids: smallvec![None; len],
});
} }
pub(crate) fn derive_macro_invocs( pub(crate) fn derive_macro_invocs(
@ -242,7 +252,12 @@ impl ItemScope {
), ),
> + '_ { > + '_ {
self.derive_macros.iter().map(|(k, v)| { self.derive_macros.iter().map(|(k, v)| {
(*k, v.iter().map(|&(attr_id, call_id, ref invocs)| (attr_id, call_id, &**invocs))) (
*k,
v.iter().map(|DeriveMacroInvocation { attr_id, attr_call_id, derive_call_ids }| {
(*attr_id, *attr_call_id, &**derive_call_ids)
}),
)
}) })
} }

View File

@ -934,11 +934,11 @@ fn derive_macro_as_call_id(
derive_attr: AttrId, derive_attr: AttrId,
derive_pos: u32, derive_pos: u32,
krate: CrateId, krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>,
) -> Result<MacroCallId, UnresolvedMacro> { ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
let def: MacroDefId = resolver(item_attr.path.clone()) let (macro_id, def_id) = resolver(item_attr.path.clone())
.ok_or_else(|| UnresolvedMacro { path: 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(), db.upcast(),
krate, krate,
MacroCallKind::Derive { MacroCallKind::Derive {
@ -947,7 +947,7 @@ fn derive_macro_as_call_id(
derive_attr_index: derive_attr.ast_index, derive_attr_index: derive_attr.ast_index,
}, },
); );
Ok(res) Ok((macro_id, def_id, call_id))
} }
fn attr_macro_as_call_id( fn attr_macro_as_call_id(

View File

@ -48,19 +48,19 @@
//! the result //! the result
pub mod attr_resolution; pub mod attr_resolution;
mod collector; pub mod proc_macro;
pub mod diagnostics; pub mod diagnostics;
mod collector;
mod mod_resolution; mod mod_resolution;
mod path_resolution; mod path_resolution;
mod proc_macro;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use std::{cmp::Ord, sync::Arc}; use std::{ops::Deref, sync::Arc};
use base_db::{CrateId, Edition, FileId}; use base_db::{CrateId, Edition, FileId};
use hir_expand::{name::Name, InFile, MacroDefId}; use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId};
use itertools::Itertools; use itertools::Itertools;
use la_arena::Arena; use la_arena::Arena;
use profile::Count; use profile::Count;
@ -76,7 +76,7 @@ use crate::{
path::ModPath, path::ModPath,
per_ns::PerNs, per_ns::PerNs,
visibility::Visibility, visibility::Visibility,
AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, ModuleId, ProcMacroId, AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, MacroId, ModuleId, ProcMacroId,
}; };
/// Contains the results of (early) name resolution. /// Contains the results of (early) name resolution.
@ -106,6 +106,9 @@ pub struct DefMap {
fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>, fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
/// The error that occurred when failing to load the proc-macro dll. /// The error that occurred when failing to load the proc-macro dll.
proc_macro_loading_error: Option<Box<str>>, 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, MacroId, MacroCallId)>>,
/// Custom attributes registered with `#![register_attr]`. /// Custom attributes registered with `#![register_attr]`.
registered_attrs: Vec<SmolStr>, registered_attrs: Vec<SmolStr>,
@ -275,6 +278,7 @@ impl DefMap {
exported_derives: FxHashMap::default(), exported_derives: FxHashMap::default(),
fn_proc_macro_mapping: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(),
proc_macro_loading_error: None, proc_macro_loading_error: None,
derive_helpers_in_scope: FxHashMap::default(),
prelude: None, prelude: None,
root, root,
modules, modules,
@ -294,12 +298,22 @@ impl DefMap {
pub fn modules(&self) -> impl Iterator<Item = (LocalModuleId, &ModuleData)> + '_ { pub fn modules(&self) -> impl Iterator<Item = (LocalModuleId, &ModuleData)> + '_ {
self.modules.iter() self.modules.iter()
} }
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)
}
pub fn registered_tools(&self) -> &[SmolStr] { pub fn registered_tools(&self) -> &[SmolStr] {
&self.registered_tools &self.registered_tools
} }
pub fn registered_attrs(&self) -> &[SmolStr] { pub fn registered_attrs(&self) -> &[SmolStr] {
&self.registered_attrs &self.registered_attrs
} }
pub fn root(&self) -> LocalModuleId { pub fn root(&self) -> LocalModuleId {
self.root self.root
} }
@ -307,6 +321,7 @@ impl DefMap {
pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> { pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> {
self.fn_proc_macro_mapping.get(&id).copied() self.fn_proc_macro_mapping.get(&id).copied()
} }
pub fn proc_macro_loading_error(&self) -> Option<&str> { pub fn proc_macro_loading_error(&self) -> Option<&str> {
self.proc_macro_loading_error.as_deref() self.proc_macro_loading_error.as_deref()
} }
@ -467,6 +482,7 @@ impl DefMap {
registered_attrs, registered_attrs,
registered_tools, registered_tools,
fn_proc_macro_mapping, fn_proc_macro_mapping,
derive_helpers_in_scope,
proc_macro_loading_error: _, proc_macro_loading_error: _,
block: _, block: _,
edition: _, edition: _,
@ -483,6 +499,7 @@ impl DefMap {
registered_attrs.shrink_to_fit(); registered_attrs.shrink_to_fit();
registered_tools.shrink_to_fit(); registered_tools.shrink_to_fit();
fn_proc_macro_mapping.shrink_to_fit(); fn_proc_macro_mapping.shrink_to_fit();
derive_helpers_in_scope.shrink_to_fit();
for (_, module) in modules.iter_mut() { for (_, module) in modules.iter_mut() {
module.children.shrink_to_fit(); module.children.shrink_to_fit();
module.scope.shrink_to_fit(); module.scope.shrink_to_fit();

View File

@ -18,7 +18,7 @@ use hir_expand::{
ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
MacroDefKind, MacroDefKind,
}; };
use itertools::Itertools; use itertools::{izip, Itertools};
use la_arena::Idx; use la_arena::Idx;
use limit::Limit; use limit::Limit;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
@ -110,7 +110,6 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
proc_macros, proc_macros,
from_glob_import: Default::default(), from_glob_import: Default::default(),
skip_attrs: Default::default(), skip_attrs: Default::default(),
derive_helpers_in_scope: Default::default(),
is_proc_macro, is_proc_macro,
}; };
if tree_id.is_block() { if tree_id.is_block() {
@ -258,9 +257,6 @@ struct DefCollector<'a> {
/// This also stores the attributes to skip when we resolve derive helpers and non-macro /// This also stores the attributes to skip when we resolve derive helpers and non-macro
/// non-builtin attributes in general. /// non-builtin attributes in general.
skip_attrs: FxHashMap<InFile<ModItem>, AttrId>, skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
/// 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>>,
} }
impl DefCollector<'_> { impl DefCollector<'_> {
@ -1059,7 +1055,7 @@ impl DefCollector<'_> {
}; };
let mut res = ReachedFixedPoint::Yes; let mut res = ReachedFixedPoint::Yes;
macros.retain(|directive| { macros.retain(|directive| {
let resolver = |path| { let resolver2 = |path| {
let resolved_res = self.def_map.resolve_path_fp_with_macro( let resolved_res = self.def_map.resolve_path_fp_with_macro(
self.db, self.db,
ResolveMode::Other, ResolveMode::Other,
@ -1067,8 +1063,12 @@ impl DefCollector<'_> {
&path, &path,
BuiltinShadowMode::Module, 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 { match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to } => { MacroDirectiveKind::FnLike { ast_id, expand_to } => {
@ -1087,21 +1087,37 @@ impl DefCollector<'_> {
} }
} }
MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos } => { 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, self.db,
ast_id, ast_id,
*derive_attr, *derive_attr,
*derive_pos as u32, *derive_pos as u32,
self.def_map.krate, 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( self.def_map.modules[directive.module_id].scope.set_derive_macro_invoc(
ast_id.ast_id, ast_id.ast_id,
call_id, call_id,
*derive_attr, *derive_attr,
*derive_pos, *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); push_resolved(directive, call_id);
res = ReachedFixedPoint::No; res = ReachedFixedPoint::No;
@ -1132,8 +1148,8 @@ impl DefCollector<'_> {
}; };
if let Some(ident) = path.as_ident() { if let Some(ident) = path.as_ident() {
if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id) { if let Some(helpers) = self.def_map.derive_helpers_in_scope.get(&ast_id) {
if helpers.contains(ident) { if helpers.iter().any(|(it, ..)| it == ident) {
cov_mark::hit!(resolved_derive_helper); cov_mark::hit!(resolved_derive_helper);
// Resolved to derive helper. Collect the item's attributes again, // Resolved to derive helper. Collect the item's attributes again,
// starting after the derive helper. // starting after the derive helper.
@ -1148,7 +1164,7 @@ impl DefCollector<'_> {
}; };
if matches!( if matches!(
def, def,
MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. } MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. }
if expander.is_derive() if expander.is_derive()
) { ) {
// Resolved to `#[derive]` // Resolved to `#[derive]`
@ -1317,19 +1333,6 @@ impl DefCollector<'_> {
self.def_map.diagnostics.push(diag); 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.derive_helpers_in_scope
.entry(ast_id.map(|it| it.upcast()))
.or_default()
.extend(helpers.iter().cloned());
}
}
}
// Then, fetch and process the item tree. This will reuse the expansion result from above. // 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 item_tree = self.db.file_item_tree(file_id);
let mod_dir = self.mod_dirs[&module_id].clone(); let mod_dir = self.mod_dirs[&module_id].clone();
@ -2140,7 +2143,6 @@ mod tests {
proc_macros: Default::default(), proc_macros: Default::default(),
from_glob_import: Default::default(), from_glob_import: Default::default(),
skip_attrs: Default::default(), skip_attrs: Default::default(),
derive_helpers_in_scope: Default::default(),
is_proc_macro: false, is_proc_macro: false,
}; };
collector.seed_with_top_level(); collector.seed_with_top_level();

View File

@ -149,6 +149,7 @@ impl Resolver {
self.resolve_module_path(db, path, BuiltinShadowMode::Module) self.resolve_module_path(db, path, BuiltinShadowMode::Module)
} }
// FIXME: This shouldn't exist
pub fn resolve_module_path_in_trait_assoc_items( pub fn resolve_module_path_in_trait_assoc_items(
&self, &self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
@ -448,10 +449,14 @@ impl Resolver {
} }
pub fn krate(&self) -> CrateId { pub fn krate(&self) -> CrateId {
self.def_map().krate()
}
pub fn def_map(&self) -> &DefMap {
self.scopes self.scopes
.get(0) .get(0)
.and_then(|scope| match scope { .and_then(|scope| match scope {
Scope::ModuleScope(m) => Some(m.def_map.krate()), Scope::ModuleScope(m) => Some(&m.def_map),
_ => None, _ => None,
}) })
.expect("module scope invariant violated") .expect("module scope invariant violated")

View File

@ -2252,6 +2252,32 @@ impl Local {
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DeriveHelper {
pub(crate) derive: MacroId,
pub(crate) idx: usize,
}
impl DeriveHelper {
pub fn derive(&self) -> Macro {
Macro { id: self.derive.into() }
}
pub fn name(&self, db: &dyn HirDatabase) -> Name {
match self.derive {
MacroId::Macro2Id(_) => None,
MacroId::MacroRulesId(_) => None,
MacroId::ProcMacroId(proc_macro) => db
.proc_macro_data(proc_macro)
.helpers
.as_ref()
.and_then(|it| it.get(self.idx))
.cloned(),
}
.unwrap_or_else(|| Name::missing())
}
}
// FIXME: Wrong name? This is could also be a registered attribute // FIXME: Wrong name? This is could also be a registered attribute
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BuiltinAttr { pub struct BuiltinAttr {

View File

@ -29,9 +29,9 @@ use crate::{
db::HirDatabase, db::HirDatabase,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{resolve_hir_path, SourceAnalyzer}, source_analyzer::{resolve_hir_path, SourceAnalyzer},
Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource, Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function,
HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, Path, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef,
ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef, Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
}; };
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -47,6 +47,7 @@ pub enum PathResolution {
SelfType(Impl), SelfType(Impl),
BuiltinAttr(BuiltinAttr), BuiltinAttr(BuiltinAttr),
ToolModule(ToolModule), ToolModule(ToolModule),
DeriveHelper(DeriveHelper),
} }
impl PathResolution { impl PathResolution {
@ -71,6 +72,7 @@ impl PathResolution {
PathResolution::BuiltinAttr(_) PathResolution::BuiltinAttr(_)
| PathResolution::ToolModule(_) | PathResolution::ToolModule(_)
| PathResolution::Local(_) | PathResolution::Local(_)
| PathResolution::DeriveHelper(_)
| PathResolution::ConstParam(_) => None, | PathResolution::ConstParam(_) => None,
PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
@ -733,6 +735,8 @@ impl<'db> SemanticsImpl<'db> {
Some(it) => it, Some(it) => it,
None => return, None => return,
}; };
let def_map = sa.resolver.def_map();
let mut stack: SmallVec<[_; 4]> = smallvec![InFile::new(sa.file_id, token)]; let mut stack: SmallVec<[_; 4]> = smallvec![InFile::new(sa.file_id, token)];
let mut cache = self.expansion_info_cache.borrow_mut(); let mut cache = self.expansion_info_cache.borrow_mut();
let mut mcache = self.macro_call_cache.borrow_mut(); let mut mcache = self.macro_call_cache.borrow_mut();
@ -764,7 +768,7 @@ impl<'db> SemanticsImpl<'db> {
while let Some(token) = stack.pop() { while let Some(token) = stack.pop() {
self.db.unwind_if_cancelled(); self.db.unwind_if_cancelled();
let was_not_remapped = (|| { let was_not_remapped = (|| {
// are we inside an attribute macro call // First expand into attribute invocations
let containing_attribute_macro_call = self.with_ctx(|ctx| { let containing_attribute_macro_call = self.with_ctx(|ctx| {
token.value.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| { token.value.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
if item.attrs().next().is_none() { if item.attrs().next().is_none() {
@ -784,53 +788,19 @@ impl<'db> SemanticsImpl<'db> {
); );
} }
// or are we inside a function-like macro call // Then check for token trees, that means we are either in a function-like macro or
if let Some(tt) = // secondary attribute inputs
// FIXME replace map.while_some with take_while once stable let tt = token.value.parent_ancestors().map_while(ast::TokenTree::cast).last()?;
token let parent = tt.syntax().parent()?;
.value
.parent_ancestors()
.map(ast::TokenTree::cast)
.while_some()
.last()
{
let parent = tt.syntax().parent()?;
// check for derive attribute here
let macro_call = match_ast! {
match parent {
ast::MacroCall(mcall) => mcall,
// attribute we failed expansion for earlier, this might be a derive invocation
// so try downmapping the token into the pseudo derive expansion
// see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
ast::Meta(meta) => {
let attr = meta.parent_attr()?;
let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
let call_id = self.with_ctx(|ctx| {
let (_, call_id, _) = ctx.attr_to_derive_macro_call(
token.with_value(&adt),
token.with_value(attr),
)?;
Some(call_id)
})?;
let file_id = call_id.as_file();
return process_expansion_for_token(
&mut stack,
file_id,
Some(adt.into()),
token.as_ref(),
);
},
_ => return None,
}
};
if tt.left_delimiter_token().map_or(false, |it| it == token.value) { if tt.left_delimiter_token().map_or(false, |it| it == token.value) {
return None; return None;
} }
if tt.right_delimiter_token().map_or(false, |it| it == token.value) { if tt.right_delimiter_token().map_or(false, |it| it == token.value) {
return None; return None;
} }
if let Some(macro_call) = ast::MacroCall::cast(parent.clone()) {
let mcall = token.with_value(macro_call); let mcall = token.with_value(macro_call);
let file_id = match mcache.get(&mcall) { let file_id = match mcache.get(&mcall) {
Some(&it) => it, Some(&it) => it,
@ -840,11 +810,77 @@ impl<'db> SemanticsImpl<'db> {
it it
} }
}; };
return process_expansion_for_token(&mut stack, file_id, None, token.as_ref()); process_expansion_for_token(&mut stack, file_id, None, token.as_ref())
} } else if let Some(meta) = ast::Meta::cast(parent.clone()) {
// attribute we failed expansion for earlier, this might be a derive invocation
// or derive helper attribute
let attr = meta.parent_attr()?;
// outside of a macro invocation so this is a "final" token let adt = if let Some(adt) = attr.syntax().parent().and_then(ast::Adt::cast) {
None // this might be a derive, or a derive helper on an ADT
let derive_call = self.with_ctx(|ctx| {
// so try downmapping the token into the pseudo derive expansion
// see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
ctx.attr_to_derive_macro_call(
token.with_value(&adt),
token.with_value(attr.clone()),
)
.map(|(_, call_id, _)| call_id)
});
match derive_call {
Some(call_id) => {
// resolved to a derive
let file_id = call_id.as_file();
return process_expansion_for_token(
&mut stack,
file_id,
Some(adt.into()),
token.as_ref(),
);
}
None => Some(adt),
}
} else {
// Otherwise this could be a derive helper on a variant or field
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 !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();
let id = self.db.ast_id_map(token.file_id).ast_id(&adt);
let helpers =
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) {
res = res.or(process_expansion_for_token(
&mut stack,
derive.as_file(),
item.clone(),
token.as_ref(),
));
}
res
} else {
None
}
})() })()
.is_none(); .is_none();

View File

@ -247,6 +247,7 @@ impl SourceToDefCtx<'_, '_> {
map[keys::ATTR_MACRO_CALL].get(&src.value).copied() map[keys::ATTR_MACRO_CALL].get(&src.value).copied()
} }
/// (AttrId, derive attribute call id, derive call ids)
pub(super) fn attr_to_derive_macro_call( pub(super) fn attr_to_derive_macro_call(
&mut self, &mut self,
item: InFile<&ast::Adt>, item: InFile<&ast::Adt>,
@ -257,6 +258,7 @@ impl SourceToDefCtx<'_, '_> {
.get(&src.value) .get(&src.value)
.map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids)) .map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
} }
pub(super) fn has_derives(&mut self, adt: InFile<&ast::Adt>) -> bool { pub(super) fn has_derives(&mut self, adt: InFile<&ast::Adt>) -> bool {
self.dyn_map(adt).as_ref().map_or(false, |map| !map[keys::DERIVE_MACRO_CALL].is_empty()) self.dyn_map(adt).as_ref().map_or(false, |map| !map[keys::DERIVE_MACRO_CALL].is_empty())
} }

View File

@ -35,6 +35,7 @@ use hir_ty::{
method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution,
TyExt, TyKind, TyLoweringContext, TyExt, TyKind, TyLoweringContext,
}; };
use itertools::Itertools;
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::{ use syntax::{
ast::{self, AstNode}, ast::{self, AstNode},
@ -43,8 +44,8 @@ use syntax::{
use crate::{ use crate::{
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr, db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
BuiltinType, Callable, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct, BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static,
ToolModule, Trait, Type, TypeAlias, Variant, Struct, ToolModule, Trait, Type, TypeAlias, Variant,
}; };
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@ -429,19 +430,21 @@ impl SourceAnalyzer {
} }
} }
let is_path_of_attr = path let meta_path = path
.syntax() .syntax()
.ancestors() .ancestors()
.map(|it| it.kind()) .take_while(|it| {
.take_while(|&kind| ast::Path::can_cast(kind) || ast::Meta::can_cast(kind)) let kind = it.kind();
ast::Path::can_cast(kind) || ast::Meta::can_cast(kind)
})
.last() .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 // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are
// trying to resolve foo::bar. // trying to resolve foo::bar.
if path.parent_path().is_some() { if path.parent_path().is_some() {
return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path) { 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| { path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| {
ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
.map(PathResolution::ToolModule) .map(PathResolution::ToolModule)
@ -449,16 +452,56 @@ impl SourceAnalyzer {
} }
res => res, 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 // 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 // in this case we have to check for inert/builtin attributes and tools and prioritize
// resolution of attributes over other namespaces // resolution of attributes over other namespaces
let name_ref = path.as_single_name_ref(); if let Some(name_ref) = path.as_single_name_ref() {
let builtin = name_ref.as_ref().and_then(|name_ref| { let builtin =
BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text()) BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text());
}); if let Some(_) = builtin {
if let Some(_) = builtin { return builtin.map(PathResolution::BuiltinAttr);
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();
for (macro_id, mut helpers) in
helpers.iter().group_by(|(_, macro_id, ..)| macro_id).into_iter()
{
if let Some(idx) = helpers.position(|(name, ..)| *name == name_ref)
{
return Some(PathResolution::DeriveHelper(DeriveHelper {
derive: *macro_id,
idx,
}));
}
}
}
}
}
} }
return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) { return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) {
Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))), Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))),

View File

@ -292,6 +292,7 @@ impl CompletionItemKind {
SymbolKind::Const => "ct", SymbolKind::Const => "ct",
SymbolKind::ConstParam => "cp", SymbolKind::ConstParam => "cp",
SymbolKind::Derive => "de", SymbolKind::Derive => "de",
SymbolKind::DeriveHelper => "dh",
SymbolKind::Enum => "en", SymbolKind::Enum => "en",
SymbolKind::Field => "fd", SymbolKind::Field => "fd",
SymbolKind::Function => "fn", SymbolKind::Function => "fn",

View File

@ -7,9 +7,9 @@
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use hir::{ use hir::{
Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, Field, Function, Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field,
GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef, Name, Function, GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef,
PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility, Name, PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility,
}; };
use stdx::impl_from; use stdx::impl_from;
use syntax::{ use syntax::{
@ -37,6 +37,7 @@ pub enum Definition {
Local(Local), Local(Local),
GenericParam(GenericParam), GenericParam(GenericParam),
Label(Label), Label(Label),
DeriveHelper(DeriveHelper),
BuiltinAttr(BuiltinAttr), BuiltinAttr(BuiltinAttr),
ToolModule(ToolModule), ToolModule(ToolModule),
} }
@ -69,6 +70,7 @@ impl Definition {
Definition::Local(it) => it.module(db), Definition::Local(it) => it.module(db),
Definition::GenericParam(it) => it.module(db), Definition::GenericParam(it) => it.module(db),
Definition::Label(it) => it.module(db), Definition::Label(it) => it.module(db),
Definition::DeriveHelper(it) => it.derive().module(db),
Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => { Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => {
return None return None
} }
@ -94,7 +96,8 @@ impl Definition {
| Definition::SelfType(_) | Definition::SelfType(_)
| Definition::Local(_) | Definition::Local(_)
| Definition::GenericParam(_) | Definition::GenericParam(_)
| Definition::Label(_) => return None, | Definition::Label(_)
| Definition::DeriveHelper(_) => return None,
}; };
Some(vis) Some(vis)
} }
@ -118,6 +121,7 @@ impl Definition {
Definition::Label(it) => it.name(db), Definition::Label(it) => it.name(db),
Definition::BuiltinAttr(_) => return None, // FIXME Definition::BuiltinAttr(_) => return None, // FIXME
Definition::ToolModule(_) => return None, // FIXME Definition::ToolModule(_) => return None, // FIXME
Definition::DeriveHelper(it) => it.name(db),
}; };
Some(name) Some(name)
} }
@ -500,6 +504,7 @@ impl From<PathResolution> for Definition {
PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr), PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr),
PathResolution::ToolModule(tool) => Definition::ToolModule(tool), PathResolution::ToolModule(tool) => Definition::ToolModule(tool),
PathResolution::DeriveHelper(helper) => Definition::DeriveHelper(helper),
} }
} }
} }

View File

@ -173,6 +173,7 @@ pub enum SymbolKind {
Const, Const,
ConstParam, ConstParam,
Derive, Derive,
DeriveHelper,
Enum, Enum,
Field, Field,
Function, Function,

View File

@ -224,7 +224,8 @@ impl<'a> Ctx<'a> {
| hir::PathResolution::SelfType(_) | hir::PathResolution::SelfType(_)
| hir::PathResolution::Def(_) | hir::PathResolution::Def(_)
| hir::PathResolution::BuiltinAttr(_) | hir::PathResolution::BuiltinAttr(_)
| hir::PathResolution::ToolModule(_) => (), | hir::PathResolution::ToolModule(_)
| hir::PathResolution::DeriveHelper(_) => (),
} }
Some(()) Some(())
} }

View File

@ -156,6 +156,8 @@ impl Definition {
Definition::SelfType(_) => return None, Definition::SelfType(_) => return None,
Definition::BuiltinAttr(_) => return None, Definition::BuiltinAttr(_) => return None,
Definition::ToolModule(_) => return None, Definition::ToolModule(_) => return None,
// FIXME: This should be doable in theory
Definition::DeriveHelper(_) => return None,
}; };
return res; return res;
@ -316,14 +318,20 @@ pub fn source_edit_from_references(
// macros can cause multiple refs to occur for the same text range, so keep track of what we have edited so far // macros can cause multiple refs to occur for the same text range, so keep track of what we have edited so far
let mut edited_ranges = Vec::new(); let mut edited_ranges = Vec::new();
for &FileReference { range, ref name, .. } in references { for &FileReference { range, ref name, .. } in references {
let name_range = name.syntax().text_range();
if name_range.len() != range.len() {
// This usage comes from a different token kind that was downmapped to a NameLike in a macro
// Renaming this will most likely break things syntax-wise
continue;
}
let has_emitted_edit = match name { let has_emitted_edit = match name {
// if the ranges differ then the node is inside a macro call, we can't really attempt // if the ranges differ then the node is inside a macro call, we can't really attempt
// to make special rewrites like shorthand syntax and such, so just rename the node in // to make special rewrites like shorthand syntax and such, so just rename the node in
// the macro input // the macro input
ast::NameLike::NameRef(name_ref) if name_ref.syntax().text_range() == range => { ast::NameLike::NameRef(name_ref) if name_range == range => {
source_edit_from_name_ref(&mut edit, name_ref, new_name, def) source_edit_from_name_ref(&mut edit, name_ref, new_name, def)
} }
ast::NameLike::Name(name) if name.syntax().text_range() == range => { ast::NameLike::Name(name) if name_range == range => {
source_edit_from_name(&mut edit, name, new_name) source_edit_from_name(&mut edit, name, new_name)
} }
_ => false, _ => false,

View File

@ -54,7 +54,9 @@ impl IntoIterator for UsageSearchResult {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FileReference { pub struct FileReference {
/// The range of the reference in the original file
pub range: TextRange, pub range: TextRange,
/// The node of the reference in the (macro-)file
pub name: ast::NameLike, pub name: ast::NameLike,
pub category: Option<ReferenceCategory>, pub category: Option<ReferenceCategory>,
} }
@ -276,16 +278,16 @@ impl Definition {
} }
} }
hir::MacroKind::BuiltIn => SearchScope::crate_graph(db), hir::MacroKind::BuiltIn => SearchScope::crate_graph(db),
// FIXME: We don't actually see derives in derive attributes as these do not
// expand to something that references the derive macro in the output.
// We could get around this by doing pseudo expansions for proc_macro_derive like we
// do for the derive attribute
hir::MacroKind::Derive | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => { hir::MacroKind::Derive | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => {
SearchScope::reverse_dependencies(db, module.krate()) SearchScope::reverse_dependencies(db, module.krate())
} }
}; };
} }
if let Definition::DeriveHelper(_) = self {
return SearchScope::reverse_dependencies(db, module.krate());
}
let vis = self.visibility(db); let vis = self.visibility(db);
if let Some(Visibility::Public) = vis { if let Some(Visibility::Public) = vis {
return SearchScope::reverse_dependencies(db, module.krate()); return SearchScope::reverse_dependencies(db, module.krate());

View File

@ -190,7 +190,8 @@ pub(crate) fn resolve_doc_path_for_def(
| Definition::SelfType(_) | Definition::SelfType(_)
| Definition::Local(_) | Definition::Local(_)
| Definition::GenericParam(_) | Definition::GenericParam(_)
| Definition::Label(_) => None, | Definition::Label(_)
| Definition::DeriveHelper(_) => None,
} }
.map(Definition::from) .map(Definition::from)
} }
@ -515,7 +516,8 @@ fn filename_and_frag_for_def(
| Definition::GenericParam(_) | Definition::GenericParam(_)
| Definition::Label(_) | Definition::Label(_)
| Definition::BuiltinAttr(_) | Definition::BuiltinAttr(_)
| Definition::ToolModule(_) => return None, | Definition::ToolModule(_)
| Definition::DeriveHelper(_) => return None,
}; };
Some((def, res, None)) Some((def, res, None))

View File

@ -115,7 +115,12 @@ pub(crate) fn hover(
}); });
} }
let descended = sema.descend_into_macros_with_same_text(original_token.clone()); let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind())));
let descended = if in_attr {
[sema.descend_into_macros_with_kind_preference(original_token.clone())].into()
} else {
sema.descend_into_macros_with_same_text(original_token.clone())
};
// FIXME: Definition should include known lints and the like instead of having this special case here // FIXME: Definition should include known lints and the like instead of having this special case here
let hovered_lint = descended.iter().find_map(|token| { let hovered_lint = descended.iter().find_map(|token| {

View File

@ -370,6 +370,7 @@ pub(super) fn definition(
// FIXME: We should be able to show more info about these // FIXME: We should be able to show more info about these
Definition::BuiltinAttr(it) => return render_builtin_attr(db, it), Definition::BuiltinAttr(it) => return render_builtin_attr(db, it),
Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))), Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))),
Definition::DeriveHelper(it) => (format!("derive_helper {}", it.name(db)), None),
}; };
let docs = match config.documentation { let docs = match config.documentation {

View File

@ -196,6 +196,8 @@ impl TryToNav for Definition {
Definition::BuiltinType(_) => None, Definition::BuiltinType(_) => None,
Definition::ToolModule(_) => None, Definition::ToolModule(_) => None,
Definition::BuiltinAttr(_) => None, Definition::BuiltinAttr(_) => None,
// FIXME: The focus range should be set to the helper declaration
Definition::DeriveHelper(it) => it.derive().try_to_nav(db),
} }
} }
} }

View File

@ -237,7 +237,8 @@ fn signature_help_for_generics(
| hir::PathResolution::Local(_) | hir::PathResolution::Local(_)
| hir::PathResolution::TypeParam(_) | hir::PathResolution::TypeParam(_)
| hir::PathResolution::ConstParam(_) | hir::PathResolution::ConstParam(_)
| hir::PathResolution::SelfType(_) => return None, | hir::PathResolution::SelfType(_)
| hir::PathResolution::DeriveHelper(_) => return None,
}; };
generic_def generic_def

View File

@ -107,6 +107,7 @@ pub struct HlRange {
// builtinType:: Emitted for builtin types like `u32`, `str` and `f32`. // builtinType:: Emitted for builtin types like `u32`, `str` and `f32`.
// comment:: Emitted for comments. // comment:: Emitted for comments.
// constParameter:: Emitted for const parameters. // constParameter:: Emitted for const parameters.
// deriveHelper:: Emitted for derive helper attributes.
// enumMember:: Emitted for enum variants. // enumMember:: Emitted for enum variants.
// generic:: Emitted for generic tokens that have no mapping. // generic:: Emitted for generic tokens that have no mapping.
// keyword:: Emitted for keywords. // keyword:: Emitted for keywords.
@ -431,6 +432,13 @@ fn traverse(
// let the editor do its highlighting for these tokens instead // let the editor do its highlighting for these tokens instead
continue; continue;
} }
if highlight.tag == HlTag::UnresolvedReference
&& matches!(attr_or_derive_item, Some(AttrOrDerive::Derive(_)) if inside_attribute)
{
// do not emit unresolved references in derive helpers if the token mapping maps to
// something unresolvable. FIXME: There should be a way to prevent that
continue;
}
if inside_attribute { if inside_attribute {
highlight |= HlMod::Attribute highlight |= HlMod::Attribute
} }

View File

@ -472,6 +472,7 @@ fn highlight_def(
Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)), Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)),
Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)), Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)),
Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)), Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)),
Definition::DeriveHelper(_) => Highlight::new(HlTag::Symbol(SymbolKind::DeriveHelper)),
}; };
let def_crate = def.krate(db); let def_crate = def.krate(db);

View File

@ -270,6 +270,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag {
Definition::Label(_) => SymbolKind::Label, Definition::Label(_) => SymbolKind::Label,
Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr, Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr,
Definition::ToolModule(_) => SymbolKind::ToolModule, Definition::ToolModule(_) => SymbolKind::ToolModule,
Definition::DeriveHelper(_) => SymbolKind::DeriveHelper,
}; };
HlTag::Symbol(symbol) HlTag::Symbol(symbol)
} }

View File

@ -134,6 +134,7 @@ impl HlTag {
SymbolKind::Const => "constant", SymbolKind::Const => "constant",
SymbolKind::ConstParam => "const_param", SymbolKind::ConstParam => "const_param",
SymbolKind::Derive => "derive", SymbolKind::Derive => "derive",
SymbolKind::DeriveHelper => "derive_helper",
SymbolKind::Enum => "enum", SymbolKind::Enum => "enum",
SymbolKind::Field => "field", SymbolKind::Field => "field",
SymbolKind::Function => "function", SymbolKind::Function => "function",

View File

@ -54,6 +54,7 @@ define_semantic_token_types![
(COMPARISON, "comparison"), (COMPARISON, "comparison"),
(CONST_PARAMETER, "constParameter"), (CONST_PARAMETER, "constParameter"),
(DERIVE, "derive"), (DERIVE, "derive"),
(DERIVE_HELPER, "deriveHelper"),
(DOT, "dot"), (DOT, "dot"),
(ESCAPE_SEQUENCE, "escapeSequence"), (ESCAPE_SEQUENCE, "escapeSequence"),
(FORMAT_SPECIFIER, "formatSpecifier"), (FORMAT_SPECIFIER, "formatSpecifier"),

View File

@ -53,7 +53,8 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
SymbolKind::Macro SymbolKind::Macro
| SymbolKind::BuiltinAttr | SymbolKind::BuiltinAttr
| SymbolKind::Attribute | 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::Module | SymbolKind::ToolModule => lsp_types::SymbolKind::MODULE,
SymbolKind::TypeAlias | SymbolKind::TypeParam | SymbolKind::SelfType => { SymbolKind::TypeAlias | SymbolKind::TypeParam | SymbolKind::SelfType => {
lsp_types::SymbolKind::TYPE_PARAMETER lsp_types::SymbolKind::TYPE_PARAMETER
@ -117,6 +118,7 @@ pub(crate) fn completion_item_kind(
SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT, SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT,
SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER, SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION, SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION,
SymbolKind::DeriveHelper => lsp_types::CompletionItemKind::FUNCTION,
SymbolKind::Enum => lsp_types::CompletionItemKind::ENUM, SymbolKind::Enum => lsp_types::CompletionItemKind::ENUM,
SymbolKind::Field => lsp_types::CompletionItemKind::FIELD, SymbolKind::Field => lsp_types::CompletionItemKind::FIELD,
SymbolKind::Function => lsp_types::CompletionItemKind::FUNCTION, SymbolKind::Function => lsp_types::CompletionItemKind::FUNCTION,
@ -561,6 +563,7 @@ fn semantic_token_type_and_modifiers(
HlTag::Symbol(symbol) => match symbol { HlTag::Symbol(symbol) => match symbol {
SymbolKind::Attribute => semantic_tokens::ATTRIBUTE, SymbolKind::Attribute => semantic_tokens::ATTRIBUTE,
SymbolKind::Derive => semantic_tokens::DERIVE, SymbolKind::Derive => semantic_tokens::DERIVE,
SymbolKind::DeriveHelper => semantic_tokens::DERIVE_HELPER,
SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE, SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE,
SymbolKind::Impl => semantic_tokens::TYPE_ALIAS, SymbolKind::Impl => semantic_tokens::TYPE_ALIAS,
SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,