diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 0d55e4a3e0b..920e18208ee 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -117,6 +117,12 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn expand(&self, macro_call: &ast::MacroCall) -> Option { self.imp.expand(macro_call) } + + /// If `item` has an attribute macro attached to it, expands it. + pub fn expand_attr_macro(&self, item: &ast::Item) -> Option { + self.imp.expand_attr_macro(item) + } + pub fn speculative_expand( &self, actual_macro_call: &ast::MacroCall, @@ -332,6 +338,16 @@ impl<'db> SemanticsImpl<'db> { Some(node) } + fn expand_attr_macro(&self, item: &ast::Item) -> Option { + let sa = self.analyze(item.syntax()); + let src = InFile::new(sa.file_id, item.clone()); + let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?; + let file_id = macro_call_id.as_file(); + let node = self.db.parse_or_expand(file_id)?; + self.cache(node.clone(), file_id); + Some(node) + } + fn speculative_expand( &self, actual_macro_call: &ast::MacroCall, diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index e0d01fa96ed..3f38e2145ed 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -3,8 +3,7 @@ use std::iter; use hir::Semantics; use ide_db::RootDatabase; use syntax::{ - algo::find_node_at_offset, ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, - SyntaxNode, WalkEvent, T, + ast, match_ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T, }; use crate::FilePosition; @@ -28,16 +27,37 @@ pub struct ExpandedMacro { pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option { let sema = Semantics::new(db); let file = sema.parse(position.file_id); - let mac = find_node_at_offset::(file.syntax(), position.offset)?; - let name = mac.path()?.segment()?.name_ref()?; - let expanded = expand_macro_recur(&sema, &mac)?; + let tok = file.syntax().token_at_offset(position.offset).left_biased()?; + let mut expanded = None; + let mut name = None; + for node in tok.ancestors() { + match_ast! { + match node { + ast::MacroCall(mac) => { + name = Some(mac.path()?.segment()?.name_ref()?.to_string()); + expanded = expand_macro_recur(&sema, &mac); + break; + }, + ast::Item(item) => { + // FIXME: add the macro name + // FIXME: make this recursive too + name = Some("?".to_string()); + expanded = sema.expand_attr_macro(&item); + if expanded.is_some() { + break; + } + }, + _ => {} + } + } + } // FIXME: // macro expansion may lose all white space information // But we hope someday we can use ra_fmt for that - let expansion = insert_whitespaces(expanded); - Some(ExpandedMacro { name: name.to_string(), expansion }) + let expansion = insert_whitespaces(expanded?); + Some(ExpandedMacro { name: name?, expansion }) } fn expand_macro_recur(