mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
Support derive-macros for rename prefix magic
This commit is contained in:
parent
96c4455114
commit
9a786d0008
@ -21,6 +21,8 @@ smol_str.opt-level = 3
|
|||||||
text-size.opt-level = 3
|
text-size.opt-level = 3
|
||||||
serde.opt-level = 3
|
serde.opt-level = 3
|
||||||
salsa.opt-level = 3
|
salsa.opt-level = 3
|
||||||
|
dissimilar.opt-level = 3
|
||||||
|
|
||||||
# This speeds up `cargo xtask dist`.
|
# This speeds up `cargo xtask dist`.
|
||||||
miniz_oxide.opt-level = 3
|
miniz_oxide.opt-level = 3
|
||||||
|
|
||||||
|
@ -67,8 +67,14 @@ pub mod keys {
|
|||||||
pub const PROC_MACRO: Key<ast::Fn, ProcMacroId> = Key::new();
|
pub const PROC_MACRO: Key<ast::Fn, ProcMacroId> = Key::new();
|
||||||
pub const MACRO_CALL: Key<ast::MacroCall, MacroCallId> = Key::new();
|
pub const MACRO_CALL: Key<ast::MacroCall, MacroCallId> = 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, MacroCallId, Box<[Option<MacroCallId>]>)> =
|
pub const DERIVE_MACRO_CALL: Key<
|
||||||
Key::new();
|
ast::Attr,
|
||||||
|
(
|
||||||
|
AttrId,
|
||||||
|
/* derive() */ MacroCallId,
|
||||||
|
/* actual derive macros */ 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.
|
||||||
|
@ -550,7 +550,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_derive_annotated(&self, adt: InFile<&ast::Adt>) -> bool {
|
pub fn is_derive_annotated(&self, adt: InFile<&ast::Adt>) -> bool {
|
||||||
self.with_ctx(|ctx| ctx.has_derives(adt))
|
self.with_ctx(|ctx| ctx.file_of_adt_has_derives(adt))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn derive_helpers_in_scope(&self, adt: &ast::Adt) -> Option<Vec<(Symbol, Symbol)>> {
|
pub fn derive_helpers_in_scope(&self, adt: &ast::Adt) -> Option<Vec<(Symbol, Symbol)>> {
|
||||||
@ -894,6 +894,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
// node is just the token, so descend the token
|
// node is just the token, so descend the token
|
||||||
self.descend_into_macros_impl(
|
self.descend_into_macros_impl(
|
||||||
InFile::new(file.file_id, first),
|
InFile::new(file.file_id, first),
|
||||||
|
false,
|
||||||
&mut |InFile { value, .. }, _ctx| {
|
&mut |InFile { value, .. }, _ctx| {
|
||||||
if let Some(node) = value
|
if let Some(node) = value
|
||||||
.parent_ancestors()
|
.parent_ancestors()
|
||||||
@ -908,14 +909,19 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
} else {
|
} else {
|
||||||
// Descend first and last token, then zip them to look for the node they belong to
|
// Descend first and last token, then zip them to look for the node they belong to
|
||||||
let mut scratch: SmallVec<[_; 1]> = smallvec![];
|
let mut scratch: SmallVec<[_; 1]> = smallvec![];
|
||||||
self.descend_into_macros_impl(InFile::new(file.file_id, first), &mut |token, _ctx| {
|
self.descend_into_macros_impl(
|
||||||
scratch.push(token);
|
InFile::new(file.file_id, first),
|
||||||
CONTINUE_NO_BREAKS
|
false,
|
||||||
});
|
&mut |token, _ctx| {
|
||||||
|
scratch.push(token);
|
||||||
|
CONTINUE_NO_BREAKS
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let mut scratch = scratch.into_iter();
|
let mut scratch = scratch.into_iter();
|
||||||
self.descend_into_macros_impl(
|
self.descend_into_macros_impl(
|
||||||
InFile::new(file.file_id, last),
|
InFile::new(file.file_id, last),
|
||||||
|
false,
|
||||||
&mut |InFile { value: last, file_id: last_fid }, _ctx| {
|
&mut |InFile { value: last, file_id: last_fid }, _ctx| {
|
||||||
if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
|
if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
|
||||||
if first_fid == last_fid {
|
if first_fid == last_fid {
|
||||||
@ -967,7 +973,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
ast::Item::Union(it) => it.into(),
|
ast::Item::Union(it) => it.into(),
|
||||||
_ => return false,
|
_ => return false,
|
||||||
};
|
};
|
||||||
ctx.has_derives(token.with_value(&adt))
|
ctx.file_of_adt_has_derives(token.with_value(&adt))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -977,7 +983,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
token: SyntaxToken,
|
token: SyntaxToken,
|
||||||
mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContext),
|
mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContext),
|
||||||
) {
|
) {
|
||||||
self.descend_into_macros_impl(self.wrap_token_infile(token), &mut |t, ctx| {
|
self.descend_into_macros_impl(self.wrap_token_infile(token), false, &mut |t, ctx| {
|
||||||
cb(t, ctx);
|
cb(t, ctx);
|
||||||
CONTINUE_NO_BREAKS
|
CONTINUE_NO_BREAKS
|
||||||
});
|
});
|
||||||
@ -985,10 +991,14 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
|
|
||||||
pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
|
pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
|
||||||
let mut res = smallvec![];
|
let mut res = smallvec![];
|
||||||
self.descend_into_macros_impl(self.wrap_token_infile(token.clone()), &mut |t, _ctx| {
|
self.descend_into_macros_impl(
|
||||||
res.push(t.value);
|
self.wrap_token_infile(token.clone()),
|
||||||
CONTINUE_NO_BREAKS
|
false,
|
||||||
});
|
&mut |t, _ctx| {
|
||||||
|
res.push(t.value);
|
||||||
|
CONTINUE_NO_BREAKS
|
||||||
|
},
|
||||||
|
);
|
||||||
if res.is_empty() {
|
if res.is_empty() {
|
||||||
res.push(token);
|
res.push(token);
|
||||||
}
|
}
|
||||||
@ -1001,7 +1011,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
) -> SmallVec<[InFile<SyntaxToken>; 1]> {
|
) -> SmallVec<[InFile<SyntaxToken>; 1]> {
|
||||||
let mut res = smallvec![];
|
let mut res = smallvec![];
|
||||||
let token = self.wrap_token_infile(token);
|
let token = self.wrap_token_infile(token);
|
||||||
self.descend_into_macros_impl(token.clone(), &mut |t, ctx| {
|
self.descend_into_macros_impl(token.clone(), true, &mut |t, ctx| {
|
||||||
if !ctx.is_opaque(self.db) {
|
if !ctx.is_opaque(self.db) {
|
||||||
// Don't descend into opaque contexts
|
// Don't descend into opaque contexts
|
||||||
res.push(t);
|
res.push(t);
|
||||||
@ -1019,7 +1029,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
token: InFile<SyntaxToken>,
|
token: InFile<SyntaxToken>,
|
||||||
mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContext) -> ControlFlow<T>,
|
mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContext) -> ControlFlow<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
self.descend_into_macros_impl(token, &mut cb)
|
self.descend_into_macros_impl(token, false, &mut cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Descends the token into expansions, returning the tokens that matches the input
|
/// Descends the token into expansions, returning the tokens that matches the input
|
||||||
@ -1092,41 +1102,41 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
fn descend_into_macros_impl<T>(
|
fn descend_into_macros_impl<T>(
|
||||||
&self,
|
&self,
|
||||||
InFile { value: token, file_id }: InFile<SyntaxToken>,
|
InFile { value: token, file_id }: InFile<SyntaxToken>,
|
||||||
|
always_descend_into_derives: bool,
|
||||||
f: &mut dyn FnMut(InFile<SyntaxToken>, SyntaxContext) -> ControlFlow<T>,
|
f: &mut dyn FnMut(InFile<SyntaxToken>, SyntaxContext) -> ControlFlow<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
let _p = tracing::info_span!("descend_into_macros_impl").entered();
|
let _p = tracing::info_span!("descend_into_macros_impl").entered();
|
||||||
|
|
||||||
let span = self.db.span_map(file_id).span_for_range(token.text_range());
|
let db = self.db;
|
||||||
|
let span = db.span_map(file_id).span_for_range(token.text_range());
|
||||||
|
|
||||||
// Process the expansion of a call, pushing all tokens with our span in the expansion back onto our stack
|
// Process the expansion of a call, pushing all tokens with our span in the expansion back onto our stack
|
||||||
let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
|
let process_expansion_for_token =
|
||||||
let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| {
|
|ctx: &mut SourceToDefCtx<'_, '_>, stack: &mut Vec<_>, macro_file| {
|
||||||
Some(
|
let InMacroFile { file_id, value: mapped_tokens } = ctx
|
||||||
ctx.cache
|
.cache
|
||||||
.get_or_insert_expansion(ctx.db, macro_file)
|
.get_or_insert_expansion(ctx.db, macro_file)
|
||||||
.map_range_down(span)?
|
.map_range_down(span)?
|
||||||
.map(SmallVec::<[_; 2]>::from_iter),
|
.map(SmallVec::<[_; 2]>::from_iter);
|
||||||
)
|
// we have found a mapping for the token if the vec is non-empty
|
||||||
})?;
|
let res = mapped_tokens.is_empty().not().then_some(());
|
||||||
// we have found a mapping for the token if the vec is non-empty
|
// requeue the tokens we got from mapping our current token down
|
||||||
let res = mapped_tokens.is_empty().not().then_some(());
|
stack.push((HirFileId::from(file_id), mapped_tokens));
|
||||||
// requeue the tokens we got from mapping our current token down
|
res
|
||||||
stack.push((HirFileId::from(file_id), mapped_tokens));
|
};
|
||||||
res
|
|
||||||
};
|
|
||||||
|
|
||||||
// A stack of tokens to process, along with the file they came from
|
// A stack of tokens to process, along with the file they came from
|
||||||
// These are tracked to know which macro calls we still have to look into
|
// These are tracked to know which macro calls we still have to look into
|
||||||
// the tokens themselves aren't that interesting as the span that is being used to map
|
// the tokens themselves aren't that interesting as the span that is being used to map
|
||||||
// things down never changes.
|
// things down never changes.
|
||||||
let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![];
|
let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![];
|
||||||
let include = file_id.file_id().and_then(|file_id| {
|
let include = file_id
|
||||||
self.s2d_cache.borrow_mut().get_or_insert_include_for(self.db, file_id)
|
.file_id()
|
||||||
});
|
.and_then(|file_id| self.s2d_cache.borrow_mut().get_or_insert_include_for(db, file_id));
|
||||||
match include {
|
match include {
|
||||||
Some(include) => {
|
Some(include) => {
|
||||||
// include! inputs are always from real files, so they only need to be handled once upfront
|
// include! inputs are always from real files, so they only need to be handled once upfront
|
||||||
process_expansion_for_token(&mut stack, include)?;
|
self.with_ctx(|ctx| process_expansion_for_token(ctx, &mut stack, include))?;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
stack.push((file_id, smallvec![(token, span.ctx)]));
|
stack.push((file_id, smallvec![(token, span.ctx)]));
|
||||||
@ -1148,62 +1158,120 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
tokens.reverse();
|
tokens.reverse();
|
||||||
while let Some((token, ctx)) = tokens.pop() {
|
while let Some((token, ctx)) = tokens.pop() {
|
||||||
let was_not_remapped = (|| {
|
let was_not_remapped = (|| {
|
||||||
// First expand into attribute invocations
|
// First expand into attribute invocations, this is required to be handled
|
||||||
let containing_attribute_macro_call = self.with_ctx(|ctx| {
|
// upfront as any other macro call within will not semantically resolve unless
|
||||||
token.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
|
// also descended.
|
||||||
// Don't force populate the dyn cache for items that don't have an attribute anyways
|
let res = self.with_ctx(|ctx| {
|
||||||
item.attrs().next()?;
|
token
|
||||||
Some((ctx.item_to_macro_call(InFile::new(expansion, &item))?, item))
|
.parent_ancestors()
|
||||||
})
|
.filter_map(ast::Item::cast)
|
||||||
});
|
// FIXME: This might work incorrectly when we have a derive, followed by
|
||||||
if let Some((call_id, item)) = containing_attribute_macro_call {
|
// an attribute on an item, like:
|
||||||
let attr_id = match self.db.lookup_intern_macro_call(call_id).kind {
|
// ```
|
||||||
hir_expand::MacroCallKind::Attr { invoc_attr_index, .. } => {
|
// #[derive(Debug$0)]
|
||||||
invoc_attr_index.ast_index()
|
// #[my_attr]
|
||||||
}
|
// struct MyStruct;
|
||||||
_ => 0,
|
// ```
|
||||||
};
|
// here we should not consider the attribute at all, as our cursor
|
||||||
// FIXME: here, the attribute's text range is used to strip away all
|
// technically lies outside of its expansion
|
||||||
// entries from the start of the attribute "list" up the invoking
|
.find_map(|item| {
|
||||||
// attribute. But in
|
// Don't force populate the dyn cache for items that don't have an attribute anyways
|
||||||
// ```
|
item.attrs().next()?;
|
||||||
// mod foo {
|
ctx.item_to_macro_call(InFile::new(expansion, &item))
|
||||||
// #![inner]
|
.zip(Some(item))
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
// we don't wanna strip away stuff in the `mod foo {` range, that is
|
|
||||||
// here if the id corresponds to an inner attribute we got strip all
|
|
||||||
// text ranges of the outer ones, and then all of the inner ones up
|
|
||||||
// to the invoking attribute so that the inbetween is ignored.
|
|
||||||
let text_range = item.syntax().text_range();
|
|
||||||
let start = collect_attrs(&item)
|
|
||||||
.nth(attr_id)
|
|
||||||
.map(|attr| match attr.1 {
|
|
||||||
Either::Left(it) => it.syntax().text_range().start(),
|
|
||||||
Either::Right(it) => it.syntax().text_range().start(),
|
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| text_range.start());
|
.map(|(call_id, item)| {
|
||||||
let text_range = TextRange::new(start, text_range.end());
|
let attr_id = match db.lookup_intern_macro_call(call_id).kind {
|
||||||
filter_duplicates(tokens, text_range);
|
hir_expand::MacroCallKind::Attr {
|
||||||
return process_expansion_for_token(&mut stack, call_id);
|
invoc_attr_index, ..
|
||||||
|
} => invoc_attr_index.ast_index(),
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
// FIXME: here, the attribute's text range is used to strip away all
|
||||||
|
// entries from the start of the attribute "list" up the invoking
|
||||||
|
// attribute. But in
|
||||||
|
// ```
|
||||||
|
// mod foo {
|
||||||
|
// #![inner]
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
// we don't wanna strip away stuff in the `mod foo {` range, that is
|
||||||
|
// here if the id corresponds to an inner attribute we got strip all
|
||||||
|
// text ranges of the outer ones, and then all of the inner ones up
|
||||||
|
// to the invoking attribute so that the inbetween is ignored.
|
||||||
|
let text_range = item.syntax().text_range();
|
||||||
|
let start = collect_attrs(&item)
|
||||||
|
.nth(attr_id)
|
||||||
|
.map(|attr| match attr.1 {
|
||||||
|
Either::Left(it) => it.syntax().text_range().start(),
|
||||||
|
Either::Right(it) => it.syntax().text_range().start(),
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| text_range.start());
|
||||||
|
let text_range = TextRange::new(start, text_range.end());
|
||||||
|
filter_duplicates(tokens, text_range);
|
||||||
|
process_expansion_for_token(ctx, &mut stack, call_id)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(res) = res {
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if always_descend_into_derives {
|
||||||
|
let res = self.with_ctx(|ctx| {
|
||||||
|
let (derives, adt) = token
|
||||||
|
.parent_ancestors()
|
||||||
|
.filter_map(ast::Adt::cast)
|
||||||
|
.find_map(|adt| {
|
||||||
|
Some((
|
||||||
|
ctx.derive_macro_calls(InFile::new(expansion, &adt))?
|
||||||
|
.map(|(a, b, c)| (a, b, c.to_owned()))
|
||||||
|
.collect::<SmallVec<[_; 2]>>(),
|
||||||
|
adt,
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
let mut res = None;
|
||||||
|
for (_, derive_attr, derives) in derives {
|
||||||
|
// as there may be multiple derives registering the same helper
|
||||||
|
// name, we gotta make sure to call this for all of them!
|
||||||
|
// FIXME: We need to call `f` for all of them as well though!
|
||||||
|
res = res.or(process_expansion_for_token(
|
||||||
|
ctx,
|
||||||
|
&mut stack,
|
||||||
|
derive_attr,
|
||||||
|
));
|
||||||
|
for derive in derives.into_iter().flatten() {
|
||||||
|
res = res
|
||||||
|
.or(process_expansion_for_token(ctx, &mut stack, derive));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// remove all tokens that are within the derives expansion
|
||||||
|
filter_duplicates(tokens, adt.syntax().text_range());
|
||||||
|
Some(res)
|
||||||
|
});
|
||||||
|
// if we found derives, we can early exit. There is no way we can be in any
|
||||||
|
// macro call at this point given we are not in a token tree
|
||||||
|
if let Some(res) = res {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Then check for token trees, that means we are either in a function-like macro or
|
// Then check for token trees, that means we are either in a function-like macro or
|
||||||
// secondary attribute inputs
|
// secondary attribute inputs
|
||||||
let tt = token
|
let tt = token
|
||||||
.parent_ancestors()
|
.parent_ancestors()
|
||||||
.map_while(Either::<ast::TokenTree, ast::Meta>::cast)
|
.map_while(Either::<ast::TokenTree, ast::Meta>::cast)
|
||||||
.last()?;
|
.last()?;
|
||||||
|
|
||||||
match tt {
|
match tt {
|
||||||
// function-like macro call
|
// function-like macro call
|
||||||
Either::Left(tt) => {
|
Either::Left(tt) => {
|
||||||
|
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
|
||||||
if tt.left_delimiter_token().map_or(false, |it| it == token) {
|
if tt.left_delimiter_token().map_or(false, |it| it == token) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if tt.right_delimiter_token().map_or(false, |it| it == token) {
|
if tt.right_delimiter_token().map_or(false, |it| it == token) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
|
|
||||||
let mcall = InFile::new(expansion, macro_call);
|
let mcall = InFile::new(expansion, macro_call);
|
||||||
let file_id = match m_cache.get(&mcall) {
|
let file_id = match m_cache.get(&mcall) {
|
||||||
Some(&it) => it,
|
Some(&it) => it,
|
||||||
@ -1216,13 +1284,16 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
let text_range = tt.syntax().text_range();
|
let text_range = tt.syntax().text_range();
|
||||||
filter_duplicates(tokens, text_range);
|
filter_duplicates(tokens, text_range);
|
||||||
|
|
||||||
process_expansion_for_token(&mut stack, file_id).or(file_id
|
self.with_ctx(|ctx| {
|
||||||
.eager_arg(self.db)
|
process_expansion_for_token(ctx, &mut stack, file_id).or(file_id
|
||||||
.and_then(|arg| {
|
.eager_arg(db)
|
||||||
// also descend into eager expansions
|
.and_then(|arg| {
|
||||||
process_expansion_for_token(&mut stack, arg)
|
// also descend into eager expansions
|
||||||
}))
|
process_expansion_for_token(ctx, &mut stack, arg)
|
||||||
|
}))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
Either::Right(_) if always_descend_into_derives => None,
|
||||||
// derive or derive helper
|
// derive or derive helper
|
||||||
Either::Right(meta) => {
|
Either::Right(meta) => {
|
||||||
// attribute we failed expansion for earlier, this might be a derive invocation
|
// attribute we failed expansion for earlier, this might be a derive invocation
|
||||||
@ -1231,31 +1302,33 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
let adt = match attr.syntax().parent().and_then(ast::Adt::cast) {
|
let adt = match attr.syntax().parent().and_then(ast::Adt::cast) {
|
||||||
Some(adt) => {
|
Some(adt) => {
|
||||||
// this might be a derive on an ADT
|
// this might be a derive on an ADT
|
||||||
let derive_call = self.with_ctx(|ctx| {
|
let res = self.with_ctx(|ctx| {
|
||||||
// so try downmapping the token into the pseudo derive expansion
|
// so try downmapping the token into the pseudo derive expansion
|
||||||
// see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
|
// see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
|
||||||
ctx.attr_to_derive_macro_call(
|
let derive_call = ctx
|
||||||
InFile::new(expansion, &adt),
|
.attr_to_derive_macro_call(
|
||||||
InFile::new(expansion, attr.clone()),
|
InFile::new(expansion, &adt),
|
||||||
)
|
InFile::new(expansion, attr.clone()),
|
||||||
.map(|(_, call_id, _)| call_id)
|
)?
|
||||||
});
|
.1;
|
||||||
|
|
||||||
match derive_call {
|
// resolved to a derive
|
||||||
Some(call_id) => {
|
let text_range = attr.syntax().text_range();
|
||||||
// resolved to a derive
|
// remove any other token in this macro input, all their mappings are the
|
||||||
let text_range = attr.syntax().text_range();
|
// same as this
|
||||||
// remove any other token in this macro input, all their mappings are the
|
tokens.retain(|(t, _)| {
|
||||||
// same as this
|
!text_range.contains_range(t.text_range())
|
||||||
tokens.retain(|(t, _)| {
|
});
|
||||||
!text_range.contains_range(t.text_range())
|
Some(process_expansion_for_token(
|
||||||
});
|
ctx,
|
||||||
return process_expansion_for_token(
|
&mut stack,
|
||||||
&mut stack, call_id,
|
derive_call,
|
||||||
);
|
))
|
||||||
}
|
});
|
||||||
None => Some(adt),
|
if let Some(res) = res {
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
Some(adt)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Otherwise this could be a derive helper on a variant or field
|
// Otherwise this could be a derive helper on a variant or field
|
||||||
@ -1269,12 +1342,9 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(expansion, &adt))) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let attr_name =
|
let attr_name =
|
||||||
attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
|
attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
|
||||||
// Not an attribute, nor a derive, so it's either an intert attribute or a derive helper
|
// Not an attribute, nor a derive, so it's either an inert attribute or a derive helper
|
||||||
// Try to resolve to a derive helper and downmap
|
// Try to resolve to a derive helper and downmap
|
||||||
let resolver = &token
|
let resolver = &token
|
||||||
.parent()
|
.parent()
|
||||||
@ -1282,7 +1352,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
self.analyze_impl(InFile::new(expansion, &parent), None, false)
|
self.analyze_impl(InFile::new(expansion, &parent), None, false)
|
||||||
})?
|
})?
|
||||||
.resolver;
|
.resolver;
|
||||||
let id = self.db.ast_id_map(expansion).ast_id(&adt);
|
let id = db.ast_id_map(expansion).ast_id(&adt);
|
||||||
let helpers = resolver
|
let helpers = resolver
|
||||||
.def_map()
|
.def_map()
|
||||||
.derive_helpers_in_scope(InFile::new(expansion, id))?;
|
.derive_helpers_in_scope(InFile::new(expansion, id))?;
|
||||||
@ -1293,20 +1363,22 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut res = None;
|
let mut res = None;
|
||||||
for (.., derive) in
|
self.with_ctx(|ctx| {
|
||||||
helpers.iter().filter(|(helper, ..)| *helper == attr_name)
|
for (.., derive) in
|
||||||
{
|
helpers.iter().filter(|(helper, ..)| *helper == attr_name)
|
||||||
// as there may be multiple derives registering the same helper
|
{
|
||||||
// name, we gotta make sure to call this for all of them!
|
// as there may be multiple derives registering the same helper
|
||||||
// FIXME: We need to call `f` for all of them as well though!
|
// name, we gotta make sure to call this for all of them!
|
||||||
res = res.or(process_expansion_for_token(&mut stack, *derive));
|
// FIXME: We need to call `f` for all of them as well though!
|
||||||
}
|
res = res
|
||||||
res
|
.or(process_expansion_for_token(ctx, &mut stack, *derive));
|
||||||
|
}
|
||||||
|
res
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
.is_none();
|
.is_none();
|
||||||
|
|
||||||
if was_not_remapped {
|
if was_not_remapped {
|
||||||
if let ControlFlow::Break(b) = f(InFile::new(expansion, token), ctx) {
|
if let ControlFlow::Break(b) = f(InFile::new(expansion, token), ctx) {
|
||||||
return Some(b);
|
return Some(b);
|
||||||
|
@ -108,7 +108,7 @@ use span::FileId;
|
|||||||
use stdx::impl_from;
|
use stdx::impl_from;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
AstNode, AstPtr, SyntaxNode,
|
AstNode, AstPtr, SyntaxNode,
|
||||||
ast::{self, HasName},
|
ast::{self, HasAttrs, HasName},
|
||||||
};
|
};
|
||||||
use tt::TextRange;
|
use tt::TextRange;
|
||||||
|
|
||||||
@ -411,10 +411,24 @@ impl SourceToDefCtx<'_, '_> {
|
|||||||
.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 file_of_adt_has_derives(&mut self, adt: InFile<&ast::Adt>) -> bool {
|
||||||
self.dyn_map(adt).as_ref().is_some_and(|map| !map[keys::DERIVE_MACRO_CALL].is_empty())
|
self.dyn_map(adt).as_ref().is_some_and(|map| !map[keys::DERIVE_MACRO_CALL].is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn derive_macro_calls<'slf>(
|
||||||
|
&'slf mut self,
|
||||||
|
adt: InFile<&ast::Adt>,
|
||||||
|
) -> Option<impl Iterator<Item = (AttrId, MacroCallId, &'slf [Option<MacroCallId>])> + use<'slf>>
|
||||||
|
{
|
||||||
|
self.dyn_map(adt).as_ref().map(|&map| {
|
||||||
|
let dyn_map = &map[keys::DERIVE_MACRO_CALL];
|
||||||
|
adt.value
|
||||||
|
.attrs()
|
||||||
|
.filter_map(move |attr| dyn_map.get(&AstPtr::new(&attr)))
|
||||||
|
.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>(
|
||||||
&mut self,
|
&mut self,
|
||||||
src: InFile<&Ast>,
|
src: InFile<&Ast>,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user