aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2019-12-06 18:30:15 +0000
committerFlorian Diebold <[email protected]>2019-12-06 20:25:22 +0000
commita565072ddeac519eea3b415a57e9fd208e20e562 (patch)
tree4c76dbe6729de22de2f00862b56ed5dfd46d699b
parenteae425b10fd7803ae67d2d39e9aca902daa353ba (diff)
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.
-rw-r--r--crates/ra_hir/src/source_binder.rs23
-rw-r--r--crates/ra_hir_expand/src/lib.rs18
-rw-r--r--crates/ra_ide/src/goto_definition.rs27
3 files changed, 63 insertions, 5 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 42c392513..cb4345ca1 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(
76 db: &impl HirDatabase, 76 db: &impl HirDatabase,
77 child: InFile<&SyntaxNode>, 77 child: InFile<&SyntaxNode>,
78) -> Option<DefWithBody> { 78) -> Option<DefWithBody> {
79 child.value.ancestors().find_map(|node| { 79 ancestors_with_macros(db, child).find_map(|node| {
80 let n = &node.value;
80 match_ast! { 81 match_ast! {
81 match node { 82 match n {
82 ast::FnDef(def) => { return Function::from_source(db, child.with_value(def)).map(DefWithBody::from); }, 83 ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); },
83 ast::ConstDef(def) => { return Const::from_source(db, child.with_value(def)).map(DefWithBody::from); }, 84 ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); },
84 ast::StaticDef(def) => { return Static::from_source(db, child.with_value(def)).map(DefWithBody::from); }, 85 ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); },
85 _ => { None }, 86 _ => { None },
86 } 87 }
87 } 88 }
88 }) 89 })
89} 90}
90 91
92fn 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
91/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of 103/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
92/// original source files. It should not be used inside the HIR itself. 104/// original source files. It should not be used inside the HIR itself.
93#[derive(Debug)] 105#[derive(Debug)]
@@ -135,6 +147,7 @@ pub struct ReferenceDescriptor {
135 pub name: String, 147 pub name: String,
136} 148}
137 149
150#[derive(Debug)]
138pub struct Expansion { 151pub struct Expansion {
139 macro_file_kind: MacroFileKind, 152 macro_file_kind: MacroFileKind,
140 macro_call_id: MacroCallId, 153 macro_call_id: MacroCallId,
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 59c69b91b..0c1dc87e6 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -76,6 +76,17 @@ impl HirFileId {
76 } 76 }
77 } 77 }
78 78
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
79 /// Return expansion information if it is a macro-expansion file 90 /// Return expansion information if it is a macro-expansion file
80 pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> { 91 pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
81 match self.0 { 92 match self.0 {
@@ -176,6 +187,13 @@ impl MacroCallKind {
176 } 187 }
177 } 188 }
178 189
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
179 pub fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> { 197 pub fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
180 match self { 198 match self {
181 MacroCallKind::FnLike(ast_id) => { 199 MacroCallKind::FnLike(ast_id) => {
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 76a741207..b1d567ca7 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -693,4 +693,31 @@ mod tests {
693 "foo FN_DEF FileId(1) [52; 63) [55; 58)", 693 "foo FN_DEF FileId(1) [52; 63) [55; 58)",
694 ); 694 );
695 } 695 }
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 }
696} 723}