diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 42c3925135d..cb4345ca149 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -76,18 +76,30 @@ fn def_with_body_from_child_node( db: &impl HirDatabase, child: InFile<&SyntaxNode>, ) -> Option { - child.value.ancestors().find_map(|node| { + ancestors_with_macros(db, child).find_map(|node| { + let n = &node.value; match_ast! { - match node { - ast::FnDef(def) => { return Function::from_source(db, child.with_value(def)).map(DefWithBody::from); }, - ast::ConstDef(def) => { return Const::from_source(db, child.with_value(def)).map(DefWithBody::from); }, - ast::StaticDef(def) => { return Static::from_source(db, child.with_value(def)).map(DefWithBody::from); }, + match n { + ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); }, + ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); }, + ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); }, _ => { None }, } } }) } +fn ancestors_with_macros<'a>( + db: &'a (impl HirDatabase), + node: InFile<&SyntaxNode>, +) -> impl Iterator> + 'a { + let file = node.with_value(()); // keep just the file id for borrow checker purposes + let parent_node = node.file_id.call_node(db); + let parent_ancestors: Box>> = + Box::new(parent_node.into_iter().flat_map(move |n| ancestors_with_macros(db, n.as_ref()))); + node.value.ancestors().map(move |n| file.with_value(n)).chain(parent_ancestors) +} + /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of /// original source files. It should not be used inside the HIR itself. #[derive(Debug)] @@ -135,6 +147,7 @@ pub struct ReferenceDescriptor { pub name: String, } +#[derive(Debug)] pub struct Expansion { macro_file_kind: MacroFileKind, macro_call_id: MacroCallId, diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 59c69b91bb2..0c1dc87e671 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -76,6 +76,17 @@ pub fn original_file(self, db: &dyn db::AstDatabase) -> FileId { } } + /// If this is a macro call, returns the syntax node of the call. + pub fn call_node(self, db: &dyn db::AstDatabase) -> Option> { + match self.0 { + HirFileIdRepr::FileId(_) => None, + HirFileIdRepr::MacroFile(macro_file) => { + let loc = db.lookup_intern_macro(macro_file.macro_call_id); + Some(loc.kind.node(db)) + } + } + } + /// Return expansion information if it is a macro-expansion file pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option { match self.0 { @@ -176,6 +187,13 @@ pub fn file_id(&self) -> HirFileId { } } + pub fn node(&self, db: &dyn db::AstDatabase) -> InFile { + match self { + MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), + MacroCallKind::Attr(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), + } + } + pub fn arg(&self, db: &dyn db::AstDatabase) -> Option { match self { MacroCallKind::FnLike(ast_id) => { diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 76a74120762..b1d567ca770 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -693,4 +693,31 @@ fn bar() { "foo FN_DEF FileId(1) [52; 63) [55; 58)", ); } + + #[test] + fn goto_through_format() { + check_goto( + " + //- /lib.rs + #[macro_export] + macro_rules! format { + ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) + } + #[rustc_builtin_macro] + #[macro_export] + macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) + } + pub mod __export { + pub use crate::format_args; + } + fn foo() -> i8 {} + fn test() { + format!(\"{}\", fo<|>o()) + } + ", + "foo FN_DEF FileId(1) [359; 376) [362; 365)", + ); + } }