Skip to content

Commit cff286d

Browse files
committed
Try to make go to definition work in format!
SourceAnalyzer didn't work properly within expression macro expansions because it didn't find the enclosing function. Fix this by going up the expansion chain to find ancestors. This makes the test work, but apparently in real usage it's still not working.
1 parent 39444bf commit cff286d

File tree

3 files changed

+63
-5
lines changed

3 files changed

+63
-5
lines changed

crates/ra_hir/src/source_binder.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,30 @@ fn def_with_body_from_child_node(
7676
db: &impl HirDatabase,
7777
child: InFile<&SyntaxNode>,
7878
) -> Option<DefWithBody> {
79-
child.value.ancestors().find_map(|node| {
79+
ancestors_with_macros(db, child).find_map(|node| {
80+
let n = &node.value;
8081
match_ast! {
81-
match node {
82-
ast::FnDef(def) => { return Function::from_source(db, child.with_value(def)).map(DefWithBody::from); },
83-
ast::ConstDef(def) => { return Const::from_source(db, child.with_value(def)).map(DefWithBody::from); },
84-
ast::StaticDef(def) => { return Static::from_source(db, child.with_value(def)).map(DefWithBody::from); },
82+
match n {
83+
ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); },
84+
ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); },
85+
ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); },
8586
_ => { None },
8687
}
8788
}
8889
})
8990
}
9091

92+
fn ancestors_with_macros<'a>(
93+
db: &'a (impl HirDatabase),
94+
node: InFile<&SyntaxNode>,
95+
) -> impl Iterator<Item = InFile<SyntaxNode>> + 'a {
96+
let file = node.with_value(()); // keep just the file id for borrow checker purposes
97+
let parent_node = node.file_id.call_node(db);
98+
let parent_ancestors: Box<dyn Iterator<Item = InFile<SyntaxNode>>> =
99+
Box::new(parent_node.into_iter().flat_map(move |n| ancestors_with_macros(db, n.as_ref())));
100+
node.value.ancestors().map(move |n| file.with_value(n)).chain(parent_ancestors)
101+
}
102+
91103
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
92104
/// original source files. It should not be used inside the HIR itself.
93105
#[derive(Debug)]
@@ -135,6 +147,7 @@ pub struct ReferenceDescriptor {
135147
pub name: String,
136148
}
137149

150+
#[derive(Debug)]
138151
pub struct Expansion {
139152
macro_file_kind: MacroFileKind,
140153
macro_call_id: MacroCallId,

crates/ra_hir_expand/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,17 @@ impl HirFileId {
7676
}
7777
}
7878

79+
/// If this is a macro call, returns the syntax node of the call.
80+
pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
81+
match self.0 {
82+
HirFileIdRepr::FileId(_) => None,
83+
HirFileIdRepr::MacroFile(macro_file) => {
84+
let loc = db.lookup_intern_macro(macro_file.macro_call_id);
85+
Some(loc.kind.node(db))
86+
}
87+
}
88+
}
89+
7990
/// Return expansion information if it is a macro-expansion file
8091
pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
8192
match self.0 {
@@ -176,6 +187,13 @@ impl MacroCallKind {
176187
}
177188
}
178189

190+
pub fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
191+
match self {
192+
MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()),
193+
MacroCallKind::Attr(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()),
194+
}
195+
}
196+
179197
pub fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
180198
match self {
181199
MacroCallKind::FnLike(ast_id) => {

crates/ra_ide/src/goto_definition.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,4 +693,31 @@ mod tests {
693693
"foo FN_DEF FileId(1) [52; 63) [55; 58)",
694694
);
695695
}
696+
697+
#[test]
698+
fn goto_through_format() {
699+
check_goto(
700+
"
701+
//- /lib.rs
702+
#[macro_export]
703+
macro_rules! format {
704+
($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
705+
}
706+
#[rustc_builtin_macro]
707+
#[macro_export]
708+
macro_rules! format_args {
709+
($fmt:expr) => ({ /* compiler built-in */ });
710+
($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
711+
}
712+
pub mod __export {
713+
pub use crate::format_args;
714+
}
715+
fn foo() -> i8 {}
716+
fn test() {
717+
format!(\"{}\", fo<|>o())
718+
}
719+
",
720+
"foo FN_DEF FileId(1) [359; 376) [362; 365)",
721+
);
722+
}
696723
}

0 commit comments

Comments
 (0)