fix: Fix expand macro recursively not working correctly for nested macro calls

This commit is contained in:
Lukas Wirth 2025-09-05 08:25:28 +02:00
parent 14872a5332
commit bd57ea0871

View file

@ -1,5 +1,5 @@
use hir::db::ExpandDatabase; use hir::db::ExpandDatabase;
use hir::{ExpandResult, InFile, InRealFile, Semantics}; use hir::{ExpandResult, InFile, Semantics};
use ide_db::{ use ide_db::{
FileId, RootDatabase, base_db::Crate, helpers::pick_best_token, FileId, RootDatabase, base_db::Crate, helpers::pick_best_token,
syntax_helpers::prettify_macro_expansion, syntax_helpers::prettify_macro_expansion,
@ -87,52 +87,55 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
return derive; return derive;
} }
let mut anc = sema let syntax_token = sema.descend_into_macros_exact(tok);
.descend_token_into_include_expansion(InRealFile::new(file_id, tok)) 'tokens: for syntax_token in syntax_token {
.value let mut anc = syntax_token.parent_ancestors();
.parent_ancestors(); let mut span_map = SpanMap::empty();
let mut span_map = SpanMap::empty(); let mut error = String::new();
let mut error = String::new(); let (name, expanded, kind) = loop {
let (name, expanded, kind) = loop { let Some(node) = anc.next() else {
let node = anc.next()?; continue 'tokens;
};
if let Some(item) = ast::Item::cast(node.clone()) if let Some(item) = ast::Item::cast(node.clone())
&& let Some(def) = sema.resolve_attr_macro_call(&item) && let Some(def) = sema.resolve_attr_macro_call(&item)
{ {
break ( break (
def.name(db).display(db, file_id.edition(db)).to_string(), def.name(db).display(db, file_id.edition(db)).to_string(),
expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?, expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?,
SyntaxKind::MACRO_ITEMS, SyntaxKind::MACRO_ITEMS,
); );
}
if let Some(mac) = ast::MacroCall::cast(node) {
let mut name = mac.path()?.segment()?.name_ref()?.to_string();
name.push('!');
let syntax_kind =
mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS);
break (
name,
expand_macro_recur(
&sema,
&ast::Item::MacroCall(mac),
&mut error,
&mut span_map,
TextSize::new(0),
)?,
syntax_kind,
);
}
};
// FIXME:
// macro expansion may lose all white space information
// But we hope someday we can use ra_fmt for that
let mut expansion = format(db, kind, position.file_id, expanded, &span_map, krate);
if !error.is_empty() {
expansion.insert_str(0, &format!("Expansion had errors:{error}\n\n"));
} }
if let Some(mac) = ast::MacroCall::cast(node) { return Some(ExpandedMacro { name, expansion });
let mut name = mac.path()?.segment()?.name_ref()?.to_string();
name.push('!');
let syntax_kind =
mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS);
break (
name,
expand_macro_recur(
&sema,
&ast::Item::MacroCall(mac),
&mut error,
&mut span_map,
TextSize::new(0),
)?,
syntax_kind,
);
}
};
// FIXME:
// macro expansion may lose all white space information
// But we hope someday we can use ra_fmt for that
let mut expansion = format(db, kind, position.file_id, expanded, &span_map, krate);
if !error.is_empty() {
expansion.insert_str(0, &format!("Expansion had errors:{error}\n\n"));
} }
Some(ExpandedMacro { name, expansion }) None
} }
fn expand_macro_recur( fn expand_macro_recur(
@ -752,8 +755,8 @@ fn test() {
} }
"#, "#,
expect![[r#" expect![[r#"
my_concat! concat!
"<>hi""#]], "<>""#]],
); );
} }