aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/goto_type_definition.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/goto_type_definition.rs')
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs105
1 files changed, 105 insertions, 0 deletions
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
new file mode 100644
index 000000000..992a08809
--- /dev/null
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -0,0 +1,105 @@
1//! FIXME: write short doc here
2
3use hir::db::AstDatabase;
4use ra_syntax::{ast, AstNode};
5
6use crate::{
7 db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget,
8 RangeInfo,
9};
10
11pub(crate) fn goto_type_definition(
12 db: &RootDatabase,
13 position: FilePosition,
14) -> Option<RangeInfo<Vec<NavigationTarget>>> {
15 let file = db.parse_or_expand(position.file_id.into())?;
16 let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?;
17 let token = descend_into_macros(db, position.file_id, token);
18
19 let node = token.value.ancestors().find_map(|token| {
20 token
21 .ancestors()
22 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())
23 })?;
24
25 let analyzer = hir::SourceAnalyzer::new(db, token.with_value(&node), None);
26
27 let ty: hir::Type = if let Some(ty) =
28 ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e))
29 {
30 ty
31 } else if let Some(ty) = ast::Pat::cast(node.clone()).and_then(|p| analyzer.type_of_pat(db, &p))
32 {
33 ty
34 } else {
35 return None;
36 };
37
38 let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?;
39
40 let nav = adt_def.to_nav(db);
41 Some(RangeInfo::new(node.text_range(), vec![nav]))
42}
43
44#[cfg(test)]
45mod tests {
46 use crate::mock_analysis::analysis_and_position;
47
48 fn check_goto(fixture: &str, expected: &str) {
49 let (analysis, pos) = analysis_and_position(fixture);
50
51 let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info;
52 assert_eq!(navs.len(), 1);
53 let nav = navs.pop().unwrap();
54 nav.assert_match(expected);
55 }
56
57 #[test]
58 fn goto_type_definition_works_simple() {
59 check_goto(
60 "
61 //- /lib.rs
62 struct Foo;
63 fn foo() {
64 let f: Foo;
65 f<|>
66 }
67 ",
68 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
69 );
70 }
71
72 #[test]
73 fn goto_type_definition_works_simple_ref() {
74 check_goto(
75 "
76 //- /lib.rs
77 struct Foo;
78 fn foo() {
79 let f: &Foo;
80 f<|>
81 }
82 ",
83 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
84 );
85 }
86
87 #[test]
88 fn goto_type_definition_works_through_macro() {
89 check_goto(
90 "
91 //- /lib.rs
92 macro_rules! id {
93 ($($tt:tt)*) => { $($tt)* }
94 }
95 struct Foo {}
96 id! {
97 fn bar() {
98 let f<|> = Foo {};
99 }
100 }
101 ",
102 "Foo STRUCT_DEF FileId(1) [52; 65) [59; 62)",
103 );
104 }
105}