Record derive helper attributes, resolve them in IDE layer

This commit is contained in:
Lukas Wirth 2022-07-24 14:05:37 +02:00
parent 4e60db2d07
commit aa1491ecde
22 changed files with 144 additions and 58 deletions

View file

@ -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 {

View file

@ -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(),

View file

@ -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))),