diff options
author | Laurențiu Nicola <[email protected]> | 2019-05-22 17:49:22 +0100 |
---|---|---|
committer | Laurențiu Nicola <[email protected]> | 2019-05-23 10:32:47 +0100 |
commit | 444e52e519a06e5ac397edf6fa2f4f2e06537e4e (patch) | |
tree | 21f3be8eeb51e97aae3bd848250e395710615937 /crates/ra_ide_api/src | |
parent | a25e103e4542637047fe388a926aebddca07b3b7 (diff) |
Move NameRef classification logic out of reference_definition
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r-- | crates/ra_ide_api/src/display/navigation_target.rs | 30 | ||||
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 104 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 11 | ||||
-rw-r--r-- | crates/ra_ide_api/src/name_ref_kind.rs | 95 |
4 files changed, 143 insertions, 97 deletions
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs index 7ea336c50..1c694cbc9 100644 --- a/crates/ra_ide_api/src/display/navigation_target.rs +++ b/crates/ra_ide_api/src/display/navigation_target.rs | |||
@@ -5,7 +5,7 @@ use ra_syntax::{ | |||
5 | ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, | 5 | ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, |
6 | algo::visit::{visitor, Visitor}, | 6 | algo::visit::{visitor, Visitor}, |
7 | }; | 7 | }; |
8 | use hir::{ModuleSource, FieldSource, ImplItem, Either}; | 8 | use hir::{ModuleSource, FieldSource, ImplItem}; |
9 | 9 | ||
10 | use crate::{FileSymbol, db::RootDatabase}; | 10 | use crate::{FileSymbol, db::RootDatabase}; |
11 | 11 | ||
@@ -77,17 +77,12 @@ impl NavigationTarget { | |||
77 | pub(crate) fn from_pat( | 77 | pub(crate) fn from_pat( |
78 | db: &RootDatabase, | 78 | db: &RootDatabase, |
79 | file_id: FileId, | 79 | file_id: FileId, |
80 | pat: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>, | 80 | pat: AstPtr<ast::Pat>, |
81 | ) -> NavigationTarget { | 81 | ) -> NavigationTarget { |
82 | let file = db.parse(file_id); | 82 | let file = db.parse(file_id); |
83 | let (name, full_range) = match pat { | 83 | let (name, full_range) = match pat.to_node(file.syntax()).kind() { |
84 | Either::A(pat) => match pat.to_node(file.syntax()).kind() { | 84 | ast::PatKind::BindPat(pat) => return NavigationTarget::from_bind_pat(file_id, &pat), |
85 | ast::PatKind::BindPat(pat) => { | 85 | _ => ("_".into(), pat.syntax_node_ptr().range()), |
86 | return NavigationTarget::from_bind_pat(file_id, &pat) | ||
87 | } | ||
88 | _ => ("_".into(), pat.syntax_node_ptr().range()), | ||
89 | }, | ||
90 | Either::B(slf) => ("self".into(), slf.syntax_node_ptr().range()), | ||
91 | }; | 86 | }; |
92 | NavigationTarget { | 87 | NavigationTarget { |
93 | file_id, | 88 | file_id, |
@@ -99,6 +94,21 @@ impl NavigationTarget { | |||
99 | } | 94 | } |
100 | } | 95 | } |
101 | 96 | ||
97 | pub(crate) fn from_self_param( | ||
98 | file_id: FileId, | ||
99 | par: AstPtr<ast::SelfParam>, | ||
100 | ) -> NavigationTarget { | ||
101 | let (name, full_range) = ("self".into(), par.syntax_node_ptr().range()); | ||
102 | NavigationTarget { | ||
103 | file_id, | ||
104 | name, | ||
105 | full_range, | ||
106 | focus_range: None, | ||
107 | kind: NAME, | ||
108 | container_name: None, | ||
109 | } | ||
110 | } | ||
111 | |||
102 | pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget { | 112 | pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget { |
103 | let (file_id, source) = module.definition_source(db); | 113 | let (file_id, source) = module.definition_source(db); |
104 | let file_id = file_id.as_original_file(); | 114 | let file_id = file_id.as_original_file(); |
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index adae29e9c..9c56f17f2 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -1,12 +1,19 @@ | |||
1 | use ra_db::{FileId, SourceDatabase}; | 1 | use ra_db::{FileId, SourceDatabase}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | AstNode, ast, | 3 | AstNode, ast, |
4 | algo::{find_node_at_offset, visit::{visitor, Visitor}}, | 4 | algo::{ |
5 | find_node_at_offset, | ||
6 | visit::{visitor, Visitor}, | ||
7 | }, | ||
5 | SyntaxNode, | 8 | SyntaxNode, |
6 | }; | 9 | }; |
7 | use test_utils::tested_by; | ||
8 | 10 | ||
9 | use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; | 11 | use crate::{ |
12 | FilePosition, NavigationTarget, | ||
13 | db::RootDatabase, | ||
14 | RangeInfo, | ||
15 | name_ref_kind::{NameRefKind::*, classify_name_ref}, | ||
16 | }; | ||
10 | 17 | ||
11 | pub(crate) fn goto_definition( | 18 | pub(crate) fn goto_definition( |
12 | db: &RootDatabase, | 19 | db: &RootDatabase, |
@@ -50,85 +57,24 @@ pub(crate) fn reference_definition( | |||
50 | 57 | ||
51 | let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); | 58 | let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); |
52 | 59 | ||
53 | // Special cases: | 60 | match classify_name_ref(db, &analyzer, name_ref) { |
54 | 61 | Some(Method(func)) => return Exact(NavigationTarget::from_function(db, func)), | |
55 | // Check if it is a method | 62 | Some(Macro(mac)) => return Exact(NavigationTarget::from_macro_def(db, mac)), |
56 | if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { | 63 | Some(FieldAccess(field)) => return Exact(NavigationTarget::from_field(db, field)), |
57 | tested_by!(goto_definition_works_for_methods); | 64 | Some(AssocItem(assoc)) => return Exact(NavigationTarget::from_impl_item(db, assoc)), |
58 | if let Some(func) = analyzer.resolve_method_call(method_call) { | 65 | Some(Def(def)) => return Exact(NavigationTarget::from_def(db, def)), |
59 | return Exact(NavigationTarget::from_function(db, func)); | 66 | Some(SelfType(ty)) => { |
60 | } | 67 | if let Some((def_id, _)) = ty.as_adt() { |
61 | } | 68 | return Exact(NavigationTarget::from_adt_def(db, def_id)); |
62 | |||
63 | //it could be a macro call | ||
64 | if let Some(macro_call) = name_ref | ||
65 | .syntax() | ||
66 | .parent() | ||
67 | .and_then(|node| node.parent()) | ||
68 | .and_then(|node| node.parent()) | ||
69 | .and_then(ast::MacroCall::cast) | ||
70 | { | ||
71 | tested_by!(goto_definition_works_for_macros); | ||
72 | if let Some(macro_call) = analyzer.resolve_macro_call(macro_call) { | ||
73 | return Exact(NavigationTarget::from_macro_def(db, macro_call)); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | // It could also be a field access | ||
78 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { | ||
79 | tested_by!(goto_definition_works_for_fields); | ||
80 | if let Some(field) = analyzer.resolve_field(field_expr) { | ||
81 | return Exact(NavigationTarget::from_field(db, field)); | ||
82 | }; | ||
83 | } | ||
84 | |||
85 | // It could also be a named field | ||
86 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) { | ||
87 | tested_by!(goto_definition_works_for_named_fields); | ||
88 | |||
89 | let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); | ||
90 | |||
91 | if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, lit.into())) { | ||
92 | if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() { | ||
93 | let hir_path = hir::Path::from_name_ref(name_ref); | ||
94 | let hir_name = hir_path.as_ident().unwrap(); | ||
95 | |||
96 | if let Some(field) = s.field(db, hir_name) { | ||
97 | return Exact(NavigationTarget::from_field(db, field)); | ||
98 | } | ||
99 | } | 69 | } |
100 | } | 70 | } |
101 | } | 71 | Some(Pat(pat)) => return Exact(NavigationTarget::from_pat(db, file_id, pat)), |
102 | 72 | Some(SelfParam(par)) => return Exact(NavigationTarget::from_self_param(file_id, par)), | |
103 | // General case, a path or a local: | 73 | Some(GenericParam(_)) => { |
104 | if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { | 74 | // FIXME: go to the generic param def |
105 | if let Some(resolved) = analyzer.resolve_path(db, path) { | ||
106 | match resolved { | ||
107 | hir::PathResolution::Def(def) => return Exact(NavigationTarget::from_def(db, def)), | ||
108 | hir::PathResolution::LocalBinding(pat) => { | ||
109 | let nav = NavigationTarget::from_pat(db, file_id, pat); | ||
110 | return Exact(nav); | ||
111 | } | ||
112 | hir::PathResolution::GenericParam(..) => { | ||
113 | // FIXME: go to the generic param def | ||
114 | } | ||
115 | hir::PathResolution::Macro(def) => { | ||
116 | let nav = NavigationTarget::from_macro_def(db, def); | ||
117 | return Exact(nav); | ||
118 | } | ||
119 | hir::PathResolution::SelfType(impl_block) => { | ||
120 | let ty = impl_block.target_ty(db); | ||
121 | |||
122 | if let Some((def_id, _)) = ty.as_adt() { | ||
123 | return Exact(NavigationTarget::from_adt_def(db, def_id)); | ||
124 | } | ||
125 | } | ||
126 | hir::PathResolution::AssocItem(assoc) => { | ||
127 | return Exact(NavigationTarget::from_impl_item(db, assoc)); | ||
128 | } | ||
129 | } | ||
130 | } | 75 | } |
131 | } | 76 | None => {} |
77 | }; | ||
132 | 78 | ||
133 | // Fallback index based approach: | 79 | // Fallback index based approach: |
134 | let navs = crate::symbol_index::index_resolve(db, name_ref) | 80 | let navs = crate::symbol_index::index_resolve(db, name_ref) |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index d4be8bd6c..f78348f74 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -18,6 +18,7 @@ mod change; | |||
18 | mod status; | 18 | mod status; |
19 | mod completion; | 19 | mod completion; |
20 | mod runnables; | 20 | mod runnables; |
21 | mod name_ref_kind; | ||
21 | mod goto_definition; | 22 | mod goto_definition; |
22 | mod goto_type_definition; | 23 | mod goto_type_definition; |
23 | mod extend_selection; | 24 | mod extend_selection; |
@@ -53,10 +54,7 @@ use ra_db::{ | |||
53 | }; | 54 | }; |
54 | use relative_path::RelativePathBuf; | 55 | use relative_path::RelativePathBuf; |
55 | 56 | ||
56 | use crate::{ | 57 | use crate::{symbol_index::FileSymbol, db::LineIndexDatabase}; |
57 | symbol_index::FileSymbol, | ||
58 | db::LineIndexDatabase, | ||
59 | }; | ||
60 | 58 | ||
61 | pub use crate::{ | 59 | pub use crate::{ |
62 | change::{AnalysisChange, LibraryData}, | 60 | change::{AnalysisChange, LibraryData}, |
@@ -73,10 +71,7 @@ pub use crate::{ | |||
73 | display::{FunctionSignature, NavigationTarget, StructureNode, file_structure}, | 71 | display::{FunctionSignature, NavigationTarget, StructureNode, file_structure}, |
74 | }; | 72 | }; |
75 | 73 | ||
76 | pub use ra_db::{ | 74 | pub use ra_db::{Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId, Edition}; |
77 | Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId, | ||
78 | Edition | ||
79 | }; | ||
80 | pub use hir::Documentation; | 75 | pub use hir::Documentation; |
81 | 76 | ||
82 | // We use jemalloc mainly to get heap usage statistics, actual performance | 77 | // We use jemalloc mainly to get heap usage statistics, actual performance |
diff --git a/crates/ra_ide_api/src/name_ref_kind.rs b/crates/ra_ide_api/src/name_ref_kind.rs new file mode 100644 index 000000000..b498fe495 --- /dev/null +++ b/crates/ra_ide_api/src/name_ref_kind.rs | |||
@@ -0,0 +1,95 @@ | |||
1 | use ra_syntax::{AstNode, AstPtr, ast}; | ||
2 | use hir::Either; | ||
3 | use crate::db::RootDatabase; | ||
4 | use test_utils::tested_by; | ||
5 | |||
6 | pub enum NameRefKind { | ||
7 | Method(hir::Function), | ||
8 | Macro(hir::MacroByExampleDef), | ||
9 | FieldAccess(hir::StructField), | ||
10 | AssocItem(hir::ImplItem), | ||
11 | Def(hir::ModuleDef), | ||
12 | SelfType(hir::Ty), | ||
13 | Pat(AstPtr<ast::Pat>), | ||
14 | SelfParam(AstPtr<ast::SelfParam>), | ||
15 | GenericParam(u32), | ||
16 | } | ||
17 | |||
18 | pub(crate) fn classify_name_ref( | ||
19 | db: &RootDatabase, | ||
20 | analyzer: &hir::SourceAnalyzer, | ||
21 | name_ref: &ast::NameRef, | ||
22 | ) -> Option<NameRefKind> { | ||
23 | use NameRefKind::*; | ||
24 | |||
25 | // Check if it is a method | ||
26 | if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { | ||
27 | tested_by!(goto_definition_works_for_methods); | ||
28 | if let Some(func) = analyzer.resolve_method_call(method_call) { | ||
29 | return Some(Method(func)); | ||
30 | } | ||
31 | } | ||
32 | |||
33 | // It could be a macro call | ||
34 | if let Some(macro_call) = name_ref | ||
35 | .syntax() | ||
36 | .parent() | ||
37 | .and_then(|node| node.parent()) | ||
38 | .and_then(|node| node.parent()) | ||
39 | .and_then(ast::MacroCall::cast) | ||
40 | { | ||
41 | tested_by!(goto_definition_works_for_macros); | ||
42 | if let Some(mac) = analyzer.resolve_macro_call(macro_call) { | ||
43 | return Some(Macro(mac)); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | // It could also be a field access | ||
48 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { | ||
49 | tested_by!(goto_definition_works_for_fields); | ||
50 | if let Some(field) = analyzer.resolve_field(field_expr) { | ||
51 | return Some(FieldAccess(field)); | ||
52 | }; | ||
53 | } | ||
54 | |||
55 | // It could also be a named field | ||
56 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) { | ||
57 | tested_by!(goto_definition_works_for_named_fields); | ||
58 | |||
59 | let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); | ||
60 | |||
61 | if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, lit.into())) { | ||
62 | if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() { | ||
63 | let hir_path = hir::Path::from_name_ref(name_ref); | ||
64 | let hir_name = hir_path.as_ident().unwrap(); | ||
65 | |||
66 | if let Some(field) = s.field(db, hir_name) { | ||
67 | return Some(FieldAccess(field)); | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | // General case, a path or a local: | ||
74 | if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { | ||
75 | if let Some(resolved) = analyzer.resolve_path(db, path) { | ||
76 | return match resolved { | ||
77 | hir::PathResolution::Def(def) => Some(Def(def)), | ||
78 | hir::PathResolution::LocalBinding(Either::A(pat)) => Some(Pat(pat)), | ||
79 | hir::PathResolution::LocalBinding(Either::B(par)) => Some(SelfParam(par)), | ||
80 | hir::PathResolution::GenericParam(par) => { | ||
81 | // FIXME: get generic param def | ||
82 | Some(GenericParam(par)) | ||
83 | } | ||
84 | hir::PathResolution::Macro(def) => Some(Macro(def)), | ||
85 | hir::PathResolution::SelfType(impl_block) => { | ||
86 | let ty = impl_block.target_ty(db); | ||
87 | Some(SelfType(ty)) | ||
88 | } | ||
89 | hir::PathResolution::AssocItem(assoc) => Some(AssocItem(assoc)), | ||
90 | }; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | None | ||
95 | } | ||