mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 07:04:49 +00:00
Merge commit 'e36a20c24f
' into ra-sync-and-pms-component
This commit is contained in:
parent
dfe84494c1
commit
a1f1b95d00
48 changed files with 627 additions and 213 deletions
|
@ -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
|
||||
#[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())),
|
||||
|
@ -733,6 +735,8 @@ impl<'db> SemanticsImpl<'db> {
|
|||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
let def_map = sa.resolver.def_map();
|
||||
|
||||
let mut stack: SmallVec<[_; 4]> = smallvec![InFile::new(sa.file_id, token)];
|
||||
let mut cache = self.expansion_info_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() {
|
||||
self.db.unwind_if_cancelled();
|
||||
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| {
|
||||
token.value.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
|
||||
if item.attrs().next().is_none() {
|
||||
|
@ -784,53 +788,19 @@ impl<'db> SemanticsImpl<'db> {
|
|||
);
|
||||
}
|
||||
|
||||
// or are we inside a function-like macro call
|
||||
if let Some(tt) =
|
||||
// FIXME replace map.while_some with take_while once stable
|
||||
token
|
||||
.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,
|
||||
}
|
||||
};
|
||||
// Then check for token trees, that means we are either in a function-like macro or
|
||||
// secondary attribute inputs
|
||||
let tt = token.value.parent_ancestors().map_while(ast::TokenTree::cast).last()?;
|
||||
let parent = tt.syntax().parent()?;
|
||||
|
||||
if tt.left_delimiter_token().map_or(false, |it| it == token.value) {
|
||||
return None;
|
||||
}
|
||||
if tt.right_delimiter_token().map_or(false, |it| it == token.value) {
|
||||
return None;
|
||||
}
|
||||
if tt.left_delimiter_token().map_or(false, |it| it == token.value) {
|
||||
return None;
|
||||
}
|
||||
if tt.right_delimiter_token().map_or(false, |it| it == token.value) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(macro_call) = ast::MacroCall::cast(parent.clone()) {
|
||||
let mcall = token.with_value(macro_call);
|
||||
let file_id = match mcache.get(&mcall) {
|
||||
Some(&it) => it,
|
||||
|
@ -840,11 +810,77 @@ impl<'db> SemanticsImpl<'db> {
|
|||
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
|
||||
None
|
||||
let adt = if let Some(adt) = attr.syntax().parent().and_then(ast::Adt::cast) {
|
||||
// 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();
|
||||
|
||||
|
|
|
@ -247,6 +247,7 @@ impl SourceToDefCtx<'_, '_> {
|
|||
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(
|
||||
&mut self,
|
||||
item: InFile<&ast::Adt>,
|
||||
|
@ -257,6 +258,7 @@ impl SourceToDefCtx<'_, '_> {
|
|||
.get(&src.value)
|
||||
.map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ use hir_ty::{
|
|||
method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution,
|
||||
TyExt, TyKind, TyLoweringContext,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use smallvec::SmallVec;
|
||||
use syntax::{
|
||||
ast::{self, AstNode},
|
||||
|
@ -43,8 +44,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 +430,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 +452,56 @@ 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();
|
||||
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) {
|
||||
Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue