diff options
author | Seivan Heidari <[email protected]> | 2019-11-28 07:19:14 +0000 |
---|---|---|
committer | Seivan Heidari <[email protected]> | 2019-11-28 07:19:14 +0000 |
commit | 18a0937585b836ec5ed054b9ae48e0156ab6d9ef (patch) | |
tree | 9de2c0267ddcc00df717f90034d0843d751a851b /crates/ra_ide/src/goto_type_definition.rs | |
parent | a7394b44c870f585eacfeb3036a33471aff49ff8 (diff) | |
parent | 484acc8a61d599662ed63a4cbda091d38a982551 (diff) |
Merge branch 'master' of https://github.com/rust-analyzer/rust-analyzer into feature/themes
Diffstat (limited to 'crates/ra_ide/src/goto_type_definition.rs')
-rw-r--r-- | crates/ra_ide/src/goto_type_definition.rs | 105 |
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 | |||
3 | use hir::db::AstDatabase; | ||
4 | use ra_syntax::{ast, AstNode}; | ||
5 | |||
6 | use crate::{ | ||
7 | db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, | ||
8 | RangeInfo, | ||
9 | }; | ||
10 | |||
11 | pub(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)] | ||
45 | mod 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 | } | ||