mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 13:51:31 +00:00
Goto definition works inside macros
This commit is contained in:
parent
786cae520a
commit
2eaa8c94a8
4 changed files with 77 additions and 11 deletions
|
@ -11,7 +11,7 @@ use hir_def::{
|
||||||
expr::{ExprId, PatId},
|
expr::{ExprId, PatId},
|
||||||
path::known,
|
path::known,
|
||||||
};
|
};
|
||||||
use hir_expand::{name::AsName, Source};
|
use hir_expand::{name::AsName, AstId, MacroCallId, MacroCallLoc, MacroFileKind, Source};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
match_ast, AstPtr,
|
match_ast, AstPtr,
|
||||||
|
@ -126,6 +126,20 @@ pub struct ReferenceDescriptor {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Expansion {
|
||||||
|
macro_call_id: MacroCallId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expansion {
|
||||||
|
pub fn translate_offset(&self, db: &impl HirDatabase, offset: TextUnit) -> Option<TextUnit> {
|
||||||
|
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 {
|
impl SourceAnalyzer {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
|
@ -386,6 +400,13 @@ impl SourceAnalyzer {
|
||||||
implements_trait(&canonical_ty, db, &self.resolver, krate, std_future_trait)
|
implements_trait(&canonical_ty, db, &self.resolver, krate, std_future_trait)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expand(&self, db: &impl HirDatabase, macro_call: &ast::MacroCall) -> Option<Expansion> {
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
|
pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
|
||||||
self.body_source_map.clone().unwrap()
|
self.body_source_map.clone().unwrap()
|
||||||
|
|
|
@ -160,6 +160,15 @@ pub struct ExpansionInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExpansionInfo {
|
impl ExpansionInfo {
|
||||||
|
pub fn translate_offset(&self, offset: TextUnit) -> Option<TextUnit> {
|
||||||
|
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)> {
|
pub fn find_range(&self, from: TextRange) -> Option<(HirFileId, TextRange)> {
|
||||||
let token_id = look_in_rev_map(&self.exp_map, from)?;
|
let token_id = look_in_rev_map(&self.exp_map, from)?;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
use hir::Source;
|
use hir::{db::AstDatabase, Source};
|
||||||
use ra_db::SourceDatabase;
|
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo::find_node_at_offset,
|
algo::find_node_at_offset,
|
||||||
ast::{self, DocCommentsOwner},
|
ast::{self, DocCommentsOwner},
|
||||||
match_ast, AstNode, SyntaxNode,
|
match_ast, AstNode, SyntaxNode, TextUnit,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -19,17 +18,29 @@ pub(crate) fn goto_definition(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
||||||
let parse = db.parse(position.file_id);
|
go(db, Source::new(position.file_id.into(), position.offset))
|
||||||
let syntax = parse.tree().syntax().clone();
|
}
|
||||||
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&syntax, position.offset) {
|
|
||||||
let navs =
|
fn go(db: &RootDatabase, offset: Source<TextUnit>) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
||||||
reference_definition(db, Source::new(position.file_id.into(), &name_ref)).to_vec();
|
let syntax = db.parse_or_expand(offset.file_id)?;
|
||||||
|
|
||||||
|
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&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()));
|
return Some(RangeInfo::new(name_ref.syntax().text_range(), navs.to_vec()));
|
||||||
}
|
}
|
||||||
if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) {
|
if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, offset.ast) {
|
||||||
let navs = name_definition(db, Source::new(position.file_id.into(), &name))?;
|
let navs = name_definition(db, offset.with_ast(&name))?;
|
||||||
return Some(RangeInfo::new(name.syntax().text_range(), navs));
|
return Some(RangeInfo::new(name.syntax().text_range(), navs));
|
||||||
}
|
}
|
||||||
|
if let Some(macro_call) = find_node_at_offset::<ast::MacroCall>(&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
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -677,4 +688,23 @@ mod tests {
|
||||||
"bar MODULE FileId(1) [0; 11) [4; 7)",
|
"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)",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,12 @@ pub fn token_tree_to_syntax_node(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenMap {
|
impl TokenMap {
|
||||||
|
pub fn token_by_offset(&self, relative_offset: TextUnit) -> Option<tt::TokenId> {
|
||||||
|
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<TextRange> {
|
pub fn relative_range_of(&self, tt: tt::TokenId) -> Option<TextRange> {
|
||||||
let idx = tt.0 as usize;
|
let idx = tt.0 as usize;
|
||||||
self.tokens.get(idx).copied()
|
self.tokens.get(idx).copied()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue