From 2eaa8c94a8a6b5cd86139c5e010ae95268b28658 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 16 Nov 2019 16:49:26 +0300 Subject: [PATCH] Goto definition works inside macros --- crates/ra_hir/src/source_binder.rs | 23 ++++++++++- crates/ra_hir_expand/src/lib.rs | 9 +++++ crates/ra_ide_api/src/goto_definition.rs | 50 +++++++++++++++++++----- crates/ra_mbe/src/syntax_bridge.rs | 6 +++ 4 files changed, 77 insertions(+), 11 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 5764dc26d1e..75a467fb392 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -11,7 +11,7 @@ expr::{ExprId, PatId}, path::known, }; -use hir_expand::{name::AsName, Source}; +use hir_expand::{name::AsName, AstId, MacroCallId, MacroCallLoc, MacroFileKind, Source}; use ra_syntax::{ ast::{self, AstNode}, match_ast, AstPtr, @@ -126,6 +126,20 @@ pub struct ReferenceDescriptor { pub name: String, } +pub struct Expansion { + macro_call_id: MacroCallId, +} + +impl Expansion { + pub fn translate_offset(&self, db: &impl HirDatabase, offset: TextUnit) -> Option { + let exp_info = self.file_id().expansion_info(db)?; + exp_info.translate_offset(offset) + } + pub fn file_id(&self) -> HirFileId { + self.macro_call_id.as_file(MacroFileKind::Items) + } +} + impl SourceAnalyzer { pub fn new( db: &impl HirDatabase, @@ -386,6 +400,13 @@ pub fn impls_future(&self, db: &impl HirDatabase, ty: Ty) -> bool { implements_trait(&canonical_ty, db, &self.resolver, krate, std_future_trait) } + pub fn expand(&self, db: &impl HirDatabase, macro_call: &ast::MacroCall) -> Option { + let def = self.resolve_macro_call(db, macro_call)?.id; + let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(macro_call)); + let macro_call_loc = MacroCallLoc { def, ast_id }; + Some(Expansion { macro_call_id: db.intern_macro(macro_call_loc) }) + } + #[cfg(test)] pub(crate) fn body_source_map(&self) -> Arc { self.body_source_map.clone().unwrap() diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 26531cb05e0..6bfbb2f7996 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -160,6 +160,15 @@ pub struct ExpansionInfo { } impl ExpansionInfo { + pub fn translate_offset(&self, offset: TextUnit) -> Option { + let offset = offset.checked_sub(self.arg_start.1)?; + let token_id = self.macro_arg.1.token_by_offset(offset)?; + let token_id = tt::TokenId(token_id.0 + self.shift); + + let (r, _) = self.exp_map.ranges.iter().find(|(_, tid)| *tid == token_id)?; + Some(r.start()) + } + pub fn find_range(&self, from: TextRange) -> Option<(HirFileId, TextRange)> { let token_id = look_in_rev_map(&self.exp_map, from)?; diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 821796e5f69..4b15814994c 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -1,11 +1,10 @@ //! FIXME: write short doc here -use hir::Source; -use ra_db::SourceDatabase; +use hir::{db::AstDatabase, Source}; use ra_syntax::{ algo::find_node_at_offset, ast::{self, DocCommentsOwner}, - match_ast, AstNode, SyntaxNode, + match_ast, AstNode, SyntaxNode, TextUnit, }; use crate::{ @@ -19,17 +18,29 @@ pub(crate) fn goto_definition( db: &RootDatabase, position: FilePosition, ) -> Option>> { - let parse = db.parse(position.file_id); - let syntax = parse.tree().syntax().clone(); - if let Some(name_ref) = find_node_at_offset::(&syntax, position.offset) { - let navs = - reference_definition(db, Source::new(position.file_id.into(), &name_ref)).to_vec(); + go(db, Source::new(position.file_id.into(), position.offset)) +} + +fn go(db: &RootDatabase, offset: Source) -> Option>> { + let syntax = db.parse_or_expand(offset.file_id)?; + + if let Some(name_ref) = find_node_at_offset::(&syntax, offset.ast) { + let navs = reference_definition(db, offset.with_ast(&name_ref)).to_vec(); return Some(RangeInfo::new(name_ref.syntax().text_range(), navs.to_vec())); } - if let Some(name) = find_node_at_offset::(&syntax, position.offset) { - let navs = name_definition(db, Source::new(position.file_id.into(), &name))?; + if let Some(name) = find_node_at_offset::(&syntax, offset.ast) { + let navs = name_definition(db, offset.with_ast(&name))?; return Some(RangeInfo::new(name.syntax().text_range(), navs)); } + if let Some(macro_call) = find_node_at_offset::(&syntax, offset.ast) { + let source_analyzer = + hir::SourceAnalyzer::new(db, offset.with_ast(macro_call.syntax()), None); + if let Some(exp) = source_analyzer.expand(db, ¯o_call) { + if let Some(offset) = exp.translate_offset(db, offset.ast) { + return go(db, Source::new(exp.file_id(), offset)); + } + } + } None } @@ -677,4 +688,23 @@ mod bar<|> { "bar MODULE FileId(1) [0; 11) [4; 7)", ); } + + #[test] + fn goto_from_macro() { + check_goto( + " + //- /lib.rs + macro_rules! id { + ($($tt:tt)*) => { $($tt)* } + } + fn foo() {} + id! { + fn bar() { + fo<|>o(); + } + } + ", + "foo FN_DEF FileId(1) [52; 63) [55; 58)", + ); + } } diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index 3f57ce3b501..37382d2df74 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs @@ -77,6 +77,12 @@ pub fn token_tree_to_syntax_node( } impl TokenMap { + pub fn token_by_offset(&self, relative_offset: TextUnit) -> Option { + let (idx, _) = + self.tokens.iter().enumerate().find(|(_, range)| range.contains(relative_offset))?; + Some(tt::TokenId(idx as u32)) + } + pub fn relative_range_of(&self, tt: tt::TokenId) -> Option { let idx = tt.0 as usize; self.tokens.get(idx).copied()