fix: fix expansion order for fn-like macros and attributes in token descending

This commit is contained in:
Lukas Wirth 2021-09-14 01:20:43 +02:00
parent 6eecd84771
commit bb946f78f6
3 changed files with 76 additions and 54 deletions

View file

@ -498,47 +498,16 @@ impl<'db> SemanticsImpl<'db> {
// otherwise push the remapped tokens back into the queue as they can potentially be remapped again. // otherwise push the remapped tokens back into the queue as they can potentially be remapped again.
while let Some(token) = queue.pop() { while let Some(token) = queue.pop() {
self.db.unwind_if_cancelled(); self.db.unwind_if_cancelled();
let was_not_remapped = (|| { let was_not_remapped = (|| {
for node in token.value.ancestors() { if let Some((call_id, item)) = token
if let Some(macro_call) = ast::MacroCall::cast(node.clone()) { .value
let tt = match macro_call.token_tree() { .ancestors()
Some(tt) => tt, .filter_map(ast::Item::cast)
None => continue, .filter_map(|item| {
}; self.with_ctx(|ctx| ctx.item_to_macro_call(token.with_value(item.clone())))
let l_delim = match tt.left_delimiter_token() { .zip(Some(item))
Some(it) => it.text_range().end(), })
None => tt.syntax().text_range().start(), .last()
};
let r_delim = match tt.right_delimiter_token() {
Some(it) => it.text_range().start(),
None => tt.syntax().text_range().end(),
};
if !TextRange::new(l_delim, r_delim)
.contains_range(token.value.text_range())
{
continue;
}
let file_id = match sa.expand(self.db, token.with_value(&macro_call)) {
Some(file_id) => file_id,
None => continue,
};
let tokens = cache
.entry(file_id)
.or_insert_with(|| file_id.expansion_info(self.db.upcast()))
.as_ref()?
.map_token_down(self.db.upcast(), None, token.as_ref())?;
let len = queue.len();
queue.extend(tokens.inspect(|token| {
if let Some(parent) = token.value.parent() {
self.cache(find_root(&parent), token.file_id);
}
}));
return (queue.len() != len).then(|| ());
} else if let Some(item) = ast::Item::cast(node.clone()) {
if let Some(call_id) = self
.with_ctx(|ctx| ctx.item_to_macro_call(token.with_value(item.clone())))
{ {
let file_id = call_id.as_file(); let file_id = call_id.as_file();
let tokens = cache let tokens = cache
@ -555,11 +524,39 @@ impl<'db> SemanticsImpl<'db> {
})); }));
return (queue.len() != len).then(|| ()); return (queue.len() != len).then(|| ());
} }
if let Some(macro_call) = token.value.ancestors().find_map(ast::MacroCall::cast) {
let tt = macro_call.token_tree()?;
let l_delim = match tt.left_delimiter_token() {
Some(it) => it.text_range().end(),
None => tt.syntax().text_range().start(),
};
let r_delim = match tt.right_delimiter_token() {
Some(it) => it.text_range().start(),
None => tt.syntax().text_range().end(),
};
if !TextRange::new(l_delim, r_delim).contains_range(token.value.text_range()) {
return None;
} }
let file_id = sa.expand(self.db, token.with_value(&macro_call))?;
let tokens = cache
.entry(file_id)
.or_insert_with(|| file_id.expansion_info(self.db.upcast()))
.as_ref()?
.map_token_down(self.db.upcast(), None, token.as_ref())?;
let len = queue.len();
queue.extend(tokens.inspect(|token| {
if let Some(parent) = token.value.parent() {
self.cache(find_root(&parent), token.file_id);
}
}));
return (queue.len() != len).then(|| ());
} }
None None
})() })()
.is_none(); .is_none();
if was_not_remapped { if was_not_remapped {
res.push(token.value) res.push(token.value)
} }

View file

@ -1,4 +1,5 @@
//! Utilities for creating `Analysis` instances for tests. //! Utilities for creating `Analysis` instances for tests.
use hir::db::DefDatabase;
use ide_db::base_db::fixture::ChangeFixture; use ide_db::base_db::fixture::ChangeFixture;
use test_utils::{extract_annotations, RangeOrOffset}; use test_utils::{extract_annotations, RangeOrOffset};
@ -44,6 +45,7 @@ pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrO
/// Creates analysis from a multi-file fixture, returns positions marked with $0. /// Creates analysis from a multi-file fixture, returns positions marked with $0.
pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) { pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) {
let mut host = AnalysisHost::default(); let mut host = AnalysisHost::default();
host.db.set_enable_proc_attr_macros(true);
let change_fixture = ChangeFixture::parse(ra_fixture); let change_fixture = ChangeFixture::parse(ra_fixture);
host.db.apply_change(change_fixture.change); host.db.apply_change(change_fixture.change);
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");

View file

@ -220,6 +220,29 @@ mod tests {
assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {:?}", navs) assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {:?}", navs)
} }
#[test]
fn goto_def_in_mac_call_in_attr_invoc() {
check(
r#"
//- proc_macros: identity
pub struct Struct {
// ^^^^^^
field: i32,
}
macro_rules! identity {
($($tt:tt)*) => {$($tt)*};
}
#[proc_macros::identity]
fn function() {
identity!(Struct$0 { field: 0 });
}
"#,
)
}
#[test] #[test]
fn goto_def_for_extern_crate() { fn goto_def_for_extern_crate() {
check( check(