1 use ra_db::{FileId, SyntaxDatabase};
4 algo::find_node_at_offset,
7 use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
9 pub(crate) fn goto_definition(
11 position: FilePosition,
12 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
13 let file = db.source_file(position.file_id);
14 let syntax = file.syntax();
15 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
16 let navs = reference_definition(db, position.file_id, name_ref).to_vec();
17 return Some(RangeInfo::new(name_ref.syntax().range(), navs.to_vec()));
19 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
20 let navs = name_definition(db, position.file_id, name)?;
21 return Some(RangeInfo::new(name.syntax().range(), navs));
26 pub(crate) enum ReferenceResult {
27 Exact(NavigationTarget),
28 Approximate(Vec<NavigationTarget>),
31 impl ReferenceResult {
32 fn to_vec(self) -> Vec<NavigationTarget> {
33 use self::ReferenceResult::*;
35 Exact(target) => vec![target],
36 Approximate(vec) => vec,
41 pub(crate) fn reference_definition(
44 name_ref: &ast::NameRef,
45 ) -> ReferenceResult {
46 use self::ReferenceResult::*;
47 if let Some(function) =
48 hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())
50 let scope = function.scopes(db);
51 // First try to resolve the symbol locally
52 if let Some(entry) = scope.resolve_local_name(name_ref) {
53 let nav = NavigationTarget::from_scope_entry(file_id, &entry);
57 // Next check if it is a method
58 if let Some(method_call) = name_ref
61 .and_then(ast::MethodCallExpr::cast)
63 let infer_result = function.infer(db);
64 let syntax_mapping = function.body_syntax_mapping(db);
65 let expr = ast::Expr::cast(method_call.syntax()).unwrap();
66 if let Some(func) = syntax_mapping
68 .and_then(|it| infer_result.method_resolution(it))
70 return Exact(NavigationTarget::from_function(db, func));
74 // Then try module name resolution
75 if let Some(module) = hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax())
77 if let Some(path) = name_ref
80 .find_map(ast::Path::cast)
81 .and_then(hir::Path::from_ast)
83 let resolved = module.resolve_path(db, &path);
84 if let Some(def_id) = resolved.take_types().or(resolved.take_values()) {
85 if let Some(target) = NavigationTarget::from_def(db, def_id) {
91 // If that fails try the index based approach.
93 .index_resolve(name_ref)
95 .map(NavigationTarget::from_symbol)
104 ) -> Option<Vec<NavigationTarget>> {
105 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
106 if module.has_semi() {
107 if let Some(child_module) =
108 hir::source_binder::module_from_declaration(db, file_id, module)
110 let nav = NavigationTarget::from_module(db, child_module);
111 return Some(vec![nav]);
120 use crate::mock_analysis::analysis_and_position;
122 fn check_goto(fixuture: &str, expected: &str) {
123 let (analysis, pos) = analysis_and_position(fixuture);
125 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info;
126 assert_eq!(navs.len(), 1);
127 let nav = navs.pop().unwrap();
128 nav.assert_match(expected);
132 fn goto_definition_works_in_items() {
139 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
144 fn goto_definition_resolves_correct_name() {
157 "Foo STRUCT_DEF FileId(2) [0; 11) [7; 10)",
162 fn goto_definition_works_for_module_declaration() {
170 "foo SOURCE_FILE FileId(2) [0; 10)",
180 "foo SOURCE_FILE FileId(2) [0; 10)",
185 fn goto_definition_works_for_methods() {
191 fn frobnicate(&self) { }
198 "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)",
208 "foo SOURCE_FILE FileId(2) [0; 10)",