1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
//! FIXME: write short doc here
use ra_db::SourceDatabase;
use ra_syntax::{ast, AstNode};
use crate::{db::RootDatabase, display::ToNav, 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 node = parse.tree().syntax().token_at_offset(position.offset).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, position.file_id, &node, None);
let ty: hir::Ty = if let Some(ty) =
ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e))
{
ty
} else if let Some(ty) = ast::Pat::cast(node.clone()).and_then(|p| analyzer.type_of_pat(db, &p))
{
ty
} else {
return None;
};
let adt_def = analyzer.autoderef(db, ty).find_map(|ty| ty.as_adt().map(|adt| adt.0))?;
let nav = adt_def.to_nav(db);
Some(RangeInfo::new(node.text_range(), vec![nav]))
}
#[cfg(test)]
mod tests {
use crate::mock_analysis::analysis_and_position;
fn check_goto(fixture: &str, expected: &str) {
let (analysis, pos) = analysis_and_position(fixture);
let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info;
assert_eq!(navs.len(), 1);
let nav = navs.pop().unwrap();
nav.assert_match(expected);
}
#[test]
fn goto_type_definition_works_simple() {
check_goto(
"
//- /lib.rs
struct Foo;
fn foo() {
let f: Foo;
f<|>
}
",
"Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
);
}
#[test]
fn goto_type_definition_works_simple_ref() {
check_goto(
"
//- /lib.rs
struct Foo;
fn foo() {
let f: &Foo;
f<|>
}
",
"Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
);
}
}
|