Goto type def through macros
//! FIXME: write short doc here
-use ra_db::SourceDatabase;
+use hir::db::AstDatabase;
use ra_syntax::{ast, AstNode};
-use crate::{db::RootDatabase, display::ToNav, FilePosition, NavigationTarget, RangeInfo};
+use crate::{
+ db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget,
+ RangeInfo,
+};
pub(crate) fn goto_type_definition(
db: &RootDatabase,
position: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
- let parse = db.parse(position.file_id);
+ let file = db.parse_or_expand(position.file_id.into())?;
+ let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?;
+ let token = descend_into_macros(db, position.file_id, token);
- let node = parse.tree().syntax().token_at_offset(position.offset).find_map(|token| {
+ let node = token.ast.ancestors().find_map(|token| {
token
- .parent()
.ancestors()
.find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())
})?;
- let analyzer =
- hir::SourceAnalyzer::new(db, hir::Source::new(position.file_id.into(), &node), None);
+ let analyzer = hir::SourceAnalyzer::new(db, token.with_ast(&node), None);
let ty: hir::Ty = if let Some(ty) =
ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e))
@@ -80,4 +83,23 @@ mod tests {
"Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
);
}
+
+ #[test]
+ fn goto_type_definition_works_through_macro() {
+ check_goto(
+ "
+ //- /lib.rs
+ macro_rules! id {
+ ($($tt:tt)*) => { $($tt)* }
+ }
+ struct Foo {}
+ id! {
+ fn bar() {
+ let f<|> = Foo {};
+ }
+ }
+ ",
+ "Foo STRUCT_DEF FileId(1) [52; 65) [59; 62)",
+ );
+ }
}
