1 use ra_db::{FileId, SourceDatabase};
4 algo::find_node_at_offset,
6 use test_utils::tested_by;
9 use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
11 pub(crate) fn goto_definition(
13 position: FilePosition,
14 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
15 let file = db.parse(position.file_id);
16 let syntax = file.syntax();
17 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
18 let navs = reference_definition(db, position.file_id, name_ref).to_vec();
19 return Some(RangeInfo::new(name_ref.syntax().range(), navs.to_vec()));
21 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
22 let navs = name_definition(db, position.file_id, name)?;
23 return Some(RangeInfo::new(name.syntax().range(), navs));
28 pub(crate) enum ReferenceResult {
29 Exact(NavigationTarget),
30 Approximate(Vec<NavigationTarget>),
33 impl ReferenceResult {
34 fn to_vec(self) -> Vec<NavigationTarget> {
35 use self::ReferenceResult::*;
37 Exact(target) => vec![target],
38 Approximate(vec) => vec,
43 pub(crate) fn reference_definition(
46 name_ref: &ast::NameRef,
47 ) -> ReferenceResult {
48 use self::ReferenceResult::*;
49 if let Some(function) =
50 hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())
52 // Check if it is a method
53 if let Some(method_call) = name_ref
56 .and_then(ast::MethodCallExpr::cast)
58 tested_by!(goto_definition_works_for_methods);
59 let infer_result = function.infer(db);
60 let syntax_mapping = function.body_syntax_mapping(db);
61 let expr = ast::Expr::cast(method_call.syntax()).unwrap();
62 if let Some(func) = syntax_mapping
64 .and_then(|it| infer_result.method_resolution(it))
66 return Exact(NavigationTarget::from_function(db, func));
69 // It could also be a field access
70 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
71 tested_by!(goto_definition_works_for_fields);
72 let infer_result = function.infer(db);
73 let syntax_mapping = function.body_syntax_mapping(db);
74 let expr = ast::Expr::cast(field_expr.syntax()).unwrap();
75 if let Some(field) = syntax_mapping
77 .and_then(|it| infer_result.field_resolution(it))
79 return Exact(NavigationTarget::from_field(db, field));
83 // Try name resolution
84 let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax());
85 if let Some(path) = name_ref
88 .find_map(ast::Path::cast)
89 .and_then(hir::Path::from_ast)
91 let resolved = resolver.resolve_path(db, &path);
92 match resolved.clone().take_types().or(resolved.take_values()) {
93 Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)),
94 Some(Resolution::LocalBinding(pat)) => {
95 let body = resolver.body().expect("no body for local binding");
96 let syntax_mapping = body.syntax_mapping(db);
97 let ptr = syntax_mapping
99 .expect("pattern not found in syntax mapping");
103 .expect("local binding from a multi-segment path");
104 let nav = NavigationTarget::from_scope_entry(file_id, name, ptr);
107 Some(Resolution::GenericParam(..)) => {
108 // TODO go to the generic param def
110 Some(Resolution::SelfType(_impl_block)) => {
111 // TODO go to the implemented type
116 // If that fails try the index based approach.
118 .index_resolve(name_ref)
120 .map(NavigationTarget::from_symbol)
129 ) -> Option<Vec<NavigationTarget>> {
130 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
131 if module.has_semi() {
132 if let Some(child_module) =
133 hir::source_binder::module_from_declaration(db, file_id, module)
135 let nav = NavigationTarget::from_module(db, child_module);
136 return Some(vec![nav]);
145 use test_utils::covers;
147 use crate::mock_analysis::analysis_and_position;
149 fn check_goto(fixuture: &str, expected: &str) {
150 let (analysis, pos) = analysis_and_position(fixuture);
152 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info;
153 assert_eq!(navs.len(), 1);
154 let nav = navs.pop().unwrap();
155 nav.assert_match(expected);
159 fn goto_definition_works_in_items() {
166 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
171 fn goto_definition_resolves_correct_name() {
184 "Foo STRUCT_DEF FileId(2) [0; 11) [7; 10)",
189 fn goto_definition_works_for_module_declaration() {
197 "foo SOURCE_FILE FileId(2) [0; 10)",
207 "foo SOURCE_FILE FileId(2) [0; 10)",
212 fn goto_definition_works_for_methods() {
213 covers!(goto_definition_works_for_methods);
219 fn frobnicate(&self) { }
226 "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)",
231 fn goto_definition_works_for_fields() {
232 covers!(goto_definition_works_for_fields);
244 "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)",