mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-06 12:18:33 +00:00
internal: Expand the derive attribute into a pseudo expansion
This commit is contained in:
parent
1fe3b2edd6
commit
7b89d5ede2
@ -5,7 +5,6 @@ mod source_to_def;
|
|||||||
use std::{cell::RefCell, fmt, iter};
|
use std::{cell::RefCell, fmt, iter};
|
||||||
|
|
||||||
use base_db::{FileId, FileRange};
|
use base_db::{FileId, FileRange};
|
||||||
use either::Either;
|
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body,
|
body,
|
||||||
resolver::{self, HasResolver, Resolver, TypeNs},
|
resolver::{self, HasResolver, Resolver, TypeNs},
|
||||||
@ -19,17 +18,16 @@ use smallvec::{smallvec, SmallVec};
|
|||||||
use syntax::{
|
use syntax::{
|
||||||
algo::skip_trivia_token,
|
algo::skip_trivia_token,
|
||||||
ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody},
|
ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody},
|
||||||
match_ast, AstNode, AstToken, Direction, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
|
match_ast, AstNode, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize,
|
||||||
TextSize, T,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
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, AssocItem, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasAttrs as _,
|
Access, AssocItem, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource,
|
||||||
HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef,
|
HirFileId, Impl, InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path,
|
||||||
Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
|
ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@ -350,14 +348,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
|||||||
self.imp.resolve_bind_pat_to_const(pat)
|
self.imp.resolve_bind_pat_to_const(pat)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_derive_ident(
|
|
||||||
&self,
|
|
||||||
derive: &ast::Attr,
|
|
||||||
ident: &ast::Ident,
|
|
||||||
) -> Option<PathResolution> {
|
|
||||||
self.imp.resolve_derive_ident(derive, ident)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
|
pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
|
||||||
self.imp.record_literal_missing_fields(literal)
|
self.imp.record_literal_missing_fields(literal)
|
||||||
}
|
}
|
||||||
@ -475,7 +465,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
let adt = InFile::new(file_id, &adt);
|
let adt = InFile::new(file_id, &adt);
|
||||||
let src = InFile::new(file_id, attr.clone());
|
let src = InFile::new(file_id, attr.clone());
|
||||||
self.with_ctx(|ctx| {
|
self.with_ctx(|ctx| {
|
||||||
let (_, res) = ctx.attr_to_derive_macro_call(adt, src)?;
|
let (.., res) = ctx.attr_to_derive_macro_call(adt, src)?;
|
||||||
Some(res.to_vec())
|
Some(res.to_vec())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -668,7 +658,27 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
// FIXME replace map.while_some with take_while once stable
|
// FIXME replace map.while_some with take_while once stable
|
||||||
token.value.ancestors().map(ast::TokenTree::cast).while_some().last()
|
token.value.ancestors().map(ast::TokenTree::cast).while_some().last()
|
||||||
{
|
{
|
||||||
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
|
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
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
@ -898,72 +908,6 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
|
self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_derive_ident(
|
|
||||||
&self,
|
|
||||||
derive: &ast::Attr,
|
|
||||||
ident: &ast::Ident,
|
|
||||||
) -> Option<PathResolution> {
|
|
||||||
debug_assert!(ident.syntax().parent().and_then(ast::TokenTree::cast).is_some());
|
|
||||||
debug_assert!(ident.syntax().ancestors().any(|anc| anc == *derive.syntax()));
|
|
||||||
// derive macros are always at depth 2, tokentree -> meta -> attribute
|
|
||||||
let syntax = ident.syntax();
|
|
||||||
|
|
||||||
let tt = derive.token_tree()?;
|
|
||||||
let file = self.find_file(derive.syntax());
|
|
||||||
let adt = derive.syntax().parent().and_then(ast::Adt::cast)?;
|
|
||||||
let adt_def = ToDef::to_def(self, file.with_value(adt.clone()))?;
|
|
||||||
let res = self.with_ctx(|ctx| {
|
|
||||||
let (attr_id, derives) = ctx.attr_to_derive_macro_call(
|
|
||||||
file.with_value(&adt),
|
|
||||||
file.with_value(derive.clone()),
|
|
||||||
)?;
|
|
||||||
let attrs = adt_def.attrs(self.db);
|
|
||||||
let mut derive_paths = attrs.get(attr_id)?.parse_path_comma_token_tree()?;
|
|
||||||
|
|
||||||
let derive_idx = tt
|
|
||||||
.syntax()
|
|
||||||
.children_with_tokens()
|
|
||||||
.filter_map(SyntaxElement::into_token)
|
|
||||||
.take_while(|tok| tok != syntax)
|
|
||||||
.filter(|t| t.kind() == T![,])
|
|
||||||
.count();
|
|
||||||
let path_segment_idx = syntax
|
|
||||||
.siblings_with_tokens(Direction::Prev)
|
|
||||||
.filter_map(SyntaxElement::into_token)
|
|
||||||
.take_while(|tok| matches!(tok.kind(), T![:] | T![ident]))
|
|
||||||
.filter(|tok| tok.kind() == T![ident])
|
|
||||||
.count();
|
|
||||||
|
|
||||||
let mut mod_path = derive_paths.nth(derive_idx)?;
|
|
||||||
|
|
||||||
if path_segment_idx < mod_path.len() {
|
|
||||||
// the path for the given ident is a qualifier, resolve to module if possible
|
|
||||||
while path_segment_idx < mod_path.len() {
|
|
||||||
mod_path.pop_segment();
|
|
||||||
}
|
|
||||||
Some(Either::Left(mod_path))
|
|
||||||
} else {
|
|
||||||
// otherwise fetch the derive
|
|
||||||
Some(Either::Right(derives[derive_idx]))
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
match res {
|
|
||||||
Either::Left(path) => {
|
|
||||||
let len = path.len();
|
|
||||||
resolve_hir_path(
|
|
||||||
self.db,
|
|
||||||
&self.scope(derive.syntax()).resolver,
|
|
||||||
&Path::from_known_path(path, vec![None; len]),
|
|
||||||
)
|
|
||||||
.filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_))))
|
|
||||||
}
|
|
||||||
Either::Right(derive) => derive
|
|
||||||
.map(|call| MacroDef { id: self.db.lookup_intern_macro_call(call).def })
|
|
||||||
.map(PathResolution::Macro),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
|
fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
|
||||||
self.analyze(literal.syntax())
|
self.analyze(literal.syntax())
|
||||||
.record_literal_missing_fields(self.db, literal)
|
.record_literal_missing_fields(self.db, literal)
|
||||||
|
@ -249,9 +249,11 @@ impl SourceToDefCtx<'_, '_> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
item: InFile<&ast::Adt>,
|
item: InFile<&ast::Adt>,
|
||||||
src: InFile<ast::Attr>,
|
src: InFile<ast::Attr>,
|
||||||
) -> Option<(AttrId, &[Option<MacroCallId>])> {
|
) -> Option<(AttrId, MacroCallId, &[Option<MacroCallId>])> {
|
||||||
let map = self.dyn_map(item)?;
|
let map = self.dyn_map(item)?;
|
||||||
map[keys::DERIVE_MACRO_CALL].get(&src.value).map(|(id, ids)| (*id, &**ids))
|
map[keys::DERIVE_MACRO_CALL]
|
||||||
|
.get(&src.value)
|
||||||
|
.map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
|
fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
|
||||||
|
@ -371,10 +371,10 @@ impl SourceAnalyzer {
|
|||||||
return builtin.map(PathResolution::BuiltinAttr);
|
return builtin.map(PathResolution::BuiltinAttr);
|
||||||
}
|
}
|
||||||
return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) {
|
return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) {
|
||||||
res @ Some(m) if m.is_attr() => res.map(PathResolution::Macro),
|
Some(m) => Some(PathResolution::Macro(m)),
|
||||||
// this labels any path that starts with a tool module as the tool itself, this is technically wrong
|
// this labels any path that starts with a tool module as the tool itself, this is technically wrong
|
||||||
// but there is no benefit in differentiating these two cases for the time being
|
// but there is no benefit in differentiating these two cases for the time being
|
||||||
_ => path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| {
|
None => path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| {
|
||||||
match self.resolver.krate() {
|
match self.resolver.krate() {
|
||||||
Some(krate) => ToolModule::by_name(db, krate.into(), &name_ref.text()),
|
Some(krate) => ToolModule::by_name(db, krate.into(), &name_ref.text()),
|
||||||
None => ToolModule::builtin(&name_ref.text()),
|
None => ToolModule::builtin(&name_ref.text()),
|
||||||
|
@ -116,11 +116,11 @@ impl ChildBySource for ItemScope {
|
|||||||
self.derive_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each(
|
self.derive_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each(
|
||||||
|(ast_id, calls)| {
|
|(ast_id, calls)| {
|
||||||
let adt = ast_id.to_node(db.upcast());
|
let adt = ast_id.to_node(db.upcast());
|
||||||
calls.for_each(|(attr_id, calls)| {
|
calls.for_each(|(attr_id, call_id, calls)| {
|
||||||
if let Some(Either::Left(attr)) =
|
if let Some(Either::Left(attr)) =
|
||||||
adt.doc_comments_and_attrs().nth(attr_id.ast_index as usize)
|
adt.doc_comments_and_attrs().nth(attr_id.ast_index as usize)
|
||||||
{
|
{
|
||||||
res[keys::DERIVE_MACRO_CALL].insert(attr, (attr_id, calls.into()));
|
res[keys::DERIVE_MACRO_CALL].insert(attr, (attr_id, call_id, calls.into()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -66,8 +66,10 @@ 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:
|
derive_macros: FxHashMap<
|
||||||
FxHashMap<AstId<ast::Adt>, SmallVec<[(AttrId, SmallVec<[Option<MacroCallId>; 1]>); 1]>>,
|
AstId<ast::Adt>,
|
||||||
|
SmallVec<[(AttrId, MacroCallId, SmallVec<[Option<MacroCallId>; 1]>); 1]>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
|
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
|
||||||
@ -210,7 +212,7 @@ impl ItemScope {
|
|||||||
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((.., invocs)) = derives.iter_mut().find(|&&mut (id, ..)| id == attr_id) {
|
||||||
invocs[idx] = Some(call);
|
invocs[idx] = Some(call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,19 +225,23 @@ impl ItemScope {
|
|||||||
&mut self,
|
&mut self,
|
||||||
adt: AstId<ast::Adt>,
|
adt: AstId<ast::Adt>,
|
||||||
attr_id: AttrId,
|
attr_id: AttrId,
|
||||||
|
call_id: MacroCallId,
|
||||||
len: usize,
|
len: usize,
|
||||||
) {
|
) {
|
||||||
self.derive_macros.entry(adt).or_default().push((attr_id, smallvec![None; len]));
|
self.derive_macros.entry(adt).or_default().push((attr_id, call_id, smallvec![None; len]));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn derive_macro_invocs(
|
pub(crate) fn derive_macro_invocs(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<
|
) -> impl Iterator<
|
||||||
Item = (AstId<ast::Adt>, impl Iterator<Item = (AttrId, &[Option<MacroCallId>])>),
|
Item = (
|
||||||
|
AstId<ast::Adt>,
|
||||||
|
impl Iterator<Item = (AttrId, MacroCallId, &[Option<MacroCallId>])>,
|
||||||
|
),
|
||||||
> + '_ {
|
> + '_ {
|
||||||
self.derive_macros
|
self.derive_macros.iter().map(|(k, v)| {
|
||||||
.iter()
|
(*k, v.iter().map(|&(attr_id, call_id, ref invocs)| (attr_id, call_id, &**invocs)))
|
||||||
.map(|(k, v)| (*k, v.iter().map(|(attr_id, invocs)| (*attr_id, &**invocs))))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
|
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
|
||||||
|
@ -34,7 +34,8 @@ pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
|
|||||||
|
|
||||||
pub const MACRO: Key<ast::Macro, MacroDefId> = Key::new();
|
pub const MACRO: Key<ast::Macro, MacroDefId> = Key::new();
|
||||||
pub const ATTR_MACRO_CALL: Key<ast::Item, MacroCallId> = Key::new();
|
pub const ATTR_MACRO_CALL: Key<ast::Item, MacroCallId> = Key::new();
|
||||||
pub const DERIVE_MACRO_CALL: Key<ast::Attr, (AttrId, Box<[Option<MacroCallId>]>)> = Key::new();
|
pub const DERIVE_MACRO_CALL: Key<ast::Attr, (AttrId, MacroCallId, Box<[Option<MacroCallId>]>)> =
|
||||||
|
Key::new();
|
||||||
|
|
||||||
/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
|
/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
|
||||||
/// equal if they point to exactly the same object.
|
/// equal if they point to exactly the same object.
|
||||||
|
@ -690,9 +690,9 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
macro_call_as_call_id(
|
macro_call_as_call_id(
|
||||||
|
db,
|
||||||
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
|
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
|
||||||
expands_to,
|
expands_to,
|
||||||
db,
|
|
||||||
krate,
|
krate,
|
||||||
resolver,
|
resolver,
|
||||||
error_sink,
|
error_sink,
|
||||||
@ -714,9 +714,9 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn macro_call_as_call_id(
|
fn macro_call_as_call_id(
|
||||||
|
db: &dyn db::DefDatabase,
|
||||||
call: &AstIdWithPath<ast::MacroCall>,
|
call: &AstIdWithPath<ast::MacroCall>,
|
||||||
expand_to: ExpandTo,
|
expand_to: ExpandTo,
|
||||||
db: &dyn db::DefDatabase,
|
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
error_sink: &mut dyn FnMut(ExpandError),
|
error_sink: &mut dyn FnMut(ExpandError),
|
||||||
@ -739,10 +739,10 @@ fn macro_call_as_call_id(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn derive_macro_as_call_id(
|
fn derive_macro_as_call_id(
|
||||||
|
db: &dyn db::DefDatabase,
|
||||||
item_attr: &AstIdWithPath<ast::Adt>,
|
item_attr: &AstIdWithPath<ast::Adt>,
|
||||||
derive_attr: AttrId,
|
derive_attr: AttrId,
|
||||||
derive_pos: u32,
|
derive_pos: u32,
|
||||||
db: &dyn db::DefDatabase,
|
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
) -> Result<MacroCallId, UnresolvedMacro> {
|
) -> Result<MacroCallId, UnresolvedMacro> {
|
||||||
@ -761,11 +761,12 @@ fn derive_macro_as_call_id(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn attr_macro_as_call_id(
|
fn attr_macro_as_call_id(
|
||||||
|
db: &dyn db::DefDatabase,
|
||||||
item_attr: &AstIdWithPath<ast::Item>,
|
item_attr: &AstIdWithPath<ast::Item>,
|
||||||
macro_attr: &Attr,
|
macro_attr: &Attr,
|
||||||
db: &dyn db::DefDatabase,
|
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
def: MacroDefId,
|
def: MacroDefId,
|
||||||
|
is_derive: bool,
|
||||||
) -> MacroCallId {
|
) -> MacroCallId {
|
||||||
let mut arg = match macro_attr.input.as_deref() {
|
let mut arg = match macro_attr.input.as_deref() {
|
||||||
Some(attr::AttrInput::TokenTree(tt, map)) => (tt.clone(), map.clone()),
|
Some(attr::AttrInput::TokenTree(tt, map)) => (tt.clone(), map.clone()),
|
||||||
@ -782,6 +783,7 @@ fn attr_macro_as_call_id(
|
|||||||
ast_id: item_attr.ast_id,
|
ast_id: item_attr.ast_id,
|
||||||
attr_args: Arc::new(arg),
|
attr_args: Arc::new(arg),
|
||||||
invoc_attr_index: macro_attr.id.ast_index,
|
invoc_attr_index: macro_attr.id.ast_index,
|
||||||
|
is_derive,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
res
|
res
|
||||||
|
@ -54,7 +54,7 @@ impl DefMap {
|
|||||||
None => return Err(UnresolvedMacro { path: ast_id.path.clone() }),
|
None => return Err(UnresolvedMacro { path: ast_id.path.clone() }),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ResolvedAttr::Macro(attr_macro_as_call_id(&ast_id, attr, db, self.krate, def)))
|
Ok(ResolvedAttr::Macro(attr_macro_as_call_id(db, &ast_id, attr, self.krate, def, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
|
pub(crate) fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
|
||||||
|
@ -1055,9 +1055,9 @@ impl DefCollector<'_> {
|
|||||||
match &directive.kind {
|
match &directive.kind {
|
||||||
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
|
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
|
||||||
let call_id = macro_call_as_call_id(
|
let call_id = macro_call_as_call_id(
|
||||||
|
self.db,
|
||||||
ast_id,
|
ast_id,
|
||||||
*expand_to,
|
*expand_to,
|
||||||
self.db,
|
|
||||||
self.def_map.krate,
|
self.def_map.krate,
|
||||||
&resolver,
|
&resolver,
|
||||||
&mut |_err| (),
|
&mut |_err| (),
|
||||||
@ -1070,10 +1070,10 @@ 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 call_id = derive_macro_as_call_id(
|
||||||
|
self.db,
|
||||||
ast_id,
|
ast_id,
|
||||||
*derive_attr,
|
*derive_attr,
|
||||||
*derive_pos as u32,
|
*derive_pos as u32,
|
||||||
self.db,
|
|
||||||
self.def_map.krate,
|
self.def_map.krate,
|
||||||
&resolver,
|
&resolver,
|
||||||
);
|
);
|
||||||
@ -1170,9 +1170,17 @@ impl DefCollector<'_> {
|
|||||||
len = idx;
|
len = idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let call_id = attr_macro_as_call_id(
|
||||||
|
self.db,
|
||||||
|
file_ast_id,
|
||||||
|
attr,
|
||||||
|
self.def_map.krate,
|
||||||
|
def,
|
||||||
|
true,
|
||||||
|
);
|
||||||
self.def_map.modules[directive.module_id]
|
self.def_map.modules[directive.module_id]
|
||||||
.scope
|
.scope
|
||||||
.init_derive_attribute(ast_id, attr.id, len + 1);
|
.init_derive_attribute(ast_id, attr.id, call_id, len + 1);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let diag = DefDiagnostic::malformed_derive(
|
let diag = DefDiagnostic::malformed_derive(
|
||||||
@ -1192,8 +1200,14 @@ impl DefCollector<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute.
|
// Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute.
|
||||||
let call_id =
|
let call_id = attr_macro_as_call_id(
|
||||||
attr_macro_as_call_id(file_ast_id, attr, self.db, self.def_map.krate, def);
|
self.db,
|
||||||
|
file_ast_id,
|
||||||
|
attr,
|
||||||
|
self.def_map.krate,
|
||||||
|
def,
|
||||||
|
false,
|
||||||
|
);
|
||||||
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
|
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
|
||||||
|
|
||||||
// Skip #[test]/#[bench] expansion, which would merely result in more memory usage
|
// Skip #[test]/#[bench] expansion, which would merely result in more memory usage
|
||||||
@ -1310,9 +1324,9 @@ impl DefCollector<'_> {
|
|||||||
match &directive.kind {
|
match &directive.kind {
|
||||||
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
|
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
|
||||||
let macro_call_as_call_id = macro_call_as_call_id(
|
let macro_call_as_call_id = macro_call_as_call_id(
|
||||||
|
self.db,
|
||||||
ast_id,
|
ast_id,
|
||||||
*expand_to,
|
*expand_to,
|
||||||
self.db,
|
|
||||||
self.def_map.krate,
|
self.def_map.krate,
|
||||||
|path| {
|
|path| {
|
||||||
let resolved_res = self.def_map.resolve_path_fp_with_macro(
|
let resolved_res = self.def_map.resolve_path_fp_with_macro(
|
||||||
@ -1959,9 +1973,9 @@ impl ModCollector<'_, '_> {
|
|||||||
// Case 1: try to resolve in legacy scope and expand macro_rules
|
// Case 1: try to resolve in legacy scope and expand macro_rules
|
||||||
let mut error = None;
|
let mut error = None;
|
||||||
match macro_call_as_call_id(
|
match macro_call_as_call_id(
|
||||||
|
self.def_collector.db,
|
||||||
&ast_id,
|
&ast_id,
|
||||||
mac.expand_to,
|
mac.expand_to,
|
||||||
self.def_collector.db,
|
|
||||||
self.def_collector.def_map.krate,
|
self.def_collector.def_map.krate,
|
||||||
|path| {
|
|path| {
|
||||||
path.as_ident().and_then(|name| {
|
path.as_ident().and_then(|name| {
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
//! Builtin attributes.
|
//! Builtin attributes.
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::AstDatabase, name, AstId, CrateId, ExpandResult, MacroCallId, MacroDefId, MacroDefKind,
|
db::AstDatabase, name, AstId, CrateId, ExpandResult, MacroCallId, MacroCallKind, MacroDefId,
|
||||||
|
MacroDefKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! register_builtin {
|
macro_rules! register_builtin {
|
||||||
@ -53,7 +55,7 @@ register_builtin! {
|
|||||||
(bench, Bench) => dummy_attr_expand,
|
(bench, Bench) => dummy_attr_expand,
|
||||||
(cfg_accessible, CfgAccessible) => dummy_attr_expand,
|
(cfg_accessible, CfgAccessible) => dummy_attr_expand,
|
||||||
(cfg_eval, CfgEval) => dummy_attr_expand,
|
(cfg_eval, CfgEval) => dummy_attr_expand,
|
||||||
(derive, Derive) => dummy_attr_expand,
|
(derive, Derive) => derive_attr_expand,
|
||||||
(global_allocator, GlobalAllocator) => dummy_attr_expand,
|
(global_allocator, GlobalAllocator) => dummy_attr_expand,
|
||||||
(test, Test) => dummy_attr_expand,
|
(test, Test) => dummy_attr_expand,
|
||||||
(test_case, TestCase) => dummy_attr_expand
|
(test_case, TestCase) => dummy_attr_expand
|
||||||
@ -79,3 +81,76 @@ fn dummy_attr_expand(
|
|||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
ExpandResult::ok(tt.clone())
|
ExpandResult::ok(tt.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn derive_attr_expand(
|
||||||
|
db: &dyn AstDatabase,
|
||||||
|
id: MacroCallId,
|
||||||
|
tt: &tt::Subtree,
|
||||||
|
) -> ExpandResult<tt::Subtree> {
|
||||||
|
// we generate a very specific expansion here, as we do not actually expand the `#[derive]` attribute
|
||||||
|
// itself in name res, but we do want to expand it to something for the IDE layer, so that the input
|
||||||
|
// derive attributes can be downmapped, and resolved
|
||||||
|
// This is basically a hack, to get rid of hacks in the IDE layer that slowly accumulate more and more
|
||||||
|
// in various places.
|
||||||
|
|
||||||
|
// we transform the token tree of `#[derive(Foo, bar::Bar)]` into
|
||||||
|
// ```
|
||||||
|
// #[Foo]
|
||||||
|
// #[bar::Bar]
|
||||||
|
// ();
|
||||||
|
// ```
|
||||||
|
// which allows fallback path resolution in hir::Semantics to properly identify our derives
|
||||||
|
let loc = db.lookup_intern_macro_call(id);
|
||||||
|
let derives = match &loc.kind {
|
||||||
|
MacroCallKind::Attr { attr_args, .. } => &attr_args.0,
|
||||||
|
_ => return ExpandResult::ok(tt.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut token_trees = Vec::new();
|
||||||
|
for (comma, group) in &derives
|
||||||
|
.token_trees
|
||||||
|
.iter()
|
||||||
|
.filter_map(|tt| match tt {
|
||||||
|
tt::TokenTree::Leaf(l) => Some(l),
|
||||||
|
tt::TokenTree::Subtree(_) => None,
|
||||||
|
})
|
||||||
|
.group_by(|l| matches!(l, tt::Leaf::Punct(tt::Punct { char: ',', .. })))
|
||||||
|
{
|
||||||
|
if comma {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let wrap = |leaf| tt::TokenTree::Leaf(tt::Leaf::Punct(leaf));
|
||||||
|
token_trees.push(wrap(tt::Punct {
|
||||||
|
char: '#',
|
||||||
|
spacing: tt::Spacing::Alone,
|
||||||
|
id: tt::TokenId::unspecified(),
|
||||||
|
}));
|
||||||
|
token_trees.push(wrap(tt::Punct {
|
||||||
|
char: '[',
|
||||||
|
spacing: tt::Spacing::Alone,
|
||||||
|
id: tt::TokenId::unspecified(),
|
||||||
|
}));
|
||||||
|
token_trees.extend(group.cloned().map(tt::TokenTree::Leaf));
|
||||||
|
token_trees.push(wrap(tt::Punct {
|
||||||
|
char: ']',
|
||||||
|
spacing: tt::Spacing::Alone,
|
||||||
|
id: tt::TokenId::unspecified(),
|
||||||
|
}));
|
||||||
|
token_trees.push(wrap(tt::Punct {
|
||||||
|
char: '(',
|
||||||
|
spacing: tt::Spacing::Alone,
|
||||||
|
id: tt::TokenId::unspecified(),
|
||||||
|
}));
|
||||||
|
token_trees.push(wrap(tt::Punct {
|
||||||
|
char: ')',
|
||||||
|
spacing: tt::Spacing::Alone,
|
||||||
|
id: tt::TokenId::unspecified(),
|
||||||
|
}));
|
||||||
|
token_trees.push(wrap(tt::Punct {
|
||||||
|
char: ';',
|
||||||
|
spacing: tt::Spacing::Alone,
|
||||||
|
id: tt::TokenId::unspecified(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
ExpandResult::ok(tt::Subtree { delimiter: tt.delimiter, token_trees })
|
||||||
|
}
|
||||||
|
@ -342,6 +342,7 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
|
|||||||
.map(|it| it.syntax().clone())
|
.map(|it| it.syntax().clone())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
MacroCallKind::Attr { is_derive: true, .. } => return None,
|
||||||
MacroCallKind::Attr { invoc_attr_index, .. } => {
|
MacroCallKind::Attr { invoc_attr_index, .. } => {
|
||||||
cov_mark::hit!(attribute_macro_attr_censoring);
|
cov_mark::hit!(attribute_macro_attr_censoring);
|
||||||
ast::Item::cast(node.clone())?
|
ast::Item::cast(node.clone())?
|
||||||
|
@ -166,6 +166,7 @@ pub enum MacroCallKind {
|
|||||||
/// Outer attributes are counted first, then inner attributes. This does not support
|
/// Outer attributes are counted first, then inner attributes. This does not support
|
||||||
/// out-of-line modules, which may have attributes spread across 2 files!
|
/// out-of-line modules, which may have attributes spread across 2 files!
|
||||||
invoc_attr_index: u32,
|
invoc_attr_index: u32,
|
||||||
|
is_derive: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,6 +432,7 @@ impl MacroCallKind {
|
|||||||
match self {
|
match self {
|
||||||
MacroCallKind::FnLike { expand_to, .. } => *expand_to,
|
MacroCallKind::FnLike { expand_to, .. } => *expand_to,
|
||||||
MacroCallKind::Derive { .. } => ExpandTo::Items,
|
MacroCallKind::Derive { .. } => ExpandTo::Items,
|
||||||
|
MacroCallKind::Attr { is_derive: true, .. } => ExpandTo::Statements,
|
||||||
MacroCallKind::Attr { .. } => ExpandTo::Items, // is this always correct?
|
MacroCallKind::Attr { .. } => ExpandTo::Items, // is this always correct?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -497,7 +499,7 @@ impl ExpansionInfo {
|
|||||||
|
|
||||||
let token_range = token.value.text_range();
|
let token_range = token.value.text_range();
|
||||||
match &loc.kind {
|
match &loc.kind {
|
||||||
MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
|
MacroCallKind::Attr { attr_args, invoc_attr_index, is_derive, .. } => {
|
||||||
let attr = item
|
let attr = item
|
||||||
.doc_comments_and_attrs()
|
.doc_comments_and_attrs()
|
||||||
.nth(*invoc_attr_index as usize)
|
.nth(*invoc_attr_index as usize)
|
||||||
@ -511,9 +513,13 @@ impl ExpansionInfo {
|
|||||||
let relative_range =
|
let relative_range =
|
||||||
token.value.text_range().checked_sub(attr_input_start)?;
|
token.value.text_range().checked_sub(attr_input_start)?;
|
||||||
// shift by the item's tree's max id
|
// shift by the item's tree's max id
|
||||||
let token_id = self
|
let token_id = attr_args.1.token_by_range(relative_range)?;
|
||||||
.macro_arg_shift
|
let token_id = if *is_derive {
|
||||||
.shift(attr_args.1.token_by_range(relative_range)?);
|
// we do not shift for `#[derive]`, as we only need to downmap the derive attribute tokens
|
||||||
|
token_id
|
||||||
|
} else {
|
||||||
|
self.macro_arg_shift.shift(token_id)
|
||||||
|
};
|
||||||
Some(token_id)
|
Some(token_id)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -15,6 +15,7 @@ fn check_hover_no_result(ra_fixture: &str) {
|
|||||||
assert!(hover.is_none(), "hover not expected but found: {:?}", hover.unwrap());
|
assert!(hover.is_none(), "hover not expected but found: {:?}", hover.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn check(ra_fixture: &str, expect: Expect) {
|
fn check(ra_fixture: &str, expect: Expect) {
|
||||||
let (analysis, position) = fixture::position(ra_fixture);
|
let (analysis, position) = fixture::position(ra_fixture);
|
||||||
let hover = analysis
|
let hover = analysis
|
||||||
|
@ -361,7 +361,7 @@ fn traverse(
|
|||||||
syntactic_name_ref_highlighting,
|
syntactic_name_ref_highlighting,
|
||||||
node,
|
node,
|
||||||
),
|
),
|
||||||
NodeOrToken::Token(token) => highlight::token(sema, krate, token).zip(Some(None)),
|
NodeOrToken::Token(token) => highlight::token(sema, token).zip(Some(None)),
|
||||||
};
|
};
|
||||||
if let Some((mut highlight, binding_hash)) = element {
|
if let Some((mut highlight, binding_hash)) = element {
|
||||||
if inside_attribute {
|
if inside_attribute {
|
||||||
|
@ -18,11 +18,7 @@ use crate::{
|
|||||||
Highlight, HlMod, HlTag,
|
Highlight, HlMod, HlTag,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) fn token(
|
pub(super) fn token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Highlight> {
|
||||||
sema: &Semantics<RootDatabase>,
|
|
||||||
krate: Option<hir::Crate>,
|
|
||||||
token: SyntaxToken,
|
|
||||||
) -> Option<Highlight> {
|
|
||||||
if let Some(comment) = ast::Comment::cast(token.clone()) {
|
if let Some(comment) = ast::Comment::cast(token.clone()) {
|
||||||
let h = HlTag::Comment;
|
let h = HlTag::Comment;
|
||||||
return Some(match comment.kind().doc {
|
return Some(match comment.kind().doc {
|
||||||
@ -39,17 +35,10 @@ pub(super) fn token(
|
|||||||
INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
|
INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
|
||||||
BYTE => HlTag::ByteLiteral.into(),
|
BYTE => HlTag::ByteLiteral.into(),
|
||||||
CHAR => HlTag::CharLiteral.into(),
|
CHAR => HlTag::CharLiteral.into(),
|
||||||
IDENT => {
|
IDENT if token.parent().and_then(ast::TokenTree::cast).is_some() => {
|
||||||
let tt = ast::TokenTree::cast(token.parent()?)?;
|
|
||||||
let ident = ast::Ident::cast(token)?;
|
|
||||||
// from this point on we are inside a token tree, this only happens for identifiers
|
// from this point on we are inside a token tree, this only happens for identifiers
|
||||||
// that were not mapped down into macro invocations
|
// that were not mapped down into macro invocations
|
||||||
(|| {
|
HlTag::None.into()
|
||||||
let attr = tt.parent_meta()?.parent_attr()?;
|
|
||||||
let res = sema.resolve_derive_ident(&attr, &ident)?;
|
|
||||||
Some(highlight_def(sema, krate, Definition::from(res)))
|
|
||||||
})()
|
|
||||||
.unwrap_or_else(|| HlTag::None.into())
|
|
||||||
}
|
}
|
||||||
p if p.is_punct() => punctuation(sema, token, p),
|
p if p.is_punct() => punctuation(sema, token, p),
|
||||||
k if k.is_keyword() => keyword(sema, token, k)?,
|
k if k.is_keyword() => keyword(sema, token, k)?,
|
||||||
|
@ -58,15 +58,15 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
|
|||||||
(
|
(
|
||||||
hir::AssocItem::Function(fn_item),
|
hir::AssocItem::Function(fn_item),
|
||||||
ImplCompletionKind::All | ImplCompletionKind::Fn,
|
ImplCompletionKind::All | ImplCompletionKind::Fn,
|
||||||
) => add_function_impl(&trigger, acc, ctx, fn_item, hir_impl),
|
) => add_function_impl(acc, ctx, &trigger, fn_item, hir_impl),
|
||||||
(
|
(
|
||||||
hir::AssocItem::TypeAlias(type_item),
|
hir::AssocItem::TypeAlias(type_item),
|
||||||
ImplCompletionKind::All | ImplCompletionKind::TypeAlias,
|
ImplCompletionKind::All | ImplCompletionKind::TypeAlias,
|
||||||
) => add_type_alias_impl(&trigger, acc, ctx, type_item),
|
) => add_type_alias_impl(acc, ctx, &trigger, type_item),
|
||||||
(
|
(
|
||||||
hir::AssocItem::Const(const_item),
|
hir::AssocItem::Const(const_item),
|
||||||
ImplCompletionKind::All | ImplCompletionKind::Const,
|
ImplCompletionKind::All | ImplCompletionKind::Const,
|
||||||
) => add_const_impl(&trigger, acc, ctx, const_item, hir_impl),
|
) => add_const_impl(acc, ctx, &trigger, const_item, hir_impl),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -126,9 +126,9 @@ fn completion_match(mut token: SyntaxToken) -> Option<(ImplCompletionKind, Synta
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_function_impl(
|
fn add_function_impl(
|
||||||
fn_def_node: &SyntaxNode,
|
|
||||||
acc: &mut Completions,
|
acc: &mut Completions,
|
||||||
ctx: &CompletionContext,
|
ctx: &CompletionContext,
|
||||||
|
fn_def_node: &SyntaxNode,
|
||||||
func: hir::Function,
|
func: hir::Function,
|
||||||
impl_def: hir::Impl,
|
impl_def: hir::Impl,
|
||||||
) {
|
) {
|
||||||
@ -199,9 +199,9 @@ fn get_transformed_assoc_item(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_type_alias_impl(
|
fn add_type_alias_impl(
|
||||||
type_def_node: &SyntaxNode,
|
|
||||||
acc: &mut Completions,
|
acc: &mut Completions,
|
||||||
ctx: &CompletionContext,
|
ctx: &CompletionContext,
|
||||||
|
type_def_node: &SyntaxNode,
|
||||||
type_alias: hir::TypeAlias,
|
type_alias: hir::TypeAlias,
|
||||||
) {
|
) {
|
||||||
let alias_name = type_alias.name(ctx.db).to_smol_str();
|
let alias_name = type_alias.name(ctx.db).to_smol_str();
|
||||||
@ -217,9 +217,9 @@ fn add_type_alias_impl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_const_impl(
|
fn add_const_impl(
|
||||||
const_def_node: &SyntaxNode,
|
|
||||||
acc: &mut Completions,
|
acc: &mut Completions,
|
||||||
ctx: &CompletionContext,
|
ctx: &CompletionContext,
|
||||||
|
const_def_node: &SyntaxNode,
|
||||||
const_: hir::Const,
|
const_: hir::Const,
|
||||||
impl_def: hir::Impl,
|
impl_def: hir::Impl,
|
||||||
) {
|
) {
|
||||||
|
@ -14,7 +14,7 @@ use hir::{
|
|||||||
use stdx::impl_from;
|
use stdx::impl_from;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
match_ast, AstToken, SyntaxKind, SyntaxNode, SyntaxToken,
|
match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::RootDatabase;
|
use crate::RootDatabase;
|
||||||
@ -142,16 +142,6 @@ impl IdentClass {
|
|||||||
token: &SyntaxToken,
|
token: &SyntaxToken,
|
||||||
) -> Option<IdentClass> {
|
) -> Option<IdentClass> {
|
||||||
let parent = token.parent()?;
|
let parent = token.parent()?;
|
||||||
// resolve derives if possible
|
|
||||||
if let Some(ident) = ast::Ident::cast(token.clone()) {
|
|
||||||
let attr = ast::TokenTree::cast(parent.clone())
|
|
||||||
.and_then(|tt| tt.parent_meta())
|
|
||||||
.and_then(|meta| meta.parent_attr());
|
|
||||||
if let Some(attr) = attr {
|
|
||||||
return NameRefClass::classify_derive(sema, &attr, &ident)
|
|
||||||
.map(IdentClass::NameRefClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::classify_node(sema, &parent)
|
Self::classify_node(sema, &parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,14 +451,6 @@ impl NameRefClass {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn classify_derive(
|
|
||||||
sema: &Semantics<RootDatabase>,
|
|
||||||
attr: &ast::Attr,
|
|
||||||
ident: &ast::Ident,
|
|
||||||
) -> Option<NameRefClass> {
|
|
||||||
sema.resolve_derive_ident(&attr, &ident).map(Definition::from).map(NameRefClass::Definition)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_from!(
|
impl_from!(
|
||||||
|
Loading…
Reference in New Issue
Block a user