aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/name_ref_kind.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/name_ref_kind.rs')
-rw-r--r--crates/ra_ide_api/src/name_ref_kind.rs95
1 files changed, 95 insertions, 0 deletions
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 @@
1use ra_syntax::{AstNode, AstPtr, ast};
2use hir::Either;
3use crate::db::RootDatabase;
4use test_utils::tested_by;
5
6pub 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
18pub(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}