mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 05:15:04 +00:00
Record derive helper attributes, resolve them in IDE layer
This commit is contained in:
parent
4e60db2d07
commit
aa1491ecde
22 changed files with 144 additions and 58 deletions
|
@ -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 {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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))),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue